
/**
 * Calculates the coords of the popup window.
 * @param width The width of the popup window.
 * @param height The height of the popup window.
 * @return The coordinates.
 */
function centered(width, height) {

    var availLeft = screen.availLeft ? screen.availLeft : 0;
    var availTop  = screen.availTop  ? screen.availTop  : 0;
    var dx        = screen.availWidth  - (width  + 10); // 10px for window frame
    var dy        = screen.availHeight - (height + 30); // 30px for window frame
    var left      = availLeft + Math.round(dx / 2);
    var top       = availTop  + Math.round(dy / 2);

    return "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top;
}

/**
 * Calculates the coords of the popup window.
 * The values used to calculate the coords are not constant!
 * @return The coordinates.
 */
function maximized() {
    return centered(screen.availWidth  - 10, screen.availHeight - 30);
}

/**
 * Opens a popup window.
 * If blocked by a popup-blocker then the returned reference may be null.
 * @param href The URL to be loaded in the newly opened window
 * @param target The name for the new window.
 * @param coords The coordinates of the new window.
 * @param features The string which lists the requested window features.
 * @return The window object reference.
 * @see http://developer.mozilla.org/en/docs/DOM:window.open
 */
function popup(href, target, coords, features) {
    var popupWindow = window.open(href, target, coords + "," + features);
    if (popupWindow && popupWindow.focus) {
        popupWindow.focus();
    }
    return popupWindow;
}

/**
 * Wrapper method for centered, resizable popup with scrollbars.
 * @param href The URL to be loaded in the newly opened window
 * @param target The name for the new window.
 * @param width The width of the popup window.
 * @param height The height of the popup window.
 * @return The window object reference.
 */
function popUpWinScrollBars(href, target, width, height) {
    return popup(href, target, centered(width, height), "resizable=yes,status=no,scrollbars=yes,toolbar=no,menubar=no,titlebar=no");
}

/**
 * Wrapper method for maximised, resizable popup without scrollbars.
 * @param href The URL to be loaded in the newly opened window
 * @param target The name for the new window.
 * @return The window object reference.
 */
function popUpMaximised(href, target) {
    return popup(href, target, maximized(), "resizable=yes,status=no,scrollbars=no,toolbar=no,menubar=no,titlebar=no");
}

/**
 * Wrapper method for maximised, resizable popup with scrollbars.
 * @param href The URL to be loaded in the newly opened window
 * @param target The name for the new window.
 * @return The window object reference.
 */
function popUpMaximisedScroll(href, target) {
    return popup(href, target, maximized(), "resizable=yes,status=no,scrollbars=yes,toolbar=no,menubar=no,titlebar=no");
}

/**
 * Wrapper method for centered, non-resizable popup with scrollbars.
 * @param href The URL to be loaded in the newly opened window
 * @param target The name for the new window.
 * @param width The width of the popup window.
 * @param height The height of the popup window.
 * @return The window object reference.
 */
function popUpWinCentered(href, target, width, height) {
    return popup(href, target, centered(width, height), "resizable=no,scrollbars=yes");
}

/**
 * @return true if OK is clicked by the conform alert box
 */
function confirmDeleteClip() {
    return confirm(
        "ARE YOU SURE YOU WISH TO DELETE THIS CLIP?" +
        "\n--------------------------------------------------------------------------------" +
        "\n\n Please select 'OK' if you would like to permanently delete this clip." +
        "\n\n Otherwise, select 'CANCEL' to return to the listing screen.");
}

/**
 * Selects all or none of the options of a select element.
 * @param elementId The id of the select element.
 * @parma selected If true then all options will be selected, else no options will be selected.
 */
function select(elementId, selected) {
    var element = document.getElementById(elementId);
    for (var i = 0, n = element.length; i < n; i++) {
        element[i].selected = selected;
    }
    if (element.onchange) {
        element.onchange();
    }
}

