/*
*/
function Yajax() {
    this.valueChangesObj=null;
    this.elementChangesObj=null;
    this.confirmUnload=false; /* Have I already confirmed the unload. */
    this.buttons=new Array();
    this.yajaxForm=new YajaxForm();
    this.serverQ=new ServerQ();
    this.timerID=null;
}

/* intended for server use */
Yajax.prototype.newRules=function(e,obj,x) { 
    if(!x||x.length==0) return;
    if(x[0]=='rules'||x[0]=='qrules') { /* check rules. rules-> submit qrules ->don't submit */
        var rules=false;
        if(x[1]===true) alert('deprecated use of rules=true');
        else if(x[1]===false) rules=false;
        else {
            rules=yajaxRules[x[1]]; 
            if(!rules) alert('id '+e.id+' has no rules '+x[1]);
        }
        this.yajaxForm.addInputEvents(e);
        obj.yjInRules=rules; 
        if(x[0]=='rules') obj.yjSrv=true;
        if(!rules) return;
        if(typeof(rules.chars)=='string') rules.chars=new RegExp(rules.chars);
    }
    else if(x[0]=='link') { 
        this.yajaxForm.addEvent(e,'click','link');
    }
    else if(x[0]=='act') { /* send act to srv via ajax . */
        this.yajaxForm.addEvent(e,x[1],'act');
    }
    else if(x[0]=='submit') { /* submit event to srv */
        this.yajaxForm.addEvent(e,'submit','submit');
    }
    else {
        alert('unknown rules type');
    }
}

Yajax.prototype.setAttrs=function(e,attrs) {
    if(!attrs) return;
    for(attr in attrs) {
        if(attr=='style') {
            var styles=attrs[attr];
            for(style in styles) {
                e.style[style]=styles[style]; 
            }
        }
        else {
            e[attr]=attrs[attr];
        }
    }
}

Yajax.prototype.setObj=function(e,obj,changes) {
    for(change in changes) obj[change]=changes[change];
}

Yajax.prototype.setInnerHTML=function() {
    var tags=yajax.elementChangesObj;
    for(var id in tags) {
        var tag=tags[id];
        if(tag.innerHTML) {
            var e=document.getElementById(id);
            if(!e) {
                alert('not found '+id);
                continue;
            }
            e.innerHTML=tag.innerHTML;
        }
    }
}

Yajax.prototype.setChanges=function(tags) {
    if(!tags) return true;
    for(var id in tags) {
        var e=document.getElementById(id);
        if(!e) {
            alert('not found '+id);
            return false;
        }
        var tag=tags[id];
        var obj=false;
        if(tag.newE) obj=core.addDOMObj(e);
        if(tag.attrs) this.setAttrs(e,tag.attrs); 
        if(!obj&&(tag.rules||tag.obj)) obj=core.getDOMObj(e); 
        if(tag.rules) this.newRules(e,obj,tag.rules);
        if(tag.obj) this.setObj(e,obj,tag.obj);
        if(tag.f) {
            var f=eval(tag.f);
            f(e,obj);
        }
        delete tags[id];
    }
    return true;
}

Yajax.prototype.setValue=function(form,name,value) {
    var cs=core.getNamedControls(form,name);
    for(var i in cs) {
        var e=cs[i];
        if(e.checked===undefined) e.value=value;
        else {
            if(e.value==value) e.checked=false;
            else e.checked=true;
        }
    }
}

Yajax.prototype.setValues=function(changes) {
    if(!changes) return;
    for(var form in changes) {
        var changes_form=changes[form];
        for(var name in changes_form) {
            this.setValue(form,name,changes_form[name]); 
        }
    }
}

/* end of special server stuff */
Yajax.prototype.checkInstance=function() {
    if(!yajax.instance) {
        if(yajax.default_instance) {
            if(!yajax.reloadConfirm||yajax.reloadConfirm()) yajax.instance=yajax.default_instance;
        }
    }
}

