var MATRIX2_VIEWSTATE_ID                  = "VIEWSTATE";
var MATRIX2_EVENTTARGET_ID                = "EVENTTARGET__";
var MATRIX2_EVENTARGUMENTS_ID             = "EVENTARGUMENT__";
var MATRIX2_VIEWSTATE_FIELD_DROPABLE_LIST = "DropableList";
var MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE= "VFormBOTable";
var MATRIX2_VIEWSTATE_FIELD_BO_STATE_LIST = "BOStateList";
var MATRIX2_VIEWSTATE_FIELD_ACTION_LIST   = "ActionList";
var MATRIX2_VIEWSTATE_FIELD_DEFAULT_ACTION= "DefaultActionList";
var MATRIX2_VIEWSTATE_FIELD_REFRESH_TABLE = "RefreshTable";
var MATRIX2_VIEWSTATE_FIELD_URL           = "URL";
var MATRIX2_VIEWSTATE_FIELD_VERSION       = "Version";
var MATRIX2_MAIN_FORM                     = "Form1";
var MATRIX2_AJAX_FIELD	                  = "Ajax";
var MATRIX2_MATRIX_VERSION_FIELD          = "MV";
var MATRIX2_ACTION_TYPE_REDIRECT          = "D";
var MATRIX2_ACTION_TYPE_MODIFY            = "M";
var MATRIX2_ACTION_TYPE_ADD               = "A";
var MATRIX2_ACTION_TYPE_REMOVE            = "R";
var MATRIX2_ACTION_NODE_ACTION            = "A";
var MATRIX2_ACTION_NODE_PARENT            = "P";
var MATRIX2_ACTION_NODE_NEXT              = "N";
var MATRIX2_ACTION_NODE_INCLUDE           = "I";
var MATRIX2_ACTION_NODE_LAYOUT            = "L";
var MATRIX2_LAYER_TYPE_POPUP              = "P";
var MATRIX2_LAYER_TYPE_INLINE             = "I";
var MATRIX2_ACTION_INCLUDE_SPLITTER       = ",";
var MATRIX2_COVER_SUFFIX                  = '_cover';
var MATRIX2_POPUP_SUFFIX                  = DRAGDROP_POPUP_SUFFIX;
var MATRIX2_LOADING_COVER_ID              = 'LoadingCoverID';
var MATRIX2_LOADING_POPUP_ID              = 'LoadingPopupID';
var MATRIX2_LOADING_COVER_CLASS           = 'LoadingCoverClass';
var MATRIX2_LOADING_POPUP_CLASS           = 'LoadingPopupClass';
var MATRIX2_BODY_ID                       = 'BodyContent';
var MATRIX2_GLOBAL_COVER_CLASS            = "PopupCoverClass";
var MATRIX2_GLOBAL_POPUP_CLASS            = "PopupWindowClass";
var MATRIX2_VALID_MARKER                  = "<INPUT ";
var MATRIX2_VFORM_DEBUG                   = "DVF";
var MATRIX2_VFORM_DEBUG_CONTROL           = "DVF" + TOOLS_CONTROL_ID_SEPARATOR + "JSDebug";
var MATRIX2_CSS_COUNTER_TIMEOUT           = 200;
var MATRIX2_EVENT_LOADING_RESIZE          = "ResLoading";
var MATRIX2_EVENT_LOADING_UPDATE          = "UpLoading";
var MATRIX2_EVENT_WINDOW_RESIZE           = "WRes";
var MATRIX2_EVENT_WINDOW_RESIZE_FIRST     = "WFirstRes";
var MATRIX2_EVENT_INIT                    = "Matrix2Init";
var MATRIX2_EVENT_UPDATE                  = "Matrix2Update";
var MATRIX2_EVENT_BEFORE_REQUEST          = "Matrix2BefReq";
var MATRIX2_EVENT_AFTER_REQUEST           = "Matrix2AftReq";
var MATRIX2_EVENT_RENDER_END              = "MatrixRendered";
var MATRIX2_SEND_DRAGDROP_EVENT           = "DragDropEvent";

// TEMP AAC
var MATRIX2_SCRIPT_START                  = "<script type=\"text/javascript\">";
var MATRIX2_SCRIPT_END                    = "</script>";

// BUY / DOWNLOAD System
var MATRIX2_BUY_BUTTON                    = TOOLS_CONTROL_ID_SEPARATOR + "Buy";
var MATRIX2_DOWNLOAD_BUTTON               = TOOLS_CONTROL_ID_SEPARATOR + "Download_Button";
var MATRIX2_DOWNLOAD_EVENT                = TOOLS_CONTROL_ID_SEPARATOR + "Download";

giMatrixVersion = 2;

var Class = {
  create: function() {
    return function() {
      this.initialize.apply(this, arguments);
    }
  }
}

var Matrix2 = Class.create();
/**
 * The Full State is an array of arrays that represent the content of the
 * page state (with all BOs State, VForm infos, ...).
 * This variable is built from the JSON Value of the ViewState field.
 * 
 * Structure Description :
 * 
 * JSON :
 * {'Version' : '2',                                                                // Matrix Version
 *  'URL' : ['http://localhost/C.aspx?VP=SimpleVPage'],                             // Current page url
 *  'DropableList' : ['Control1', 'Control2', 'Control3'],                          // List of dropable elements
 *  'RefreshTable' : {'VForm1@Control1' : 1000, 'VForm2@ControlA' : 2000},          // List of refresh control
 *  'VFormBOTable' : {'VForm1' : ['BO1', 'BO2'],                                    // VForm / Bo link
 *                    'VForm2' : ['BO2']
 *                   },
 *  'BOStateList' :  {'BO1' : 'EncryptedBO1State',                                  // BO States
 *                    'BO2' : 'EncryptedBO2State'
 *                   },
 *  'VFormAlias' :   [''],                                                          // VForm alias list (short and full VForm ID)
 *  'ActionList' :   {'VForm3' : {'A' : 'A', 'P' : 'VForm2.VForm3Panel', 'N' : 'VFormNextID', 'I' : ['File1.css','File2.css','File3.js'], 'L' : 'P'},
 *                    'VForm2' : {'A' : 'R'}
 *                    'VForm2' : {'A' : 'M'}
 *                    'http://www.google.com' : {'A' : 'D'}
 *                   }
 * 'DefaultactionList' : {'VF1': '__doPostBack()', 'VF2': 'new Matrix2.update()'}   // Default action on 'Enter Key' pressed on this VForm
 */

/* Register events */
Event_AddAction(MATRIX2_EVENT_WINDOW_RESIZE_FIRST, Matrix2_Event_onResizeFirst);
Event_AddAction(MATRIX2_EVENT_WINDOW_RESIZE, Matrix2_Event_onResize);
Event_AddAction(MATRIX2_EVENT_LOADING_RESIZE, Matrix2_Event_onResizeLoading);
Event_AddAction(MATRIX2_EVENT_LOADING_UPDATE, Matrix2_Event_onUpdateLoading);
Event_AddAction(MATRIX2_EVENT_UPDATE, Matrix2_Event_Update);
Event_AddAction(MATRIX2_EVENT_BEFORE_REQUEST, Matrix2_Event_BeforeRequest);
Event_AddAction(MATRIX2_EVENT_AFTER_REQUEST, Matrix2_Event_AfterRequest);
Event_AddAction(MATRIX2_EVENT_RENDER_END, Matrix2_Event_Rendered);
Event_AddAction(EVENT_PAGE_LOAD, Matrix2_Event_PageLoad);
Event_AddAction(DRAGDROP_EVENT_DROP, Matrix2_Event_Drop);

/** INFO : We use the global variable hack to add a pause just before the
 * Transfert. Used to compute the real loading time.
 */
var goRequestArgs;
function Matrix2_Event_Update()
{
    // Sets the processing flag
    Matrix2.gbProcessing     = true;
    
    // Init Timer
    Matrix2.giTimerGetInput  = 0;
    Matrix2.giTimerTransfert = 0;
    Matrix2.giTimerRender    = 0;
    
    // Start Input timer
    Matrix2.giTimerGetInput = new Date();
}

function Matrix2_Event_BeforeRequest()
{
    // Stop the Get Input timer
    Matrix2.giTimerGetInput = new Date() - Matrix2.giTimerGetInput;
    
    // Start the transert timer
    Matrix2.giTimerTransfert = new Date();
}

function Matrix2_Event_AfterRequest()
{
    // Stop the timer
    Matrix2.giTimerTransfert = new Date() - Matrix2.giTimerTransfert;

    // Start the Render timer
    Matrix2.giTimerRender = new Date();
}

function Matrix2_Event_Rendered()
{
    // Remove the processing flag
    Matrix2.gbProcessing     = false;

    // Stop the Timer of the render
    Matrix2.giTimerRender = new Date() - Matrix2.giTimerRender;
}

function Matrix2_Event_onResizeLoading()
{
    // Get the loading cover

    // Resize it
    // Visible ?
    if(Matrix2.goCoverElement != null)
    {
        Tools_ResizeCover(Matrix2.goCoverElement);
    }
    
    // Update loading panel position
    Tools_UpdateFixedPosition(Matrix2.goLoadingElement);
}