/**
 * Counts the number checkbox elements that are checked.
 * @parma parent (Element) The parent element to use for finding the checkbox elements.
 * @param name (DOMString) The name of the checkbox elements.
 * @return A count of the checked elements.
 */
function countChecked(parent, name) {

    var elements;  // NodeList of HTMLInputElement
    var input;     // HTMLInputElement
    var i;         // unsigned long
    var n;         // unsigned long
    var count = 0; // unsigned long

    elements = parent.getElementsByTagName("input");

    for (i = 0, n = elements.length; i < n; i++) {

        input = elements[i];

        if (input.type == "checkbox" && input.name == name && input.checked) {
            count++;
        }
    }

    return count;
}

/**
 * Disables non=checked checkbox elements.
 * @parma parent (Element) The parent element to use for finding the checkbox elements.
 * @param name (DOMString) The name of the checkbox elements.
 */
function disableNonChecked(parent, name) {

    var elements;  // NodeList of HTMLInputElement
    var input;     // HTMLInputElement
    var i;         // unsigned long
    var n;         // unsigned long

    elements = parent.getElementsByTagName("input");

    for (i = 0, n = elements.length; i < n; i++) {

        input = elements[i];

        if (input.type == "checkbox" && input.name == name) {
            input.disabled = !input.checked;
        }
    }
}

/**
 * Updates the checked attribute of a group of checkbox elements.
 * @parma parent (Element) The parent element to use for finding the checkbox elements.
 * @param name (DOMString) The name of the checkbox elements.
 * @parma checked (boolean) The new value of the checked attribute.
 */
function setChecked(parent, name, checked) {

    var elements; // NodeList of HTMLInputElement
    var input;    // HTMLInputElement
    var i;        // unsigned long
    var n;        // unsigned long

    elements = parent.getElementsByTagName("input");

    for (i = 0, n = elements.length; i < n; i++) {

        input = elements[i];

        if (input.type == "checkbox" && input.name == name) {
            input.checked = checked;
        }
    }
}

/**
 * Executes the specified action value.
 * An input element with id="action" must be specified on the form.
 * The onsubmit event of the form is executed if available.
 * @actionValue The action to request.
 */
function doAction(actionValue) {
    var action = document.getElementById("action");
    var form   = action.form;

    action.value = actionValue;

    if (!form.onsubmit || form.onsubmit()) {
        form.submit();
    }
}

/**
 * Submits a form; calling the onsubmit function if one exists.
 * @param formId The ID of the form to submit.
 */
function submit_form(formId) {
    var form = document.getElementById(formId);
    if (!form.onsubmit || form.onsubmit()) {
        form.submit();
    }
}

/**
 * Constructor for creating an object that has the query string
 * parameters as instance fields.
 *
 * This constructor will not work with NN3, IE3 or earlier.
 *
 * For example the following will set foo equal to "bar", foz equal to ""
 * and fop equal to "default fop".
 *
 * assert(document.URL == "http://example.com/index.html?foz=&foo=bar");
 * var PARAMETERS = new Parameters(document.URL);
 * var foo = "foo" in PARAMETERS ? PARAMETERS.foo : "default foo";
 * var foz = "foz" in PARAMETERS ? PARAMETERS.foz : "default foz";
 * var fop = "foy" in PARAMETERS ? PARAMETERS.fop : "default fop";
 *
 * @param URL The URL containing the query string to parse.
 */
function Parameters(URL) {

    var indexOf = URL.indexOf("?");

    if (indexOf != -1) {

        var parameters = URL.substring(indexOf + 1).split("&");

        var i, n, parameter;

        for (i = 0, n = parameters.length; i < n; i++) {
            parameter = parameters[i].split("=");
            if (parameter.length > 1) {
                this[parameter[0]] = parameter[1];
            }
        }
    }
}

/**
 * Reloads the opener of a popup.
 * e.g. window.onunload = reloadOpener;
 */
function reloadOpener() {
    window.opener.location.reload(true);
}