Yajax.prototype.timer=function() {
    yajax.mergeInChanges(); 
}

Yajax.prototype.mergeInChanges=function() {
    if(!yajax.setChanges(this.elementChangesObj)) {
        /* if(yajax.timerID===null) yajax.timerID=setInterval(this.timer,1000); */
        return false;
    }
    clearInterval(yajax.timerID);
    yajax.timerID=null;
    yajax.setValues(this.valueChangesObj);
    yajax.elementChangesObj=null;
    yajax.valueChangesObj=null;
    return true;
}

Yajax.prototype.checkDefaultInst=function() {
    if(this.nonStatic&&!this.instance&&this.recoverInstance) {
        var cookie=core.getCookie('s_id');
        cookie=cookie.split('-',6);
        var def=parseInt(cookie[4],16);
        var longev=parseInt(cookie[3],16);
        var create=parseInt(cookie[2],16);
        var expiry=0;
        if(longev>0) expiry=create+longev;
        cookie=cookie[0]+'-'+cookie[1]+'-'+cookie[2]+'-'+cookie[3]+'-0-'+cookie[5];
        if(this.recoverInstance===true||this.recoverInstance()) this.instance=def;
        else {
            core.setCookie('s_id',cookie,expiry,'/');
        }
    }
}

Yajax.prototype.init=function() {
    if(!compatible) return;
    yajax.checkDefaultInst();
    eventHandler.handleEvent(window,'beforeunload').push(yajax.serverQ.onbeforeunload); 
    yajax.setInnerHTML();
    yajax.mergeInChanges();
}


function ServerQ() {
    this.wait=null; /* queue of waiting tasks */
    this.sent=null; /* array of tasks sent and awaiting server response/time-out */
    this.qtime=(new Date()).getTime(); /* last time server accessed.  Used for throttle. */
    this.timeoutId=null; /* TimeOut object for server response time-out */
    this.throttleId=null; /* TimeOut object for ending throttle-based wait */
    this.waiting=false; /* Am I currently in throttle mode, intentionally not sending */
    this.xmlhttp=null; /* the xmlhttprequest object */
}


ServerQ.prototype.pushQ=function(todo) {
    var now=(new Date()).getTime();
    if(this.wait===null) this.wait=new Array();
    var wait=this.wait;
    if(wait.length>0) {
        if(todo[0]=='in'&&wait[wait.length-1][0]=='in'&&todo[1]==wait[wait.length-1][1])
            wait[wait.length-1]=todo;
    }
    else wait[wait.length]=todo;
    if(this.waiting==false&&!this.sent) {
        if(now>this.qtime+500) {
            this.qtime=now;
            this.deQ();
        }
        else {
        /* this creates a bit of a throttle.  If the user is hammering on some kind of event, then they
        are accumulated so that they can be handled more efficiently, even if the server can handle them all.*/
            this.throttleId=setTimeout(this.restartQ,5000);
            this.waiting=true;
        }
    }
    this.qtime=now;
}

ServerQ.prototype.restartQ=function() {
    yajax.serverQ.waiting=false;
    yajax.serverQ.deQ();
}

ServerQ.prototype.deQ=function() {
    if(this.waiting==true) return;
    if(this.sent) return;
    if(!this.wait) return;
    var s_rnd=core.getCookie('s_rnd');
    var str=s_rnd+'\n';
    var wait=this.wait;
    this.sent=wait;
    this.wait=null;
    for(var i=0;i<wait.length;i++) {
        var todo=wait[i];
        for(var j=0;j<todo.length;j++) {
            if(j>0) str+=':';
            if(typeof todo[j]!='Undefined'&&todo[j]!==null) str+=escape(todo[j].toString());
        }
        str+='\n';
    }
    this.xmlhttp=core.XMLHttpRequest();
    this.xmlhttp.onreadystatechange = this.reqReturn; 
    var href=core.urlAdds(location.href,'yajax_req=y');
    if(yajax.instance) href=core.urlAdds(href,'inst='+yajax.instance);
    this.xmlhttp.open('POST',href,true);
    this.xmlhttp.send(str);
    this.timeoutId=setTimeout(this.reqAbort,10000);
}

