// Gaia Ajax Copyright (C) 2008 - 2009 Gaiaware AS. details at http://gaiaware.net/

/* 
 * Gaia Ajax - Ajax Control Library for ASP.NET
 * Copyright (C) 2008 - 2009 Gaiaware AS
 * All rights reserved.
 * This program is distributed under either GPL version 3 
 * as published by the Free Software Foundation or the
 * Gaia Commercial License version 1 as published by
 * Gaiaware AS
 * read the details at http://gaiaware.net
 */
 
/* ------------------------------------------------------------------------------------------------
   Register control helpers
   "Static" methods and fields on class
   ------------------------------------------------------------------------------------------------ */


// Main namespace for ENTIRE library (JS parts)
if( !window.Gaia  )
  Gaia = Class.create();


// Creating the base Control class
Gaia.Control = Class.create();

// This is the array containing e.g. the Gaia Timer and other in-visible controls like that
Gaia.Control._registeredInvisibleControls = new Array();


// This is the list (hash) of controls registered on the page
Gaia.Control._registeredControls = $H({});


// This is a reference to the currently used UpdateControl!
// Note that this only works for the _FIRST_ Ajax request meaning if you don't set 
// it in e.g. the Page_Load method for _EVERY_ call it will only be visible once...
Gaia.Control._updateControl = null;

// Called every time by the Gaia Engine
Gaia.Control.setUpdateControl = function(isCallback, el){
  Gaia.Control._updateControl = el;
  if( el ){
    Element.hide(el);
  }
}
// Shorthand notation for the setUpdateControl
Gaia.SU = Gaia.Control.setUpdateControl;

Gaia.Control.replace = function(id, html){
    var control = $G(id);
    if (control != null)
        control.destroy();
        
    Element.replace(id, html);
}

// Shorthand notation for the Gaia.Control.replace
Gaia.CRP = Gaia.Control.replace;

// Static method responsible for registering a newly created control
Gaia.Control.registerControl = function(control){
  Gaia.Control._registeredControls.set(control.element.id, control);
  return control;
}

Gaia.RC = Gaia.Control.registerControl;

// Retrieves the control with the given ID (basically just the ID of the Element of the control)
$G = function(control){
  return Gaia.Control._registeredControls.get(control);
}


// Retrieves an input field, if it doesn't exists it creates a new hidden field and returns that one
$FC = function(field){
  if($(field))
    return $(field);
  
  var input = new Element('input', {
    'id'  : field,
    'type': 'hidden',
    'name': field
  });
    
  document.getElementsByTagName('form')[0].appendChild(input);
  return input;
}


// Used for incrementally updating the ViewState!
// Takes the new value together with an offset of from where to start "replacing" the current ViewState
$FChange = function(field, unchangedTo, value){
  var el = $FC(field);
  el.value = el.value.substr(0, unchangedTo) + value;
}


// The default URL to postback to when Ajax request initiated, note that this CAN be 
// overridden in widget level...
Gaia.Control._defaultUrl = null;


// This one is true of there's an active request in progress
// Gaia cannot work due to ViewState, EventValidation and similar concepts with
// simultaneous Ajax Requests towars the server and therefor it "queues" up requests 
// so that there will be only _one_ active request at any given time...!
Gaia.Control._activeRequest = false;


// This is the queue of currently pending requests (waiting to be executed while the currently 
// active one is being executed)
Gaia.Control._activeRequests = new Array();


// Called when an Ajax Request finishes
Gaia.Control._onSuccessfulAjaxRequestFinished = function(callback, t){
  // Making update control in-visible (if we have one)
  if( t && t.request && t.request.options && !t.request.options.runGlobalUpdateControl )
    t.request.options.widgetRaisingEvent.tryToHideLocalUpdateControl();
  else
    if( Gaia.Control._updateControl )
      Element.hide(Gaia.Control._updateControl);

  // Calling calback method to call after successful Ajax Request
  if( callback ) {
    try {
      callback(t);    
    } catch (e) {
      // Setting "current active request" to false to enable the NEXT request to execute...
      Gaia.Control._activeRequest = false;
      
      if (Prototype.Browser.IE)
        alert('Server response evaluation failure:\n ' + e.message);
      else
        alert('Server response evaluation failure:\n ' + e.name + '\n ' + e.message);
    }
  } else {
    Gaia.Control._activeRequest = false;
  }
  // Removing the current request OUT of the Request queue
  Gaia.Control._activeRequests.splice(0,1);
}