function Matrix2_Event_onUpdateLoading()
{
    // Visible ?
    if(Matrix2.goLoadingElement != null)
    {
        // Update loading panel position
        Tools_UpdateFixedPosition(Matrix2.goLoadingElement);
    }
}

// EVENTS CALLBACK
function Matrix2_Event_onResize()
{
    Matrix2_Event_onResizeLoading();
    
    // Loop on the array
    for(var i=0; i<Matrix2.gasPopupList.length; i++)
    {
        // Get the element from the id
        var sID = Matrix2.gasPopupList[i];
        var oCoverElement = document.getElementById(sID + MATRIX2_COVER_SUFFIX);
        var oPopupElement = document.getElementById(sID + MATRIX2_POPUP_SUFFIX);

        // Resize and center popup
       Tools_ResizeCover(oCoverElement);
       Tools_CenterPopup(oPopupElement);
    }
}

function Matrix2_Event_onResizeFirst()
{
    // Center requested ?
    if(Matrix2.gbCenterRequest)
    {
        // Check that the Css Are loaded in cache
        if((!Tools_CheckCssLoaded())
        && (Matrix2.giDelayCssCounter < MATRIX2_CSS_COUNTER_TIMEOUT))
        {
            window.setTimeout(Matrix2_Event_onResizeFirst, 1)
            
            // Increase delay counter
            Matrix2.giDelayCssCounter++;
        }
        else
        {
            // Cancel centering
            Matrix2.gbCenterRequest = false;
                
            // Timeout ?
            if(Matrix2.giDelayCssCounter >= MATRIX2_CSS_COUNTER_TIMEOUT)
            {
                // Are we in debug mode ?
                if(document.getElementById(MATRIX2_VFORM_DEBUG))
                {
                    // Adds an alert to set the that a CSS is not loadable
                    alert('CSS Loading timeout(' + Matrix2.giDelayCssCounter + ') : A css file can not be loaded. Check that all CSS files have at least one rule');
                }
                else
                {
                    // Adds an alert to set the that a CSS is not loadable
                    alert('A communication problem has occured... Loading timeout.');
                }
            }
            
            // Reset counter
            Matrix2.giDelayCssCounter = 0;
            
            // Resize popup
            Matrix2_Event_onResize();
        
            // Shows the popups
            if(Matrix2.gasPopupList.length > 0)
            {
                for(var i=0; i<Matrix2.gasPopupList.length; i++)
                {
                    // Get the element from the id
                    var sID = Matrix2.gasPopupList[i];
                    var oCoverElement = document.getElementById(sID + MATRIX2_COVER_SUFFIX);
                    var oPopupElement = document.getElementById(sID + MATRIX2_POPUP_SUFFIX);
            
                    // Are we on IE ?
                    if(Tools_IsIE4)
                    {
                        // Are there popup ?
                        if(Matrix2.gasPopupList.length > 0)
                        {
                            // Remove al Dropdown list
                            Tools_SetBuggedControlVisibility(document.documentElement, "hidden");
                            
                            // Get the last id
                            var sID = Matrix2.gasPopupList[Matrix2.gasPopupList.length - 1];
                            var oNode = document.getElementById(sID);
        
                            // Valid ?
                            if(oNode != null)
                            {
                                // Make drop down on popup visible
                                Tools_SetBuggedControlVisibility(oNode, "visible");
                            }
                        }
                    }
        
                    // Resize and center popup
                    // Valid ?
                    if(oCoverElement != null)
                    {
                        oCoverElement.style.visibility = "visible";
                    }
                    
                    if(oPopupElement != null)
                    {
                        oPopupElement.style.visibility = "visible";
                    }
                }
            }
            else
            {
                // Are we on IE ?
                if(Tools_IsIE4)
                {
                    // Set all element as visible
                    Tools_SetBuggedControlVisibility(document.documentElement, "visible");
                }
            }
        }
    }
}

function Matrix2_Event_OnRefresh(_sVFormID, _sControlID, _iPeriod, _iLoop)
{
    var oMatrix2 = new Matrix2();
    
    var asBO = new Array();
    
    if(_iLoop == TOOLS_UNDEFINED_VALUE)
    {
        _iLoop = 0;
    }
    
    // Gets BO List
    var oBOs = oMatrix2._getBOList(new Array(_sVFormID));
    
    // loop on the BO array
    for(var sKey in oBOs)
    {
        // Adds BO
        asBO.push(sKey);
    }
    
    // Build refresh string callback
    var sCallback = "Matrix2_Event_OnRefresh('" + _sVFormID   + "', " +
                               "'" + _sControlID + "', " +
                               "'" + _iPeriod    + "', " +
                                     parseInt(_iLoop + 1)  + ")";

    // Already updating or dragging ?
    if((Matrix2.gbProcessing)
    || (DragDrop_goDragElementList != null))
    {
        window.setTimeout(sCallback, 1000);
    }
    else
    {
        window.setTimeout(sCallback, _iPeriod);

        // Executes Ajax call (if not first call) and not already processing
        if(_iLoop > 0)
        {
            new Matrix2().update(asBO, _sVFormID + TOOLS_CONTROL_ID_SEPARATOR + _sControlID, '');
        }
    }
}

function Matrix2_Event_OnKeyPress()
{
    // Get the Key Pressed
    var iKeyCode = Tools_giKeyCode;
    
    // Enter ?
    if(iKeyCode == EVENT_KEY_ENTER)
    {
        // Get the control that get the focus
        var sControlID = Tools_goActiveField.id;
        
        // Valid ?
        if(sControlID != null)
        {
            var iIdx = sControlID.indexOf(TOOLS_CONTROL_ID_SEPARATOR);
            
            // Valid ?
            if(iIdx >= 0)
            {
                var sVFormID = sControlID.substr(0, iIdx);
                
                // Valid ?
                if(sVFormID != null)
                {
                    // Gets DefaultActionList
                    new Matrix2();
                    var sAction = Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_DEFAULT_ACTION][sVFormID];
                    
                    // Valid ?
                    if(sAction != null)
                    {
                        // Executes it
                        eval(sAction);
                    }
                }
            }
        }
    }
}

function Matrix2_Event_PageLoad()
{
    // Initialize view state
    new Matrix2();
    
    // Register the Key press event
    Event_AddAction(EVENT_KEY_PRESS, Matrix2_Event_OnKeyPress);
    
    // Update the dropable list
    var asDropableList = Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_DROPABLE_LIST];
    
    // Valid array ?
    if((asDropableList != null)
    && (asDropableList.length))
    {
        // update the dragable list
        for(var i=0; i<asDropableList.length; i++)
        {
            DragDrop_AddTarget(asDropableList[i]);
        }
    }
    
    // Executes all the Viewstate evaluation => Transformation into a HASH
    
    // 
    var oRefreshTable = Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_REFRESH_TABLE];
    
    // Init refresh callback system
    for(var sKey in oRefreshTable)
    {
        // Gets value
        var iPeriod = parseInt(oRefreshTable[sKey]);
        
        // Valid number ?
        if(!isNaN(iPeriod))
        {
            // Gets VForm and control ID
            var iSplitterIdx = sKey.indexOf(TOOLS_CONTROL_ID_SEPARATOR);
            
            // Valid ?
            if((iSplitterIdx > 0)
            && (iSplitterIdx < sKey.length - 1))
            {
                // Gets VForm ID
                var sVFormID = sKey.substr(0, iSplitterIdx);
                var sControlID = sKey.substr(iSplitterIdx + 1);
                
                // Calls the refresh function
                Matrix2_Event_OnRefresh(sVFormID, sControlID, iPeriod);
            }
        }
    }
    
    // Inits Cover and loading elements
    Matrix2.goCoverElement      = document.getElementById(MATRIX2_LOADING_COVER_ID);
    Matrix2.goLoadingElement    = document.getElementById(MATRIX2_LOADING_POPUP_ID);
    
}

function Matrix2_Event_Drop(_oDragContext, _oEvent, _oDropContext)
{
    // Get Matrix2
    var oMatrix2 = new Matrix2();
    
    // Valid drop context ?
    if(_oDropContext != null)
    {
        // Serialize the context
        var sArguments = json.serialize(_oDropContext);
        
        // Valid ?
        if((sArguments != null)
        && (sArguments.length > 0))
        {
            // Build the VForm list
            var asVFormList = new Array();
            for(var i=0; i<_oDropContext.aoSourceElementList.length; i++)
            {
                // Gets Full control ID
                var sControlID = _oDropContext.aoSourceElementList[i];
                
                // Gets VForm ID
                var iSplitterIdx = sControlID.indexOf(TOOLS_CONTROL_ID_SEPARATOR);
                
                // Valid ?
                if(iSplitterIdx > 0)
                {
                    // Split
                    var sVFormID = sControlID.substr(0, iSplitterIdx);
                    
                    // Valid ?
                    if((sVFormID != null)
                    && (sVFormID.length > 0))
                    {
                        asVFormList.push(sVFormID);
                    }
                }
            }
            
            // Build the target VForm BO List
            
            // Gets VForm ID
            var sFullControlID = _oDropContext.oTargetElement;
            var iSplitterIdx   = sFullControlID.indexOf(TOOLS_CONTROL_ID_SEPARATOR);
            
            // Valid ?
            if(iSplitterIdx > 0)
            {
                // Split
                var sVFormID = sFullControlID.substr(0, iSplitterIdx);
                
                // Valid ?
                if((sVFormID != null)
                && (sVFormID.length > 0))
                {
                    asVFormList.push(sVFormID);
                }
            }

            // Gets the BO Array list
            var oBOList = oMatrix2._getBOList(asVFormList);
            var asBOList = new Array();
            
            // Valid ?
            if(oBOList != null)
            {
                for(var sKey in oBOList)
                {
                    asBOList.push(sKey);
                }
                
                if(asBOList.length > 0)
                {
                    // Send server event
                    oMatrix2.update(asBOList, MATRIX2_SEND_DRAGDROP_EVENT, sArguments);
                }
            }
        }
    }
}

