var genlib = {
    
    html_encode : function (text) {
        var div = document.createElement('div')
        div.innerHTML = text
        return div.innerHTML    
    },

    dom_ready : function (func) {
        if (func) {
            if (document.addEventListener) {
                document.addEventListener("DOMContentLoaded", 
                    function() {
                        document.removeEventListener( "DOMContentLoaded", arguments.callee, false)
                        func()
                    }, 
                    false)
            }
            else if (document.attachEvent) {  // IE
                document.attachEvent("onreadystatechange", 
                    function() {
                        if (document.readyState === "complete") {
                            document.detachEvent("onreadystatechange", arguments.callee)
                            func()
                        }
                    })
            }
        }
    },

    load_from : function (container_id, path, initialize) {
        var result = genlib.request(path,
            function (req) {
                var e = genlib.get_element(container_id)
                e.innerHTML = req.responseText
                if (initialize) {
                    initialize(e)
                }
            })
    },

    
    set_exclusive : function (element, selected_class, container_id) {
        var current = genlib.get_child_elements_with_class(genlib.get_element(container_id), selected_class)
        for (var j = 0; j < current.length; j++) {
            genlib.node_remove_class(current[j], selected_class)
        }
        genlib.node_add_class(element, selected_class)
    },
    
    set_value : function (element_id, value) {
        genlib.get_element(element_id).value = value
    },

    get_child_element_index : function (container, child_to_find) {
        var index = 0
        for (var child = container.firstChild; child; child = child.nextSibling) {
            if (child_to_find == child)
                break
            index += 1
        }
        return index
    },

    get_child_element_with_class : function (container, child_class) {
        for (var child = container.firstChild; child; child = child.nextSibling) {
            if (genlib.element_node_has_class(child, child_class))
                return child
        }
    },

    get_child_elements_with_class : function (container, child_class) {
        var children_found = []
        for (var child = container.firstChild; child; child = child.nextSibling) {
            if (genlib.element_node_has_class(child, child_class)) {
                children_found.push(child)
            }
            var more_children_found = genlib.get_child_elements_with_class(child, child_class)
            if (more_children_found.length > 0) {
                children_found = children_found.concat(more_children_found)
            }
        }
        return children_found
    },

    get_child_elements : function (container) {
        var elements = new Array()
        for (var child = container.firstChild; child; child = child.nextSibling) {
            if (child.nodeType == 1) {
                elements.push(child)
            }
        }
        return elements
    },

    element_node_has_class : function (elem_node, class_name) {
        if (elem_node.nodeType == 1) { //avoid accessing className except with Element node types
            var class_names = elem_node.className.split(" ")
            for (var j = 0; j < class_names.length; j++) {
                if (class_names[j] == class_name)
                    return true
                    }
            return false
        }
        return false
    },

    get_child_with_class : function (container, child_class) {
        for (var child = container.firstChild; child; child = child.nextSibling) {
            if (genlib.node_has_class(child, child_class))
                return child
        }
    },

    get_parent_with_class : function (container, parent_class) {
        for (var parent = container.parentNode; parent; parent = parent.parentNode) {
            if (parent && genlib.node_has_class(parent, parent_class))
                return parent
        }
    },

    show_popup : function (container, popup_class) {
        var popup = genlib.get_child_with_class(container, popup_class)
        popup.style.display = 'block'
    },

    hide_popup : function (container, popup_class) {
        var popup = genlib.get_child_with_class(container, popup_class)
        popup.style.display = 'none'
    },

    node_get_text_data_list : function (node, exceptions) {
        // Return list of data strings from text nodes from node and below.
        if (exceptions) {
            for (i in exceptions) {
                if (genlib.node_has_class(node, exceptions[i])) {
                    return []
                }            
            }
        }

        if (node.data) {
            return [node.data]
        } else {
            var parts = []
            for (var child = node.firstChild; child; child = child.nextSibling)
                parts = parts.concat(genlib.node_get_text_data_list(child, exceptions))
            return parts
        }
    },

    node_get_text_data_string : function (node, exceptions) {
        return genlib.node_get_text_data_list(node, exceptions).join(" ")
    },

    node_has_class : function (node, class_name) {
        if (node.className) {
            var class_names = node.className.split(" ")
            for (var j = 0; j < class_names.length; j++) {
                if (class_names[j] == class_name)
                    return true
            }
        }
        return false
    },

    node_add_class : function (node, class_name) {
        if (!genlib.node_has_class(node, class_name)) {
            if (node.className)
                node.className += " " + class_name
            else
                node.className = class_name
        }
    },

    node_remove_class : function (node, class_name) {
        if (genlib.node_has_class(node, class_name)) {
            var class_names = node.className.split(" ")
            var new_class_names = []
            for (var j = 0; j < class_names.length; j++) {
                if (class_names[j] != class_name)
                    new_class_names.push(class_names[j])
            }
            node.className = (new_class_names.join(" ") || "")
        }
    },

    node_toggle_class : function (node, class_name) {
        if (genlib.node_has_class(node, class_name))
            genlib.node_remove_class(node, class_name)
        else
            genlib.node_add_class(node, class_name)
    },

    node_replace_children : function (node, node_array) {
        while (node.firstChild)
            node.removeChild(node.firstChild)
        for (var j = 0; j < node_array.length; j++)
            node.appendChild(node_array[j])
    },
    
    classify_children : function (container, test_function, yes_class, no_class) {
        if (!no_class) {
            no_class = "not_" + yes_class
        }
        if (container) {
            var children = genlib.get_child_elements(container)
            for (var i in children) {
                var child = children[i]
                if (test_function(child)) {
                    genlib.node_add_class(child, yes_class)
                    genlib.node_remove_class(child, no_class)                    
                } else {
                    genlib.node_add_class(child, no_class)
                    genlib.node_remove_class(child, yes_class)
                }
            }
        }
    },

    install_hovering : function (node, hovering) {
        if (!hovering) { hovering = 'hovering' }
        node.onmouseover = function (e) {genlib.node_add_class(node, hovering)}
        node.onmouseout  = function (e) {genlib.node_remove_class(node, hovering)}
    },
    
    map : function (func, array) {
        var result = []
        for (var j = 0; j < array.length; j++)
            result.push(func(array[j]))
        return result
    },

    filter : function (predicate, array) {
        var result = []
        for (var j = 0; j < array.length; j++)
            if (predicate(array[j]))
                result.push(array[j])
        return result
    },

    first : function (x) { return x[0] },

    second : function (x) { return x[1] },

    sorted_with_key : function (array, get_key_func) {
        var tmp = genlib.map(function (x) { return [get_key_func(x), x] }, array)
        tmp.sort()
        return genlib.map(genlib.second, tmp)
    },

    all_words_match_text : function (words, text) {
        for (var j = 0; j < words.length; j++) {
            if (text.indexOf(words[j]) == -1) {
                return false
            }
        }
        return true
    },

    get_element : function (id, quiet) {
        var e = document.getElementById(id)
        if (!quiet && !e)
            console.error('Element not found: ', id)
        return e
    },

    get : function (id) {
        return document.getElementById(id)
    },

    element_toggle_display : function (e) {
        if (e.style.display != 'none')
            e.style.display = 'none'
        else
            e.style.display = 'block'
    },

    display_block : function (id) {
        var e = document.getElementById(id)
        if (e) {
            e.style.display = 'block'
        }
    },
    
    display_none : function (id) {
        var e = document.getElementById(id)
        if (e) {
            e.style.display = 'none'
        }
    }, 

    id_toggle_display : function (id) {
        if (id && genlib.get_element(id, true)) {
            genlib.element_toggle_display(genlib.get_element(id))
        }
    },

    set_img_src_from_delayed : function (container) {
        var imgs = container.getElementsByTagName('img');
        var img;
        var path;
        for (var j=0; j < imgs.length; j++) {
            img = imgs[j]
            if (!img.src) {
                path = img.getAttribute('delayed')
                if (path) {
                    img.src = path
                }
            }
        }
    },

    request : function (url, callback, post_body) {
        // Get an XMLHTTPRequest object and send a GET for the URL.
        // When the complete response has returned, call the callback
        // function with the XMLHTTPRequest object as the argument,
        // but only if the response status is 200 and the response
        // body is not empty.
        var req = null
        if (window.XMLHttpRequest) {
            req = new XMLHttpRequest()
        } else if (window.ActiveXObject) {
            try {
                req = new ActiveXObject("Msxml2.XMLHTTP")
            } catch (exc) {
                try {
                    req = new ActiveXObject("Microsoft.XMLHTTP")
                } catch (exc) {
                    req = new ActiveXObject("Msxml2.XMLHTTP.4.0")
                }
            }
        }
        req.onreadystatechange = function (e) {
            if (req.readyState == 4) {
                if (req.status == 200 && req.responseText != '') callback(req)
                else console.error(req.status, '\n', req.responseText)
            }
        }
        if (post_body) {
            req.open("POST", url, true)
            req.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
            // post_body should be in the form of: param1=param1value&params2=param2value
            // PARAM VALUES must be encoded
            req.send(post_body)
        } else {
            req.open("GET", url, true)
            req.send("")
        }
    },

    animate : function (func, start_value, end_value, duration_ms, done_func, frames) {
        if (done_func == undefined || done_func == null) {
            var done_func = function () {}
        }
        if (frames == undefined || frames == null) {
            var frames = (duration_ms / 1000) * 60
        }
        if (frames < 1) {
            frames = Math.abs(Math.round(end_value) - Math.round(start_value))
        }
        var fps = (frames * 1000) / duration_ms
        var step = (end_value - start_value) / frames
        var count = 0
        var interval_id = setInterval(
            function () {
                if (count < frames) {
                    if (count == frames-1) {
                        start_value = end_value
                    } else {
                        start_value += (end_value - start_value) / (frames - count)
                    }
                    func(start_value)
                    count++
                } else {
                    clearInterval(interval_id)
                    done_func()
                }
            },
            1000 / fps)
    },

    center_container_based_on_children : function(container, parent_width) {
        var children = genlib.get_child_elements(container)
        var width_sum = 0
        for (var i in children) {
            var child = children[i]
            if (child.offsetHeight > 0) {
                var width = child.offsetWidth
                if (width && width > 0) {
                    width_sum += width
                }
            }
        }
        if (width_sum > 0) {
            var extra_width = 5
            width_sum += extra_width
            container.style.width = width_sum + "px"
            var left_padding = Math.floor((parent_width - width_sum) / 2)
            if (left_padding > 0) {
                container.style.marginLeft = left_padding + "px" 
            }
        }
    }

} // genlib
