// This file contains lots of Javascript that is common to the entire CubiksOnline platform

// -------------------------------- DOM ON PAGE LOAD --------------------------------
// Allows you to register a function to run once the DOM has loaded on the page
// (doesn't have to wait for all images to load, just all DOM/JS resources).
//
// *** BIG NOTE ***
// THIS IS NOT CURRENTLY USED
// IT ALLOWS THE JS TO START RUNNING <BEFORE> ALL THE BINARY (IMAGE) CONTENT HAS LOADED
// THAT IS EXPLICITLY <NOT> WHAT WE WANTED WITH THE 'HOURGLASS' PRINCIPLE!
//
// I've left this here just in case we want to do some quick-dom-ready stuff.
// ****************
//
// To use: call addDOMLoadEvent one or more times with functions, ie:
//    function something() {
//       // do something
//    }
//    addDOMLoadEvent(something);
//
//    addDOMLoadEvent(function() {
//        // do other stuff
//    });
//
// Based on work here: http://www.thefutureoftheweb.com/blog/adddomloadevent
// and here: http://dean.edwards.name/weblog/2006/06/again/
function addDOMLoadEvent(func)
{
    /* create event function stack */
    var load_events = [],
        load_timer,
        done = arguments.callee.done,
        exec,
        old_onload,
        init = function()
        {
            if (done)
            {
                return;
            }
            /* kill the timer */
            clearInterval(load_timer);
            load_timer = null;
            done = true;
            /* execute each function in the stack in the order they were added */
            while ((exec = load_events.shift()))
            {
                exec();
            }
        };

    if (func && !load_events[0])
    {
        /* for Mozilla/Opera9 */
        if (window.addEventListener)
        {
            /*the Object referenced here should be "window" and not "document",
            otherwise it's not destroyed in memory and produces memory leak
            on  page leaving, reloading, etc.*/
            window.addEventListener('DOMContentLoaded', init, false);
        }
        /* for Internet Explorer - lifted from http://dean.edwards.name/weblog/2006/06/again/#comment335794 */
        /*@cc_on
            if (document.body)
            {
                try
                {
                    document.createElement('div').doScroll('left');
                    return init();
                } catch (e) { }
            }
        @*/

        /* for Safari */
        else if (/KHTML|WebKit/i.test(navigator.userAgent))
        { /* sniff */
            load_timer = setInterval(function()
            {
                if (/loaded|complete/.test(document.readyState))
                {
                    init(); /* call the onload handler */
                }
            }, 10);
        }

        /* for other browsers set the window.onload, but also execute the old window.onload */
        else
        {
            old_onload = window.onload;
            window.onload = function()
            {
                init();
                if (old_onload)
                {
                    old_onload();
                }
            };
        }
    }
    load_events.push(func);
}


// -------------------------------- GENERIC FUNCTIONS --------------------------------

// very useful methods for finding the various dimensions of the viewport.
// especially useful when there are floating elements on the page that break the normal .clientHeight
function Viewport()
{
    this.windowX = (document.documentElement && document.documentElement.clientWidth) || window.innerWidth || self.innerWidth || document.body.clientWidth;
    this.windowY = (document.documentElement && document.documentElement.clientHeight) || window.innerHeight || self.innerHeight || document.body.clientHeight;
    this.scrollX = (document.documentElement && document.documentElement.scrollLeft) || window.pageXOffset || self.pageXOffset || document.body.scrollLeft;
    this.scrollY = (document.documentElement && document.documentElement.scrollTop) || window.pageYOffset || self.pageYOffset || document.body.scrollTop;
    this.pageX = (document.documentElement && document.documentElement.scrollWidth) ? document.documentElement.scrollWidth : (document.body.scrollWidth > document.body.offsetWidth) ? document.body.scrollWidth : document.body.offsetWidth;
    this.pageY = (document.documentElement && document.documentElement.scrollHeight) ? document.documentElement.scrollHeight : (document.body.scrollHeight > document.body.offsetHeight) ? document.body.scrollHeight : document.body.offsetHeight;
}

