/**
 * Wrapper for DOM
 * @author Nando Vieira <fnando dot vieira at gmail dot com>
 * @license: http://creativecommons.org/licenses/by/2.5/
 * @license: http://creativecommons.org/licenses/by/2.5/deed.pt
 *
 * Contribution
 * - Klaus Paiva <klaus dot paiva at gmail dot com>
*/

var dom = {
    'support': function()
    {
        return document.getElementById && document.getElementsByTagName && document.createElement;
    }
    ,
    'byId': function(a)
    {
        var elements = [], el;

        for (var i = 0; i < arguments.length; i++) {
            el = document.getElementById(arguments[i]);

            if (el) {
                elements.push(el);
            }
        }

        if (elements.length == 0) {
            return false;
        } else if (elements.length == 1) {
            return elements[0];
        } else {
            return elements;
        }
    }
    ,
    'byTag': function(tagname, target)
    {
        if (!target) {
            target = document;
        }

        return target.getElementsByTagName(tagname);
    }
    ,
    'byClass': function(classname, target, tag)
    {
        if (!target) {
            target = document;
        }

        if (!tag) {
            tag = '*';
        }

        var elements = this.byTag(tag);
        var result = [];
        var len = elements.length;
        var re = new RegExp('\\b' + classname + '\\b');

        for (var i = 0; i < len; i++) {
            if (!elements[i].className) {
                continue;
            }

            if (elements[i].className.match(re)) {
                result.push(elements[i]);
            }
        }

        return result;
    }
    ,
    /**
     * parseStyle
     * Parses a string to style's property (workaround for IE which doesn't
     * applies something like obj.setAttribute('style', 'background: red; color: #f00');
     *
     * @param node target
     * @param string style
     */
    'parseStyle': function(target, style)
    {
        if (!target) {
            return;
        }

        var styles = style.split(';');

        for (var i = 0; i < styles.length; i++) {
            var rule = styles[i].split(':');

            if (rule[0] && rule[1]) {
                target.style[rule[0]] = rule[1];
            }
        }
    }
    ,
   /**
     * @param string type           The element name (i.e. H1, DIV, ...)
     * @param string content        The element content
     * @param array attrib          Element's attributes (i.e. ['id', 'myid', 'class', 'myclass', 'href', 'page.html']
     * @param mixed nodes           Target's nodes. Can be the element itself or string.
     * @param string mode           Append or replace the target node (usage: 'append' or 'replace')
     * @param bool remove_content   If true, removes all child nodes. Makes sense only with append mode.
     * @return mixed                If nodes are empty, returns the element that you're creating. Otherwise, returns nothing;
     */
    'element': function(type, content, attrib, nodes, mode, remove_content)
    {
        var tag = document.createElement(type);
        nodes = typeof nodes != 'object'? (nodes? [nodes] : []) : (nodes.length? nodes : [nodes]);
        attrib = attrib? attrib : {};
        mode = mode == 'replace'? 'replace' : 'append';

        var content = typeof content != 'object' && content? document.createTextNode(content) : content;

        for (var name in attrib) {
            if (name == 'class') {
                tag.className = attrib[name];
            } else if (name == 'style') {
                    this.parseStyle(tag, attrib[name]);
            } else {
                tag.setAttribute(name, attrib[name]);
            }
        }

        if (content) {
            tag.appendChild(content);
        }

        if (nodes.length == 0) {
            return tag;
        }

        for (var i = 0; i < nodes.length; i++) {
            var node = typeof nodes[i] == 'string'? this.byId(nodes[i]) : nodes[i];

            if (!node) {
                continue;
            }

            if (remove_content) {
                this.empty(node);
            }

            if (mode == 'replace') {
                node.parentNode.replaceChild(tag.cloneNode(true), node);
            } else {
                node.appendChild(tag.cloneNode(true));
            }
        }

        return tag;
    }
    ,
    /**
     * Adds content to specified node
     *
     * @param node target Target node
     * @param mixed content Can be string or element itself.
     * @param empty Empty element before adding content
     * @return node
     */
    'content': function(target, content, empty)
    {
        if (!target) {
            return;
        }

        if (empty) {
            this.empty(target);
        }

        content = typeof content != 'object'? document.createTextNode(content) : content;

        target.appendChild(content);
    }
    ,
    /**
     * Wrapper for document.createTextNode
     *
     * @param string content
     * @return node
     */
    'text': function (content)
    {
        return document.createTextNode(content);
    }
    ,
    /**
     * Empty the specified node
     * @return void
     */
    'empty': function(target)
    {
        if (!target) {
            return;
        }

        while (target.firstChild) {
            dom.remove(target.firstChild);
        }
    }
    ,
    /**
     * Wrapper for node.replaceChild
     */
    'replace': function(node, target)
    {
        try {
            target.parentNode.replaceChild(node, target);
            return true;
        } catch (e) {
            return false;
        }
    }
    ,
    /**
     * Wrapper for node.removeChild
     */
    'remove': function(target)
    {
        if (!target) {
            return false;
        }

        target.parentNode.removeChild(target);
    }
    ,
    /**
     * Wrapper for node.insertBefore
     */
    'before': function(node, target)
    {
        node.parentNode.insertBefore(node, target);
    }
    ,
    /**
     * Emulates insertAfter
     */
    'after': function(node, target)
    {
        target.parentNode.insertBefore(node, target.nextSibling);
    }
    ,
    /**
     * Inserts node as firstChild
     */
    'first': function(node, target)
    {
        if (target.firstChild) {
            this.before(node, target.firstChild);
        } else {
            target.appendChild(node);
        }
    }
    /**
     * Clones target
     */
    ,
    'clone': function(target, recursive)
    {
        return target.cloneNode(recursive);
    }
    /**
     * Creates a document fragment
     */
    ,
    'fragment': function()
    {
        return document.createDocumentFragment();
    }
    ,
    /**
     * Parses the node into array
     *
     * @param node node
     * @return array
     */
    'tree': function(node, nonrecursive, parent)
    {
        if (!node) {
            return false;
        }

        if (node.nodeType != 1) {
            return false;
        }

        var nodes = node.childNodes;
        var len = nodes.length;
        var tag, tmp, tree;
        var passed = false;

        parent = parent? parent : {'self': node};

        for (var i = 0; i < len; i++) {
            if (nodes[i].nodeType != 1) {
                continue;
            }

            tag = nodes[i].tagName.toLowerCase();
            element = nodes[i];

            if (!parent[tag]) {
                parent[tag] = {'length': 0, 'tag': tag};
            }

            index = parent[tag].length++;

            if (!nonrecursive && element.hasChildNodes()) {
                parent[tag].self = element;
                parent[tag][index] = this.tree(element, false, parent[tag][index]);
            } else {
                parent[tag][index] = {'length': 0, 'tag': tag, 'self': element};
            }
        }

        return parent;
    },

    'treeXML': function(node, parent)
    {
        if (!node) {
            return false;
        }

        if (node.nodeType != 1) {
            return false;
        }

        var nodes = node.childNodes;
        var len = nodes.length;
        var tag, tmp, tree;
        var passed = false;

        parent = parent? parent : {'self': node};

        for (var i = 0; i < len; i++) {
            if (nodes[i].nodeType != 1) {
                continue;
            }

            tag = nodes[i].tagName; // .toLowerCase()
            element = nodes[i];

            if (!parent[tag]) {
                parent[tag] = {'length': 0, 'tag': tag};
            }

            index = parent[tag].length++;

            if (element.hasChildNodes()) {
              if (element.childNodes.length == 1 && element.firstChild.nodeType != 1) {
                parent[tag][index] = {'length': 0, 'tag': tag, 'data': element.firstChild.data};
              } else {
                parent[tag][index] = this.treeXML(element, parent[tag][index]);
              }
            } else {
                parent[tag][index] = {'length': 0, 'tag': tag, 'data': element.data};
            }
        }

        return parent;
    }
    ,
    /**
     * Returns array with all applied styles
     *
     * @param node el
     * @return array
     */
    'currentStyle': function(target)
    {
        if (!document.defaultView && !document.body.currentStyle) {
            return false;
        }

        var style = target.currentStyle? target.currentStyle : document.defaultView.getComputedStyle(target,'');
        var s = {};

        if (!style) {
            return null;
        }

        if (style.item) {
            for (var i = 0; i < style.length; i++) {
                var prop = style.item(i);
                s[prop] = style.getPropertyValue(prop);
            }
        } else {
            for (var o in style) {
                s[o] = style[o];
            }
        }
        return s;
    }
    ,
    'parseXML': function(str)
    {
        var x = false;

        //str = '<xml>' + str + '</xml>';

        if (typeof DOMParser != "undefined") {
            var parser = new DOMParser();
            try {
                x = parser.parseFromString(str, 'text/xml');
            } catch (e) {
                return false;
            }
        } else if (window.ActiveXObject) {
            try {
                x = new ActiveXObject("Microsoft.XMLDOM");
                x.async = false;
                x.loadXML(str);
            } catch (e) {
                return false;
            }
        }
        return x? x.firstChild : false;
    }

    ,
    'parseHTML': function(html, parent)
    {
        var xml = this.parseXML(html), type;

        if (!xml) {
            return false;
        }

        if (!parent) {
            parent = this.fragment();
        }

        var __parseNode__   = function(node, parent)
        {
            var child, element, i, attrib = {}, type;
            type = node.nodeType;

            if (type == 1) {
                if (node.attributes.length > 0) {
                    for(i = 0; i < node.attributes.length; i++) {
                        attrib[node.attributes[i].name] = node.attributes[i].value;
                    }
                }

                element = dom.element(node.nodeName, '', attrib);

                for (i = 0; i < node.childNodes.length; i++) {
                    type = node.childNodes[i].nodeType;

                    if (type == 1 || type == 3) {
                        element = __parseNode__(node.childNodes[i], element);
                    }
                }
            } else if (type == 3) {
                element = dom.text(node.nodeValue);
            }

            parent.appendChild(element);
            return parent;
        };

        for (var i = 0; i < xml.childNodes.length; i++) {
            type = xml.childNodes[i].nodeType;

            if (type == 1 || type == 3) {
                parent = __parseNode__(xml.childNodes[i], parent);
            }
        }

        return parent;
    }
    ,
    'getPosition': function (target)
    {
    		var curleft = curtop = 0;
    		var obj = target;
        if (obj.offsetParent) {
                curleft = obj.offsetLeft
                curtop = obj.offsetTop
                while (obj = obj.offsetParent) {
                        curleft += obj.offsetLeft
                        curtop += obj.offsetTop
                }
        }
        return [curleft,curtop];
    }
};