// Static Property 
Matrix2.goViewState;
Matrix2.gasPopupList;
Matrix2.goCoverElement   = null;
Matrix2.goLoadingElement = null;


Matrix2.giDelayCssCounter;

Matrix2.giTimerGetInput;
Matrix2.giTimerTransert;
Matrix2.giTimerRender;
Matrix2.giTimerInputTransfert;

Matrix2.giUploadSize;
Matrix2.giDownloadSize;

Matrix2.gbProcessing = false;
Matrix2.gbCenterRequest;

Matrix2.gsDebugString = "";
Matrix2.goLookupArray = new Array();

Matrix2.prototype =
{
    /**
     * Constructor
     */
    initialize: function()
    {
        // Initialize the popup list
        if(Matrix2.gasPopupList == TOOLS_UNDEFINED_VALUE)
        {
            Matrix2.gasPopupList = new Array();
            
            // define the Event
            Event_AddAction(EVENT_RESIZE, Matrix2_Event_onResize);
            //Event_AddAction(EVENT_SCROLL, Event_OnResize);
        }
        
        // Initialize the CSS loading timeout counter
        if(Matrix2.giDelayCssCounter == TOOLS_UNDEFINED_VALUE)
        {
            Matrix2.giDelayCssCounter = 0;
        }
        
        if(Matrix2.giUploadSize == TOOLS_UNDEFINED_VALUE)
        {
            Matrix2.giUploadSize = 0;
        }

        if(Matrix2.giDownloadSize == TOOLS_UNDEFINED_VALUE)
        {
            Matrix2.giDownloadSize = 0;
        }

        if(Matrix2.gbProcessing == TOOLS_UNDEFINED_VALUE)
        {
            Matrix2.gbProcessing = false;
        }

        // Initialize the Centering request
        if(Matrix2.gbCenterRequest == TOOLS_UNDEFINED_VALUE)
        {
            Matrix2.gbCenterRequest = false;
        }
        
        // Initilize the View State
        if (Matrix2.goViewState == TOOLS_UNDEFINED_VALUE)
        {
            // Calls the init event
            Event_Execute(MATRIX2_EVENT_INIT);

            var oTempViewState;

            // Inits arrays
            Matrix2.goViewState = new Object();
            Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE] = new Object();
            Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_BO_STATE_LIST]  = new Object();
            Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_DEFAULT_ACTION] = new Object();
            Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_REFRESH_TABLE]  = new Object();
            Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_DROPABLE_LIST]  = new Array();

            // Gets temporary viewstate
            oTempViewState = eval('(' + document.getElementById(MATRIX2_VIEWSTATE_ID).value + ')');
            
            // Stores version
            giMatrixVersion = oTempViewState[MATRIX2_VIEWSTATE_FIELD_VERSION];

            // Stores URLs
            Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_URL] = oTempViewState[MATRIX2_VIEWSTATE_FIELD_URL];

            // Stores dropable list
            Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_DROPABLE_LIST] = oTempViewState[MATRIX2_VIEWSTATE_FIELD_DROPABLE_LIST];

            // Stores refresh table
            for(var sControlKey in oTempViewState[MATRIX2_VIEWSTATE_FIELD_REFRESH_TABLE])
            {
                Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_REFRESH_TABLE][sControlKey] = oTempViewState[MATRIX2_VIEWSTATE_FIELD_REFRESH_TABLE][sControlKey];
            }

            // Stores VForm-BO table array
            for(var sVFormBOKey in oTempViewState[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE])
            {
                Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE][sVFormBOKey] = oTempViewState[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE][sVFormBOKey];
            }

            // Stores BO list array
            for(var sBOKey in oTempViewState[MATRIX2_VIEWSTATE_FIELD_BO_STATE_LIST])
            {
                Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_BO_STATE_LIST][sBOKey] = oTempViewState[MATRIX2_VIEWSTATE_FIELD_BO_STATE_LIST][sBOKey];
            }

            // Stores default action
            for(var sVFormID in oTempViewState[MATRIX2_VIEWSTATE_FIELD_DEFAULT_ACTION])
            {
                Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_DEFAULT_ACTION][sVFormID] = oTempViewState[MATRIX2_VIEWSTATE_FIELD_DEFAULT_ACTION][sVFormID];
            }
        }
        
        // Register the Key press event
        Event_AddAction(EVENT_KEY_PRESS, Matrix2_Event_OnKeyPress);
    },
    
    /**
     * Function called when there is an event on an Ajax Control (like a button)
     * @param _asBOs Array of BOs name
     * @return Returns true if the operation has succeeded, else false
     */
    update: function(_asBOs, _sEventName, _sEventArgument)
    {
        // Default return value
        var bRet = false;

        if(!Matrix2.gbProcessing)
        {
            // Calls the update event
            Event_Execute(MATRIX2_EVENT_UPDATE);
            
            // Valid input
            if ((_asBOs !== null) &&
                (_asBOs !== TOOLS_UNDEFINED_VALUE) &&
                (typeof _asBOs == 'object'))
            {
                // Get the list of VForm associated to the BO List
                var asVForms = this._getVFormList(_asBOs);
                
                // Now, get the controls associated to these VForms
                var oControlFields = this._getControlsValues(asVForms, _sEventName, _sEventArgument);
                
                // Get the BO List linked to these VForms
                var asBOs = this._getBOList(asVForms);
                
                // Get the View State associated to the BOs
                var oViewState = this._getViewState(asBOs, asVForms);
                
	            // BUY (For the BUY AND DOWNLOAD FEATURE)
                // Is it a BUY on the OrderVForm ?
                if((_sEventName != null)
                && (_sEventName != ""))
                {
                    var iBuy = _sEventName.indexOf(MATRIX2_BUY_BUTTON);
                    
                    // Found ?
                    if(iBuy > 0)
                    {
                        // Stores the VForm Name that has requested the BUY
                        Matrix2.gsBuyAndDownloadVForm = _sEventName.substr(0, iBuy);
                    }
                    else
                    {
                        Matrix2.gsBuyAndDownloadVForm = null;
                    }
                }
    
                // No, execute the request            		
                bRet = this._sendRequest(oControlFields, oViewState);
            }
        }
        
        // returns Success status
        return bRet;
    },
    
    /**
     * Returns the head element of the HTML document
     * @return Returns the HEAD element if found, else returns null
     */
    _getHeadElement: function()
    {
        // Default return value
        var oRet = null;
        
        // Get the document element
        var oHTMLElement = document.documentElement;
        
        // Valid ?
        if((oHTMLElement != null)
        && (oHTMLElement.childNodes != null))
        {
            // Loop on childs to get the head
            var iLength = oHTMLElement.childNodes.length;
            for(var i = 0; (oRet == null) && (i < iLength); i++)
            {
                // Node
                var oNode = oHTMLElement.childNodes[i];
                
                // Head found ?
                if(oNode.nodeName == "HEAD")
                {
                    // returns it
                    oRet = oNode;
                }
            }
        }
        
        // Returns the head element
        return oRet;
    },
    
    /**
     * Returns the body element of the HTML document
     * @return Returns the body element id found, else returns false
     */
    _getBodyElement: function()
    {
        // Default return value
        var oRet = null;
        
        // Get the document element
        var oHTMLElement = document.documentElement;
        
        // Valid ?
        if((oHTMLElement != null)
        && (oHTMLElement.childNodes != null))
        {
            // Loop on childs to get the head
            var iLength = oHTMLElement.childNodes.length;
            for(var i = 0; (oRet == null) && (i < iLength); i++)
            {
                // Node
                var oNode = oHTMLElement.childNodes[i];
                
                // Head found ?
                if(oNode.nodeName == "BODY")
                {
                    // returns it
                    oRet = oNode;
                }
            }
        }
        
        // Returns the head element
        return oRet;
    },
    
    /**
     * Returns true if the CSS file is included
     * in the document
     * @param _sFileName File name to check
     * @return true if included, else false
     */
    _isCSSIncluded: function(_sFileName)
    {
        // Default return value
        var bRet = false;
        
        // Valid parameters ?
        if(_sFileName != null)
        {
            // Get Head
            var oHead = this._getHeadElement();
            
            // Valid ?
            if(oHead != null)
            {
                // get the list of CSS nodes
                var iLength = oHead.childNodes.length;
                for(var i = 0; (!bRet) && (i < iLength); i++)
                {
                    // Get node
                    var oChildNode = oHead.childNodes[i];
                    
                    // Valid ?
                    if((oChildNode != null)
                    && (oChildNode.nodeType == TOOLS_NODE_ELEMENT_NODE)
                    && (oChildNode.nodeName == "LINK")
                    && (oChildNode.getAttribute("HREF") == _sFileName))
                    {
                        bRet = true;                        
                    }
                }
            }
        }
        
        // Returns true if found
        return bRet;
    },
    
    /**
     * Returns true if the CSS file is included
     * in the document
     * @param _sFileName File name to check
     * @return true if included, else false
     */
    _isJSIncluded: function(_sFileName)
    {
        // Default return value
        var bRet = false;
        
        // Valid parameters ?
        if(_sFileName != null)
        {
            // Get Head
            var oHead = this._getHeadElement();
            
            // Valid ?
            if(oHead != null)
            {
                // get the list of CSS nodes
                var iLength = oHead.childNodes.length;
                for(var i = 0; (!bRet) && (i < iLength); i++)
                {
                    // Get node
                    var oChildNode = oHead.childNodes[i];
                    
                    // Valid ?
                    if((oChildNode != null)
                    && (oChildNode.nodeType == TOOLS_NODE_ELEMENT_NODE)
                    && (oChildNode.nodeName == "SCRIPT")
                    && (oChildNode.getAttribute("SRC") == _sFileName))
                    {
                        bRet = true;                        
                    }
                }
            }
        }
        
        // Returns true if found
        return bRet;
    },
    
    /**
     * Adds a new file to the document (CSS, JS, ...)
     * @param[in] _sFileName File Name to link
     */
    _addLinkedFile: function(_sFileName)
    {
        // Valid input ?
        if(_sFileName != null)
        {
            // Get head
            var oHeadElement = this._getHeadElement();
            
            // Valid ?
            if(oHeadElement != null)
            {
                // Check the file type.
                var sFile = _sFileName.toUpperCase();
                
                // Checks extension
                var iDotIndex = sFile.lastIndexOf('.');
                
                // Found ?
                if((iDotIndex >= 0)
                && (iDotIndex < sFile.length))
                {
                    var oLinkNode = null;
                    
                    // JS ?
                    if(sFile.substring(iDotIndex + 1) == "JS")
                    {
                        // Not already included ?
                        if(!this._isJSIncluded(_sFileName))
                        {
                            // Inserts JAVASCRIPT dynamically
                            oLinkNode = document.createElement("SCRIPT");
                            oLinkNode.type  = "text/javascript";
                            oLinkNode.src   = _sFileName;
                        }
                    }
                    else if(sFile.substring(iDotIndex + 1) == "CSS")
                    {
                        // Not already included ?
                        if(!this._isCSSIncluded(_sFileName))
                        {
                            // Inserts CSS dynamically
                            oLinkNode = document.createElement("LINK");
                            oLinkNode.rel   = "stylesheet";
                            oLinkNode.href  = _sFileName;
                        }
                    }
                    
                    // Valid link ?
                    if(oLinkNode != null)
                    {
                        // Attach it
                        oHeadElement.appendChild(oLinkNode);
                        
                        // Warning message about the dynamic file inclusion (CSS or JS)
                        Matrix2.gsDebugString += '<div style="color: red; font-size: 14px; font-weight: bold;">' + _sFileName + ' Has been inserted dynamically. It will reduce performances !</div>';
                        
                    }
                }
            }
        }
    },
    
    /**
     * Get the VForm list (associated to the BO list)
     * @param _asBOs List of BOs
     * @return The list of VForms associated to the BO list, null if a problem occurs
     */
    _getVFormList: function(_asBOs)
    {
        // Default return value
        var oRet = null;
        
        // Valid inputs ?
        if ((_asBOs != null) &&
            (_asBOs != TOOLS_UNDEFINED_VALUE) &&
            (typeof _asBOs == 'object') &&
            (Matrix2.goViewState != null) &&
            (Matrix2.goViewState != TOOLS_UNDEFINED_VALUE))
        {
            // Creates the empty VForm list
            oRet = new Object();
            
            // Traverse the list of BOs and retrieve the VForms
            
            // 1 - Get the VForm Array
            var aoVFormArray = Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE];
            
            // Valid VForm Array ?
            if((aoVFormArray != null)
            && (aoVFormArray != TOOLS_UNDEFINED_VALUE))
            {
                // Traverse keys
                for(var sVFormName in aoVFormArray)
                {
                    // Not already added?
                    if(Tools_ContainsValue(oRet, sVFormName) == false)
                    {
                        var asBOs = aoVFormArray[sVFormName];

                        // Valid array ?
                        if((asBOs != null)
                        && (asBOs != TOOLS_UNDEFINED_VALUE))
                        {
                            // Traverse the array
                            for(var sBOKey in asBOs)
                            {
                                // The BO Name is present in the update list, and the
                                // associated VForm was not already added to the VForm
                                // List ?        	            
                                if(Tools_ContainsValue(_asBOs, asBOs[sBOKey]) != false)
                                {
                                   // Store the VForm Name
                                   oRet[sVFormName] = sVFormName;
                                   
                                   break;
                                }
                            }        	        
                        }
                    }
                }
            }
        }
        
        // Returns VForms List
        return oRet;
    },
    
    /**
     * Retrieve a Hash of {{Ctrl1Name : Ctrl1Value}, {Ctrl2Name : Ctrl2Value}}
     * @param _asVForms List of VForm to use
     * @return Returns a Hash of Control keys / Values indexed by the VForm Name
     */
    _getControlsValues: function(_asVForms, _sEventName, _sEventArgument)
    {
        // Default Return value
        var oRet = null;
        
        // Valid Inputs ?
        if((_asVForms != null)
        && (_asVForms != TOOLS_UNDEFINED_VALUE)
        && (typeof _asVForms == 'object'))
        {
            // Create the Hash
            oRet = new Object();
            
            // Add event name and event arguments
            oRet[MATRIX2_EVENTTARGET_ID]    = _sEventName;
            oRet[MATRIX2_EVENTARGUMENTS_ID] = _sEventArgument;
            
            // Creates an entry for each VForm if it exist in the document
            for(var sVFormName  in _asVForms)
            {
                // Not a function?
                if (typeof sVFormName == 'string')
                {
                    // Get the Element
                    var oVFormElement = document.getElementById(sVFormName);
                    
                    // VForm found ?
                    if(oVFormElement != null)
                    {
                        // Get all controls
                        var oFormNode = document.getElementById(sVFormName);
                        this._getInputs(sVFormName, sVFormName + TOOLS_CONTROL_ID_SEPARATOR, oFormNode, oRet);
                    }
                }
            }
        }
        
        // Returns fields
        return oRet;
    },
    
    /**
     * Returns true if the node is an input control
     * @param _oNode Node of the control
     * @return Returns true if the node is an input control
     */
    _isInputControl: function(_oNode)
    {
        // Default return value
        var bRet = false;
        
        // Valid ?
        if(_oNode != null)
        {
            bRet = ((_oNode.nodeName == 'INPUT')
                 || (_oNode.nodeName == 'TEXTAREA')
                 || (_oNode.nodeName == 'SELECT'));
        }

        
        // Returns true if node is a control
        return bRet;
    },
    
    /**
     * Returns true if the value is valid.
     * @param _oNode Node of the control
     * @param _InOutID ID of the control (in / out)
     * @param _OutValue Returned value
     * @return Returns true if the out value is valid
     */
    _getInputValue: function(_oNode, _oOutput)
    {
       // Default return value
       var bRet = false;

       // Valid params ?
       if(_oNode != null)
       {
           // Get type
           var sType = _oNode.type;
           
           // Checkbox ?
           if(_oNode.type == "checkbox")
           {
               // Checked?
               if(_oNode.checked != false)
               {
                   _oOutput['VALUE'] = "true";
               }
               else
               {
                   _oOutput['VALUE'] = "false";
               }
               
               // Valid control
               bRet = true;
           }
           else if(_oNode.type == "radio")
           {
               // Checked ?
               if(_oNode.checked)
               {
                   // Out ID is the name
                   _oOutput['ID']       = _oNode.getAttribute("name");
                   _oOutput['VALUE']    = _oNode.value;
                   bRet = true;
               }
           }
           else
           {
               _oOutput['VALUE'] = _oNode.value;
               bRet = true;
           }
       }
       
       // Returns validity
       return bRet;
    },
    
    /**
     * @param {Object} _sVFormName
     */
    _getInputs : function(_sVFormName, _sControlName,  _oRootNode, _oControlsValues)
    {
        // Valid Root Node?
        if((_oRootNode != null)
        && (_oRootNode.nodeType == TOOLS_NODE_ELEMENT_NODE))
        {
            // Gets ID
            var sID = _oRootNode.getAttribute("id");
            
            // Same VForm or no ID?
            if((sID == null)
            || (sID == "")
            || (sID == _sVFormName)
            || (sID == _sVFormName + MATRIX2_COVER_SUFFIX)
            || (sID == _sVFormName + MATRIX2_POPUP_SUFFIX)
            || (sID.substr(0, _sControlName.length) == _sControlName))
            {
                // Is it a input control
                if((_oRootNode.nodeName == 'INPUT')
                || (_oRootNode.nodeName == 'TEXTAREA')
                || (_oRootNode.nodeName == 'SELECT'))
                {
                   // Gets value
                   var oOutValue = new Array();
                   oOutValue['ID']      = sID;
                   oOutValue['VALUE']   = null;
                   if(this._getInputValue(_oRootNode, oOutValue))
                   {
                       if(oOutValue != null)
                       {
                           var sID      = oOutValue['ID'];
                           var sValue   = oOutValue['VALUE'];
                           
                           // Valid ?
                           if((sID != null)
                           && (sValue != null))
                           {
                               _oControlsValues[sID] = sValue;
                           }
                       }
                   }
                }
                else
                {
                    var oChildList;
                    
                    // Gets child list
                    oChildList = _oRootNode.childNodes;

                    // For all children
                    for(var i = 0; i < oChildList.length; i++)
                    {
                        // Gets node inputs
                        this._getInputs(_sVFormName, _sControlName, oChildList[i], _oControlsValues);
                    }
                }
            }
        }
    },
    
    /**
     * Retrieve the List of BO (as an array), that are used by VForms
     * @param _asVForms Array of VForm name
     * @return Returns an Array of BO Name linked to _asVForms
     */
    _getBOList: function(_asVForms)
    {
        // Default return value
        var asRet = null;
        
        // Valid input ?
        if((_asVForms != null)
        && (_asVForms != TOOLS_UNDEFINED_VALUE)
        && (typeof _asVForms == 'object')
        && (Matrix2.goViewState != null)
        && (Matrix2.goViewState != TOOLS_UNDEFINED_VALUE))
        {
            // Creates the Result array
            asRet = new Object();
            
            // Set the BO List
            var oBOList = Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE];
            
            // Traverse the VForm list
            for(var sVFormKey in _asVForms)
            {
                // Gets VForm name
                var sVFormName = _asVForms[sVFormKey];

                // Is this VForm present in the View State ?
                if(Tools_ContainsKey(oBOList, sVFormName) != false)
                {
                    // Get the Array
                    var asBOs = oBOList[sVFormName];
                    
                    // Valid array ?
                    if(asBOs != null)
                    {
                        // Traverse this array and if BO Name are not already present in the array
                        // to return, add them.
                        for(var sBOKey in asBOs)
                        {
                            // Gets BO Name
                            var sBOName = asBOs[sBOKey];
                            
                            // Not already in the array ?
                            if(Tools_ContainsValue(asRet, sBOName) == false)
                            {
                                asRet[sBOName] = sBOName;
                            }
                        }
                    }
                }
            }
        }
        
        // Returns the BO Array
        return asRet;
    },
    
    /**
     * Retrieve the viewstate from the BO List
     * @param _asBOs BO Name Array
     * @return Return a Hash with list of Viewstate for the BOS
     */
    _getViewState: function(_asBOs, _asVForms)
    {
        // Default return value
        var oRet          = null;
        var oBOStateList  = null;
        var oVFormBOTable = null;

        // Valid input ?
        if((Matrix2.goViewState != null)
        && (Matrix2.goViewState != TOOLS_UNDEFINED_VALUE))
        {
            // Creates the arrays
            oRet          = new Object();
            oBOStateList  = new Object();
            oVFormBOTable = new Object();

            // BO list valid?
            if((_asBOs != null)
            && (_asBOs != TOOLS_UNDEFINED_VALUE)
            && (typeof _asBOs == 'object'))
            {
                // Traverse the BO Array
                for(var sBOKey in _asBOs)
                {
                    // Get BO Name
                    var sBOName = _asBOs[sBOKey];
                    
                    // Get the BO State
                    var sBOState = Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_BO_STATE_LIST][sBOName];
                    
                    // Valid BO State ?
                    if((sBOState != null)
                    && (sBOState != TOOLS_UNDEFINED_VALUE))
                    {
                        oBOStateList[sBOName] = sBOState;
                    }
                }
            }

            // VForm list valid?
            if((_asVForms != null)
            && (_asVForms != TOOLS_UNDEFINED_VALUE)
            && (typeof _asVForms == 'object'))
            {
                // Traverse the VForm Array
                for(var sVFormKey in _asVForms)
                {
                    // Get BO Name
                    var sVFormName = _asVForms[sVFormKey];
                    
                    // Get VForm-BO table
                    var oVFormBOList = Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE][sVFormName];
                    
                    // VForm-BO table valid?
                    if((oVFormBOList != null)
                    && (oVFormBOList != TOOLS_UNDEFINED_VALUE))
                    {
                        oVFormBOTable[sVFormName] = oVFormBOList;
                    }
                }
            }
            
            // Updates return value
            oRet[MATRIX2_VIEWSTATE_FIELD_BO_STATE_LIST]  = oBOStateList;
            oRet[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE] = oVFormBOTable;
            oRet[MATRIX2_VIEWSTATE_FIELD_URL]            = Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_URL];
        }
        
        // Return the Hash for the BOs State
        return oRet;
    },
    
    /**
     * TODO : ADD TDD For this function
     * Returns the Document URL : At the moment, use the document.location =>
     * Replace it with 
     */
    _getCurrentURL: function()
    {
        // Get URL From View state
        var sRet = Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_URL][0];
        
        // Now, remove parameters if they exists
        var iEnd = sRet.indexOf('?');
        
        // Not Found ?
        if(iEnd < 0)
        {
            iEnd = sRet.length;
        }

        sRet = sRet.substring(0, iEnd);
        
        // Returns current URL
        return sRet;
    },
    
    /**
     * Retrieve the Page Name from the URL (VP=SimpleVPage for example)
     */
     _getPageName: function()
     {
        // Get URL From View state
        var sRet = Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_URL][0];
        
        // Get position
        var iStart = sRet.indexOf('VP=');
        var iEnd = sRet.indexOf('&');
        
        if (iEnd < 0)
        {
            iEnd = sRet.length;
        }
        
        sRet = sRet.substring(iStart, iEnd);
        
        // Returns current URL
        return sRet;
     },
    
    /**
     * TODO : ADD TDD For this function
     */
    _getPostBody: function(_oControlFields, _oViewState)
    {
        // Default return value
        var oRet = null;
        
        // Valid inputs ?
        if((_oControlFields != null)
        && (_oControlFields != TOOLS_UNDEFINED_VALUE)
        && (typeof _oControlFields == 'object')
        && (_oViewState != null)
        && (_oViewState != TOOLS_UNDEFINED_VALUE)
        && (typeof _oViewState == 'object'))
        {
            // ViewState
            _oControlFields[MATRIX2_VIEWSTATE_ID] = json.serialize(_oViewState);
            _oControlFields[MATRIX2_AJAX_FIELD]   = "1";
            _oControlFields[MATRIX2_MATRIX_VERSION_FIELD] = giMatrixVersion;

            // Stores it
            oRet = _oControlFields;
        }

        // Return Post Body
        return oRet;
    },
    
    _addLoading: function()
    {
        // Get the body content ID
        var oBody = document.getElementById(MATRIX2_BODY_ID);
        
        // Valid ?
        if(oBody != null)
        {
            // Set visibility
            if(Matrix2.goCoverElement != null)
            {
                Matrix2.goCoverElement.style.visibility   = "visible";
            }

            if(Matrix2.goLoadingElement != null)
            {
                Matrix2.goLoadingElement.style.visibility   = "visible";
            }
            
            // Change cursor
//            document.documentElement.style.cursor = "wait";
            
            // resize loading (add a delay for IE threading with alpha component)
            window.setTimeout(Matrix2_Event_onResizeLoading, 1);
            Matrix2_Event_onUpdateLoading();
        }
    },
    
    _removeLoading: function()
    {
        // Hide elements

        // Change cursor
//        document.documentElement.style.cursor = "default";
        
        // Set visibility
        if(Matrix2.goCoverElement != null)
        {
            Matrix2.goCoverElement.style.width  = 0;
            Matrix2.goCoverElement.style.height = 0;
            Matrix2.goCoverElement.style.visibility = "hidden";
        }
        
        if(Matrix2.goLoadingElement != null)
        {
            Matrix2.goLoadingElement.style.visibility   = "hidden";
        }
    },
    
    /**
     * Send an AJAX request with a POST method on the Server
     * (Same URL as the current page). POST the Control Fields and BO State.
     * @param _oControlFields Hash of Control Values for each VForm
     * @param _oViewState BO State Hash
     * @return Returns true if the operation succeed, else return false
     */
    _sendRequest: function(_oControlFields, _oViewState)
    {
        // Default return value
        var bRet = false;

        // Valid input ?
        if((_oControlFields != null)
        && (_oControlFields != TOOLS_UNDEFINED_VALUE)
        && (typeof _oControlFields == 'object')
        && (_oViewState != null)
        && (_oViewState != TOOLS_UNDEFINED_VALUE)
        && (typeof _oViewState == 'object'))
        {
            // Get the current URL
            // For the POST Method
            var sUrl = this._getCurrentURL();
            var oPostBody = this._getPostBody(_oControlFields, _oViewState);
            
            // Valid Post Body and URL ?
            if((oPostBody != null)
            && (sUrl != null))
            {
                Matrix2.giUploadSize = 0;
                                               
//                window.setTimeout("new Matrix2()._httpRequest()", 1);
//                new Matrix2()._httpRequest();
                // Send event
                Event_Execute(MATRIX2_EVENT_BEFORE_REQUEST);
                
                // Do an Ajax request
                
                /*
                var bSync = false;
                
                // Download button ?
                if(Matrix2.gsBuyAndDownloadVForm != null)
                {
                  // Safari ?
                  if(Tools_IsSafari)
                  {
                    bSync = false;
                  }
                  else
                  {
                    bSync = true;
                  }
                }
                goRequestArgs = {url:      sUrl,
                                 method:   'POST',
                                 content:  oPostBody,
                                 sync:     bSync,
                                 mimetype: "text/html",
                                 load:     this._AjaxSuccess,
                                 error:    this._AjaxFailure
                                };
                                
                // Try to use the XML HTTP transport
                //var oRequest = dojo.io.bind(goRequestArgs);
                  if((oRequest != null)
                && (oRequest.bindSuccess)
                && (!bSync))
                {
                    // Adds a loading message
                    this._addLoading();
                }
                 */
                
                try {
                    // Store the upload size
                    var sViewState = "";
                    for(var sKey in oPostBody)
                    {
                        sViewState += sKey + "=" + encodeURIComponent(oPostBody[sKey]) + "&";
                    }
                    //Matrix2.giUploadSize = sViewState.length;
                    sViewState = sViewState.substr(0, sViewState.length - 1);
                    
                    this._addLoading();
                    
                    YAHOO.util.Connect.asyncRequest('POST', sUrl, {success:this._AjaxSuccess, failure:this._AjaxFailure}, sViewState);
                } catch(Exception) {
                    this._removeLoading();
                    alert('ajax error : replace lib/dojo.js include by following includes : \n yui/build/yahoo/yahoo-min.js \n yui/build/event/event-min.js \n yui/build/connection/connection-min.js');                    
                }                
            }

            // Success
            bRet = true;
        }

        // Return Status
        return bRet;
    },
    
    _httpRequest: function()
    {
        // Send event
        Event_Execute(MATRIX2_EVENT_BEFORE_REQUEST);
        
        // Do an Ajax request
        
        // Try to use the XML HTTP transport
        var oRequest = dojo.io.bind(goRequestArgs);
        
        // Valid bind               
        if((oRequest != null)
        && (oRequest.bindSuccess))
        {
            // Adds a loading message
            this._addLoading();
        }
    },
    
    /**
     * Save the list of VForm contents which appears inside a VForm (_sID)
     */
    _getSavedVForm: function(_sID, _oCurrentNode, _oSavedVForm)
    {
        // Default return value 
        var oRet = _oSavedVForm;
      
        // Valid input ?
        if((_sID != null)
        && (_sID != "")
        && (typeof _sID == 'string')
        && (_oCurrentNode != null)
        && (_oSavedVForm != null))
        {
            var bContinue = true;
            
            if (_oCurrentNode.nodeType == TOOLS_NODE_ELEMENT_NODE)
            {
                var sNodeID = _oCurrentNode.getAttribute("id");

                if((sNodeID != null)
                && (sNodeID != _sID)
                && (sNodeID != "")
                && (sNodeID.indexOf(TOOLS_CONTROL_ID_SEPARATOR) < 0)
                && (sNodeID.indexOf(MATRIX2_COVER_SUFFIX) < 0)
                && (sNodeID.indexOf(MATRIX2_POPUP_SUFFIX) <0))
                {
                    oRet[sNodeID] = new Object();
                    oRet[sNodeID]["Value"]    = _oCurrentNode;
                    oRet[sNodeID]["ParentID"] = _oCurrentNode.parentNode.getAttribute("id");

                    // Stops at this level
                    bContinue = false;
                }
            }

            // Gets deeper?
            if(bContinue != false)
            {
                // Gets child list
                var oChildList = _oCurrentNode.childNodes;

                // Valid?
                if(oChildList != null)
                {
                    // For all children
                    for(var i = 0; i < oChildList.length; i++)
                    {
                        // Gets child node
                        var oChild = oChildList[i];

                        // Recurses                         
                        this._getSavedVForm(_sID, oChild, oRet);
                    }
                }
            }
        }
    },

    _getPlaceHolder: function(_sID, _oParentNode, _oNewParentNode)
    {
        var oResult = null;

        // Gets parent child nodes
        var oChildNodes = _oParentNode.childNodes;

        // For all children        
        for(var i = 0; i < oChildNodes.length; i++)
        {
            // Gets child
            var oChild   = oChildNodes[i];

            // Is an element node?
            if(oChild.nodeType == TOOLS_NODE_ELEMENT_NODE)
            {
                var sChildID = oChild.getAttribute("id"); 

                // Found?
                if(sChildID == _sID)
                {
                    oResult             = oChild;
                    _oNewParentNode[0]  = oChild.parentNode;
    
                    break;
                }
//                else if(sChildID == _sParentID)
//                {
//                    _oNewParentNode[0] = oChild;
//                }
                
                // Recurse
                var oTempResult = this._getPlaceHolder(_sID, oChild, _oNewParentNode);
                if(oTempResult != null)
                {
                    oResult = oTempResult;
                }
            }
        }

        // Done!
        return oResult;
    },
    
    
    /**
     * Updates a popup
     * @param {Object} _oNode
     */
    _updatePopup : function(_oNode, _sVisibility)
    {
        // Gets ID
        var sID = _oNode.getAttribute("id");
        
        // Compute the z-index position
        var sStyleZIndex = Tools_IndexOf(Matrix2.gasPopupList, sID) * 100;
        
        // Insert 2 nodes in the VForm : the _cover node and the _popup node
        var oCoverNode = document.createElement("DIV");
        var oPopupNode = document.createElement("DIV");
        
        // Hides the popup and cover element

        if(_sVisibility != TOOLS_UNDEFINED_VALUE)
        {
            oCoverNode.style.display = "none";
            oPopupNode.style.display = "none";
        }

        // Sets new id
        oCoverNode.setAttribute("id", sID + MATRIX2_COVER_SUFFIX);
        oPopupNode.setAttribute("id", sID + MATRIX2_POPUP_SUFFIX);
        
        // move all the childs of the VForm in the _popup node
        while(_oNode.childNodes.length > 0)
        {
            var oChild = _oNode.childNodes[0];
            _oNode.removeChild(oChild)
            oPopupNode.appendChild(oChild);
        }
        
        // Set the style
        oCoverNode.style.zIndex = (Matrix2.gasPopupList.length * 100);
        oPopupNode.style.zIndex = (Matrix2.gasPopupList.length * 100 + 1);

        if(_sVisibility != TOOLS_UNDEFINED_VALUE)
        {
            // Hide the rendering process (compute width)
            oCoverNode.style.visibility = _sVisibility;
            oPopupNode.style.visibility = _sVisibility;
            
            // Use block to compute space
            oCoverNode.style.display = "block";
            oPopupNode.style.display = "block";
            _oNode.style.display     = "block";
        }

        // Now, add the two new nodes under the VForm
        _oNode.appendChild(oCoverNode);
        _oNode.appendChild(oPopupNode);
        
        // Set the default class name (taken from the global css file)
        oCoverNode.className = MATRIX2_GLOBAL_COVER_CLASS;
        oPopupNode.className = MATRIX2_GLOBAL_POPUP_CLASS;
       _oNode.className = MATRIX2_GLOBAL_POPUP_CLASS;
    },
        
    /**
     * Set a VForm as Popup
     */
    _addPopup : function(_oNode)
    {
        // Valid ?
        if(_oNode != null)
        {
            // Gets ID
            var sID = _oNode.getAttribute("id");
            
            // Adds the id to the popup list
            Matrix2.gasPopupList.push(sID);

            // Updates it
            this._updatePopup(_oNode, "hidden");
            
            // Request centering
            Matrix2.gbCenterRequest = true;
        }
    },
    
    _removePopup: function(_oNode)
    {
        // Valid ?
        if(_oNode != null)
        {
            // Get ID
            var sID = _oNode.getAttribute("id");
            
            // Valid ?
            if(sID != null)
            {
                var oNewPopupList = new Array();
                
                // loop the popup list
                for(var i=0; i<Matrix2.gasPopupList.length; i++)
                {
                    // Not the popup to remove ?
                    if(Matrix2.gasPopupList[i] != sID)
                    {
                        // Copy it in the new array
                        oNewPopupList.push(Matrix2.gasPopupList[i]);
                    }
                }
                
                // Update the list
                Matrix2.gasPopupList = oNewPopupList;
            }

            // Request centering
            Matrix2.gbCenterRequest = true;
        }
    },

    /**
     * Insert the static VForm in the Page (All VForms that have a PlaceHolderPanel)
     */
    _renderStaticVForm: function(_oXMLResponse)
    {
        // Remove the loading message
        this._removeLoading();

        // Valid Response ?
        if (_oXMLResponse != null)
        {
            // Get the Node list
            var aoNodeList = _oXMLResponse.childNodes;
            
            // Valid node list ?
            if(aoNodeList != null)
            {
                var oNewViewState = null;
                var oNodeTable    = new Object();
                var oActionList;
                var oVFormBOTable;
                
                // Hash table of Added VForm (VForm ID as key, DOM Array index as value)
                var oAddTable = new Array();

                // Traverse the node list and get their id
                for(var i = 0; i < aoNodeList.length; i++)
                {
                    // Get node
                    var oNode = aoNodeList[i];

                    // Element ?
                    if((oNode != null) && (oNode.nodeType == TOOLS_NODE_ELEMENT_NODE))
                    {
                        var sID = oNode.getAttribute("id");
                        
                        // Valid ID ?
                        if(sID != null)
                        {
                            // View State ?
                            if(sID == MATRIX2_VIEWSTATE_ID)
                            {
                                // Gets viewstate
                                oNewViewState = oNode.getAttribute("value");
                                oNewViewState = eval('(' + oNewViewState + ')');

                                // Gets action list
                                oActionList = oNewViewState[MATRIX2_VIEWSTATE_FIELD_ACTION_LIST];

                                // Gets BO state list
                                var oBOStateList = oNewViewState[MATRIX2_VIEWSTATE_FIELD_BO_STATE_LIST];

                                // Updates it
                                for(var sKey in oBOStateList)
                                {
                                    // Gets BO value
                                    var sValue = oBOStateList[sKey];

                                    // Copy
                                    Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_BO_STATE_LIST][sKey] = sValue;
                                }

                                // Gets VForm-BO table
                                oVFormBOTable = oNewViewState[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE];
                                
                                // Gets refresh table list
                                var oRefreshTable = oNewViewState[MATRIX2_VIEWSTATE_FIELD_REFRESH_TABLE];
                                
                                // Copy by loop
                                for(var sControlKey in oRefreshTable)
                                {
                                    // Not already in the list ?
                                    if(!Tools_ContainsKey(Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_REFRESH_TABLE], sControlKey))
                                    {
                                        // Adds Refresh Event for the new timer

                                        // Gets value
                                        var iPeriod = parseInt(oRefreshTable[sControlKey]);
                                        
                                        // Valid number ?
                                        if(!isNaN(iPeriod))
                                        {
                                            // Gets VForm and control ID
                                            var iSplitterIdx = sControlKey.indexOf(TOOLS_CONTROL_ID_SEPARATOR);
                                            
                                            // Valid ?
                                            if((iSplitterIdx > 0)
                                            && (iSplitterIdx < sControlKey.length - 1))
                                            {
                                                // Gets VForm ID
                                                var sVFormID = sControlKey.substr(0, iSplitterIdx);
                                                var sControlID = sControlKey.substr(iSplitterIdx + 1);
                                                
                                                // Calls the refresh function
                                                Matrix2_Event_OnRefresh(sVFormID, sControlID, iPeriod);
                                            }
                                        }
                                    }

                                    // Copy
                                    Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_REFRESH_TABLE][sControlKey] = oRefreshTable[sControlKey]
                                }
                                
                                // Gts dropable list
                                var asDropableList = oNewViewState[MATRIX2_VIEWSTATE_FIELD_DROPABLE_LIST];
                                
                                if((asDropableList != null)
                                && (asDropableList != TOOLS_UNDEFINED_VALUE))
                                {
                                    // update the dropable list
                                    for(var iDropIdx=0; iDropIdx<asDropableList.length; iDropIdx++)
                                    {
                                        DragDrop_AddTarget(asDropableList[iDropIdx]);
                                    }
                                }
                            }
                            else
                            {
                                // Adds it to node table
                                oNodeTable[sID] = oNode;
                            }
                        }
                    }
                }
                
                // Viewstate found?
                if(oNewViewState != null)
                {
                    for(var sID in oActionList)
                    {
                        // Gets VForm Table
                        var sVFormTable = oVFormBOTable[sID];

                        // Gets action
                        var oAction = oActionList[sID];
                        
                        switch(oAction[MATRIX2_ACTION_NODE_ACTION])
                        {
                            // Redirect?
                            case MATRIX2_ACTION_TYPE_REDIRECT:

                                document.location = sID;
                                
                                return;

                            // Add?
                            case MATRIX2_ACTION_TYPE_MODIFY:

                                // Adds it
                                Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE][sID] = sVFormTable;

                                // Gets element from page
                                var oPageElement = document.getElementById(sID);
                                
                                // Does it exists?
                                if(oPageElement != null)
                                {
                                    // Gets new node
                                    oNode = oNodeTable[sID];

                                    // Found?
                                    if(oNode)
                                    {
                                        var oSavedVForm = new Object();
                                        
                                        // Keeps embedded VForms
                                        this._getSavedVForm(sID, oPageElement, oSavedVForm);

                                        // Was popup?
                                        if(Tools_IndexOf(Matrix2.gasPopupList, sID) >= 0)
                                        {
                                            // Get the popup window node
                                            var oPopupWindow = document.getElementById(sID + MATRIX2_POPUP_SUFFIX);
                                            
                                            // Valid ?
                                            if(oPopupWindow != null)
                                            {
                                                // Remove all sub elements
                                                while(oPopupWindow.childNodes.length > 0)
                                                {
                                                    oPopupWindow.removeChild(oPopupWindow.childNodes[0]);
                                                }
                                                
                                                // Insert new nodes
                                                for(var i = 0; i < oNode.childNodes.length; i++)
                                                {
                                                    // Only replace with element node
                                                    if(oNode.childNodes[i].nodeType == TOOLS_NODE_ELEMENT_NODE)
                                                    {
                                                        oPopupWindow.appendChild(oNode.childNodes[i]);
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            // Replaces node
                                            Tools_ReplaceNode(oPageElement.parentNode, oNode, oPageElement)
                                        }

                                        // Traverses the saved form keys
                                        for(var sVFormName in oSavedVForm)
                                        {
                                            // Is it possible to insert it in the Node childs ?
                                            var oInsertNode = document.getElementById(oSavedVForm[sVFormName]["ParentID"]);

                                           // Found ?
                                            if(oInsertNode != null)
                                            {
                                                var oOldNode;

                                                // Gets old node
                                                oOldNode = document.getElementById(sVFormName);
                                                
                                                // Get new node
                                                var oNewNode = oSavedVForm[sVFormName]["Value"];

                                                // Already exist?
                                                if(oOldNode)
                                                {
                                                    // Replaces it
//                                                    Tools_ReplaceNode(oInsertNode, oNewNode, oOldNode);
                                                    Tools_ReplaceNode(oOldNode.parentNode, oNewNode, oOldNode);
                                                }
                                                else
                                                {
                                                    // Adds it
                                                    oInsertNode.appendChild(oNewNode);
                                                }
                                            }
                                        }
                                    }
                                }
                            
                                break;

                            // Add?
                            case MATRIX2_ACTION_TYPE_ADD:

                                // Adds it
                                Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE][sID] = sVFormTable;
                                
                                // Gets Include list
                                var asIncludeList = oAction[MATRIX2_ACTION_NODE_INCLUDE];
                                
                                // Valid ?
                                if((asIncludeList != null)
                                && (asIncludeList.length > 0))
                                {
                                    // loop and add the file to the document
                                    for(var i = 0; i < asIncludeList.length; i++)
                                    {
                                        this._addLinkedFile(asIncludeList[i]);
                                    }
                                }

                                // Gets parent element ID
                                var sParentElementID = oAction[MATRIX2_ACTION_NODE_PARENT];

                                // Valid?
                                if(sParentElementID != null)
                                {
                                    var oPlaceHolder = null;

                                    // Gets parent element
                                    var oParentElement = document.getElementById(sParentElementID);

                                    // Not found?
                                    if(oParentElement == null)
                                    {
                                        var oNewParentNode = new Array();
                                        
                                        // Gets VForm ID
                                        var sVFormID = sParentElementID;
                                        
                                        // Get ID length
                                        var sVFormIDLength = sParentElementID.indexOf(TOOLS_CONTROL_ID_SEPARATOR);
                                        
                                        // Splitter found ?
                                        if(sVFormIDLength > 1)
                                        {
                                            // Gets Parent ID
                                            sVFormID = sVFormID.substr(0, sVFormIDLength);
                                        }
                                        
                                        // Gets parent from new set
                                        oParentElement = oNodeTable[sVFormID];
                                        
                                        // Get Place Holder
                                        oPlaceHolder   = this._getPlaceHolder(sID, oParentElement, oNewParentNode);
                                        
                                        // has new parent?
                                        if(oNewParentNode.length > 0)
                                        {
                                            // Updates parent node
                                            oParentElement = oNewParentNode[0];
                                        }
                                        else
                                        {
                                            // Get panel node
                                            var oPanelNode = this._getPlaceHolder(sParentElementID, oParentElement, oNewParentNode);
                                            
                                            // Valid ?
                                            if(oPanelNode != null)
                                            {
                                                oParentElement = oPanelNode;
                                            }
                                        }
                                    }

                                    // Everything's fine?
                                    if(oParentElement != null)
                                    {    
                                        // Gets new node
                                        oNode = oNodeTable[sID];

                                        // Found?
                                        if(oNode)
                                        {
                                            // Is it a popup ?
                                            if(oAction[MATRIX2_ACTION_NODE_LAYOUT] == MATRIX2_LAYER_TYPE_POPUP)
                                            {
                                                // Hides the node that will be inserted
                                                oNode.style.display = "none";
                                            }
                                            
                                            // Had placeholder?
                                            if(oPlaceHolder != null)
                                            {
                                                Tools_ReplaceNode(oParentElement, oNode, oPlaceHolder);
                                            }
                                            else
                                            {
                                                var oOldNode;
                                                
                                                // Gets old node
                                                oOldNode = document.getElementById(sID);
                                                
                                                // Found?
                                                if(oOldNode)
                                                {
                                                    // Removes old
                                                    oOldNode.parentNode.removeChild(oOldNode);
                                                }

                                                // Get next id ?
                                                var sNextID     = oAction[MATRIX2_ACTION_NODE_NEXT];
                                                var oRefChild   = null;
                                                
                                                // Inserts it ?
                                                if((sNextID != null)
                                                && (sNextID != ''))
                                                {
                                                    // gets ref child
                                                    var oRefChild = document.getElementById(sNextID);
                                                }
                                                
                                                // Valid ref child ?
                                                if(oRefChild != null)
                                                {
                                                    // Yes, inserts it before next ID
                                                    oParentElement.insertBefore(oNode, oRefChild);
                                                }
                                                else
                                                {
                                                    // Appends it at the end of the list
                                                    oParentElement.appendChild(oNode);
                                                }
                                            }

                                            // Is it a popup ?
                                            if(oAction[MATRIX2_ACTION_NODE_LAYOUT] == MATRIX2_LAYER_TYPE_POPUP)
                                            {
                                                // Set the element as popup
                                                this._addPopup(oNode);
                                            }
                                        }
                                    }
                                }

                                break;

                            // Remove?
                            case MATRIX2_ACTION_TYPE_REMOVE:

                                // Removes it
                                Matrix2.goViewState[MATRIX2_VIEWSTATE_FIELD_VFORM_BO_TABLE][sID] = null;
                                
                                // Remove the Dropable elements from the list
                                DragDrop_RemoveAllTarget(sID);

                                // Gets element from page
                                var oPageElement = document.getElementById(sID);

                                // Valid?
                                if(oPageElement != null)
                                {
                                    // Removes it
                                    oPageElement.parentNode.removeChild(oPageElement);
                                    
                                    // Remove the popup
                                    this._removePopup(oPageElement);
                                }

                                break; 
                        }
                    }
                    
                    // A BUY Event was Sent ?
                    if(Matrix2.gsBuyAndDownloadVForm != null)
                    {
                        // Download Button Exists ?
                        var oDownloadButton = document.getElementById(Matrix2.gsBuyAndDownloadVForm + MATRIX2_DOWNLOAD_BUTTON);
                        if(oDownloadButton != null)
                        {
                            __doPostBack(Matrix2.gsBuyAndDownloadVForm + MATRIX2_DOWNLOAD_EVENT, '');
                        }
                    }

                    // Resize popup
                    window.setTimeout(Matrix2_Event_onResizeFirst, 1);
                }
            }
        }
    },
    
    _addDynamicVForm : function(_oAction, _oParentNode, _oNode)
    {
        // Found?
        if(_oNode)
        {
            // Is it a popup ?
            if(_oAction[MATRIX2_ACTION_NODE_LAYOUT] == MATRIX2_LAYER_TYPE_POPUP)
            {
                // Hides the node that will be inserted
                _oNode.style.display = "none";
            }
                    
            // Gets old node
            var oOldNode = document.getElementById(_oNode.id);

            // Found?
            if(oOldNode)
            {
                // Removes old
                oOldNode.parentNode.removeChild(oOldNode);
            }

            // Get next id ?
            var sNextID     = _oAction[MATRIX2_ACTION_NODE_NEXT];
            var oRefChild   = null;
            
            // Inserts it ?
            if((sNextID != null)
            && (sNextID != ''))
            {
                // gets ref child
                var oRefChild = document.getElementById(sNextID);
            }
            
            // Valid ref child ?
            if(oRefChild != null)
            {
                // Yes, inserts it before next ID
                _oParentNode.insertBefore(_oNode, oRefChild);
            }
            else
            {
                // Appends it at the end of the list
                _oParentNode.appendChild(_oNode);
            }
        }

        // Is it a popup ?
        if(_oAction[MATRIX2_ACTION_NODE_LAYOUT] == MATRIX2_LAYER_TYPE_POPUP)
        {
            // Set the element as popup
            this._addPopup(_oNode);
        }
    },
    
    _showDebug: function()
    {
      var iTotal = Matrix2.giTimerGetInput + Matrix2.giTimerTransfert + Matrix2.giTimerRender;
      var sTimer = "<b>JAVASCRIPT TIMER</b><br />"
                 + "<b>Get input</b> = " + Matrix2.giTimerGetInput + " ms <br />" 
                 + "<b>Transfert</b> = " + Matrix2.giTimerTransfert + " ms (UPLOAD : " + Matrix2.giUploadSize + " / DOWNLOAD : " + Matrix2.giDownloadSize + ") <br />"
                 + "<b>Render</b> = " + Matrix2.giTimerRender + " ms <br />"
                 + "<b>Total</b> = " + iTotal + " ms <br />"
                 + "<b>WARNING MESSAGES :<b>" + Matrix2.gsDebugString;
                 
      // Debug VForm ?
      var oDebugNode = document.getElementById(MATRIX2_VFORM_DEBUG);
      
      // Valid ?
      if(oDebugNode != null)
      {
          // Get the Javascript node
          var oJSDebugNode = document.getElementById(MATRIX2_VFORM_DEBUG_CONTROL);
          
          // Not valid ?
          if(oJSDebugNode == null)
          {
              // Creates it
              oJSDebugNode = document.createElement("DIV");
              oJSDebugNode.setAttribute("id", MATRIX2_VFORM_DEBUG_CONTROL);
              oDebugNode.appendChild(oJSDebugNode);
          }
          
          // Set content
          oJSDebugNode.innerHTML = sTimer;
      }
    },

