/*
   name         : ClassBehaviours, the javascript framework based on class-name parsing
   update         : 9.11.9
   author         : Maurice van Creij, Marc Molenwijk
   dependencies   : jquery.classbehaviours.js
   info         : http://www.classbehaviours.com/

    This file is part of jQuery.classBehaviours.

    ClassBehaviours is a javascript framework based on class-name parsing.
    Copyright (C) 2008  Maurice van Creij

    ClassBehaviours is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ClassBehaviours is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with ClassBehaviours. If not, see http://www.gnu.org/licenses/gpl.html.
*/

   // create the jQuery object if it doesn't already exist
   if(typeof(jQuery)=='undefined') jQuery = function(){};

   // create the root classbehaviours object if it doesn't already exist
   if(typeof(jQuery.classBehaviours)=='undefined') jQuery.classBehaviours = function(){};

   // create the handlers child object if it doesn't already exist
   if(typeof(jQuery.classBehaviours.handlers)=='undefined') jQuery.classBehaviours.handlers = function(){}

   // AJAX interface
   jQuery.classBehaviours.ajax = {
      // properties
      queue: new Array(),
      // utilities
      getNodeValue: function(node){
         // get the text value from a node if there is one
         strValue = (node.childNodes.length>0) ? node.firstChild.nodeValue : null ;
         // return it as text or a number
         return (isNaN(strValue) || strValue==null) ? strValue : parseInt(strValue);
      },
      setNodeValue: function(node,strValue){
         // if this node already has a textNode
         if(node.childNodes.length>0){
            // set the value of the existing textNode
            node.firstChild.nodeValue = strValue;
         }else{
            // or make a new textNode
            var objNewNode = ajax.createTextNode(strValue);
            node.appendChild(objNewNode);
         }
      },
      getChildNumber: function(node){
         var nodes = node.parentNode.childNodes;
         // look for the matching node in a list of childNodes
         var intTextNodes = 0;
         for(var intA=0; intA<nodes.length; intA++){
            // count the amount of text nodes
            if(nodes[intA].nodeName == '#text') intTextNodes += 1;
            // return the found node and substract the amount of text nodes
            if(node==nodes[intA]) return intA - intTextNodes;
         }
         // return null if no match was found
         return null;
      },
      serialize: function(node, inner){
         // if the standard method is valid
         if(typeof(XMLSerializer)!='undefined'){
            nodeXml = (new XMLSerializer()).serializeToString(node);
         }
         // if this is MSIE and XML was given
         else if(node.xml!=null){
            nodeXml = node.xml;
         }
         // if this is MSIE and a whole document was given
         else if(node.getElementsByTagName('HTML').length>0){
            nodeXml = '<!DOCTYPE html><HTML>' + node.getElementsByTagName('HTML')[0].innerHTML + '</HTML>';
         }
         // if this is MSIE and an HTML4 fragment was given
         else if(node.innerHTML!=null && node.innerHTML!=''){
            nodeXml = '<' + node.nodeName + ' class="' + node.className + '" id="' + node.id + '">' + node.innerHTML + '</' + node.nodeName + '>';
         }
         // if this is MSIE and an HTML5 fragment was given
         else {
            // pick parent nodes until a node is found for which the innerHTML actually works
            nodeXml = '';
            nodeRoot = node;
            while(nodeRoot!=nodeRoot.parentNode && nodeXml==''){
               // try the next parent
               nodeRoot = nodeRoot.parentNode;
               // get the innerHTML of that
               nodeXml = nodeRoot.innerHTML;
            }
            // to find the tag name of the string, look for the id in the string
            idIndex = nodeXml.indexOf('id="' + node.id + '"');
            if(idIndex<0) idIndex = nodeXml.indexOf('id=' + node.id + ' ');
            if(idIndex<0) idIndex = nodeXml.indexOf('id=' + node.id + '>');
            // split the string before this position
            tagName = nodeXml.substring(0, idIndex);
            // find the last < and split it after
            openTagIndex = tagName.lastIndexOf('<');
            tagName = tagName.substring(openTagIndex+1);
            // find the first ' ' and split it before
            tagName = tagName.substring(0, tagName.lastIndexOf(' '));
            // split the string after the id position
            endTags = nodeXml.substring(idIndex);
            // while the close tag was not found and the string did not end
            charCount = openTagIndex + 1;
            tagCount = 1;
            tries = 0;
            while(tagCount>0 && tries<5){
               // look forward through the string to count similar tags
               nextStart = nodeXml.indexOf('<' + tagName, charCount);
               if(nextStart==-1) nextStart = 1000000;
               nextClose = nodeXml.indexOf('</' + tagName + '>', charCount);
               if(nextClose==-1) nextClose = 1000000;
               // if you find a similar opening tag add 1 to the count
               if(nextStart<nextClose){
                  tagCount += 1;
                  charCount = nextStart + 1;
               }
               // if you find a similar closing tag substract 1 from the count
               if(nextClose<nextStart){
                  tagCount -= 1;
                  charCount = nextClose + 1;
               }
               // if the count reaches 0 the proper closing tag was found
               tries += 1;
            }
            // cut off the rest of the string using the character counter
            closeTagIndex = nextClose + tagName.length + 3;
            // return the resulting clip
            nodeXml = nodeXml.substring(openTagIndex, closeTagIndex);
         }
         // remove the outer tag if required
         if(inner){
            // split after first > and before last <
            nodeXml = nodeXml.substring(nodeXml.indexOf('>')+1, nodeXml.lastIndexOf('<'));
         }
         // give the extracted element back
         return nodeXml;
      },
      deserialize: function(text){
         newElement = document.createElement('DIV');
         newElement.innerHTML = text;
         return newElement;
      },
      deserialize2: function(text){
         if (window.DOMParser){
            parser = new DOMParser();
            xmlDoc = parser.parseFromString(text,"text/xml");
         }else{
            xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
            xmlDoc.async = "false";
            xmlDoc.loadXML(text);
         }
         return xmlDoc;
      },
      defaultProgress: function(progressStatus, progressNode, progressError){
         // fill the refered node with a progress report
         progressNode.innerHTML = (progressStatus>-1) ? 'Loading: ' + Math.round(progressStatus * 100) + '%' : 'Error: ' + progressError ;
      },
      defaultLoad: function(loadXml, loadNode, loadText){
         // fill the refered node with the returned html string
         loadNode.innerHTML = loadText.split('<body>')[1].split('</body>')[0];
      },
      getPostValues: function(node, nodeId){
         // use the id if needed
         if(nodeId!=null) node = document.getElementById(nodeId);
         // get all elements of this form
         formNode = jQuery.classBehaviours.utilities.rootNode(node, 'FORM');
         rootNode = jQuery.classBehaviours.utilities.rootNode(node, 'FIELDSET');
         allNodes = rootNode.getElementsByTagName('*');
         postValues = '';
         // see if we can do anything to make it easier for .NET
           // .NET replaces the last _ with a $ for eventtarget validation
           eventTarget = '';
           split = node.id.split("_");
           if (split.length > 1) {
               for (var a = 0; a < split.length; a++)
                   if (split[a] != '')
                   eventTarget += (a == split.length - 1 ? '$' + split[a] : '_' + split[a]);
           }
           else eventTarget = node.id;
           postValues = '__EVENTTARGET=' + eventTarget + '&';
         // for all elements
         for(var a=0; a<allNodes.length; a++){
            // build the query string from the name and value pairs
            if(
               allNodes[a].nodeName=='INPUT' ||
               allNodes[a].nodeName=='SELECT' ||
               allNodes[a].nodeName=='TEXTAREA'
            )
               if(
                  allNodes[a].getAttribute('type')!='submit' &&
                  allNodes[a].getAttribute('type')!='image' &&
                  allNodes[a].getAttribute('type')!='button' &&
                  allNodes[a].getAttribute('type')!='reset'
               )
                  //  Add all except the viewstate and eventtarget (.NET)
                  if(allNodes[a].name!='__VIEWSTATE' && allNodes[a].name!='__EVENTTARGET')
                     postValues += (allNodes[a].checked || (allNodes[a].type!='radio' && allNodes[a].type!='checkbox')) ?
                        allNodes[a].name + '=' + escape(allNodes[a].value) + '&' :
                        '';
         }
         // add the value of the pressed button
         if(node.nodeName == 'BUTTON' || node.getAttribute('type')=='submit' || node.getAttribute('type')=='button') postValues += node.id + '=' + node.value + '&';
         // add the cookie value too
         if (document.cookie != null) postValues += document.cookie.replace(/; /gi, "&");
         // return the values
         return postValues;
      },
      addRequest: function(url, loadHandler, progressHandler, post, referingObject){
         // get the first free slot in the que
         index = this.queue.length;
         // add new request to the end of the que
         this.queue[index] = new this.HttpRequest();
         // set request constants
         this.queue[index].idx         =   index;
         this.queue[index].url         =   url;
         this.queue[index].post         =   post;
         this.queue[index].method      =   (post!=null) ? 'POST' : 'GET' ;
         // request events
         this.queue[index].doOnLoad      =   loadHandler;
         this.queue[index].doOnProgress   =   progressHandler;
         this.queue[index].referObject   =   referingObject;
         // request properties
         this.queue[index].time         =   new Date();
         // ask the queue handler to handle the next queued item
         this.handleQueue();
         // return false
         return false;
      },
      // events
      makeRequest: function(queued){
         // branch for native XMLHttpRequest object
         if(window.XMLHttpRequest){
            queued.request = new XMLHttpRequest();
            queued.request.onreadystatechange = this.progress;
            queued.request.open(queued.method, queued.url, true);
            if(queued.method == 'POST'){
               queued.request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
               queued.request.setRequestHeader("Content-length", queued.post.length);
               queued.request.setRequestHeader("Connection", "close");
            }
            queued.request.send(queued.post);
         // branch for IE/Windows ActiveX version
         }else if(window.ActiveXObject){
            queued.request = new ActiveXObject("Microsoft.XMLHTTP");
            queued.request.onreadystatechange = this.progress;
            queued.request.open(queued.method, queued.url, true);
            if(queued.method == 'POST'){
               queued.request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
               queued.request.setRequestHeader("Content-length", queued.post.length);
               queued.request.setRequestHeader("Connection", "close");
            }
            queued.request.send(queued.post);
         // if all else fails: load the document in an iFrame
         }else if(window.frames){
            // create an iframe to read the document in
               objIframe = document.createElement("IFRAME");
               objIframe.src = queued.url;
               objIframe.id = "feedimport0";
               objIframe.name = "feedimport0";
               objIframe.style = "visibility : invisible; position : absolute; left : -1600px; top : -1600px;";
               //objIframe.onload = ajax_load; // Doesn't work in Opera
            // append the iframe to the document
               document.body.appendChild(objIframe);
            // wait for the iframe to load
            this.wait();
         }
      },
      handleQueue: function(){
         queue = jQuery.classBehaviours.ajax.queue;
         // if the first item in the queue is a completed request
         if(queue.length>0){
            if(queue[0].ready==4 /*&& ajax.queue[0].status==200*/){
               // remove the completed request
               queue.reverse();
               queue.length = queue.length - 1;
               queue.reverse();
            }
         }
         // if the first item in the queue isn't allready in progress
         if(queue.length>0){
            if(!(queue[0].ready<4 && queue[0].ready!=null)){
               this.makeRequest(queue[0]);
            }
         }
      },
      // events
      progress: function(){
         queued = jQuery.classBehaviours.ajax.queue[0];
         // remember the readyState
         queued.ready = queued.request.readyState;
         // only if req shows "complete"
         if(queued.request.readyState == 4){
            // remember the status
            queued.status = queued.request.status;
            // only if "OK"
            if(queued.request.status == 200 || queued.request.status == 304){
               // update optional progress indicator code
               if(queued.doOnProgress) queued.doOnProgress(4, queued.referObject, queued.request.status, queued.time);
               // get the imported text
               queued.text = queued.request.responseText;
               // get the imported document
               queued.document = queued.request.responseXML;
               // if the document is empty use a deserialized version of the text instead
               if(queued.document==null) queued.document = jQuery.classBehaviours.ajax.deserialize(queued.text)
               else if(queued.document.childNodes.length==0) queued.document = jQuery.classBehaviours.ajax.deserialize(queued.text);
               // trigger the load event
               if(queued.doOnLoad) queued.doOnLoad(queued.document, queued.referObject, queued.text, queued.time);
               // request the next item in the queue to be handled
               jQuery.classBehaviours.ajax.handleQueue();
            }else{
               // update optional progress indicator code
               if(queued.doOnProgress) queued.doOnProgress(-1, queued.referObject, queued.request.status, queued.time);
            }
         }else{
            // update optional progress indicator code
            if(queued.doOnProgress) queued.doOnProgress(queued.request.readyState, queued.referObject, 200, queued.time);
         }
         // return the status if desired
         return queued.request.readyState;
      },
      wait: function(){
         queued = jQuery.classBehaviours.ajax.queue[0];
         // if the xml document has loaded in the iframe
         if(window.frames["feedimport0"]){
            // define the xml document object
            queued.document = window.frames["feedimport0"].document;
            queued.text = window.frames["feedimport0"].document.body.innerHTML;
            // what to do after the xml document loads
            queued.doOnLoad(queued.document, queued.referObject, queued.text, queued.time);
         // else try again in a while
         }else{
            setTimeout("ajax.wait()",256);
         }
      }
   }
      // prototype http request object
      jQuery.classBehaviours.ajax.HttpRequest = function(){
         // request constants
         this.idx         =   null;
         this.url         =   null;
         this.post         =   null;
         this.method         =   'GET';
         // request events
         this.doOnLoad      =   null;
         this.doOnProgress   =   null;
         this.referObject   =   null;
         // request properties
         this.request      =   null;
         this.document      =   null;
         this.text         =   null;
         this.ready         =   null;
         this.status         =   null;
         this.time         =   null;
      }

   // debug console
   jQuery.classBehaviours.console = {
      // porperties
      id: 'debugConsole0',
      limit: 32,
      // methods
      debug: function(){
         // create the debug console if it doesn't exist yet
         debugConsole = document.getElementById(jQuery.classBehaviours.console.id);
         if(debugConsole==null){
            // create a new console node
            newConsole = document.createElement('div');
            newConsole.id = jQuery.classBehaviours.console.id;
            newText = document.createTextNode('-- newest at top --');
            newConsole.appendChild(newText);
            document.body.appendChild(newConsole);
            debugConsole = document.getElementById(jQuery.classBehaviours.console.id);
         }
         // set the console's properties
         if(debugConsole.style.background=='')    debugConsole.style.background = '#ffffff url(../images/button_passive.png) no-repeat 0px 0px';
         if(debugConsole.style.border=='')       debugConsole.style.border = 'solid 1px #000000';
         if(debugConsole.style.bottom=='')       debugConsole.style.bottom = 'auto';
         if(debugConsole.style.height=='')       debugConsole.style.height = (navigator.userAgent.indexOf('MSIE 6')>-1) ? '580px' : '98%' ;
         if(debugConsole.style.left=='')       debugConsole.style.left = 'auto';
         if(debugConsole.style.overflow=='')    debugConsole.style.overflow = 'auto';
         if(debugConsole.style.padding=='')       debugConsole.style.padding = '1% 1% 1% 1%';
         if(debugConsole.style.position=='')    debugConsole.style.position = (navigator.userAgent.indexOf('MSIE 6')>-1) ? 'absolute' : 'fixed' ;
         if(debugConsole.style.right=='')       debugConsole.style.right = '-1px';
         if(debugConsole.style.top=='')          debugConsole.style.top = '-1px';
         if(debugConsole.style.width=='')       debugConsole.style.width = '128px';
         debugConsole.onclick = jQuery.classBehaviours.console.move;
         jQuery.classBehaviours.fader.setFade(debugConsole, 75);
         // send all the incoming arguments to the debug console
         newList = document.createElement('ul');
         for(var a=0; a<arguments.length; a++){
            // create a list item for each argument
            newListItem = document.createElement('li');
            newListText = document.createTextNode(arguments[a]);
            newListItem.appendChild(newListText);
            newList.appendChild(newListItem);
         }
         debugConsole.insertBefore(newList, debugConsole.firstChild);
         // if there are too many enties, remove a few
         allLists = debugConsole.getElementsByTagName('ul');
         if(allLists.length>jQuery.classBehaviours.console.limit)
            for(var a=allLists.length-1; a>jQuery.classBehaviours.console.limit; a--)
               removedChild = debugConsole.removeChild(allLists[allLists.length-1]);
      },
      clear: function(){
         // clear the entries
         debugConsole = document.getElementById(jQuery.classBehaviours.console.id);
         if(debugConsole!=null){
            allLists = debugConsole.getElementsByTagName('ul');
            for(var a=allLists.length-1; a>=0; a--)
               removedChild = debugConsole.removeChild(allLists[allLists.length-1]);
         }
      },
      html: function(string){
         string = '<xmp>' + string + '</xmp>'
         this.debug(string)
      },
      // events
      move: function(that){
         var node = (typeof(this.nodeName)=='undefined') ? that : this ;
         // get the console
         debugConsole = document.getElementById(jQuery.classBehaviours.console.id);
         // toggle through several console positions
         if(debugConsole.style.right == '-1px' && debugConsole.style.width == '128px'){
            debugConsole.style.bottom = 'auto';
            debugConsole.style.height = (navigator.userAgent.indexOf('MSIE 6')>-1) ? '580px' : '98%' ;
            debugConsole.style.left = '-1px';
            debugConsole.style.right = 'auto';
            debugConsole.style.top = '-1px';
            debugConsole.style.width = '128px';
            debugConsole.style.zIndex = '100000';
         }else if(debugConsole.style.left == '-1px' && debugConsole.style.width == '128px'){
            debugConsole.style.bottom = '-1px';
            debugConsole.style.height = '128px';
            debugConsole.style.left = '-1px';
            debugConsole.style.right = 'auto';
            debugConsole.style.top = 'auto';
            debugConsole.style.width = '98%';
            debugConsole.style.zIndex = '100000';
         }else if(debugConsole.style.bottom == '-1px' && debugConsole.style.width == '98%'){
            debugConsole.style.bottom = 'auto';
            debugConsole.style.height = (navigator.userAgent.indexOf('MSIE 6')>-1) ? '580px' : '98%' ;
            debugConsole.style.left = 'auto';
            debugConsole.style.right = '-1px';
            debugConsole.style.top = '-1px';
            debugConsole.style.width = '128px';
            debugConsole.style.zIndex = '100000';
         }
      }
   }

   // cookies Library
   jQuery.classBehaviours.cookies = {
      // methods
      getDaysFromNow: function(intDays){
         var dateCurrent = new Date();
         var intCurrent = Date.parse(dateCurrent);
         var intPeriod = intDays*24*60*60*1000;
         var datePeriodInFuture = new Date(intCurrent+intPeriod);
         return datePeriodInFuture;
      },
      setCookie: function(name, value, expires, path, domain, secure) {
         var curCookie = name + "=" + escape(value) +
            ((expires) ? "; expires=" + expires.toGMTString() : "") +
            ((path) ? "; path=" + path : "") +
            ((domain) ? "; domain=" + domain : "") +
            ((secure) ? "; secure" : "");
         document.cookie = curCookie;
      },
      getCookie: function(name) {
         var dc = document.cookie;
         var prefix = name + "=";
         var begin = dc.indexOf("; " + prefix);
         if (begin == -1) {
            begin = dc.indexOf(prefix);
            if (begin != 0) return null;
         }else{
            begin += 2;
         }
         var end = document.cookie.indexOf(";", begin);
         if (end == -1)
            end = dc.length;
         return unescape(dc.substring(begin + prefix.length, end));
      },
      deleteCookie: function(name, path, domain) {
         if (getCookie(name)) {
            document.cookie = name + "=" +
            ((path) ? "; path=" + path : "") +
            ((domain) ? "; domain=" + domain : "") +
            "; expires=Thu, 01-Jan-70 00:00:01 GMT";
         }
      },
      fixDate: function(date) {
         var base = new Date(0);
         var skew = base.getTime();
         if (skew > 0)
            date.setTime(date.getTime() - skew);
      },
      makeBoolean: function(strIn){
         if(strIn=='true'){
            booOut = true;
         }else{
            booOut = false;
         }
         return booOut;
      },
      csv2array: function(csvIn){
         return csvIn.split(',');
      },
      array2csv: function(arrIn){
         return ''+arrIn;
      }
   }

   // decorative fader animations
   jQuery.classBehaviours.fader = {
      // properties
      getFade: function(node){
         var fadeValue = null;
         if(node!=null){
            // get the fade value using the proper method
            if(typeof(node.style.MozOpacity)!='undefined')   fadeValue = Math.round(parseFloat(node.style.MozOpacity)*100);
            if(typeof(node.style.filter)!='undefined')      fadeValue = parseInt(node.filters.alpha.opacity);
            if(typeof(node.style.opacity)!='undefined')      fadeValue = Math.round(parseFloat(node.style.opacity)*100);
         }
         // return the value
         return fadeValue;
      },
      setFade: function(node, amount){
         if(node!=null){
            // set the fade value using the proper method
            if(typeof(node.style.MozOpacity)!='undefined')   node.style.MozOpacity = amount/100;
            if(typeof(node.style.filter)!='undefined')      node.style.filter = "alpha(opacity=" + amount + ")";
            if(typeof(node.style.opacity)!='undefined')      node.style.opacity = amount/100;
         }
         /*
         filter:alpha(opacity=50);   imageobject.filters.alpha.opacity=opacity
         -moz-opacity: 0.5;         imageobject.style.MozOpacity=opacity/100
         opacity: 0.5;
         -khtml-opacity: 0.5;
         */
      },
      getSize: function(node){
         // measure the height of the container
         var nodeWidth = node.offsetWidth;
         var nodeHeight = node.offsetHeight;
         // measure the height of all the childnodes of the container
         var totalWidth = 0;
         var totalHeight = 0;
         var contents = node.childNodes;
         for(var a=0; a<contents.length; a++){
            totalWidth += (contents[a].offsetWidth) ? contents[a].offsetWidth : 0 ;
            totalHeight += (contents[a].offsetHeight) ? contents[a].offsetHeight : 0 ;
         }
         // pass back the largest number
         return new Array(nodeWidth, nodeHeight, totalWidth, totalHeight);
      },
      setSize: function(node, xAmount, yAmount){
         if(xAmount!=null) node.style.width = xAmount + 'px';
         if(yAmount!=null) node.style.height = yAmount + 'px';
      },
      // methods
      fade: function(id, start, end, step, delay, acceleration, evalOnEnd){
         var cf = jQuery.classBehaviours.fader;
         // get the target node
         target = document.getElementById(id);
         // get the start value if missing
         if(start==null)       start = cf.getFade(target);
         if(end==null)          end = 100;
         if(step==null)          step = 1;
         if(delay==null)       delay = 10;
         if(acceleration==null)    acceleration = 1;
         if(evalOnEnd==null)    evalOnEnd = '';
         // calculate the new value
         if(start<end)      {value = (start+step>end) ? end : start+step ;}
         else if(start>end)   {value = (start-step<end) ? end : start-step ;}
         // set the fade
         cf.setFade(target, value);
         // order the next step
         if(value!=end)       {setTimeout("jQuery.classBehaviours.fader.fade('"+id+"',"+value+","+end+","+(step+acceleration)+","+delay+","+acceleration+",'"+evalOnEnd+"')", delay);}
         else             {eval(evalOnEnd);}
      },
      size: function(id, start, end, step, delay, acceleration, evalOnEnd){
         var cf = jQuery.classBehaviours.fader;
         // get the target node
         target = document.getElementById(id);
         // get the start value if missing
         if(start==null)       start = cf.getSize(target)[1];
         if(end==null)          end = cf.getSize(target)[3];
         if(step==null)          step = 10;
         if(delay==null)       delay = 10;
         if(acceleration==null)    acceleration = 10;
         if(evalOnEnd==null)    evalOnEnd = '';
         // calculate the new value
         if(start<end)      {value = (start+step>end) ? end : start+step ;}
         else if(start>end)   {value = (start-step<end) ? end : start-step ;}
         // set the fade
         cf.setSize(target, null, value);
         // order the next step
         if(value!=end)       {setTimeout("jQuery.classBehaviours.fader.size('"+id+"',"+value+","+end+","+(step+acceleration)+","+delay+","+acceleration+",'"+evalOnEnd+"')", delay);}
         else             {eval(evalOnEnd);}
      },
      /* START: legacy functions */
      fadeIn: function(id, step, delay, evalOnEnd, acceleration){
         jQuery.classBehaviours.fader.fade(id, 0, 100, step, delay, acceleration, evalOnEnd);
      },
      fadeOut: function(id, step, delay, evalOnEnd, acceleration){
         jQuery.classBehaviours.fader.fade(id, 100, 0, step, delay, acceleration, evalOnEnd);
      },
      crossFade: function(idIn, idOut, amount, step, delay, evalOnEnd, acceleration){
         jQuery.classBehaviours.fader.fade(idIn, 0, 100, step, delay, acceleration, evalOnEnd);
         jQuery.classBehaviours.fader.fade(idOut, 100, 0, step, delay, acceleration, '');
      },
      grow: function(id, step, delay, evalOnEnd, acceleration){
         jQuery.classBehaviours.fader.size(id, 1, null, step, delay, acceleration, evalOnEnd);
      },
      shrink: function(id, step, delay, evalOnEnd, acceleration){
         jQuery.classBehaviours.fader.size(id, null, 1, step, delay, acceleration, evalOnEnd);
      }
      /* END: legacy functions */
   }

   // general purpose functions
   jQuery.classBehaviours.utilities = {
      // returns all nodes of the same class
      getElementsByClassName: function(className, node){
         // use the whole body if no target was provided
         target = (node!=null) ? node : document ;
         // make an empty array for the results
         var foundNodes = new Array();
         // for all elements in the parent node
         var allNodes = (target.all) ? target.all : target.getElementsByTagName("*");
         for(var a=0; a<allNodes.length; a++){
            // if the item has a className
            if(allNodes[a].className){
               // process the classname
               nodeClass = allNodes[a].className + ' ';
               // add it to the results if the classname was found
               if(nodeClass.indexOf(className+' ')>-1) foundNodes[foundNodes.length] = allNodes[a];
            }
         }
         // return the list
         return foundNodes;
      },
      // emulates getElementById for XML documents
      getXmlElementById: function(id, doc, echo){
         // default value
         node = null ;
         // if a valid id was given
         if(id!=null){
            // look through all nodes
            allNodes = doc.getElementsByTagName('*');
            // until you find one with the right ID
            var a = 0;
            while(node==null && a<allNodes.length){
               if(allNodes[a].getAttribute('id')==id){
                  node = allNodes[a];
               }
               a += 1;
            }
         }
         // return the cleaned content
         return (echo && node==null) ? doc : node;
      },
      // places a sibling node after a given node
      insertAfter: function(parentNode, siblingNode, newNode){
         nextNode = jQuery.classBehaviours.utilities.nextNode(siblingNode);
         if(nextNode==siblingNode) parentNode.appendChild(newNode)
         else parentNode.insertBefore(newNode, nextNode);
      },
      // reverse a string - String.prototype.reverse=function(){return this.split("").reverse().join("");}
      reverseString: function(inString){
         return inString.split("").reverse().join("");
      },
      // rebuild a number into a currency string
      makeIntoCurrency: function(number, decimal, thousands){
         currency = number;
         // format the number
         currency = number.toFixed(2);
         // reverse the string
         currency = jQuery.classBehaviours.utilities.reverseString(currency);
         // add thousands markers
         if(currency.length>6) currency = currency.substr(0, 6) + thousands + currency.substr(6);
         if(currency.length>10) currency = currency.substr(0, 10) + thousands + currency.substr(10);
         if(currency.length>15) currency = currency.substr(0, 15) + thousands + currency.substr(15);
         // reverse the string back
         currency = jQuery.classBehaviours.utilities.reverseString(currency);
         // return the string
         return currency;
      },
      // detaries the available room on the screen
      screenHeight: function(){
         return (window.innerHeight) ? window.innerHeight : (document.documentElement.clientHeight) ? document.documentElement.clientHeight : document.body.clientHeight ;
      },
      // return a parameter from the url's query strings
      getQueryParameter: function(paramName, defaultValue){
         // split the query string at the parameter name
         var queryParameters = document.location.search.split(paramName+"=");
         // split the parameter value from the rest of the string
         var queryParameter = (queryParameters.length>1) ? queryParameters[1].split("&")[0] : null ;
         // return the value
         return (queryParameter!=null) ? queryParameter : defaultValue ;
      },
      // gets the value of a parameter from a className
      getClassParameter: function(targetNode, paramName, defaultValue){
         if(targetNode!=null){
            // get the class parameter from the classname
            var classParameter = targetNode.className;
            // split the classname between the parameter name
            classParameter = (classParameter!=null) ? classParameter.split(paramName + '_') : new Array();
            // split the second piece between spaces and take the first part,  if there are two pieces
            classParameter = (classParameter.length>1) ? classParameter[1].split(' ')[0] : null ;
            // return the value
            return (classParameter!=null) ? classParameter : defaultValue ;
         }
      },
      // sets the value of a parameter from a className
      setClassParameter: function(targetNode, paramName, newValue){
         if(targetNode!=null){
            // create a default value, if there isn't one
            if(targetNode.className.indexOf(' ' + paramName + '_')<0) targetNode.className += ' ' + paramName + '_0';
            // get the old value
            oldValue = this.getClassParameter(targetNode, paramName, null);
            // replace the old value
            if(oldValue!=null) targetNode.className = targetNode.className.replace(paramName+'_'+oldValue, paramName+'_'+newValue);
         }
      },
      // get the next node without worrying about text nodes
      nextNode: function(node, count){
         testNode = node;
         if(count==null) count = 1;
         // look for the next html node
         for(var a=0; a<count; a++){
            do {
               testNode = testNode.nextSibling;
               if(testNode==null) testNode = node;
            }while(testNode.nodeName.indexOf('#text')>-1);
         }
         // return it
         return testNode;
      },
      // get the previous node without worrying about text nodes
      previousNode: function(node, count){
         testNode = node;
         if(count==null) count = 1;
         // look for the previous html node
         for(var a=0; a<count; a++){
            do {
               testNode = testNode.previousSibling;
               if(testNode==null) testNode = node;
            }while(testNode.nodeName.indexOf('#text')>-1);
         }
         // return it
         return testNode;
      },
      // get the first real child node without worrying about text nodes
      firstNode: function(node, count){
         return this.nextNode(node.firstChild, count);
      },
      // find the parent node with the given classname
      rootNode: function(node, rootTag, rootId, rootClass){
         // try parent nodes until you find the one which meets the conditions
         rootFound = false;
         while(!rootFound && node.nodeName!='BODY'){
            rootFound = (rootTag && node.nodeName) ? (node.nodeName.indexOf(rootTag)>-1) : rootFound ;
            rootFound = (rootId && node.id) ? (node.id.indexOf(rootId)>-1) : rootFound ;
            rootFound = (rootClass && node.className) ? (node.className.indexOf(rootClass)>-1) : rootFound ;
            node = (!rootFound) ? node.parentNode : node;
         }
         // pass it back
         return node;
      },
      // returns the visible display state needed for this element
      getVisibleState: function(node){
         // what kind of node is this
         switch(node.nodeName.toLowerCase()){
            case 'table' : visibleState='table' ; break;
            case 'thead' : visibleState='table-header-group' ; break;
            case 'tfoot' : visibleState='table-footer-group' ; break;
            case 'tbody' : visibleState='table-row-group' ; break;
            case 'tr' : visibleState='table-row' ; break;
            case 'td' : visibleState='table-cell' ; break;
            case 'th' : visibleState='table-cell' ; break;
            default : visibleState='block';
         }
         // apply the state
         return (document.all && navigator.userAgent.indexOf('Opera')<0) ? 'block' : visibleState;
      },
      // hide select form elements for graphic glitches in certain browsers
      hideSelects: function(node){
         if(
            navigator.userAgent.indexOf('MSIE 6')>-1 ||
            (navigator.userAgent.indexOf('Firefox/2')>-1 && navigator.userAgent.indexOf('Mac')>-1)
         ){
            allSelects = (node) ? node.getElementsByTagName('SELECT') : document.getElementsByTagName('SELECT') ;
            for(var a=0; a<allSelects.length; a++){
               allSelects[a].style.visibility = 'hidden';
            }
         }
      },
      showSelects: function(node){
         if(
            navigator.userAgent.indexOf('MSIE 6')>-1 ||
            (navigator.userAgent.indexOf('Firefox/2')>-1 && navigator.userAgent.indexOf('Mac')>-1)
         ){
            allSelects = (node) ? node.getElementsByTagName('SELECT') : document.getElementsByTagName('SELECT') ;
            for(var a=0; a<allSelects.length; a++){
               allSelects[a].style.visibility = 'visible';
            }
         }
      },
      // adds an event-handler the proper way
      addEvent: function(node, eventName, eventHandler){
         if(node.addEventListener)    node.addEventListener(eventName, eventHandler, false)
         else if(node.attachEvent)    node.attachEvent("on"+eventName, function(event){eventHandler(event)})
         else                   node["on"+eventName] = eventHandler;
         return true;
      }
   }

   // Parser functions
   jQuery.classBehaviours.parser = {
      // status of the parser
      waiting: false,
      // verify the state of the object
      start: function(){
         // if the document is complete enough start the behaviours, else wait until everything is loaded
         var handlersCount = 0;
         for (b in jQuery.classBehaviours.handlers) handlersCount++;
         this.waiting = (handlersCount>0 && document.body) ? this.parseDocument() : jQuery.classBehaviours.utilities.addEvent(window, 'load', this.parseDocument) ;
      },
      // scan the whole document
      parseDocument: function(){
         // pass the document object to the parser
         jQuery.classBehaviours.parser.parseNode(document);
         // return the status
         return false;
      },
      // scan the document object model for target classNames
      parseNode: function(node){
         // for all childnodes of the given node
         var allNodes = node.getElementsByTagName("*");
         for(var a=0; a<allNodes.length; a++){
            // process the classnames of the node
            this.processNode(allNodes[a]);
         }
         // do the same for the parent node
         this.processNode(node);
      },
      // recursive version of the same function
      parseNode: function(node){
         // process the node
         parseChildNodes = (node.className!=null) ? this.processNode(node) : true ;
         // parse any childnodes
         if(parseChildNodes) for(var a=0; a<node.childNodes.length; a++) this.parseNode(node.childNodes[a]);
      },
      // process the classnames of the node
      processNode: function(node){
         // get its className
         nodeClass = ' ' + node.className + ' ';
         // for all class behaviours
         for(b in jQuery.classBehaviours.handlers){
            // if the behaviour name is in the className tested node

            if(nodeClass.indexOf(' ' + jQuery.classBehaviours.handlers[b].name + ' ')>-1){
               // apply its respective behaviour
               jQuery.classBehaviours.handlers[b].start(node);
            }
         }
         // decide if to continue parsing deeper into this branch
         return (nodeClass.indexOf('doNotParse')<0);
      }
   }

   // short-cut wrapper functions
   debug = jQuery.classBehaviours.console.debug;
   getXmlElementById = jQuery.classBehaviours.utilities.getXmlElementById;
   getElementsByClassName = jQuery.classBehaviours.utilities.getElementsByClassName;

   // add this addon to the jQuery object
   if(typeof(jQuery.fn)!='undefined'){
      // set the event handler for this jQuery method
      $(document).ready(
         function(){
// TODO: at the moment this is handled by the individual classbehaviours modules, until this proves too slow
//            $.classBehaviours.parser.parseDocument()
         }
      );
   }else{
      // start the (backup) parsing of classes if the main jQuery library is not available
      jQuery.classBehaviours.parser.start()
   }
