/* atlas64.js - Primary js include file for Atlas 64.
(c) 2002-2007 Shawn Houser, All Rights Reserved
This is proprietary and confidential material.
Please note Atlas is NO LONGER open source as of 2004.
NOTE: Most protocol support for prior Atlas versions has been dropped.

SECURITY NOTES:
It is known that the client templating will allow injection attacks.
However... so what?  Heck, it might even be beneficial, since it will
confuse the heck out of scanners & script kiddies.  None of the injection
will be allowed to leave their browser of course.  And inherently we already
assume everything on the browser is known and anything coming in from the
browser is unsafe.  Anyhow, I hesitate to fix this with a filter such as
the backend's ATMakeSafeString, because in JS the performance penalty
would be so severe, and it already is too slow for many things.
*/

var ATTplTemp = new Array();// Temp storage for templates
var AT_IGNORE=0,AT_ALPHANUM=1,AT_ALPHA=2,AT_NUMBER=3,AT_PHONE=4,AT_EMAIL=5,AT_URL=6,AT_CREDITCARD=7,AT_TITLE=8,AT_ADDRESS=9

/////////////////////////////////////////////////////////////////////////////// Async IO
var ATGTXMLObjs = new Array();      // List of XML request objects
var ATGTAllocatedObjs = 0;          // # of currently allocated objs
var ATGTQueryFrames = new Array();  // List of query frames (for non-XML)
var ATGTAllocatedFrames = 0;        // # of currently allocated frames

var ATGTRequestID = 1;              // Request ID counter
var ATGT_MAX_OBJS = 25;             // Max objs I can allocate
var ATGT_MAX_FRAMES = 25;           // Max frames I can allocate
var ATGT_TIMEOUT = 10000;           // Default timeout in MS
var ATGT_TIMEOUT_ERR= -987;         // Timeout error value
var ATSems = new Array();           // Used to monitor for changes

var ATM_MAX_RADIANS = (2.0 * Math.PI);        // Useful radians/degrees stuff
var ATM_ONE_DEGREE =  (ATM_MAX_RADIANS / 360.0);
var ATM_DEG2RADCOMP = 360.0/ATM_MAX_RADIANS;
var AT_MOD_POLL =     50;                     // Poll wait time in milliseconds

