﻿$namespace("web.w3.dom");

// DOM compat type, configurable at runtime.
// programmer can program targeting either IE or Mozilla without consider
// compatility issues.
__web_w3_dom.CompatType = Enum.create(
    "IE_Mozilla",   // IE to Mozilla compatibility
    "Mozilla_IE");  // Mozilla to IE compatibility, default

__web_w3_dom.compat = new function()
{
    var _type = __web_w3_dom.CompatType.Mozilla_IE; // default type
    
    this.getType = function() { return _type; }
    this.setType = function() { _type = value; }
    
    function _establish_mozilla_ie_compat(w)
    {
        function get_xml() 
        { 
            return (new XMLSerializer()).serializeToString(this); 
        }
        
        function get_scripts() 
        { 
            return document.getElementsByTagName("script"); 
        }
        
        function get_all()
        { 
            var _this = this; 
            var _all = document.getElementsByTagName("*"); 
            _all.tags = function(tagName) { return _this.getElementsByTagName(tagName); }
            return _all;
        }
        
        function get_selection()
        {
            //TODO: implements section APIs.
        }
        
        var _emptyTags = {
                            "IMG":   true,
                            "BR":    true,
                            "INPUT": true,
                            "META":  true,
                            "LINK":  true,
                            "PARAM": true,
                            "HR":    true
                           };

        function get_element_all()
        {
            return this.getElementsByTagName("*");
        }
        
        function get_outerHTML()
        {
            if(typeof(this.tagName) == 'undefined') return this.data;
            var html = "<" + this.tagName;
            var attrs = this.attributes;
            for(var i = 0; i < attrs.length; i ++)
            {
                if(i > 0) html += " ";
                html += (attrs[i].name + "=\"" + attrs[i].value + "\"");
            }
            if(_emptyTags[this.tagName]) html += " />";
            else html += (">" + this.innerHTML + "></" + this.tagNmae + ">");
            
            return html;
        }
        
        function set_outerHTML(sHTML)
        {
            var range = this.ownerDocument.createRange();
            range.setStartBefore(this);
            var documentFragment = range.crateContextualFragment(sHTML);
            this.parentNode.replaceChild(documentFragment, this);
        }
        
        function get_innerText()
        {
            var range = this.ownerDocument.createRange();
            range.selectNodeContents(this);
            return range.toString();
        }
        
        function set_innerText(text)
        {
            if(!text) text = "";
            this.innerHTML = text.replace(/\&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g, "&gt;");
        }
        
        function get_outerText()
        {
            var _text = "";
            for(var i = 0; i < this.childNodes.length; i++)
            {
                if(this.childNodes[i].nodeType == __web_w3_dom.NodeType.TEXT_NODE)
                {
                    _text += this.childNodes[i].data;   
                }
                else
                {
                    _text += this.childNodes[i].outerText;
                }
            }
            return _text;
        }
        
        function set_outerText(sText)
        {
            if(!sText) sText = "";
            this.outerHTML = sText.replace(/\&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g, "&gt;");
        }
        
        function get_children()
        {
            var _children = [];
            
            for(var i = 0; i < this.childNodes; i++)
            {
                if(this.childNodes[i].nodeType != __web_w3_dom.NodeType.ELEMENT_TYPE) continue;
                _children.push(this.childNodes[i]);
                var name = this.childNodes[i].name;
                if(name) {
                    if(!_children[name]) _children[name] = [];
                    _children[name].push(this.childNodes[i]);
                }
                if(this.childNodes[i].id) 
                    _children[id] = this.childNodes[i];
            }
            
            return _children;
        }
        
        function get_parentElement()
        {
            return __web_w3_dom.getNonTextNode(this.parentNode);
        }
        
        function get_uniqueID()
        {
            if(!arguments.callee.__idGen) arguments.callee.__idGen = 0;
            var uniqueId = "moz_compat_id" + arguments.callee.__idGen ++;
            w[uniqueId] = this;
            this.__defineGetter__("uniqueID", function() { return uniqueId; });
            return uniqueId;
        }
        
        function CurrentStyle(el)
	    {
		    var _properties = ["Top","Left","Right","Bottom"];
		    var _objComputedStyle = document.defaultView.getComputedStyle(el,null);
		    for (var i=0;i<_properties.length;i++)
		    {
			    this["border" + _properties[i] + "Width"] = _objComputedStyle.getPropertyValue("border-" + _properties[i] + "-width")
			    this["margin" + _properties[i]] = _objComputedStyle.getPropertyValue("margin-" + _properties[i])
			    this["padding" + _properties[i]] = _objComputedStyle.getPropertyValue("padding-" + _properties[i])
		    }
		    
		    this.position = _objComputedStyle.getPropertyValue("position");
		    this.height = _objComputedStyle.getPropertyValue("height");
		    this.width = _objComputedStyle.getPropertyValue("width");
		    this.zIndex = _objComputedStyle.getPropertyValue("z-index");
		    this.cursor = _objComputedStyle.getPropertyValue("cursor");
		    this.left = _objComputedStyle.getPropertyValue("left");
		    this.top = _objComputedStyle.getPropertyValue("top");
	    }
	
	    function get_currentStyle()
	    {
	        return new CurrentStyle(this);
	    }
	    
	    function get_runtimeStyle()
	    {
	        // TODO: should verify if it's already implemented by Mozilla.
	        // return document.defaultView.getOverrideStyle(this, null);
	        return this.style;
	    }
	    
	    function get_ownerDocument()
	    {
	        alert("hey, get owner document");
	        return w.document;
	    }
	    
	    function _setupEvent(evObj) 
	    {
	        window.event = evObj; 
	    }
	    
	    function _checkEnter(el)
	    {
	        return function(evObj)
	        {
	            if (!this.contains(event.fromElement))
		        {
			        var oEvt = document.createEvent("MouseEvents");
			        oEvt.initEvent("mouseenter",false,false);
			        el.dispatchEvent(oEvt);
		        }
		    }
	    }
	    
	    function _checkLeave(el)
	    {
	        return function(evObj)
	        {
	            if (!this.contains(event.toElement))
		        {
			        var oEvt = document.createEvent("MouseEvents");
			        oEvt.initEvent("mouseleave",false,false);
			        el.dispatchEvent(oEvt);
		        }
		    }
	    }
	    
	    function _attachEvtWrap(el, evType, handler)
        {
            evType = evType.substring(2);
            switch(evType)
            {
                case "mousewheel": 
                    evType = "DOMMouseScroll"; 
                    break;
                case "mouseenter":
                    el.addEventListener("mouseover", _setupEvent, true); // setup event in capture phase
                    el.addEventListener("mouseover", _checkEnter(el), false);
                    break;
                case "mouseleave":
                    el.addEventListener("mouseout", _setupEvent, true); // setup event in capture phase
                    el.addEventListener("mouseout", _checkLeave(el), false);
                    break;
                case "propertychange":
                    el.addEventListener("keyup", _setupEvent, true); 
                    el.addEventListener("change", _setupEvent, true);
                    break;
                default:
                    el.addEventListener(evType, _setupEvent, true); // setup event in capture phase
                    break;
            }
            
            if(evType == "propertychange")
            {
                el.addEventListener("keyup", handler, false); 
                el.addEventListener("change", handler, false);
            }
            else
            {
                el.addEventListener(evType, handler, false);
            }
        }
	   
	    function _detachEvtWrap(el, evType, handler)
	    {
	        evType = evType.substring(2);
	        switch(evType)
            {
                case "mousewheel": 
                    evType = "DOMMouseScroll"; 
                    break;
                default:break;
            }
            el.removeEventListener(evType, handler, false);
	    }
	    
	    function attachEvent(evType, handler)
	    {
	        _attachEvtWrap(this, evType, handler);
	    }
	    
	    function detachEvent(evType, handler)
	    {
	        _detachEvtWrap(this, evType, handler);
	    }
	    
	    // TODO: need test
	    function createEventObject()
	    {
	        return document.createEvent("Events");
	    }
	    
	    // TODO: need test
	    function fireEvent(name, ev)
	    {
	        if(!ev) ev = this.createEventObject();
	        var evType = name.slice(2);
	        if("mousewheel" == evType) evType = "DOMMouseScroll";
	        ev.initEvent(evType, false, false);
	        this.dispatchEvent(ev);
	        if(typeof(this[name]) == "function") this[name](ev);
	        else if(this.getAttribute(name)) eval(this.getAttribute(name));
	    }
	    
	    /***************************************************************************
         * Compatibilty for Window
         ***************************************************************************/
         
	    w.attachEvent = attachEvent, w.detachEvent = detachEvent;
	    
	    /***************************************************************************
         * Compatibilty for *Document (HTMLDocument, XMLDocument)
         ***************************************************************************/
         
        w.HTMLDocument.prototype.__defineGetter__("xml", get_xml);
        w.HTMLDocument.prototype.__defineGetter__("scripts", get_scripts);
        w.HTMLDocument.prototype.__defineGetter__("all", get_all);
        w.HTMLDocument.prototype.__defineGetter__("selection", get_selection);
        
        w.HTMLDocument.prototype.attachEvent = attachEvent;
        w.HTMLDocument.prototype.detachEvent = detachEvent;
        w.HTMLDocument.prototype.createEventObject = createEventObject;
        
        w.XMLDocument.prototype.transformNodeToObject = function(objXsl)
        {
            var objXsltProcessor = new XSLTProcessor();
            objXsltProcessor.importStyleSheet(objXsl);
            var documentFragment = document.implementation.createDocument("", "", null);
            return objXsltProcessor.transformToFragment(this, documentFragment);
        }
        
        /***************************************************************************
         * Compatibilty for HTMLElement
         ***************************************************************************/
         
        w.HTMLElement.prototype.__defineGetter__("uniqueID", get_uniqueID);
        w.HTMLElement.prototype.__defineGetter__("all", get_element_all);
        w.HTMLElement.prototype.__defineGetter__("outerHTML", get_outerHTML);
        w.HTMLElement.prototype.__defineSetter__("outerHTML", set_outerHTML);
        w.HTMLElement.prototype.__defineGetter__("innerText", get_innerText);
        w.HTMLElement.prototype.__defineSetter__("innerText", set_innerText);
        w.HTMLElement.prototype.__defineGetter__("outerText", get_outerText);
        w.HTMLElement.prototype.__defineSetter__("outerText", set_outerText);
        w.HTMLElement.prototype.__defineGetter__("children", get_children);
        w.HTMLElement.prototype.__defineGetter__("parentElement", get_parentElement);
        w.HTMLElement.prototype.__defineGetter__("currentStyle", get_currentStyle);
        w.HTMLElement.prototype.__defineGetter__("runtimeStyle", get_runtimeStyle);
//        w.HTMLElement.prototype.__defineGetter__("ownerDocument", get_ownerDocument);
        
        w.HTMLElement.prototype.attachEvent = attachEvent;
        w.HTMLElement.prototype.detachEvent = detachEvent;
        w.HTMLElement.prototype.createEventObject = createEventObject;
        w.HTMLElement.prototype.fireEvent = fireEvent;
        
        w.HTMLElement.prototype.removeNode = function()
        {
            return this.parentNode.removeChild(this);
        }
        
        w.HTMLElement.prototype.contains = function(oElement)
        {
            if(this == oElement) return true;
            if(null == oElement) return false;
            return this.contains(oElement.parentElement);
        }
        
        w.HTMLElement.prototype.insertAdjacentHTML = function(sWhere, sHTML)
        {
            var documentFragment = null;
            var range = this.ownerDocument.createRange();
            switch(String(sWhere).toLowerCase())
            {
                case "beforebegin":
                    range.setStartBefore(this); // move to before this element
                    documentFragment = range.createContextualFragment(sHTML);
                    this.parentNode.insertBefore(documentFragment, this);
                    break;
                case "afterbegin":
                    range.selectNodeContents(this);
                    range.collapse(true); // move to before the 1st element of this element
                    documentFragment = range.createContextualFragment(sHTML);
                    this.insertBefore(documentFragment, this.firstChild);
                    break;
                case "beforeend":
                    range.selectNodeContents(this);
                    range.collapse(false); // move to before end of 1st element of this element
                    documentFragment = range.createContextualFragment(sHTML);
                    this.appendChild(documentFragment);
                    break;
                case "afterend":
                    range.setStartAfter(this); // move to end of this element
                    documentFragment = range.createContextualFragment(sHTML);
                    if(this.nextSibling) this.parentNode.insertBefore(documentFragment, this.nextSibling);
                    else this.parentNode.appendChild(documentFragment);
                    break;
                default: throw "Invalid Argument"; break;
            }
        }
        
        w.HTMLElement.prototype.insertAdjacentElement = function (sWhere,oElement)
	    {
		    switch (String(sWhere).toLowerCase())
		    {
			    case "beforebegin":
				    this.parentNode.insertBefore(oElement,this);
				    break;
			    case "beforeend":
				    this.appendChild(oElement);
				    break;
			    case "afterbegin":
				    this.insertBefore(oElement,this.firstChild);
				    break;				
			    case "afterend":
				    if (this.nextSibling) this.parentNode.insertBefore(oElement,this.nextSibling);
				    else this.parentNode.appendChild(oElement);
				    break;
			    default: throw "Invalid Argument"; break;
		    }
	    }
	
        w.HTMLElement.prototype.insertAdjacentText = function(sWhere, sText)
        {
            this.insertAdjacentHTML(sWhere, 
                sText.replace(/\&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g, "&gt;")
                );
        }
        
        /***************************************************************************
         * Compatibilty for CSSStyleDeclaration
         ***************************************************************************/
         
        function get_pixelStyleProperty(propertyType)
        {
            return function() { return this["pixel" + propertyType]; }
        }
        
        function set_pixelStyleProperty(propertyType)
        {
            return function(value) { this[propertyType.toLowerCase()] = value + "px"; }
        }
        
        // pixelLeft, pixelTop, pixelWidth, pixelHeight
        ["Left", "Top", "Width", "Height"].forEach(function(propertyType) {
            w.CSSStyleDeclaration.prototype.__defineGetter__("pixel" + propertyType, get_pixelStyleProperty(propertyType));
            w.CSSStyleDeclaration.prototype.__defineSetter__("pixel" + propertyType, set_pixelStyleProperty(propertyType));
        });
        
        /***************************************************************************
         * Compatibilty for Events
         ***************************************************************************/
         
        function StopEvtPropagation(ev)
        {
            ev.preventDefault();
            ev.stopPropagation();
        }
        
        function setCapture(ev)
        {
            if(!m_capturingTarget) return false;
            
            ev.preventDefault();
            
            document.removeEventListener("mousemove", setCapture, true);
            var evObj = document.createEvent("MouseEvents");
            evObj.initMouseEvent(
                ev.type, ev.bubbles, ev.cancelable, // Events Args
                ev.view, ev.detail, // UIEvents Args
                ev.screenX, ev.screenY, ev.clientX, ev.clientY, // MouseEvents Args
                ev.ctrlKey, ev.altKey, ev.shiftKey, ev.metaKey, // MouseEvents Args
                ev.button, ev.relatedTarget // MouseEvents Args
                );
            m_capturingTarget.dispatchEvent(evObj);
            
            document.addEventListener("mousemove", setCapture, true);
            ev.stopPropagation();
        }
        
        function releaseCapture(ev)
        {
            if(!m_capturingTarget) return false;
            
            document.removeEventListener("mousemove", setCapture, true);
            document.removeEventListener("mouseup", releaseCapture, true);
            
            var _bubbles = "mouseup" == ev.type ? ev.bubbles : false;
            var _cancelable = "mouseup" == ev.type ? ev.cancelable : false;
            
            var evObj = document.createEvent("MouseEvents");
            evObj.initMouseEvent(
                ev.type, _bubbles, _cancelable, // Events Args
                ev.view, ev.detail, // UIEvents Args
                ev.screenX, ev.screenY, ev.clientX, ev.clientY, // MouseEvents Args
                ev.ctrlKey, ev.altKey, ev.shiftKey, ev.metaKey, // MouseEvents Args
                ev.button, ev.relatedTarget // MouseEvents Args
                );
            m_capturingTarget.dispatchEvent(evObj);

            ev.preventDefault();
            ev.stopPropagation();
        }
        
        w.HTMLElement.prototype.setCapture = function(bContainerCapture)
        {
            m_capturingTarget = this;
            document.addEventListener("mousemove", setCapture, true);
            document.addEventListener("mouseover", StopEvtPropagation, true);
            document.addEventListener("mouseout", StopEvtPropagation, true);
            document.addEventListener("mouseenter", StopEvtPropagation, true);
            document.addEventListener("mouseleave", StopEvtPropagation, true);
            document.addEventListener("mouseup", releaseCapture, true);
        }
        
        w.HTMLElement.prototype.releaseCapture = function()
        {
            m_capturingTarget = null;
            document.removeEventListener("mousemove", setCapture, true);
            document.removeEventListener("mouseover", StopEvtPropagation, true);
            document.removeEventListener("mouseout", StopEvtPropagation, true);
            document.removeEventListener("mouseenter", StopEvtPropagation, true);
            document.removeEventListener("mouseleave", StopEvtPropagation, true);
            document.removeEventListener("mouseup", releaseCapture, true);
        }
        
        function $offset(el)
        {
            _ret = {x:0,y:0}; 
            while(el)
            {
                _ret.x+=el.offsetLeft;
                _ret.y+=el.offsetTop;
                el=el.offsetParent;
            } 
            return _ret;
        }
        
        function get_srcElement(){return __web_w3_dom.getNonTextNode(this.target);}
        function set_cancelBubble(bCancel){if(bCancel)this.stopPropagation();}
        function set_returnValue(v){if(!v) this.preventDefault();this.cancelDefault=v;}
        function get_returnValue(){return this.cancelDefault;}
        function get_offsetX() {return window.pageXOffset+this.clientX-$offset(this.target).x;}
        function get_offsetY() {return window.pageXOffset+this.clientY-$offset(this.target).y;}
        function get_x() {return this.offsetX;}
        function get_y() {return this.offsetY;}
        function get_fromElement()
        {
            var _el = null;
            if("mouseover" == this.type) _el = this.relatedTarget;
            else if("mouseout" == this.type) _el = this.target;
            return __web_w3_dom.getNonTextNode(_el);
        }
        function get_toElement()
        {
            var _el = null;
            if("mouseover" == this.type) _el = this.target;
            else if("mouseout" == this.type) _el = this.relatedTarget;
            return __web_w3_dom.getNonTextNode(_el);
        }
        
        w.Event.prototype.__defineGetter__("srcElement", get_srcElement);
        w.Event.prototype.__defineSetter__("cancelBuuble", set_cancelBubble);
        w.Event.prototype.__defineSetter__("returnValue", set_returnValue);
        w.Event.prototype.__defineGetter__("offsetX", get_offsetX);
        w.Event.prototype.__defineGetter__("offsetY", get_offsetY);
        w.Event.prototype.__defineGetter__("x", get_x);
        w.Event.prototype.__defineGetter__("y", get_y);
        w.Event.prototype.__defineGetter__("fromElement", get_fromElement);
        w.Event.prototype.__defineGetter__("toElement", get_toElement);
    }
    
    // TODO: provide ie to mozilla compatibility support.
    function _establish_ie_mozilla_compat(w)
    {}
    
    this.establish = function(w)
    {
        switch(__web_w3_dom.compat._type)
        {
            case __web_w3_dom.CompatType.Mozilla_IE:_establish_mozilla_ie_compat(w);break;
            case __web_w3_dom.CompatType.IE_Mozilla:_establish_ie_mozilla_compat(w);break;
        }
    }
}

__web_w3_dom.compat._type = __web_w3_dom.CompatType.Mozilla_IE;
if(sys.env.userAgent.isMozilla())
    __web_w3_dom.compat.establish(window);
