/*extern Event, HTMLHtmlElement, HTMLElement, getElementNode */

function getPageCoords(element)
{
    var coords = {x : 0, y : 0};
    while (element) {
        coords.x += element.offsetLeft;
        coords.y += element.offsetTop;
        element   = element.offsetParent;
    }

    return coords;
}


if (document.addEventListener && Event.prototype.__defineSetter__) {
    function patchEvents(eventNames)
    {
        for (var i=0; i<eventNames.length; ++i) {
            document.addEventListener(eventNames[i],
                function (e)
                {
                    window.event = e;
                }
                , true); // use capture
        }
    }

    patchEvents(['click', 'dblclick', 'mouseover', 'mouseout', 'mousedown', 'mouseup', 'mousemove', 'keydown', 'keypress', 'keyup']);

    Event.prototype.__defineSetter__('cancelBubble',
        function (b)
        {
            if (b) {
                this.stopPropagation();
            }
        }
    );

    Event.prototype.__defineSetter__('returnValue',
        function (b)
        {
            if (!b) {
                this.preventDefault();
            }
        }
    );

    Event.prototype.__defineGetter__('srcElement',
        function ()
        {
            return getElementNode(this.target);
        }
    );


    Event.prototype.__defineGetter__('fromElement',
        function ()
        {
            var node;
            if ('mouseover' == this.type) {
                node = this.relatedTarget;
            }
            else if ('mouseout' == this.type) {
                node = this.target;
            }
            else {
                return null;
            }

            return getElementNode(node);
        }
    );

    Event.prototype.__defineGetter__('toElement',
        function ()
        {
            var node;
            if ('mouseout' == this.type) {
                node = this.relatedTarget;
            }
            else if ('mouseover' == this.type) {
                node = this.target;
            }
            else {
                return null;
            }

            return getElementNode(this.target);
        }
    );

    Event.prototype.__defineGetter__('offsetX',
        function ()
        {
            var target = this.target;
            if ('undefined' == typeof(target)) {
                target = target.parentNode;
            }

            var pc = getPageCoords(target);
            return window.pageXOffset + this.clientX - pc.x;
        }
    );


    Event.prototype.__defineGetter__('offsetY',
        function ()
        {
            var target = this.target;
            if ('undefined' == typeof(target)) {
                target = target.parentNode;
            }

            var pc = getPageCoords(target);
            return window.pageYOffset + this.clientY - pc.y;
        }
    );

    HTMLHtmlElement.prototype.__defineGetter__('clientWidth',
        function()
        {
            return window.innerWidth;
        }
    );

    HTMLHtmlElement.prototype.__defineGetter__('clientHeight',
        function()
        {
            return window.innerHeight;
        }
    );

    HTMLElement.prototype.__defineGetter__('currentStyle',
        function()
        {
            return this.ownerDocument.defaultView.getComputedStyle(this, '');
        }
    );

	HTMLElement.prototype.__defineGetter__('parentElement',
	   function ()
	   {
            if (this.parentNode == this.ownerDocument) {
                return null;
            }

            return this.parentNode;
        }
    );

    HTMLElement.prototype.contains =
        function (element)
        {
            if (this == element) {
                return true;
            }

            if (null == element) {
                return false;
            }

            return this.contains(element.parentNode);
        };


    HTMLElement.prototype.__defineGetter__('children',
        function ()
        {
            var i;
            var r = [];
            var j = 0;
            var l = this.childNodes.length;

            for (i=0; i<l; ++i) {
                var n = this.childNodes[i];

                if (1 == n.nodeType) {
                    r[j] = n;
                    ++j;
                    if (n.name) {
                        if (!r[n.name]) {
                            r[n.name] = [];
                        }

                        r[n.name][r[n.name].length] = n;
                    }

                    if (n.id) {
                        r[n.id] = n;
                    }
                }
            }

            return r;
        }
    );


    function text2Html(s)
    {
    	s = s.replace(/\&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\n/g, '<br/>');
        while (/\s\s/.test(s)) {
            s = s.replace(/\s\s/, '&nbsp; ');
    	}

    	return s.replace(/\s/g, ' ');
    }

    HTMLElement.prototype.insertAdjacentHTML = function(sWhere, html) {
    	/**
    	 * @var DocumentFragment
    	 */
        var df;
    	var r = this.ownerDocument.createRange();

    	switch (String(sWhere).toLowerCase()) {
    		case 'beforebegin':
    			r.setStartBefore(this);
    			df = r.createContextualFragment(html);
    			this.parentNode.insertBefore(df, this);
    			break;

    		case 'afterbegin':
    			r.selectNodeContents(this);
    			r.collapse(true);
    			df = r.createContextualFragment(html);
    			this.insertBefore(df, this.firstChild);
    			break;

    		case 'beforeend':
    			r.selectNodeContents(this);
    			r.collapse(false);
    			df = r.createContextualFragment(html);
    			this.appendChild(df);
    			break;

    		case 'afterend':
    			r.setStartAfter(this);
    			df = r.createContextualFragment(html);
    			this.parentNode.insertBefore(df, this.nextSibling);
    			break;
    	}
    };


    HTMLElement.prototype.__defineSetter__('outerHTML',
        function(html) {
            var r = this.ownerDocument.createRange();
            r.setStartBefore(this);

            var df = r.createContextualFragment(html);
            this.parentNode.replaceChild(df, this);

            return html;
        }
    );

    HTMLElement.prototype.__defineGetter__('canHaveChildren',
        function()
        {
            switch (this.tagName) {
                case 'AREA':
                case 'BASE':
                case 'BASEFONT':
                case 'COL':
                case 'FRAME':
                case 'HR':
                case 'IMG':
                case 'BR':
                case 'INPUT':
                case 'ISINDEX':
                case 'LINK':
                case 'META':
                case 'PARAM':
                	return false;
            }

            return true;
        }
    );

    HTMLElement.prototype.__defineGetter__('outerHTML',
        function()
        {
            var str = '';
            var a, l, attr, i;

            switch (this.nodeType) {
            	case 1:    //ELEMENT_NODE
                    a = this.attributes;
                    l = a.length;
            		str += '<' + this.nodeName;

                    for (i=0; i<l; ++i) {
                        attr = a[i];

                        if (attr.specified) {
                            str += ' ' + attr.name + '="' + attr.value + '"';
                        }
                    }

            		if (0 == this.childNodes.length) {
            			str += '/>';
            		}
            		else {
            			str += '>' + this.innerHTML + '<\/' + this.nodeName + '>';
            		}
            		break;

            	case 3:    //TEXT_NODE
            		str += this.nodeValue;
            		break;

            	case 4:    //CDATA_SECTION_NODE
            		str += '<![CDATA[' + this.nodeValue + ']]>';
            		break;

            	case 5:    //ENTITY_REFERENCE_NODE
            		str += '&' + this.nodeName + ';';
            		break;

            	case 8:    //COMMENT_NODE
            		str += '<!--' + this.nodeValue + '-->';
            		break;
            }

            return str;
        }
    );


    HTMLElement.prototype.__defineSetter__('innerText',
        function(text)
        {

            this.innerHTML = text2Html(text);
            return text;
        }
    );

    var tmpGet = function()
    {
    	var r = this.ownerDocument.createRange();
    	r.selectNodeContents(this);
    	return r.toString();
    };

    HTMLElement.prototype.__defineGetter__('innerText', tmpGet);

    HTMLElement.prototype.__defineSetter__('outerText',
        function(text)
        {
            this.outerHTML = text2Html(text);
            return text;
        }
    );

    HTMLElement.prototype.__defineGetter__('outerText', tmpGet);

    HTMLElement.prototype.insertAdjacentText =
        function(sWhere, text)
        {
            this.insertAdjacentHTML(sWhere, text2Html(text));
        };


    HTMLElement.prototype.__defineSetter__('innerHTML',
        function(str)
        {
            var r = this.ownerDocument.createRange();
            r.selectNodeContents(this);
            r.deleteContents();

            var df = r.createContextualFragment(str);
            this.appendChild(df);

            return str;
        }
    );

    HTMLElement.prototype.__defineGetter__('innerHTML',
        function()
        {
            var result = '';
            var n = this.childNodes;
            var l = n.length;

            for (var i=0; i<l; ++i) {
                result += n.item(i).outerHTML;
            }

            return result;
        }
    );

    Event.LEFT   = 0;
    Event.MIDDLE = 1;
    Event.RIGHT  = 2;
}
else {
    Event = {};
	// IE is returning wrong button number
	Event.LEFT   = 1;
	Event.MIDDLE = 4;
	Event.RIGHT  = 2;
}

if (null === Array.prototype.push) {

    Array.prototype.push = function(item)
    {
        this[this.length] = item;
        return this.length;
    };
}

