1 /*
  2  * 	Copyright (C) 2012-2013 DFKI GmbH
  3  * 	Deutsches Forschungszentrum fuer Kuenstliche Intelligenz
  4  * 	German Research Center for Artificial Intelligence
  5  * 	http://www.dfki.de
  6  * 
  7  * 	Permission is hereby granted, free of charge, to any person obtaining a 
  8  * 	copy of this software and associated documentation files (the 
  9  * 	"Software"), to deal in the Software without restriction, including 
 10  * 	without limitation the rights to use, copy, modify, merge, publish, 
 11  * 	distribute, sublicense, and/or sell copies of the Software, and to 
 12  * 	permit persons to whom the Software is furnished to do so, subject to 
 13  * 	the following conditions:
 14  * 
 15  * 	The above copyright notice and this permission notice shall be included 
 16  * 	in all copies or substantial portions of the Software.
 17  * 
 18  * 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 19  * 	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 20  * 	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 21  * 	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 22  * 	CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 23  * 	TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 24  * 	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 25  */
 26 
 27 
 28 define ( [ 'commonUtils', 'helper', 'logger', 'module' ],
 29 	//this comment is needed by jsdoc2 [copy of comment for: function Controller(...]
 30 	/**
 31 	 * The Controller Class is a kind of interface-class which gives access to the methods of a controller and its helper. <br>
 32 	 * Also holds information about views and partials associated with the controller.
 33 	 * 
 34 	 * @param {String} name Name of the Controller
 35 	 * @param {Object} jsonDef Information about the controllers views and partials
 36 	 * 
 37 	 * @name Controller
 38 	 * @class
 39 	 */
 40 	function (
 41 			commonUtils, Helper, Logger, module
 42 ){
 43 	
 44 	/**
 45 	 * @private
 46 	 * @type Logger
 47 	 * @memberOf Controller#
 48 	 * @see mmir.Logging#create
 49 	 */
 50 	var logger = Logger.create(module);
 51 	
 52 	//the next comment enables JSDoc2 to map all functions etc. to the correct class description
 53 	/** @scope Controller.prototype */
 54 	
 55 	//set to @ignore in order to avoid doc-duplication in jsdoc3
 56 	/**
 57 	 * @ignore
 58 	 * 
 59 	 * The Controller Class is a kind of interface-class which gives access to the methods of a controller and its helper. <br>
 60 	 * Also holds information about views and partials associated with the controller.
 61 	 * 
 62 	 * @constructs Controller
 63 	 * @param {String} name Name of the Controller
 64 	 * @param {Object} jsonDef Information about the controllers views and partials
 65 	 */
 66 	function Controller(name, jsonDef){
 67 //	    console.log("controller name " + name);
 68 	    /**
 69 	     * The definition of the controller object, containing all properties and functions of the controller.<br>
 70 	     * A method of the controller can be called via:
 71 
 72 		this.script.methodController(parameter);
 73 
 74 	     * 
 75 	     * @type Object
 76 	     * @public
 77 	     */
 78 		// this can only be invoked, if a function with the name "name" exists
 79 	    this.script = new window[name]();
 80 
 81 	    /**
 82 	     * The json definition of the views and partials associated with the controller. Also contains paths to controller and its views/partials. 
 83 	     * 
 84 	     * @type Object
 85 	     * @public
 86 	     */
 87 	    this.def = jsonDef;//new JPath(jsonDef);
 88 
 89 	    /**
 90 	     * The name of the controller. 
 91 	     * 
 92 	     * @type String
 93 	     * @public
 94 	     */
 95 	    this.name = name;
 96 	    
 97 	    var viewDefs = this.def.views;//.query('views');
 98 
 99 	    /**
100 	     * An array holding the names of all views associated with the controller.  
101 	     * 
102 	     * @type Array<String>
103 	     * @public
104 	     */
105 	    this.views = new Array();
106 	    this.parseViews(viewDefs);
107 
108 	    // parsing the partials and saving the names in an array
109 	    var partialDefs = this.def.partials;//.query('partials');
110 	    
111 
112 	    /**
113 	     * An array holding the names of all partials associated with the controller.  
114 	     * 
115 	     * @type Array<String>
116 	     * @public
117 	     */
118 	    this.partials = new Array();
119 	    this.parsePartials(partialDefs);
120 
121 
122 	    /**
123 	     * The instance of the with the controller associated helper.  
124 	     * 
125 	     * @type Helper
126 	     * @public
127 	     */
128 	    this.helper;
129 	    
130 	    /**
131 	     * The layout (info) for this controller (if undefined, the default layout should be used)
132 	     * @type Object
133 	     */
134 	    this.layout = this.def.layout;
135 	}
136 
137 
138 	/**
139 	 * This function loads the helper of the controller - if it exists.
140 	 * 
141 	 * @function
142 	 * @param {String} name Name of the Helper to be loaded
143 	 * @param {String} helperPath Path to the helper file to load  
144 	 * @public
145 	 */
146 	Controller.prototype.loadHelper = function(name, helperPath){
147 		var self = this;
148 		
149 		//TODO move check of helper existance to Controller.foundControllersCallBack ?
150 
151 		//determine if there is a helper for the controller:
152 		var path = helperPath;
153 		var fileName = path;
154 		var lastPathSeparatorIndex = path.lastIndexOf('/');
155 		if(lastPathSeparatorIndex !== -1){
156 			path = path.substring(0,lastPathSeparatorIndex);
157 			fileName = fileName.substring( lastPathSeparatorIndex + 1 );
158 		}
159 		//get contents of the helper directory:
160 		var dirContents = commonUtils.getDirectoryContents(path);
161 		if(!dirContents){
162 			logger.warn('Could not determine contents for directory "'+path+'"');
163 			return; ////////////////////// EARLY EXIT //////////////////////////////
164 		}
165 		else if(! commonUtils.isArray(dirContents) || dirContents.length < 1){
166 			logger.warn('Invalid information for contents of directory "'+path+'": '+dirContents);
167 			return; ////////////////////// EARLY EXIT //////////////////////////////
168 		}
169 		
170 		//check, if there is an implementation file for this helper:
171 		var helperIsSpecified = false;
172 		for(var i=0, size= dirContents.length; i < size; ++i){
173 			if(dirContents[i] === fileName){
174 				helperIsSpecified = true;
175 				break;
176 			}
177 		}
178 		
179 		if( ! helperIsSpecified){
180 			if(logger.isVerbose()) logger.v("[HELPER] no helper available (not implemented) at '"+ helperPath+"'");
181 			return; ////////////////////// EARLY EXIT //////////////////////////////
182 		}
183 		
184 		//if there is a file: load the helper
185 		commonUtils.getLocalScript(helperPath, function(data, textStatus, jqxhr){
186 			
187 				if(logger.isVerbose()) logger.v("[HELPER] load "+ helperPath);//debug
188 				
189 				self.helper =   new Helper(self, name);//new window["GoogleMapHelper"]();
190 			},
191 			function(exception) {
192 				// print out an error message
193 				logger.error("[WARN] Could not load helper -> '"+ helperPath + "'", exception); //failure
194 			}
195 		);
196 	};
197 
198 
199 	/**
200 	 * This function performs an action of a controller - which is represented by this instance of the Controller <br>
201 	 * class - by calling the method from the corresponding controller, e.g. assets/www/controllers/application.js   
202 	 * 
203 	 * @function
204 	 * @param {String} actionName Name of the method to be executed
205 	 * @param {Object} data Data to pass to the method of the controller as argument
206 	 * @returns {Object} The return value of the executed method 
207 	 * @public
208 	 */
209 	Controller.prototype.perform = function(actionName, data){
210 		
211 		if(logger.isVerbose()) logger.v("should perform '" + actionName + "' of '" + this.name + "'"+ ((typeof data !== 'undefined' && data !== null)? " with data: "+JSON.stringify(data): ""));//debug
212 		
213 	    if(arguments.length > 2){
214 	    	return this.script[actionName](data, arguments[2]);
215 		}
216 		else {
217 			return this.script[actionName](data);
218 		}
219 	};
220 
221 	/**
222 	 * 
223 	 * This function performs an action of a controller, but only if an action with this name exists; otherwise nothing is done.
224 	 * 
225 	 * In difference to perform(..), the method does not trigger an ERROR, if the action does not exist / is not implemented.
226 	 * As a consequence, this method refers to "optionally" implemented functions, whereas perform(..) refers to mandatory functions.
227 	 * 
228 	 * @function
229 	 * @param {String} actionName Name of the method to be executed
230 	 * @param {Object} data Data to pass to the method of the controller as argument
231 	 * @returns {Object} The return value of the executed method 
232 	 * @public
233 	 */
234 	Controller.prototype.performIfPresent = function(actionName, data){
235 		if(typeof this.script[actionName] === 'function'){
236 		    
237 			if(logger.isVerbose()) logger.v("performing '" + actionName + "' of '" + this.name + "'"+ ((typeof data !== 'undefined' && data !== null)? " with data: "+JSON.stringify(data): ""));//debug
238 		    
239 		    return this.perform.apply(this, arguments);
240 		    
241 		} else if(typeof this.script[actionName] !== 'undefined'){
242 			if(logger.isVerbose()) logger.info("could not perform '" + actionName + "' of '" + this.name + "'"+ ((typeof data !== 'undefined' && data !== null)? " with data: "+JSON.stringify(data): "")+": no function ("+typeof this.script[actionName]+")");//debug
243 		} else {
244 			if(logger.isVerbose()) logger.debug("could not perform '" + actionName + "' of '" + this.name + "'"+ ((typeof data !== 'undefined' && data !== null)? " with data: "+JSON.stringify(data): "")+": not implemented (undefined)");//debug
245 		}
246 	};
247 
248 
249 	/**
250 	 * This function performs a helper action of a controller by calling the appropriate method<br>
251 	 * {@link Helper#perform} of the instance of the helper class associated with the controller.
252 	 * 
253 	 * @function
254 	 * @param {String} actionName Name of the helper method to be executed
255 	 * @param {Object} data Data to pass to the helper method as argument
256 	 * @returns {Object} The return value of the executed method 
257 	 * @public
258 	 */
259 	Controller.prototype.performHelper = function(actionName, data){
260 		if(arguments.length > 2){
261 			return this.helper.perform(actionName, data, arguments[2]);
262 		}
263 		else {
264 			return this.helper.perform(actionName, data);
265 		}
266 	};
267 
268 
269 	/**
270 	 * Returns the helper of the controller instance.
271 	 * 
272 	 * @function
273 	 * @returns {Object} The helper instance 
274 	 * @public
275 	 */
276 	Controller.prototype.getHelper = function(){
277 		return this.helper;
278 	};
279 
280 
281 	/**
282 	 * Stores all names of the views of the controller by iterating over the array of the views definition.<br>
283 	 * This function is called by the constructor of the {@link mmir.Controller} class.
284 	 * 
285 	 * @function
286 	 * @param {Array} viewDefs Array of the json-definition of the controllers views - containing name of the views and their corresponding path to the js-files
287 	 * @public
288 	 */
289 	Controller.prototype.parseViews = function(viewDefs){
290 		
291 		for(var i=0, size = viewDefs.length; i < size; ++i){
292 			this.views.push(viewDefs[i].name);
293 		}
294 		
295 	};
296 
297 
298 	/**
299 	 * Stores all names of the partials of the controller by iterating over the array of the partials definition.<br>
300 	 * This function is called by the constructor of the {@link mmir.Controller} class.
301 	 * 
302 	 * @function
303 	 * @param {Array} partialDefs Array of the json-definition of the controllers partials - containing name of the partials and their corresponding path to the js-files
304 	 * @public
305 	 */
306 	Controller.prototype.parsePartials = function(partialDefs){
307 	    
308 	    for(var i=0, size = partialDefs.length; i < size; ++i){
309 			this.partials.push(partialDefs[i].name);
310 		}
311 	    
312 	};
313 
314 
315 	/**
316 	 * Returns the view names for the controller instance.
317 	 * 
318 	 * @function
319 	 * @returns {Array<String>} An array of the controllers views 
320 	 * @public
321 	 */
322 	Controller.prototype.getViewNames = function(){
323 		return this.views;
324 	};
325 
326 	/**
327 	 * Returns the view names for the controller instance.
328 	 * 
329 	 * TODO should this be private/hidden? -> provides "internal" JSON-details object (used in PresentationManager)
330 	 * 
331 	 * Each info object has properties:
332 	 * {String} name
333 	 * {String} path
334 	 * 
335 	 * @function
336 	 * @returns {Array<Object>} An array of the controllers views 
337 	 * @public
338 	 */
339 	Controller.prototype.getViews = function(){
340 		return this.def.views;
341 	};
342 
343 	/**
344 	 * Returns the partial names for the controller instance.
345 	 * 
346 	 * @function
347 	 * @returns {Array<String>} An array of the controllers partials 
348 	 * @public
349 	 */
350 	Controller.prototype.getPartialNames = function(){
351 	    return this.partials;
352 	};
353 
354 	/**
355 	 * Returns the partial info object for the controller instance.
356 	 * 
357 	 * TODO should this be private/hidden? -> provides "internal" JSON-details object (used in PresentationManager)
358 	 * 
359 	 * Each info object has properties:
360 	 * {String} name
361 	 * {String} path
362 	 * 
363 	 * @function
364 	 * @returns {Array<Object>} An array of the controllers partials 
365 	 * @public
366 	 */
367 	Controller.prototype.getPartials = function(){
368 	    return this.def.partials;
369 	};
370 
371 	/**
372 	 * Returns the layout of the controller instance.
373 	 * 
374 	 * If undefined, the default layout should be used.
375 	 * 
376 	 * TODO should this be private/hidden? -> provides "internal" JSON-details object (used in PresentationManager)
377 	 * 
378 	 * The info object has properties:
379 	 * {String} name
380 	 * {String} path
381 	 * {String} fileName
382 	 * 
383 	 * @function
384 	 * @returns {Object} The controller's layout (may be undefined) 
385 	 * @public
386 	 */
387 	Controller.prototype.getLayout = function(){
388 	    return this.layout;
389 	};
390 
391 	/**
392 	 * Returns the layout name for the controller instance.
393 	 * 
394 	 * This is equal to the controller name.
395 	 * 
396 	 * If undefined, the default layout should be used.
397 	 * 
398 	 * @function
399 	 * @returns {String} The controller's layout name (may be undefined) 
400 	 * @public
401 	 */
402 	Controller.prototype.getLayoutName = function(){
403 	    return this.layout? this.layout.name : this.layout;
404 	};
405 
406 	/**
407 	 * Returns the name of the controller instance.
408 	 * 
409 	 * @function
410 	 * @returns {String} The name of the controller 
411 	 * @public
412 	 */
413 	Controller.prototype.getName = function(){
414 	    return this.name;
415 	};
416 
417 	return Controller;
418 	
419 });
420