// For custom Error handling
Gaia.Control.setErrorHandler = function(errorHandler) {
  Gaia.Control.errorHandler = errorHandler;
}

// shorthand for the error handler
Gaia.SE = Gaia.Control.setErrorHandler;

// Debugging global element for showing the error result from the server
Gaia.Control._errorHandlerIFrame = null;


// Called when the error handler IFrame should be closed
Gaia.Control._closeErrorHandler = function(){
  document.body.removeChild(Gaia.Control._errorHandlerIFrame);
}


// Called when an Ajax Request FAILES...!
Gaia.Control._onFailedAjaxRequestFinished = function(callback, err){

  // Resetting to get Ajax Angine up running again...!
  Gaia.Control._activeRequest = false;

  if( Gaia.Control._hasShownErrorForCurrentRequest )
    return;

  // Making sure we don't show more than one error message for each request
  // Firefox will call this method TWICE due to that the onFailure and onException are "overlapping"...
  // Though at the same time we need them bot since only onException will fire if connection is lost
  // and only onFailure will fire WITH the exception info, lucky us though the onFailure will fire first... ;)
  // (Meaning we DO get the exception trace form the server since the onFailure will fire first (if it fires at all) and that one carries
  // the exception trace...)
  Gaia.Control._hasShownErrorForCurrentRequest = true;

  // Checking to see if the user has created his own error handler
  if( Gaia.Control.errorHandler) {
    if( !Gaia.Control.errorHandler (
      (err && err.status) ? err.status : -1, 
      (err && err.statusText) ? err.statusText : 'CONNECTION_PROBLEM',
      (err && err.responseText) ? err.responseText : 'Unknown problem in page, probably caused by a connection failure to the server') )
      this._onSuccessfulAjaxRequestFinished(callback);
    return;
  }

  if( !err.status ){
    alert('Something went wrong with the communication, possible reasons are:\n1. Connection Breakdown\n2. Session Timeout\n3. Response.Redirect()');
  } else if( confirm('Error was received from server:\n Status: ' + err.status + ',\n Message: "'+ err.statusText+ '"\n\nDo you wish to see the debug results?') ){
    var dbg = document.createElement('iframe');
    dbg.width = '100%';
    dbg.height = '100%';
    dbg.style.position = 'absolute';
    dbg.style.left = '0px';
    dbg.style.top = '0px';
    dbg.style.zIndex = 10000;
    document.body.appendChild(dbg);
    dbg.contentWindow.document.open();
    dbg.contentWindow.document.write(err.responseText);
    dbg.contentWindow.document.write(
        '<div onclick="window.parent.Gaia.Control._closeErrorHandler();"'+
            'style="cursor:pointer;z-index:10001;right:5px;top:5px;width:100px;height:40px;border:Solid 3px Black;background-color:Red;position:absolute;padding-top:15px;text-align:center;font-size:18px;">'+
            'Close'+
         '</div>');
    dbg.contentWindow.document.close();
    Gaia.Control._errorHandlerIFrame = dbg;
  }
  this._onSuccessfulAjaxRequestFinished(callback);
}


// Adding up Form Value into parameter collection
Gaia.Control._addUpParameter = function(formInputName, ajaxOptions){
  var value = $(formInputName);
  if( value ) {
    ajaxOptions.postBody += '&' + formInputName + '=' + encodeURIComponent($F(formInputName));
  }
}


// If true a debug dialog for the currently executing request has been shown already...
Gaia.Control._hasShownErrorForCurrentRequest = false;


