﻿(function() {  
    //Check that the required YUI libraries are availble.
    var MissingYUILibraries = '';
    if(null == window.YAHOO)
        alert('The YUI YAHOO library is missing.\nPlease add it to your page\'s client scripts.');
    else
    {
        if(null == YAHOO.env.modules.dom)
            MissingYUILibraries += 'DOM, ';
            
        if(null == YAHOO.env.modules.event)
            MissingYUILibraries += 'Event, ';

        if(null == YAHOO.env.modules.connection)
            MissingYUILibraries += 'Connection, ';
            
        if(MissingYUILibraries != '')
            alert('The following YUI libraries are missing: ' + MissingYUILibraries.substr(0, MissingYUILibraries.length-2) + '.\nPlease add them to your page\'s client scripts.');
    }
})();

//=============================================================================
// The PageActions class is a part of the DFAPage class infrastructure which
// enables the page developer to simply perform small AJAX operations.
//
// By calling the RegisterActionsWebService method of the DFAPage class, 
// a single instance of the PageActions class will be created for the page 
// on startup.
//
// To invoke page action AJAX calls, the page developer should call the 
// Execute method of the PageActions class. The Execute method expects two 
// arguments; actionID (unique string which identifies the action), and args 
// (string, or array of strings which contains the actions arguments).
//=============================================================================

//Define the PageActions class.
function PageActions(webServiceURL, callbackFunction)
{
    var PrivateMethods = { instance: this };

    //Properties
    this.WebServiceURL = webServiceURL;
    this.CallbackFunctionName = callbackFunction
    this.CallbackFunction = _EvalFunction(callbackFunction);
    
    //Private AJAX callback handler for page actions.
    PrivateMethods.CreatePageActionsCallback = function(userCallback)
    {
        return PrivateMethods.CreateCallback(userCallback, _PageActionsCallback_ProcessResponse);
    }
    
    //Private AJAX callback handler for get-page operations
    PrivateMethods.CreateGetPageCallback = function(userCallback)
    {
        return PrivateMethods.CreateCallback(userCallback, _GetPageCallback_ProcessResponse);
    }

    PrivateMethods.CreateCallback = function(userCallback, processingCallback)
    {
        var UserCallbackFunction;
        UserCallbackFunction = userCallback
        if (null != UserCallbackFunction)
        {   
            if(typeof(userCallback) == "string")
                UserCallbackFunction = _EvalFunction(userCallback);
            else 
                UserCallbackFunction = userCallback;
        }
        else
            UserCallbackFunction = _EvalFunction(this.instance.CallbackFunctionName)    
    
        return {
            success: _Callback_ProcessResponse,
            failure: _Callback_HandleError,
            argument: { 
                userCallback: UserCallbackFunction, 
                userCallbackName: _GetFunctionName(UserCallbackFunction), 
                processingCallback: processingCallback 
            }
        }
    }


    //Methods
    //Execute an AJAX page action.
    this.Execute = function (actionID, args, callbackFunction)
    {
        var arguments, ReloadDataField;
        
        //Mark that the page requires data reload.
        ReloadDataField = document.getElementById('ReloadData');
        if(null != ReloadDataField)
            ReloadDataField.value = 'true';
        
        // Serialize the page action's arguments.
        arguments = args;
        if(args.join)
            arguments = escape(args.join('^^'));
            
        // Call the page's action processing web service.
        var PageActionsConnection = YAHOO.util.Connect.asyncRequest(
            'POST', this.WebServiceURL, 
            PrivateMethods.CreatePageActionsCallback(callbackFunction), 
            'actionID=' + actionID + '&args=' + arguments);
    }


    //Save changes.
    this.SaveChanges = function (saveID, pendingChanges, callbackFunction)
    {
        var changes;
        
        // Serialize the page action's arguments.
        changes = pendingChanges;
        if(pendingChanges.join != null)
            changes = pendingChanges.join('^^');
            
        // Call the page's action processing web service.
        var SaveChangesConnection = YAHOO.util.Connect.asyncRequest(
            'POST', this.WebServiceURL, 
            PrivateMethods.CreatePageActionsCallback(callbackFunction), 
            "actionID=" + escape("__SAVE__") + "&saveID=" + saveID + "&pendingChanges=" + changes);
    }
    
    
    //Get a full page or page sections from the server.
    this.GetPage = function (pageID, pageName, queryString, callbackFunction)
    {
        var PageQueryString;
            
        PageQueryString = queryString;
        if(queryString && queryString.length > 0)
            PageQueryString = PageQueryString + "&"
        PageQueryString = PageQueryString + "getpage=1&ie=" + new Date().getTime(); // the ie querystring param is a hack to prevent IE from caching this response   
            
        // Call the page's action processing web service.
        var GetPageConnection = YAHOO.util.Connect.asyncRequest(
            'GET', pageName + '?' + PageQueryString, PrivateMethods.CreateGetPageCallback(callbackFunction));
    }
    
    return this;
}


