// Core operating file for the codebase

/*
 Future additions:
    1. Needs to be broken into modules to keep file size down and to prevent from loading data that isn't needed
    2. Needs to be able to differentiate between certain elements so calls to the Element.extend function can be sped up and specific functions can be extended to only their destined elements
    3. Needs better support for handling the _extended private variable
    4. Enumeration.
    5. New functions load and loadOnce
    6. Runtime could be faster.  Conditionals are needed for browser or function sniffing but instead of a single function with a conditional for either case, a conditional that would create a function specific for that browser would be faster at runtime.  However file size would most definitely be larger and initialization time would be greater.
*/

//====================================================================================================
//= Function Prototype
//====================================================================================================
Function.prototype.bind=function(obj)
{
   var _this=this;
   
   return function(){ return _this.method.apply(obj, arguments); };
};

//====================================================================================================
//= Array Prototype
//====================================================================================================
Array.prototype.indexOf=function(value)
{
   for(var i=0; i<this.length; i++)
      if(this[i]==value)
         return true;
         
   return false;
};


//====================================================================================================
//= Object Prototype
//====================================================================================================
Object.extend=function(sub, sup)
{
   for(var tempProperty in sup)
      sub[tempProperty]=sup[tempProperty];
      
   return sub;
};

Object.extend(Object, {
   
   keyExists: function(o, key)
      {
         return o[key]!==undefined;
      },

   keys: function(object)
      {
         var keys=[];
         for(var property in object)
            keys.push(property);
         
         return keys;
      },
      
   values: function(object)
      {
         var values=[];
         for(var property in object)
            values.push(object[property]);

         return values;
      },
   
   clone: function(object)
      {
         return Object.extend({}, object);
      },
      
   isObject: function(o)
      {
         return (o.constructor.toString().indexOf("Object")>-1 ? true : false);
      },
      
   isArray: function(a)
      {
         return (a.constructor.toString().indexOf("Array")>-1 ? true : false);
      },
   
   isString: function(s)
      {
         return (s.constructor.toString().indexOf("String")>-1 ? true : false);
      },
      
   isNumber: function(n)
      {
         if(typeof n!="number")
         {
            for(var i=0; i<n.length; i++)
               if("0123456789.".indexOf(n[i])==-1)
                  return false;

            return true;
         }
         else
         {
            return true;
         }
      }
});

String.prototype.camelize=function()
{
   var oStringList=this.split('-');
   
   // Has no "-"
   if(oStringList.length==1)
      return oStringList[0];
      
   var camelizedString=(this.indexOf('-') == 0 ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) : oStringList[0]);
   
   for(var i=1, len=oStringList.length; i<len; i++)
   {
      var s=oStringList[i];
      camelizedString+=s.charAt(0).toUpperCase() + s.substring(1);
   }
   
   return camelizedString;
}

String.prototype.trim=function()
{
   return this.replace(/^\s+|\s+$/g,"");
}

String.prototype.ltrim=function()
{
   return this.replace(/^\s+/,"");
}

String.prototype.rtrim=function()
{
   return this.replace(/\s+$/,"");
}

//====================================================================================================
//= Event Class
//====================================================================================================
Event.collection=[];
         