// Implementation of the actual Ajax Request engine
// Creates the request and hits the server setting the Ajax engine into "busy" mode (meaning no other request can
// be created before the current request returns)
Gaia.Control._createNewRequestImplementation = function(
  controlToCallFor,   /* Server side Control to dispatch into */
  methodAfter,        /* Callback to execute afterwards */
  evt,                /* Some events needs access to the event object to give extra parameters to the Ajax Engine, e.g. ImageButton click sends over X and Y coords */ 
  extraParams,        /* Extra parameters to pass, used in e.g. calling Controls Methods like where we need to pass parameters to that method etc */
  url,                /* URL to create the Ajax Request against */
  skipDisable         /* TRUE if we should SKIP disabling of the "this control" */){

  // In cases where we have stacked requests where the previous one DESTROYE the widget we're
  // about to handle the request for now
  if( controlToCallFor && !controlToCallFor.element && controlToCallFor.setEnabled) {
    Gaia.Control._activeRequest = false;
    Gaia.Control._activeRequests.splice(0,1);
    return;
  }

  // Showing our update control which will be visible until the request returns...
  var shouldRunGlobalUpdateControl = true;
  var hasRunSpecialUpdater = false;
  if( controlToCallFor && controlToCallFor.tryToShowLocalUpdateControl ) {
    hasRunSpecialUpdater = !controlToCallFor.tryToShowLocalUpdateControl();
    shouldRunGlobalUpdateControl= !hasRunSpecialUpdater;
  }

  var disableThisWidget = true;
  if( shouldRunGlobalUpdateControl ) {
    if( Gaia.Control._updateControl ) {
      disableThisWidget = false;
      Element.show(Gaia.Control._updateControl);
    }
  }
  if( disableThisWidget && !hasRunSpecialUpdater ) {
    // Making sure our element is NOT enabled during the request!
    // Note that Widget will only be disabled IF there is no UpdateControl on the page...!!
    if( !skipDisable && controlToCallFor && controlToCallFor.setEnabled)
      controlToCallFor.setEnabled(false);
  }

  // Creating options for Ajax.Request
  var opt = {
    method: 'post',
    postBody: 'GaiaCallback=true',
    onSuccess: Gaia.Control._onSuccessfulAjaxRequestFinished.bind(this, methodAfter),
    onFailure: Gaia.Control._onFailedAjaxRequestFinished.bind(this, methodAfter),
    onException: Gaia.Control._onFailedAjaxRequestFinished.bind(this, methodAfter),
    evalJS: false,
    widgetRaisingEvent: controlToCallFor,
    runGlobalUpdateControl: shouldRunGlobalUpdateControl
  };

  // Checking to see if this is an "event" raised for the Control
  var isEvent = (!extraParams) || (extraParams == '') || (extraParams.indexOf('ControlMethod') != -1);

  // Adding up the extra parameters lke for instance parameters to Control Methods and so on...!
  if( extraParams ){
    opt.postBody += '&gaiaParams='+encodeURIComponent(extraParams);
  }

  // Serializing all Controls to the post body of the request
  // Note that this method iterates through all GAIA Controls and non-GAIA elements
  // and serialize them into the Ajax Request as parameters and basically "fakes" 
  // a normal ASP.NET Postback...
  // It might be difficult to UPDATE the non Gaia controls, but to send them to the SERVER
  // is relatively easy...?

  // TODO: Seriously optimize this whole logic...! ????
  
  // serialize gaia controls
  var gaiaControls = Gaia.Control._registeredControls.values(); 
  for( var idx = 0, length = gaiaControls.length; idx < length; ++idx ){
    var ctrl = gaiaControls[idx];
   
    // The "this" control should ALWAYS be serialized
    if( isEvent && ctrl == controlToCallFor )
      opt.postBody += ctrl._getElementPostValueEvent(evt);

    // Other controls MIGHT be serialized, but not necessarely!
    else
      opt.postBody += ctrl._getElementPostValue();
  }
  
  // Getting all form elements which are not Gaia Widgets
  var theAspNetForm = document.aspnetForm || document.getElementsByTagName('form')[0];

  var nodeCollection = $A();
  var nodesToSerialize = $A();
  
  nodeCollection.push(theAspNetForm);
  while(nodeCollection.length > 0) {
    var node = nodeCollection.pop();
    
    // if not a gaia control
    if ($G(node.id) == null) {
        // if the node can be serialized
        if (Form.Element.Serializers[node.tagName.toLowerCase()]) {
            nodesToSerialize.push(node);
        }
        
        // get all direct children, which are of type 'element'
        // could have used CSS child selector, but IE6 does not support it
        for(var index = 0, children = node.childNodes, length = children.length; index < length; ++index) {
         var child = children[index];
         if (child.nodeType === 1) {
          nodeCollection.push(child);
         }
        }
    }
    // else skip all child nodes of gaia control,
    // because they should be handled by gaia control serialization logic
  }
  
  // __EVENTTARGET and __EVENTARGUMENT are serialized from the specific Gaia Widgets
  // and if not, it's another type of postback meaning we won't be in this method in the first place.
  // __LASTFOCUS makes only sense in a POSTBACK scenario...!
  nodesToSerialize = nodesToSerialize.without($('__EVENTTARGET'), $('__EVENTARGUMENT'), $('__LASTFOCUS'));
  
  // serialize non-gaia controls
  if( nodesToSerialize.length > 0 ){
    opt.postBody += '&' + Form.serializeElements(nodesToSerialize, {submit:false, hash:false});
  }

  // Setting the "has shown error for current request" to false...
  Gaia.Control._hasShownErrorForCurrentRequest = false;

  // Initiating our request
  // Note we try to use the incoming URL, then if none we try the URL of the Control then if none there too
  // we default to the "Page Public URL"...!
  new Ajax.Request((url || ((controlToCallFor  && controlToCallFor.options) ? controlToCallFor.options.url : null) || Gaia.Control._defaultUrl), opt);
}


