Mohawk.EventInterface = {
    preventDefault: function () {
        // Cross browser function to prevent the default action from occurring.
        if (this.button == BTN_RIGHT) {
            if (OPERA) {
                // small trick to make opera happy with context menu
                // adds input button under mouse pointer
                no_ctmenu = window.no_ctmenu;
                if (!no_ctmenu) {
                    no_ctmenu = document.createElement('input');
                    no_ctmenu.type='button';
                    document.body.appendChild(no_ctmenu);
                }
                no_ctmenu.style.position = 'fixed';
                no_ctmenu.style.top = this.clientY - 2 + 'px';
                no_ctmenu.style.left = this.clientX - 2 + 'px';
                no_ctmenu.style.width = '5px';
                no_ctmenu.style.height = '5px';
                no_ctmenu.style.opacity = 0;
                no_ctmenu.style.zIndex = 10000;
            }
        }
        if (typeof this.returnValue != 'undefined') {
            this.returnValue = false;
        } else {
            if (window._preventDefault instanceof Function) {
                window._preventDefault.call(this);
            }
        }

        return false;
    },

    stopPropagation: function () {
        // Cross browser function to prevent the event from bubbling.
        if (typeof this.cancelBubble != 'undefined') {
            this.cancelBubble = true;
        } else {
            if (window._stopPropagation instanceof Function) {
                window._stopPropagation.call(this);
            }
        }
        return false;
    },

    cursor: function () {
        var x = 0, y = 0;

        if (this.pageX || this.pageY) {
            x = this.pageX;
            y = this.pageY;
        } else if (this.clientX || this.clientY) {
            x = this.clientX + document.scrollLeft();
            y = this.clientY + document.scrollTop();
        } else if (this.screenX || this.screenY) {
            x = this.screenX;
            y = this.screenY;
        }

        return new Pixel(x, y);
    },

    element: function () {
        return this.srcElement || this.currentTarget;
    }
};

if (window.Event) {
    window._preventDefault = window.Event.prototype.preventDefault;
    window._stopPropagation = window.Event.prototype.stopPropagation;
    extend(window.Event, Mohawk.EventInterface);
}

Mohawk.NodeClassInterface = {
    //return all classes as an array
    getClasses: function() {
        return this.className ? this.className.split(new RegExp('\\s+')) : [];
    },

    //checks class
    hasClass: function(className) {
        var classes = this.getClasses();
        for (var i = 0; i < arguments.length; i ++) {
            if (classes.has(arguments[i])) {
                return true;
            }
        }
        return false;
    },

    //add class
    addClass: function(className) {
        for (var i = 0; i < arguments.length; i ++) {
            if (!this.hasClass(arguments[i])) {
                this.className += ' ' + arguments[i];
            }
        }
    },

    //remove class
    removeClass: function(className) {
        var classes = this.getClasses();
        for (var i = 0; i < arguments.length; i ++) {
            classes.pull(arguments[i]);
        }
        this.className = classes.join(' ');
    },

    //replace class
    replaceClass: function(find, replace) {
        var classes = this.getClasses();
        if ((key = classes.key(find)) !== false) {
            classes[key] = replace;
            this.className = classes.join(' ');
        } else {
            this.addClass(replace);
        }
    },

    //set class if not isset, otherwise remove it
    flipClass: function(className) {
        for (var i = 0; i < arguments.length; i ++) {
            if (this.hasClass(arguments[i])) {
                this.removeClass(arguments[i]);
            } else {
                this.addClass(arguments[i]);
            }
        }
    },

    copyClassesTo: function (element) {
        var classes = this.getClasses();
        classes.forEach (function () {
            element.addClass(this);
        });
    },
    
    setClassesTo: function (element) {
        element.className = this.className;
    }
};

Mohawk.NodeEventInterface =  {
    _events: null,

    addEvent: function (event, action, useCapture) {
        var source = action.parse();
        var $event = source.params[0] || 'event';
        source.body = 'if (IE) {extend(' + $event + ', Mohawk.EventInterface)};' + source.body;
        action = new Function(source.params, source.body);

        var hash = action.toString();
        if (!this._events) {
            this._events = new Object;
        }

        if (!this._events[event]) {
            this._events[event] = [];
        }

        if (!this._events[event][hash]) {

            this._events[event][hash] = action;

            if (!useCapture) {
                useCapture = false;
            }
            if (this.addEventListener) {
                this.addEventListener(event, action, useCapture);
            } else if (this.attachEvent) {
                this.attachEvent('on' + event, action);
            }
        }

        return action;
    },

    removeEvent: function (event, action, useCapture) {
        var source = action.parse();
        var $event = source.params[0] || 'event';
        source.body = 'if (IE) {extend(' + $event + ', Mohawk.EventInterface)};' + source.body;
        action = new Function(source.params, source.body);

        var hash = action.toString();
        if (this._events[event] && this._events[event][hash]) {
            action = this._events[event][hash];
        }

        if (!useCapture) {
            useCapture = false;
        }
        if (this.removeEventListener) {
            this.removeEventListener(event, action, useCapture);
        } else if (this.detachEvent) {
            this.detachEvent('on' + event, action);
        }
        
        if (this._events[event] && this._events[event][hash]) {
           delete(this._events[event][hash]);
       }
    }
};

