1 
  2 
  3 
  4 define(['jquery', 'parserModule'],
  5 /**
  6  * Extends the parser-module with helper functions for
  7  * storing/restoring compiled templates (eHTML -> layout, view, partial etc)
  8  *
  9  * Dependencies:
 10  * 
 11  *  @requires jQuery.extend 
 12  *  
 13  *  alternatively: set <code>mmir.parser.CLASS_EXTENDER</code> with an object that 
 14  *  exposes a function <tt>extend(obj1,obj1)</tt>, i.e.
 15  *  
 16  *  <code>mmir.parser.CLASS_EXTENDER.extend(obj1, obj2)</code>
 17  * 
 18  * @class
 19  * @name StorageUtils
 20  * @memberOf mmir.parser
 21  * 
 22  */		
 23 function($, parser){
 24 
 25 /**
 26  * @public
 27  * @constant
 28  * @memberOf mmir.parser
 29  */
 30 var STORAGE_FILE_FORMAT_NUMBER = 2;
 31 parser.STORAGE_FILE_FORMAT_NUMBER = STORAGE_FILE_FORMAT_NUMBER;
 32 
 33 /**
 34  * Creates the appropriate object from a JSON-like <tt>storedObject</tt>.
 35  * 
 36  * <p>
 37  * NOTE that in difference to a real JSON object, the <tt>storedObject</tt> 
 38  * may contain function definitions.
 39  * 
 40  * <p>
 41  * The storedObject must have a String property <strong>classConstructor</strong>
 42  * <ul>
 43  * 	<li>that must correspond to a constructor function (which will be invoked with <tt>new</tt>)</li>
 44  * 	<li>the constructor function must be invokable without parameters</li>
 45  * 	<li>the constructor function must be accessable from the global namespace
 46  * 		(or <tt>classConstuctor</tt> must contain the code for retrieving the constructor
 47  * 		 function from the global namespace)</li>
 48  * </ul>
 49  * 
 50  * 
 51  * <p>
 52  * If <tt>storedObject</tt> contains a function <tt>init</tt>, then this function will be invoked
 53  * before returning the new newly created object.
 54  * 
 55  * 
 56  * @function
 57  * @static
 58  * @public
 59  * @memberOf mmir.parser
 60  * 
 61  * @param {Object} storedObject 
 62  * 				    a JSON-like object with fields and functions (which will be transfered to the returned object).
 63  * @param {Boolean} [isTriggerPublish] OPTIONAL
 64  * 					if <code>true</code> then the restore function call
 65  * 					<code>initPublish()</code> on the restored object before returning.
 66  * 					This should only be <code>true</code> for the root-object
 67  * 					(e.g. the View-object or Partial-object; nested objects should NOT invoke
 68  * 					 restoreObject() with this argument set to true).
 69  * @param {Number} [fileFormatNo] OPTIONAL
 70  * 					NOTE: if argument <code>isTriggerPublish</code> was used with value <code>true</code>,
 71  * 						  then this argument MUST be used too!
 72  * 					If the number given does not match {@link parser.STORAGE_FILE_FORMAT_NUMBER}
 73  * 					the file format is assumed to be out-dated and an Error will be thrown.
 74  * @returns {Object} 
 75  *          an new instance created with the constructor <tt>classConstructor</tt> and
 76  * 			set with all properties (fields and functions) from <tt>storedObject</tt>.
 77  * 
 78  * @throws Error if <code>fileFormatNo</code> does not match STORAGE_FILE_FORMAT_NUMBER.
 79  * 
 80  */
 81 function restoreObject(storedObject, isTriggerPublish, fileFormatNo){
 82 	
 83 	if(isTriggerPublish && fileFormatNo != parser.STORAGE_FILE_FORMAT_NUMBER){
 84 		
 85 		throw new Error('Compiled template file has wrong format: need file with format version '
 86 				+ parser.STORAGE_FILE_FORMAT_NUMBER +', but got: '+ fileFormatNo
 87 				+ '. Please re-compile views / templates.'
 88 		);
 89 		
 90 	}
 91 	
 92 	var classExtender;
 93 	if(parser.CLASS_EXTENDER && typeof parser.CLASS_EXTENDER.extend === 'function'){
 94 		classExtender = parser.CLASS_EXTENDER;
 95 	}
 96 	else {
 97 		classExtender = $;
 98 	}
 99 	
100 //	//NOTE: classConstructor contains a list of Strings:
101 //	//       * the constructor-function is either in global namespace
102 //	//         e.g. "View" -> then the list contains exactly one entry ["View"]
103 //	//
104 //	//       * if the constructor-function is in a sub-namespace (e.g. "sub-module")
105 //	//         then the list contains the "path" to the constructorf-function
106 //	//         starting with the module that is in the global namespace
107 //	//         e.g. "mobileDS.parser.ParsingResult" -> ["mobileDS, "parser", "ParsingResult"]
108 //	//
109 //	var constructor = window[storedObject.classConstructor[0]];//FIXME need to convert this to require
110 //	for(var i=1, size = storedObject.classConstructor.length; i < size; ++i){
111 //		constructor = constructor[storedObject.classConstructor[i]];
112 //	}
113 	
114 	//requirejs version (for this to work, all Classe (i.e. JS-files) need to already have been loaded & required!)
115 	var constructor = require(storedObject.classConstructor);
116 	
117 	var obj = classExtender.extend( new constructor(), storedObject);
118 	if(typeof obj.init === 'function'){
119 		obj.init();
120 	}
121 	
122 	if(isTriggerPublish && typeof obj.initPublish === 'function'){
123 		obj.initPublish();
124 	}
125 	
126 	return obj;
127 };
128 parser.restoreObject = restoreObject;
129 
130 /**
131  * Creates String-representations (JSON-like) for the specified properties and appends them to the StringBuffer.
132  * 
133  * <p>
134  * This function iterates over the Array <tt>propertyNames</tt>:
135  * If a property with that name exists in <tt>obj</tt>, a JSON-like String-representation is generated
136  * and appended at the end of the array <tt>stringBuffer</tt>.
137  * <br>
138  * Multiple representations are separated by comma entry <code>','</code> in <tt>stringBuffer</tt>.
139  * <br>
140  * The last entry in <tt>stringBuffer</tt> is a comma entry <code>','</code> if at least one property 
141  * entry was inserted in <tt>stringBuffer</tt>.
142  * 
143  * <p>
144  * NOTE that the String-representation inserted into <tt>stringBuffer</tt> may not have a 1:1 correspondence
145  * with properties (only the last entry is guaranteed to be <code>','</code>, if a property was inserted).
146  * <br>
147  * For pratical use, the returned (or modified) <tt>stringBuffer</tt> should be converted into a String
148  * e.g. by <code>stringBuffer.join('')</code>.
149  * 
150  * 
151  * @function
152  * @static
153  * @public
154  * @memberOf mmir.parser
155  * 
156  * @param {Object} obj 
157  * 				the object, that contains the properties for which String representations should be generated.
158  * @param {Array<String>} propertyNames 
159  * 				the names of the properties, for which String-representations should be generated.
160  * @param {Array<String>} stringBuffer 
161  * 				the buffer: String-representations will be appended as entries at the end of the buffer
162  * @param {String} [propertyNamePostfix] 
163  * 				OPTIONAL if present, this postfix will be appended to each property name, before processing it.
164  * 				This is a convenience method, e.g. if all properties in <tt>propertyNames</tt> should end with
165  * 				the same String / postfix.
166  * @param {Function} [valueFunc] 
167  * 				OPTIONAL by default, value representations are generated using the <code>JSON.stringify</code>
168  * 				function. If instead this argument is present, this function will be invoked for creating
169  * 				the string representation of the property-value.
170  * 				The function signature is <code>valueFunc(propertyName : String, propertyValue : Object) : String</code>.
171  * 				If the function returns <code>void</code>, then the corresponding property will not be added/stringified.
172  * 
173  * @returns {Array<String>} the modified <tt>stringBuffer</tt>
174  * 
175  * @requires JSON.stringify
176  * 
177  * @example
178  * 
179  * var obj = {
180  * 	some: "properties",
181  * 	including: function(arg1,arg2){ return 'functions' }
182  * };
183  * 
184  * var sb = mobileDS.parser.appendStringified(obj, ['some'], []);
185  * var str = sb.join(',');
186  * //str will be: "some:\"properties\","
187  * 
188  */
189 function appendStringified(obj, propertyNames, stringBuffer, propertyNamePostfix, valueFunc){
190 	
191 	//"shift" arguments, if necessary
192 	if(typeof propertyNamePostfix === 'function' && ! valueFunc){
193 		valueFunc = propertyNamePostfix;
194 		propertyNamePostfix = null;
195 	}
196 	
197 	var prop, val;
198 	for(var i=0, size = propertyNames.length; i < size; ++i){
199 		prop = propertyNames[i];
200 		
201 		if(propertyNamePostfix){
202 			prop += propertyNamePostfix;
203 		}
204 
205 		if(typeof obj[prop] === 'undefined'){
206 			continue;
207 		}
208 		
209 		
210 		if(valueFunc){
211 			val = valueFunc(prop, obj[prop]);
212 		}
213 		else {
214 			val = JSON.stringify(obj[prop]);
215 		}
216 		
217 		if(typeof val === 'undefined'){
218 			continue;
219 		}
220 		
221 		stringBuffer.push( prop );
222 		stringBuffer.push( ':' );
223 		stringBuffer.push( val );
224 
225 		stringBuffer.push( ',' );
226 	}
227 	
228 	return stringBuffer;
229 };
230 parser.appendStringified = appendStringified;
231 
232 return parser;
233 
234 });//END: define(..., function(){