
// Javascript array intersection function. 
// Source: http://snippets.dzone.com/posts/show/4354
Object.extend(Array.prototype, {
    intersect: function(array){
        return this.findAll( function(token){ return array.include(token) } );
    }
});

String.prototype.trim = function () {
    return this.replace(/^\s*/, "").replace(/\s*$/, "");
}


Element.addMethods( {
    
    /** Returns all elements that match the specified tag and className */
    getElementsByTagAndClass: function( element, tag, className )
    {
        var tagMatch = element.getElementsByTagName( tag );
        var classMatch = [];

        var x;        
        for( x=0; x<tagMatch.length; x++ )
        {            
            if( Element.hasClassName( tagMatch[x], className ))
            {        
                classMatch.push( tagMatch[x] );                
            }            
        }

        return classMatch;
    },
    
    findParentByClass: function( element, className )
    {
        var parent = element.parentNode;
        if( Element.hasClassName( element, className ) )        
            return element;   
        
        return Element.findParentByClass( parent, className );       
    },

    getChildrenByClassName: function( element, className )
    {
        var results = [];
        
        var children = element.childNodes;
        var x;
        for( x=0; x<children.length; x++ )
        {
            var child = $(children[x]);
            
            if( ! child.hasClassName ) continue;
            
            if( $(child).hasClassName( className ))
                results.push( child );
        }
        
        return results;
    }
    
});

// Determines if an object is empty.
// (i.e null, an empty list, or a hash with no non-empty values)
function isObjectEmpty( object )
{       

    if( Object.isArray( object ) || Object.isString( object )){        
        return object.length == 0;
    }
               
    if( Object.isNumber( object )){        
        return (object == 0);
    }

    if( Object.isUndefined( object )){        
        return true;
    }

    if( Object.isElement( object ) || Object.isFunction( object )){        
        return false
    }

    var empty = true;
    for( i in object )
    {

        var value = object[i];
        if( !isObjectEmpty(value) )
        {
            empty = false;            
            break;
        }
    }
        
    
    return empty;
    
}
     

// http://www.quirksmode.org/js/findpos.html
function findPos(obj) {
    var curleft = curtop = 0;
    if (obj.offsetParent) {
        curleft = obj.offsetLeft
                curtop = obj.offsetTop
                while (obj = obj.offsetParent) {
            curleft += obj.offsetLeft
                    curtop += obj.offsetTop
                }
    }
    return [curleft,curtop];
}


// Get the value of the specified component (FIXME: Allow each component to define it's own custom "getComponentValue" function)
function componentValue( component )
{
    if( ! component ) return null;        

    var component = $(component); 

    // Allow the component to define it's own componentValue method if it wants. 
    var classes = component.className.split( " " );
    var x;
    for( x=0; x<classes.length; x++ )
    {        
        var className = classes[x];

        if(!className.trim()) continue;
        var methodName = "getComponentValue_" + className;
                
        var method = window[methodName];        

        if( method )
            return method( component );
    }

    if( component.hasClassName( "LinkComponentTable" ))
    {
        var fieldIdentity = component.id.replace( "widget-", "" );
                
        var inputs = document.getElementsByName( "assessment." + fieldIdentity + ":record:list" );
                                               
        if( inputs.length == 0 ) return null;        
        return inputs[0].value;                                
    }

    if( component.tagName == "DIV" ) // Assume radio/checkbox
    {
        var inputs = component.getElementsByTagName("input");           
        
        if( component.className == "SelectionComponent-Radio" || component.className == "MultiSelectionComponent_Checkbox" ) // Is a Selection-Radio widget
        {
            var x;
            for( x=0; x<inputs.length; x++ )
            {
                var input = inputs[x];
                if( input.selected || input.checked )
                {
                    return input.value;
                }
            }
        }
        else if( component.className == "DateTimeComponent_wrapper" )
        {
            var inputs = component.getElementsByTagName( "input" );
            var hidden = inputs[0];
            return hidden.value;
        }        
        else // Is a checkbox
        {                   
            var input = inputs[0];
            
            return input.selected || input.checked;
        }
    }
    
    
    if( ! component.type )  // no component type, most likely a select option
    {
        return component.selected;
    }
    
    

    else if( component.type == 'checkbox' || component.type == 'radio' )
    {        
        return component.type == 'checkbox' ? component.checked : component.selected;   
    }    
    else 
    {
        return component.value;   
    }
    
}



// Jump to the specific section
function jumpToSection( mySectionIdentity )
{
    processSubmitHandlers();    
    document.getElementById('action_name').value   = "navigateToSection";
    document.getElementById('action_target').value = mySectionIdentity;

    document.getElementById('assess_form').submit();
}

