/* minimize using http://closure-compiler.appspot.com/home with Simple optimization */
var PFLY = new (function () {
    var changedElementsStore, saveButton,
        ldtPrefix = "|LDT|", ldtSep = "|";

    // Resource strings are initialized with default texts
    this.ResourceStrings = {
        ErrorTitle: "Error",
        InfoTitle: "Information",
        ConfirmTitle: "Please confirm",
        OkButton: "Ok",
        CancelButton: "Cancel",
        LdtEditTitle: "Edit texts",
        RequiredFlashVersion: 9,
        CurrentLanguageId: "en-GB",
        AciveLanguages: [{ id: "en-GB", name: "English"}]
    }

    // Replaces the default texts with provided texts
    this.SetResourceStrings = function (resourceStrings) {
        for (var name in resourceStrings) {
            if (this.ResourceStrings.hasOwnProperty(name))
                this.ResourceStrings[name] = resourceStrings[name];
        }
    }

    this.DecorateSelectionDependant = function (active) {
        jQuery(".SelectionDependant").each(function () {
            var t = jQuery(this);
            if (active)
                t.addClass("Active");
            else
                t.removeClass("Active");
        });
    }

    this.FocusFirstInput = function (isPostback) {
        if (!isPostback) {
            jQuery(function () {
                function SetFocus() { jQuery(':input:not(:checkbox):visible:enabled:first').focus(); }
                setTimeout(SetFocus, 100); // must delay to function
            });
        }
    }

    this.EnableDefaultButton = function (formSelector, buttonSelector) {
        jQuery(function () {
            var saveButton = jQuery(buttonSelector);
            if (saveButton.length > 0) {
                jQuery(formSelector).keypress(function (e) {
                    if (e.which == 13) {
                        saveButton.click();
                        return false;
                    }
                });
            }
        });
    }

    // Binds a click event handler to all anchors with target="dialog" which
    // opens page in jQuery dialog window.
    this.InitDialogLinks = function () {
        jQuery(function () {
            jQuery("a").each(function () {
                var a = jQuery(this);
                if (a.attr("target") == "dialog") {
                    a.click(function (elem) {
                        PFLY.OpenDialog(a.attr("href"));
                        return false;
                    });
                }
            });
        });
    }

    function HighlightSaveButton() {
        if (saveButton) {
            saveButton.addClass('Active');
        }
    }

    function HighlightChangedElement(input) {
        if (input) {
            input.addClass('InputChanged');
        }
    }

    function AddChangedElement(input) {
        if (changedElementsStore) {
            var id = input.attr('id');
            if (id && id.length > 0) {
                id = "[" + id + "]";
                var elems = changedElementsStore.attr('value');
                if (elems.indexOf(id) < 0) {
                    if (elems.length == 0)
                        HighlightSaveButton();
                    else
                        elems += ",";
                    elems += id;
                    changedElementsStore.attr('value', elems);
                }
            }
        }
    }

    this.ToggleGroup = function (collapser, state, fs) {
        state = jQuery("#" + state);
        var show = state.val() != "1";
        state.val(show ? "1" : "0");
        fs = jQuery("#" + fs);
        fs.toggle(show);
        collapser = jQuery(collapser);
        if (show)
            collapser.addClass("Collapse");
        else
            collapser.removeClass("Collapse");
    }

    // Marke element (identified by jQuery object) as changed
    this.MarkAsChanged = function (jq) {
        if (jq.hasClass('NoHighlight') || jq.parent().hasClass('NoHighlight')) return false;
        HighlightChangedElement(jq);
        AddChangedElement(jq);
    }

    // Add change listeners to specified input element which
    // highlights the field when changes are detected
    this.ListenForChanges = function (input) {
        function Highlight() {
            HighlightChangedElement(input);
            AddChangedElement(input);
            input.unbind("keydown", OnKeydown);
            input.unbind("keypress", OnKeyPress);
            input.unbind("change", OnChange);
            input.unbind("click", OnChange);
        }

        function OnChange(e) {
            Highlight();
            return true;
        }
        function OnKeyPress(e) {
            if (e.which != 0) {
                Highlight();
            }
            return true;
        }
        function OnKeydown(e) {
            if (e.which == 8 || e.which == 46) {
                Highlight();
            }
            return true;
        }

        input.bind("keydown", OnKeydown);
        input.bind("keypress", OnKeyPress);
        input.bind("change", OnChange);
        if (input.attr('type') == 'checkbox' || input.attr('type') == 'radio')
            input.bind("click", OnChange);
    }

    // Highlight elements changed
    this.HighlightChangedElements = function (changedElementsStoreId) {
        jQuery(function () {
            changedElementsStore = jQuery('#' + changedElementsStoreId);
            saveButton = jQuery('.SaveButton');
            PFLY.MarkChangedElements();

            function HandleThisElement(input) {
                if (input.hasClass('NoHighlight') || input.parent().hasClass('NoHighlight') || input.attr("readonly") == true) return false;
                if (input.attr('type') == 'checkbox') {
                    var name = input.attr('name');
                    if (name && name.length >= 8 && name.lastIndexOf('selector') == (name.length - 8)) return false;
                }
                return true;
            }

            // listen for changes
            jQuery('input, textarea, select').each(function () {
                var input = jQuery(this);

                if (HandleThisElement(input)) {
                    PFLY.ListenForChanges(input);
                }
            });
        });
    }

    // Highlight elements changed
    this.MarkChangedElements = function (val) {
        if (changedElementsStore) {
            if (val)
                changedElementsStore.val(val);
            var elems = changedElementsStore.val().split(',');
            var firstTime = true;
            for (var i = 0; i < elems.length; i++) {
                var id = elems[i];
                if (id && id.length > 2) {
                    id = id.substring(1, id.length - 1);
                    if (firstTime) {
                        HighlightSaveButton();
                        firstTime = false;
                    }
                    HighlightChangedElement(jQuery('#' + id));
                }
            }
        }
    }

    // Adjust container heights to take the space available
    this.AdjustContainerHeights = function () {
        jQuery(function () {
            var firstTime = true;
            var isIE6 = PFLY.IsIE6();
            var resizeTimer = null;

            function AdjustHeights() {
                var maxH = jQuery(".DoMaxHeight")
                var maxW = jQuery(".DoMaxWidth");
                var winWidth = jQuery(window).width();
                var m = jQuery('#NodeMenuContainer');
                var content = jQuery('.Pf_Content');
                var f = jQuery('#Pf_Footer');
                var bTop = jQuery('#Pf_AdminBottom').offset().top;
                var fHeight = f.height();

                if (isIE6) bTop += 15;

                if (firstTime && f.children().length > 0)
                    f.show();
                //alert("left: " + left);

                var left = content.offset().left; // prefetch before doing changes
                var top = content.offset().top;

                if (m.length > 0)
                    m.height(bTop - m.offset().top);

                maxH.each(
                    function () {
                        var e = jQuery(this);
                        e.height(bTop - e.offset().top - fHeight - 1); // -1 needed for iframes
                    }
                );
                maxW.each(
                    function () {
                        var e = jQuery(this);
                        e.width(winWidth - e.offset().left);
                    }
                );

                content.height(bTop - top - fHeight);
                content.width(winWidth - left);
                firstTime = false;
            }
            if (isIE6)
                setTimeout(AdjustHeights, 200);
            else {
                AdjustHeights();
                setTimeout(AdjustHeights, 1); // some browsers requires a final run 
            }
            jQuery(window).resize(function () {
                if (isIE6) {
                    clearTimeout(resizeTimer);
                    resizeTimer = setTimeout(AdjustHeights, 200);
                } else
                    AdjustHeights();
            });
        });
    }

    // Set the title of the dialog within the parent window
    this.SetParentWindowDialogTitle = function (title) {
        jQuery(function () {
            if (window.parent)
                window.parent.jQuery("#UrlDialog").dialog('option', 'title', title);
        });
    }

    // Restore and save scroll position of given target element
    this.MaintainScrollPosition = function (targetId, offsetTopId) {
        if (PFLY.jQueryExist())
            jQuery(function () {
                var target = jQuery('#' + targetId);
                var offset = jQuery('#' + offsetTopId);

                target.scrollTop(offset.val());

                target.scroll(function () {
                    offset.val(target.scrollTop());
                    return true;
                });
            });
    }

    // Returns true if the flash palyer is installed
    this.IsFlashPlayerInstalled = function () {
        // The following is extracted from Flash Player Version Detection - Rev 1.6, see http://www.adobe.com/products/flashplayer/download/detection_kit/
        var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
        var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
        var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;

        function ControlVersion() {
            var version;
            var axo;
            var e;

            // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry

            try {
                // version will be set for 7.X or greater players
                axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
                version = axo.GetVariable("$version");
            } catch (e) {
            }

            if (!version) {
                try {
                    // version will be set for 6.X players only
                    axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");

                    // installed player is some revision of 6.0
                    // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29,
                    // so we have to be careful. 

                    // default to the first public version
                    version = "WIN 6,0,21,0";

                    // throws if AllowScripAccess does not exist (introduced in 6.0r47)		
                    axo.AllowScriptAccess = "always";

                    // safe to call for 6.0r47 or greater
                    version = axo.GetVariable("$version");

                } catch (e) {
                }
            }

            if (!version) {
                try {
                    // version will be set for 4.X or 5.X player
                    axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
                    version = axo.GetVariable("$version");
                } catch (e) {
                }
            }

            if (!version) {
                try {
                    // version will be set for 3.X player
                    axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
                    version = "WIN 3,0,18,0";
                } catch (e) {
                }
            }

            if (!version) {
                try {
                    // version will be set for 2.X player
                    axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
                    version = "WIN 2,0,0,11";
                } catch (e) {
                    version = -1;
                }
            }

            return version;
        }

        // JavaScript helper required to detect Flash Player PlugIn version information
        function GetSwfVer() {
            // NS/Opera version >= 3 check for Flash plugin in plugin array
            var flashVer = -1;

            if (navigator.plugins != null && navigator.plugins.length > 0) {
                if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
                    var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
                    var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
                    var descArray = flashDescription.split(" ");
                    var tempArrayMajor = descArray[2].split(".");
                    var versionMajor = tempArrayMajor[0];
                    var versionMinor = tempArrayMajor[1];
                    var versionRevision = descArray[3];
                    if (versionRevision == "") {
                        versionRevision = descArray[4];
                    }
                    if (versionRevision[0] == "d") {
                        versionRevision = versionRevision.substring(1);
                    } else if (versionRevision[0] == "r") {
                        versionRevision = versionRevision.substring(1);
                        if (versionRevision.indexOf("d") > 0) {
                            versionRevision = versionRevision.substring(0, versionRevision.indexOf("d"));
                        }
                    }
                    flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
                }
            }
            // MSN/WebTV 2.6 supports Flash 4
            else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4;
            // WebTV 2.5 supports Flash 3
            else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3;
            // older WebTV supports Flash 2
            else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2;
            else if (isIE && isWin && !isOpera) {
                flashVer = ControlVersion();
            }
            return flashVer;
        }

        // When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
        function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) {
            var versionStr = GetSwfVer();
            if (versionStr == -1) {
                return false;
            } else if (versionStr != 0) {
                if (isIE && isWin && !isOpera) {
                    // Given "WIN 2,0,0,11"
                    tempArray = versionStr.split(" "); 	// ["WIN", "2,0,0,11"]
                    tempString = tempArray[1]; 		// "2,0,0,11"
                    versionArray = tempString.split(","); // ['2', '0', '0', '11']
                } else {
                    versionArray = versionStr.split(".");
                }
                var versionMajor = versionArray[0];
                var versionMinor = versionArray[1];
                var versionRevision = versionArray[2];

                // is the major.revision >= requested major.revision AND the minor version >= requested minor
                if (versionMajor > parseFloat(reqMajorVer)) {
                    return true;
                } else if (versionMajor == parseFloat(reqMajorVer)) {
                    if (versionMinor > parseFloat(reqMinorVer))
                        return true;
                    else if (versionMinor == parseFloat(reqMinorVer)) {
                        if (versionRevision >= parseFloat(reqRevision))
                            return true;
                    }
                }
                return false;
            }
        }

        var rv = this.ResourceStrings.RequiredFlashVersion.split(".");
        return DetectFlashVer(rv[0], rv[1] != undefined ? rv[1] : '0', rv[2] != undefined ? rv[2] : '0')
    }

    this.CloseDialog = function () {
        if (window.opener && !window.opener.closed) {
            window.opener.focus();
            window.close();
        } else if (window.parent) {
            window.parent.jQuery("#UrlDialog").dialog("close");
        }
    }

    this.DisableInputWhileProcessing = function (delay, dialogSelector) {
        jQuery(function () {
            // we use a modal dialog to block input, but we should check out the jQuery block plugin ...
            // note that the dialog content must be predefined - it can not be genrated dynamically as it is
            // to be displayed on the form submit event
            function DisableInput() {
                jQuery(dialogSelector).dialog({
                    modal: true,
                    resizable: false,
                    draggable: false,
                    dialogClass: "DisableInputWhileProcessingDialog"
                });
            }

            jQuery("form").each(function () {
                // note: We do not use the jQuery "submit" event because it does not fire for asp (Javascript) postbacks
                var theForm = this;
                var currentSubmitFunction = theForm.onsubmit;

                function SubmitHook() {
                    var ok = true;
                    if (theForm != null) {
                        if (currentSubmitFunction != null && typeof (currentSubmitFunction) == "function") {
                            with (theForm)
                                ok = currentSubmitFunction();
                        }
                        if (ok) {
                            setTimeout(DisableInput, delay);
                            theForm = null; // to avoid possible GC error in IE
                        }
                    }
                    return ok;
                }

                this.onsubmit = SubmitHook;
            }
            );
        });
    }

    // Execute callback and if requested close current window when done.
    this.ExecuteCallback = function (callback, doClose) {
        // find the window object to execute the callback
        var executor;
        if (window.opener && !window.opener.closed)
            executor = window.opener;
        else if (window.parent)
            executor = window.parent;
        else
            executor = null;

        // as of jQuery 1.4.2, we must close the dialog before executing the callback
        // else a callback performing a submit click will not work
        if (doClose)
            this.CloseDialog();

        // finally, do the callback
        if (executor != null) {
            try {
                executor.eval(callback);
            } catch (e) {
                executor.alert("Executing '" + callback + "' failed, error: " + e.Message);
            }
        }
    }

    // Build url by adding parameters to base urls.
    // The urlParams must be a list of paramater name and value pairs.
    // Eksamles of legal calls:
    //      BuildUrl("page.aspx")
    //      BuildUrl("page.aspx","p1",value1,"p2",value2)

    this.BuildUrl = function (baseUrl, urlParams) {
        var sep = baseUrl.indexOf('?') >= 0 ? '&' : '?';
        for (var i = 1; i < arguments.length; i += 2) {
            baseUrl += sep + arguments[i] + '=' + escape(arguments[i + 1]);
            sep = '&';
        }
        return baseUrl;
    }

    this.jQueryExist = function () {
        return typeof jQuery == "function";
    }

    this.useJQ = function () {
        return this.jQueryExist();
    }

    this.dialogNumber = 0;

    this.ShowDialog = function (diaTitle, diaText, diaClass, diaButtons) {
        var diaId = "DynamicDialog_" + (this.dialogNumber++);
        jQuery("body").append("<div id='" + diaId + "'><div class='" + diaClass + "'>" + diaText + "</div></div>");
        var dia = jQuery("#" + diaId);
        var options =
            {
                bgiframe: true,
                modal: true,
                autoOpen: true,
                closeOnEscape: true,
                resizable: false,
                minWidth: 0,
                minHeight: 0,
                title: diaTitle,
                close: function (event, ui) {
                    dia.dialog('destroy');
                    dia.remove();
                }
            };


        if (diaText != null && diaText.length > 100)
            options.width = 800; // since jQuery does not support width='auto' on all browsers, we use a fixed size bigger dialog if the text is very long

        if (diaButtons)
            options.buttons = diaButtons;
        dia.dialog(options);
        return dia;
    }

    this.ShowErrorDialog = function (message) {
        var buttons = {};
        buttons[this.ResourceStrings.OkButton] = function () { jQuery(this).dialog('close'); };
        this.ShowDialog(this.ResourceStrings.ErrorTitle, message, "ErrorDialog", buttons)
    }

    this.ShowConfirmDialog = function (message, okFunction) {
        var buttons = {};
        buttons[this.ResourceStrings.OkButton] = function () { jQuery(this).dialog('close'); if (okFunction) okFunction(); };
        buttons[this.ResourceStrings.CancelButton] = function () { jQuery(this).dialog('close'); };
        this.ShowDialog(this.ResourceStrings.ConfirmTitle, message, "ConfirmDialog", buttons)
    }

    this.ShowInfoDialog = function (message, closeFunction) {
        var buttons = {};
        buttons[this.ResourceStrings.OkButton] = function () { jQuery(this).dialog('close'); if (closeFunction) closeFunction(); };
        this.ShowDialog(this.ResourceStrings.InfoTitle, message, "InfoDialog", buttons)
    }

    this.IsIE6 = function () {
        return jQuery.browser.msie && (jQuery.browser.version == "6.0") && !window.XMLHttpRequest;
    }

    // Open centered dialog window showing the defined url.
    // If the width and/or height parameters are unspecified default sizes are used.
    this.OpenDialog = function (url, width, height, callbackOnClose) {
        if ((!width) || width < 1) width = jQuery(window).width() - 100;
        if ((!height) || height < 1) height = jQuery(window).height() - 100;

        var IsIE6 = this.IsIE6();

        if (!IsIE6) {
            var body = jQuery("body");
            var bodyOverflow = body.css("overflow");
            body.css("overflow", "hidden");
            body.append("<div id='UrlDialog' style='display:none;'></div>");
            var dia = jQuery("#UrlDialog");

            dia.append('<iframe id="UrlDialogIFrame" style="display:none;" marginheight="0" marginwidth="0" frameborder="0" src="' + url + '"></iframe>');

            var theIFrame = jQuery('#UrlDialogIFrame');

            function DoResize() {
                theIFrame.height(dia.height());
                theIFrame.width(dia.width());
                theIFrame.show();
            }

            function DoOpen() {
                if (!isOpened) {
                    isOpened = true;
                    dia.dialog('open');
                    DoResize();
                }
            }

            function DoCustomize(w) {
                if (typeof (w.DialogOptions) == "function") {
                    var localOptions = w.DialogOptions();
                    if (localOptions.width || localOptions.height) {
                        theIFrame.hide();
                        if (localOptions.width)
                            dia.dialog("option", "width", localOptions.width + wSpace);
                        if (localOptions.height)
                            dia.dialog("option", "height", localOptions.height + hSpace);
                        dia.dialog("option", "position", "center");
                        if (isOpened)
                            DoResize();
                    }
                    if (localOptions.title)
                        dia.dialog("option", "title", localOptions.title);
                }
                DoOpen();
            }

            var hSpace = 40;
            var wSpace = 30;
            var isOpened = false;
            var options =
                    {
                        bgiframe: true,
                        modal: true,
                        autoOpen: false,
                        closeOnEscape: false,
                        resizable: true,
                        minHeight: 0,
                        minWidth: 0,
                        height: height + hSpace,
                        width: width + wSpace,
                        resizeStart: function () { theIFrame.hide(); },
                        resizeStop: DoResize,
                        dragStart: function () { theIFrame.hide(); },
                        dragStop: function () { theIFrame.show(); },
                        close: function () {
                            dia.dialog('destroy');
                            dia.remove();
                            body.css("overflow", bodyOverflow);
                            if (callbackOnClose) eval(callbackOnClose);
                        }
                    };

            dia.dialog(options);

            theIFrame.load(function () {
                DoCustomize(this.contentWindow);
            });

            // we delay the opening a second hoping for the iframe load to execute first
            setTimeout(DoOpen, 1000);

            return window; // hm ..
        } else {
            var left = (screen.width - width) / 2;
            var top = (screen.height - height) / 2;
            var params = 'width=' + width + ', height=' + height;
            params += ', top=' + top + ', left=' + left;
            params += ', directories=no';
            params += ', location=no';
            params += ', menubar=no';
            params += ', resizable=yes';
            params += ', scrollbars=yes';
            params += ', status=no';
            params += ', toolbar=no';
            var newwin = window.open(url, '_blank', params);

            if (callbackOnClose) {
                var timer = null;
                function DetectClose() {
                    if (newwin.closed) {
                        window.clearInterval(timer);
                        eval(callbackOnClose);
                    }
                }
                timer = window.setInterval(DetectClose, 1000);
            }

            if (newwin.focus)
                newwin.focus();
            return newwin;
        }
    }

    this.DateSelectedTarget = null;

    this.DateSelectedCallback = function (date) {
        PFLY.DateSelectedTarget.attr('value', date);
        PFLY.MarkAsChanged(PFLY.DateSelectedTarget);
    }

    // Build calendard url.
    // If p2 is NOT supplied, p1 must be the ID of the element containing the date to be modified.
    // If p2 is supplied, p1 must contain the currently selected date and p2 a callback method.
    // The date format to use is the format produced bye the C# DateTime.ToShortDateString() method.
    this.BuildCalendarUrl = function (url, p1, p2) {
        var callback;
        var selectedDate;
        if (p2) {
            selectedDate = p1;
            callback = p2;
        } else {
            PFLY.DateSelectedTarget = jQuery('#' + p1);
            selectedDate = PFLY.DateSelectedTarget.attr('value');
            callback = "PFLY.DateSelectedCallback";
        }

        url += "?SelectedDate=" + selectedDate + "&CB=" + callback;
        return url;
    }

    // Open calendar dialog.
    // If p2 is NOT supplied, p1 must be the ID of the element containing the date to be modified.
    // If p2 is supplied, p1 must contain the currently selected date and p2 a callback method.
    // The date format to use is the format produced bye the C# DateTime.ToShortDateString() method.
    this.OpenCalendar = function (url, p1, p2) {
        return this.OpenDialog(this.BuildCalendarUrl(url, p1, p2), 180, 180);
    }

    // Call this function from body onload when you want to ensure that the session lives 
    // even if the user is passive for a longer periode than the session timeout. Don't missuse!
    //
    // The method uses AJAX to regulary poll the server ensuring the session is keept alive.
    // pathPrefix: Path prefix identifying the site root. 
    //
    // Example: If you call this method from a page "mypages/mypage1.aspx", use the following code:
    //    <head>
    //        <script language="JavaScript1.2" type="text/JavaScript" src="../jscript/papirfly.js" ></script>
    //    </head>
    //    <body onload="PFLY.KeepSessionAlive('..');">
    //    ...
    this.KeepSessionAlive = function (pathPrefix) {
        var ka = new KeepAlive(pathPrefix);
        setTimeout(ka.Run, 5 * 60 * 1000);
    }

    this.KeepSessionAliveCount = 0;

    function KeepAlive(pathPrefix) {
        this.Run = function () {
            window.status = 'KeepSessionAlive #' + (++PFLY.KeepSessionAliveCount);
            var httpReq = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
            httpReq.onreadystatechange = function () {
                if (httpReq.readyState == 4 && httpReq.status == 200) {
                    PFLY.KeepSessionAlive(pathPrefix);
                }
            };

            httpReq.open("GET", pathPrefix + "/common/KeepSessionAlive.ashx", true);
            httpReq.send(null);
        };
    }

    function hasClass(obj) {
        var c = obj.getAttributeNode("class");
        return c != null ? c.value : false;
    }

    // Set alternating row background colors for given table
    // The background color is set on each td, but only if the td has no css class and no background style set
    this.Stripe = function (id, evenColor, oddColor) {
        var table = document.getElementById(id);
        if (!table)
            return;

        if (!evenColor)
            evenColor = "#ffffff";
        if (!oddColor)
            oddColor = "#e0e0e0";

        var even = false;
        var trs = table.rows;

        for (var i = 0; i < trs.length; i++) {
            var color = even ? evenColor : oddColor;
            even = !even;
            var mytr = trs[i];

            if (!hasClass(mytr) && !mytr.style.backgroundColor) {
                var tds = mytr.cells;
                for (var j = 0; j < tds.length; j++) {
                    var mytd = tds[j];
                    if (!hasClass(mytd) && !mytd.style.backgroundColor) {
                        mytd.style.backgroundColor = color;
                    }
                }
            }
        }
    }

    // Set all checkboxes with given name to given value
    this.SetCheckboxes = function (checkboxName, checked) {
        jQuery("input[name='" + checkboxName + "']:not(:disabled)").each(function () {
            this.checked = checked;
        });
    }

    // Get the number of checked and enabled checkboxes with given name
    this.GetSelectedCount = function (checkboxName) {
        var count = 0;
        jQuery("input[name='" + checkboxName + "']:checked:not(:disabled)").each(function () {
            count++;
        });
        return count;
    }

    // Get the values, as comma separated list, of all checked and enabled checkboxes with given name
    this.GetSelectedValues = function (checkboxName) {
        var result = "";
        var sep = "";
        jQuery("input[name='" + checkboxName + "']:checked:not(:disabled)").each(function () {
            result += sep + this.value;
            sep = ",";
        });
        return result;
    }

    this.OpenWindow = function (url, width, height) {
        var top = (window.screen.availHeight - height) / 2;
        var left = (window.screen.availWidth - width) / 2;
        var features = "top=" + top + ",left=" + left + ",width=" + width + ",height=" + height +
            ",resizable=1,scrollbars=0,status=0";
        var w = window.open(url, "_blank", features);
        w.focus();
        return w;
    }

    this.ShowDropDownList = function (id) {
        var opener = jQuery('#' + id + '_opener');
        var selector = jQuery('#' + id + '_selector');
        selector.css("top", opener.position().top);
        selector.css("left", opener.position().left);
        //opener.hide();
        selector.slideDown("fast");
        jQuery(document).bind("mousedown.papirfly", function (e) {
            var offset = selector.offset();
            if (e.pageX < offset.left || e.pageX > (offset.left + selector.outerWidth()) ||
                e.pageY < offset.top || e.pageY > (offset.top + selector.outerHeight()))
                PFLY.HideDropDownList(id);
            return true;
        });
    }

    this.HideDropDownList = function (id) {
        var opener = jQuery('#' + id + '_opener');
        var selector = jQuery('#' + id + '_selector');
        selector.hide();
        //opener.show();
        jQuery(document).unbind("mousedown.papirfly");
    }

    this.SelectDropDownItem = function (id, value) {
        var opener = jQuery('#' + id + '_opener');
        var dropdown = jQuery('#' + id);
        dropdown.val(value);
        opener.html(dropdown.find(":selected").text());
        this.HideDropDownList(id);
        dropdown.change();
    }

    this.LdtSet = function (valStore, newVal, langId) {
        if (langId == undefined)
            langId = this.ResourceStrings.CurrentLanguageId;
        var valStoreEntries = valStore.substr(ldtPrefix.length).split(ldtSep);
        var found = -1;
        for (var i = 0; i < valStoreEntries.length; i++) {
            var val = valStoreEntries[i];
            if (val.length > langId.length && val.substr(1, langId.length) == langId) {
                found = i;
                break;
            }
        }
        if (newVal != null)
            newVal = newVal.replace(ldtSep, "?").replace(langId + "]", "?");
        newVal = "[" + langId + " " + newVal + " " + langId + "]";
        if (found >= 0)
            valStoreEntries[found] = newVal;
        else
            valStoreEntries.push(newVal);
        var allVals = valStoreEntries.length == 1 ? valStoreEntries[0] : valStoreEntries.join(ldtSep);
        return ldtPrefix + allVals;
    }

    this.LdtGet = function (valStore, langId) {
        var valStoreEntries = valStore.substr(ldtPrefix.length).split(ldtSep);
        for (var i = 0; i < valStoreEntries.length; i++) {
            var val = valStoreEntries[i];
            if (val.length >= langId.length && val.substr(1, langId.length) == langId)
                return val.length >= 2 * (langId.length + 1) ? val.substr(langId.length + 2, val.length - 2 * (langId.length + 2)) : "";
        }
        return "";
    }

    this.LdtEdit = function (jStore, jDisp) {
        var curLangId = this.ResourceStrings.CurrentLanguageId;
        var activeLangs = this.ResourceStrings.AciveLanguages;
        var valStore = jStore.val();

        function BuildInput() {
            var styleAttr = " style='width:" + jDisp.css("width") + ";height:" + jDisp.css("height") + "'";
            var isInput = jDisp.attr("rows") == undefined;
            var textareaAttrs = isInput ? null : (" rows='" + jDisp.attr("rows") + "' cols='" + jDisp.attr("rows") + "'");

            var inputs = "<table class='LdtInputDialog'>";
            for (var i = 0; i < activeLangs.length; i++) {
                var ln = activeLangs[i];
                inputs += "<tr>";
                inputs += "<td>";
                inputs += ln.name;
                inputs += "</td><td>";
                if (isInput) {
                    inputs += "<input type='text'";
                    inputs += " id='langinput-";
                    inputs += ln.id + "'";
                    inputs += " value='";
                    inputs += PFLY.LdtGet(valStore, ln.id) + "'";
                    inputs += styleAttr;
                    inputs += " />";
                } else {
                    inputs += "<textarea";
                    inputs += " id='langinput-";
                    inputs += ln.id + "'";
                    inputs += styleAttr;
                    inputs += textareaAttrs;
                    inputs += ">";
                    inputs += PFLY.LdtGet(valStore, ln.id);
                    inputs += "</textarea>";
                }
                inputs += "</td></tr>";
            }
            inputs += "</table>";
            return inputs;
        }

        function SaveChanges() {
            for (var i = 0; i < activeLangs.length; i++) {
                var ln = activeLangs[i];
                var input = jQuery("#langinput-" + ln.id);
                valStore = PFLY.LdtSet(valStore, input.val(), ln.id);
            }
            jStore.val(valStore);
            jDisp.val(PFLY.LdtGet(valStore, curLangId));
            PFLY.MarkAsChanged(jDisp);
        }

        function BuildButtons() {
            var buttons = {};
            buttons[PFLY.ResourceStrings.OkButton] = function () { SaveChanges(); jQuery(this).dialog('close'); };
            buttons[PFLY.ResourceStrings.CancelButton] = function () { jQuery(this).dialog('close'); };
            return buttons;
        }

        this.ShowDialog(this.ResourceStrings.LdtEditTitle, BuildInput(), null, BuildButtons());
    }

})();

// backward compatible PFLY accessor
function Papirfly() {
    return PFLY;
}