//=============================================================================
// If an AJAX operation ended successfully, the response object will be loaded
// with the error information, and returned to the user's callback function.
//=============================================================================
function _Callback_ProcessResponse(o) {
    // SUCCESS: the following funtion will be called if the page action's 
    // ajax call was completed successfully (Status Code = 200)
     
    var UserCallback, RawResponse, ResponseInfo;

    //Retrieve the text representation of the JavaScript response object from the response text.
    if(null != o.responseText) {
         RawResponse = o.responseText;
    }
      
    try {
        //Try to get a reference of the user's callback function.
        UserCallback = this.argument.userCallback;
        if(null == UserCallback) {
            UserCallback = _EvalFunction(this.argument.userCallbackName);    
        }
        
        ResponseInfo = this.argument.processingCallback(RawResponse);
        
        //Call the user's callback function so he can process the response.
        if(null != UserCallback) {
            UserCallback(ResponseInfo);
        }
    }
    catch(ex) {
        if(null != UserCallback)
            UserCallback({error: 'An error has occurred while trying to process the response (' + ex + ')'});
        else
            alert('An error has occurred while trying to process the response');
    }
}

//=============================================================================
// If an AJAX operation ended with an error, the response object will be loaded
// with the error information, and returned to the user's callback function.
//=============================================================================
function _Callback_HandleError(errorInfo)
{
    // FAILURE: the following funtion will be called if the page action's 
    // ajax call has failed (Status Code != 200)

    var COMM_CODE = 0; //Communication Failure

    //Create an object with the error information.
    var ResponseInfo = { 
        error : errorInfo.statusText + ' (' + errorInfo.status + ')', 
        stackTrace : errorInfo.responseText };
    
    //Call the user's callback function so he can handle the error.          
    if (null == this.argument.CallbackFunction) {
        this.argument.CallbackFunction = _EvalFunction(this.argument.CallbackFunctionName);    
    }
        
    if (null != this.argument.CallbackFunction) {
        this.argument.CallbackFunction(ResponseInfo);
    } else if (errorInfo.status != COMM_CODE) {
        alert(ResponseInfo.error);
    }
}


//=============================================================================
// If a page action was invoked, this callback will handle the response and
// create the response object that will be returned to the user's callback
// function.
//=============================================================================
function _PageActionsCallback_ProcessResponse(rawResponse) 
{
    var ResponseInfo, arrResponseInfo, RawDataBlockIndex;

    //Get data blocks.
    arrResponseInfo = rawResponse.split("##RAW##");

    //Convert the text representation of the response into a JavaScript object.
    eval("ResponseInfo = " + arrResponseInfo[0] + ";");
    
    if(arrResponseInfo.length > 1)
    {
        ResponseInfo.rawData = [ ];
        for(RawDataBlockIndex = 1; RawDataBlockIndex < arrResponseInfo.length; RawDataBlockIndex++)
            ResponseInfo.rawData.push(arrResponseInfo[RawDataBlockIndex]);    
    }      
    
    return ResponseInfo;
}