/**
 * Returns a copy of string without leading and trailing whitespace.
 * Whitespace is defined as {char c | c <= ' '}.
 * Note: We avoid regular expressions in implementation to support older user agents.
 * @param string The string to trim.
 * @return A copy of the specified string without leading and trailing whitespace.
 */
function StringUtil_trim(string) {

    /* Lookup the LENGTH of string. */
    var LENGTH = string.length;

    /* Find the first character that is not whitespace. */
    var beginIndex = 0;
    while (beginIndex != LENGTH && string.charAt(beginIndex) <= ' ') {
        beginIndex++;
    }

    /* If all characters are whitespace then return the empty String. */
    if (beginIndex == LENGTH) {return "";}

    /* Find the last character that is not whitespace. */
    var endIndex = LENGTH;
    while (endIndex != beginIndex && string.charAt(endIndex - 1) <= ' ') {
        endIndex--;
    }

    /*
     * If no leading or trailing whitespace found then return the string,
     * otherwise return the substring without leading and trailing whitespace.
     */
    return ((beginIndex === 0) && (endIndex === LENGTH)) ?
        string : string.substring(beginIndex, endIndex);
}

/**
 * Place cursor at end of content.
 * @param element An HTMLInputElement or HTMLTextAreaElement instance.
 */
function placeCursorAtEnd(element) {

    var length = element.value.length;

    if (length === 0) {
        element.focus();
        return;
    }

    /* The Mozilla way. */
    if (element.setSelectionRange) {
        element.focus();
        element.setSelectionRange(length, length);
        return;
    }

    /* The Microsoft way. */
    if (element.createTextRange) {
        var textRange = element.createTextRange();
        textRange.move("character", length);
        textRange.select();
        element.focus();
        return;
    }
}

/**
 * Does value start with the prefix?
 * @param value The value to test.
 * @param prefix The prefix to match.
 * @return true if value starts with prefix; false otherwise.
 */
function startsWith(value, prefix) {
    return value.length >= prefix.length &&
            value.substr(0, prefix.length) == prefix;
}

/**
 * Checks if all characters in the value are in the set of valid characters.
 * Note: if value is the empty string then this function will return true.
 * @param value The characters to check.
 * @param valid The valid characters.
 * @return true if all characters in value are in valid; false otherwise.
 */
function allCharsInValid(value, valid) {

    for (var i = 0, n = value.length; i < n; i++) {
        if (valid.indexOf(value.charAt(i)) == -1) {
            return false;
        }
    }

    return true;
}

/**
 * Opens the calendar control for a specified field.
 * @param fieldId The ID of the field.
 */
function newCal(fieldId) {

    var fieldvalue = document.getElementById(fieldId).value;

    var now = new Date();

    var monthnum, yearnum;

    var fields = fieldvalue ? fieldvalue.split('/') : false;

    if (fields && fields.length == 3) {

        monthnum = allCharsInValid(fields[1], "0123456789") ? parseInt(fields[1], 10) - 1 : now.getMonth();
        if (monthnum < 0) {
            monthnum = 0;
        } else if (monthnum > 11) {
            monthnum = 11;
        }

        yearnum  = allCharsInValid(fields[2], "0123456789") ? parseInt(fields[2], 10) : now.getFullYear();

        if (yearnum < 100) {
            yearnum += 2000;
        }
        if (yearnum < 2000) {
            yearnum = 2000;
        }
        if (yearnum > now.getFullYear() + 1) {
            yearnum = now.getFullYear() + 1;
        }

    } else {
        monthnum = now.getMonth();
        yearnum  = now.getFullYear();
    }

    window.open("/clips/calendar.jsp?field=" + fieldId + "&month=" + monthnum + "&year=" + yearnum, "calendar", "width=205,height=190");
}

/**
 * Sets the disabled attribute on all HTMLInputElement and HTMLSelectElement
 * children of the parent HTMLElement.
 * @param parent (HTMLElement) The parent to get the child elements from.
 * @param disabled (boolean) The new value for the "disabled" attribute.
 */