// Timer ticker which checks to see if there is an on going request and if none starts the next request in queue
// If there are an ongoing request it will check back again in 50 milliseconds...
// Note that this one guarantees the ORDER of the requests meaning the first one in the queue will be executed
// first, then the second one etc...
Gaia.Control._dispatchNextRequest = function(){

  // Note that the Engine MIGHT get into a state where it believes that there IS an
  // ongoing request still there are none, this is by DESIGN to freeze up the Ajax Engine
  // on the client if there is an exception in the JavaScript executing logic since otherwise the
  // client and the server might get "out of sync" with no warning for the developer and/or user
  // of your application...
  if( Gaia.Control._activeRequests.length == 0 )
    return;

  if( Gaia.Control._activeRequest ) {
    // Ongoing request, must wait for it to finish, else ViewState and everything else goes bananas...!!
    setTimeout(function(){
      Gaia.Control._dispatchNextRequest()
    }, 50);
  } else {

    // "Locking" to make sure there can be only ONE at the same time...
    // In theory one could get a race condition here, but it's highly unlikely...!
    Gaia.Control._activeRequest = true;

    var request = Gaia.Control._activeRequests[0];
    Gaia.Control._createNewRequestImplementation(
      request.control, 
      request.method, 
      request.evtIn, 
      request.xtraParams, 
      request.urlToCall, 
      request.skipDisable);
  }
}


// Creates a new request and adds up into the queue...
// If there are no requests in the que it is being executed immediately, if 
// there are any ongoing requests the request is being added up into the queue and 
// executed whenever there's no more ongoing requests to the server...
Gaia.Control._createNewRequest = function(controlToCallFor, methodAfter, evt, extraParams, url, skipDisable){

  // Creating our request placeholder (contains the data for the request)
  var retVal = {
    control: controlToCallFor, 
    method: methodAfter, 
    evtIn: evt, 
    xtraParams: extraParams, 
    urlToCall: url, 
    skipDisable: skipDisable
  };

  // Push this request into the list of "requested requests"...
  Gaia.Control._activeRequests.push(retVal);

  // Dispatches the next request in the queue...
  Gaia.Control._dispatchNextRequest();
  return retVal;
}


// This one contains the return value from e.g. Page and Control Methods
// TODO: REMOVE this one and exchange with a "non-Global" parameter or something if possible...!
Gaia.Control.returnValue = null;


