Source: core/extras/dom.js

/*global Node, Element*/

/**
 * @module montage/core/dom
 * @requires montage/core/geometry/point
*/


// Extend Node.prototype
if (typeof Node !== "undefined") {

    var NodePrototype = Node.prototype;

    NodePrototype.get = function (key) {
        return this.getAttribute(key) || this[key];
    };

    NodePrototype.parentOf = function (child) {

        do {
            child = child.parentNode;
        } while(child && child !== this);

        //If child is defined then we didn't walk all the way up to the root
        return child ? true : false;
    };
}


// Extend Element.prototype
if (typeof Element !== "undefined") {

var ElementPrototype = Element.prototype;

    /**
     * @function external:Element#set
     * @param {string} string
     * @param {string} string
     * @param {number} string
     */
    Object.defineProperty(ElementPrototype, "set", {
        value: function (aPropertyPath, value, currentIndex) {
            var dotIndex = aPropertyPath.indexOf(".", currentIndex),
                result,
                currentKeyComponent,
                indexEnd,
                styleKey,
                classKey;

            currentIndex = currentIndex || 0;

            currentKeyComponent = aPropertyPath.substring(currentIndex, (dotIndex === -1 ? aPropertyPath.length : dotIndex));

            if (dotIndex === -1) {
                //This is only using properties.
                this.setAttribute(currentKeyComponent, value);
            }
            else {
                indexEnd = aPropertyPath.lastIndexOf(".");
                if (currentKeyComponent === "style") {
                    styleKey = aPropertyPath.substring(dotIndex + 1, aPropertyPath.length);
                    this.style[styleKey] = value;
                } else if (currentKeyComponent === "classList") {
                    classKey = aPropertyPath.substring(dotIndex + 1, aPropertyPath.length);
                    if (value) {
                        this.classList.add(classKey);
                    } else {
                        this.classList.remove(classKey);
                    }
                }

                else if ((result = this.get(aPropertyPath.substring(0, indexEnd)))) {
                    (result[aPropertyPath.substring(indexEnd + 1, aPropertyPath.length)] = value);
                }
            }

        },
        enumerable: false
    });

    var classListProp = "classList";
    
    /**
     * @function external:Element#set
     * classList.js
     *
     * Implements a cross-browser element.classList getter.
     */
    if (!ElementPrototype.hasOwnProperty(classListProp)) {
        var trim = /^\s+|\s+$/g,
            setClasses = function (elem, classes) {
                elem.setAttribute("class", classes.join(" "));
            },
            checkAndGetIndex = function (classes, token) {
                if (token === "") {
                    throw "SYNTAX_ERR";
                }
                if (/\s/.test(token)) {
                    throw "INVALID_CHARACTER_ERR";
                }

                return classes.indexOf(token);
            },
            classListGetter = function () {
                var elem = this,
                    classes = elem.getAttribute("class") || "";

                classes = classes.replace(trim, "").split(/\s+/);
                return {
                    length: classes.length,
                    item: function (i) {
                        return classes[i] || null;
                    },
                    contains: function (token) {
                        return checkAndGetIndex(classes, token) !== -1;
                    },
                    add: function (token) {
                        if (checkAndGetIndex(classes, token) === -1) {
                            classes.push(token);
                            this.length = classes.length;
                            setClasses(elem, classes);
                        }
                    },
                    remove: function (token) {
                        var index = checkAndGetIndex(classes, token);
                        if (index !== -1) {
                            classes.splice(index, 1);
                            this.length = classes.length;
                            setClasses(elem, classes);
                        }
                    },
                    toggle: function (token) {
                        if (checkAndGetIndex(classes, token) === -1) {
                            this.add(token);
                        } else {
                            this.remove(token);
                        }
                    },
                    toString: function () {
                        return (elem.getAttribute("class") || "");
                    }
                };
            };

        if (Object.defineProperty) {
            Object.defineProperty(ElementPrototype, classListProp, { get: classListGetter, enumerable: true });
        } else if (Object.prototype.__defineGetter__) {
            ElementPrototype.__defineGetter__(classListProp, classListGetter);
        }
    }
}


// Extend Node.prototype
if (typeof Node !== "undefined") {

    var DocumentPrototype = Object.getPrototypeOf(document);

    DocumentPrototype.addRule = function (selectorText, definition) {
        var styleSheet, cssRule;
        if (!(styleSheet = document.styleSheets[0])) {
            var style = document.createElement("style");
            style.type = "text/css";
            document.head.appendChild(style);
            styleSheet = document.styleSheets[0];
        } else {
            cssRule = document.getRule(selectorText, styleSheet);
        }
        if (!cssRule) {
            styleSheet.insertRule(selectorText + " " + definition, styleSheet.cssRules.length);
        }
    };

    DocumentPrototype.getRule = function (ruleKey, styleSheet) {
        var cssRule;
        if (styleSheet.cssRules) {
            for (var j = 0; (cssRule = styleSheet.cssRules[j]); j++) {
                if (cssRule.name && cssRule.name === ruleKey) {
                    // keyframe animation
                    return cssRule;
                } else if (cssRule.selectorText === ruleKey) {
                    return cssRule;
                }
            }
        }
    };
}

// Extend DOMTokenList.prototype
if (typeof DOMTokenList !== "undefined") {
    var DOMTokenListPrototype = window.DOMTokenList.prototype;

    if (typeof DOMTokenListPrototype.has === 'undefined') {
        Object.defineProperty(DOMTokenListPrototype, 'has', {
            value: function (key) {
                return this.contains(key);
            }
        });
    }
}