ServerQ.prototype.reqAbort=function() {
    srv=yajax.serverQ;
    srv.timeOutId=null;
    if(!srv.xmlhttp) { srv.deQ(); return; }
    if(srv.xmlhttp.readyState==4) { srv.deQ(); return; }
    srv.xmlhttp.abort();    
    srv.xmlhttp.onreadystatechange=null;
    if(!srv.sent) { srv.deQ(); return; }
    if(srv.wait) srv.wait=srv.sent+srv.wait;
    else srv.wait=srv.sent;
    srv.sent=null;
    srv.deQ();
}


ServerQ.prototype.reqReturn=function() {
    var srv=yajax.serverQ;
    var xmlhttp=srv.xmlhttp;
    if(xmlhttp.readyState!=4) return;
    xmlhttp.onreadystatechange=returnOne;
    if(srv.timeoutId!==false) clearTimeout(srv.timeoutId);
    if(xmlhttp.sent>=300||xmlhttp.sent<200) { 
        if(srv.wait) srv.wait=srv.sent+srv.wait;
        else srv.wait=srv.sent;
        srv.sent=null;
    }
    else {
        srv.sent=null;
        eval(xmlhttp.responseText);
        yajax.setInnerHTML();
        yajax.mergeInChanges();
    }
    srv.deQ();
}

/* note that this can be called by onclicklink, since beforeunload is nonstandard.
In which case ev and e are set by that event 
*/
ServerQ.prototype.onbeforeunload=function(ev) {
    if(yajax.confirmUnload) return(true);
    var srv=yajax.serverQ;
    if(srv.sent||srv.wait) {
        cnfm=yajax.complainWaiting(); 
        if(!cnfm) {
            eventHandler.preventDefault=true;
            return(false);
        }
    }
    yajax.confirmUnload=true;
    return(true);
}

function YajaxForm() {
}


YajaxForm.prototype.checkValue=function(rules,x) {
    if(!rules) return(true);
    tests=rules.tests;
    if(!tests) return(true);
    for(var i in tests) {
        var test=tests[i];
        if(test[0]=='reg') {
            if(typeof(test[2])=='string') test[2]=new RegExp('^'+test[2]);
            if(!test[2].test(x)) return(test[1]);
        }
        else if(test[0]=='len') {
            if(x.length>test[2]) return(test[1]);
        }
        else if(test[0]=='stp') {
            return(test[1]);
        }
    }
    return true;
}

YajaxForm.prototype.checkElements=function(a) {
    for(var i in a) {
        var id=a[i];
        var obj=core.getDOMObj(id);
        if(obj===null) alert('Element '+id+' does not exist');
        var e=obj.element;
        var rules=obj.yjInRules; 
        var r=this.checkValue(rules,e.value);
        if(r!==true) return(r);
    }
    return(true);
}

YajaxForm.prototype.onkeypress=function(ev) {
    var eObj=eventHandler.eObj;
    var rules=eObj.yjInRules; 
    var c;

    ch='';
    if(typeof ev.charCode=='number') c=ev.charCode;
    else if(ev.keyCode) c=ev.keyCode;
    else return;  // allow non-printable chars
    if(c<32||c>60000) return; 
    ch=String.fromCharCode(c);

    if(rules.chars&&!rules.chars.test(ch)) { 
        eventHandler.preventDefault=true; 
        return; 
    }
    if(rules.len&&this.value.length>=rules.len) {
        eventHandler.preventDefault=true;
    }
}