// Called when either a Page Method or a Control Method has succeeded and the client code is supposed to be called...
Gaia.Control._onSuccessServerSideMethodCalled = function(onFinished, t){
  // TODO: The logic about the Gaia.Control.returnValue shoud REALLY be refactored...!!!!
  // Now we're asserting a return value for EVERY request, also the ones that doesn't really have
  // any return values like the "WebControl Event Wrapper" system etc...!
  try {
    if( this && this.setEnabled )
      this.setEnabled(true);
    if( t && t.responseText && t.responseText.length > 0 )
      Gaia.Control.evalServerCallback('Gaia.Control.returnValue = ' + t.responseText);
    else
      Gaia.Control.returnValue = false;

    if( onFinished )
      onFinished(Gaia.Control.returnValue);
  } catch(e) {
    // Setting "current active request" to false to enable the NEXT request to execute...
    Gaia.Control._activeRequest = false;
    alert('Server response evaluation failure:\n ' + e.name + '\n ' + e.message);
  }
}


// Parse parameters to pass to server side method
Gaia.Control._parseParametersForServerSideMethod = function(method, params, methodType){
  var parsedParams = method + ',' + methodType;
  if( params ){
    params.each(function(param){
      var idxParam = param;
      if( typeof param == 'string' ){
        while( idxParam.indexOf(',') != -1 ){
          idxParam = idxParam.replace(',', '|$|');
        }
      }
      parsedParams += ',' + idxParam;
    });
  }
  return parsedParams;
}


// Calls server-side method, either a Page method or a control method
Gaia.Control._callServerSideMethod = function(method, params, onFinished, methodType, skipDisable){
  var parsedParams = Gaia.Control._parseParametersForServerSideMethod(method, params, methodType);
  Gaia.Control._createNewRequest(
    this, 
    Gaia.Control._onSuccessServerSideMethodCalled.bind(this, onFinished),
    null,
    parsedParams,
    null,
    skipDisable);
}


// Calls a method on the PAGE!
// Takes the name of the method as a string, the params as an iterateable collection and the callback to
// be called when the server returns.
// The callback will be passed the return value from the server-side method...
Gaia.Control.callPageMethod = function(method, params, onFinished){
  Gaia.Control._callServerSideMethod.bind(this)(method, params, onFinished, 'PageMethod');
}


// Call a method on a WebControl, used for extension control developers.
// Look at e.g. Window to see samples of uses...
Gaia.Control.callControlMethod = function(method, params, onFinished, passId, skipDisable){
  Gaia.Control._callServerSideMethod.bind(this)(method, params, onFinished, 'ControlMethod,' + (passId || this.element.id), skipDisable);
}


// Calls a method on an ASPECT
Gaia.Control.callAspectMethod = function(method, params, onFinished, passId){
  Gaia.Control._callServerSideMethod.bind(this)(method, params, onFinished, 'AspectMethod,' + (passId || this.element.id), true);
}


// Used to track which script fragment of a "batch" is currently being executed.
// Gaia separates "script fragments" by ";;;;" and runs a split on the different script fragments
// returned from the server.
// At the moment this is basically only used to make sure the JavaScript files included from an Ajax
// Callback is being fully included before moving on to the next "batch" of script executions
// since some logic returned from the server might be dependant upon some JavaScript files being
// included before it runs. But in the future we might also separate OTHER types of logic
// into different sets of batch jobs for the JavaScript engine...
Gaia.Control.currentScriptFragmentExecutingIdx = 0;


// This is the list of the scripts loaded (split by ";;;;") from the server
// which we're supposed to be executing.
// They are executed in sequential order!
Gaia.Control.lastLoadedListOfScripts = null;


// This one is being called when the server returns with a string fragment which will be split by ";;;;"
// Then they are executed sequentially to make sure the first part of the batch is finished executing BEFORE the 
// next part of the batch is executed...
// For the time being this is only used for making sure we fully include JavaScript files from an Ajax Callback
// before we start to execute logic from those files...
Gaia.Control.evalServerCallback = function(script){

  // Set the javascript files to wait for to ZERO
  // When we load a JavaScript files this one is incremented by on, when that JavaScript file
  // is finished loading typeof typeToWaitFor != 'undefined' the Gaia.javaScriptFilesToWaitFor is decremented
  // When it reaches ZERO execution will pass to NEXT part of the script...!!
  //(parts are separated by ";;;"...)
  Gaia.javaScriptFilesToWaitFor = 0;
  Gaia.Control.lastLoadedListOfScripts = script.split(';;;;;');
  Gaia.Control.currentScriptFragmentExecutingIdx = 0;
  return Gaia.Control._executeScript(true);
}