Event.primitives=["abort", "blur", "change", "click", "dblclick", "error", "focus", "keydown", "keypress", "keyup", "load", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup", "reset", "resize", "select", "submit", "unload"];

// Event(type[, data])
function Event(type)
{
   if(type!="")
   {
      var data=(arguments.length>1 ? arguments[1] : "");
   
      var finalEvent=Object.extend(this, (type!="" && data!="" ? Object.extend(data, {"type":type}) : (type!="" && data=="" ? {"type":type} : {})))
      
      Event.collection[type]=finalEvent;
   
      return finalEvent;
   }
   
   return null;
}

Event.getIndex=function(obj, type, listener)
{
   for(var i=0; i<Event.collection.length; i++)
      if(Event.collection[i]["obj"]==obj && Event.collection[i]["type"]==type && Event.collection[i]["listener"]==listener)
         return i;
   
   return -1; 
};

//====================================================================================================
//= Element Class
//====================================================================================================
if(!window.Element)
   var Element=new Object();
   
function Element(tag)
{
   return Element.extend(document.createElement(tag));
}

// Create Event.Methods for extending across window, document, and html elements
Event.Methods={

   addListener: function()
      {
         var type=arguments[0];
         var fn=arguments[1];
         var useCapture=(arguments.length==3 ? arguments[2] : false);
         
         if(Event.primitives.indexOf(type))
         {
            if(this.addEventListener)
            {
               this.addEventListener(type, fn, useCapture);
            }
            else
            {
               this.attachEvent("on" + type, fn);
            }
         }
         else
         {
            if(this["on" + type]!=undefined)
            {
               this["on" + type].push(fn)
            }
            else
            {
               this["on" + type]=[fn];
            }
         }
      },
   
   removeListener: function()
      {
         var type=arguments[0];
         var fn=arguments[1];
         var useCapture=(arguments.length==3 ? arguments[2] : false);
         
         Event.primitives.indexOf(type) ? this.removeEventListener ? this.removeEventListener(type, fn, useCapture) : this.detachEvent("on" + type, fn) : Event.remove(this, type, fn);
      },

   fire: function(type)
      {
         var type=arguments[0];
         var data=(arguments.length==2 ? arguments[1] : []);
         
         var listeners=this["on" + type]!=undefined ? this["on" + type] : [];
         
	      for(var i=0; i<listeners.length; i++)
         {
            listeners[i].call(this, data); // Replace with listener.bind(obj)??
         }
      }
};

Element.Methods={
   
   destroy: function()
      {
         while(this.firstChild)
         {
            var temp=Element.extend(this.firstChild);
            
            if(typeof(temp.destroy)=="function")
               temp.destroy();
         }
      
         this.parentNode.removeChild(this);
         delete this;
      },
   
   /*
       3 paramater options:
          1. 2 parameters, property/value pairs.  Non-camelized properties will be camelized. (fastest, single property)
          2. 1 parameter, Style object(pre camelized) with property/value pairs, semi-colon delimiter (fastest, multi-property) -----> could add extra conditional for non-camelized
          3. 1 parameter, Style definition string (slowest, single or multi)
          
   */
   setStyle: function()
      {
         var style={};
         
         if(arguments.length==1)
         {
            if(typeof(arguments[0])=="string")
            {
               var declarations=arguments[0].split(";");
               
               for(var i=0; i<declarations.length; i++)
               {
                  var splitData=declarations[i].split(":");
                  
                  if(splitData.length==2)
                     style[splitData[0].trim()]=splitData[1].trim();
               }
            }
            else if(typeof(arguments[0])=="object")
            {
               style=arguments[0];
            }
         }
         else if(arguments.length==2)
         {
            style[arguments[0]]=arguments[1];
         }
         
         for(var key in style)
         {
            if(/^float$/.test(key))
            {
               key=(Browser.isIE ? "styleFloat" : "cssFloat");
            }
               
            if(/^opacity$/.test(key))
            {
               this.style.filter="alpha(opacity:" + style[key]*100 + ")"; // IE filter opacity:
               this.style.KHTMLOpacity=style[key]; // Safari and Konqueror:
               this.style.MozOpacity=style[key]; // Old Mozilla and Firefox:
               this.style.opacity=style[key]; // CSS3 opacity for browsers that support it:
            }
            
            this.style[key.camelize()]=style[key];
         }
         
         return this;   
      },
      
   toggleDisplay: function()
      {
         var tempDisplay;
         
         switch(this.tagName)
         {
            case "DIV" : tempDisplay="block";
               break;
            case "SPAN" : tempDisplay="inline";
               break;
            case "TABLE" : tempDisplay="table";
               break;
            case "TR" : tempDisplay="table-row";
               break;
            case "TD" : tempDisplay="table-cell";
               break;
            default : tempDisplay="block";
         }
         
         this.style.display=(this.style.display=="none" ? tempDisplay : "none");
      },
  
   toggleVisibility: function()
      {
         this.style.visibility=(this.style.visibility=="" || this.style.visibility=="visible" ? "hidden" : "visible");
      },
      
   disableSelection: function()
      {
         this.style.MozUserSelect="none";
         this.style.KhtmlUserSelect="none";
         this.unselectable="on";
      },
       
   enableSelection: function()
      {
         this.style.MozUserSelect="";
         this.style.KhtmlUserSelect="";
         this.unselectable="off";
      },
      
   center: function()
      {
         return this.setStyle({"position":"absolute", 
                               "top":((window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : (document.body ? document.body.clientHeight : 0)))/2-this.getHeight()/2 + "px"), 
                               "left":((window.innerWidth ? window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : (document.body ? document.body.clientWidth : 0)))/2-this.getWidth()/2 + "px")});
      },
      
   setOpacity: function(opacity)
      {
         this.style.filter="alpha(opacity:" + opacity*100 + ")"; // IE filter opacity:
         this.style.KHTMLOpacity=opacity; // Safari and Konqueror:
         this.style.MozOpacity=opacity; // Old Mozilla and Firefox:
         this.style.opacity=opacity; // CSS3 opacity for browsers that support it:
      },
      
   getOpacity: function()
      {
         return this.style.KHTMLOpacity | this.style.MozOpacity | this.style.opacity;
      },
      
   _getBorderWidthObj: function()
      {
         var _this=this;
         
         var t=((Browser.isOpera || Browser.isFF) ? parseInt(_this.style.borderTopWidth) : (Browser.isSafari ? parseInt(_this.style.getPropertyValue("border-top-width")) : parseInt(_this.style.border)));
         var l=((Browser.isOpera || Browser.isFF) ? parseInt(_this.style.borderLeftWidth) : (Browser.isSafari ? parseInt(_this.style.getPropertyValue("border-left-width")) : parseInt(_this.style.border)));
         var b=((Browser.isOpera || Browser.isFF) ? parseInt(_this.style.borderBottomWidth) : (Browser.isSafari ? parseInt(_this.style.getPropertyValue("border-bottom-width")) : parseInt(_this.style.border)));
         var r=((Browser.isOpera || Browser.isFF) ? parseInt(_this.style.borderRightWidth) : (Browser.isSafari ? parseInt(_this.style.getPropertyValue("border-right-width")) : parseInt(_this.style.border)));
         
         return {top: t,
                 left: l,
                 bottom: b,
                 right: r};
         
      },
      
   getInnerLeft: function()
      {
         var borderLeftWidth=this._getBorderWidthObj().left;
         
         return this.getLeft()+borderLeftWidth;
      },
      
   getInnerTop: function()
      {
         var borderTopWidth=this._getBorderWidthObj().top;
         
         return this.getTop()+borderTopWidth;
      },
      
   getInnerWidth: function()
      {
         var borderLeftWidth=this._getBorderWidthObj().left;
         var borderRightWidth=this._getBorderWidthObj().right;
         
         return this.getWidth()-borderLeftWidth-borderRightWidth;
      },
      
   getInnerHeight: function()
      {
         var borderTopWidth=this._getBorderWidthObj().top;
         var borderBottomWidth=this._getBorderWidthObj().bottom;
         
         return this.getHeight()-borderTopWidth-borderBottomWidth;
      },
      
   getLeft: function()
      {
         elem=this;
         
         var currentLeft=0;
         if(elem.offsetParent)
         {
            while(elem.offsetParent)
            {
               currentLeft+=elem.offsetLeft;
               elem=elem.offsetParent;
            }
         }
         else if(elem.x)
         {
            currentLeft+=elem.x;
         }
         
         return currentLeft;
      },
   
   getTop: function()
      {
         var currentTop=0;
         
         elem=this;

         if(elem.offsetParent)
         {
            while(elem.offsetParent)
            {
               currentTop+=elem.offsetTop;
               elem=elem.offsetParent;
            }
         }
         else if(elem.y)
         {
            currentTop+=elem.y;
         }

         return currentTop;
      },
      
   getWidth: function()
      {
         return this.offsetWidth;
      },
      
   getHeight: function()
      {
         return this.offsetHeight;
      },
  
   addClassName: function(tempClassName)
      {
         if(!this.classNameExists(tempClassName))
         {
            this.className+=" " + tempClassName;
            
            return true;
         }
         return false;
      },

   removeClassName: function(tempClassName)
      {
         return (this.replaceClassName(tempClassName, ""));
      },
  
   replaceClassName: function(oldClassName, newClassName)
      {
         if(this.classNameExists(oldClassName))
         {
            this.className=this.className.replace(oldClassName, newClassName);

            return true;
         }

         return false;
      },

   classNameExists: function(tempClassName)
      {
         var classArray=this.className.split(" ");

         for(var i=0; i<classArray.length; i++)
            if(new RegExp("(^|\\s)" + tempClassName + "(\\s|$)").test(classArray[i]))
               return true;

         return false;
      }
 };
 