// Jump to the next section. 
function jumpToNextSection()
{
    processSubmitHandlers();    
    document.getElementById('action_name').value   = "navigateToNextSection";
    document.getElementById('action_target').value = sectionIdentity;

    document.getElementById('assess_form').submit();
}

// Jump to the previous section
function jumpToPrevSection()
{
    processSubmitHandlers();    
    document.getElementById('action_name').value   = "navigateToPrevSection";
    document.getElementById('action_target').value = sectionIdentity;

    document.getElementById('assess_form').submit();
}


var submitHandlers = [];

// Register a submit handler with the system. This registered function will be executed before form submission
function registerSubmitHandler( func )
{
    submitHandlers.push( func );
}

// Process submit handlers. this allows components to register functions to execute before the form is submitted
// (i.e. Select lists selecting all elements so they get submitted correctly)
function processSubmitHandlers()
{
    setPageModified( false );

    for( x=0 ; x < submitHandlers.length ; x++ )
    {
        submitHandlers[x]();
    }
}

// Submit the assessment (with the given button uid)
function submitAssessment(buttonUID)
{
    processSubmitHandlers();
    $("action_name").value   = "submit";
    $("action_target").value = buttonUID;
    document.getElementById('assess_form').submit();
}

function backgroundSave( buttonUID )
{
    processSubmitHandlers();
    setPageModified(true); // Revert the "page modified" handler to unsaved. We don't want to trigger that until the operation is complete
            
    $("action_name").value   = "ajax-save";
    $("action_target").value = buttonUID;
    $("assess_form").target = "ajax-frame";    
    $("assess_form").submit();
    $("assess_form").target = "";
}

function confirmSave()
{
    setPageModified( false );
}

var auto_save_interval = null;

function autoSave()
{
    backgroundSave();
    startAutoSave();
}

function startAutoSave()
{
    setTimeout( autoSave, auto_save_interval * 60 * 1000 );
    //setTimeout( autoSave, 1000 );
}        
        
function setAutoSaveInterval( interval )
{
    auto_save_interval = interval;
    startAutoSave();    
}


// Cancel the assessment
function cancelAssessment()
{   
    
    if( isPopup )
        window.close();
    else
    {
        processSubmitHandlers();    
        $('action_name').value   = "cancel";
        $('assess_form').submit(); 
    }
}

// Trigger the specific "Action" object inside the Model. 
function triggerAction( identity )
{
    processSubmitHandlers();    
    document.getElementById('action_name').value   = "triggerAction";
    document.getElementById('action_target').value = identity;
    document.getElementById('assess_form').submit();
}


// Popup a new window
function popWindow( url, target, width, height )
{
    if(!width)  width=640;
    if(!height) height=480;

    var size;
    if( isMobile )
        size = "";
    else
        size = "width=" + width + ",height=" + height + ",";

    return window.open( url, target, size + "scrollbars=yes,resizable=yes" );
}   


var pageModified = false;

// Mark the page as being modified
function setPageModified(modified)
{
    if( modified )
        enableSaveButtons();
    else
        disableSaveButtons();

    pageModified = modified;            
}

function enableSaveButtons()
{
    var buttons = $$(".SaveButton");
    var x; 
    for( x=0; x<buttons.length; x++)
    {
        buttons[x].disabled = false;
    }
}

function disableSaveButtons()
{
    var buttons = $$(".SaveButton");
    var x;
    for( x=0; x<buttons.length; x++)
    {
        buttons[x].disabled = true;
    }
}


// Display a warning to the end user if the haven't saved the current form
function unloadProtection()
{
    if( pageModified )
    {
        return "You have not saved this page. Are you sure you want to leave?";            
    }    
}


/// Handle hiding options from Selection elements in a cross-browser way. (display:none doesn't work in WebKit/KHTML)

var removed_options = {}; // Stores a map of id->RemovedOption

// Class RemovedOption - Data structure to store information about a removed option
// This option must be passed to this class BEFORE deleting from the Selection element. 
function RemovedOption( option, position )
{
    this.id     = option.id;
    this.option = option;
    this.parent = option.parentNode;
    this.position = position;
}

// Hide the option with the specified id from any selection component. 
// (This function does NOT support OPTGROUPs)
function hideOption( id )
{

    var option = document.getElementById( id );    
    if(! ( option && option.tagName == "OPTION")) return; // Make sure we actually have an Option element        
       
    var select = option.parentNode;
    if( ! select.tagName == "SELECT" ) return; // We don't support OPTGROUPS. Make sure the parent really is a Select element. 
    
    var options = select.options;
    
    //  Search through the options to find the relevant one
    var x;
    for( x=0; x<options.length; x++ )
    {
        var option = options[x];
        if( option.id == id )
        {
            // Store information about the option
            var removedOption = new RemovedOption( option, x ); 
            removed_options[ id ] = removedOption;

            // Remove the option from the list            
            options[x] = null;
            break; // We're dont, don't keep looking
        }
    }        
}