function ATGTSetCallbacks(NF,Frame,isXML,Callback,Timeout,ArgumentEcho) {
    var UseTimeout;
    ATCallOnModified(NF,"ATGTGetCB",4,Frame,isXML,Callback,ArgumentEcho);
    Timeout?UseTimeout=Timeout:UseTimeout=ATGT_TIMEOUT;
    if(UseTimeout>-1){
        var req;
        (isXML)?req=NF.RequestID:req=NF.ATRequestID;
        setTimeout("ATGTCheckTO("+Frame+","+isXML+","+req+")",UseTimeout);
    }
}
// Returns IO callbacks.  For XML args passed are: XMLObj, Callback text as passed in, argument echo as passed in.  For NON-XML args passed are: iframe obj, frame tracking object, Callback text as passed in, argument echo as passed in.
function ATGTGetCB(Frame,isXML,Callback,ArgumentEcho) {
    var MyObj,FN=this[Callback];// Get user's cb function reference
    if(!FN)FN=eval(Callback);
    if (parseInt(isXML)) {
        MyObj=ATGTXMLObjs[parseInt(Frame)];
        MyObj.RequestID = 0;// This makes sure we don't time out
        if(FN) FN(MyObj, Callback, ArgumentEcho);
    }
    else {
        MyObj=ATGTQueryFrames[parseInt(Frame)];
        MyObj.ATRequestID=0;// This makes sure we don't time out
        FN(frames[ATGTQueryFrames[parseInt(Frame)].id],
            ATGTQueryFrames[parseInt(Frame)], Callback, ArgumentEcho);
    }
}
function ATGTCheckTO(Frame,isXML,RequestID) {
    if (!isXML) {
        var MyFrame=ATGTQueryFrames[parseInt(Frame)];
        if ( MyFrame.ATRequestID==RequestID ){
            MyFrame.src="";
            MyFrame.ATErrorCode=ATGT_TIMEOUT_ERR;
            MyFrame.ATErrorMess="Timeout.";
            MyFrame.ATModified=1;
        }
    }
    else {
        var MyObj=ATGTXMLObjs[parseInt(Frame)];
        if ( MyObj.RequestID==RequestID ){
            MyObj.ATErrorCode=ATGT_TIMEOUT_ERR;
            MyObj.ReqObj.abort();
            MyObj.ATModified = 1;
        }
    }
}
// ALWAYS CALL ATFreeXGet TO RELEASE THE OBJECT ASAP.
function ATXGet(URL,Callback,Async,Timeout,CallbackArg) {
    var Obj,UseTimeout;
    if ((Obj=ATGTGetAvailObj())==null)
        return alert("ATGET ERROR: Failed ATGTGetAvailObj");
    var XObj=ATGTXMLObjs[Obj];
    (URL.indexOf("?")>0) ? URL+="&":URL+="?";
    URL+="ATNOCACHE="+(Math.random());
    XObj.ReqObj.open("GET",URL,Async);
    XObj.CB=Callback;XObj.CBArg=CallbackArg;XObj.URL=URL;
//alert(URL);
    if (!Async) {
        XObj.ReqObj.send(null);
        return XObj;
    }
    else{
        XObj.ReqObj.send(null);
        ATGTSetCallbacks(XObj,Obj,1,Callback,Timeout,CallbackArg);
    }
    return null;
}
//********** Gets a given request, then calls back specified CB function,       ATGet
// passing a reference to the loaded frame, with a timeout as specified in MS.
// If no timeout specified, then default is used.  -1 means no timeout.  The
// loaded frame MUST support atlas protocol for this call to work correctly. The
// ArgumentEcho parameter will be passed back to the caller as the last arg- the
// contents of it are completely up to the user.
// ALWAYS CALL ATFreeGet TO RELEASE THE FRAME ASAP
function ATGet(Request,Callback,Timeout,ArgumentEcho) {
    var Frame;
    if ( (Frame = ATGTGetEmptyFrame()) == -1 ) return;  // Get an empty frame
    (Request.indexOf("?")>0) ? Request+="&":Request+="?";// Cache buster & frame ID
    Request+="ATGETID="+Frame+"&ATNOCACHE="+(Math.random());
    var NF = ATGTQueryFrames[Frame];
    ATGTResetFrame(NF);                                 // Reset the frame
    NF.src = Request;                                   // Make the request
    ATGTSetCallbacks(NF,Frame,0,Callback,Timeout,ArgumentEcho);// Set callbacks
}
//********** Release frame for reuse, and also tells browser to ignore frame.   ATFreeGet
// Should be passed ref to frame
function ATFreeGet(IntFrame) {
    for ( z = 0; z < ATGTQueryFrames.length; z++ ) {
        if ( IntFrame == ATGTQueryFrames[z].FrameRef ) {
            ATGTQueryFrames[z].src = "";         // Tell browser to ignore
            ATGTQueryFrames[z].ATRequestID = -1; // Mark for reuse
            break;
        }
    }
}
//********** INTERNAL CALL Tries to find a frame to use for the get             ATGTGetEmptyFrame
function ATGTGetEmptyFrame() {
    var Frame=-1;
    for ( var z = 0; z < ATGTAllocatedFrames; z++ ) {   // Try to find empty frame
        if ( ATGTQueryFrames[z].ATRequestID == -1 ) {
            Frame = z;
            // Hide frame in case it was previously displayed
            ATGTQueryFrames[z].style.visibility =   "hidden";
            break;
        }
    }
    if ( Frame == -1 ) {                            // Can't find, can we make?
        if ( ATGTAllocatedFrames < ATGT_MAX_FRAMES ) {
            Frame=ATGTAllocatedFrames++;
            var NF = document.createElement("iframe");
            NF.setAttribute("id", "ATQF"+ Frame);
            NF.setAttribute("name", "ATQF"+ Frame);
//            NF.style.visibility =   "hidden";
// The following lines are for debug purposes- comment out visibility above & change coords to see frame
/*NF.style.visibility = "visible";
NF.style.zIndex =   5000;
NF.style.top = "450px";
NF.style.left = "0px";
NF.style.width = "500px";
NF.style.height = "500px";*/

            NF.style.position = "absolute";
            NF.style.overflow = "hidden";
            NF.style.top = "-750px";
            NF.style.left = "-750px";
            NF.style.width = "3px";
            NF.style.height = "3px";

            document.body.appendChild(NF);

            NF.FrameRef = frames[NF.id];
            ATGTQueryFrames[Frame]=NF;
        }
        else {
            alert("ATGET ERROR: Out of query frames!");
            return -1;
        }
    }
    return Frame;
}
function ATGTResetFrame(NF) {
    NF.ATModified = 0;                  // Clear the modified flag
    NF.ATErrorCode = 0;                 // Clear the error stuff
    NF.ATErrorMess = "";
    NF.ATRequestID = ATGTRequestID++;   // Assign a request ID
}
// Uses XML methods to post data- identical to ATXGet otherwise. 'DATA' is
// the key fieldname posted to server, with passed data as the value. THIS CALL
// WILL AUTOMATICALLY RUN escape() OVER THE DATA TO BE POSTED.
// ALWAYS CALL ATFreeXPost TO RELEASE THE OBJECT ASAP.
function ATXPostData(data,URL,Callback,Async,Timeout,CallbackArg) {
    var Obj,UseTimeout;
    if ((Obj=ATGTGetAvailObj())==null)
        return alert("ATPOST ERROR: Failed ATGTGetAvailObj");
    var XObj=ATGTXMLObjs[Obj];
    (URL.indexOf("?")>0) ? URL+="&":URL+="?";
    URL+="ATNOCACHE="+(Math.random());
    XObj.ReqObj.open("POST",URL,Async);
    data=escape(data);
    XObj.ReqObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    XObj.ReqObj.setRequestHeader("Content-length", data.length+5);
    XObj.CB=Callback;XObj.CBArg=CallbackArg;XObj.URL=URL;
    XObj.ReqObj.send("data="+data);
    if (!Async) return XObj;
    ATGTSetCallbacks(XObj,Obj,1,Callback,Timeout,CallbackArg);
    return null;
}
// Post a form to server- ALWAYS FREE ASAP
function ATXPostForm(Frm,URL,Callback,Async,Timeout,CallbackArg) {
    var Obj,UseTimeout,Field,Ctr=0,Data="";
    if ((Obj=ATGTGetAvailObj())==null)
        return alert("ATPOST ERROR: Failed ATGTGetAvailObj");
    var XObj=ATGTXMLObjs[Obj];
    (URL.indexOf("?")>0) ? URL+="&":URL+="?";
    URL+="ATNOCACHE="+(Math.random());
    for(Ctr=0;Ctr<Frm.length;Ctr++){
//    for ( Field in Frm ) {
        if ( Frm[Ctr] && Frm[Ctr].name && Frm[Ctr].value ) {
            if ( (Frm[Ctr].tagName == "INPUT")    ||
                 (Frm[Ctr].tagName == "SELECT")   ||
                 (Frm[Ctr].tagName == "TEXTAREA") ) {
                if ( Frm[Ctr].type == "checkbox" ) {
                    if(Ctr)Data+="&";
                    Data+=Frm[Ctr].name+"="+Frm[Ctr].checked;
                }
                else if ( Frm[Ctr].type == "radio" ) {
                    if ( Frm[Ctr].checked ) {
                        if(Ctr)Data+="&";
                        Data+=Frm[Ctr].name+"="+escape(Frm[Ctr].value);
                    }
                }
                else {
                    if(Ctr)Data+="&";
                    Data+=Frm[Ctr].name+"="+escape(Frm[Ctr].value);
                }
            }
        }
//        Ctr++;
    }
    XObj.ReqObj.open("POST",URL,Async);
    XObj.ReqObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    XObj.ReqObj.setRequestHeader("Content-length", Data.length);
    XObj.CB=Callback;XObj.CBArg=CallbackArg;XObj.URL=URL;
//alert(Data);
    XObj.ReqObj.send(Data);
    if (!Async) return XObj;
    ATGTSetCallbacks(XObj,Obj,1,Callback,Timeout,CallbackArg);
    return null;
}
function ATGTGetAvailObj() {
    var obj;
    for (var z=0;z<ATGTAllocatedObjs;z++){
        if (ATGTXMLObjs[z].RequestID==-1){
            ATGTXMLObjs[z].RequestID=ATGTRequestID++;
            ATGTXMLObjs[z].ATModified=0;
            return z;
        }
    }
    if (ATGTAllocatedObjs>=ATGT_MAX_OBJS) alert("ATGET ERROR: Exceeded Max Obj Alloc");
    ATGTXMLObjs[ATGTAllocatedObjs++]=new ATGTXObj();
    return ATGTAllocatedObjs-1;
}
// Should be passed ref to obj
function ATFreeXGet(Obj) {
    Obj.RequestID=-1;
}
// Should be passed ref to obj
function ATFreeXPost(Obj) {
    ATFreeXGet(obj);
}
// Object that is an XML request
function ATGTXObj() {
    this.RequestID=0;this.CB="";this.CBArg="";this.URL="";this.ATModified=0;this.ATErrorCode=0;
    // IE (prior to 7) vs. everyone else
    (window.XMLHttpRequest) ?
        this.ReqObj=new XMLHttpRequest():this.ReqObj=new ActiveXObject("Microsoft.XMLHTTP");
}
//********** Calls a list of requested functions, with requested args, when a
// specified ATCallOnModified object's "ATModified" member is set, or if it's an
// XML request obj it will watch for obj.XMLObj.readyState==4 as well.  Pass the
// object to watch first, then as a comma separated list:
// {function name to call IN QUOTES, # of args, arg1, argn...}
// Repeat section in braces as needed.  It will clear the modified flag.
function  ATCallOnModified() {
    var ExitNow = 0, args, z, i;
    if ( typeof(arguments[0]) == "object" ){// If the first time I have called
        for(z=0;z<ATSems.length;z++){       // Look for an empty spot in array
            if(!ATSems[z]) break;
        }
        ATSems[z]=arguments[0];             // Save this object in the array
        args = "ATCallOnModified(\"" +      // Set the first arg & mark for exit
            z + "\",";
        ExitNow++;
    }                                       // If not an XML object and not modified
    else if ( !ATSems[parseInt(arguments[0])].ReqObj && !ATSems[parseInt(arguments[0])].ATModified ) {
        args = "ATCallOnModified(\"" +      // Set the first arg & mark for exit
            arguments[0] + "\",";
        ExitNow++;
    }                                       // If an xml object & not modified
    else if ( ATSems[parseInt(arguments[0])].ReqObj && (!(ATSems[parseInt(arguments[0])].ReqObj.readyState==4) && !(ATSems[parseInt(arguments[0])].ATModified)) ) {
        args = "ATCallOnModified(\"" +  // Set the first arg & mark for exit
            arguments[0] + "\",";
        ExitNow++;
    }
    if ( ExitNow ) {                        // If we are ready to exit
        for ( i = 1; i < arguments.length; ++i ) {  // Add all args to recall
            args += "\"" + arguments[i] + "\"";
            if ( i < (arguments.length - 1) ) args += ",";
        }
        args += ")";
        setTimeout(args, AT_MOD_POLL);              // Then set the recall
        return;
    }
    var FS, FN, NA;     // Function name, # args, args list- repeat as needed
    var NewArgs = new Array();

    ATSems[parseInt(arguments[0])].ATModified = 0;  // Reset the modified flag
    ATSems[parseInt(arguments[0])]=null;            // Take obj out of wait list
    for( i = 1; i < arguments.length; ++i ) {       // Loop thru all the args
        NA = 0;                                     // Assume 0 args
        FS = arguments[i];                          // Function name string
        FN = this[FS];                              // Get function reference
        if(!FN)FN=eval(FS);
        if ( !FN ) alert("Function "+FS+" not found!");// Toast if not found
        i++;
        if ( i < arguments.length ) {               // If there are any args
            NA = parseInt(arguments[i]);            // Find out how many
            i++;
        }

        switch( NA ) {                              // Call the function
            case 0: FN();
                    break;
            case 1: FN(arguments[i]);
                    break;
            case 2: FN(arguments[i], arguments[i+1]);
                    break;
            case 3: FN(arguments[i], arguments[i+1], arguments[i+2]);
                    break;
            case 4: FN(arguments[i], arguments[i+1], arguments[i+2], arguments[i+3]);
                    break;
            case 5: FN(arguments[i], arguments[i+1], arguments[i+2], arguments[i+3], arguments[i+4]);
                    break;
            default:    alert("Add a case in CallOnLoaded!  Too many args!");
        }
        if ( NA > 0 ) i += (NA - 1);                // Move to the next function
    }
}
/////////////////////////////////////////////////////////////////////////////// Support
var ATBrowser=new ATBrowserObject();        // Object containing browser attrs