//=============================================================================
// If a get page request was invoked, this callback will handle the response. 
// It will dynamically load all of the client scripts on the requested page, 
// extract all of the HTML markup blocks, and call the user's callback.
//=============================================================================
function _GetPageCallback_ProcessResponse(rawResponse) 
{
    var ResponseInfo;
    var StartIndex, EndIndex, arrSectionStartTags, SectionIndex;
    
    //Initialize the response object.
    ResponseInfo = { };
    ResponseInfo.scriptBlocks = [ ];
    ResponseInfo.htmlBlocks = [ ];
    ResponseInfo.loadEvents = YAHOO.util.Event.getListeners(window, "load") || [ ];
    
    //Retrieve all the script blocks in the returned page and store them in the response. 
    StartIndex = rawResponse.indexOf("<script");
    while(StartIndex >= 0)
    {
        EndIndex = rawResponse.indexOf("</script>", StartIndex) + 9;
        ResponseInfo.scriptBlocks[ResponseInfo.scriptBlocks.length] = { 
            script:rawResponse.substring(StartIndex, EndIndex), 
            position:StartIndex };
        StartIndex = rawResponse.indexOf("<script", StartIndex+1);
    }

    //Retrieve all of the HTML sections from the returned page and store them in the response.  
    arrSectionStartTags = rawResponse.match(/(<!--\s?SECTION\s?:\s?).+(\s?-->)/ig)
    if(arrSectionStartTags && arrSectionStartTags.length > 0)
    {
        for (SectionIndex = 0; SectionIndex < arrSectionStartTags.length; SectionIndex++)
        {
            StartIndex = rawResponse.indexOf(arrSectionStartTags[SectionIndex]);
            EndIndex = rawResponse.indexOf(arrSectionStartTags[SectionIndex].replace("SECTION", "END-SECTION"));
            if (StartIndex != -1 && EndIndex != -1)
            {
                ResponseInfo.htmlBlocks[ResponseInfo.htmlBlocks.length] = {
                    id:arrSectionStartTags[SectionIndex].substring(arrSectionStartTags[SectionIndex].indexOf(":")+1, arrSectionStartTags[SectionIndex].indexOf("-->")),
                    html:rawResponse.substring(StartIndex, EndIndex),
                    position:StartIndex,
                    outputElement:"",
                    loaded:false };    
            }
        }
    }

    ResponseInfo.LoadPendingScripts = LoadPendingScripts;
    ResponseInfo.CompletePendingScripts = CompletePendingScripts;
    ResponseInfo.setTimeout = function(callback, delay)
    {
        var that = ResponseInfo;
        var closureCallback = function()
        {
            callback.call(that);
        }
        window.setTimeout(closureCallback, delay);
    }    
    
    ResponseInfo.apply = function(onLoadHendler)
    {
        var CurrentOutputElement, htmlBlockIndex, CurrentHtmlBlock, OutputElement, GetNextHtmlBlock;
        var ScriptTag, CurrentScript, ScriptExists, ScriptIndex, srcStartChar, srcStart, srcEnd;
        
        if(onLoadHendler)
            this.onload = onLoadHendler;
        
        //Clear the html output element before updating their content.
        for(htmlBlockIndex = 0; htmlBlockIndex < this.htmlBlocks.length; htmlBlockIndex++)
        {
            CurrentOutputElement = this.htmlBlocks[htmlBlockIndex].outputElement;
            if(typeof(CurrentOutputElement) == "string") {
                CurrentOutputElement = document.getElementById(CurrentOutputElement);
            }
            
            if(null != CurrentOutputElement) {
                this.htmlBlocks[htmlBlockIndex].outputElement = CurrentOutputElement;
                CurrentOutputElement.innerHTML = "";
            }
        }

        htmlBlockIndex = 0;
        CurrentHtmlBlock = this.htmlBlocks[htmlBlockIndex];
        this.PendingScripts = [ ];
        for(ScriptIndex = 0; ScriptIndex < this.scriptBlocks.length; ScriptIndex++)
        {
            ScriptExists = false;
            CurrentScript = this.scriptBlocks[ScriptIndex];
            
            ScriptTag = document.createElement("script");
            ScriptTag.type = "text/javascript";

            srcStart = CurrentScript.script.indexOf("src=");
            if(srcStart > 0)
            {
                srcStart = srcStart+4;
                srcEnd = 0;
                
                srcStartChar = CurrentScript.script.substr(srcStart, 1);
                if(srcStartChar == "\"" || srcStartChar == "\'") {
                    srcStart++;
                    srcEnd = CurrentScript.script.indexOf(srcStartChar, srcStart+2);
                } else {
                    srcEnd = CurrentScript.script.indexOf(" ", srcStart+2);
                    if(srcEnd == 0) {
                        srcEnd = CurrentScript.script.indexOf(">", srcStart+2);
                    }
                }
                
                ScriptTag.src = CurrentScript.script.substring(srcStart, srcEnd).replace(/&amp;/g, "&");
                var scriptTags = YAHOO.util.Dom.getElementsBy(function(el) 
                { 
                    return (el.getAttribute("src") != "" && ScriptTag.src.indexOf(el.getAttribute("src")) != -1); 
                }, "script", null); //document.getElementsByTagName('head')[0]
                ScriptExists = (scriptTags && scriptTags.length > 0);
            }
            else
            {
                srcStart = CurrentScript.script.indexOf(">")+1;
                srcEnd = CurrentScript.script.length-9;
                if(ScriptTag.outerHTML)
                {
                    //IE
                    ScriptTag.text = CurrentScript.script.substring(srcStart, srcEnd); 
                }
                else
                {
                    //FireFox and all the rest.
                    ScriptTag.innerHTML = CurrentScript.script.substring(srcStart, srcEnd);
                }
            }
                    
                                
            if (null != CurrentHtmlBlock && !CurrentHtmlBlock.loaded) {
                GetNextHtmlBlock = false;
                if(null != CurrentHtmlBlock.outputElement) {
                    if(CurrentScript.position > CurrentHtmlBlock.position) {
                        CurrentHtmlBlock.outputElement.innerHTML += CurrentHtmlBlock.html;
                        CurrentHtmlBlock.loaded = true;
                        GetNextHtmlBlock = true;
                    }
                } else {
                    GetNextHtmlBlock = true;
                }
                
                if (GetNextHtmlBlock === true && (htmlBlockIndex+1) < this.htmlBlocks.length) {
                    htmlBlockIndex++;
                    CurrentHtmlBlock = this.htmlBlocks[htmlBlockIndex];                
                }
            }
                    

            if(!ScriptExists && null != CurrentHtmlBlock.outputElement)           
            {
                var SpaceHolder;
                
                if(ScriptTag.src == "")
                {
                    SpaceHolder = document.createElement("span");
                    CurrentHtmlBlock.outputElement.appendChild(SpaceHolder);
                    ScriptTag.spaceHolder = SpaceHolder;
                }

                ScriptTag.isComplete = false;
                ScriptTag.loadCount = 0;

                if(ScriptTag.outerHTML)
                {
                    //IE                        
                    ScriptTag.isLoaded = false;
                    ScriptTag.onreadystatechange = ScriptStateChanged;
                    this.PendingScripts[this.PendingScripts.length] = ScriptTag;    
                }
                else
                {
                    //FireFox
                    ScriptTag.isLoaded = true;
                    ScriptTag.onload = ScriptStateChanged;
                }
                
                this.PendingScripts[this.PendingScripts.length] = ScriptTag;
            }
        }

        //Load all of the HTML blocks that were not loaded yet.
        for (htmlBlockIndex = htmlBlockIndex; htmlBlockIndex < this.htmlBlocks.length; htmlBlockIndex++) {
            CurrentHtmlBlock = this.htmlBlocks[htmlBlockIndex];
            if(null != CurrentHtmlBlock.outputElement && !CurrentHtmlBlock.loaded)
            {
                CurrentHtmlBlock.outputElement.innerHTML += CurrentHtmlBlock.html;
                CurrentHtmlBlock.loaded = true;
            }  
        }
                 
        this.LoadPendingScripts();   
    }
   
    return ResponseInfo;
}