YajaxForm.prototype.upStr=function(up) {
    var up_str='';
    if(!up) up_str='';
    else if(typeof(up)=='string') up_str=up;
    else {
        for(i=0;i<up.length;i++) {
            if(i>0) up_str+=':';
            up_str+=escape(up[i]);
        }
    }
    return up_str;
}

YajaxForm.prototype.changeIt=function(e,value,ev) {
    var eObj=eventHandler.eObj;
    var rules=eObj.yjInRules;
    var res=this.checkValue(rules,value)
    if(res!==true) {
        yajax.complain(res);
        eObj.valid=false;
    }
    if(!REQcompatible) return;
    if(eObj.yajaxUp===false) return;
    if(!eObj.yjSrv) return;
    var name=e.name;
    if(!name) name=eObj.name;
    if(!name) name=e.id;
    yajax.serverQ.pushQ(new Array('in',ev.type,e.form.id,name,e.value,e.id,this.upStr(eObj.yajaxUp)));
}

YajaxForm.prototype.onchangetext=function(ev) {
    yajax.yajaxForm.changeIt(this,this.value,ev);
}

YajaxForm.prototype.onclickbox=function(ev) {
    var value;
    if(this.checked) value=this.value; else value="";
    yajax.yajaxForm.changeIt(this,value,ev);
}

YajaxForm.prototype.onclicklink=function(ev) {
    var srv=yajax.serverQ;
    if(srv.onbeforeunload(ev)==false) return;
    if(!yajax.instance) location.href=this.href;
    if(this.href.indexOf('&inst=')!=-1) location.href=this.href;
    else if(this.href.indexOf('?inst=')!=-1) location.href=this.href;
    else location.href=core.urlAdds(location.href,'inst='+yajax.instance);
}

YajaxForm.prototype.formsubmit=function(ev) {
    var yf=yajax.yajaxForm;
    var eObj=eventHandler.eObj;
    var up=eObj.yajaxUp;
    if(!REQcompatible) return;
    eventHandler.preventDefault=true;
    if(up===false) return;
    var id=this.id;
    if(eventHandler.submitClick) id=eventHandler.submitClick.id;
    var name=this.name;
    if(name.substring(0,11)=='__yajax_act[') name=false;
    if(!name) name=eObj.name;
    if(!name) name=id;
    yajax.serverQ.pushQ(new Array('act',ev.type,this.id,name,id,yf.upStr(up)));
}

YajaxForm.prototype.procEvent=function(ev) {
    var yf=yajax.yajaxForm;
    var eObj=eventHandler.eObj;
    var up=eObj.yajaxUp;
    if(!REQcompatible) return;
    if(up===false) return;
    if(this.nodeName=='INPUT'&&(this.type=='submit'||this.type=='image')) eventHandler.preventDefault=true;
    var i;
    var id=this.id;
    var name=this.name;
    if(!name) name=eObj.name;
    if(!name) name=id;
    yajax.serverQ.pushQ(new Array('act',ev.type,this.form,name,id,yf.upStr(up)));
}

YajaxForm.prototype.addEvent=function(control,event,f) {
    if(f=='link') f=this.onclicklink;
    else if(f=='act') f=this.procEvent;
    else if(f=='submit') f=this.formsubmit; 
    evh=eventHandler.handleEvent(control,event);
    if(f) evh.push(f);
}

YajaxForm.prototype.addInputEvents=function(control) { 
    eObj=core.getDOMObj(control);
    if(control.nodeName=='SELECT') {
        eventHandler.handleEvent(eObj,'change').push(this.onchangetext);
    }
    else if(control.nodeName=='TEXTAREA'||control.type=='text'||control.type=='password') {
        eventHandler.handleEvent(eObj,'keypress').push(this.onkeypress);
        eventHandler.handleEvent(eObj,'change').push(this.onchangetext);
    }
    else {
        eventHandler.handleEvent(eObj,'click').push(this.onclickbox);
    }
}

yajax=new Yajax();

