Ajaxify={
    Request: {
        count:0,  // Contains total count of Ajax requests, which are in progress.
        /**
         * Sends Ajax request. After getting a response, function fnAfter is called (if any),
         * then update of blocks is performed.
         * @param string sUrl  - url to be visited.
         * @param function fnAfter - callback, see above
         * @param mixed    oParams - can be string or js object - those params will be included as POST data
         */
        send:function(sUrl,fnAfter,oParams) {
            if(typeof(oParams)=='string') {
                oParams=oParams.toQueryParams();
            }
            if(!oParams) {
                oParams={ajaxify:true, sanitizeJSON:true};
            }
            else {
                oParams.ajaxify=true;
                oParams.sanitizeJSON=true;
            }
            Ajaxify.Event.fire('ajax_request_prepared');
            Ajaxify.Request.count++;
            var request=new Ajax.Request(sUrl, {onSuccess: function(response){
                    var oData=response.responseText.evalJSON()
                    if(fnAfter)
                        fnAfter(oData);
                    if(oData.blocks) {
                        for(var i=0,j=oData.blocks.length;i<j;i++) {
                            Ajaxify.Update.doUpdate(oData.blocks[i].code,oData.blocks[i].content);
                        }
                    }
                    Ajaxify.addHandlers();
                    Ajaxify.Event.fire('ajax_request_complete',response);
                    Ajaxify.Request.count--;
            }, parameters: oParams});
        }
    },
    Update: {
        hDest:new Hash(),
        /**
         * Combines target elements with block identifiers
         * @sCode string block identifier
         * @oDest mixed  target object, can be object itself or CSS selector (first occurence will be used, so call like $$(selector)[0]
         * @oParam object additional params, mode and parse (callback function, that will be launched before update on content)
         */
        add:function (sCode,oDest,oParam) {
            if(!oParam)
                oParam={};
            var oData={'target':oDest,'params':oParam};
            this.hDest.set(sCode,oData);
        },
        /**
         * There is no reason to call this function manually ;)
         */
        doUpdate:function(sCode,oData) {
            
            var oBlock=this.hDest.get(sCode);
            
            if(oBlock) {
                Ajaxify.Event.fire('before_'+sCode+'_update',oBlock.target);
                if(typeof(oBlock.target)=='string')
                    oTarget=$$(oBlock.target)[0];
                else
                    oTarget=oBlock.target;
                if(oBlock.params.parse) {
                    oData=oBlock.params.parse(oData);
                }
                if(!oBlock.params.mode) {
                    oTarget.replace(oData);
                }
                else {
                    switch(oBlock.params.mode) {
                        case 'top':oTarget.insert({top:oData});break;
                        case 'bottom':oTarget.insert({bottom:oData});break;
                        case 'after':oTarget.insert({after:oData});break;
                        case 'before':oTarget.insert({before:oData});break;
                        case 'update':oTarget.update(oData);break;
                        default:var fnCallback=oBlock.params.mode;
                                oTarget.fnCallback(oData);
                    }

                }
            }

            Ajaxify.Event.fire('after_'+sCode+'_update',oBlock.target);
        }
    },
    Event: {
        enabled:true, //if false, events will be not processed;
        enable:function() {
          this.enabled=true;
        },
        disable:function() {
            this.enabled=false;
        },
        init:function() {
            this.registred=new Hash();
        },
        register: function (sName,fnCallback) {  // add event_name and callback for it
            if(!this.registred)
                this.init();
            if(!this.registred.get(sName))
                this.registred.set(sName,new Array());
            this.registred.get(sName)[this.registred.get(sName).length]=fnCallback;
        },
        fire: function (sName,oParams) {  // trigger event_name with params
            if(!this.enabled)
                return;
            if(this.registred.get(sName)) {
                for(var i=0,j=this.registred.get(sName).length;i<j;i++) {
                    this.registred.get(sName)[i](oParams);
                }
            }
        }
    },
    hHandlers:new Hash,
    handle: function (sSelector,fnCallback) {
        this.hHandlers.set(sSelector,fnCallback);
    },
    /**
     * Default, generic handlers
     */
    Handler: {
        /**
         * Handler for elements with onclick="setLocation("some url...")
         */
      location:function(oRef) {
          var sOnclick=oRef.getAttribute('onclick');
          if(!sOnclick)
              return;
          var sHref=sOnclick.substr(13, sOnclick.length-15);
          oRef.onclick=function() {
              Ajaxify.Request.send(sHref);
              return false;
          }
      },
      /**
       * Handler for a links, with support for default onclick events (but that is not tested well)
       */
      link:function(oRef) {
           var fnCurrent=oRef.onclick;
           oRef.onclick=function() {
               if(fnCurrent)
                   var answer=fnCurrent();
               if(answer||answer==undefined) {
                    Ajaxify.Request.send(oRef.href);
               }
               return false;
           }
      }
    },
    __init:function() {
        this.Event.init();
 
        Element.observe(window,'load',function(){
            Ajaxify.addHandlers(true);
            });
    },
    /**
     * Process handlers. By default it skips previously ajaxified elements, unless you set bForce to true.
     */
    addHandlers:function(bForce) {
        if(!bForce)
            bForce=false;
        this.hHandlers.each(function(item){
               var aElements=$$(item.key);
               for(var i=0,j=aElements.length;i<j;i++) {
                   if(aElements[i].hasAttribute('ajaxified')&&!bForce)
                       continue;
                   item.value(aElements[i]);
                   aElements[i].setAttribute('ajaxified', true);
               }
            });
    }
}
Ajaxify.__init();