// use this method to prevent an event from propagating any further
function haltEventPropagation(event)
{
	if(event.stopPropagation)
	{
		// this code is for Mozilla and Opera
		event.stopPropagation();
		event.preventDefault();
	}
	else if(window.event)
	{
		// this code is for IE
		window.event.cancelBubble = true;
		window.event.returnValue = false;
	}
	return false;
}

function disableLinks()
{
    alert('As of SB, code should not make use of the disableLinks JavaScript function');
}

function setFocusToFirstControl()
{
    alert('As of SB, code should not make use of the setFocusToFirstControl JavaScript function');
}

// this is a fudge to disable the "BACK" button functionality of a browser.
// it works in IE by forcing the browser to move forward in its history list
// (nothing happend if we are at the end of the list though).
//
// When a user presses "BACK" then the previous page is requested, possibly servered (if nonUIP)
// and then this javascript forces the browser to advance in its history list and request
// the original page = 2 GET requests!

javascript:window.history.forward(10);

// browser-safe method of obtaining the keycode for the supplied event
function getKey(e)
{
	if(window.event)
		return window.event.keyCode;
	else if(e)
		return e.which;
	else
		return null;
}

function enterPressed(theEvent)
{
	var keycode = getKey(theEvent);
	if(keycode == null)
		return false;

	return (keycode == 13);
}

function submitOnEnter(theEvent, doPostBackValue)
{
    alert('As of SB, code should not make use of the submitOnEnter JavaScript function. Look at ALogon/PLogon for examples of how to use an asp.panel and specify the defaultbutton property.');
	return false;
}

// This method returns the value of a form element. controlName is passed in its basic
// form i.e. myControl - but the method will match controls named _ctl:myControl,
// _abc:myControl as well as myControl. This gets around the ASP.NET renaming.
var getControlByNameCache = new Object();
function getControlByName(controlName)
{
    var x, y;
    var requestedName = new String(controlName);
    var controlRef = getControlByNameCache[controlName];
    
    //console.log("getControlByName: %s", requestedName);

    if (controlRef == null) {
        function isMatch(c, r, delimiter) {
            //console.log("isMatch: [%s] [%s] [%s]",c, r, delimiter);
            var li = c.lastIndexOf(delimiter + r);
            var sub = c.substring(li + 1);
            return sub.length == r.length && sub == r;
        }

        // look for ASP.NET 2 controls first
        var tags = new Array('a', 'div', 'label', 'span', 'h1', 'h2', 'li', 'dl', 'tr');
        for (var t = 0; t < tags.length && controlRef == null; t++) {
            var elements = document.getElementsByTagName(tags[t]);
            for (x = 0; x < elements.length && controlRef == null; x++) {
                if (isMatch(new String(elements[x].id), requestedName, '_'))
                    controlRef = getControlByNameCache[controlName] = elements[x];
            }
        }

        // evaluate all of the forms on the page
        for (x = 0; x < document.forms.length && controlRef == null; x++) 
    	{
            var form = document.forms[x];
            // evaluate each input control on the form
            for (y = 0; y < form.length && controlRef == null; y++)
            {
                var ctrlName = new String(form[y].name);
                var ctrlID = new String(form[y].id);

                // have we found an exact match for control name
    	        if (ctrlName == requestedName || ctrlID == requestedName)
                    controlRef = getControlByNameCache[controlName] = form[y];
                // have we found an asp.net match for control name
                else if (isMatch(ctrlName, requestedName, ':') || isMatch(ctrlID, requestedName, ':'))
                    controlRef = getControlByNameCache[controlName] = form[y];
                // have we found an asp.net2 match for control name
                else if (isMatch(ctrlName, requestedName, '$') || isMatch(ctrlID, requestedName, '$'))
                    controlRef = getControlByNameCache[controlName] = form[y];
                else if (isMatch(ctrlName, requestedName, '_') || isMatch(ctrlID, requestedName, '_'))
                    controlRef = getControlByNameCache[controlName] = form[y];
            }
        }
    }

    return controlRef;
}

