/**
 * ContentAddModuleController.js
 * Define the ContentAddModuleController class/prototype
 *
 * $Revision: 1.48 $
 * $Date: 2012/01/11 15:37:37 $
 * Last edited by: $Author: danderson $
 */
ContentAddModuleController = function( oOptions )
{
  if( ContentAddModuleController.instanceIsOpen === true )
  {
    this.init = function(){};
  }
  else
  {
    this.init( oOptions );
  }
};

ContentAddModuleController.make = function( oOptions )
{
  return new ContentAddModuleController( oOptions || {} );
};

ContentAddModuleController.prototype = {
  init: function( oOptions )
  {
    ContentAddModuleController.instanceIsOpen = true;

    this.$BaseInterface = null;
    this.$FocalArea = null;
    this.$StagingArea = null;
    this.$ControlArea = null;
    this.$ControlLoadingMask = null;
    this.$NextButton = null;
    this.$PreviousButton = null;
    this.sTmpSaveButtonLabel = '';
    this.$CurrentFocus = null;
    this.$CurrentGroup = null;
    this.$CurrentModule = null;

    this.bControlsDisabled = false;
    this.bIsMaximized = false;
    this.sCurrentTabBarKey = null;
    this.oBackupObjects = {};
    this.bModal = true;
    this.oWindowContext = null;
    this.bCloseOpeningWindow = false;
    this.callbacks = new ContentAddModuleController.callbackManager();
    this.oInitOptions = {};                 /*Options passed into create Content add - might be needed to break out into fullscreen*/
    this.iDialogHeight = 530;
    this.iDialogWidth = 660;
    this.sPageData = null;

    //check if we need to open a new window
    oOptions = this._checkForNewWindow( oOptions );

    if( this.bNewWindow )
    {
      this._launchIntoFullWindow( oOptions );
    }
    else
    {
      //store the options so if we breakout to a new window later - we keep everything
      jQuery.extend( true, this.oInitOptions, oOptions );
      //parse the options
      oOptions = this._parseOptions( oOptions );

      if( this.bCloseOpeningWindow && window.opener )
      {
        this.oWindowContext = window.opener;
        while( this.oWindowContext.opener )
        {
          this.oWindowContext = this.oWindowContext.opener;
        }
        window.opener.close();
      }
      if( !this.oWindowContext )
      {
        if( this.bModal )
        {
          this.oWindowContext = window;
        }
        else
        {
          this.oWindowContext = window.opener;
        }
      }

      /* Setup Display */
      this._populateBackupObjects();
      this._buildBaseInterface();
      this._disableControls();
      this._hideControls();

      if( this.sPageData === null )
      {
        this._fetchModuleGroup( null, oOptions );
      }
      else
      {
        this._processPage( this.sPageData );
      }
    }

    $( document ).unbind( 'LaunchSeeAlsoAdvanced' ).bind( 'LaunchSeeAlsoAdvanced', function( oEvent, oData )
    {
      var aUrn, sAddSeeAlsoUrl;
      if( oData.bSuccess )
      {
        aUrn = oData.object.sURN.split( ':' );

        sAddSeeAlsoUrl = '/workflow/setupSeeAlsoInteractiveWorkflow.html?' + $.param( {
          objectType: aUrn[2],
          objectId: aUrn[3],
          attachOnSave: 'yes',
          fetchSeeAlsos: 'yes'
        } ) ;
        window.open( sAddSeeAlsoUrl );
      }
    });
  },
  _launchIntoFullWindow: function( oOptions )
  {
    /*
      TODO - double check that this works properly
    */
    if( typeof oOptions !== 'object' || oOptions === null )
    {
      oOptions = {};
    }

    $( '<form />' ).
      attr( {
        action: '/addStuffFullScreen.html',
        target: '_blank',
        method:'post'
      } ).
      append( $( '<textarea />' ).
        attr( {
          name: 'oOptions'
        } ).
        hide().
        val( JSON.stringify( oOptions ) )
      ).
      appendTo( $( 'body' ) ).
      submit();
  },
  _checkForNewWindow: function( oOptions )
  {
    if( typeof oOptions === 'object' && oOptions != null )
    {
      this.bNewWindow = false;
      if( typeof oOptions.bNewWindow === 'boolean' )
      {
        this.bNewWindow = oOptions.bNewWindow;
        delete oOptions.bNewWindow;
      }
    }
    else
    {
      oOptions = {};
    }
    return oOptions;

  },
  _parseOptions: function( oOptions )
  {
    if( typeof oOptions === 'object' && oOptions != null )
    {
      this.sModalTitle = 'Add Stuff';
      if( typeof oOptions.sModalTitle === 'string' && oOptions.sModalTitle != '' )
      {
        this.sModalTitle = oOptions.sModalTitle;
        delete oOptions.sModalTitle;
      }

      this._sInitialModulesURL = '/addStuff.html';
      if( typeof oOptions.sURL === 'string' && oOptions.sURL != '' )
      {
        this._sInitialModulesURL = oOptions.sURL;
        delete oOptions.sURL;
      }

      if( typeof oOptions.bModal === 'boolean' )
      {
        this.bModal = oOptions.bModal;
        delete oOptions.bModal;
      }

      if( typeof oOptions.bCloseOpeningWindow === 'boolean' )
      {
        this.bCloseOpeningWindow = oOptions.bCloseOpeningWindow;
      }

      if( typeof oOptions.sPageData === 'string' )
      {
        this.sPageData = oOptions.sPageData;
        delete oOptions.sPageData;
      }

      if( typeof oOptions.oEventTriggers === 'object' && oOptions.oEventTriggers !== null )
      {
        var callbackTypes = [ 'success', 'error' ], ctIndex, currentCallbackType, currentEventTriggerOption, currentIndex;
        for( ctIndex in callbackTypes )
        {
          currentCallbackType = callbackTypes[ ctIndex ];
          if( typeof oOptions.oEventTriggers[currentCallbackType] === 'object' && oOptions.oEventTriggers[currentCallbackType] !== null )
          {
            currentEventTriggerOption = oOptions.oEventTriggers[currentCallbackType];
            if( ( typeof currentEventTriggerOption.events === 'object' && typeof currentEventTriggerOption.events.length === 'number' ) )
            {
              this.callbacks.addEvent( currentEventTriggerOption.events, ContentAddModuleController.callbacks.callbackTypes[ currentCallbackType ] );
            }
            if( typeof currentEventTriggerOption.allowDefault === 'boolean' )
            {
              this.callbacks.bDoDefault[ currentCallbackType ] = currentEventTriggerOption.allowDefault;
            }
          }

        }
        delete oOptions.oEventTriggers;
      }
    }
    else
    {
      oOptions = {};
    }
    return oOptions;
  },
  _populateBackupObjects: function()
  {
    /*  Save these objects and put them back when we are done */
    var aBackupNames = [ 'kwSearch', 'SearchWidgetWords' ], x, sBackupItem;

    for( x = 0; x < aBackupNames.length; x++ )
    {
      sBackupItem = aBackupNames[ x ];
      if( typeof window[ sBackupItem ] !== 'undefined' && window[ sBackupItem ] !== null )
      {
          this.oBackupObjects[ sBackupItem ] = window[ sBackupItem ];
      }
    }

    /*
      Need to cleanup quick form validation because it uses generic method names.  Causes issues because it isn't always used - but it is called if it exists - so we need to clean up for it
    */
    this._removeOrphanedMethods();
  },
  _buildBaseInterface: function()
  {
    var oThis = this;

    this.$BaseInterface = $( '\
      <div class="contentadd-instance">\
        <div class="contentadd-focal-area"></div>\
        <div class="contentadd-staging-area" style="display: none;"></div>\
        <div class="contentadd-control-area">\
          <button class="contentadd-control-previous fltlft" style="visibility:visible"><span class="ui-icon ui-icon-triangle-1-w"></span></button>\
          <button class="contentadd-control-next fltrt" style="visibility:visible"><span class="ui-icon ui-icon-triangle-1-e"></span></button>\
          <button class="contentadd-control-cancel">Cancel</button>\
          <span class="contentadd-control-custom"></span>\
        </div>\
        <div class="contentadd-control-loadingmask  ui-state-active">\
          <h2>\
            <img src="/images/loadingsmall.gif" />\
            Loading\
          </h2>\
        </div>\
      </div>\
    ' ).data( 'oContentAddInstance', this );

    /* ! The anonymous function wrappers in these calls to click() are needed for scope adjustment when the real handler is called */
    this.$PreviousButton = this.$BaseInterface.find( 'button.contentadd-control-previous' ).click( function()
    {
      oThis._previousButtonHandler();
    } );

    this.$NextButton = this.$BaseInterface.find( 'button.contentadd-control-next' ).click( function()
    {
      oThis._nextButtonHandler();
    } );

    this.$BaseInterface.find( 'button.contentadd-control-cancel' ).click( function()
    {
      oThis._cancelButtonHandler();
    } );

    this.$CustomControlContainer = this.$BaseInterface.find( 'span.contentadd-control-custom' );
    this.$ControlLoadingMask = this.$BaseInterface.find( 'div.contentadd-control-loadingmask' );
    this.$FocalArea = this.$BaseInterface.find( 'div.contentadd-focal-area' );
    this.$StagingArea = this.$BaseInterface.find( 'div.contentadd-staging-area' );
    this.$ControlArea = this.$BaseInterface.find( 'div.contentadd-control-area' );

    if( this.bModal )
    {
      this.$BaseInterface.
        dialog( {
          autoOpen: false,
          position: 'center',
          title: this.sModalTitle,
          closeOnEscape: false,
          width: oThis.iDialogWidth,
          height: oThis.iDialogHeight,
          dialogClass: 'contentAdd'
        } )
        .parents( 'div.ui-dialog' )
          .append( this.$ControlArea )
          .append( this.$ControlLoadingMask )
          .find( 'div.ui-dialog-titlebar' )
            .append(
              $( [
                '<a href="#" class="ui-state-default ui-corner-all ybx_dialog-size" role="button" unselectable="on" style="position: absolute; right: .5em;">',
                  '<span class="ui-icon ui-icon-plusthick" unselectable="on" title="Maximize Window"></span>',
                 '</a>'
              ].join( '' ) )
              .click( function( e )
              {
                oThis._toggleDialogSize.call( oThis );
                e.preventDefault();
              } )
              .hover(
                function()
                {
                  $( this ).addClass( 'ui-state-hover' );
                },
                function()
                {
                  $( this ).removeClass( 'ui-state-hover' );
                }
              )
            )
            .find( 'a.ui-dialog-titlebar-close' )
              .hide()
              .end()
            .end()
          .end()
          .dialog( 'open' );
      /*  TODO - when the close button is added back, we should add a before close so we don't allow the user to close if he/she is saving  */
    }
    else
    {
      $( 'body' ).append( this.$BaseInterface.attr( 'id', 'contentAdd-full' ) );
    }

    /* This class is needed for theme-roller styling, but cannot be added before dialoging or
     * UI will drop the div since we don't have any buttons defined in the dialog options. */
    this.$ControlArea.addClass( 'ui-dialog-buttonpane ui-state-default ui-corner-bottom' );
    this.$ControlLoadingMask.addClass( 'ui-dialog-buttonpane ui-corner-bottom' );
  },
  _maximizeInterface: function()
  {
    var iWindowWidth, iWindowHeight, iNewHeight, iNewWidth, iNewTop, iNewLeft,
        sMaxIconClass = 'ui-icon-plusthick',       /*  class used for the Maxmize icon */
        sMinIconClass = 'ui-icon-minusthick',              /*  class used for the restore icon */
        $icon = $( 'a.ybx_dialog-size' ).find( 'span' );

    if( !this.bIsMaximized )
    {
      $icon.removeClass( sMaxIconClass ).addClass( sMinIconClass );

      /*  Get the height and width of the window  */
      iWindowHeight = $( window ).height();
      iWindowWidth = $( window ).width();

      iNewHeight = Math.floor( iWindowHeight * .97 );
      iNewWidth = Math.floor( iWindowWidth * .97 );

      iNewTop = Math.floor( iWindowHeight * .01 );
      iNewLeft = Math.floor( iWindowWidth * .01 );

      /*  Update the dialog's position and size */
      this.$BaseInterface.dialog( 'option',{
        height: iNewHeight,
        width: iNewWidth,
        position: [ iNewLeft, iNewTop ]
        } );

      this.$BaseInterface.css( 'height', iNewHeight );
      this.bIsMaximized = true;
    }
  },
  _restoreInterface: function()
  {
    var sMaxIconClass = 'ui-icon-plusthick',       /*  class used for the Maxmize icon */
        sMinIconClass = 'ui-icon-minusthick',              /*  class used for the restore icon */
        $icon = $( 'a.ybx_dialog-size' ).find( 'span' );


    if( this.bIsMaximized )
    {
      $icon.removeClass( sMinIconClass ).addClass( sMaxIconClass );

      /*  Update the dialog's position and size */
      this.$BaseInterface
        .dialog( 'option', {
          height: this.iDialogHeight,
          width: this.iDialogWidth
        } )
        /* Must be separate call from dimensions as jQ seems to run position before dimensions which throws the whole thing way off */
        .dialog( 'option', 'position', 'center' );

      this.$BaseInterface.css( 'height', this.iDialogHeight );
      this.bIsMaximized = false;
    }
  },
  _toggleDialogSize: function()
  {
    var iWindowWidth, iWindowHeight, iNewHeight, iNewWidth, iNewTop, iNewLeft,
        bDoMaximize = !this.bIsMaximized,     /* set what we are going to do - makes code easier to follow & go ahead and change state */
        sMaxIconClass = 'ui-icon-plusthick',       /*  class used for the Maxmize icon */
        sMinIconClass = 'ui-icon-minusthick';              /*  class used for the restore icon */

    if( bDoMaximize )
    {
      this._maximizeInterface();
    }
    else
    {
      this._restoreInterface();
    }
  },
  _teardownBaseInterface: function()
  {
    var sBackupItem;

    if( this.bModal )
    {
      try
      {
        this
          .$BaseInterface
          .find('textarea.kwRichText')
            .each( function()
            {
              var curTinyMCE = $( this ).tinymce();
              if( typeof curTinyMCE === 'object' && curTinyMCE !== null && typeof curTinyMCE.remove === 'function' )
              {
                curTinyMCE.remove();
              }
            } );
      }
      catch( e ) {}

      this.$BaseInterface.dialog( 'destroy' ).remove();

      /*
        Remove orphaned methods left over form Quickform validation
      */
      this._removeOrphanedMethods();

      for( sBackupItem in this.oBackupObjects )
      {
        /*
          this verifies that we don't write inherited properties from BackupObjects
          to window
        */
        if( Object.prototype.hasOwnProperty.call( this.oBackupObjects, sBackupItem ) )
        {
          window[ sBackupItem ] = this.oBackupObjects[ sBackupItem ];
        }
      }

      ContentAddModuleController.instanceIsOpen = false;
    }
    else
    {
      if( typeof this.oWindowContext === 'object' &&
          this.oWindowContext !== null &&
          typeof this.oWindowContext.focus == 'function'
        )
      {
        this.oWindowContext.focus();
      }
      ContentAddModuleController.instanceIsOpen = false;
      window.close();
    }
  },
  _removeOrphanedMethods: function()
  {
    var x, sCleanupItem, aMethodCleanup = [ 'requiredValidate', 'optionalValidate' ];
    /*
      Clean up any methods that content add uses that may conflict with other content adds
    */
    for( x = 0; x < aMethodCleanup.length; x++ )
    {
      sCleanupItem = aMethodCleanup[ x ];
      if( typeof window[ sCleanupItem ] !== 'undefined' &&
          window[ sCleanupItem ] !== null )
      {
        window[ sCleanupItem ] = null;
      }
    }

  },
  _showLoadingMask: function()
  {
    this.$ControlLoadingMask.show()
  },
  _hideLoadingMask: function()
  {
    this.$ControlLoadingMask.hide()
  },
  _CurrentFocusIsFirst: function()
  {
    return this.$CurrentFocus.prevAll( 'div.contentadd-focal-container' ).length === 0;
  },
  _CurrentFocusIsLast: function()
  {
    return this.$CurrentFocus.nextAll( 'div.contentadd-focal-container' ).length === 0;
  },
  _CurrentTabIsFirst: function()
  {
    return this.$CurrentFocus.data( 'tabs' ).panels.index( this.$CurrentModule ) === 0;
  },
  _CurrentTabIsLast: function()
  {
    var $CurrentFocusTabPanels = this.$CurrentFocus.data( 'tabs' ).panels;
    return $CurrentFocusTabPanels.index( this.$CurrentModule ) >= $CurrentFocusTabPanels.length - 1;
  },
  _showControls: function()
  {
    this.$ControlArea.children().css( 'visibility', 'visible' );

    if( this._CurrentTabIsFirst() && this._CurrentFocusIsFirst() )
    {
      this.$PreviousButton.css( 'visibility', 'hidden' );
    }

    if( this._CurrentTabIsLast() && this._CurrentFocusIsLast() )
    {
      this.$NextButton.css( 'visibility', 'hidden' );
    }
  },
  _hideControls: function()
  {
    this.$ControlArea.children().css( 'visibility', 'hidden' );
  },
  _enableControls: function()
  {
    this.bControlsDisabled = false;
  },
  _disableControls: function()
  {
    this.bControlsDisabled = true;
  },
  _areControlsDisabled: function()
  {
    return this.bControlsDisabled;
  },
  _toggleSaveSuspend: function( oButton )
  {
    var $Button, SuspendedSaveButtonClass = 'contentadd-control-suspendedsave';

    if( typeof oButton === 'object' && oButton !== null )
    {
      $Button = $( oButton );
      this._disableControls();
      this.sTmpSaveButtonLabel = $Button.html();
      $Button.
        html( '<img src="/images/loadingsmall.gif" /> Saving...' ).
        addClass( SuspendedSaveButtonClass );
    }
    else
    {
      this._enableControls();
      this.$BaseInterface.
        find( '.' + SuspendedSaveButtonClass ).
          html( this.sTmpSaveButtonLabel ).
          removeClass( SuspendedSaveButtonClass );
    }
  },
  _getFocalContainers: function()
  {
    return this.$FocalArea.children( 'div.contentadd-focal-container' );
  },
  _setupFocalContainerForNewGroup: function( bNewGroupIsFocal )
  {
    var $FocalContainers;

    $FocalContainers = this._getFocalContainers().hide();
    if( bNewGroupIsFocal || $FocalContainers.length === 0 )
    {
      this.$CurrentFocus = $( '\
        <div class="contentadd-focal-container" style="display: none;">\
          <ul class="contentadd-tab-list" />\
        </div>\
      ').
        appendTo( this.$FocalArea );
    }
  },
  _reTabCurrentFocus: function( iSelectTab )
  {
    var oThis = this;
    if( typeof iSelectTab !== 'number' || isNaN( iSelectTab ) )
    {
      iSelectTab = 0;
    }
    /* TODO this is not the preferred method to accomplish the add of a tab, but the preferred method is broken in jQ UI
     * http://forum.jquery.com/topic/tabs-inconsistency-between-build-of-tabs-and-add-of-a-tab
     * http://dev.jqueryui.com/ticket/4578 */
    this.$CurrentFocus.
      tabs( 'destroy' ).
      tabs( {
        selected: iSelectTab,
        show: function( e, ui )
        {
          oThis._tabHandler( e, ui );
        }
      } );
  },
  _changeFocus: function( $NewFocus )
  {
    if( typeof $NewFocus === 'object' && $NewFocus !== null && $NewFocus instanceof jQuery )
    {
      this.$CurrentFocus.hide();
      $NewFocus.show();
      this.$CurrentFocus = $NewFocus;
    }
  },
  _nextButtonHandler: function()
  {
    if( ! this._areControlsDisabled() )
    {
      if( ! this._CurrentTabIsLast() )
      {
        var oCurrentTabsInterface = this.$CurrentFocus.data( 'tabs' );
        oCurrentTabsInterface.select( oCurrentTabsInterface.panels.index( this.$CurrentModule ) + 1 );
      }
      else if( ! this._CurrentFocusIsLast() )
      {
        this._changeFocus( this.$CurrentFocus.next( 'div.contentadd-focal-container' ) );
        this._loadModule( this.$CurrentFocus.find( 'div.contentadd-module-container:first' ) );
      }
    }
  },
  _previousButtonHandler: function()
  {
    if( ! this._areControlsDisabled() )
    {
      if( ! this._CurrentTabIsFirst() )
      {
        var oCurrentTabsInterface = this.$CurrentFocus.data( 'tabs' );
        oCurrentTabsInterface.select( oCurrentTabsInterface.panels.index( this.$CurrentModule ) - 1 );
      }
      else if(  ! this._CurrentFocusIsFirst()  )
      {
        this._changeFocus( this.$CurrentFocus.prev( 'div.contentadd-focal-container' ) );
        this._loadModule( this.$CurrentFocus.find( 'div.contentadd-module-container:last' ) );
      }
    }
  },
  _cancelButtonHandler: function()
  {
    this._teardownBaseInterface();
  },
  _tabHandler: function( e, ui )
  {
    var $panel;

    if( ! this._areControlsDisabled() )
    {
      $panel = $( ui.panel );

      this.$CurrentGroup = $panel.parent( 'div.contentadd-group-container' );
      this._loadModule( $panel );
    }
  },
  _onSuccess: function( oData, sTextStatus )
  {
    if( typeof oData !== 'object' || oData === null || ( typeof oData.bSuccess !== 'boolean' && typeof oData.bRedirect !== 'boolean' ) )
    {
      oData = {
        bSuccess: false,
        sMessage: 'Server returned invalid data format'
      };
    }

    if( typeof oData.bRedirect === 'boolean' && oData.bRedirect )
    {
      /* if we are doing a redirect - call any callbacks for successful submit */
      /* store the default */
      var bRunDefault = this.callbacks.bDoDefault.success;
      this.callbacks.bDoDefault.success = false;
      /* execute the callback */
      this.callbacks.run( oData, ContentAddModuleController.callbacks.callbackTypes.success, this.oWindowContext );
      /* restore the default and clear the eventList */
      this.callbacks.bDoDefault.success = bRunDefault;
      this.callbacks.eventList.success = [];
      /* restart the content add */
      this._restartWithParams( oData.aParameters );
    }
    else if( ! oData.bSuccess )
    {
      this._onError( null, oData.sMessage, null );
    }
    else
    {
      this.callbacks.run( oData, ContentAddModuleController.callbacks.callbackTypes.success, this.oWindowContext );
      this._teardownBaseInterface();
    }
  },
  _onError: function( oXMLHttpRequest, sTextStatus, oErrorThrown )
  {
    alert( sTextStatus );
    this.callbacks.run(
      {
        bSuccess: false,
        sMessage: sTextStatus
      },
      ContentAddModuleController.callbacks.callbackTypes.error,
      this.oWindowContext
    );
    this._toggleSaveSuspend();
  },
  _doModuleValidation: function()
  {
    var bResult = false;
    this._removeAllErrorMessages();
    if( this._validateModules() )
    {
      if( this._displayPromptModules() )
      {
        bResult = true;
      }
      else
      {
        bResult = false;
      }
    }
    else
    {
      bResult = false;
    }
    return bResult;
  },
  _loadChildModule: function( sURL, oOptions )
  {
    var oThis = this;
    /* Find all Group Containers which follow this one in the Current Focus--they're no longer needed */
    this.$CurrentGroup.nextAll().each( function()
    {
      $( this ).
        /* Iterate the children which are modules (tab panels) */
        children().
          each( function()
          {
            /* Look for the tab anchors in the current focus window which have HREFs pointing at the current module's (tab's) ID */
            oThis.
              $CurrentFocus.
              find( 'ul li a[href="#' + $( this ).attr( 'id' ) + '"]' ).
              /* Remove the parent of the found anchor */
              parent().
                remove();
          } ).
          /* Having removed all tab anchor parents, back out of the child iteration and remove the Group Container we were currently in */
          end().
        remove();
    } );
    this.$CurrentFocus.nextAll().remove();
    this._reTabCurrentFocus( this.$CurrentFocus.data( 'tabs' ).panels.index( this.$CurrentModule ) );
    this._fetchModuleGroup( sURL, oOptions );
  },
  /*
    Restart a content add to begining - and load bundle with given options
  */
  _restartWithParams: function( oOptions )
  {
    this.$CustomControlContainer.empty();
    this._hideControls();
    this.$FocalArea.empty();
    this._fetchModuleGroup( null, oOptions );
  },
  handlePrompt: function( $Module, sText )
  {
    if( typeof sText !== 'string' )
    {
      sText = $Module.data( 'ContentAddModule' ).getPromptMessage();
    }
    $( '<h2 class="ui-state-highlight promptMessage" />' ).
      text( sText ).
      prependTo( $Module );

    this._loadModule( $Module );
  },
  handleErrorMessages: function( $Module, aErrorInfo )
  {
    var x, oThis = this, oErrorInfo;

    this._loadModule( $Module );

    if( ! ( typeof aErrorInfo === 'object') || ! ( aErrorInfo instanceof Array ) )
    {
      aErrorInfo = $Module.data( 'ContentAddModule' ).getErrorMessages()
    }

    for( x = 0; x < aErrorInfo.length; x++ )
    {
      ( function( oErrorInfo )
      {
        var $container;
        oErrorInfo.displayObject = $( '<h2 class="ui-state-error" />' ).text( oErrorInfo.message );
        
				if ( $('#byop').length !== 0)
				{
          
					$container = $Module.find('.contentadd-module-container-pages-tabs');
          if( $container.length > 0 )
          {
            $container.prepend( oErrorInfo.displayObject );
          }
          else
          {
            $Module.find( 'div.maincontent' ).prepend( oErrorInfo.displayObject );
          }
				}
        else
				{
					$Module.prepend( oErrorInfo.displayObject );
				}
	
        if( typeof oErrorInfo.object !== 'undefined' && oErrorInfo.object !== null )
        {
          $( oErrorInfo.object ).
            bind( 'click.removeErrorMessage', function()
            {
              oThis._removeErrorMessage( oErrorInfo )
            } ).
            focus().
            parent().
              addClass( 'ui-state-error' );
        }
      } )( aErrorInfo[ x ] );
    }
  },
  _removeAllErrorMessages: function()
  {
    this.$BaseInterface.find( 'h2.promptMessage' ).remove();
    this.$BaseInterface.find( 'h2.ui-state-error' ).remove();
  },
  _removeErrorMessage: function( oErrorInfo )
  {
    oErrorInfo.displayObject.remove();
    $( oErrorInfo.object )
      .unbind( 'click.removeErrorMessage' ).
      parent().
        removeClass( 'ui-state-error' );

  },
  _loadModule: function( $Module )
  {
    if( typeof $Module === 'object' && $Module instanceof jQuery )
    {
      this._disableControls();
      this._hideControls();

      this.$CurrentModule = $Module;
      this.$CurrentGroup = this.$CurrentModule.parents( 'div.contentadd-group-container' ).show();
      this.$CurrentFocus = this.$CurrentGroup.parents( 'div.contentadd-focal-container' ).show();

      this.$CurrentFocus.tabs( 'select', this.$CurrentFocus.data( 'tabs' ).panels.index( this.$CurrentModule ) );

      $Module.data( 'ContentAddModule' ).clearPrompt();

      this._setupGroup();

      this._enableControls();
      this._showControls();
      this._hideLoadingMask();

      $(this.$BaseInterface).trigger('camc.loadModuleComplete');
    }
  },
  _setupGroup: function()
  {
    var $CurrentGroup = this.$CurrentGroup,
        aButtons = [];

    $CurrentGroup.find( 'div.contentAddControlButtons > button' ).each( function()
    {
      var $this = $( this ),
          ToolbarProperties = $this.data( 'ToolbarProperties' );

      ToolbarProperties.oControlOptions.sLabel = $this.text();

      aButtons.push( ToolbarProperties );
    } );

    this.$CustomControlContainer.empty();

    var x, oControlOptions, oProcessingOptions, sActionType, sBindType, bShowInToolbar, sSelector, sLabel, oHTMLObject;
    for( x = 0; x < aButtons.length; x++ )
    {
      oControlOptions              = aButtons[ x ].oControlOptions;
      oProcessingOptions           = aButtons[ x ].oProcessingOptions;

      sActionType           = oControlOptions.sActionType;
      sBindType             = oControlOptions.sBindType || 'click';
      bShowInToolbar        = ( oControlOptions.bShowInToolbar == "1" );
      sSelector             = oControlOptions.sSelector;
      sLabel                = oControlOptions.sLabel;

      oHTMLObject = null;

      if( typeof sActionType != 'undefined' && sActionType != null )
      {
        if( bShowInToolbar == "1" )
        {
          oHTMLObject = $( '<button>' ).text( sLabel );
          sBindType = 'click';
          this.$CustomControlContainer.append( oHTMLObject );
        }
        else if( sSelector != '' )
        {
          oHTMLObject = this.$BaseInterface.find( sSelector );
        }

        switch( sActionType.toUpperCase() )
        {
          case 'LOAD_CHILD_MODULE':
            oHTMLObject.bind( sBindType, this._generate_LOAD_CHILD_MODULE( oProcessingOptions ) );
            break;
          case 'FUNCTION':
            oHTMLObject.bind( sBindType, this._generate_FUNCTION( oProcessingOptions ) );
            break;
          case 'SUBMIT':
            oHTMLObject.bind( sBindType, this._generate_SUBMIT( oProcessingOptions, $CurrentGroup.data( 'ModuleGroup' ).getForm() ) );
            break;
        }
      }
    }
  },
  _validateModules: function()
  {
    var oThis = this, returnValue = true;

    this.$CurrentGroup.find( '.contentadd-module-container' ).each( function()
    {
      var $Module = $( this );
      if( ! $Module.data( 'ContentAddModule' ).validate() )
      {
        oThis.handleErrorMessages( $Module );
        returnValue = false;
      }
      return returnValue;
    } );

    return returnValue;
  },
  _displayPromptModules: function()
  {
    var oThis = this, returnValue = true;
    /* TODO Start adding a class to modules which should be prompted.  We'll remove the class when the module has been viewed.
     *      Then we don't need to iterate all modules here, we can just look for an element with the class and prompt on it */
    this.$CurrentGroup.find( '.contentadd-module-container' ).each( function()
    {
      var $Module = $( this );
      if( $Module.data( 'ContentAddModule' ).isPrompt() )
      {
        oThis.handlePrompt( $Module );
        returnValue = false;
      }
      return returnValue;
    } );

    return returnValue;
  },
  _fetchModuleGroup: function ( sURL, oOptions )
  {
    var oThis = this, headerInject, oldBeforeSend, newBeforeSend;

    if( typeof sURL !== 'string' || sURL === '' )
    {
      sURL = this._sInitialModulesURL;
    }

    this._disableControls();

    if( typeof oOptions !== 'object' || oOptions === null )
    {
      oOptions = {};
    }

    if( typeof oOptions == 'undefined' )
    {
      oOptions = {};
    }

    this._hideControls();
    this._showLoadingMask();

    headerInject = function( oXHR )
    {
      oXHR.setRequestHeader( 'X-VNE-Asset-Injection', 'force' );
    };

    if( typeof $.ajaxSettings.beforeSend === 'function' )
    {
      oldBeforeSend = $.ajaxSettings.beforeSend;
      newBeforeSend = function()
      {
        oldBeforeSend.apply( this, arguments );
        headerInject.apply( this, arguments );
      };
    }
    else
    {
      newBeforeSend = headerInject;
    }

    var oXHR = $.ajax( {
      url: sURL,
      type: 'POST',
      dataType: 'html',
      /* TODO replace the below self-executing lambda with jQuery parameterization when jQ 1.4.2 is available */
      data: ( function( oOptions )
      {
        var retval = {};
        var key;

        for( key in  oOptions )
        {
          if( typeof  oOptions[key] == 'object' )
          {
            retval[key] =  JSON.stringify( oOptions[key] );
          }
          else
          {
            retval[key] =  oOptions[key];
          }
        }

        return retval;
      }( oOptions ) ),
      beforeSend: newBeforeSend,
      success: function( sData, sTextStatus )
      {
        oThis._processPage.call( oThis, sData, sTextStatus, oXHR );
      }
    } );
  },
  _processPage: function( sData, sTextStatus, oXHR )
  {
    /*
     *  TODO - look at handling the eventable resource - go to fullscreen once a calnedar is selected
     *  Be sure to pass the initOptions to the full page
     */
    var sContentAddSize = 'MODAL';

    //  check if we have an oXHR object - if so - get its Content Add Size
    if( typeof oXHR !== 'undefined' && oXHR !== null )
    {
      sContentAddSize = oXHR.getResponseHeader( 'X-YBX-ContentAdd-Size' );
    }

    if( this.bModal && sContentAddSize === 'NEWWINDOW' )
    {
      this._transferToNewWindow( sData );
    }
    else
    {
      this._removeOrphanedMethods();

      this._stageGroup( sData, sContentAddSize );
    }
  },
  _stageGroup: function( sHTML, sContentAddSize )
  {
    var oInstance = this;

    this.$StagingArea.html( sHTML );

    if( sContentAddSize === 'MAXIMIZE' )
    {
      this._maximizeInterface();
    }

    $LAB.queue( function()
    {
      oInstance._processStagedGroup.call( oInstance );
      oInstance._loadModule.call( oInstance, oInstance.$CurrentGroup.find( '.contentadd-module-container:first' ) );
    } );

    $LAB.executeQueue();
  },
  _transferToNewWindow: function( sHTML )
  {
    var oOptions = jQuery.extend( true, this.oInitOptions, { sPageData: sHTML } );
    this._launchIntoFullWindow( oOptions );
    this._teardownBaseInterface();
  },
  _processStagedGroup: function()
  {
    var oThis, $form, bFocalBundle, oModuleGroup, $TabsList, iSelectTab;

    oThis = this;
    $form = this.$StagingArea.find( 'form' ).submit( function(){return false;} );

    bFocalBundle = $form.hasClass( 'focalBundle' );

    this._setupFocalContainerForNewGroup( bFocalBundle );

    oModuleGroup = new ContentAddModuleGroup( $form, bFocalBundle );

    $TabsList = this.$CurrentFocus.find( 'ul.contentadd-tab-list' );
    iSelectTab = $TabsList.children().length;

    this.$CurrentGroup = $( '<div class="contentadd-group-container" />' ).
      append( this.$StagingArea.children() ).
      data( 'ModuleGroup', oModuleGroup ).
      find( 'div.contentadd-module-container' ).
        not( 'div.contentadd-module-processed' ).
          each( function( index )
          {
            var $this, oModule;

            $this = $( this );
            if( index === 0 )
            {
              /* When adding a new module group, the first module becomes current */
              oThis.$CurrentModule = $this;
            }

            oModule = $this.data( 'ContentAddModule' );

            /* TODO this is not the preferred method to accomplish the add of a tab, but the preferred method is broken in jQ UI
             * http://forum.jquery.com/topic/tabs-inconsistency-between-build-of-tabs-and-add-of-a-tab
             * http://dev.jqueryui.com/ticket/4578 */
            $TabsList.
              append( '<li><a href="#' + $this.attr( 'id' ) + '" >' + oModule.getTitle() + '</a></li>' );

            $this.addClass( 'contentadd-module-processed' );
          } ).
        end().
      end().
      appendTo( this.$CurrentFocus );

    this._reTabCurrentFocus( iSelectTab );
    this.$CurrentFocus.show();

    this.$StagingArea.empty();
    $(this.$BaseInterface).trigger('camc.stagingComplete').unbind('camc.stagingComplete');
  },
  /* TODO refactor this function to be the handler rather than generate and return one */
  _generate_SUBMIT: function( oProcessingOptions, $form )
  {
    var oThis                 = this;

    var bValidation           = ( oProcessingOptions.bValidation == "1" );
    var sFunctionName         =  oProcessingOptions.sFunctionName;
    var oFunctionOptions      = {};
    oFunctionOptions          = oProcessingOptions.oFunctionOptions || {};

    return ( function()
    {
      if( ! oThis._areControlsDisabled() )
      {
        if( ! bValidation || ( bValidation && oThis._doModuleValidation() ) )
        {
          var oOptions = null;
          if( typeof oFunctionOptions != 'undefined' && oFunctionOptions != null )
          {
            oOptions = oFunctionOptions;
          }

          if( ( typeof sFunctionName === 'undefined' || sFunctionName === null ) || ( window[ sFunctionName ]( oOptions ) ) )
          {
            oThis._toggleSaveSuspend( this );

            var oAjaxSubmitOptions = {
              /* success and error wrapped in lambdas to do needed scope correction due to async code */
              success: function( oData, sTextStatus )
              {
                oThis._onSuccess( oData, sTextStatus );
              },
              error: function( oXMLHttpRequest, sTextStatus, oErrorThrown )
              {
                oThis._onError( oXMLHttpRequest, sTextStatus, oErrorThrown );
              },
              dataType: 'json'
            };

            if (typeof tinyMCE != 'undefined')
            {
              for(var i=0; i<tinyMCE.editors.length; i++)
              {
                if (tinyMCE.editors[i].isDirty())
                {
                  tinyMCE.editors[i].save();
                }
              }
            }

            $form.ajaxSubmit( oAjaxSubmitOptions );
          }
        }
      }
    } );
  },
  /* TODO refactor this function to be the handler rather than generate and return one */
  _generate_FUNCTION: function( oProcessingOptions )
  {
    var oThis              = this;

    var bValidation        = ( oProcessingOptions.bValidation == "1" );
    var bCloseAfterExecute = ( oProcessingOptions.bCloseAfterExecute == "1" );
    var sFunctionName      = oProcessingOptions.sFunctionName;
    var oFunctionOptions   = {};
    oFunctionOptions       = oProcessingOptions.oFunctionOptions || {};

    return  ( function()
    {
      if( !oThis._areControlsDisabled() )
      {
        if( !bValidation || ( bValidation && oThis._doModuleValidation() ) )
        {
          var oOptions = null;
          if( typeof oFunctionOptions != 'undefined' && oFunctionOptions != null )
          {
            oOptions = oFunctionOptions;
          }
          if( typeof sFunctionName === 'function' )
          {
            sFunctionName( oOptions );
          }
          else if( typeof sFunctionName === 'string' && typeof window[ sFunctionName ] === 'function' )
          {
            window[ sFunctionName ]( oOptions );
          }
          if( bCloseAfterExecute == "1" )
          {
            oThis._teardownBaseInterface();
          }
        }
      }
    } );
  },
  /* TODO refactor this function to be the handler rather than generate and return one */
  _generate_LOAD_CHILD_MODULE: function( oProcessingOptions )
  {
    var oThis              = this;

    var sFunctionName      = null;
    var fFunction          = null;
    var bValidation        = ( oProcessingOptions.bValidation == "1" );
    var bCloseAfterExecute = ( oProcessingOptions.bCloseAfterExecute == "1" );
    var oFunctionOptions   = {};
    oFunctionOptions       = oProcessingOptions.oFunctionOptions || {};


    if( typeof oProcessingOptions.sFunctionName !== 'undefined' && oProcessingOptions.sFunctionName !== null && typeof window[ oProcessingOptions.sFunctionName ] === 'function' )
    {
      sFunctionName = oProcessingOptions.sFunctionName;
    }
    else if( typeof oProcessingOptions.fFunction === 'function' && oProcessingOptions.fFunction !== null && typeof oProcessingOptions.fFunction === 'function' )
    {
      fFunction = oProcessingOptions.fFunction;
    }

    return  ( function()
    {
      var oOptions = null,
          oNewModuleOptions = null;

      if( !oThis._areControlsDisabled() )
      {
        if( !bValidation || ( bValidation && oThis._doModuleValidation() ) )
        {
          if( typeof oFunctionOptions != 'undefined' && oFunctionOptions != null )
          {
            oOptions = oFunctionOptions;
          }

          if( fFunction !== null )
          {
            oNewModuleOptions = fFunction( oOptions );
          }
          else if( sFunctionName !== null )
          {
            oNewModuleOptions = window[ sFunctionName ]( oOptions );
          }

          if( oNewModuleOptions !== null )
          {
            oThis._loadChildModule( oNewModuleOptions.sURL, oNewModuleOptions.oOptions );
            if( bCloseAfterExecute )
            {
              oThis._teardownBaseInterface();
            }
          }
        }
      }
    } );
  },
  /* TODO refactor this function to be the handler rather than generate and return one */
  _generate_LAUNCH_FULLSCREEN: function( oProcessingOptions )
  {
    var oThis              = this;

    if( ! oThis.bModal )
    {
      return oThis._generate_LOAD_CHILD_MODULE( oProcessingOptions );
    }

    var bValidation        = ( oProcessingOptions.bValidation == "1" );
    var bCloseAfterExecute = true;
    var sFunctionName      =  oProcessingOptions.sFunctionName;
    var oFunctionOptions   = {};
    oFunctionOptions       = oProcessingOptions.oFunctionOptions || {};

    return ( function()
    {
      if( !oThis._areControlsDisabled() )
      {
        if( !bValidation || ( bValidation && oThis._doModuleValidation() ) )
        {
          /*This assignment will cause oInitOptions to be updated as well since it done by reference, but that's ok because this content add will close*/
          var oOptions = oThis.oInitOptions;
          if( typeof oFunctionOptions != 'undefined' && oFunctionOptions != null )
          {
            /*we are adding oFunctionOptions to oOptions*/
            jQuery.extend( true, oOptions, oFunctionOptions );
          }
          var oNewModuleOptions = window[ sFunctionName ]( oOptions );
          oThis._launchIntoFullWindow( oNewModuleOptions.oOptions );
          oThis._teardownBaseInterface();
        }
      }
    } );
  },
  /* TODO refactor this function to be the handler rather than generate and return one */
  _generate_SUCCESS_TRIGGER: function( oProcessingOptions )
  {
    var oThis = this;

    return function()
    {
      if( typeof window[oProcessingOptions.sFunctionName] === 'function' )
      {
        if( window[oProcessingOptions.sFunctionName]() )
        {
          oThis.callbacks.addEvent( oProcessingOptions.sTriggerName, ContentAddModuleController.callbacks.callbackTypes.success );
        }
        else
        {
          oThis.callbacks.removeEvent( oProcessingOptions.sTriggerName, ContentAddModuleController.callbacks.callbackTypes.success );
        }
      }
    };
  },

  setDialogTitle: function( sTitle )
  {
    if( this.bModal )
    {
      this.$BaseInterface.dialog( 'option', 'title', sTitle );
    }
  }
}

