
/*global jQuery*/
(function (window) {
    "use strict";
    var $ = window.jQuery,
        ua = window.navigator.userAgent,
        unloadFunctions = [];

    $.isIE = (/MSIE\s\d/).test(ua);
    $.isIE7 = (/MSIE\s7/).test(ua);
    $.isIE8 = (/MSIE\s8/).test(ua);
    $.isIE9 = (/MSIE\s9/).test(ua);

    $.isEPIEditMode = (function () {
        return (document.location.search && (/idkeep=True/).test(document.location.search));
    } ());

    $.log = function (message) {
        if (window.console && window.console.debug) {
            window.console.debug(arguments);
        } else if (window.console && window.console.log) {
            window.console.log(arguments);
        } else {
            alert(message);
        }
    };

    $.alert = function () {
        var args = arguments,
            i = 0,
            l = args.length,
            str = "";

        for (i = 0; i < l; i += 1) {
            if (!i) {
                str += args[i];
            } else {
                str += "\n" + args[i];
            }
        }

        alert(str);
    };

    $.uniqueId = function (id) {
        var count = 0,
			result;

        if (document.getElementById(id + count)) {
            while (document.getElementById(id + count)) {
                count += 1;
            }
        }

        result = id + count;

        return result;
    };

    $.fn.replaceClass = function (oldClass, newClass) {
        var that = this;

        that.removeClass(oldClass);
        that.addClass(newClass);

        return that;
    };

    $.fn.normalize = function () {
        this.each(function () {
            var that = this,
                childNodes = that.childNodes,
                ih = "",
                hasTagKids = function (node) {
                    return node.getElementsByTagName ? node.getElementsByTagName("*").length : 0;
                },
                length = childNodes.length,
                i = 0;

            for (i = length - 1; i > -1; i -= 1) {
                if (childNodes[i].nodeType === 3 && !$.trim(childNodes[i].nodeValue)) {
                    childNodes[i].parentNode.removeChild(childNodes[i]);
                }
            }


            if (that.nodeType === 1 && that.tagName && !hasTagKids(that)) {
                ih = $.trim(that.innerHTML);
                that.innerHTML = ih;
            }

        });
        return this;
    };

    $.getDimensions = function (elem) {
        if (!elem.tagName) {
            throw new Error(elem + " is not an HTMLTagElement");
        } else {

            var jqElem = $(elem),
                ofs = jqElem.offset(),
                ret = {
                    width: parseFloat(jqElem.width()),
                    height: parseFloat(jqElem.height()),
                    ofs: {
                        top: elem.offsetTop,
                        left: elem.offsetLeft
                    },
                    top: ofs.top,
                    left: ofs.left,
                    padding: {
                        top: parseFloat(jqElem.css("padding-top")) ? parseFloat(jqElem.css("padding-top")) : 0,
                        right: parseFloat(jqElem.css("padding-right")) ? parseFloat(jqElem.css("padding-right")) : 0,
                        bottom: parseFloat(jqElem.css("padding-bottom")) ? parseFloat(jqElem.css("padding-bottom")) : 0,
                        left: parseFloat(jqElem.css("padding-left")) ? parseFloat(jqElem.css("padding-left")) : 0
                    },
                    margin: {
                        top: parseFloat(jqElem.css("margin-top")) ? parseFloat(jqElem.css("margin-top")) : 0,
                        right: parseFloat(jqElem.css("margin-right")) ? parseFloat(jqElem.css("margin-right")) : 0,
                        bottom: parseFloat(jqElem.css("margin-bottom")) ? parseFloat(jqElem.css("margin-bottom")) : 0,
                        left: parseFloat(jqElem.css("margin-left")) ? parseFloat(jqElem.css("margin-left")) : 0
                    }
                };

            ret.fullWidth = parseFloat(ret.width + ret.margin.left + ret.margin.right + ret.padding.left + ret.padding.right);

            ret.fullHeight = parseFloat(ret.height + ret.margin.top + ret.margin.bottom + ret.padding.top + ret.padding.bottom);

            if (elem === document.body) {
                ret.scroll = {
                    top: document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop,
                    left: document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft
                };

                ret.view = {
                    height: document.documentElement.clientHeight ? document.documentElement.clientHeight : elem.parentNode.clientHeight,
                    width: document.documentElement.clientWidth ? document.documentElement.clientWidth : elem.parentNode.clientWidth
                };
            }

            return ret;
        }
    };

    /*$.fn.changedDOM = function changedDOM(func, name) {
        
        var that = this;

        if (!name) {
            throw new Error("name is not specified");
        }

        that.each(function () {

            var elem = this,
                funcs = $(elem).data("hasChangedDOM") ? $(elem).data("hasChangedDOM") : {};

            if (name && !funcs[name]) {

                if (elem.addEventListener) {
                    elem.addEventListener("DOMNodeInserted", func, false);
                } else if (elem.attachEvent) { // <= IE8
                    elem.attachEvent("onpropertychange", func);

                    unloadFunctions.push([elem, "onpropertychange", func]);
                }

                funcs[name] = func;

                $(elem).data("hasChangedDOM", funcs);
            }
        });

        return that;
    };*/

    $(window).unload(function () {
        if (unloadFunctions.length && window.attachEvent) {
            var i = 0,
                l = unloadFunctions.length;

            for (i = 0; i < l; i += 1) {
                unloadFunctions[i][0].detachEvent(unloadFunctions[i][1], unloadFunctions[i][2]);
            }
        }
    });

    /**
    * Creates and returns a HTMLElement.
    * 
    * Has shaky or no support for <object> & <embed> (depending on browser).
    * 
    * ex: newElementObject = {tagName : "a", href : "/", innerHTML : "A link", className : "someClass"}.
    * 
    * Specials: 
    *     append: {tagName: "ul", append: [{tagName: "li"}, {tagName: "li"}]}, append takes an Array of Objects or a single Object
    *     repeat: {tagName: "p", repeat: 10} adds 10 cloned <p>
    * 
    * 
    * @param {Object|String|empty} newElementObject|tagName (if !newElementObject $.create returns an empty <div>)
    * @returns HTMLElement
    */
    $.create = function (newElementObject) {

        // custom create HTML
        var neo = newElementObject,
			neoLength = neo ? (neo.length ? neo.length : 0) : 0,
	        tagName = (neo && neo.tagName) ? neo.tagName : "div",
	        newElement = document.createElement(tagName),
	        ne = newElement,
	        p, // properties
	        i = 0,
			clone = null,
			clones = neo ? (neo.repeat ? true : false) : false,
			df = document.createDocumentFragment(),
			dynProp = null, //dynamic property
			breakLoop = false,
            loadFunctions = [],
            data;

        if (!newElementObject) {
            return ne;
        }

        if (typeof newElementObject === "string") {
            return document.createElement(newElementObject);
        }

        if (neo.type) {
            ne.setAttribute("type", neo.type);
        }

        if (neo.src) {
            ne.setAttribute("src", neo.src);
        }

        if (neo) {

            if ($.isArray(neo)) {

                df = document.createDocumentFragment();

                for (i = 0; i < neoLength; i += 1) {
                    try {
                        df.appendChild($.create(neo[i]));
                    } catch (e) {
                        alert(e + "\n\ntagName = " + tagName + "\n\n" + p + ":\n" + neo[p] + "\n\ncreate loop (isArray)");
                        break;
                    }
                }

                newElement = df;
            } else {

                for (p in neo) {

                    if ((/(string|function|object|number)/).test(typeof neo[p])) {

                        if ((/^((un)?load|(dbl)?click|change|resize|scroll|select|submit|focus(in|out)?|blur|mouse(enter|leave|over|out|move|down|up)|key(press|down|up))$/).test(p) && typeof neo[p] === "function") {
                            $(ne).bind(p, neo[p]);

                            if (p === "load") {
                                loadFunctions.push(neo[p]);
                            }

                        } else if (typeof neo[p] === "function") {
                            dynProp = neo[p]();
                            if (dynProp) {
                                if (typeof ne.style[p] === "string") {
                                    ne.style[p] = dynProp;
                                } else {
                                    ne[p] = dynProp;
                                }
                            }
                        } else if (p === "data") {
                            for (data in neo[p]) {
                                if ((/^(object|function|number|string|boolean)$/).test(typeof neo[p][data])) {
                                    $.data(ne, data, neo[p][data]);
                                }
                            }

                        } else if (!(/(tagName|append(Child(ren)?)?|type|repeat|src)/).test(p)) {

                            if (typeof ne.style[p] === "string" && p !== "opacity") {
                                ne.style[p] = neo[p];
                            } else if (typeof ne.style[p] === "string" && p === "opacity") {

                                //$.alert("Do not use Opacity here", "Do it with CSS or the JS lib of your choise");

                                /*if (parseInt(neo[p], 10) === 1) {
                                opacity = 100;
                                } else {
                                opacity = parseInt(neo[p].replace(/^0\./g, "") * 10, 10);
                                }

                                ne.style.filter = "alpha(opacity=" + opacity + ")";
                                ne.style["-ms-filter"] = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + opacity + ")";*/

                                $(ne).css("opacity", neo[p]);

                            } else {
                                try {
                                    /**
                                    * create does not add a class attribute if there is no value.
                                    */
                                    if (!((/^(className)$/).test(p) && !neo[p])) {
                                        ne[p] = neo[p];
                                    }

                                } catch (f) {
                                    alert(f + "\n\ntagName = " + tagName + "\n\n" + p + ":\n" + neo[p] + "\n\nattribute");
                                    breakLoop = true;
                                }
                            }
                        } else if ((/^(append)(Child(ren)?)?$/).test(p)) {
                            if (typeof neo[p] === "object" && neo[p].length) {
                                for (i = 0; i < neo[p].length; i += 1) {
                                    try {
                                        ne.appendChild($.create(neo[p][i]));
                                    } catch (g) {
                                        alert(g + "\n\ntagName = " + tagName + "\n\n" + p + ":\n" + neo[p] + "\n\ncreate loop");
                                        breakLoop = true;
                                    }
                                }
                            } else if (typeof neo[p] === "object") {
                                try {
                                    ne.appendChild($.create(neo[p]));
                                } catch (h) {
                                    alert(h + "\n\ntagName = " + tagName + "\n\n" + p + ":\n" + neo[p] + "\n\ncreate");
                                    breakLoop = true;
                                }
                            }
                        }

                        if (breakLoop) {
                            break;
                        }
                    }
                }
            }
        }

        if (clones) {
            df = document.createDocumentFragment();

            for (i = 0; i < neo.repeat; i += 1) {
                clone = newElement.cloneNode(true);
                df.appendChild(clone);
            }

            newElement = df;
        }

        if (loadFunctions.length) {
            setTimeout(function () {

                for (i = 0; i < loadFunctions.length; i += 1) {
                    loadFunctions[i].apply(newElement);
                }

            }, 10);
        }

        return newElement;
    };

    /**
    * $.runWhenExists will run a maximum of 50 times before trying to run the callback.
    * If that does not work it does nothing.
    *
    * @param {Function} exists (have to return a falsy or truthy value)
    * @param {Function} callback (will run when "exists" returns a truthy value)
    * @param {Number} customTime (change the initial interval timer)
    * @param {Number} customWait (change the default wait-to-run-callbak timer)
    */
    $.runWhenExists = function runWhenExists(exists, callback, customTime, customWait) {

        var iv = 0,
            ivCount = 0,
            time = (customTime && typeof customTime === "number") ? customTime : 100,
            wait = (customWait && typeof customWait === "number") ? customWait : 100,
            allCount = 0,
            maxCount = 50,
            runFunc = function runFunc() {
                ivCount += 1;
                allCount += 1;

                if (allCount >= maxCount) {
                    clearInterval(iv);
                    try {
                        callback();
                    } catch (error) {
                        //throw new Error(error);
                    }
                }

                if (exists()) {
                    // wait 100 or [customWait] ms before running the callback, just in case...
                    clearInterval(iv);
                    setTimeout(callback, wait);
                }

                if (ivCount === 5) {
                    clearInterval(iv);

                    time = (time * 2);
                    ivCount = 0;

                    iv = setInterval(runFunc, time);
                }
            };

        iv = setInterval(runFunc, time);

    };

    $.objectWalk = function (obj, func, args) {
        var i,
			ar = args ? args : [];

        if (typeof func !== "function") {
            throw new Error("file: custom.js, Error: " + func + " is not a function");
        }
        func.apply(obj, ar);

        for (i in obj) {
            if (obj[i] && typeof obj[i] === "object" && (!obj[i].length && obj[i].length !== 0)) {
                $.objectWalk(obj[i], func, ar);
            }
        }
    };

    $.runAll = function () {
        var that = this;

        $.objectWalk(that, function () {
            if (this[arguments[0]]) {
                if (typeof this[arguments[0]] === "function") {
                    this[arguments[0]]();
                } else if (typeof this[arguments[0]] === "object" && (!this[arguments[0]].length && this[arguments[0]].length !== 0)) {
                    var prop,
                        obj = this[arguments[0]];

                    for (prop in obj) {
                        if (typeof obj[prop] === "function") {
                            obj[prop]();
                        }
                    }
                }
            }
        }, [arguments[0]]);
    };

    $.runInit = function (obj) {
        $.runAll.apply(obj, ["init"]);
    };

}(window));
