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