define (
['mmirf/commonUtils','mmirf/viewConstants','mmirf/yield','mmirf/storageUtils','mmirf/contentElement','require' ],
function(
commonUtils, ViewConstants, YieldDeclaration, storageUtils, ContentElement, require
) {
//set to @ignore in order to avoid doc-duplication in jsdoc3
/**
* The Layout class
* The constructor parses the layout and divides them into containers (headerContents, bodyContents, dialogsContents).
*
* @class
* @name Layout
* @memberOf mmir.view
* @param {String} name
* Name of the Layout (usually the same name as the Layout's controller).
* @param {String} definition
* Layout description, i.e. the raw template code that will be processed.
* May be empty: in this case the processed contents must be
* added manually (cf. parser.StorageUtils)
* @param {Boolean} [remote] if the layout refers to a remote resource (DEFAULT: false)
* @param {Boolean} [ignoreMissingBody] if parsing should ignore missing BODY tag (DEFAULT: false)
*
* @requires if param <code>definition</code> is NOT empty: parser.RenderUtils (must be loaded beforehand via <code>require(["mmirf/renderUtils"]...</code>)
*
* @requires if param <code>definition</code> is NOT empty: parser.ParseUtils (must be loaded beforehand via <code>require(["mmirf/parseUtils"]...</code>)
*/
function Layout(name, definition, remote, ignoreMissingBody){
// console.log("[Layout] initialize '"+name+"'.");
//FIXME MODIFICATIONS for "remote content layout object":
/**
* if layout is a remote resouce (will mark script- etc. tags accordingly)
* @memberOf mmir.view.Layout#
* @member remoteaccess
* @type {Boolean}
*/
this.remoteaccess = false;
if ((typeof remote !== 'undefined') && (remote == true)){
this.remoteaccess = true;
}
/**
* The definition string of the layout (ehtml-format, taken from assets/www/views/layout/*.ehtml)
*
* @type Object
* @public
* @memberOf mmir.view.Layout#
* @member def
*/
this.def = definition? definition.replace(commonUtils.regexHTMLComment, '') : definition;//remove HTML comments!
/**
* The name of the layout.
*
* @type String
* @public
* @memberOf mmir.view.Layout#
* @member name
*/
this.name = name;
/**
* This variable holds the contents of the header part of the layout.
*
* @type String
* @public
* @deprecated unused
* @memberOf mmir.view.Layout#
* @member headerContents
*/
this.headerContents = '';
/**
* List for extracted & parsed SCRIPT, LINK and STYLE tags
*
* @type Array<mmir.view.Layout.TagElement>
* @public
* @memberOf mmir.view.Layout#
* @member headerElements
*/
this.headerElements = [];
/**
* The page / layout title
*
* Will be extracted from <em>definition</em>'s TITLE-tag, if present.
*
* @type String
* @public
* @memberOf mmir.view.Layout#
* @member title
*/
this.title = '';
/**
* This variable holds the contents of the body part of the layout.
*
* @type String
* @public
* @deprecated unused
* @memberOf mmir.view.Layout#
* @member bodyContents
*/
this.bodyContents = "";
/**
* This variable holds the contents of the dialogs part of the layout.
*
* @type String
* @public
* @memberOf mmir.view.Layout#
* @member dialogsContents
*/
this.dialogsContents = '';
/**
* The (parsed) content for the body-container.
*
* @type ContentElement
* @public
* @memberOf mmir.view.Layout#
* @member bodyContentElement
*/
this.bodyContentElement = void(0);
/**
* A list holding the content-references (yield declarations)
* for the containers (except for body):
* header, footer, and dialogs
*
* @type Array
* @public
* @memberOf mmir.view.Layout#
* @member yields
*/
this.yields = [];
/**
* A JSON-like object containing the attributes of the BODY-tag as String values.
*
* For example, for the following BODY-tag:
* <pre>
* <body onload="handleOnLoad()" class = 'some css-classes' >
* </pre>
* the bodyAttributes would be
* <pre>
* {
* "onload": "handleOnLoad()",
* "class": "some css-classes"
* }
* </pre>
*
* @type Object
* @default undefined
* @public
* @memberOf mmir.view.Layout#
* @member bodyAttributes
*/
this.bodyAttributes = void(0);
if(this.def){
//console.debug('Layout<constructor>: start rendering layout for "'+this.name+'"'+(remote?' (REMOTE)':'')+', RAW: '+this.def);
var parser = typeof WEBPACK_BUILD !== 'undefined' && WEBPACK_BUILD? __webpack_require__('mmirf/parseUtils') : require('mmirf/parseUtils');
var parseResult = parser.parse(this.def, this);
var renderer = typeof WEBPACK_BUILD !== 'undefined' && WEBPACK_BUILD? __webpack_require__('mmirf/renderUtils') : require('mmirf/renderUtils');
var renderedLayout = renderer.renderLayout(parseResult, null/*FIXME?*/);
//DISABLED: parsing a string as HTML via jQuery etc. does not work (removes head, body,... tags):
// var doc = new DOMParser().parseFromString(renderedLayout, "text/html");
// if(!doc){
// doc = document.implementation.createHTMLDocument('LAYOUT');
// }
// var $layout = $(doc);//$.parseHTML(renderedLayout, doc);
//
// var headerElements = $('head', $layout);
// for(var i=0, size = headerElements.length; i < size; ++i){
// this.headerContents += headerElements[i].outerHTML;
// }
//
// var bodyElement = $('body', $layout);
// this.bodyContents = bodyElement.html();
// var dialogElement = $('dialogs', $layout);
// this.dialogsContents = dialogElement.html();
//TODO remove this (replace with real HTML parser?)
var self = this;
this.markerAttributeName = ViewConstants.REMOTE_RESOURCES_ATTR_NAME;
this.markerAttributeValue = ViewConstants.REMOTE_RESOURCES_ATTR_VALUE;
this.markerUseSingleQuotes = false;//<- set value enclosed in single-quotes (true) or double-quotes (false)
//appends the marker attribute for "signaling"/marking that a
// script/style/link-TAG was parsed&evaluated by this layout object
var addMarkerAttribute = function(strStartOfTag){
return strStartOfTag + ' '
+ self.markerAttributeName + '='
+ (self.markerUseSingleQuotes? '\'': '"')
+ self.markerAttributeValue
+ (self.markerUseSingleQuotes? '\'': '"');
};
//pure HTML:
// (1) removed all HTML comments (via RegExpr)
// (2) removed template comments (via parser/renderer)
var pureHtml = renderedLayout;
var regExprTagContent = //match one of the following (as groups):
'(('+ //match as groups
'('+ //(1) CDATA: this may contain anything, even a closing script-statement
'<!\\[CDATA\\['+ //CDATA open: <![CDATA[
'(.|[\\r\\n])*?'+ //allow anything within CDATA, but match non-greedily...
'\\]\\]>'+ //...for the CDATA-closing statement: ]]>
// (i.e. the first time we encounter this, we stop)
')'+ //close group for CDATA-matching
'|[\\r\\n]'+ //(2) OR line breaks \r and \n (in any combination)
//DISABLED (using . instead!): '|[^<]'+ //(3) OR any symbol that is NOT <
'|.'+ //(4) OR any symbol (REQUIRED for allowing <-symbol within script!
')'+ //close group
'*'+ //match this any number of times (or even none)
'?'+ //do matching non-greedy (i.e. until the next pattern in the RegExpr can be matched the first time)
')'; //close outer group (i.e. one group for ALL content that is matched)
//_non-greedy_ RegExpr for
// * any line-break: \r or \n or a combination of them
// * any other character but < (less-symbol); note that this itself does not include line breaks
var regExprTagInternal = '('+ //open group: match as a group (i.e. give access to the matched content via an index in the RegExpr object)
'[\\r\\n]'+ //line breaks \r and \n
'|'+ //OR
'[^<]'+ //any symbol that is NOT <
')'+ //close group
'*'+ //match this any number of times (or even none)
'?'; //do matching non-greedy (i.e. until the next pattern in the RegExpr can be matched the first time)
//matching: <script some attributes...> some content and '<!CDATA[' with any content allowed </script>
// or: <script some attributes... />
// var regExpScriptTag = /((<script([\r\n]|[^<])*?>)(((<!\[CDATA\[(.|[\r\n])*?\]\]>)|[\r\n]|.)*?)(<\/script>))|(<script([\r\n]|[^<])*?\/>)/igm;// /((<script([\r\n]|[^<])*?>)((<!\[CDATA|[\r\n]|[^<])*?)(<\/script>))|(<script([\r\n]|[^<])*?\/>)/igm;
var strRegExpScriptTag = '((<script'+regExprTagInternal+'>)'+regExprTagContent+'(</script>))'+ //DETECT "normal" script-TAG with optional content
'|(<script'+regExprTagInternal+'/>)'; //OR DETECT "self-closing" script TAG
var regExpScriptTag = new RegExp(strRegExpScriptTag,'igm');
//change to the following RegExpr (change the ones for link/style etc too)
// from
// /((<script([\r\n]|[^<])*?>)(([\r\n]|[^<])*?)(<\/script>))|(<script([\r\n]|[^<])*?\/>)/igm;
// to
// /((<script([\r\n]|[^<])*?>)(((<!\[CDATA\[(.|[\r\n])*?\]\]>)|[\r\n]|.)*?)(<\/script>))|(<script([\r\n]|[^<])*?\/>)/igm
//
// -> this RegExpr additionally
// * respects CDATA (<![CDTATA[ ... ]]>), with any content within its boundaries, even a "closing" </script>-TAG
// * allows opening < within the script-TAGs
// LIMITATIONS: both this and the current one do not allow a < within the script-TAGS attributes, e.g.
// NOT: <script data-condition=" i < 5">
// WORKAROUND: encode the <-symbol, i.e. instead use <script data-condition=" i < 5">
// ==> use "<" or "<" instead of "<" in TAG attributes!
// regExpScriptTag[0]: complete match
// regExpScriptTag[1]: script with start and end tag (if matched)
// regExpScriptTag[4]: TEXT content of script-tag (if present/matched)
// regExpScriptTag[9]: self-closing script (if matched)
var matchScriptTag = null;
self.headerContents = '';
var removedScriptAndLinkHmtl = new Array();
// var matchIndex;
while(matchScriptTag = regExpScriptTag.exec(pureHtml)){
// matchIndex = matchScriptTag[1] ? 1 : (matchScriptTag[9]? 9 : -1);
if(matchScriptTag[0]){//matchIndex != -1){
// console.warn("Remote: " + self.remoteaccess);
if (self.remoteaccess) {
// self.headerContents += matchScriptTag[0].replace("<script", "<script loc=\"remote\"");//[matchIndex];
self.headerContents += addMarkerAttribute('<script') + matchScriptTag[0].substring('<script'.length);
} else {
self.headerContents += matchScriptTag[0];
}
//remove script tag, and continue search
// pureHtml = pureHtml.substring(0,matchScriptTag.index) + pureHtml.substring(matchScriptTag.index + matchScriptTag[0].length);// pureHtml.replace(matchScriptTag[matchIndex], '');
removedScriptAndLinkHmtl.push({start: matchScriptTag.index, end: matchScriptTag.index + matchScriptTag[0].length});
self.headerElements.push(new Layout.TagElement(matchScriptTag[2] || matchScriptTag[9], matchScriptTag[4], 'SCRIPT'));
}
}
//matching: <link some attributes...> some content and '<!CDATA[' with any content allowed </link>
// or: <link some attributes... />
// var regExpLinkTag = /((<link([\r\n]|[^<])*?>)(((<!\[CDATA\[(.|[\r\n])*?\]\]>)|[\r\n]|.)*?)(<\/link>))|(<link([\r\n]|[^<])*?\/>)/igm;
var strRegExpLinkTag = '((<link'+regExprTagInternal+'>)'+regExprTagContent+'(</link>))'+ //DETECT "normal" script-TAG with optional content
'|(<link'+regExprTagInternal+'/>)'; //OR DETECT "self-closing" script TAG
var regExpLinkTag = new RegExp(strRegExpLinkTag,'igm');
// regExpLinkTag[0]: complete match
// regExpLinkTag[1]: link with start and end tag (if matched)
// regExpLinkTag[4]: TEXT content of link-tag (if present/matched)
// regExpLinkTag[9]: self-closing link (if matched)
var matchLinkTag = null;
while(matchLinkTag = regExpLinkTag.exec(pureHtml)){
// console.warn("Matchlinktag: " + matchLinkTag[0]);
if(matchLinkTag[0]){
// console.warn("Remote: " + self.remoteaccess);
if (self.remoteaccess) {
// self.headerContents += matchLinkTag[0].replace("<link", "<link loc=\"remote\"");
self.headerContents += addMarkerAttribute('<link') + matchLinkTag[0].substring('<link'.length);
} else {
self.headerContents += matchLinkTag[0];
}
removedScriptAndLinkHmtl.push({start: matchLinkTag.index, end: matchLinkTag.index + matchLinkTag[0].length});
self.headerElements.push(new Layout.TagElement(matchLinkTag[2] || matchLinkTag[9], matchLinkTag[4], 'LINK'));
}
}
//matching: <style type="text/css" some attributes...> some content and '<!CDATA[' with any content allowed </style>
// var regExpStyleTag = /((<style([\r\n]|[^<])*?type="text\/css"([\r\n]|[^<])*?>)(((<!\[CDATA\[(.|[\r\n])*?\]\]>)|[\r\n]|.)*?)(<\/style>))/igm;
var strRegExpStyleTag = '((<style'+regExprTagInternal+'>)'+regExprTagContent+'(</style>))'; //DETECT only "normal" style-TAG with content
var regExpStyleTag = new RegExp(strRegExpStyleTag,'igm');
// regExpStyleTag[0]: complete match
// regExpStyleTag[1]: script with start and end tag (if matched)
// regExpStyleTag[4]: TEXT content of style-tag (if present/matched)
var matchStyleTag = null;
while(matchStyleTag = regExpStyleTag.exec(pureHtml)){
// matchIndex = matchStyleTag[1] ? 1 : -1;
if(matchStyleTag[0]){//matchIndex != -1){
// console.warn("Remote: " + self.remoteaccess);
if (self.remoteaccess) {
// self.headerContents += matchStyleTag[0].replace("<style", "<style loc=\"remote\"");//[matchIndex];
self.headerContents += addMarkerAttribute('<style') + matchStyleTag[0].substring('<style'.length);
} else {
self.headerContents += matchStyleTag[0];
}
//remove script tag, and continue search
// pureHtml = pureHtml.substring(0,matchStyleTag.index) + pureHtml.substring(matchStyleTag.index + matchStyleTag[0].length);// pureHtml.replace(matchStyleTag[matchIndex], '');
removedScriptAndLinkHmtl.push({start: matchStyleTag.index, end: matchStyleTag.index + matchStyleTag[0].length});
self.headerElements.push(new Layout.TagElement(matchStyleTag[2], matchStyleTag[4], 'STYLE'));
}
}
//only need to "process" removed script/link tags, if some were found:
if(removedScriptAndLinkHmtl.length > 0){
removedScriptAndLinkHmtl.sort(function(a,b){
return a.start - b.start;
});
var cleanedHtml = new Array();
var remPos = 0;
var removalElement = removedScriptAndLinkHmtl[0];
for(var i=0, size = removedScriptAndLinkHmtl.length; i < size; ++i){
removalElement = removedScriptAndLinkHmtl[i];
var text = pureHtml.substring(remPos, removalElement.start);
cleanedHtml.push(text);
remPos = removalElement.end;
}
//add rest of the HTML if necessary
if(removalElement.end < pureHtml.length){
cleanedHtml.push(pureHtml.substring(removalElement.end));
}
//replace HTML with the removed/clean version:
pureHtml = cleanedHtml.join('');
}
//TODO this is needed my be of interest for further processing
// (for processing partial HTML responses)
//-> should this be part of the framework? (i.e. "public" property for Layout; TODO add to stringify()?)
this._processedDef = pureHtml;
//FIXME reg-expr does not detect body-TAG, if body has no content (i.e. body="<body></body>")
var regExpBodyTag = /(<body([\r\n]|.)*?>)(([\r\n]|.)*?)(<\/body>)/igm;
// matchBodyTag[0]: complete match
// matchBodyTag[1]: body start tag
// matchBodyTag[2]: last CHAR within body start tag, before closing, e.g. "...lskdjf>" -> "f"
// matchBodyTag[3]: body text content
// matchBodyTag[4]: last CHAR within body text content, before closing, e.g. "...lsk</body>" -> "k"
// matchBodyTag[5]: body end tag
var matchBodyTag = regExpBodyTag.exec(pureHtml);
self.bodyContents = '';
if(matchBodyTag && matchBodyTag[3]){
self.bodyContents += matchBodyTag[3];
}
else if(!ignoreMissingBody) {
//TODO throw error?
console.error('Layout.<constructor>: Layout template does not contain a <body> element!');
}
//TEST: experimental -> "remember" attributes of body tag
//NOTE this assumes that matchBodyTag-RegExpr starts with: /(<body([\r\n]|.)*?>) ...
if(matchBodyTag && matchBodyTag[1] && matchBodyTag[1].length > '<body>'.length){
// //NOTE: 1st case should really never occur.
// var reTagSelfClose = /\/>$/;
// var bodyAttrEnd = reTagSelfClose.test(matchBodyTag[1])? matchBodyTag[1].length-2 : matchBodyTag[1].length-1;
// var bodyAttr = '<div ' + matchBodyTag[1].substring('<body'.length, bodyAttrEnd) + '</div>';
// bodyAttr = jQuery(bodyAttr);
self.bodyAttributes = Layout.getTagAttr(matchBodyTag[1]);
}
//Extract title-tag
var regExpTitleTag = /(<title([\r\n]|.)*?>)(([\r\n]|.)*?)(<\/title>)/igm;
// matchTitleTag[0]: complete match
// matchTitleTag[1]: title start tag
// matchTitleTag[2]: last CHAR within title start tag, before closing, e.g. "...lskdjf>" -> "f"
// matchTitleTag[3]: title text content
// matchTitleTag[4]: last CHAR within title text content, before closing, e.g. "...lsk</title>" -> "k"
// matchTitleTag[5]: title end tag
var matchTitleTag = regExpTitleTag.exec(pureHtml);
if(matchTitleTag && matchTitleTag[3]){
self.title = matchTitleTag[3];
}
var regExpDialogsTag = /(<dialogs([\r\n]|.)*?>)(([\r\n]|.)*?)(<\/dialogs>)/igm;
// matchDialogsTag[0]: complete match
// matchDialogsTag[1]: dialogs start tag
// matchDialogsTag[2]: last CHAR within dialogs start tag, before closing, e.g. "...lskdjf>" -> "f"
// matchDialogsTag[3]: dialogs text content
// matchDialogsTag[4]: last CHAR within dialogs text content, before closing, e.g. "...lsk</dialogs>" -> "k"
// matchDialogsTag[5]: dialogs end tag
var matchDialogsTag = regExpDialogsTag.exec(pureHtml);
self.dialogsContents = '';
if(matchDialogsTag && matchDialogsTag[3]){
self.dialogsContents += matchDialogsTag[3];
}
var parseBodyResult = new ContentElement({name: this.name, content: this.bodyContents}, this, parser, renderer);// parser.parse(this.bodyContents, this);
// for(var i=0, size = parseBodyResult.yields.length; i < size ; ++i){
// this.yields.push(new YieldDeclaration(parseBodyResult.yields[i], ViewConstants.CONTENT_AREA_BODY));
// }
// parseBodyResult.yields = void(0);
var all = parseBodyResult.allContentElements.concat(parseBodyResult.yields);
all.sort(function(parsedElem1, parsedElem2){
return parsedElem1.getStart() - parsedElem2.getStart();
});
parseBodyResult.allContentElements = all;
parseBodyResult.getController = function(){ return {//FIXME
name: null,
getName: function(){
return this.name;
}
}};
this.bodyContentElement = parseBodyResult;
var parseDialogResult = parser.parse(this.dialogsContents, this);
for(var i=0, size = parseDialogResult.yields.length; i < size ; ++i){
this.yields.push(new YieldDeclaration(parseDialogResult.yields[i], ViewConstants.CONTENT_AREA_DIALOGS));
}
}//END: if(this.def)
}//END: Layout()
/**
* HELPER: extracts TAG attributes into an JSON-object
*
* @memberOf mmir.view.Layout
* @function getTagAttr
* @private
* @static
*
* @param {String} str
* the start-TAG as String
* @param {Object} [target] OPTIONAL
* the target-object to which the extracted attributes will be attached
* if omitted, a new, empty object will be created
*
* @return {Object} the object with the extracted attributes as properties
* (if <em>target</em> was provided, then this is the <em>target</em> object)
*
* @example
* e.g. <body onload="on_load();" class = 'biggestFont'>
* -->
* {"onload": "on_load()", "class": "biggestFont"}
*
*/
Layout.getTagAttr = function(str, target){
//RegExp for:
// name = "..."
//or
// name = '...'
//
//NOTE: the RegExp does not extract "single properties", as e.g. <tag required>
// ... instead, for extraction, they must be specified as follows: <tag required="...">
//NOTE: values MUST be enclosed in double-quotes or quotes, "quote-less" attribute values cannot be extracted!
// ... e.g. NOT: <tag name=value>, instead: <tag name="value"> or <tag name='value'>
//NOTE: the RegExp also detects escaped double-quotes and quotes respectively
var regExpr = /\s+([^=]*?)\s*=\s*(("((\\"|[^"])*)")|('((\\'|[^'])*)'))/igm;
var result = target || {};
var match;
while(match = regExpr.exec(str)){
if(match[4]){
result[match[1]] = match[4];
}
else if(match[7]){
result[match[1]] = match[7];
}
}
return result;
};
/**
* HELPER class: extract raw TAG Strings into a property-object
*
* @public
* @constructor
* @memberOf mmir.view
* @param {String} tag
* the start TAG
* @param {String} content
* the TEXT content of the TAG (may be empty)
* @param {String} tagType
* the TAG type, e.g. "SCRIPT"
*
* @returns {mmir.view.Layout.TagElement}
*
* prop {String} tagName: the TAG type, e.g. "SCRIPT"
* prop {String} textContent: the TEXT content of the TAG (may be an empty String)
* prop EXTRACTED ATTRIBUTES: the extracted attributes form the start-TAG
*
* func {String} attr(STRING name): returns the attribute-value for name (may be undefined)
* func {String} html(): returns the TEXT content of the TAG (may be an empty String)
*
* func {Boolean} isScript(): returns TRUE if tagType is SCRIPT
* func {Boolean} isStyle(): returns TRUE if tagType is STYLE
* func {Boolean} isLink(): returns TRUE if tagType is LINK
*/
Layout.TagElement = function TagElement(tag, content, tagType){
/** the TAG type, e.g. "SCRIPT" */
this.tagName = tagType;
/** the TEXT content of the TAG (may be an empty String) */
this.textContent = content || '';
var tis = this;
// tis.attr = function(name){
// return this[name];
// };
// tis.html = function(){
// return this.textContent;
// };
// tis.isScript = function(){return this.tagName === 'SCRIPT';};
// tis.isStyle = function(){return this.tagName === 'STYLE'; };
// tis.isLink = function(){return this.tagName === 'LINK'; };
//extract attributes as properties from TAG string:
Layout.getTagAttr(tag, tis);
return tis;
};
/**
* Prototype for TagElement
*
* func {String} attr(STRING name): returns the attribute-value for name (may be undefined)
* func {String} html(): returns the TEXT content of the TAG (may be an empty String)
*
* func {Boolean} isScript(): returns TRUE if tagType is SCRIPT
* func {Boolean} isStyle(): returns TRUE if tagType is STYLE
* func {Boolean} isLink(): returns TRUE if tagType is LINK
*
* @lends mmir.view.Layout.TagElement
*/
Layout.TagElement.prototype = {
/**
* @param {String} name the attribute name
* @returns {any} the attribute-value for name (may be undefined)
*/
attr: function(name){
return this[name];
},
/** @returns {String} the TEXT content of the TAG (may be an empty String) */
html: function(){
return this.textContent;
},
/** @returns {Boolean} returns TRUE if tagType is SCRIPT */
isScript: function(){return this.tagName === 'SCRIPT';},
/** @returns {Boolean} returns TRUE if tagType is STYLE */
isStyle: function(){return this.tagName === 'STYLE'; },
/** @returns {Boolean} returns TRUE if tagType is LINK */
isLink: function(){return this.tagName === 'LINK'; }
};
/**
* This methods returns an associative array holding the contents of the different containers: header, body, footer and dialogs.
*
* @function
* @returns {Array} An associative array holding the contents of the different containers: header, body, footer and dialogs
* @public
*/
Layout.prototype.getYields = function(){
return this.yields;
};
/**
* This methods returns the contents of the header part of the layout.
*
* @function getHeaderContents
* @returns {String} The contents of the header part of the layout
* @public
* @memberOf mmir.view.Layout#
*/
Layout.prototype.getHeaderContents = function(){
return this.headerContents;
};
/**
* This methods returns the contents of the dialog part of the layout.
*
* @function getDialogsContents
* @returns {String} The contents of the dialog part of the layout
* @public
* @memberOf mmir.view.Layout#
*/
Layout.prototype.getDialogsContents = function(){
return this.dialogsContents;
};
/**
* This methods returns the contents of the body part of the layout.
*
* @function getBodyContents
* @returns {String} The contents of the body part of the layout
* @public
* @memberOf mmir.view.Layout#
*/
Layout.prototype.getBodyContents = function(){
return this.bodyContents;
};
/**
* Gets the name of the layout.
*
* @function getName
* @returns {String} The name of the layout.
* @public
* @memberOf mmir.view.Layout#
*/
Layout.prototype.getName = function(){
return this.name;
};
/**
* HELPER: add prototype functions of Layout.TagElement to the #headerElements
*
* @function _extHeaderElements
* @protected
* @memberOf mmir.view.Layout#
*/
Layout.prototype._extHeaderElements = function(){
var prot = Layout.TagElement.prototype;
var funcs = Object.keys(prot);
var len = funcs.length -1;
var i, j, elem, fname;
for(i = this.headerElements.length-1; i >= 0; --i){
elem = this.headerElements[i];
for(j = len; j >= 0; --j){
fname = funcs[j];
elem[fname] = prot[fname];
}
}
};
/**
* @function stringify
* @memberOf mmir.view.Layout#
*
* @param {Boolean} [disableStrictMode] OPTIONAL disable JavaScript strict mode in the generated view code
* @return {String} stringified representation of the layout
*/
Layout.prototype.stringify = function(disableStrictMode){
// "plain properties" list
var propList = [
'name',
'remoteaccess',
'def',
'headerElements',
'headerContents',
'title',
// 'bodyContents', //DISABLED: store in this.bodyContentElement.definition now
'dialogsContents',
'markerAttributeName',
'markerAttributeValue',
'markerUseSingleQuotes',
'bodyAttributes'
];
//stringify-able properties
var stringifyPropList = [
'bodyContentElement' //element type: ContentElement (stringify-able)
];
//complex Array-properties
var arrayPropList = [
'yields' //element type: YieldDeclaration (stringify-able)
];
//function for iterating over the property-list and generating JSON-like entries in the string-buffer
var appendStringified = storageUtils.appendStringified;
var moduleNameString = '"'+this.name+'Layout"';
var sb = [storageUtils.getCodeWrapPrefix(disableStrictMode), 'require("mmirf/storageUtils").restoreObject({ classConstructor: "mmirf/layout"', ','];
appendStringified(this, propList, sb);
//NOTE the use of require() here, assumes that the dependency has already been loaded (i.e. has already been request by some other module!)
sb.push( 'initPublish: function(){ this._extHeaderElements(); this.bodyContents=this.bodyContentElement.definition; require("mmirf/presentationManager").addLayout(this); }');
sb.push(',');
//non-primitives properties with stringify() function:
appendStringified(this, stringifyPropList, sb, null, function stringifyValueExtractor(name, obj){
return obj.stringify(disableStrictMode);
});
//non-primitives array-properties with stringify() function:
appendStringified(this, arrayPropList, sb, null, function arrayValueExtractor(name, arrayValue){
var buf =['['];
for(var i=0, size = arrayValue.length; i < size; ++i){
buf.push(arrayValue[i].stringify(disableStrictMode));
buf.push(',');
}
//remove last comma
if(arrayValue.length > 0){
buf.splice( buf.length - 1, 1);
}
buf.push(']');
return buf.join('');
});
//if last element is a comma, remove it
if(sb[sb.length - 1] === ','){
sb.splice( sb.length - 1, 1);
}
//TODO use requirejs mechanism? (see remark above)
// sb.push(' }, true); });\n require(['//<- add require-call, so that this JS-file adds itself to the loaded dependencies in requirejs
// + moduleNameString + ']);');
sb.push(' }, true, '+storageUtils.STORAGE_FILE_FORMAT_NUMBER+');');
sb.push(storageUtils.STORAGE_CODE_WRAP_SUFFIX);
return sb.join('');
};
return Layout;
});