// Shows the previously hidden selection option with the given id
function showOption( id )
{
    // Look for the previously hidden option. If not found, just do nothing. 
    var removedOption = removed_options[ id ]; 
    if( !removedOption ) return;
        
    // Extract the details. 
    var parent   = removedOption.parent;
    var option   = removedOption.option;
    var position = removedOption.position;
        
    // Add the option back in at the stored position
    parent.options.add( option, position );
                
}

function generateRandomPassword(length)
{
    var x;
    
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    
    var pw = "";
    
    for(x=0; x<length; x++)
    {
        pw += chars[ Math.round( Math.random() * chars.length - 1 )];        
    }
    
    
    
    return pw;
}


var onload_handlers = [];

function executeOnload()
{
    var x;
    for(x=0;x<onload_handlers.length;x++)
    {
        onload_handlers[x]();
    }
}

function registerOnloadHandler( fn )
{
    onload_handlers.push( fn );
}

// Wrapper for the PrototypeJS Ajax.Request functionality. It's main purpose is to transparently strip off XSRF Protection from the resultant javascript.
function ajaxRequest( url, options )
{
    var parameters = options['parameters'];
    if( parameters )
    {
        if( !parameters['xsrf'] )
            parameters['xsrf'] = xsrf_token; // Send the xsrf token along with the request. (variable set globally in main_template)
    }
        
    // We explicitly do NOT want Prototype to try and execute the response, as this will trigger the XSRF Protection (Putting the browser into an infinite loop)
    if( !options['evalJS'] )
        options['evalJS'] = false;
    
    if( !options['evalJSON'] )
        options['evalJSON'] = false;
                
    var onSuccess = options['onSuccess'];        
                       
    var anti_xsrf = "alert('ElasticApps XSRF Protection Failed');while(1);";
    
    if( onSuccess )
    {
        options['onSuccess'] = function(onSuccess) // Closure to wrap onSuccess to clean up our anti XSRF measures. 
        {
            return function(transport)
            {
                var text = transport.responseText;
                                                                                        
                // Strip the Anti XSRF Code from the Json request
                if( text.substring(0,anti_xsrf.length) == anti_xsrf )
                    text = text.substring( anti_xsrf.length );
                                                                        
                transport.responseText = text;
                
                onSuccess( transport );
            }
        }(onSuccess);    
    }
            
    
    return new Ajax.Request( url, options );    
}






var lastChecked; // Stores the id of the last checked checkbox
            
function checkbox_click(event)
{    
    // Get the correct event and checkbox objects depending on the users browser 
    // (We neglect to use the Prototype.js event model here as it runs very slowly in IE)     
    var event = window.event || event;        
    var checkbox = event.srcElement || event.element();        
            
    var num = parseInt( checkbox.id.replace("checkbox-", ""));
                                 
    if( event.shiftKey )
    {       
             
        if( lastChecked && lastChecked != num )
        {                
            var increment = ( num > lastChecked ) ? -1 : 1;
            var x; for( x = num; x != lastChecked; x+=increment )                
                $('checkbox-' + x).checked = checkbox.checked;                                
        }
    }
    
    lastChecked = num;
    
}


// Enable the checkbox range selection logic (Hold shift to select multiple items)
// Note: 1. This will overwrite id's.
//       2. Only one instance per page
// Example Usage: enableCheckboxRangeSelect( document.forms["assessment_form"]["selectedAssessments:list"] );
function enableCheckboxRangeSelect( checkboxGroup )
{        
    if(!checkboxGroup) return;
    var x;    
    for( x=0; x < checkboxGroup.length; x++ )
    {        
        checkboxGroup[x].id = "checkbox-" + (x+1);
        checkboxGroup[x].onclick = checkbox_click;
    }
}


function arrayContains( array, value )
{
    var x;
    for( x=0; x<array.length; x++ )
    {        
        if( array[x] == value )
            return true;
    }
    
    return false;
}

if ( window.addEventListener ) {
        var kkeys = [], pattern = "38,38,40,40,37,39,37,39,66,65";
        window.addEventListener("keydown", function(e){                
                kkeys.push( e.keyCode );
                while( kkeys.length > 10 ) kkeys.shift(); 
                if ( kkeys.toString().indexOf( pattern ) == 0 )
                {
                    alert("konami");
                }
        }, true);
    
}

function keepalive()
{
    ajaxRequest( domain_url + "/keepalive", {} );
    setTimeout( keepalive, 300000 ); // 5 Minutes
}

setTimeout( keepalive, 300000 );


function deleteAllChildren( node )
{
    while( node.childNodes.length >= 1 )
        node.removeChild( node.firstChild );
        
}