function ScriptStateChanged()
{

    if(this.onload) {
        //FireFox
        this.isComplete = true;
    } else {
        //IE
        if(this.readyState == 'loaded') {
            this.isLoaded = true;
        } else if(this.readyState == 'complete') {
            this.isComplete = true;
        }
    }
}        

function LoadPendingScripts()
{
    var ScriptIndex, AllScriptsLoaded;

    AllScriptsLoaded = true;
    for(ScriptIndex = 0; ScriptIndex < this.PendingScripts.length && AllScriptsLoaded; ScriptIndex++)
        AllScriptsLoaded = AllScriptsLoaded & (this.PendingScripts[ScriptIndex].isLoaded || this.PendingScripts[ScriptIndex].src == "")
        
    if(AllScriptsLoaded)
    {
        for(ScriptIndex = 0; ScriptIndex < this.PendingScripts.length; ScriptIndex++)
            if(this.PendingScripts[ScriptIndex].src != "")
                document.getElementsByTagName('head')[0].appendChild(this.PendingScripts[ScriptIndex]);
                //this.PendingScripts[ScriptIndex].spaceHolder.appendChild(this.PendingScripts[ScriptIndex]);

        this.CompletePendingScripts();
    }
    else
    {   
        this.setTimeout(LoadPendingScripts, 500);
    }
}