// when entering data into the ScreenText tables, people should use '\n' to
// denote a new line - however, javascript actually displays this as-is, instead
// of starting a new line!
// Use this function when calling confirm() or alert() to fix this problem.
function formatStringForDisplay(message)
{
    return new String(String(message).replace(/\\n/g, '\n'));
}

// formats a string similar to string.Format in C# 
// e.g.the result of calling:   formatPlaceholders('Page {0} of {1}','6','7') 
// will be a string returned as "Page 6 of 7"
function formatPlaceholders(str)
{
	if( arguments.length == 0 )
        return null;
	
	for(i=1;i<arguments.length;i++)
	{
		str = str.replace('{' + (i-1) + '}', arguments[i]);
	}
	return str;
}

// does the context string start with the specific characters?
if (!String.prototype.startsWith)
{
    String.prototype.startsWith = function(str)
    {
        return !this.indexOf(str);
    }
}

// does the context string end with the specific characters?
if (!String.prototype.endsWith)
{
    String.prototype.endsWith = function(str)
    {
        var lastIndex = this.lastIndexOf(str);
        return (lastIndex != -1) && (lastIndex + str.length == this.length);
    }
}


// -------------------------------- PNG ALPHA IMAGE IE6 TRICK --------------------------------

// wrap an IE6-alpha-safe span around the specified image.
// when replaceImgageCtrlWithFix==true, image node in DOM is replaced with the span and NULL is returned
// when replaceImgageCtrlWithFix==false, DOM is not touched, and the new span is returned
function fixPNGAlphaForIE6(imageCtrl, replaceImgageCtrlWithFix)
{
    if (imageCtrl == null)
        return null;

    // conditional compilation: 
    /*@cc_on

    // only do alphaimage for IE6 and below (IE6 XP SP3 == 5.7, previous IE6 was 5.6)
    @if (@_jscript_version <= 5.7)
    if (document.body.filters && imageCtrl.src.toUpperCase().indexOf('.PNG') > 0)
    {
        var node = document.createElement('span');
        node.id = imageCtrl.id;
        node.className = imageCtrl.className;
        node.title = imageCtrl.title;
        node.style.cssText = imageCtrl.style.cssText;
        node.style.setAttribute('filter', "progid:DXImageTransform.Microsoft.AlphaImageLoader"
                                        + "(src=\'" + imageCtrl.src + "\', sizingMethod='scale')");
        node.style.fontSize = '0';
        node.style.width = imageCtrl.width.toString() + 'px';
        node.style.height = imageCtrl.height.toString() + 'px';
        node.style.display = 'inline-block';

        if (!replaceImgageCtrlWithFix)
            return node;
            
        imageCtrl.parentNode.replaceChild(node, imageCtrl);
        return null;
    }

    @end
    @*/

    // otherwise not IE6, so do nothing special
    if (!replaceImgageCtrlWithFix)
        return imageCtrl.cloneNode(false);

    return null;
}


// -------------------------------- CONTROL VISIBILITY --------------------------------

function setVisibility(node, onOrOff)
{
    if (onOrOff == true)
    {
        removeClassName(node, "hidden");
    }
    else
    {
        addClassName(node, "hidden");
    }
}

function isVisible(node)
{
    return hasClassName(node, "hidden") == false;
}

function toggleVisibility(node)
{
    if (hasClassName(node, "hidden"))
        removeClassName(node, "hidden");
    else
        addClassName(node, "hidden");
}


// -------------------------------- PANEL FUNCTIONS --------------------------------

function toclink(ng)
{
	if(getControlByName('UserHasOpenTask').value == "True")
	{
		if (getControlByName('GoTOCConfirmText').value!="") // i.e. user has turned warnings off
			if(!confirm(formatStringForDisplay(getControlByName('GoTOCConfirmText').value)))
				return;
	}
	getControlByName('GoTOC').value = ng;
	__doPostBack('', '' );
}

function recentFolderOrParticipantSearch()
{
	if(getControlByName('UserHasOpenTask').value == "True")
	{
		if (getControlByName('GoTOCConfirmText').value!="") // i.e. user has turned warnings off
			return confirm(formatStringForDisplay(getControlByName('GoTOCConfirmText').value));
	}
	
	return true;
}

