/**
 * Representation of the XBL bound element.
 * 
 * @author Mikko Pohja
 */

function BoundElement()
{
    var boundDOMElement;
    var originalBound;
    var boundStyle = "";
    var bindings = new Array();
    this.setElement = setElement;
    this.addBinding = addBinding;
    this.getElement = getElement;
    this.applyBindings = applyBindings;
    var parts; // URI fragments of the extends attribute
    var bindingDoc; // Inherited binding document
    
    function getElement()
    {
        return boundDOMElement;
    }
    
    function setElement(elem)
    {
        boundDOMElement = elem;
    }
    
    function addBinding(binding)
    {
        bindings.push(binding);
    }
    
    function applyImplementations()
    {
        
    }

    /**
     * Apply bindings for this bound element. Most derived binding
     * is added first.
     */    
    function applyBindings()
    {
        if (bindings.length > 0)
        {
            var binding = bindings.pop();
            
            if (binding.getElementsByTagNameNS("http://www.w3.org/ns/xbl", "template").item(0) != null)
            {            
                // Remove explicit children from bound
                removeBoundChildren();
            }
            
            // Add most derived binding 
            addShadowTree(binding);
            addContent();
            fetchStyle(binding);
            
            // Add inherited binding
            if (binding.hasAttribute("extends"))
            {
                addExplicitInheritance(binding);
            }
            
            // Add implicitly inherited bindings
            while (bindings.length > 0)
            {
                binding = bindings.pop();
                addImplicitInheritance(binding);

                // Evaluate content of the implementation element
                evalImpl(binding);
              
                // Add handlers
                addHandlers(binding);          
                fetchStyle(binding);
            }
        }
        
        // Remove remaining inherited elements
        var inheritedElements = boundDOMElement.getElementsByTagNameNS("http://www.w3.org/ns/xbl", "inherited");
        replaceInheritedElements(inheritedElements, 0);
        attachStyles();
    }
    
    function removeBoundChildren()
    {
        // Save clone of the bound. Children will replace the content elements. 
        originalBound = boundDOMElement.cloneNode(true);
        // Remove children of the bound element
        var content = boundDOMElement.childNodes;
        var removed = new Array();
        var i = 0;
        while (content.length > 0)
        {
            removed[i] = boundDOMElement.removeChild(content.item(0));
            i++
        }
    }

    function addShadowTree(binding)
    {
        // Add shadow tree to the bound element
        var template = binding.getElementsByTagNameNS("http://www.w3.org/ns/xbl", "template").item(0);
        if (template != null)
        {
            var tempClone = template.cloneNode(true);
            var templateChildren = tempClone.childNodes;
            while (templateChildren.length > 0)
            {
                boundDOMElement.appendChild(templateChildren.item(0))
            }
            addAtributes();
        }
        // Evaluate content of the implementation element
        evalImpl(binding);
      
        // Add handlers
        addHandlers(binding);          
    }

    /**
     * Process the xbl:attr attributes. Check every element in a shadow
     * tree for xbl:attr attribute and process them. 
     */ 
    function addAtributes()
    {
        var allElements = boundDOMElement.getElementsByTagName("*");
        for (var j = 0; j < allElements.length; j++)
        {
            var elem = allElements[j];
            var attr = elem.getAttribute("attr");
            if (attr != null)
            {
                var attributes = attr.split(" ");
                for (var i = 0; i < attributes.length; i++)
                {
                    var names = attributes[i].split("=");
                    if (names.length == 2)
                    {
                        if (names[0].indexOf(":text") >= 0)
                            createTextNodeFromAttr(elem, names[1]);
                        else if (names[1].indexOf(":text") >= 0)
                            createAttrFromText(elem, names[0]);
                        else
                            elem.setAttribute(names[0], boundDOMElement.getAttribute(names[1]));
                    }
                    else
                    {
                        var val = boundDOMElement.getAttribute(names[0]);
                        elem.setAttribute(names[0], val);
                    }
                }
            }
        }
    }
    
    function createTextNodeFromAttr(element, attr)
    {
        if (!element.hasChildNodes())
        {
            var textNode = document.createTextNode(boundDOMElement.getAttribute(attr));
            element.appendChild(textNode);
        }
    }
    
    function createAttrFromText(element, attr)
    {
        element.setAttribute(attr, originalBound.textContent);
    }

    function addContent()
    {
        // Replace content elements of the shadow tree
        var contentElems = boundDOMElement.getElementsByTagNameNS("http://www.w3.org/ns/xbl", "content");
      
        for (var l = 0; l < contentElems.length; l++)
        {  
            var contentElem = contentElems.item(l);
            var locked = contentElem.getAttribute("locked");
            if (locked == null || (locked != null && locked != "true"))
            {
                var contentParent = contentElem.parentNode;
                if (contentElem.hasAttribute("includes"))
                {
                    var selector = contentElem.getAttribute("includes");
                    //var included = getElementsBySelector(selector, originalBound);
                    var included = cssQuery(selector, originalBound);
                }
                else if (originalBound != null)
                {
                    var included = originalBound.childNodes;
                }
                if (included != null && included.length > 0)
                {
                    var contentChildren = contentElem.childNodes;
                    while (contentChildren.length > 0)
                    {
                        contentElem.removeChild(contentChildren[0]);
                    }
                
                    var count = included.length;
                    for (var g = 0; g < count; g++)
                    {
                        contentParent.insertBefore(included[0], contentElem);
                    }
                }
            }
        }
        // Remove remaining content elements.
        removeContentElements(contentElems);
    }

    function removeContentElements(contentElems)
    {
        while (contentElems.length > 0)
        {
            var contentElem = contentElems.item(0);
            var contentChildren = contentElem.childNodes;
            var contentParent = contentElem.parentNode;
            
            while (contentChildren.length > 0)
            {
                contentParent.insertBefore(contentChildren.item(0), contentElem);
            }
            contentParent.removeChild(contentElem);
        }
    }

    function addExplicitInheritance(binding)
    {
        var inheritedSet = boundDOMElement.getElementsByTagName("inherited");
        var inherited = inheritedSet.item(0);
      
        // Replace other inherited elements with their content
        replaceInheritedElements(inheritedSet, 1)
        if (inherited != null)
        {
            var inheParent = inherited.parentNode;
            var tempElem = document.createElementNS("myxblimpl", "temp");
            inheParent.replaceChild(tempElem, inherited);
            var uri = binding.getAttribute("extends");
            parts = uri.split("#");
            if (parts[0] != "")
            {
                bindingDoc = document.implementation.createDocument("", "", null);
                bindingDoc.onload = function () {setInherited(parts[1], inheParent)};
                bindingDoc.load(parts[0]);
            }
            else
            {
                var bindings = document.getElementsByTagNameNS("http://www.w3.org/ns/xbl", "binding");
                for (var i = 0; i < bindings.length; i++)
                {
                    if (parts[1] == bindings.item(i).getAttribute("id"))
                    {
                        // Evaluate content of the implementation element
                        evalImpl(bindings.item(i));
                        // Add handlers
                        addHandlers(bindings.item(i));
                        // Add style sheets
                        fetchStyle(bindings.item(i));
                        return addInheritedBinding(bindings.item(i), inheParent);
                    }
                }
            }
        }
    }
    

    function replaceInheritedElements(inheritedElements, firstElement)
    {
        while (inheritedElements.length > firstElement)
        {
            var inhe = inheritedElements.item(firstElement);
            var inheChildren = inhe.childNodes;
            var inheParent = inhe.parentNode;
            
            while (inheChildren.length > 0)
            {
              inheParent.insertBefore(inheChildren.item(0), inhe);
            }
            inheParent.removeChild(inhe);
        }
    }
    
    function addInheritedBinding(inheritedBinding, inheritedParent)
    {
        var template = inheritedBinding.getElementsByTagNameNS("http://www.w3.org/ns/xbl", "template").item(0);
        var tempClone = template.cloneNode(true);
        var contentElems = tempClone.getElementsByTagNameNS("http://www.w3.org/ns/xbl", "content");
        var tempElem = document.getElementsByTagNameNS("myxblimpl", "temp").item(0);
        
        var templateChildren = tempClone.childNodes;
        while (templateChildren.length > 0)
        {
            inheritedParent.insertBefore(templateChildren.item(0), tempElem);
        }
        inheritedParent.removeChild(tempElem);
        
        addContent();
        evalImpl(inheritedBinding);
        
        if (inheritedBinding.hasAttribute("extends"))
        {
            addExplicitInheritance(inheritedBinding);
        }
    }
    
    function addImplicitInheritance(binding)
    {
        var inheritedSet = boundDOMElement.getElementsByTagName("inherited");
        var inherited = inheritedSet.item(0);
        if (inherited != null)
        {
            var inheParent = inherited.parentNode;
            var tempElem = document.createElementNS("myxblimpl", "temp");
            inheParent.replaceChild(tempElem, inherited);
      
            // Replace other inherited elements with their content
            replaceInheritedElements(inheritedSet, 1)
            addInheritedBinding(binding, inheParent)
        }
    }

    function setInherited(uid, inheritedParent)
    {
        //var binding = bindingDoc.getElementById(uid);
        //addInheritedBinding(binding, inheritedParent);
        var bindings = bindingDoc.getElementsByTagName("binding");
        for (var i = 0; i < bindings.length; i++)
        {
            if (uid == bindings.item(i).getAttribute("id"))
                addInheritedBinding(bindings.item(i), inheritedParent);
        }
    }
    
    function evalImpl(binding)
    {
        if (binding.getElementsByTagName("implementation") != null && binding.getElementsByTagName("implementation").item(0) != null)
        {
            var implElem = binding.getElementsByTagName("implementation").item(0);
            if (implElem.hasAttribute("src"))
            {
                // Using XHR to read the file.
                var xmlhttp;
                if (window.XMLHttpRequest)
                { // code for Mozilla, Safari, ** And Now IE 7 **, etc
                    xmlhttp=new XMLHttpRequest();
                }
                // code for IE
                else if (window.ActiveXObject)
                {
                    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP")
                }
                if (typeof(xmlhttp)=='object')
                {
                    xmlhttp.open('GET', implElem.getAttribute("src"), false);
                    xmlhttp.send(null);
                }
                var functions = eval(xmlhttp.responseText);
                for(var p in functions)
                    boundDOMElement[p] = functions[p];
            }
            else
            {
                var funks = eval(implElem.textContent);
                for(var p in funks)
                    boundDOMElement[p] = funks[p];
            }
        }
    }
    
    function addHandlers(binding)
    {
        var handlersElement = binding.getElementsByTagName("handlers").item(0);
        if(handlersElement != null)
        {
            var handlers = handlersElement.getElementsByTagName("handler");
            
            for (var i = 0; i < handlers.length; i++)
            {
                addHandler(handlersElement, handlers[i]);
            }
        }
    }

    function addHandler(handlersElement, handler)
    {
        var ev = handler.getAttribute("event");
        if (ev.substring(0,2) == "on")
            ev = ev.substring(2);
            
        var code = handler.firstChild.nodeValue;
        
        var capture = false;
        if (handler.getAttribute("phase") == "capture")
            capture = true;
            
        var trusted = false;
        if (handler.getAttribute("trusted") == "true")
            trusted = true;
            
        var buttons = "";
        if (handler.getAttribute("button") != null)
            buttons = handler.getAttribute("button");

        var clicks = "";
        if (handler.getAttribute("click-count") != null)
            clicks = handler.getAttribute("click-count");

            
        function tempFunc(event)
        {
            var runCode = true;
            if (trusted)
            {
                if (!event.isTrusted)
                    runCode = false;
            }
            
            if (buttons != "")
            {
                var bArray = buttons.split(" ");
                while (bArray.length > 0)
                {
                    var token = bArray.pop();
                    if (token != event.button)
                        runCode = false;
                    else if (token == event.button)
                    {
                        runCode = true;
                        break;
                    }
                }
            }
                
            if (clicks != "")
            {
                var cArray = clicks.split(" ");
                while (cArray.length > 0)
                {
                    var token = cArray.pop();
                    if (token != event.detail)
                        runCode = false;
                    else if (token == event.detail)
                    {
                        runCode = true;
                        break;
                    }
                }
            }
                
            if (runCode)
                eval(code);
        }
                
        boundDOMElement.addEventListener(ev, tempFunc, capture);
    }

    function fetchStyle(binding)
    {
        var styleElems = binding.getElementsByTagName("style");
        if (styleElems.length > 0)
        {
            for (var p = 0; p < styleElems.length; p++)
            {
                var sElem = styleElems[p];
                if (sElem.hasAttribute('src'))
                {
                    // Using XHR to read the file.
                    var xmlhttp;
                    if (window.XMLHttpRequest)
                    { // code for Mozilla, Safari, ** And Now IE 7 **, etc
                        xmlhttp=new XMLHttpRequest();
                    }
                    // code for IE
                    else if (window.ActiveXObject)
                    {
                        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP")
                    }
                    if (typeof(xmlhttp)=='object')
                    {
                        xmlhttp.open('GET', sElem.getAttribute("src"), false);
                        xmlhttp.send(null);
                    }
                    boundStyle += xmlhttp.responseText;
                }
                else
                boundStyle += sElem.firstChild.textContent;
            }
            boundStyle.fulltrim();
        }
    }
    
    function attachStyles()
    {
        var declarations = boundStyle.split('}');
        for (var i = 0; i < declarations.length; i++)
        {
            var parts = declarations[i].split('{');
            var selector = parts[0].fulltrim();
            var property = parts[1];
            //alert(property);
            
            if (selector != "" && property != "")
            {
                // Apply to bound element first ...
                var elems = cssQuery(selector, boundDOMElement.parent);
                for (var j = 0; j < elems.length; j++)
                {
                    var el = elems[j];
                    if (el == boundDOMElement)
                    {
                        if (el.hasAttribute("style"))
                            el.setAttribute("style", el.getAttribute("style") + property);
                        else
                            el.setAttribute("style", property);
                    }
                }
                
                // ... and then to the ancestors.
                var elems = cssQuery(selector, boundDOMElement);
                for (var j = 0; j < elems.length; j++)
                {
                    var el = elems[j];
                    if (el.hasAttribute("style"))
                        el.setAttribute("style", el.getAttribute("style") + property);
                    else
                        el.setAttribute("style", property);
                }
            }
        }
    }
}