function setDisabled(parent, disabled) {

    var elements; // NodeList of HTMLInputElement or HTMLSelectElement
    var i;        // unsigned long
    var n;        // unsigned long

    elements = parent.getElementsByTagName("input");

    for (i = 0, n = elements.length; i < n; i++) {
        elements[i].disabled = disabled;
    }

    elements = parent.getElementsByTagName("select");

    for (i = 0, n = elements.length; i < n; i++) {
        elements[i].disabled = disabled;
    }

    elements = parent.getElementsByTagName("textarea");

    for (i = 0, n = elements.length; i < n; i++) {
        elements[i].disabled = disabled;
    }

}

function setStyle(element, property, value) {
    if (element && element.style) {
        element.style[property] = value;
    }
}

function stripSubstring(value, substring) {
    var indexOfSubstring;
    for (indexOfSubstring = value.indexOf(substring); indexOfSubstring != -1; indexOfSubstring = value.indexOf(substring)) {
        value = value.substring(0, indexOfSubstring) + value.substring(indexOfSubstring + substring.length, value.length);
    }
    return value;
}

function stripDelimited(value, begin, end) {
    var indexOfBegin, indexOfEnd;
    for (indexOfBegin = value.indexOf(begin); indexOfBegin != -1; indexOfBegin = value.indexOf(begin)) {
        indexOfEnd = value.indexOf(end, indexOfBegin + begin.length);
        if (indexOfEnd == -1) {
            /* Found 'begin' without 'end', stop processing. */
            return value;
        }
        /* Strip delimited substring. */
        value =
            value.substring(0, indexOfBegin) +
            value.substring(indexOfEnd + end.length, value.length);
    }
    /* All delimited substrings stripped. */
    return value;
}

/**
 * Encodes all non-ASCII characters using HTML entities.
 * acottrell 2006-08-25
 * @param value The value to encode
 * @return The HTML safe string.
 */
function encodeNonAsciiAsEntities(value) {

    var result = "";

    var i, n, charAt;

    for (i = 0, n = value.length; i < n; i++) {

        charAt = value.charAt(i);

        if (charAt <= '~') {
            result = result + charAt;
        } else {
            result = result + "&#x" + value.charCodeAt(i).toString(16) + ";";
        }
    }

    return result;
}

function stripTags(value) {

    var stripped = "";

    var inTag = false;

    var charAt;

    for (var i = 0, n = value.length; i < n; i++) {
        charAt = value.charAt(i);
        if (inTag) {
            if (charAt == ">") {
                inTag = false;
            } else {
                // ignore characters within tags.
            }
        } else {
            if (charAt == "<") {
                inTag = true;
            } else {
                stripped += charAt;
            }
        }
    }

    return stripped;
}

/**
 * Normalizes all line breaks to a line feed (&#x000A;)
 *
 * A line break is defined to be:
 *     a carriage return (&#x000D;)
 *     a line feed (&#x000A;)
 *     a carriage return/line feed pair
 */
function normalizeLineBreaks(value) {
    return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
}

function escapeLineBreaks(value) {
    return normalizeLineBreaks(value).replace(/\n/g, "\\n");
}

function newlineToBreaks(value) {
    return normalizeLineBreaks(value).replace(/\n/g, "<br>");
}

function stripLineBreaks(value) {
    return normalizeLineBreaks(value).replace(/\n/g, "");
}

function breaksToNewlines(value) {
    return stripLineBreaks(value).replace(/<br>/gi, "\n").replace(/<\/p><p>/gi, "\n\n");
}

/**
 * Counts the number of fields.
 * @param parent The parent container.
 * @param name The name of the fields.
 * @return The number of fields counted.
 */
function countFields(parent, name) {

    var elements; // NodeList of HTMLInputElement or HTMLSelectElement
    var i;        // unsigned long
    var n;        // unsigned long
    var count = 0;

    elements = parent.getElementsByTagName("input");

    for (i = 0, n = elements.length; i < n; i++) {
        if (elements.item(i).name == name) {
            count++;
        }
    }

    return count;
}