/*
 * Static methods for all content adds.
 */
ContentAddModuleController.getContainer = function( oDomObject )
{
  return $( oDomObject ).parents( 'div.contentadd-instance' );
};
ContentAddModuleController.getInstance = function( oDomObject )
{
  return ContentAddModuleController.getContainer( oDomObject ).data( 'oContentAddInstance' );
};

ContentAddModuleController.getModuleContainer = function( oDomObject )
{
  return $( oDomObject ).parents( 'div.contentadd-module-container' );
};
ContentAddModuleController.getModuleInstance = function( oDomObject )
{
  return ContentAddModuleController.getModuleContainer( oDomObject ).data( 'ContentAddModule' );
};

/*
 * Static property for all content adds to determine if another instance is already open.
 */
ContentAddModuleController.instanceIsOpen = false;

/*
 * Static properties of ContentAddModuleController to store some basic info about callbacks
 */
ContentAddModuleController.callbacks = {
  defaultEvents: {
      error: 'ContentAddError',
      success: 'ContentAddSuccess'
  },
  callbackTypes: {
    error: 0,
    success: 1
  }
};
/*
 * Dynamic properties of ContentAddModuleController to hold instantiated Content Add's callback controls
 */
ContentAddModuleController.callbackManager = function()
{
  var self = this;
  this.bDoDefault = {
    error : true,
    success : true
  }
  this.eventList = {
    error : [],
    success : []
  };
  this.addEvent = function( sEvent,  iType )
  {
    var index;

    if( typeof sEvent === 'object' && typeof sEvent.length === 'number' )
    {
      for( index in sEvent )
      {
        self.addEvent( sEvent[ index ], iType );
      }
    }
    else if( typeof sEvent === 'string' && sEvent !== '' )
    {
      var iterationList, found = false;
      switch( iType )
      {
        case ContentAddModuleController.callbacks.callbackTypes.error:
          iterationList = this.eventList.error;
          break;

        case ContentAddModuleController.callbacks.callbackTypes.success:
          iterationList = this.eventList.success;
          break;
      }
      if( typeof iterationList !== 'undefined' )
      {
        for( index in iterationList )
        {
          if( iterationList[ index ] == sEvent )
          {
            found = true;
            break;
          }
        }
        if( found === false )
        {
          iterationList.push( sEvent );
        }
      }
    }
  };
  this.removeEvent = function( sEvent,  iType )
  {
    var index;

    if( typeof sEvent === 'object' && typeof sEvent.length === 'number' )
    {
      for( index in sEvent )
      {
        self.removeEvent( sEvent[ index ], iType );
      }
    }
    else if( typeof sEvent === 'string' && sEvent !== '' )
    {
      var iterationList;
      switch( iType )
      {
        case ContentAddModuleController.callbacks.callbackTypes.error:
          iterationList = this.eventList.error;
          break;

        case ContentAddModuleController.callbacks.callbackTypes.success:
          iterationList = this.eventList.success;
          break;
      }
      if( typeof iterationList !== 'undefined' )
      {
        for( index in iterationList )
        {
          if( iterationList[ index ] === sEvent )
          {
            iterationList.splice( index, 1 );
          }
        }
      }
    }
  };
  this.run = function( oData, iType, oWindowContext )
  {
    var iterationList, index;

    if( oWindowContext == null || typeof oWindowContext !== 'object' || typeof oWindowContext.document !== 'object' )
    {
      oWindowContext = window;
    }

    switch( iType )
    {
      case ContentAddModuleController.callbacks.callbackTypes.error:
        if( this.bDoDefault.error )
        {
          this.addEvent( ContentAddModuleController.callbacks.defaultEvents.error, iType );
        }
        iterationList = this.eventList.error;
        break;

      case ContentAddModuleController.callbacks.callbackTypes.success:
        if( this.bDoDefault.success )
        {
          this.addEvent( ContentAddModuleController.callbacks.defaultEvents.success, iType );
        }
        iterationList = this.eventList.success;
        break;
    }
    for( index in iterationList )
    {
      if( typeof iterationList[ index ] === 'string' )
      {
        oWindowContext.jQuery( oWindowContext.document ).trigger( iterationList[ index ], oData );
      }
    }
  };
};

