// ---------------------------------------
//
// dL library. jQuery-like library with stuff
//
// ---------------------------------------
    
var dL = (function namespace() {
    
// ---------------------------------------
//
// the dL Object
//
// ---------------------------------------    


function dL(query) {     
    if (arguments[1] !== "noRecurse")                                       // this way the constructor can be used without "new";    
        return new dL(query, "noRecurse");                                  // can't be done with apply() because of the "new" argument

    if (!query)
        return;   

    this.nodes = [];   
    
    // first, check for type: "#id"; this.nodes will contain a single node; a node will be created if it does not exist;
    if (typeof query !== "string")                                              // just wrap a dL object around the node if a node is passed as argument
         if(query.nodeType) {             
            this.nodes = [];
            this.nodes[0] = query;    
            return this;
    } 

    if (query.match(/#/)) {                                               
        
        query = query.match(/[^#]+/);
        this.nodes[0] = document.getElementById(query);       
        if (!this.nodes[0]) {                                               
            this.nodes[0] = document.createElement("div");
            document.body.appendChild(this.nodes[0]);
            this.nodes[0].id = query;  
            
        }   
        
    
    // else, check for ".class" type selector; 

    } else if (query.match(/\./)) { 
        query = query.match(/[^\.]+/);    
                       
        this.nodes = document.getElementsByClassName(query);                        
        
    // else, check for HTMLtag; 
    
    } else if (query.match(/<\w+>/)) {   
        query = query.match(/[^<>]+/);        
        this.nodes = document.getElementsByTagName(query);
    }   
    this.query = query;  
    
}                                

dL.prototype.toString = function () {           
    return "[object dL]";            
}    

dL.prototype.appendAndGetChild = function (element) {
    for (var i = 0; i < this.nodes.length; i++) {
        var thisNode = this.nodes[i];
        var childElement = document.createElement(element);    
        thisNode.appendChild(childElement);
        this.nodes[i] = childElement;
    }
    return this;
}

dL.prototype.html = function () {
    for (var j = 0; j < this.nodes.length; j++) {
        for (var i = 0; i < arguments.length; i++) {
           if (typeof arguments[i] === "object") 
                  for (var property in arguments[i]) {
                        this.nodes[j][property] = arguments[i][property]
                   } 
         } 
    }
    return this;
}

dL.prototype.style = function () {                // expects an object with css-properties as parameter       
    if (this.nodes) {
        for (var j = 0; j < this.nodes.length; j++) {        
            for (var i = 0; i < arguments.length; i++) {                  
                if (typeof arguments[i] === "object")         // everything that's passed as an object, is assumed to be a style-declaration        
                    for (var property in arguments[i]) {                    
                        if (property === "opacity")
                            dL.cssSetOpacity(this.nodes[j], arguments[i].opacity)
                        else if (dL.isCSSProperty(property))
                            this.nodes[j].style[property] = arguments[i][property] + dL.cssSuffix(property);                                            
                    }
            }            
        }
    }
    return this;        
}  

dL.prototype.getStyle = function (property) {              
var node = this.nodes[0];

if (node.style[property])
           var value = this.nodes[0].style[property];    
    if (!value) {
        value = dL.getComputedStyle(this.nodes[0], property);
    }
    if (!value && this.getStyle("display") === "none" && ( property === "height" || property === "width")) {
          // hack to get the height or width if display is set to none;              
          var oldPosition = this.nodes[0].style.position;
          var oldVisibility = this.nodes[0].style.visibility;
          this.style({position: "absolute", visibility : "hidden" , display: "block"});                  
          value = this.getStyle(property);          
          this.style({position: oldPosition, visibility: oldVisibility, display: "none"});
    }   
    if (typeof value === "string" && value.match(/px/))
              value = Number(value.match(/\d+/));              
    return value; 
}


// external events

dL.prototype.addEventListener = function (event, listener) {    
    if (!this.eventListeners)
        this.eventListeners = [];
    this.eventListeners.push({"event": event, "listener": listener})   // keep a list of listeners to remove if necessary;
    for (var i = 0; i < this.nodes.length; i++)
        dL.addEventListener(event, this.nodes[i], listener);
    return this;
}

dL.prototype.removeEventListener = function (event, listener) {    
    for (var i = 0; i < this.nodes.length; i++)
        dL.removeEventListener(event, this.nodes[i], listener);    
    for (var i = 0; i < this.eventListeners.length; i++)
        if (this.eventListeners[i].event === event && this.eventListeners[i].listener === listener)
                this.eventListeners.splice(i, 1);
    return this;
}

dL.prototype.removeAllEventListeners = function (event) {        
    for (var j = 0; j < this.nodes.length; j++)
       if (this.eventListeners) {
            for (var i = 0; i< this.eventListeners.length; i++) {
                if (event) 
                    if (this.eventListeners[i].event !== event)
                        continue;
                dL.removeEventListener(this.eventListeners[i].event, this.nodes[j], this.eventListeners[i].listener);                
            }
       }
   
}


// internal events

dL.prototype.addInternalEventListener = function (event, listener) {
    if (!this.internalEventListeners)
        this.internalEventListeners = [];
    this.internalEventListeners.push({"event": event, "listener": listener})   // keep a list of listeners to remove if necessary;
    return this;
}

dL.prototype.removeInternalEventListener = function (event, listener) {
    for (var i = 0; i < this.internalEventListeners.length; i++) 
        if (this.internalEventListeners[i].event === event && this.internalEventListeners[i].listener === listener) {
            this.internalEventListeners.splice(i,1);
            return true;
        }
    return false;        
}

dL.prototype.removeAllInternalListeners = function () {
    if (this.internalEventListeners)
        this.internalEventListeners = null;
}
    
dL.prototype.broadcastInternalEvent = function (event) {     
    if (this.internalEventListeners)
        for (var i = 0; i < this.internalEventListeners.length; i++)
            if (this.internalEventListeners[i].event === event)
                this.internalEventListeners[i].listener();
}


dL.prototype.removeNodes = function () {
    for (var i = 0; i < this.nodes.length; i++) {
        var parentNode = this.nodes[i].parentNode;
        if (parentNode !== null)
            parentNode.removeChild(this.nodes[i]);
    }
    return this;        
}

// ------------------------------------
//
// public (static) helper functions and constants
//
// ------------------------------------

dL.isRelativeValue = function (value) {        
    if (typeof value === "string" && value.match(/\+\=|\-\=/))     
        return true;                   
    return false;                                                           
}

dL.convertRelativeValue = function (value) {        
    if (typeof value !== "string" || value.match(/\+\=|\-\=/) === null)
        return;
    var sign = -1;
    if (value.match(/\+\=/) !== null) 
        sign = 1;     
    //alert(value.match(/\d+/));
    return value.match(/\d+/) * sign;
}

dL.isHTMLTag = function (tag) {
    if (tag === "body" || tag === "h1")
        return true;
    return false;
}

dL.isEvent = function (event) { 
    if (dL.events.indexOf(event) !== -1)                                       // note: hover is an event not native to JS or CSS
        return true;
    return false;
}

dL.isCSSProperty = function (property) {
    if (dL.cssProperties.indexOf(property) !== -1)
         return true;
    return false;
}   

dL.isCSSNumericProperty = function (property) {
    if (dL.pxSuffixProperties.indexOf(property) !== -1)                        // if it has a px suffix, it's a numeric value (?)
        return true;
    return false;            
}

dL.cssSuffix = function (property) {
    if (dL.pxSuffixProperties.indexOf(property) !== -1)
        return "px";
    return "";
} 

dL.cssSetOpacity = function (node, value) {        
    if (dL.isIE7)
        node.style.filter = "alpha(opacity=" + Math.round(100 * value) + ")"; // IE7 style opacity;
    else
        node.style.opacity = value;
}

dL.cssToHexColor = function (value) {                                 // expects object with r,g and b values;    
    if (typeof value === "number") {        
        value = {r: parseInt(value / 65536),
                 g: parseInt((value % 65536) / 256),
                 b: parseInt((value % 65536 % 256))};            
    }    
    var color ="#";
    for (var component in value) {
        if (value[component] < 16)
            color += "0";        
        color += value[component].toString(16);
    }           
    return color;    
};

dL.cssToRGBColor = function (value) {                                 // expects "#RRGGBB" or rgb(r,g,b) style colors;        
    if (typeof value === "string") {
        if (value.match(/rgb/)) {                 
            var components = value.match(/\d+/g);                                    
            var color = {r: +components[0], g: +components[1], b: +components[2]};              // converts to numbers            
        } else {
            var color = {r: parseInt("0x" + value.slice(1,3)), g: parseInt("0x" + value.slice(3,5)), b: parseInt("0x" + value.slice(5,7))};                
        }
        return color; 
    }
};

dL.getStyle = function (node, property) {
               var value = node.style[property];               
               if (!value) {
                    value = dL.getComputedStyle(node, property);
               }               
               if (typeof node.style.display !== String && ( property === "height" || property === "width")) {                      
                      var oldPosition = node.style.position;
                      var oldVisibility = node.style.visibility;
                      node.style.position = "absolute"; node.style.visibility = "hidden"; node.style.display = "block";                      
                      value = node.style[property];
                      if (!value) 
                            value = dL.getComputedStyle(node, property);
                      node.style.position = oldPosition; node.style.visibility = oldVisibility; node.style.display = "none";
               }     
               if (typeof value === "string" && value.match(/px/))
                          value = Number(value.match(/\d+/));                         
               return value; 
}

dL.getComputedStyle = function(node, property) {
    var value;
    if (window.getComputedStyle)  // all browser except IE8 and below {
                value = window.getComputedStyle(node)[property];
    else if (node.currentStyle) {
          value = node.currentStyle[property];
         
    }
    // is value set to auto? 
    if (value === "auto") {
        if (property === "height" || property === "width")
            property = "offset" + property.replace(/\w/, function(letter) { return letter.toUpperCase(); });            
            value = node[property];
    }    
    return value;    
}         

dL.addEventListener = function (event, node, method) {  
    if (!dL.isEvent(event)) {
        alert("Can't add listener: " + event + " is not an event!");
        return;
    }
    
    // hover, mouseenter and mouseleave don't exist at DOM-level in all browsers;    
    if (event === "hover") {
        //console.debug(event);
        method.mouseHoverListener = function (event) { mouseHoverListener(event, node, method); };
        dL.addEventListener("mouseenter", node, method.mouseHoverListener)
        dL.addEventListener("mouseleave", node, method.mouseHoverListener);        
        return;
    }    
    else if (event === "mouseenter") { 
        method.mouseOverListener = function (event) {mouseOverListener(event, node, method);};    // create unique mouseOutHandler            
        addEventListener("mouseover", node, method.mouseOverListener);
        
    }    
    else if (event === "mouseleave") {                                      
        method.mouseOutListener = function (event) {mouseOutListener(event, node, method);};
        addEventListener("mouseout", node, method.mouseOutListener);
    }        
    else                
        addEventListener(event, node, method);
}

dL.removeEventListener = function (event, node, method) {        
    
    //hover, mouseenter and mouseleave don't exist at DOM-level in all browsers;
    
    if (event === "hover") 
        if (method.mouseHoverListener) {            
            dL.removeEventListener("mouseenter", node, method.mouseHoverListener)
            dL.removeEventListener("mouseleave", node, method.mouseHoverListener);
            return;
    }
    
    if (event === "mouseenter") {
        if (method.mouseOverListener)             
            removeEventListener("mouseover", node, method.mouseOverListener);        
    }
    else if (event === "mouseleave") {
        if (method.mouseOutListener)            
            removeEventListener('mouseout', node, method.mouseOutListener);        
    }
    else 
        removeEventListener(event, node, method);        
}

dL.detectBrowser = function () {    
    var agent = navigator.userAgent.toLowerCase();
    dL.isIE7 = agent.indexOf("msie 7.0") > -1;
    dL.isIE8 = agent.indexOf("msie 8.0") > -1;   
}

// ----------------------------------------------
//
// variables
//
// ----------------------------------------------

// ----------------------------------------------
//
// constants
//
// ----------------------------------------------

dL.cssProperties = ["position", "width", "height", "left", "top", "color", "background", "backgroundColor",  "backgroundImage", 
    "borderRadius", "borderTopLeftRadius", "borderTopRightRadius", "opacity", "overflow", "overflowX", "overflowY", "margin", 
    "marginLeft", "marginRight", "display", "visibility", "float",
    "border", "font", "fontSize", "verticalAlign", "padding", "paddingBottom"];
dL.pxSuffixProperties = ["width", "height", "left", "top", "borderRadius", "borderTopLeftRadius", "borderTopRightRadius", 
    "margin", "marginLeft", "marginRight", "padding", "paddingBottom"];
dL.events = ["load", "click", "mousemove", "mouseenter", "mouseleave", "mouseover", "mouseout", "hover", "keydown",
    "keypress", "keyup", "submit", "change", "scroll"];


//dL.mouseX = dL.mouseY = 0;

// ----------------------------------------------
//
// private helper functions
//
// ----------------------------------------------
 
function addEventListener(event, node, method) {
    if (window.addEventListener) 
        node.addEventListener(event, method, false);
    else if (window.attachEvent) // IE
        node.attachEvent('on' + event, method);         
}

function removeEventListener(event, node, method) {
    if(window.removeEventListener)
        node.removeEventListener(event, method, false);
    else if (window.detachEvent)
       node.detachEvent('on' + event, method);      
}

function mouseOverListener (event, node, method) {                           // helper functions for mouseenter and mouseleave events         
    var fromElement = event.relatedTarget ? event.relatedTarget : event.fromElement;    
    if (!contains(node, fromElement))           
        method.call(node, event);     
}

function mouseOutListener(event, node, method){        
    var toElement = event.relatedTarget ? event.relatedTarget: event.toElement;
    if (!contains(node, toElement))                 
        method.call(node, event);                
}

function mouseHoverListener(event, node, method) {        
    method.call(node, event);    
}

//function switchClickListener (event, node, methodOn, methodOff) {            // switchClick works like a on/off switch;  
//    node.On = !node.On;
//
//    if (node.On)
//        methodOn.call(node, event);
//    else
//        if (methodOff)
//            methodOff.call(node, event);
//}

function contains(node, descendant) {                                       //  helper function to see if a node contains a descendant
    if (node.contains)             
        return (node.contains(descendant))        
    else if (node.compareDocumentPosition)                                  // firefox
        return (!!((node.compareDocumentPosition(descendant) & 0x10) || node === descendant));
}


    
// -------------------------------------------
//
// code that needs to run
//
// -------------------------------------------






// IE 7.0 and 8.0 don't support indexOf(), so add it manually; from a Stack Overflow answer

if (!Array.prototype.indexOf)         
    Array.prototype.indexOf = function(object, start) {
        for (var i = (start || 0), j = this.length; i < j; i++) {
            if (this[i] === object) {return i;}
        }
        return -1;
    } 
 
// IE 7.0 and 8.0 don't support getElementsByClassName so I created my own crappy method

if (!document.getElementsByClassName)        
          document.getElementsByClassName = function (className) {                       
                var matchingElements = [];                
                var allElements = document.getElementsByTagName("*");                
                var regEx = new RegExp( className);               
                for (var i = 0; i < allElements.length; i++) {
                      if (allElements[i].className.match(regEx)) {
                                    matchingElements.push(allElements[i]);                                    
                       }
          }             
          return matchingElements;
}

// last() returns the last element of an array. Trying if this is quicker "than array[array.length - 1]"

if (!Array.prototype.last)
    Array.prototype.last = function() {        
        return this[this.length - 1];
    }    


// shouldn't put this here, but where?

if (!window.concat)
    window.concat = function (object1, object2) {
        for (var property in object2)
            object1[property] = object2[property]
        return object1;
    }

// to stop IE7 error reports if console is not defined;

    
// ------------------------------
//
// return dL 
//
// ------------------------------

return dL;

}()); // namespace;

dL.addEventListener("mousemove", window, function (event) { dL.mouseX = event.clientX; dL.mouseY = event.clientY; });        
dL.detectBrowser();