/**
 * Prepends a function to be called when the window.onload event fires.
 */
function prependOnload(func) {
    if (typeof window.onload == 'function') {
        var previous = window.onload;
        window.onload = function(){func();previous();};
    } else {
        window.onload = func;
    }
}

/**
 * Appends a function to be called when the window.onload event fires
 */
function appendOnload(func) {
    if (typeof window.onload == 'function') {
        var previous = window.onload;
        window.onload = function(){previous();func();};
    } else {
        window.onload = func;
    }
}

/**
 * Removes all children nodes from an element.
 * Useful for removing all HTMLOptionElement nodes from an HTMLSelectElement.
 *
 * @param element The element to remove the child node from.
 */
function removeChildren(element) {
    while (element.hasChildNodes()) {
        element.removeChild(element.firstChild);
    }
}

/**
 * Removes all children nodes from an element.
 * Work-around for an MSIE bug -- only use for visible HTMLSelectElement instances.
 * Useful for removing all HTMLOptionElement nodes from an HTMLSelectElement.
 *
 * @param element The element to remove the child node from.
 */
function removeChildren2(element) {
    var newElement = element.cloneNode(false);
    while (newElement.hasChildNodes()) {
        newElement.removeChild(newElement.firstChild);
    }
    element.parentNode.replaceChild(newElement, element);
}

function clearDefaultValue(element, defaultValue) {
    if (element && element.value == defaultValue) {
        element.value = "";
    }
}

/**
 * Cross platform function to make instance of XMLHttpRequest.
 * @return An instance of XMLHttpRequest; or null if unsupported.
 */
function makeXMLHttpRequest() {

    if (window.XMLHttpRequest) {
        return new XMLHttpRequest();
    }

    if (window.ActiveXObject) {
        var request = new ActiveXObject("Microsoft.XMLHTTP");
        if (request) {return request;}
        alert(
            "Could not create a \"Microsoft.XMLHTTP\" ActiveXObject." +
            "\nInternet Explorer 5.0 or later is required.");
        return null;
    }

    alert("XMLHttpRequest not supported by your browser.");
    return null;
}

/**
 * Updates the options of a HTMLSelectElement.
 *
 * @param element The HTMLSelectElement to recieve the options.
 * @param url The location of the XML document.
 */
function updateSelectElement(element, url) {

    var request = makeXMLHttpRequest();
    if (!request) {
        return;
    }

    resetSelectElement(element, "Loading...", "LOADING", false);

    request.onreadystatechange = function() {

        if (request.readyState != 4) {
            return;
        }

        /* If URL request is FORBIDDEN. */
        if (request.status == 403) {
            var login = confirm(
                "SORRY...\n" +
                "...you have been disconnected from the site due to a period of inactivity.\n" +
                "\nWould you like to be taken to the login page?\n");
            if (login) {
                open("/login.jsp", "_top");
            }
            return;
        }

        if (request.status != 200) {
            alert(
                "Error while making request." +
                "\nURL: " + url +
                "\nStatus: " + request.status +
                "\nMessage: " + request.statusText);
            return;
        }

        /* Shallow copy of the element. */
        var newElement = element.cloneNode(false);

        /* Remove old options. */
        removeChildren(newElement);

        var options = request.responseXML.getElementsByTagName("option");

        var i, n, option, newOption;

        for (i = 0, n = options.length; i != n; ++i) {

            option = options.item(i);

            newOption          = document.createElement("option");
            newOption.text     = option.getAttribute("text");
            newOption.value    = option.getAttribute("value");
            newOption.selected = option.getAttributeNode("selected") !== null;

            newElement.options[i] = newOption;
        }

        /* Finally replace the old element with the new element. */
        element.parentNode.replaceChild(newElement, element);

        fireOnchange(newElement);
    };

    request.open("GET", url, true);

    request.send(null);
}