function CompletePendingScripts()
{
    var ScriptIndex, CurrentScriptCompleted, AllScriptsCompleted;
    var CurrentLoadEvents, EventIndex, OldEventIndex;
    
    if (null == this.response) {
        this.response = { success: true };    
    }
    
    //Check if all of the dynamically loaded scripts have completed thier load process.
    AllScriptsCompleted = true;
    for(ScriptIndex = 0; ScriptIndex < this.PendingScripts.length && AllScriptsCompleted; ScriptIndex++) {
        CurrentScriptCompleted = (this.PendingScripts[ScriptIndex].isComplete || this.PendingScripts[ScriptIndex].src == "");
        AllScriptsCompleted = AllScriptsCompleted & CurrentScriptCompleted;

        if (CurrentScriptCompleted == false) {
            this.PendingScripts[ScriptIndex].loadCount++;
            if (this.PendingScripts[ScriptIndex].loadCount == 10) {
                this.PendingScripts[ScriptIndex].isComplete = true;
                this.response.success = false;
                this.response.error = "Can't load the following script library:\n" + this.PendingScripts[ScriptIndex].src;
            }
        }
    }

    if(AllScriptsCompleted)
    {
        for(ScriptIndex = 0; ScriptIndex < this.PendingScripts.length; ScriptIndex++)
            if(this.PendingScripts[ScriptIndex].src == "")
                this.PendingScripts[ScriptIndex].spaceHolder.appendChild(this.PendingScripts[ScriptIndex]);

        //Get the current set of window.onload events, and compare them to the 
        //event set that was available at the beginning of the PageActions.GetPage call.
        CurrentLoadEvents = YAHOO.util.Event.getListeners(window, "load");
        if(CurrentLoadEvents && CurrentLoadEvents.length > 0)
        {
            for(OldEventIndex = 0; OldEventIndex < this.loadEvents.length; OldEventIndex++)
            {
                for(EventIndex = 0; EventIndex < CurrentLoadEvents.length; EventIndex++)
                {
                    if(this.loadEvents[OldEventIndex].index == CurrentLoadEvents[EventIndex].index)
                    {
                        CurrentLoadEvents[EventIndex].isLoaded = true;
                        break;
                    }
                }
            }

            //Manually call all of the window.onload event handlers that were not previously called.
            for(LoadEventIndex = 0; LoadEventIndex < CurrentLoadEvents.length; LoadEventIndex++)
            {
                if(!CurrentLoadEvents[LoadEventIndex].isLoaded)
                {
                    CurrentLoadEvents[LoadEventIndex].fn.call(CurrentLoadEvents[LoadEventIndex].adjust);
                }
            }
        }
         
        //If the user specified an onload event handler, it will be called.
        if(this.onload)
        {
            if(typeof(this.onload) == "string")
                this.onload = _EvalFunction(this.onload);
            
            if(this.onload)    
                this.onload.call(this.response, this.response);
        }
    }
    else
    {
        this.setTimeout(CompletePendingScripts, 500);
    }
}








//This function evaluate a function name and return a reference to the function if it exists.
function _EvalFunction(functionName)
{
    return eval('(typeof ' + functionName + ' == \'undefined\' ? null : eval(\'' + functionName + '\'));');
}

//Get a function's name.
function _GetFunctionName(functionObject) 
{
    var FunctionName;
    
    FunctionName = functionObject.name;
    if(!FunctionName)
    {
        FunctionName = functionObject.toString();
        FunctionName = FunctionName.substr("function ".length);        
        FunctionName = FunctionName.substr(0, FunctionName.indexOf("("));   
    }

    return FunctionName;
}