      1 define([
      2     'jquery',
      3     'underscore',
      4     'three'
      5 ], function($, _, THREE) {
      6   /**
      7    *
      8    * @class EmperorViewControllerABC
      9    *
     10    * Initializes an abstract tab. This has to be contained in a DOM object and
     11    * will use the full size of that container.  The title represents the title
     12    * of the jQuery tab.  The description will be used as help text to describe
     13    * the functionality of each subclass tab.
     14    *
     15    * @param {UIState} uiState the shared state object
     16    * @param {Node} container Container node to create the controller in.
     17    * @param {String} title Title of the tab.
     18    * @param {String} description Helper description.
     19    *
     20    * @return {EmperorViewControllerABC} Returns an instance of the
     21    * EmperorViewControllerABC.
     22    * @constructs EmperorViewControllerABC
     23    *
     24    */
     25   function EmperorViewControllerABC(uiState, container, title, description) {
     26     THREE.EventDispatcher.call(this);
     27
     28     /**
     29      * @type {UIState}
     30      * The shared state
     31      */
     32     this.UIState = uiState;
     33
     34     /**
     35      * @type {Node}
     36      * jQuery element for the parent container.
     37      */
     38     this.$container = $(container);
     39     /**
     40      * @type {String}
     41      * Human-readable title of the tab.
     42      */
     43     this.title = title;
     44     /**
     45      * @type {String}
     46      * Human-readable description of the tab.
     47      */
     48     this.description = description;
     49
     50     /**
     51      * @type {Node}
     52      * jQuery element for the canvas, which contains the header and the body.
     53      */
     54     this.$canvas = null;
     55     /**
     56      * @type {Node}
     57      * jQuery element for the body, which contains the lowermost elements
     58      * displayed in tab. This goes below the header.
     59      */
     60     this.$body = null;
     61     /**
     62      * @type {Node}
     63      * jQuery element for the header which contains the uppermost elements
     64      * displayed in a tab.
     65      */
     66     this.$header = null;
     67     /**
     68      * @type {Boolean}
     69      * Indicates whether the tab is front most
     70      * @default false
     71      */
     72     this.active = false;
     73     /**
     74      * @type {String}
     75      * Unique hash identifier for the tab instance.
     76      * @default "EMPtab-xxxxxxx"
     77      */
     78     this.identifier = 'EMPtab-' + Math.round(1000000 * Math.random());
     79     /**
     80      * @type {Boolean}
     81      * Indicates if tab can be accessed.
     82      * @default true
     83      */
     84     this.enabled = true;
     85
     86     if (this.$container.length < 1) {
     87       throw new Error('Emperor requires a valid container, ' +
     88           this.$container + ' does not exist in the DOM.');
     89     }
     90
     91     // the canvas contains both the header and the body, note that for all
     92     // these divs the width should be 100% (whatever we have available), but
     93     // the height is much trickier, see the resize method for more information
     94     this.$canvas = $('<div name="emperor-view-controller-canvas"></div>');
     95     this.$canvas.width('100%');
     96     this.$container.append(this.$canvas);
     97
     98     this.$canvas.width(this.$container.width());
     99     this.$canvas.height(this.$container.height());
    100
    101     // the margin and width properties are set this way to center all the
    102     // contents of the divs themselves, see this SO answer:
    103     // http://stackoverflow.com/a/114549
    104     this.$header = $('<div name="emperor-view-controller-header"></div>');
    105     this.$header.css('margin', '0 auto');
    106     this.$header.css('width', '100%');
    107
    108     this.$body = $('<div name="emperor-view-controller-body"></div>');
    109     this.$body.css('margin', '0 auto');
    110     this.$body.css('width', '100%');
    111
    112     // inherit the size of the container minus the space being used for the
    113     // header
    114     this.$body.height(this.$canvas.height() - this.$header.height());
    115     this.$body.width(this.$canvas.width());
    116
    117     this.$canvas.append(this.$header);
    118     this.$canvas.append(this.$body);
    119
    120     return this;
    121   }
    122   EmperorViewControllerABC.prototype = Object.create(
    123       THREE.EventDispatcher.prototype);
    124   EmperorViewControllerABC.prototype.constructor = THREE.EventDispatcher;
    125
    126   /**
    127    * Sets whether or not elements in the tab can be modified.
    128    *
    129    * @param {Boolean} trulse option to enable elements.
    130    */
    131   EmperorViewControllerABC.prototype.setEnabled = function(trulse) {
    132     if (typeof(trulse) === 'boolean') {
    133       this.enabled = trulse;
    134     }
    135     else {
    136       throw new Error('`trulse` can only be of boolean type');
    137     }
    138   };
    139
    140   /**
    141    * Sets whether or not the tab is visible.
    142    *
    143    * @param {Boolean} trulse option to activate tab
    144    * (i.e. move tab to foreground).
    145    */
    146   EmperorViewControllerABC.prototype.setActive = function(trulse) {
    147     if (this.enabled === true) {
    148       if (typeof(trulse) === 'boolean') {
    149         this.active = trulse;
    150       }
    151       else {
    152         throw new Error('`trulse` can only be of boolean type');
    153       }
    154     }
    155   };
    156
    157   /**
    158    * Resizes the container, note that the body will take whatever space is
    159    * available after considering the size of the header. The header shouldn't
    160    * have height variable objects, once added their height shouldn't really
    161    * change.
    162    *
    163    * @param {Float} width the container width.
    164    * @param {Float} height the container height.
    165    */
    166   EmperorViewControllerABC.prototype.resize = function(width, height) {
    167     // This padding is required in order to make space
    168     // for the horizontal menus
    169     var padding = 10;
    170     this.$canvas.height(height);
    171     this.$canvas.width(width - padding);
    172
    173     this.$header.width(width - padding);
    174
    175     // the body has to account for the size used by the header
    176     this.$body.width(width - padding);
    177     this.$body.height(height - this.$header.height());
    178   };
    179
    180   /**
    181    *
    182    * Converts the current instance into a JSON string.
    183    *
    184    * @return {Object} ready to serialize representation of self.
    185    */
    186   EmperorViewControllerABC.prototype.toJSON = function() {
    187     throw Error('Not implemented');
    188   };
    189
    190   /**
    191    * Decodes JSON string and modifies its own instance variables accordingly.
    192    *
    193    * @param {Object} parsed JSON string representation of an instance.
    194    */
    195   EmperorViewControllerABC.prototype.fromJSON = function(jsonString) {
    196     throw Error('Not implemented');
    197   };
    198
    199   /**
    200    * Writes the current settings to the active decomposition view(s).
    201    * Will be called when the decomposition view is swapped out
    202    * for a different view type.
    203    */
    204   EmperorViewControllerABC.prototype.forceRefresh = function() {
    205     this.fromJSON(this.toJSON());
    206   };
    207
    208   return {'EmperorViewControllerABC': EmperorViewControllerABC};
    209 });
    210