Mohawk.NodeStructureInterface = {
    coordinates: function () {
        var x = 0, y = 0;
        var element = this;
        while (element) {
            x += element.offsetLeft;
            y += element.offsetTop;
            element = element.offsetParent;
        }
        return new Pixel(x, y);
    },

    isDescendantOf: function (node) {
        var parent = this.parentNode;
        while (parent) {
            if (parent == node) {
                return true;
            }
            parent = parent.parentNode;
        }
        return false;
    },

    isAncestorOf: function (node) {
        var parent = node.parentNode;
        while (parent) {
            if (parent == this) {
                return true;
            }
            parent = parent.parentNode;
        }
        return false;
    },

    removeChildren: function () {
        while (this.firstChild) {
            this.removeChild(this.firstChild);
        }
    },

    firstTag: function (tag) {
        if (typeof tag == 'undefined') {
            tag = false;
        }
        var current = this.firstChild;
        while (current && (!current.tagName || (tag && current.tagName.toLowerCase() != tag.toLowerCase()))) {
            current = current.nextSibling;
        }
        return current;
    },

    nextTag: function (tag) {
        if (typeof tag == 'undefined') {
            tag = false;
        }
        var current = this.nextSibling;
        while (current && (!current.tagName || (tag && current.tagName.toLowerCase() != tag.toLowerCase()))) {
            current = current.nextSibling;
        }
        return current;
    },

    ancestorTag: function (tag) {
        if (typeof tag == 'undefined') {
            tag = false;
        }
        var current = this.parentNode;
        while (current && (!current.tagName || (tag && current.tagName.toLowerCase() != tag.toLowerCase()))) {
            current = current.parentNode;
        }
        return current;
    },

    previousTag: function (tag) {
        if (typeof tag == 'undefined') {
            tag = false;
        }
        var current = this.previousSibling;
        while (current && (!current.tagName || (tag && current.tagName.toLowerCase() != tag.toLowerCase()))) {
            current = current.previousSibling;
        }
        return current;
    },

    replace: function (node) {
        this.parentNode.replaceChild(node, this);
    },
    
    insertFirst: function(node) {
        this.insertBefore(node, this.firstChild);
    },
    
    insertAfter: function (node, before) {
        this.insertBefore(node, before.nextSibling);
    },

    getElementsByClassName: function (class_name) {
        var elements = this.getElementsByTagName('*');
        var found = [];
        for (var i = 0; i < elements.length; i ++) {
            if (elements[i].hasClass && elements[i].hasClass(class_name)) {
                found.push(elements[i]);
            }
        }
        return found;
    },
    
    getElementsByLang: function (lang) {
        var elements = this.getElementsByTagName('*');
        var found = [];
        for (var i = 0; i < elements.length; i ++) {
            if (elements[i].lang == lang) {
                found.push(elements[i]);
            }
        }
        return found;
    },
    
    setHTML: function (html) {
        this.innerHTML = html;
        Mohawk.DOM.enchaseDocumentNodes(this);
    },
    
    prependHTML: function (html) {
    	this.innerHTML = html + this.innerHTML;
    	Mohawk.DOM.enchaseDocumentNodes(this);
    },

	appendHTML: function (html) {
		this.innerHTML += html;
		Mohawk.DOM.enchaseDocumentNodes(this);
	}

};