// Executes one part of the batch returned from the server
// Note that if the previous part of the batch is NOT finished executing
// it will wait until that batch is finished executing before executing the next batch
Gaia.Control._executeScript = function(shouldExecute){
  var retVal = null;
  if( Gaia.Control.currentScriptFragmentExecutingIdx < Gaia.Control.lastLoadedListOfScripts.length ) {
    if( shouldExecute ) {
      retVal = eval(Gaia.Control.lastLoadedListOfScripts[Gaia.Control.currentScriptFragmentExecutingIdx]);
      // Then we need to call our NEXT JavaScript part
      // This time we set the "execute" to false, this makes sure we get into our 
      // "wait for JavaScript files to load" logic before we move on
      setTimeout('Gaia.Control._executeScript(false);', 100);
    } else {
      // Checking to see if we need to wait for more JavaScript files...!!
      if( Gaia.javaScriptFilesToWaitFor == 0 ){
        // Running NEXT script...!!
        // No more files to wait for...
        Gaia.Control.currentScriptFragmentExecutingIdx += 1;
        Gaia.Control._executeScript(true);
      } else {
        // We've got more files to load
        // Try again in 1/10 of a second
        setTimeout('Gaia.Control._executeScript(false);', 100);
      }
    }
  } else {
    Gaia.Control._activeRequest = false;
  }
  return retVal;
}








/* ---------------------------------------------------------------------------
   Common class for all Controls, basically wraps the WebControls.Control 
   class.
   --------------------------------------------------------------------------- */
