// bea.wlp.disc._util
/*
 * B E A   S Y S T E M S
 *
 * Copyright (c) 2000-2008  BEA Systems, Inc.
 *
 * All Rights Reserved. Unpublished rights reserved under the copyright laws of the United States. The software
 * contained on this media is proprietary to and embodies the confidential technology of BEA Systems, Inc. The
 * possession or receipt of this information does not convey any right to disclose its contents, reproduce it, or use,
 * or license the use, for manufacture or sale, the information or anything described therein. Any use, disclosure, or
 * reproduction without BEA System's prior written permission is strictly prohibited.
 *
 * Any entity defined in this scope that is named with a leading underscore ('_') is reserved for internal use only.
 */

bea.wlp.disc.Module.create("bea.wlp.disc._util", {
    declare: function($, L) {
        var $Disc = bea.wlp.disc;
        var ua = navigator.userAgent;

        $.Browser = {
            IE: !!(window.attachEvent && !window.opera) && new function() {
                var offset = ua.indexOf("MSIE ");
                this.version = +(ua.substring(offset + 5, ua.indexOf(";", offset)));
                this.is7Up = (this.version >= 7);
                this.isPre7 = !this.is7Up;
                this.jscript = ScriptEngine && parseFloat(ScriptEngineMajorVersion() + "." + ScriptEngineMinorVersion());
            },
            Gecko: !!(ua.indexOf("Gecko") > -1 && ua.indexOf("KHTML") == -1) && { },
            Opera: !!window.opera && { },
            WebKit: !!(ua.indexOf("AppleWebKit/") > -1) && { }
        }

        // See bea.wlp.disc._Object for more utilities available on this object
        $.Object = $Disc._Object.mixin({
            // Add additional object utilities here, as needed
        }, $Disc._Object);

        $.Array = {
            each: function(src, iterator, when) {
                if (src) {
                    for (var i = 0, len = src.length; i < len; i++) {
                        if (!when || when(i, src[i])) {
                            iterator(i, src[i]);
                        }
                    }
                }
            }
        }

        $.Function = {
            nop: function() { },
            filter: function(condition, fn) {
                return (condition ? fn : $.Function.nop);
            },
            name: function(fn) {
                var match = fn.toString().match(/function\s*([^\(\s]*)/);
                return (match ? match[1] : "");
            },
            signature: function(fn, body) {
                if (body) {
                    var sig = fn.toString().replace(/\s+/g, " ");
                    return (sig.length > 100 ? sig.substring(0, 97) + "..." : sig);
                }
                else {
                    var match = fn.toString().match(/function[^\)]*/);
                    return (match ? match[0] + ")" : "");
                }
            },
            trace: function(fn, start, depth, max) {
                fn = (fn || arguments.callee && arguments.callee.caller);
                if (fn && fn.caller) {
                    var trace = [];
                    start = (start || 0), depth = (depth || 0), max = (max || 10);
                    if (depth >= start) {
                        trace.push(fn);
                    }
                    if (depth < max) {
                        try {
                            var next = arguments.callee(fn.caller, start, depth + 1, max);
                            if (next) { trace = trace.concat(next); }
                        }
                        catch (ignore) {
                        }
                    }
                    if (depth == 0) {
                        trace.toString = function() {
                            var st = "";
                            $.Array.each(this, function(i, v) { st += "  " + $.Function.signature(v, true) + "\n"; });
                            return st;
                        }
                    }
                    return trace;
                }
            }
        }
        
        $.Error = $Disc.Class.create({
            initialize: function(error, traceFrom) {
                if (error instanceof Error) {
                    this.name = error.name;
                    this.message = error.message;
                    this.cause = error;
                }
                else if (typeof error == "string") {
                    this.name = "Error";
                    this.message = args;
                }
                else if (typeof error == "object") {
                    $.Object.mixin(this, error);
                }
                else {
                    this.name = "Error";
                }
                if (arguments.callee.caller) {
                    this.trace = $.Function.trace(arguments.callee, (traceFrom || 0) + 2);
                }
            },
            toString: function() {
                return (
                    (this.trace && (L("Trace to capture:") + "\n" + this.trace.toString())) ||
                    (this.cause && this.cause.toString()) ||
                    (this.name + (this.message ? ": " + this.message : ""))
                );
            }
        });

        $.Dom = {
            VISIBILITY: $.$meta.getName("Dom.visibility"),

            suppressSelects: $.Function.filter(
                $.Browser.IE.isPre7,
                function() {
                    var selects = document.getElementsByTagName("select");
                    if (selects) {
                        for (var i = 0, len = selects.length; i < len; i++) {
                            var visibility = selects[i].style.visibility;
                            var isSet = visibility && visibility.length > 0;
                            selects[i][$.Dom.VISIBILITY] = (isSet ? visibility : "visible");
                            selects[i].style.visibility = "hidden";
                        }
                    }
                }
            ),

            restoreSelects: $.Function.filter(
                $.Browser.IE.isPre7,
                function() {
                    var selects = document.getElementsByTagName("select");
                    if (selects) {
                        for (var i = 0, len = selects.length; i < len; i++) {
                            var visibility;
                            try {
                                visibility = selects[i][$.Dom.VISIBILITY];
                            }
                            catch (ignore) {
                            }
                            if (visibility) {
                                selects[i].style.visibility = visibility;
                            }
                            selects[i][$.Dom.VISIBILITY] = undefined;
                        }
                    }
                }
            ),

            purge: $.Function.filter(
                ($.Browser.IE && $.Browser.IE.jscript < 5.7),
                function(d) {
                    var a = d.attributes, i, l, n;
                    if (a) {
                        for (i = 0, l = a.length; i < l; i++) {
                            n = a[i].name;
                            if (typeof d[n] === 'function') {
                                d[n] = null;
                            }
                        }
                    }
                    a = d.childNodes;
                    if (a) {
                        for (i = 0, l = a.length; i < l; i++) {
                            arguments.callee(d.childNodes[i]);
                        }
                    }
                }
            ),

            clearContents: function(container) {
                while (container.hasChildNodes()) {
                    $.Dom.purge(container.firstChild);
                    container.removeChild(container.firstChild);
                }
            },

            getTextContent: function(el) {
                return (el.innerText ? el.innerText : (el.textContent ? el.textContent : ""));
            },

            getViewport: function() {
                var w = 0, h = 0;
                if ($.Browser.Gecko) {
                    w = document.documentElement.clientWidth;
                    h = window.innerHeight;
                }
                else {
                    if (!$.Browser.Opera && window.innerWidth) {
                        w = window.innerWidth;
                        h = window.innerHeight;
                    }
                    else {
                        if (!$.Browser.Opera && document.documentElement && document.documentElement.clientWidth) {
                            var w2 = document.documentElement.clientWidth;
                            if (!w || w2 && w2 < w) {
                                w = w2;
                            }
                            h = document.documentElement.clientHeight;
                        }
                        else {
                            if (document.body && document.body.clientWidth) {
                                w = document.body.clientWidth;
                                h = document.body.clientHeight;
                            }
                        }
                    }
                }
                return { width: w, height: h };
            },

            getScroll: function () {
                var top, left;
                if (window.pageYOffset)
                {
                    top = window.pageYOffset;
                    left = window.pageXOffset;
                }
                else if (document.documentElement)
                {
                    top = document.documentElement.scrollTop;
                    left = document.documentElement.scrollLeft;
                }
                else if (document.body)
                {
                    top = document.body.scrollTop;
                    left = document.body.scrollLeft;
                }
                return { top: top || 0, left: left || 0 };
            },

            getHeadTag: function() {
                var head = document.getElementsByTagName("head");
                if (head) {
                    head = head[0];
                }
                else {
                    head = document.createElement("head");
                    var body = document.getElementsByTagName("body");
                    if (body) {
                        document.documentElement.insertBefore(head, body[0]);
                    }
                    else {
                        document.appendChild(head);
                    }
                }
                return head;
            },

            eachAncestor: function(element, action, self) {
                if (!self) {
                    element = element.parentNode;
                }
                while (element && action(element)) {
                    element = element.parentNode;
                }
            },

            eachDescendantLinear: function(element, action, tagName) {
                var name = tagName || '*';
                var elements = element.getElementsByTagName(name);
                for (var i = 0; i < elements.length; i++) {
                    if (!action(elements[i])) {
                        break;
                    }
                }
            },

            eachDescendantRecursive: function(element, action, tagName) {
                var name = tagName.toLowerCase();
                var elements = element.childNodes;
                for (var i = 0; i < elements.length; i++) {
                    var child = elements[i];
                    var childName = child.tagName && child.tagName.toLowerCase();
                    if ( !((name == childName) || (name == '*')) || action(child)) {
                        this.eachDescendantRecursive(child, action, tagName);
                    }
                }
            }
        }

        $.Event = {
            getEvent: function(event) {
                return (event ? event : window.event);
            },
            connect: function(target, type, handler) {
                function wrapper(event) {
                    return handler($.Event.getEvent(event));
                }
                var result = false;
                if (target.addEventListener) {
                    target.addEventListener(type, wrapper, false);
                    result = true;
                }
                else if (target.attachEvent) {
                    result = target.attachEvent("on" + type, wrapper);
                }
                else {
                    var name = "on" + type;
                    var old = (target[name]) ? target[name] : function() {};
                    target[name] = function(event) { old(event); wrapper(event); };
                    result = true;
                }
                return result;
            }
        }

        $.Json = {
            parse: function(jsonText) {
                if (jsonText.indexOf("/*") == 0 && jsonText.lastIndexOf("*/") == jsonText.length - 2) {
                    jsonText = jsonText.substring(2, jsonText.length - 2);
                }
                return eval("(" + jsonText + ")");
            },
            serialize: function (obj) {
                var serialize = arguments.callee;
                var objtype = typeof (obj);
                var props = [];
                var value;
                var val;

                if (obj === null) {
                    value = "null";
                }
                else if (objtype == "undefined") {
                    value = "undefined";
                }
                else if ((objtype == "number") || (objtype == "boolean")) {
                    value = obj;
                }
                else if (objtype == "string") {
                    value = '"' + obj + '"';
                }
                else if (objtype == "function") {
                    value = "(function)";
                }
                else if ((objtype != "function") && (typeof (obj.length) == "number")) {
                    for (var i = 0; i < obj.length; i++) {
                        val = serialize(obj[i]);
                        props.push(val);
                    }
                    value = '[' + props.join(',') + ']';
                }
                else {
                    for (var p in obj) {
                        val = serialize(obj[p]);
                        props.push('"' + p + '"' + ':' + val);
                    }
                    value = '{' + props.join(',') + '}';
                }
                return value;
            }
        }

        // @review Param operations are compact, but inefficient
        $.Uri = $Disc.Class.create(function(Uri) {
            var regex = /^((\w+):\/\/)?((\w+):?(\w+)?@)?([^\/\?:]+)?:?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(\w*)/;
            var fields = { 'Protocol': 2, 'UserName': 4, 'Password': 5, 'Host': 6, 'Port': 7, 'PathName': 8, 'QueryString': 9, 'Fragment': 10 };
            this.initialize = function(config) {
                this._values = { };
                var self = this;
                $.Object.each(fields, function(k, v) {
                    self['get' + k] = function() { return this._values[k]; };
                    self['set' + k] = function(value) { this._values[k] = value || ''; };
                });
                if (config instanceof Uri) {
                    $.Object.each(fields, function(k, v) {
                        self['set' + k].call(self, config['get' + k].call(config));
                    });
                }
                else if (typeof config == 'object') {
                    $.Object.each(fields, function(k, v) {
                        var value = config[k.charAt(0).toLowerCase() + k.slice(1)];
                        value && self['set' + k].call(self, value);
                    });
                    config.params && this.setParams(config.params);
                }
                if (typeof config == 'string') {
                    var match = regex.exec(config);
                    if (!match) {
                        throw L('Invalid URI: {0}', config);
                    }
                    $.Object.each(fields, function(k, v) {
                        self['set' + k].call(self, match[v]);
                    });
                }
                $.Object.each({
                    add: function(name, value) { this[name] = (this[name] || []).push(value); },
                    set: function(name, value) { this[name] = [value]; },
                    get: function(name) { return this[name]; },
                    remove: function(name) { delete this[name]; }
                }, function(k, v) {
                    self[k + 'Param'] = function() {
                        var params = self.getParams();
                        var result = v.apply(params, arguments);
                        self.setParams(params);
                        return result;
                    }
                });
            }
            this.getParams = function() {
                var params = { };
                $.Array.each(this.getQueryString().split('&'), function(i, v) {
                    var kv = v.split('=');
                    if (kv[0]) {
                        var name = decodeURIComponent(kv[0]), value = decodeURIComponent(kv[1]) || '';
                        (params[name] = params[name] || []).push(value);
                    }
                });
                return params;
            }
            this.setParams = function(params) {
                var qs = '';
                $.Object.each(params, function(k, v) {
                    $.Array.each(v instanceof Array ? v : [v], function(i, item) {
                        qs = qs + (qs ? '&' : '') + encodeURIComponent(k) + '=' + encodeURIComponent(item);
                    });
                });
                this.setQueryString(qs);
            }
            this.toString = function() {
                var uri = '', self = this;
                function get(f) { return self._values[f]; }
                function pre(d, f) { return get(f) ? (d ? d : '') + get(f) : ''; }
                function post(f, d) { return get(f) ? get(f) + (d ? d : '') : ''; }
                uri += post('Protocol', '://');
                uri += post('UserName', pre(':', 'Password') + '@');
                uri += post('Host', pre(':', 'Port'));
                uri += pre('', 'PathName');
                uri += pre('?', 'QueryString');
                uri += pre('#', 'Fragment');
                return uri;
            }
        });
        
        $.Wlp = {
            // @review Ideally the WLP JSON response would encode the parsed fields separately
            parseHookId: function(hookId) {
                var label = hookId.slice(0, hookId.length - 5);
                var type = label.slice(0, 2);
                label = label.slice(2, label.length);
                return { label: label, type: type };
            }
        }
    }
});