function home()
{
	if(getControlByName('UserHasOpenTask').value == "True")
	{
		if (getControlByName('GoHomeConfirmText').value!="") // i.e. user has turned warnings off
			if(!confirm(formatStringForDisplay(getControlByName('GoHomeConfirmText').value)))
				return;
	}
	
	getControlByName('GoHome').value = "True";
	__doPostBack('', '' );
}

function logout()
{
	if(getControlByName('UserHasOpenTask').value == "True")
	{
		if (getControlByName('GoLogoffConfirmText').value!="") // i.e. user has turned warnings off
			if(!confirm(formatStringForDisplay(getControlByName('GoLogoffConfirmText').value)))
				return;
	}
	
	getControlByName('GoLogoff').value = "True";
	__doPostBack('', '' );
}

// -------------------------------- CSS @class FUNCTIONS --------------------------------

// does the 'obj' have a .className that contains the 'strClass'?
// this function understands that .className may contain multiple classes.
function hasClassName(obj, strClass)
{
    if(obj.className)
    {
        var arrList = obj.className.split(' ');
        var strClassUpper = strClass.toUpperCase();     // are classes case sensitive?

        for(var i = 0; i < arrList.length; i++)
        {
            if(arrList[i].toUpperCase() == strClassUpper)
                return true;
        }
    }
    
    return false;
}

// removes the specified strClass class from obj.className
// this function understands that obj.className may contain multiple classes.
function removeClassName(obj, strClass)
{
    if(obj.className)
    {
        var arrList = obj.className.split(' ');
        var strClassUpper = strClass.toUpperCase();

        // find all instances and remove them
        for(var i = 0; i < arrList.length; i++)
        {
            if(arrList[i].toUpperCase() == strClassUpper)
            {
                // remove array item
                arrList.splice(i, 1);

                // decrement loop counter as we have adjusted the array's contents
                i--;
            }
        }

        // assign modified .className
        obj.className = arrList.join(' ');
    }
}

// add the 'strClass' to the specified obj.className.
// if obj.className does not exist then it is created and set = strClass.
// this function understands that obj.className may contain multiple classes.
function addClassName(obj, strClass)
{
    if(obj.className)
    {
        var arrList = obj.className.split(' ');
        
        // is the class already assigned?
        if(hasClassName(obj, strClass) == false)
        {
            // nope, so add it
            
            // add to END of list
            arrList[arrList.length] = strClass;

            // add to START of list?
            //arrList.splice(0, 0, strClass);

            // assign modified .className
            obj.className = arrList.join(' ');
        }


    }
    else
    {
        // no existing .className, so make one
        obj.className = strClass;
    }
}

// -------------------------------- CUBIKS CONTROL LIBRARY HELPERS --------------------------------

function ShowOrHideValidationMarker(controlName, isValid)
{
    var labelControl= document.getElementById(controlName + "_Label");
    var markerControl= document.getElementById(controlName + "_ErrorMarker");
    if (isValid)
    {
        if (labelControl!=null)
            removeClassName(labelControl, 'errorLabel');
        if (markerControl!=null)
        {
            removeClassName(markerControl, 'errorMarkerShow');
            addClassName(markerControl, 'errorMarkerHide');
        }
    }
    else
    {
        if (labelControl!=null)
            addClassName(labelControl, 'errorLabel');
        if (markerControl!=null)
        {
            addClassName(markerControl, 'errorMarkerShow');
            removeClassName(markerControl, 'errorMarkerHide');
        }
    }
}

// This function is hooked up to CubiksDropDownListButton dropdownlist
// onchange event to conditionally disable the associated linkbutton.

function cubiksDropDownListButtonOnChange(controlID, valueToDisableButton)
{
    var dropdown= document.getElementById(controlID + "_dropDown");
    var buttonDisabled= document.getElementById(controlID + "_buttonDisabled");
    var buttonEnabled= document.getElementById(controlID + "_buttonEnabled");
    
    var selectedValue= dropdown.options[dropdown.selectedIndex].value;
    
    // We have two panels - one contains a stylised label (a disabled button)
    // the other contains an enabled button. We decide which to show and which to hide.
    if (valueToDisableButton==selectedValue)
    {   
        removeClassName(buttonDisabled, "hidden");
        addClassName(buttonEnabled, "hidden");
    }
    else
    {
        removeClassName(buttonEnabled, "hidden");
        addClassName(buttonDisabled, "hidden");
    }
}