//********** Use to dump an array/object to pop-up (if possible) or alert.
// Noshow just returns string, level is optional start depth,showfunctions is optional.
// ONLY CATCHES FIRST LEVEL RECURSION, default stops at 10 levels. (most browser objs exceed,see below for suggestion)
// ATDebugObj(document.body,0,0,null,null,1);
function ATDebugObj(obj,noshow,showfunctions,level,parentObj,maxDepth){
    var out="",pad="",j,item,value,win,doc;
    if(!level) level=0;
    if(!maxDepth) maxDepth=10;
    for(j=0;j<level+1;j++) pad += "    ";
    if(typeof(obj) == 'object') {
        for(item in obj){
            try{
                value = obj[item];
                if(value&&typeof(value)=='object'){
                    if((typeof(value.length)==='number')&&!(value.propertyIsEnumerable('length')))
                        out+=pad+"'"+item+"'[] (array)\n";
                    else
                        out+=pad+"'"+item+"'{} (object)\n";
                    if((value!=obj)&&(value!=parentObj)&&(level<maxDepth))// Avoid recursing self
                        out+=ATDebugObj(value,noshow,showfunctions,level+1,obj,maxDepth);
                    else{
                        (value==obj)? out+=pad+"<"+value+" points to self>\n":
                            out+=pad+"    <"+item+" points to parent object>\n";
                    }
                } else if(value&&typeof(value)=='function'){
                    if(showfunctions) out+=pad+"'"+item+"' => \""+value+"\" (function)\n";
                } else {
                    if(value.replace) value=value.replace(/</g,"&lt;");
                    out+=pad+"'"+item+"' => \""+value+"\" ("+typeof(value)+")\n";
                }
            }
            catch(err){
                out+=pad+"'"+item+"' (NOT ALLOWED ACCESS OR NULL REF)\n";
                continue;
            }
        }
    } else {
        if(obj){
            if(obj.replace) obj=obj.replace(/</g,"&lt;");
            out="===>"+obj+"<=== ("+typeof(obj)+")";
        } else {
            out="===> NULL VALUE <===";
        }
    }
    if ( !noshow ) {
        try {
            win=window.open("",null,"width=800,height=600,scrollbars=yes,resizable=yes,status=no,"+
                "location=no,menubar=no,toolbar=no");
            if (!win)
                alert("(popups blocked):\n"+out);
            else {
                doc=win.document;
                doc.write("<html><head><title>Object Debug</title></head><body><pre>"+out+
                    "</pre></body></html>");
                doc.close();
            }
        }
        catch(err){;}
    }
    return out;
}
function ATTrim(str){
    return str.replace(/^\s*/,'').replace(/\s*$/,'');
}
function ATMakeProper(str){
    var st=str+"";//Due to form fields
    return st.toLowerCase().replace(/^(.)|\s(.)/g,function($1){return $1.toUpperCase();});
}
function ATMakeSafe(str){// This needs to fit into a better strategy, integrated w/backend processing
    return str.replace(/[^\x09\x0A\x20-\x7F]/g,'').replace(/"/g,"&#034;").
        replace(/'/g,"&#039;").replace(/</g,"&lt;");
}
function ATCheckField(fld,nm,len,mode){
    if(!(fld.value=ATTrim(fld.value)).length && len){
        alert("Please fill out the "+nm+" field.");
        return false;
    }
    if(fld.value.length<len){
        alert(nm+" must have at least "+len+" characters.");
        return false;
    }
    if(len) return ATMaskCheck(nm,fld.value,mode);
    return true;
}
// Modes: AT_IGNORE=0,AT_ALPHANUM=1,AT_ALPHA=2,AT_NUMBER=3,AT_PHONE=4,AT_EMAIL=5,AT_URL=6,AT_CREDITCARD=7,AT_TITLE=8,AT_ADDRESS=9
function ATMaskCheck(name,value,mode){
    switch(mode){
        case 1:if(value.match(/[^a-zA-Z0-9\-\.\+ ]/)){alert(name+" may contain letters and numbers only.");return false;}break;
        case 2:if(value.match(/[^a-zA-Z ]/)){alert(name+" may contain letters only.");return false;}break;
        case 3:if(value.match(/[^0-9\.\-\+]/)){alert(name+" may contain numbers only.");return false;}break;
        case 4:if(value.match(/[^0-9\.\-\+\ \(\)]/)){alert(name+" does not appear to be a valid phone number.");return false;}break;
        case 5:if(!value.match(/^[A-Z0-9\._\%\-\&\']+@[A-Z0-9.-\_]+\.[A-Z]{2,10}$/i)){alert(name+" does not appear to be a valid email address.");return false;}break;
        case 6:if(!value.match(/^[A-Z0-9\._\%\-\?\&\=\:\,]/i)){alert(name+" does not appear to be a valid URL.");return false;}break;
        // Note: there are regexs to find TYPE of card... for example: http://www.evolt.org/article/Validating_a_Credit_Card_Number_with_JavaScript/17/24700/index.html
        case 7:if(!value.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/)){alert(name+" does not appear to be a valid credit card.");return false;}
            var sum=0,alt=false,i,thedigit;// Luhn algo
            for(i=value.length-1;i>=0;--i){
                thedigit=parseInt(value.charAt(i));
                if (alt) {
                    thedigit*=2;
                    if(thedigit>9){
                        thedigit-=9;
                    }
                }
                sum=(sum+thedigit)%10;
                alt=!alt;
            }
            if(sum==0){alert(name+" does not appear to be a valid credit card.");return false;}
            break;
        case 8:if(value.match(/[^a-zA-Z0-9\-\.\+\'\,\&\? ]/)){alert(name+" contains an invalid character.");return false;}break;
        case 9:if(value.match(/[^a-zA-Z0-9\-\.\+\#\&\, ]/)){alert(name+" contains an invalid character for an address.");return false;}break;
    }
    return true;
}
function ATCheckMatch(fld1,fld2,nm,len,mode) {
    if(!(fld1.value=ATTrim(fld1.value)).length){
        alert("Please fill out the "+nm+" fields.");
        return false;
    }
    if(fld1.value.length<len){
        alert(nm+" fields should have at least "+len+" characters.");
        return false;
    }
    fld2.value=ATTrim(fld2.value);
    if(fld1.value!=fld2.value){
        alert(nm+" fields do not match.  Please re-enter them.");
        return false;
    }
    return ATMaskCheck(nm,fld1.value,mode);
}
function ATCheckDropDown(fld,nm,unsetVal) {
    if(fld[fld.selectedIndex].value==unsetVal){
        alert("Please make a selection for "+nm);
        return false;
    }
    return true;
}
function ATBrowserObject() {
    if(navigator.appName.indexOf("Internet Explorer")>=0)
        this.IsIE=true;
    else if(navigator.appName.indexOf("Netscape")>=0){
        this.IsNS=true;
        if(navigator.userAgent.indexOf("Safari")>=0)
            this.IsSF=true;
        else if(navigator.userAgent.indexOf("Firefox")>=0)
            this.IsFF=true;
        else if(navigator.appCodeName.indexOf("Mozilla")>=0)
            this.IsMZ=true;
    }
    else if(navigator.userAgent.indexOf("Firefox")>=0)
        this.IsFF=true;
    else if(navigator.userAgent.indexOf("Safari")>=0)
        this.IsSF=true;
    else if(navigator.userAgent.indexOf("Mozilla")>=0)
        this.IsMZ=true;
    else{
        if(document.styleSheets[0].cssRules)
            this.IsNS=true;
        else {
            this.IsIE=true;
            this.IsNewerIE=true;;
        }
    }
    if (navigator.appVersion.indexOf("Mac")>=0)this.IsMac=true;
    // CSS rules array naming test
    if(this.IsMZ||this.IsNS)this.CSSRules = "cssRules";
    else this.CSSRules = "rules";
}
//auto,crosshair,default,pointer,hand(IE ONLY!),wait,text,help,move,
//n-resize,ne-resize,nw-resize,e-resize,w-resize,s-resize,se-resize,sw-resize
function ATSetCursor(obj,type){
    (ATBrowser.IsIE)?document.body.style.cursor=type:obj.style.cursor=type;
}
function ATGetRandom(range) {
    return Math.floor(Math.random()*(range+1));
}
function ATGetWindowWidth(){
   if(self.innerWidth)
       return self.innerWidth;
   else if (document.documentElement&&document.documentElement.clientWidth)
       return document.documentElement.clientWidth;
   else
       return document.body.clientWidth;
}
function ATGetWindowHeight() {
   if(self.innerHeight)
       return self.innerHeight;
   else if(document.documentElement&&document.documentElement.clientWidth)
       return document.documentElement.clientHeight;
   else
       return document.body.clientHeight;
}
function ATCreateCookie(name,value,minutes,domain) {
    var expires,str;
    if(minutes){
        var date=new Date();
        date.setTime(date.getTime()+(minutes*60*1000));
        expires="; expires="+date.toGMTString();
    }
    else expires="";
    str = name+"="+value+expires+"; path=/";
    if (domain) str += "; domain="+domain;
    document.cookie=str;
}
function ATGetCookie(name) {
    name+="=";
    var ca=document.cookie.split(';');
    for(var i=0;i<ca.length;i++){
        while(ca[i].charAt(0)==' ')ca[i]=ca[i].substring(1);
        if(ca[i].indexOf(name)==0)return ca[i].substring(name.length);
    }
    return null;
}
function ATDeleteCookie(name,domain){
    ATCreateCookie(name,"",-60,domain);
}
// Returns data obj from form obj- all els must have 'name' & 'value' attr (except checkbox which needs 'checked')
// SELECT-MULTIPLE NOT YET SUPPORTED
function ATObjFromForm(Form,trimIt,makeSafe){

    var i,o=new Object(),fi,z;
    fi=Form.elements;
    for(i=0;i<fi.length;i++){
//alert(fi[i].name+":"+fi[i].type+":"+fi[i].value);
        if((fi[i].type=="text")||(fi[i].type =="textarea")||(fi[i].type=="button")||(fi[i].type=="file")||(fi[i].type=="hidden")||(fi[i].type=="password")){
            o[fi[i].name]=fi[i].value;
            if(trimIt) o[fi[i].name]=o[fi[i].name].replace(/^\s*/,'').replace(/\s*$/,'');
            if(makeSafe) o[fi[i].name]=ATMakeSafe(o[fi[i].name]);
        }
        else if(fi[i].type=="checkbox"){
            o[fi[i].name]=fi[i].checked;
        }
        else if((fi[i].type=="select-one")){
            o[fi[i].name]=fi[i].options[fi[i].selectedIndex].value;
            if(trimIt) o[fi[i].name]=o[fi[i].name].replace(/^\s*/,'').replace(/\s*$/,'');
            if(makeSafe) o[fi[i].name]=ATMakeSafe(o[fi[i].name]);
        }
    }

/* The old method caused IE7 to crash, the POS

    var i,o=new Object(),z;
    for(i in Form){
        if(Form[i]&&Form[i].name&&Form[i].tagName&&Form[i].type){

            if((Form[i].tagName=="INPUT")||(Form[i].tagName=="TEXTAREA")||(Form[i].tagName=="SELECT")){
                if (Form[i].type=="checkbox"){
                    o[i]=Form[i].checked;
                } else if(Form[i].type=="radio") {
                    if(Form[i].checked) {
                        o[i]=Form[i].value;
                        if(trimIt) o[i]=o[i].replace(/^\s*//*,'').replace(/\s*$/,'');
                        if(makeSafe) o[i]=ATMakeSafe(o[i]);
                    }
                } else {
                    o[i]=Form[i].value;
                    if(trimIt) o[i]=o[i].replace(/^\s*//*,'').replace(/\s*$/,'');
                    if(makeSafe) o[i]=ATMakeSafe(o[i]);
                }
            }
        }
        else if(Form[i]&&Form[i].length&&Form[i][0]&&Form[i][0].name&&Form[i][0].tagName&&(Form[i][0].tagName=="INPUT")&&(Form[i][0].type=="radio")){// Radio objects are wierd
            for(z=0;z<Form[i].length;z++){
                if(Form[i][z].checked) {
                    o[i]=Form[i][z].value;
                    if(trimIt) o[i]=o[i].replace(/^\s*//*,'').replace(/\s*$/,'');
                    if(makeSafe) o[i]=ATMakeSafe(o[i]);
                    break;
                }
            }
        }
    }
*/

    return o;

}
// Populates form obj from data obj- all els must have 'name' & 'value' attr
// !! UNTESTED !!
function ATFormFromObj(obj,Form){
    var i,o;
    for(i in obj){
        if(Form[i]&&Form[i].name&&Form[i].tagName){
            if((Form[i].tagName=="INPUT")||(Form[i].tagName=="TEXTAREA")){
                if(Form[i].type=="checkbox")
                    Form[i].checked=obj[i];
                else if(Form[i].type=="radio")
                    Form[i].checked=true;
                else
                    Form[i].value=obj[i];
            }
            else if (Form[i].tagName=="SELECT"){
                for(o=0;o<Form[i].options.length;o++){
                    if(Form[i].options[o].value==obj[i])
                        Form[i].options[o].selected=true;
                }
            }
        }
    }
}
/////////////////////////////////////////////////////////////////////////////// ATExists
function ATExists(array,value){
    var i,l=array.length;
    for(i=0;i<l;i++){
        if(array[i]==value)return true;
    }
    return false;
}

/////////////////////////////////////////////////////////////////////////////// JSON
//********** The following are JSON stringifiers directly from json.org
if (!Object.prototype.toJSONString) {
Array.prototype.toJSONString = function () {
var a = ['['], b, i, l = this.length, v;

function p(s) {
    if (b) {
        a.push(',');
    }
    a.push(s);
    b = true;
}

for (i = 0; i < l; i += 1) {
    v = this[i];
    switch (typeof v) {
    case 'undefined':
    case 'function':
    case 'unknown':
        break;
    case 'object':
        if (v) {
            if (typeof v.toJSONString === 'function') {
                p(v.toJSONString());
            }
        } else {
            p("null");
        }
        break;
    default:
        p(v.toJSONString());
    }
}
a.push(']');
return a.join('');
};

Boolean.prototype.toJSONString = function () {
return String(this);
};

Date.prototype.toJSONString = function () {

function f(n) {
    return n < 10 ? '0' + n : n;
}

return '"' + this.getFullYear() + '-' +
        f(this.getMonth() + 1) + '-' +
        f(this.getDate()) + 'T' +
        f(this.getHours()) + ':' +
        f(this.getMinutes()) + ':' +
        f(this.getSeconds()) + '"';
};

Number.prototype.toJSONString = function () {
return isFinite(this) ? String(this) : "null";
};

Object.prototype.toJSONString = function () {
var a = ['{'], b, i, v;

function p(s) {
    if (b) {
        a.push(',');
    }
    a.push(i.toJSONString(), ':', s);
    b = true;
}

for (i in this) {
    if (this.hasOwnProperty(i)) {
        v = this[i];
        switch (typeof v) {
        case 'undefined':
        case 'function':
        case 'unknown':
            break;
        case 'object':
            if (v) {
                if (typeof v.toJSONString === 'function') {
                    p(v.toJSONString());
                }
            } else {
                p("null");
            }
            break;
        default:
            p(v.toJSONString());
        }
    }
}
a.push('}');
return a.join('');
};

(function (s) {
var m = {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '"' : '\\"',
    '\\': '\\\\'
};

s.parseJSON = function (hook) {
    try {
        if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.
                test(this)) {
            var j = eval('(' + this + ')');
            if (typeof hook === 'function') {
                function walk(v) {
                    if (v && typeof v === 'object') {
                        for (var i in v) {
                            if (v.hasOwnProperty(i)) {
                                v[i] = walk(v[i]);
                            }
                        }
                    }
                    return hook(v);
                }
                return walk(j);
            }
            return j;
        }
    } catch (e) {
    }
    throw new SyntaxError("parseJSON");
};

s.toJSONString = function () {
    if (/["\\\x00-\x1f]/.test(this)) {
        return '"' + this.replace(/([\x00-\x1f\\"])/g, function(a, b) {
            var c = m[b];
            if (c) {
                return c;
            }
            c = b.charCodeAt();
            return '\\u00' +
                Math.floor(c / 16).toString(16) +
                (c % 16).toString(16);
        }) + '"';
    }
    return '"' + this + '"';
};
})(String.prototype);
}
//////////////////////////////////////////////////////////////////////////////// ATImgBox
// Constructor for a simple box with img corners/sides.  Created within a
// div appended to body. Win is outer frame, body is inner body,title is title.
// Classes must be absolute pos.  All images are optional.
function ATImgBox(Parent,BoxID,BodyID,TitleID,WinClass,BodyClass,TitleClass,ImgClass,ULImgName,URImgName,LLImgName,LRImgName,LImgName,RImgName,TImgName,BImgName,CrnrWidth,TImgHeight,BImgHeight){
    this.CrnrWidth=CrnrWidth;this.TImgHeight=TImgHeight;this.BImgHeight=BImgHeight;this.ImgClass=ImgClass;

    var w=(this.Win = document.createElement("div"));
    w.id=BoxID;w.className=WinClass;
    Parent.appendChild(w);
    this.Body=document.createElement("div");this.Body.id=BodyID;this.Body.className=BodyClass;w.appendChild(this.Body);
    this.Title=document.createElement("div");this.Title.id=TitleID;this.Title.className=TitleClass;w.appendChild(this.Body);
    this.AddImg=function(Div,ID,Src,Height){
        if(!Src)return;
        var Img=document.createElement("img");
        Img.id=ID;Img.src=Src;Img.className=this.ImgClass;Img.style.position="absolute";Img.style.width=this.CrnrWidth;Img.style.height=Height;
        Div.appendChild(Img);
    }
    this.AddImg(w,w.id+"_UL",ULImgName,TImgHeight);this.AddImg(w,w.id+"_UR",URImgName,TImgHeight);
    this.AddImg(w,w.id+"_LR",LRImgName,BImgHeight);this.AddImg(w,w.id+"_LL",LLImgName,BImgHeight);
    this.LD=document.createElement("div");this.LD.className=ImgClass;this.LD.style.backgroundImage="url('"+LImgName+"')";w.appendChild(this.LD);
    this.RD=document.createElement("div");this.RD.className=ImgClass;this.RD.style.backgroundImage="url('"+RImgName+"')";w.appendChild(this.RD);
    this.TD=document.createElement("div");this.TD.className=ImgClass;this.TD.style.backgroundImage="url('"+TImgName+"')";w.appendChild(this.TD);
    this.BD=document.createElement("div");this.BD.className=ImgClass;this.BD.style.backgroundImage="url('"+BImgName+"')";w.appendChild(this.BD);

    this.X=this.Y=-100;
    this.Width=this.Height=50;

    this.Resize=function(width,height){
        this.Width=width;this.Height=height;this.Draw();
    }
    this.Position=function(x,y) {
        this.X=x;this.Y=y;this.Draw();
    }
    this.Hide=function() {
        var w=this.Win.style;w.left=this.Width*-2;w.top=this.Height*-2;
    }
    this.Show=function() {
        var w=this.Win.style;w.left=this.X;w.top=this.Y;
    }
    this.Draw=function(){
        var ws=this.Win.style,img,ht,cw,cw2;
        ws.top=this.Y;ws.left=this.X;ws.height=this.Height;ws.width=this.Width;
        var bs=this.Body.style,ht=this.Height-(this.TImgHeight+this.BImgHeight);if(ht<1)ht=1;
        cw=this.Width-this.CrnrWidth;
        if(cw<1)cw=1;
        cw2=this.Width-(this.CrnrWidth*2);
        if(cw2<1)cw2=1;
        bs.top=this.TImgHeight;bs.left=this.CrnrWidth;bs.height=ht;bs.width=cw2;
        var ts=this.Title.style;
        ts.top=0;ts.left=this.CrnrWidth;ts.height=this.TImgHeight;ts.width=cw2;
        if((img=document.getElementById(this.Win.id+"_UL"))){img.style.top=0;img.style.left=0;}
        if((img=document.getElementById(this.Win.id+"_UR"))){img.style.top=0;img.style.left=cw;}
        if((img=document.getElementById(this.Win.id+"_LR"))){img.style.top=this.Height-this.BImgHeight;img.style.left=cw;}
        if((img=document.getElementById(this.Win.id+"_LL"))){img.style.top=this.Height-this.BImgHeight;img.style.left=0;}
        if((img=this.LD.style)){img.top=this.TImgHeight;img.left=0;ht=this.Height-(this.TImgHeight+this.BImgHeight);if(ht<1)ht=1;img.height=ht;img.width=this.CrnrWidth;}
        if((img=this.RD.style)){img.top=this.TImgHeight;img.left=cw;ht=this.Height-(this.TImgHeight+this.BImgHeight);if(ht<1)ht=1;img.height=ht;img.width=this.CrnrWidth;}
        if((img=this.TD.style)){img.top=0;img.left=this.CrnrWidth;ht=this.TImgHeight;if(ht<1)ht=1;img.height=ht;img.width=cw2;}
        if((img=this.BD.style)){img.top=this.Height-this.BImgHeight;img.left=this.CrnrWidth;ht=this.BImgHeight;if(ht<1)ht=1;img.height=ht;img.width=cw2;}
    }
}

/////////////////////////////////////////////////////////////////////////////// Templates
//********** Very simple HTML client-side templates. Pass the template file to
// load, CB to notify once loaded with CBArg.
function ATTTemplate(filename,CB,CBArg) {
    this.filename=filename;this.CB=CB;this.CBArg=CBArg;this.Tpl="";
    this.Blocks = new Array();
    this.ParseTpl=ATTParseTpl;
    this.ParseBlock=ATTParseBlock;
    this.SetVar=ATTSetVar;
    this.SetBlock=ATTSetBlock;
    this.SetVarQ=ATTSetVarQ;
    this.SetSelect=ATTSetSelect;
    this.SetVarsFromObj=ATTSetVarsFromObj;
    // Find a temp storage spot & take it
    var ctr=0;
    while(ATTplTemp[ctr]) ctr++;
    ATTplTemp[ctr] = this;
    // Fetch the template
    ATXGet(this.filename,"ATTSTplLoad",1,60000,ctr);
}
// Handles template load callback
function ATTSTplLoad(Obj,Callback,CB) {
    var success=1;

    // Store the template if okay
    if (!Obj.ATErrorCode && Obj.ReqObj.status==200)
        ATTplTemp[CB].Tpl=Obj.ReqObj.responseText;
    // Else note error
    else {
        success=0;
        ATTplTemp[CB].Tpl="ERROR: Failed loading template: "+ATTplTemp[CB].filename;
    }
    // Parse it
    ATTplTemp[CB].ParseTpl();

    // Make callback
    if ( ATTplTemp[CB].CB ) {
        var FN = this[ATTplTemp[CB].CB];
        if(!FN)FN=eval(ATTplTemp[CB].CB);
        FN(ATTplTemp[CB].CBArg,success);
    }

    // Free everything
    ATFreeXGet(Obj);
    ATTplTemp[CB]=null;
}
// Parses the template into blocks (odd regex due to safari issue)
function ATTParseTpl() {
    // Block start marker
    var BStart=/<!--\s*BLOCK\s+/;
    // Block data
    var BName=/^(\w+)\s*-->([\w|\W]*)/;
    this.Lines=this.Tpl.split(BStart);

    // For all valid blocks create objects
    var cnt=this.Lines.length,exp;
    for (var i=0;i<cnt;i++){
        if ( BName.test(this.Lines[i]) ) {
            exp=RegExp.$1;
            this.Blocks[exp]=new ATTBlock();
            this.Blocks[exp].Raw=RegExp.$2;
        }
    }

    this.lastBlock=this.Blocks[exp];
}
// Parses a block & returns the string,blockName optional (uses currblock)
function ATTParseBlock(blockName) {
    var block,ret="",rep;
    (blockName) ? block=this.Blocks[blockName]:block=this.lastBlock;
    if ( block ) {
        ret=block.Raw;
        for(rep in block.VarREs) {
            if(block.VarREs[rep])
                ret=ret.replace(block.VarREs[rep],block.VarValues[rep]);
        }
    }
    return ret;
}
// Sets the current block (for rep calls)
function ATTSetBlock(blockName) {
    this.lastBlock=this.Blocks[blockName];
}
// Sets a variable within a block,passing block (sets currblock)
function ATTSetVar(blockName,varName,varValue) {
    var block=this.Blocks[blockName];
    this.lastBlock=block;
    if ( block ) {
        if(!block.VarREs[varName])
            block.VarREs[varName]=new RegExp("<@"+varName+"@>","g");
        block.VarValues[varName]=varValue;
    }
}
// Sets a variable within a block,using last block accessed
function ATTSetVarQ(varName,varValue){
    var block=this.lastBlock;
    if ( block ) {
        if(!block.VarREs[varName])
            block.VarREs[varName]=new RegExp("<@"+varName+"@>","g");
        block.VarValues[varName]=varValue;
    }
}
// Sets variables within a block based on a common prefix- commonly used for SELECT types (sets currblock)
// All vars w/prefix selPrefix are cleared, vars matching selFull is set to varValue.  selPrefix must be unique in block.
function ATTSetSelect(blockName,selPrefix,selFull,varValue) {
    var block=this.Blocks[blockName];
    this.lastBlock=block;
    if ( block ) {
        block.VarREs[selPrefix]=new RegExp("<@"+selPrefix+"(?!"+selFull.substr(selPrefix.length)+")[^@]*@>","g");
        block.VarValues[selPrefix]="";
        block.VarREs[selFull]=new RegExp("<@"+selFull+"@>","g");
        block.VarValues[selFull]=varValue;
    }
}
// Sets a variable within a block using all members of an object to do so (not-recursive)
function ATTSetVarsFromObj(blockName,varObj) {
    var block=this.Blocks[blockName],i;
    this.lastBlock=block;
    if ( block ) {
        for(i in varObj) {
            if(!block.VarREs[i])
                block.VarREs[i]=new RegExp("<@"+i+"@>","g");
            block.VarValues[i]=varObj[i];
        }
    }
}
// Block object constructor
function ATTBlock(){
    this.VarValues=new Array();this.VarREs=new Array();
    this.Raw="";
}
//********** Find a stylesheet rule object text, sequential search (NOT fast)   ATGetCSSRuleText
// We return the text rather than the object because browsers do not yet support
// direct access to attrs, darnit! It is part of W3C spec, though.  Text is
// returned lower case. 7/25/03
// THIS IS DEPRECRATED
function ATGetCSSRuleText(rule) {
    var BrowserSwitch,NumRules;
        rule = rule.toLowerCase();

    // Different browsers name the CSS rules array differently
    ( ATBrowser.CSSRules == "rules" ) ? BrowserSwitch = 1: BrowserSwitch = 0;

    // Loop thru all the stylesheets for this page
    var NumSheets = document.styleSheets.length;
    for ( SheetsIdx = 0; SheetsIdx < NumSheets; SheetsIdx++ ) {

        // Loop thru all the rules within each stylesheet
        ( BrowserSwitch ) ?
            NumRules = document.styleSheets[SheetsIdx].rules.length:
            NumRules = document.styleSheets[SheetsIdx].cssRules.length;

        for ( RulesIdx = 0; RulesIdx < NumRules; RulesIdx++ ) {
            // Return the rule object if the name matches
            if ( BrowserSwitch ) {
                if ( rule ==
                    document.styleSheets[SheetsIdx].rules[RulesIdx].
                        selectorText.toLowerCase()){
                    return document.styleSheets[SheetsIdx].rules[RulesIdx].
                        style.cssText.toLowerCase();
                }
            }
            else {
                if ( rule == document.styleSheets[SheetsIdx].cssRules[RulesIdx].
                        selectorText.toLowerCase()){
                    return document.styleSheets[SheetsIdx].cssRules[RulesIdx].
                        style.cssText.toLowerCase();
                }
            }
        }
    }
    return null;
}
//********** Return a value from a list of NV pairs based on name (SLOW)        ATGetNVPValue
// THIS IS DEPRECRATED
function ATGetNVPValue(list, name) {
    var Pair;
    for ( i = 0; i < list.length; ++i ) {
        Pair = list[i].split(":");
        Pair[0] = Pair[0].replace(/^\s*/g,"").replace(/\s*$/g,"");
        if ( Pair[0] == name )
            return (Pair[1] = Pair[1].replace(/^\s*/g,"").replace(/\s*$/g,""));
    }
    return null;
}
//********** Track mouse when passed events                                     ATTrackMouse
function ATTrackMouse(event) {
    var XOffset, YOffset;

    if ( ATBrowser.IsIE ) {
        ATMouseX = window.event.offsetX;
        ATMouseY = window.event.offsetY;
        ATMousePageX = window.event.x + document.body.scrollLeft;
        ATMousePageY = window.event.y + document.body.scrollTop;
    }
    else if ( ATBrowser.IsNS ) {
        var Offsets = ATGetMouseEventOffsets(event);
        ATMouseX = Offsets.X;
        ATMouseY = Offsets.Y;
        ATMousePageX = event.pageX;
        ATMousePageY = event.pageY;
    }
    else
        alert("ATTrackMouse: Unable to determine mouse location.");

    // Adjust for atlast Iframes, if applicable
    if ( self.ATTopOffset ) {
        ATMousePageX+=self.ATLeftOffset;
        ATMousePageY+=self.ATTopOffset;
    }
}
//********** Get mouse event offsets for an element                             ATGetMouseEventOffsets
function ATGetMouseEventOffsets(event) {
    var Elmnt, ElmntCoords, EventCoords;

    if ( event.offsetX )            // IE style
        return { X: event.offsetX, Y: event.offsetY };
    else if ( event.target ) {      // NS style
        if ( window.opera ) {       // Opera special case
            Elmnt = event.target;
        }
        else { // Must differentiate between element node & non-element node
            Elmnt = ( event.target.nodeType == 1 ) ?
                event.target : event.target.parentNode;
        }
        EventCoords = { X: event.clientX + window.pageXOffset,
                        Y: event.clientY + window.pageYOffset };
        ElmntCoords = ATGetPageCoords(Elmnt);
        return { X: EventCoords.X - ElmntCoords.X,
                 Y: EventCoords.Y - ElmntCoords.Y };
    }
    alert("ATGetMouseEventOffsets: Unable to determine mouse location.");
    return null;
}
//********** Get page coords for an element                                     ATGetPageCoords
function ATGetPageCoords(Elmnt) {
  var Coords = {X: 0, Y: 0};
  while ( Elmnt ) {
    Coords.X += Elmnt.offsetLeft;
    Coords.Y += Elmnt.offsetTop;
    Elmnt = Elmnt.offsetParent;
  }
  return Coords;
}