Gaia.Control.prototype = {


  // This one should have been "overridet" in extended classes and that method should call "baseInitializeControl" instead!
  initialize: function(){
    throw "You must override the initialize function in your derived class";
  },


  // "Constructor" for object
  initializeControl: function(element, options){

    // Finding the element in the DOM and caching it to the this.element member
    this.element = $(element);
    if( !this.element )
      this.element = element; // IN-Visible controls....  E.g. the "Timer" control etc...

    // Setting the options
    this.options = Object.extend({
      url: Gaia.Control._defaultUrl
    }, options || {});

    // If control has set focus we make sure it really does...
    if( this.options.hasSetFocus )
      this.setFocus();
  },


  getControlId: function(){
    return this.element.id;
  },


  // Shows or hides the wrapped element
  // Note that normal elements are just being set the visibility property for
  // while container widgets are physically being destroyed...
  // (Which is overridden in derived classes)
  setVisible: function(value){
    value ? Element.show(this.element) : Element.hide(this.element);
    if( value != true ){
      // Making sure control and child controls is destroyed...
      this.destroy();
    }
    return this;
  },


  // Sets the focus to the control
  setFocus: function(){
    // This looks REALLY stupid but is needed for situations where widget
    // is inside of a Window since the window will be rendered with "display:none" 
    // by default which will prevent the focus() method from working...
    setTimeout(function(){
      this.setElementFocus();
    }.bind(this), 500);
    return this;
  },
  
  setElementFocus: function() {
    this.element.focus();
  },

  setContent: function(value) {
    this.element.innerHTML = value;
    return this;
  },

  // Returns true if global updatecontrol logic should run...
  tryToShowLocalUpdateControl: function(){
    var retVal = true;
    if( this.options.aspects ){
      for( var idx=0, length = this.options.aspects.length; idx < length; ++idx ) {
        if( this.options.aspects[idx].startAjaxRequest && this.options.aspects[idx].startAjaxRequest() ) {
          retVal = false;
          break;
        }
      }
    }
    return retVal;
  },


  // Returns true if global updatecontrol logic should run...
  tryToHideLocalUpdateControl: function(){
    var retVal = true;
    if( this.options.aspects ){
      for( var idx=0, length = this.options.aspects.length; idx < length; ++idx ) {
        if( this.options.aspects[idx].endAjaxRequest && this.options.aspects[idx].endAjaxRequest() ) {
          retVal = false;
          break;
        }
      }
    }
    return retVal;
  },


  // Destroys the widget
  // Override and call "_destroyImp()" to make sure you run the Control.destroy implementation if
  // you override this method...
  destroy: function(){
    this._destroyImpl();
  },

  _destroyImpl: function() {
    // Stopping event observers
    if( this._subscribedEvents ) {
      for( var idx = 0, length = this._subscribedEvents.length; idx < length ; ++idx ){
        var evt = this._subscribedEvents[idx];
        Element.stopObserving(this.element, evt.name, evt.evt);
      }
      
      delete this._subscribedEvents;
    }

    // Destroying all of its ASPECTS...!
    if( this.options.aspects ) {
      for( var idx=0, length = this.options.aspects.length; idx < length; ++idx ) {
        this.options.aspects[idx].destroy();
      }
    }

    // Removing control from Control Collection....!
    Gaia.Control._registeredControls.unset(this.element.id);
    
    // Create a placeholder and remove from DOM tree
    var placeholder = document.createElement('span');
    placeholder.id = this.element.id;
    placeholder.style.display = 'none';
        
    this.element.parentNode.replaceChild(placeholder, this.element);
    delete this.element;
    this.element = null;
  },

  // Use to observe specific events and call server when they occur
  // If you observe an event through this method the server WILL be called when that event occurs
  // and if there's a match on the server for such an event that event will be called
  // Used to map the events inherited from ASP.NET WebControl controls like Button.Click event etc...
  // Use Gaia.Control.callControlMethod if you're implementing CUSTOM events and such...
  observe: function(evtName, bubbleUp){
  
    // Calling the implementation, if inheriting then override this and call "_observeImpl" to make sure
    // you're calling the base class implementation...!
    this._observeImpl(evtName, (bubbleUp ? true : false));
    return this;
  },

  // Implementation of "observe"...!
  _observeImpl: function(evtName, bubbleUp){

    // Making sure the array of events this widget is subscribing to is initialized...
    if( this._subscribedEvents == null )
      this._subscribedEvents = new Array();

    var _onEventEvent = this._onEvent.bindAsEventListener(this, evtName, bubbleUp);
    Element.observe(this.element, evtName, _onEventEvent);
    this._subscribedEvents.push({name:evtName, evt:_onEventEvent});
  },


  // Private implementation of the observe event handler
  // This is the one doing the actually "ajax magic" and calls our server when
  // a WebControl "native" event occurs.
  // This is the one subscribing to whatever JavaScript DOM event that triggers the event
  // like for instance the JavaScript "click" event for the input type="button" event and calls the
  // server.
  _onEventImpl: function(evt, evtName, skipDisable){
    
    // if the element is disabled, do not fire any event
    if (this.element.disabled || this.element.hasAttribute('disabled'))
        return;

    // Checking to see if we should "validate" page (RequiredFieldValidator etc...)
    // TODO: This should probably be removed since we're giving people the false
    // feeling of actually supporting validators which will create confusion when
    // validators don't work when created in callbacks etc...
    if( typeof(WebForm_OnSubmit) == 'function' ){
      if(WebForm_OnSubmit() != true)
        return;
    }

    // Code to circumvent flaw in Sharepoint 2007
    // This is a BUG in SharePoint, but what can we do.... ;)
    if ( window['_spFormOnSubmitCalled'] != null && window['_spFormOnSubmitCalled'] != 'undefined' ) {
      _spFormOnSubmitCalled = false;
    }

    // Sets the updater control for the widget event
    // TODO: Remove this one since I see no point in having it here....!! ???
    this._updaterControl = Gaia.Control._updateControl;

    // Creating actually Ajax.Request
    Gaia.Control._createNewRequest(
      this, 
      this._afterEvent.bind(this, skipDisable), 
      evt, 
      null,
      null,
      skipDisable);
  },


  // MUST override function!
  // Called when _ANY_ control creates a request and wants to call back
  // to the server. This is the "value" property of the widget (if any)
  // Just return '' in your overridden function if there are no value for it!
  _getElementPostValue: function(){
    return '';
  },


  // This is the method called when the widget is ITSELF raising an event which goes to the server
  // and THIS is the value of the Widget in ADDITION to potentially "meta data" to indicate that
  // THIS widget raised the event!
  _getElementPostValueEvent: function(){
    return '';
  },


  // This is the "callback name" for the widget used internally by ASP.NET to
  // map the widget to it's server side "object" and fill out it's values etc to
  // reference the widget.
  getCallbackName: function(){

    // Checking to see if we've got it in the cache first
    if( this.options.callbackName && this.options.callbackName.length > 0 )
      return this.options.callbackName;

    // Then checking against the "name" attribute in the ELEMENT of the widget
    if( this.element.name && this.element.name.length > 0 ) {
      this.options.callbackName = this.element.name;
      return this.options.callbackName;
    }

    // Then resorting to "last resort" by constructing the callbackName by 
    // taking the ID of the element and replacing every occurency of an underscore "_"
    // with a "$" sign...
    // This method is REALLY not good and all widgets that doesn't have the name attribute should
    // override the object initialization script ("registerControl") with the Name attribute 
    // for the widget since in containers and similar having underscores as part
    // of their ID this logic WILL fail...!!!!!!
    var retVal = this.element.id;
    while( retVal.indexOf('_') != -1 ){
      retVal = retVal.replace('_', '$');
    }

    // Setting cache and returning the callback name value
    this.options.callbackName = retVal;
    return retVal;
  },


  _onEvent: function(evt, evtName, bubbleUp){
     // modified 04-09-2007 by Matthew M.
     // -- added updated alert message if this section errors out
     // suggestion - all events should have a 'fall-through' to normal web function (non-ajax) if 
     // this routine fails.

     // Raising event
    try {
      this._onEventImpl(evt, evtName);
    } catch(err) {
      // More informative error report
      alert('Gaia Event Handling Error:\n\nError Message:\n\n' + err);
    }

    // Preventing event to bubble upwards!
    // But only if we SHOULD prevent it...
    if( !bubbleUp )
      Event.stop(evt);
  },


  // Called by framework when server side event handler returns
  // Basically just re-enables the widget and make sure the JS returned from the server
  // is being executed...
  _afterEvent: function(skipDisable, t){
    
    // We MUST set this value FIRST since some of the properties set in the callback might be to DISABLE the "this" control...!
    // First checking to see if the "this" haven't been deleted by the response script though...
    if( $(this.element.id) ) {
      if( this._updaterControl == null && !skipDisable )
        this.setEnabled(true);
    }
    
    // Evaluating the response from the server...!
    if( t ) {
      Gaia.Control.evalServerCallback(t.responseText);
    } else {
      Gaia.Control._activeRequest = false;
    }
  }
};

/*
 *  Gaia Effect helpers to ensure effects are run AFTER the oter JS have finished
 *  when instantiated from the client. This class is only here to ensure that
 *  ALL OTHER JavaScript returned from the server is executed BEFORE the 
 *  effect is executed since otherwise the effect will "stutter"...
 */

Gaia.Effect = Class.create();
Object.extend(Gaia.Effect.prototype, {

  initialize: function(effectType, element, options){
    // This class serves only the purpose of "delaying" the effect execution till
    // AFTER all OTHER JS is finished executed...
    // Therefor we only create a timer which guaranteed will be executed AFTER
    // the "current batch" is executing...
    setTimeout(function(){
      new effectType(element, options);
    }, 1);
  }
});

// These type of static members you will find in EVERY JavaScript file and their purpose is to make sure
// that the JavaScript file is finished loading...!
// That is since no JavaScript logic can be executed before all JavaScript files from the same batch
// are finished loading...!
// The basic alog is;
// while( allOfMyJavaScriptFiles.browserFinishedLoading != true) { continueWaitingForJSFileToLoad }
Gaia_Control_browserFinishedLoading = true;