/**
 * Updates the value of an HTMLInputElement.
 *
 * @param element The HTMLInputElement to be updated.
 * @param url The location of the XML document.
 */
function updateInputElement(element, url) {

    var request = makeXMLHttpRequest();
    if (!request) {
        return;
    }

    request.onreadystatechange = function() {

        if (request.readyState != 4) {
            return;
        }

        /* If URL request is FORBIDDEN. */
        if (request.status == 403) {
            var login = confirm(
                "SORRY...\n" +
                "...you have been disconnected from the site due to a period of inactivity.\n" +
                "\nWould you like to be taken to the login page?\n");
            if (login) {
                open("/login.jsp", "_top");
            }
            return;
        }

        if (request.status != 200) {
//            alert(
//                "Error while making request."
//                + "\nURL: " + url
//                + "\nStatus: " + request.status
//                + "\nMessage: " + request.statusText
//            );
            return;
        }

        element.value = request.responseXML.documentElement.getAttribute("value");
    };

    request.open("GET", url, true);

    request.send(null);
}

/**
 * Resets an HTMLSelectElement to have a single default option.
 *
 * @param element The HTMLSelectElement to reset.
 * @param text The text of the default option.
 * @param value The value of the default option.
 */
function resetSelectElement(element, text, value, selected) {
    removeChildren(element);
    element.options[0] = new Option(text, value, selected);
}

/** Fire the onchange event if it exists. */
function fireOnchange(element) {
    if (element.onchange) {
        element.onchange();
    }
}

/**
 * Selects an option.
 *
 * @param element The HTMLSelectElement to select an HTMLOptionElement of.
 * @param index The index of the HTMLOptionElement to select.
 */
function selectOption(element, index) {
    element.options[index].selected = "selected";
}

/**
 * Gets the selected option.
 *
 * @param element The HTMLSelectElement.
 * @return The selected HTMLOptionElement, or null if none selected.
 */
function getSelectedOption(element) {
    var selectedIndex = element.selectedIndex;
    return selectedIndex == -1 ? null : element.options[element.selectedIndex];
}

/**
 * Gets a comma delimited String of the values of selected options.
 *
 * @param element The HTMLSelectElement.
 * @return A comma delimited DOMString of the values of selected options.
 */
function getCommaDelimitedValuesOfSelectedOptions(element) {

    var selectedIndex = element.selectedIndex; // long

    if (selectedIndex == -1) {
        /* No options selected */
        return "";
    }

    var options = element.options; // HTMLCollection

    var values = options.item(selectedIndex).value; // DOMString

    var i, n; // long

    var option; // HTMLOptionElement

    for (i = selectedIndex + 1, n = options.length; i < n; i++) {
        option = options.item(i);
        if (option.selected) {
            values = values + "," + option.value;
        }
    }

    return values;
}

/**
 * Create a cookie with the specified name and value.
 */
function Cookie_set(name, value) {
    document.cookie = name + "=" + escape(value) + "; path=/";
}

/**
 * Retrieve the value of the cookie with the specified name.
 */
function Cookie_get(name, defaultValue) {

    var allCookies = document.cookie.split("; ");

    for (var i = 0, n = allCookies.length; i < n; i++) {

        var cookie = allCookies[i].split("=");

        if (name == cookie[0]) {
            return unescape(cookie[1]);
        }
    }

     return defaultValue;
}

/**
 * Delete the cookie with the specified name.
 */
function Cookie_delete(name) {
    document.cookie = name + "=; expires=Fri, 31 Dec 1999 23:59:59 GMT;";
}

/**
 * Disables the "View Clips" button on the page.
 */
function disableButtonViewClips() {
    var buttonViewClips = document.getElementById("button_view_clips");
    buttonViewClips.onclick = null;
    buttonViewClips.style.color = "gray";
    buttonViewClips.style.cursor = "wait";
}
