/*
Copyright (c) 2009 Jordan Richmeier

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**********************************/
/**********************************/
/**                              **/
/**         Ajax Engine          **/
/**                              **/
/**********************************/
/**********************************/

var ajaxEngine = new Class({
    Implements: [Events, Options],
    options: {
        linksrc: false,
        loadFrom: false, //module will assume the targetElement id is the same as the loadFrom if this option is not set
        loadingImg: false, //if no image is added module will use the word "Loading..."
        defaultIndex: 0,
        evalScripts: false,
        customEvents: false //if true does not pre-set events (for extreme customization)
    },
    running: false,
    orginalState: true,
    initialize: function(targetElement,options){
        var that = this; //referance to this object inside non-bound object functions
        this.setOptions(options);
        this.targetElement = $(targetElement);
        if(!this.options.loadFrom) this.options.loadFrom = targetElement;
        this.initLinks();

        if(Browser.Engine.trident){ //IE history fix
          if(!$defined($('aeHistory'))){
               this.iHistory = new IFrame({
                    'id':'aeHistory',
                    'src': window.location,
                    'styles':{display: 'none'}
               });
               this.iHistory.inject($(document.body),'bottom');
          }else this.iHistory = $('aeHistory');
          var iframe = this.iHistory.contentWindow.document;
          iframe.open(); iframe.close(); //pushes new hash history item
          iframe.location.hash = window.location.hash;
        }
        
        //set filler loading html
        if(!this.options.loadingImg){
          this.loadingHTML =  "<h2>Loading...</h2>";
        }else this.loadingHTML = "<img src='"+this.options.loadingImg+"' />";
        
        //initialize ajax request object
        this.contentReq = new Request.HTML({
          evalScripts: this.options.evalScripts,
          method: 'get',
          onRequest: function(){
               that.running=true;
               that.fireEvent("start");
          },
          onSuccess: function(responseTree,responseElements ){
               responseElements.each(function(item){ 
                    try{
                         if(item.id == that.options.loadFrom){
                              that.fireEvent("complete",item);
                         }
                    }catch(err){}
               });
               that.running=false;
          },
          onFailure: function(xhr) {
               that.running=false;
               that.fireEvent("failure",xhr);
          }
        });
        
        if(!this.options.customEvents){
             //pre-set event functions (can be removed through .removeEvents)
             this.addEvents({
               "start":function(){
                    this.targetElement.set('html',this.loadingHTML);
               },
               "complete":function(responseElement){
                    this.targetElement.set('html',responseElement.get('html'));
               }
             });
             
             this.initHash();
        }else {
          this.addEvent('initialize',function(){
               this.initHash();
          });
        }
        
    },
    initLinks: function(){
        var that = this;
        
        if(!this.options.linksrc) { //no menu id given, defaults to all links on page
          this.linksrc = $$("a");
        }else if($type(this.options.linksrc) == "array"){ //multiple menu id's
          this.linksrc = $(this.options.linksrc[0]).getElements("a"); //creates a base link array from the first id (required in order to properly extend the link array)
          this.options.linksrc.erase(this.options.linksrc[0]);
          this.options.linksrc.each(function(item){
               this.linksrc.extend($(item).getElements("a"));
          }.bind(this));
        }else { //single menu id
          this.linksrc = $(this.options.linksrc).getElements("a");
        }
        
        this.linksrc.each(function(item,index){ //create and store an id for each menu item, also extends any relative href paths to absolute paths
          item.href = item.href;
          item.store('linkID',index);
        });
              
        this.linksrc.addEvent("click",function(event){
           event.preventDefault(); event.stopPropagation();          
           that.updateLocHash(this.retrieve('linkID'));
           that.accessRequest(this.get('href'));
        });
    },
    initHash: function(){
        this.pageHash = window.location.hash;
        if(this.pageHash != ""){ //if this objects hash info exsists load hash info
          this.orginalState = false;
          var pageId = this.getHashCode(this.pageHash);
          if($chk(pageId)){
               this.updateLocHash(pageId.toInt());
               this.accessRequest(this.linksrc[pageId.toInt()].get('href'));
          }
        }

        this.hashHandler = this.hasher.periodical(300,this); //poll the hash information (allows for use of broweser back and forward buttons)
    },
    hasher: function(){
          var curHash = window.location.hash;
          if(Browser.Engine.trident) curHash = this.iHistory.contentWindow.document.location.hash;
          
          if(curHash == "#") curHash = ''; //ie quirk fix
         
          if(this.pageHash != curHash){ //avoids the need to call getHashCode every 300 miliseconds
               if(this.getHashCode(this.pageHash) != this.getHashCode(curHash)){ //triggers only when this objects hash code is changed
                    this.pageHash = curHash;
                    var pageId = this.getHashCode(this.pageHash);
                    if($chk(pageId)){
                         this.accessRequest(this.linksrc[pageId.toInt()].get('href'));
                    }else if(this.orginalState == false){
                         this.orginalState = true;
                         this.accessRequest(window.location.toString());
                    }
               }
               if(Browser.Engine.trident) window.location.hash = curHash;
               this.pageHash = curHash
          }
    },
    getHashCode: function(curHash){ //returns this objects hash page link
          curHash = curHash.replace(/\x23/,'');
          if( curHash.contains(this.targetElement.id) ){
               var hashSplit = curHash.split("/");
               var returnString = "";
               hashSplit.each(function(item){
                    if( item.contains(this.targetElement.id) ){
                         returnString = item.toString();
                    }
               }.bind(this));
               return returnString.replace(this.targetElement.id,'');
          }else {
               return false;
          }
    },
    updateLocHash: function(linkID){
          var hashString = window.location.hash;
          if(hashString == '#') hashString = ''; //ie quirk fix
          if( hashString.contains(this.targetElement.id) ){
               while(hashString.search(/\x23/,'') >-1) hashString = hashString.replace(/\x23/,''); //removes all #
               var varSplit = hashString.split("/"); //seperates possible multiple variables with in hash placed by the ajaxEngine instances
               varSplit.each(function(item){
                    if( item.contains(this.targetElement.id) ){ //ensure only this instances hash ID is updated
                         hashString = hashString.replace(item,this.targetElement.id + linkID);
                    }
               }.bind(this));
          }else {
               if(!$chk(hashString)){ //ensures no overwriting of other ajax engines information takes place
                    hashString = this.targetElement.id + linkID;
               }else hashString += "/" + this.targetElement.id + linkID;
          }
          
          while(hashString.search(/\x23/,'') >-1) hashString = hashString.replace(/\x23/,''); //removes #
          window.location.hash = this.pageHash = hashString;
          
          if(Browser.Engine.trident){
               var iframe = this.iHistory.contentWindow.document;
               iframe.open(); iframe.close(); //push current state into history then update iframe with new page states hash
               iframe.location.hash = window.location.hash;
          }
          this.orginalState = false;
    },
    accessRequest: function(contentURL){
          this.contentReq.cancel();
          this.contentReq.options.url = contentURL;
          this.contentReq.get();
    }
});
