
var OTOS = window.OTOS || {};
OTOS.Dom = function () {

    return {

        /**
         * Creates a box with rounded corners
         *
         * @param   {element}   content     The content for the box
         * @param   {String}    classname   The class-attribute for the box
         * @return  The box with the rounded corners
         * @type    element
         */
        createRoundedBox: function (content, classname) {
            return OTOS.Dom.roundCorners(
                Builder.node('div', { className: 'roundedContent' }, [ content ]),
                'images/cornerImage.gif', 3, 10);
        },


        /**
         * Creates a html text element with a text included and returns it.
         *
         * @param    tag         A html element containing the text
         * @param    str         The text to append to the element
         * @return   element     A DOM node (html element) with a text node as a child
         */
        createTextElement: function (tag, str) {
            return Builder.node(tag, [ document.createTextNode(str) ]);
        },


        /**
         * Creates a heading with rounded corners
         *
         * @param   heading     The heading level (h1, h2 etc.)
         * @param   content     The content of the heading
         * @return  element     The heading in a container
         */
        createHeading: function (level, content) {
            return OTOS.Dom.roundCorners(Builder.node(level, [
                Builder.node('div', { className: 'roundedContent' }, [ content ])]), 'images/cornerImage.gif', 3, 10);
        },


        /**
         * Applies round corners to the element
         *
         * @param   {element}   el      The element
         * @param   {String}    img     The address of the image to be used. The image has all 4 corners in it,
         *                              merged vertically. The order of the corners is topleft, topright,
         *                              bottomleft, bottomright
         * @param   {int}       mask    Which corners are going to be rounded. 1 = top corners, 2 = bottom corners,
         *                              3 = all the corners
         * @param   {int}       size    The size of ONE corner in pixels (for example 10)
         * @return  The element with the rounded corners
         * @type    {element}
         */
        roundCorners: function (el, img, mask, size) {

            var _ROUND_UPPER = 1
            var _ROUND_LOWER = 2;

            if (mask == null) mask = 3;
            if (size == null) size = 10;

            var c = [];

            for (var j = 0; j < 4; j++) {

                c[j]                    = document.createElement("b");
                c[j].style.display      = "block";
                c[j].style.height       = size + "px";
                c[j].style.fontSize     = "1px";
                c[j].style.border       = "none";
                c[j].style.background   = 'url(' + img + ') no-repeat ' + ((j % 2 == 0) ? '0' : '100%') + ' -' + (j * size) + 'px';

            } // for

            if (mask & _ROUND_UPPER) {
                c[0].appendChild(c[1]);
                el.insertBefore(c[0], el.firstChild);
            } // if

            if (mask & _ROUND_LOWER) {
                c[2].appendChild(c[3]);
                el.appendChild(c[2]);
            } // if

            return el;

        },


        /**
         * Creates an navigation element with a heading and contents.
         *
         * @param   {string}    heading     The heading text for the element
         * @param   {element}   content     The content for the element
         * @param   {string}    helptext    The help text for the element
         * @param   {string}    contentId   The id of the content element
         * @return  The navigation element
         * @type    {element}
         */
        createNavBox: function (heading, content, helptext, contentId) {

            var navHeading  = Builder.node('h2', heading),
                navButtons  = Builder.node('ul', { className: 'navButtons' }, [
                    Builder.node('li', { className: 'toggleHelp' }, [ '?' ]),
                    Builder.node('li', { className: 'toggleNavBox' }, [ '\u25BC' ])
                ]),
                navCont = Builder.node('div', {
                    className: 'navContent',
                    id: contentId
                }, [ content ]),
                navBox = Builder.node('div', { className: 'navBox' }, [
                    navHeading,
                    navButtons,
                    navCont
                ]);

            Event.observe(navButtons.firstChild, 'click', function () {
                var myPopup = new Popup(
                        'helpPopup_' + contentId, {
                            parent: navHeading,
                            content: OTOS.Dom.createTextElement('p', helptext),
                            width: 200,
                            height: 200
                        });
                myPopup.show();
            });

            Event.observe(navButtons.lastChild, 'click', function () {
                OTOS.Util.toggleNavBox(navButtons.lastChild);
            });

            return navBox;

        },


        /**
         * Finds and returns the elements first ancestor element by its classname.
         *
         * @param   {element}   el          The element from where we're starting the search
         * @param   {String}    className   The classname we're searching
         * @return  Element's first ancerstor that has the classname that we are giving
         *          as an argument or null if not found
         * @type    {element}
         */
        getFirstAncestorByClassName: function (el, className) {

            var parent = el;

            while (parent = parent.parentNode) {
                if (Element.hasClassName(parent, className)) {
                    return parent;
                } // if
            } // while

            return null;

        },


        /**
         * Finds the absolute coordinates of an element in the page.
         *
         * @param   obj     The element which coordinates we're searching
         * @return  Array   An array with the coordinates:
         *                   - [0] => Elements x-coordinate
         *                   - [1] => Elements y-coordinate
         */
        findPos: function (obj) {

            var curleft = curtop = 0;

            // if the object supports offsetParent

            if (obj.offsetParent) {

                curleft = obj.offsetLeft;
                curtop  = obj.offsetTop;

                // update the coordinates while the element (object) has an offsetParent

                while (obj = obj.offsetParent) {
                    curleft += obj.offsetLeft;
                    curtop += obj.offsetTop;
                } // while

            } // if

            return [curleft, curtop];

        },


        /**
         * Clears an element by removing all the childnodes
         *
         * @param   el  The element to be cleared
         */
        removeChildNodes: function (el) {
            while (el.firstChild) {
                el.removeChild(el.firstChild);
            } // while
        },


        /**
         * Inserts the content as the first child of the parent element
         *
         * @param   {element}   content     The element to be inserted
         * @param   {element}   parent      The parent element
         */
        setAsFirstChild: function (content, parent) {

            if (Element.cleanWhitespace(parent).hasChildNodes()) {
                parent.insertBefore(content, parent.firstChild);
            } else {
                parent.appendChild(content);
            } // else

        },


        /**
         * Inserts the element as the next sibling of an element inside the parent
         * element. If the elementBefore is the last child of the parent, the new element
         * will be set as the last child of the parent element.
         *
         * @param   {element}   content         The element to be inserted
         * @param   {element}   elementBefore   The element that will be the previous
         *                                      sibling of the element we're inserting
         * @param   {element}   parent          The parent element
         */
        insertAfter: function (content, elementBefore, parent) {

            var nextSib = elementBefore.nextSibling;

            if (nextSib == undefined) {
                parent.appendChild(content);
            } else {
                parent.insertBefore(content, elementBefore.nextSibling);
            } // else

        },


        /**
         * Inserts the element after the elements in the elementArr -array. The function
         * checks whether the first element in the array exists and inserts the new element
         * as the next sibling of the element if it exits. If the element doesn't exist, the
         * next element in the array is checked and so on. If none of the elements in the
         * elementArr -array exist, the new element will be set as the first child of the
         * parent element.
         *
         * @param   {element}   content     The element being inserted
         * @param   {array}     elementArr  An array containing elements after which content
         *                                  will be inserted
         * @param   {element}   parent      The parent element
         */
        insertAfterElements: function (content, elementArr, parent) {

            var inserted = false;

            for (var i = 0; i < elementArr.length; i++) {
                if (elementArr[i] != undefined) {
                    OTOS.Dom.insertAfter(content, elementArr[i], parent);
                    inserted = true;
                    break;
                }  // if
            } // for

            if (!inserted) {
                OTOS.Dom.setAsFirstChild(content, parent);
            } // if

        },


        /**
         * Returns the first child element that has className in its class-attribute
         *
         * @param   {element}   className   The class name we're searching
         * @param   {element}   parent      The parent of which children we're going through
         * @return  The first child element that has className in its class-attribute
         * @type    {element}
         */
        getFirstChildByClassName: function (className, parent) {
            return document.getElementsByClassName(className, parent)[0];
        },


        /**
         *
         */
        getMaximumChildHeight: function (container, className) {

            var elList      = document.getElementsByClassName('element', container),
                elCount     = elList.length,
                maxHeight   = 0;

            for (var i = 0; i < elCount; i++) {

                var height = elList[i].offsetHeight;

                if (height > maxHeight) {
                    maxHeight = height;
                } // if

            } // for

            return maxHeight;

        },


        /**
         *
         */
        setChildHeight: function (container, className, newHeight) {

            var elList  = document.getElementsByClassName('element', container),
                elCount = elList.length;

            for (var i = 0; i < elCount; i++) {

                var currentEl = elList[i];

                currentEl.style.minHeight = newHeight + 'px';

                // a hack for ie, because it doesn't understand min-height
                if (currentEl.offsetHeight < newHeight) {
                    currentEl.style.height = newHeight + 'px';
                } // if

            } // for

        },


        /**
         * Returns the HTML element the event actually took place on, even
         * when the event handler has been registered not on the HTML element
         * but on one of its ancestors. IE doesn't support any kind of
         * currentTarget-property, so this is (propably) the only cross-browser
         * way to get the target element.
         *
         * @param   {event}     evt     The current event
         * @return  The target HTML-element
         * @type    {element}
         */
        getEventTarget: function (evt) {
            return (evt.target ? evt.target : (evt.srcElement ? evt.srcElement : false));
        }

    };

}();
