define(['mmirf/util/extend', 'mmirf/parserModule', 'mmirf/codeGenUtils', 'require'],
/**
* Extends the parser-module with helper functions for
* storing/restoring compiled templates (eHTML -> layout, view, partial etc)
*
* Dependencies:
*
* alternatively: set <code>mmir.parser.CLASS_EXTENDER</code> with an object that
* exposes a function <tt>extend(obj1,obj1)</tt>, i.e.
*
* <code>mmir.parser.CLASS_EXTENDER.extend(obj1, obj2)</code>
*
* @class
* @name StorageUtils
* @memberOf mmir.parser
* @hideconstructor
*
*/
function(extend, parser, codeGenUtils, require){
/**
* @public
* @constant
* @memberOf mmir.parser
*/
var STORAGE_FILE_FORMAT_NUMBER = 4;
parser.STORAGE_FILE_FORMAT_NUMBER = STORAGE_FILE_FORMAT_NUMBER;
/**
*
* Prefix for wrapping persisted objects:
*
* <ul>
* <li> wraps code into a closure
* </li><li> makes global namespace available as variable <code>global</code> (for setting global namespace)
* </li><li> makes mmirf/core available as variable <code>mmir</code> (if mmirf/core is present in global namespace)
* </li><li> makes mmirf/core's require function available as <code>require</code> (if mmirf/core is present and has require function)
* </ul>
*
* @public
* @function
* @memberOf mmir.parser
*
* @param {Boolean} [disableStrictMode] OPTIONAL disable JavaScript strict mode in the generated view code
* @returns {String} the prefix code for generated grammars (i.e. prepend to generated grammar code)
*
* @see mmir.tools.CodeGenUtils#getCodeWrapPrefix
*/
parser.getCodeWrapPrefix = function(disableStrictMode){
return codeGenUtils.getCodeWrapPrefix(disableStrictMode);
};
/**
* Suffix for wrapping persisted objects:
*
* <ul>
* <li> sets global namespace to <code>window</code> (browser), <code>self</code> (webworker) etc
* </li>
* </ul>
*
* @public
* @function
* @memberOf mmir.parser
*
* @see mmir.tools.CodeGenUtils#getCodeWrapSuffix
*/
var STORAGE_CODE_WRAP_SUFFIX = '\n})(typeof window !== "undefined" ? window : typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this);';
parser.getCodeWrapSuffix = function(){
return codeGenUtils.getCodeWrapSuffix()
};
/**
* Creates the appropriate object from a JSON-like <tt>storedObject</tt>.
*
* <p>
* NOTE that in difference to a real JSON object, the <tt>storedObject</tt>
* may contain function definitions.
*
* <p>
* The storedObject must have a String property <strong>classConstructor</strong> (type <code>String</code>)
* <ul>
* <li>that must correspond to a require'able module that represents a constructor function (which will be invoked with <tt>new</tt>)</li>
* <li>the require'able module must be represents a constructor function (which will be invoked with <tt>new</tt>)</li>
* <li>the constructor function must be invokable without parameters</li>
* </ul>
*
*
* <p>
* If <tt>storedObject</tt> contains a function <tt>init</tt>, then this function will be invoked
* before returning the new newly created object.
*
*
* @function
* @static
* @public
* @memberOf mmir.parser
*
* @param {Object} storedObject
* a JSON-like object with fields and functions (which will be transfered to the returned object).
* @param {Boolean} [isTriggerPublish] OPTIONAL
* if <code>true</code> then the restore function call
* <code>initPublish()</code> on the restored object before returning.
* This should only be <code>true</code> for the root-object
* (e.g. the View-object or Partial-object; nested objects should NOT invoke
* restoreObject() with this argument set to true).
* @param {Number} [fileFormatNo] OPTIONAL
* NOTE: if argument <code>isTriggerPublish</code> was used with value <code>true</code>,
* then this argument MUST be used too!
* If the number given does not match {@link parser.STORAGE_FILE_FORMAT_NUMBER}
* the file format is assumed to be out-dated and an Error will be thrown.
* @returns {Object}
* an new instance created with the constructor <tt>classConstructor</tt> and
* set with all properties (fields and functions) from <tt>storedObject</tt>.
*
* @throws Error if <code>fileFormatNo</code> does not match STORAGE_FILE_FORMAT_NUMBER.
*
*/
function restoreObject(storedObject, isTriggerPublish, fileFormatNo){
if(isTriggerPublish && fileFormatNo != parser.STORAGE_FILE_FORMAT_NUMBER){
throw new Error('Compiled template file has wrong format: need file with format version '
+ parser.STORAGE_FILE_FORMAT_NUMBER +', but got: '+ fileFormatNo
+ '. Please re-compile views / templates.'
);
}
var classExtender = {};
if(parser.CLASS_EXTENDER && typeof parser.CLASS_EXTENDER.extend === 'function'){
classExtender = parser.CLASS_EXTENDER;
}
else {
classExtender = {extend: extend};
}
//NOTE: for require-ing to work, all Classes (i.e. JS-files) need to already have been loaded & required (i.e. "async-required" once before)
var constructor = typeof WEBPACK_BUILD !== 'undefined' && WEBPACK_BUILD? __webpack_require__(storedObject.classConstructor) : require(storedObject.classConstructor);
var obj = classExtender.extend( new constructor(), storedObject);
if(typeof obj.init === 'function'){
obj.init();
}
if(isTriggerPublish && typeof obj.initPublish === 'function'){
obj.initPublish();
}
return obj;
};
parser.restoreObject = restoreObject;
/**
* Creates String-representations (JSON-like) for the specified properties and appends them to the StringBuffer.
*
* <p>
* This function iterates over the Array <tt>propertyNames</tt>:
* If a property with that name exists in <tt>obj</tt>, a JSON-like String-representation is generated
* and appended at the end of the array <tt>stringBuffer</tt>.
* <br>
* Multiple representations are separated by comma entry <code>','</code> in <tt>stringBuffer</tt>.
* <br>
* The last entry in <tt>stringBuffer</tt> is a comma entry <code>','</code> if at least one property
* entry was inserted in <tt>stringBuffer</tt>.
*
* <p>
* NOTE that the String-representation inserted into <tt>stringBuffer</tt> may not have a 1:1 correspondence
* with properties (only the last entry is guaranteed to be <code>','</code>, if a property was inserted).
* <br>
* For pratical use, the returned (or modified) <tt>stringBuffer</tt> should be converted into a String
* e.g. by <code>stringBuffer.join('')</code>.
*
*
* @function
* @static
* @public
* @memberOf mmir.parser
*
* @param {Object} obj
* the object, that contains the properties for which String representations should be generated.
* @param {Array<String>} propertyNames
* the names of the properties, for which String-representations should be generated.
* @param {Array<String>} stringBuffer
* the buffer: String-representations will be appended as entries at the end of the buffer
* @param {String} [propertyNamePostfix]
* OPTIONAL if present, this postfix will be appended to each property name, before processing it.
* This is a convenience method, e.g. if all properties in <tt>propertyNames</tt> should end with
* the same String / postfix.
* @param {Function} [valueFunc]
* OPTIONAL by default, value representations are generated using the <code>JSON.stringify</code>
* function. If instead this argument is present, this function will be invoked for creating
* the string representation of the property-value.
* The function signature is <code>valueFunc(propertyName : String, propertyValue : Object) : String</code>.
* If the function returns <code>void</code>, then the corresponding property will not be added/stringified.
*
* @returns {Array<String>} the modified <tt>stringBuffer</tt>
*
* @requires JSON.stringify
*
* @example
*
* var obj = {
* some: "properties",
* including: function(arg1,arg2){ return 'functions' }
* };
*
* var sb = mobileDS.parser.appendStringified(obj, ['some'], []);
* var str = sb.join(',');
* //str will be: "some:\"properties\","
*
*/
function appendStringified(obj, propertyNames, stringBuffer, propertyNamePostfix, valueFunc){
//"shift" arguments, if necessary
if(typeof propertyNamePostfix === 'function' && ! valueFunc){
valueFunc = propertyNamePostfix;
propertyNamePostfix = null;
}
var prop, val;
for(var i=0, size = propertyNames.length; i < size; ++i){
prop = propertyNames[i];
if(propertyNamePostfix){
prop += propertyNamePostfix;
}
if(typeof obj[prop] === 'undefined'){
continue;
}
if(valueFunc){
val = valueFunc(prop, obj[prop]);
}
else {
val = JSON.stringify(obj[prop]);
}
if(typeof val === 'undefined'){
continue;
}
stringBuffer.push( prop );
stringBuffer.push( ':' );
stringBuffer.push( val );
stringBuffer.push( ',' );
}
return stringBuffer;
};
parser.appendStringified = appendStringified;
return parser;
});//END: define(..., function(){