Mohawk.DOM = new Singletone ({
    ELEMENT_NODE:        1,
    TEXT_NODE:           3,
    CDATA_SECTION_NODE : 4,
    DOCUMENT_NODE :      9,

    enchaseNode: function (node) {
		if (![self.ELEMENT_NODE, self.DOCUMENT_NODE].has(node.nodeType)) {
			return;
		}
		
		if (node._enchased instanceof Function && node._enchased() === true) {
            return;
        }
		
        node._cloneNode = node.cloneNode;
        node.cloneNode = function (flag) {
            var clone = this._cloneNode(flag);
            self.enchaseNode(clone);
            return clone;
        };

        node._getElementsByTagName = node.getElementsByTagName;
        node.getElementsByTagName = function (tag) {
        	var collection = node._getElementsByTagName(tag);
        	collection.forEach = ObjectInterface.forEach;
        	return collection;
        };
        
        switch (node.nodeName.toUpperCase()) {
        case 'TABLE':
            node._insertRow = node.insertRow;
            node.insertRow = function (index) {
                var row = (HTMLTableElement.prototype.insertRow || this._insertRow).call(this, index);
                self.enchaseNode(row);
                return row;
            };
            
            node._createTHead = node.createTHead;
            node.createTHead = function () {
                var thead = (HTMLTableElement.prototype.insertRow || this._createTHead).call(this);
                self.enchaseNode(thead);
                return thead;
            };
            
            node._createTFoot = node.createTFoot;
            node.createTFoot = function () {
                var tfoot = (HTMLTableElement.prototype.insertRow || this._createTFoot).call(this);
                self.enchaseNode(tfoot);
                return tfoot;
            };
            
            break;
            
        case 'TR':
            node._insertCell = node.insertCell;
            node.insertCell = function (index) {
                var cell = (HTMLTableRowElement || this._insertCell).call(this, index);
                self.enchaseNode(cell);
                return cell;
            };
            break;
            
        case 'FORM':
            if (typeof Mohawk.FormsInterface != 'undefined') {
                extend(node, Mohawk.FormsInterface);
            }
            break;
        }
        
        extend(node, Mohawk.NodeClassInterface);
        extend(node, Mohawk.NodeEventInterface);
        extend(node, Mohawk.NodeStructureInterface);
        
        if (Mohawk.NodeEffectInterface instanceof Object) {
            extend(node, Mohawk.NodeEffectInterface);
        }

        node._enchased = function () {
        	return true;
        };
    },

    enchaseDocument: function () {
        if (document._document_enchased) {
            return;
        }
        document._document_enchased = true;
        document._createElement = document.createElement;
        document.createElement = function (tag_name) {
            if (typeof Document == 'undefined') {
                var element = document._createElement(tag_name);
            } else {
                var element = (Document.prototype.createElement || document._createElement).call(document, tag_name);
            }
            self.enchaseNode(element);
            return element;
        };
        
        document._getElementById = document.getElementById;
        document.getElementById = function (id) {
            if (typeof Document == 'undefined') {
                var element = document._getElementById(id);
            } else {
                var element = (Document.prototype.getElementById || document._getElementById).call(document, id);
            }
            if (element) {
                self.enchaseDocumentNodes(element);
            }
            return element;
        };

        document.scrollLeft = function () {
            return self.pageXOffset
                || (document.documentElement && document.documentElement.scrollLeft)
                || (document.body && document.body.scrollLeft);
        };
        document.scrollTop = function () {
            return self.pageYOffset
                || (document.documentElement && document.documentElement.scrollTop)
                || (document.body && document.body.scrollTop);
        };
        document.size = function () {
            var w = 0, h = 0;
            var win = window.window;
    
            if (win.self.innerHeight) {
                w = win.self.innerWidth;
                h = win.self.innerHeight;
            } else if (win.document.documentElement && win.document.documentElement.clientWidth) {
                w = win.document.documentElement.clientWidth;
                h = win.document.documentElement.clientHeight;
            } else if (win.document.body) {
                w = win.document.body.clientWidth;
                h = win.document.body.clientHeight;
            }
            return {width: w, height: h};
        };
        self.enchaseNode(document);

        if (typeof document.defaultView == 'undefined') {
            document.defaultView = {};
        }
        if (typeof document.defaultView.getComputedStyle == 'undefined') {
            document.defaultView.getComputedStyle = function (element, pseudoElement) {
                return element.currentStyle;
            };
        }
    },

    enchaseDocumentNodes: function (node) {
        if (node) {
            self.enchaseNode(node);
        }
        var all = (node || document.body).getElementsByTagName('*');
        if (all.forEach instanceof Function) {
	        all.forEach(function () {
	        	self.enchaseNode(this);
	        });
        } else {
			for (var i = 0; i < all.length; i ++) {
				self.enchaseNode(all[i]);
			}
        }
    }
});

Mohawk.DOM.event = function (event) {
    event = event || window.event;
    extend(event, Mohawk.EventInterface);
    return event;
};

Mohawk.DOM.enchaseDocument();

document.addLoader(
    function () {
        Mohawk.DOM.enchaseDocumentNodes();
    }
);

window.ID = function (id) {
    return document.getElementById(id);
};