Element.extend=function(element)
   {
      if(element && !element._extended)
      {
         Object.extend(element, Element.Methods);
         Object.extend(element, Event.Methods);
         
         if(element.tagName=="CANVAS")
         {
            Object.extend(element, Canvas.Methods);
         }
         
         element._extended=true;
      }
      
      return element;
   };
   
Object.extend(window, Event.Methods);
Object.extend(window, {resizeInnerTo: function(w, h){ this.resizeTo(w, h); this.resizeTo(w+(w-this.document.body.clientWidth), h+(h-this.document.body.clientHeight)); }});
Object.extend(document, Event.Methods);

//====================================================================================================
//= CB Object
//====================================================================================================
var locarray=window.location.href.split("/");
delete locarray[(locarray.length-1)];

window.isLoaded=false;

function ScriptFile(p)
{
   this._path=p;
}

var CB=
   {
      util:
         {
            cwd: locarray.join("/"),

            location: window.location.href,
         
            loadedFiles: new Array(),
         
            isLoaded: function(filePath)
               {
                  // Loop through files that were loaded using the script tag
                  //*** SHOULD DO THIS ONCE ON LOAD OF CORE, THEN ADD TO THE WHEN A NEW SCRIPT IS LOADED LIST
                  var scriptArray=document.getElementsByTagName("script");

                  for(var i=0; i<scriptArray.length; i++)
                     if(scriptArray[i].src==filePath)
                        return true;
                        
                  var index=this.getIndex(filePath);
                        
                  return (index>=0 ? true : false);
               },
            
            getIndex: function(filePath)
               {
                  // Loop through files that were added using the util framework functions
                  for(i=0; i<CB.util.loadedFiles.length; i++)
                     if(CB.util.loadedFiles[i]==filePath)
                        return i;

                  return -1;
               },
         
            load: function(filePath) // xBrow dynamic loader
               {
                  if(document.createElement) // Second best method through dom scripting
                  {
                     var script=document.createElement("script");
                     script.type="text/javascript";
                     script.src=filePath;
                     
                     document.getElementsByTagName("head")[0].appendChild(script);
                     
                     var scriptFileObject=new ScriptFile(filePath);

                     CB.util.loadedFiles[CB.util.loadedFiles.length]=Object.extend(scriptFileObject, Event.Methods);

                     return scriptFileObject;
                  }
                  else // Crappiest method, almost useless
                  {
                     document.write("<script type='text/javascript' src='" + filePath + "'></script>");

                     CB.util.loadedFiles[CB.util.loadedFiles.length]=filePath;

                     return true;
                  }

                  return false;
               }
         }
   };
   
   // ====================================================================================================
   //=These functions are $money$
   // ====================================================================================================
   function $id(el)
   {
      return (document.getElementById ? typeof el=="string" ? Element.extend(document.getElementById(el)) : Element.extend(el) : null);
   }
   
   function $tagName(tagName)
   {
      if(document.getElementsByTagName)
      {
         var els=document.getElementsByTagName(tagName);
         
         for(var i=0; i<els.length; i++)
            Element.extend(els[i]);
      }
      
      return els;
   }
   
   function $className(className)
   {
      var elementArray=document.getElementsByTagName("*");

      var returnElements=new Array();

      for(var i=0; i<elementArray.length; i++)
         if(Element.extend(elementArray[i]).classNameExists(className))
            returnElements.push(elementArray[i]);

      return returnElements;
   }
   
   function $selector(selector)
   {
      if(selector.charAt(0)==".")
         return $className(selector.replace(".", ""));
      else if(selector.charAt(0)=="#")
         return $id(selector.replace("#", ""));
      else
         return $tagName(selector);
   }
   
   var $=$id;
   var $$=$selector;