// dojo: _AjaxSuccess: function(sType, oData, _oResponse)
    _AjaxSuccess: function(_oResponse)
    {
        // Raise Event
        Event_Execute(MATRIX2_EVENT_AFTER_REQUEST);

        var sTextResponse = _oResponse.responseText;
        
        // Get the download size
        Matrix2.giDownloadSize = sTextResponse.length; 
        
        // The text has to start with a <input (wich is the view state)
        if(sTextResponse.substr(0, MATRIX2_VALID_MARKER.length).toUpperCase() == MATRIX2_VALID_MARKER)
        {
            // Create a node
            var oXMLResponse = document.createElement("DIV");
            oXMLResponse.innerHTML = sTextResponse;
            
            // Render VForm content
            new Matrix2()._renderStaticVForm(oXMLResponse);
            
            // Safari ?
            if(Tools_IsSafari ||Tools_IsIE)
            {
                // Look for <script...
                var iEndIdx = 0;
                var bEnd = false;
                while(!bEnd)
                {
                    var iScriptStart = sTextResponse.indexOf(MATRIX2_SCRIPT_START, iEndIdx);
                    var iScriptEnd   = -1;
                    
                    // Found ?
                    if(iScriptStart >= 0)
                    {
                        var iScriptEnd = sTextResponse.indexOf(MATRIX2_SCRIPT_END, iEndIdx);
                        
                        // Found ?
                        if(iScriptEnd > iScriptStart)
                        {
                            // Gets the content to exectute
                            var sScript = sTextResponse.substring(iScriptStart + MATRIX2_SCRIPT_START.length, iScriptEnd);
                            
                            // Executes the content
                            eval(sScript);
                            
                            // Stores end idx
                            iEndIdx = iScriptEnd + MATRIX2_SCRIPT_END.length;
                        }
                    }
                    
                    // End ?
                    bEnd = (iScriptStart < 0) || (iScriptEnd < 0)
                }
            }
        }
        else
        {
            new Matrix2()._WriteFailureContent(sTextResponse);
        }
        
        window.setTimeout('new Matrix2()._closeTime()', 1);
        
    },
    
    _closeTime: function()
    {
        // Raise event
        Event_Execute(MATRIX2_EVENT_RENDER_END);

        // Display informations in the page
        new Matrix2()._showDebug();
    },
    
    //dojo : _AjaxFailure: function(sType, oData, _oResponse)
    _AjaxFailure: function(_oResponse)
    {
        // Raise Event
        Event_Execute(MATRIX2_EVENT_AFTER_REQUEST);

        // Write failure content
        new Matrix2()._WriteFailureContent(_oResponse.responseText);
        
        // Raise event
        Event_Execute(MATRIX2_EVENT_RENDER_END);
    },
    
    _WriteFailureContent: function(_sContent)
    {
        document.write(_sContent);
        document.close();
    },

    _AjaxException: function(sType, oError)
    {
        alert('_AjaxException : ' + oRequest.url + ' = ' + oException.name + " : " + oException.message);
    }
};