// This function is used by CubiksGridView. It is called when one of select All, None, Invert is clicked.
function cgv_selectGroup(thisControlID, operation) {

    // Find the onscreen counts
    var selectedCount = 1;
    var totalCount = 0
    var confirmPrompt = 0;

    confirmPrompt = document.getElementById(thisControlID + "_confirmreset").value;

    // We may not have these options if the display of counts has been turned off
    try {
        selectedCount = Number(document.getElementById(thisControlID + "_selectedCount").innerHTML);
        totalCount = Number(document.getElementById(thisControlID + "_totalCount").innerHTML);
    }
    catch (ex) { }

    // Only proceed if there are currently no selections, or operation is invert, or if user confirms its ok to lose selections
    if (selectedCount == totalCount || selectedCount == 0 || operation == "Invert" || confirm(confirmPrompt)) {
    
        // Keep a record of selections. We need to calculate onscreen and offscreen selections separately
        var onPageTotalCount = 0;
        var onPageCheckedCountBefore = 0;
        var onPageCheckedCountAfter = 0;
        var offPageTotalCount = 0;
        var offPageCheckedCountBefore = 0;
        var offPageCheckedCountAfter = 0;

        // Look for all the checkboxes on the form that belong to this CubiksGridView
        for (var i = 0; i < document.forms[0].elements.length; i++) {
            var el = document.forms[0].elements[i];
            if (el.type == 'checkbox' && !el.disabled && new String(el.id).indexOf(thisControlID, 0) == 0) {
                // Calculate counts and update checkbox checked status
                onPageTotalCount++;

                if (el.checked)
                    onPageCheckedCountBefore++;

                if (operation == "All") {
                    el.checked = true;
                    onPageCheckedCountAfter++;
                }
                else if (operation == "None") {
                    el.checked = false;
                }
                else {
                    el.checked = !el.checked;
                    if (el.checked)
                        onPageCheckedCountAfter++;
                }
            }
        }

        // Calculate the number of off screen selected checkboxes
        offPageTotalCount = totalCount - onPageTotalCount;
        offPageCheckedCountBefore = selectedCount - onPageCheckedCountBefore;

        if (operation == "All")
            offPageCheckedCountAfter = offPageTotalCount;
        else if (operation == "None")
            offPageCheckedCountAfter = 0;
        else 
            offPageCheckedCountAfter = offPageTotalCount - offPageCheckedCountBefore;

        // We need to send the group selection info to the server so it can update server side selections.
        // We pass a csv of the selections i.e. None,All,Invert,Invert - server will process in order...
        var selectGroupCurrent = document.getElementById(thisControlID + "_selectgroup").value;
        if (new String(selectGroupCurrent).length > 0)
            selectGroupCurrent += ',';
        selectGroupCurrent += operation;
        document.getElementById(thisControlID + "_selectgroup").value = selectGroupCurrent;
        
        // Update the onscreen counts
        document.getElementById(thisControlID + "_selectedCount").innerHTML = onPageCheckedCountAfter + offPageCheckedCountAfter;
    }
}

// This function is used by CubiksGridView. It is called whenever a checkbox is clicked
function cgv_toggleCheck(thisControlID, checkboxToToggle) {
    // We may not have a selected count to update if that option has been turned off
    var control = document.getElementById(thisControlID + "_selectedCount");
    if (control != null) {
        // We are going to update the onscreen selectedCount based on whether we are checking or unchecking a checkbox
        var selectedCount = Number(document.getElementById(thisControlID + "_selectedCount").innerHTML);
        var cb = document.getElementById(checkboxToToggle);
        if (cb.checked)
            selectedCount++
        else
            selectedCount--;
        document.getElementById(thisControlID + "_selectedCount").innerHTML = selectedCount;
    }
}

function enableOrDisableTextBoxBasedOnPrecedingCheckBox() {
    var lastCheckBox= null;

    // evaluate each input control on the form (there is only ever one form)
    for (y = 0; y < document.forms[0].length; y++) {
        var thisControl = document.forms[0][y];

        // Keep a reference to the latest checkbox that we have encountered
        if (thisControl.type == 'checkbox')
            lastCheckBox = thisControl;
        // All textboxes are disabled if their preceding checkbox is unchecked
        else if (thisControl.type == 'text')
            thisControl.disabled = (lastCheckBox!=null && (lastCheckBox.checked == false || lastCheckBox.disabled == true));
    }
}

// This function is used by CubiksLocaleListControl. It is called when one of select All, None, Invert is clicked.
function lt_selectGroup(operation, enableOrDisable) {
    // Find the onscreen counts and confirm prompt
    var confirmPrompt = getControlByName("controlHiddenConfirmReset").value;
    var selectedCount = Number(getControlByName("controlHiddenSelectedCount").value);
    var totalCount = Number(getControlByName("controlHiddenTotalCount").value);

    // Look for all the checkboxes on the form that belong to this CubiksLocaleListControl
    var newSelectionCount = 0;
    if (selectedCount == totalCount || selectedCount == 0 || operation == "Invert" || confirm(confirmPrompt)) {
        for (var i = 0; i < document.forms[0].elements.length; i++) {
            var el = document.forms[0].elements[i];
            if (el.type == 'checkbox' && !el.disabled && new String(el.id).indexOf("localeChk", 0) > -1) {

                switch (operation) {
                    case "All":
                        el.checked = true;
                        break;
                    case "None":
                        el.checked = false;
                        break;
                    case "Invert":
                        el.checked = !el.checked;
                        if (el.checked)
                            newSelectionCount++;
                        break;
                }
            }
        }
        // Re-assign the new selectedCount value to the hidden form input
        switch (operation) {
            case "All":
                getControlByName("controlHiddenSelectedCount").value = totalCount;
                break;
            case "None":
                getControlByName("controlHiddenSelectedCount").value = 0;
                break;
            case "Invert":
                getControlByName("controlHiddenSelectedCount").value = newSelectionCount;
                break;
        }
    }
    
    if (enableOrDisable == "True" || enableOrDisable == true)
        enableOrDisableTextBoxBasedOnPrecedingCheckBox();
}

// This function is used by the CubiksLocaleListControl. It is called whenever a checkbox is clicked
function lt_toggleCheck(checkboxToToggle, enableOrDisable) {
    // We are going to update the onscreen selectedCount based on whether we are checking or unchecking a checkbox
    var selectedCount = Number(getControlByName("controlHiddenSelectedCount").value);
    var cb = getControlByName(checkboxToToggle);
    if (cb.checked)
        selectedCount++
    else
        selectedCount--;
    getControlByName("controlHiddenSelectedCount").value = selectedCount;
    if (enableOrDisable == true) {
        enableOrDisableTextBoxBasedOnPrecedingCheckBox();
    }
}

/* This function hides a control that has been rendered in a dt/dd pair i.e. it hides both the dd and the dt */
/* MAKE SURE YOU PASS THE RAW SERVER CONTROL ID */

function setFieldSetControlVisibility(rawServerControlID, onOrOff) {
    var label = document.getElementById('master_cmpBody_' + rawServerControlID + '_Label').parentNode;
    var control = document.getElementById('master_cmpBody_' + rawServerControlID).parentNode;
    if (onOrOff == true) {
        removeClassName(label, "hidden");
        removeClassName(control, "hidden");
    }
    else {
        addClassName(label, "hidden");
        addClassName(control, "hidden");
    }
}


// WARNING! CommonJSLoaded() MUST BE THE LAST FUNC IN Common.JS
function CommonJSLoaded() { return true; }

// Decrease count of pages left to load, so that we know when to hide the hourglass and show the mainpage
if (typeof (jsScriptCount) != "undefined")
    jsScriptCount--;
