weekly release 4.1dev
[moodle.git] / lib / amd / build / templates.min.js
1 /**
2  * Template renderer for Moodle. Load and render Moodle templates with Mustache.
3  *
4  * @module     core/templates
5  * @copyright  2015 Damyon Wiese <damyon@moodle.com>
6  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7  * @since      2.9
8  */
9 define("core/templates",["core/mustache","jquery","core/ajax","core/str","core/notification","core/url","core/config","core/localstorage","core/icon_system","core_filters/events","core/yui","core/log","core/truncate","core/user_date","core/pending"],(function(mustache,$,ajax,str,notification,coreurl,config,storage,IconSystem,filterEvents,Y,Log,Truncate,UserDate,Pending){var uniqInstances=0,templateCache={},templatePromises={},cachePartialPromises={},iconSystem={},loadTemplateBuffer=[],isLoadingTemplates=!1,disallowedNestedHelpers=["js"],getNormalisedComponent=function(component){return component&&"moodle"!==component&&"core"!==component?component:"core"},getTemplatePromiseFromCache=function(searchKey){if(searchKey in templatePromises)return templatePromises[searchKey];if(searchKey in templateCache)return templatePromises[searchKey]=$.Deferred().resolve(templateCache[searchKey]).promise(),templatePromises[searchKey];if(M.cfg.templaterev<=0)return null;var cached=storage.get("core_template/"+M.cfg.templaterev+":"+searchKey);return cached?(templateCache[searchKey]=cached,templatePromises[searchKey]=$.Deferred().resolve(cached).promise(),templatePromises[searchKey]):null},processLoadTemplateBuffer=function(){if(loadTemplateBuffer.length&&!isLoadingTemplates){isLoadingTemplates=!0;var templatesToLoad=loadTemplateBuffer.slice(),serverRequestsDeferred=$.Deferred(),requests=[],templatePromises=templatesToLoad.map((function(templateData){var component=getNormalisedComponent(templateData.component),name=templateData.name,searchKey=templateData.searchKey,theme=templateData.theme,templateDeferred=templateData.deferred,promise=null,cachedPromise=getTemplatePromiseFromCache(searchKey);if(cachedPromise)promise=cachedPromise;else{requests.push({methodname:"core_output_load_template_with_dependencies",args:{component:component,template:name,themename:theme,lang:$("html").attr("lang").replace(/-/g,"_")}});var index=requests.length-1;promise=serverRequestsDeferred.promise().then((function(promises){return templatePromises[searchKey]=promises[index].then((function(response){var templateSource=null;return response.templates.forEach((function(data){data.component=getNormalisedComponent(data.component);var tempSearchKey=[theme,data.component,data.name].join("/");templateCache[tempSearchKey]=data.value,M.cfg.templaterev>0&&storage.set("core_template/"+M.cfg.templaterev+":"+tempSearchKey,data.value),data.component==component&&data.name==name&&(templateSource=data.value)})),response.strings.length&&str.cache_strings(response.strings.map((function(data){return{component:getNormalisedComponent(data.component),key:data.name,value:data.value}}))),templateSource})),templatePromises[searchKey]}))}return promise.then((function(source){return templateDeferred.resolve(source)})).catch((function(error){throw templateDeferred.reject(error),error}))}));requests.length?serverRequestsDeferred.resolve(ajax.call(requests,!0,!1,!1,0,M.cfg.templaterev)):serverRequestsDeferred.resolve(),$.when.apply(null,templatePromises).then((function(){loadTemplateBuffer.splice(0,templatesToLoad.length),isLoadingTemplates=!1,processLoadTemplateBuffer()})).catch((function(){loadTemplateBuffer.splice(0,templatesToLoad.length),isLoadingTemplates=!1,processLoadTemplateBuffer()}))}},Renderer=function(){this.requiredStrings=[],this.requiredJS=[],this.requiredDates=[],this.currentThemeName=""};Renderer.prototype.requiredStrings=null,Renderer.prototype.requiredDates=[],Renderer.prototype.requiredJS=null,Renderer.prototype.currentThemeName="",Renderer.prototype.getTemplate=function(templateName){var currentTheme=this.currentThemeName,searchKey=currentTheme+"/"+templateName,cachedPromise=getTemplatePromiseFromCache(searchKey);if(cachedPromise)return cachedPromise;var existingBufferRecords=loadTemplateBuffer.filter((function(record){return record.searchKey==searchKey}));if(existingBufferRecords.length)return existingBufferRecords[0].deferred.promise();var parts=templateName.split("/"),component=getNormalisedComponent(parts.shift()),name=parts.join("/"),deferred=$.Deferred();return loadTemplateBuffer.push({component:component,name:name,theme:currentTheme,searchKey:searchKey,deferred:deferred}),processLoadTemplateBuffer(),deferred.promise()},Renderer.prototype.prefetchTemplates=function(templateNames,currentTheme){templateNames.forEach((function(templateName){var searchKey=currentTheme+"/"+templateName;if(!getTemplatePromiseFromCache(searchKey)&&!loadTemplateBuffer.filter((function(record){return record.searchKey==searchKey})).length){var parts=templateName.split("/"),component=getNormalisedComponent(parts.shift()),name=parts.join("/");loadTemplateBuffer.push({component:component,name:name,theme:currentTheme,searchKey:searchKey,deferred:$.Deferred()})}})),processLoadTemplateBuffer()},Renderer.prototype.partialHelper=function(name){var searchKey=this.currentThemeName+"/"+name;return searchKey in templateCache||notification.exception(new Error("Failed to pre-fetch the template: "+name)),templateCache[searchKey]},Renderer.prototype.renderIcon=function(key,component,title){var modulename=config.iconsystemmodule;component=getNormalisedComponent(component);var ready=$.Deferred();return require([modulename],(function(System){var system=new System;system instanceof IconSystem?(iconSystem=system,system.init().then(ready.resolve).catch(notification.exception)):ready.reject("Invalid icon system specified"+config.iconsystemmodule)})),ready.then(function(iconSystem){return this.getTemplate(iconSystem.getTemplateName())}.bind(this)).then((function(template){return iconSystem.renderIcon(key,component,title,template)}))},Renderer.prototype.pixHelper=function(context,sectionText,helper){var parts=sectionText.split(","),key="",component="",text="";parts.length>0&&(key=helper(parts.shift().trim(),context)),parts.length>0&&(component=helper(parts.shift().trim(),context)),parts.length>0&&(text=helper(parts.join(",").trim(),context));var templateName=iconSystem.getTemplateName(),searchKey=this.currentThemeName+"/"+templateName,template=templateCache[searchKey];return component=getNormalisedComponent(component),key=key.replace(/&#x2F;/gi,"/"),iconSystem.renderIcon(key,component,text,template)},Renderer.prototype.jsHelper=function(context,sectionText,helper){return this.requiredJS.push(helper(sectionText,context)),""},Renderer.prototype.stringHelper=function(context,sectionText,helper){var parts=sectionText.split(","),key="",component="",param="";parts.length>0&&(key=parts.shift().trim()),parts.length>0&&(component=parts.shift().trim()),parts.length>0&&(param=parts.join(",").trim()),component=getNormalisedComponent(component),""!==param&&(param=helper(param,context)),0===param.indexOf("{")&&0!==param.indexOf("{{")&&(param=JSON.parse(param));var index=this.requiredStrings.length;return this.requiredStrings.push({key:key,component:component,param:param}),"[[_s"+index+"]]"},Renderer.prototype.cleanStringHelper=function(context,sectionText,helper){return this.stringHelper(context,sectionText,helper).replace("s","c")},Renderer.prototype.quoteHelper=function(context,sectionText,helper){var content=helper(sectionText.trim(),context);return'"'+(content=content.replace(/"/g,'\\"').replace(/\t/g,"&#9;").replace(/([{}]{2,3})/g,"{{=<% %>=}}$1<%={{ }}=%>").replace(/(\r\n|\r|\n)/g,"&#x0a;"))+'"'},Renderer.prototype.shortenTextHelper=function(context,sectionText,helper){var parts=sectionText.match(/(.*?),(.*)/),length=parts[1].trim(),content=helper(parts[2].trim(),context);return Truncate.truncate(content,{length:length,words:!0,ellipsis:"..."})},Renderer.prototype.userDateHelper=function(context,sectionText,helper){var parts=sectionText.match(/(.*?),(.*)/),timestamp=helper(parts[1].trim(),context),format=helper(parts[2].trim(),context),index=this.requiredDates.length;return this.requiredDates.push({timestamp:timestamp,format:format}),"[[_t_"+index+"]]"},Renderer.prototype.addHelperFunction=function(helperFunction,context){return function(){return function(sectionText,helper){var originalHelpers=disallowedNestedHelpers.reduce((function(carry,name){return context.hasOwnProperty(name)&&(carry[name]=context[name]),carry}),{});disallowedNestedHelpers.forEach((function(helperName){context[helperName]=function(){return""}}));var result=helperFunction.apply(this,[context,sectionText,helper]);for(var name in originalHelpers)context[name]=originalHelpers[name];return result}.bind(this)}.bind(this)},Renderer.prototype.addHelpers=function(context,themeName){this.currentThemeName=themeName,this.requiredStrings=[],this.requiredJS=[],context.uniqid=uniqInstances++,context.str=this.addHelperFunction(this.stringHelper,context),context.cleanstr=this.addHelperFunction(this.cleanStringHelper,context),context.pix=this.addHelperFunction(this.pixHelper,context),context.js=this.addHelperFunction(this.jsHelper,context),context.quote=this.addHelperFunction(this.quoteHelper,context),context.shortentext=this.addHelperFunction(this.shortenTextHelper,context),context.userdate=this.addHelperFunction(this.userDateHelper,context),context.globals={config:config},context.currentTheme=themeName},Renderer.prototype.getJS=function(){var js="";return this.requiredJS.length>0&&(js=this.requiredJS.join(";\n")),js},Renderer.prototype.treatStringsInContent=function(content,strings){var treated,index,strIndex,walker,char,strFinal,isClean,pattern=/\[\[_(s|c)\d+\]\]/;do{for(treated="",index=content.search(pattern);index>-1;){treated+=content.substring(0,index),isClean="c"==(content=content.substr(index))[3],strIndex="",walker=4,char=content.substr(walker,1);do{strIndex+=char,walker++,char=content.substr(walker,1)}while("]"!=char);void 0===(strFinal=strings[parseInt(strIndex,10)])&&(Log.debug("Could not find string for pattern [[_"+(isClean?"c":"s")+strIndex+"]]."),strFinal=""),isClean&&(strFinal=mustache.escape(strFinal)),treated+=strFinal,index=(content=content.substr(6+strIndex.length)).search(pattern)}index=(content=treated+content).search(pattern)}while(index>-1);return content},Renderer.prototype.treatDatesInContent=function(content,dates){return dates.forEach((function(date,index){var re=new RegExp("\\[\\[_t_"+index+"\\]\\]","g");content=content.replace(re,date)})),content},Renderer.prototype.doRender=function(templateSource,context,themeName){this.currentThemeName=themeName;var iconTemplate=iconSystem.getTemplateName(),pendingPromise=new Pending("core/templates:doRender");return this.getTemplate(iconTemplate).then(function(){this.addHelpers(context,themeName);var result=mustache.render(templateSource,context,this.partialHelper.bind(this));return $.Deferred().resolve(result.trim(),this.getJS()).promise()}.bind(this)).then(function(html,js){return this.requiredStrings.length>0?str.get_strings(this.requiredStrings).then(function(strings){return this.requiredDates=this.requiredDates.map(function(date){return{timestamp:this.treatStringsInContent(date.timestamp,strings),format:this.treatStringsInContent(date.format,strings)}}.bind(this)),html=this.treatStringsInContent(html,strings),js=this.treatStringsInContent(js,strings),$.Deferred().resolve(html,js).promise()}.bind(this)):$.Deferred().resolve(html,js).promise()}.bind(this)).then(function(html,js){return this.requiredDates.length>0?UserDate.get(this.requiredDates).then(function(dates){return html=this.treatDatesInContent(html,dates),js=this.treatDatesInContent(js,dates),$.Deferred().resolve(html,js).promise()}.bind(this)):$.Deferred().resolve(html,js).promise()}.bind(this)).then((function(html,js){return pendingPromise.resolve(),$.Deferred().resolve(html,js).promise()}))};var runTemplateJS=function(source){if(""!==source.trim()){var newscript=$("<script>").attr("type","text/javascript").html(source);$("head").append(newscript)}},domReplace=function(element,newHTML,newJS,replaceChildNodes){var replaceNode=$(element);if(replaceNode.length){var newNodes=$(newHTML);return replaceChildNodes?(new Y.NodeList(replaceNode.children().get()).destroy(!0),replaceNode.empty(),replaceNode.append(newNodes)):(new Y.NodeList(replaceNode.get()).destroy(!0),replaceNode.replaceWith(newNodes)),runTemplateJS(newJS),filterEvents.notifyFilterContentUpdated(newNodes),newNodes.get()}return[]};Renderer.prototype.scanForPartials=function(templateSource){var tokens=mustache.parse(templateSource),partials=[],findPartial=function(tokens,partials){var i,token;for(i=0;i<tokens.length;i++)">"!=(token=tokens[i])[0]&&"<"!=token[0]||partials.push(token[1]),token.length>4&&findPartial(token[4],partials)};return findPartial(tokens,partials),partials},Renderer.prototype.cachePartials=function(templateName,parentage){var searchKey=this.currentThemeName+"/"+templateName;return searchKey in cachePartialPromises||(parentage=parentage||[searchKey],cachePartialPromises[searchKey]=$.Deferred(),this.getTemplate(templateName).then(function(templateSource){var fetchThemAll=this.scanForPartials(templateSource).filter(function(partialName){return!(parentage.indexOf(this.currentThemeName+"/"+partialName)>=0)&&partialName!=templateName}.bind(this)).map(function(partialName){return parentage.push(this.currentThemeName+"/"+partialName),this.cachePartials(partialName,parentage)}.bind(this));return $.when.apply($,fetchThemAll).then((function(){return cachePartialPromises[searchKey].resolve(templateSource)}))}.bind(this)).catch(cachePartialPromises[searchKey].reject)),cachePartialPromises[searchKey]},Renderer.prototype.render=function(templateName,context,themeName){void 0===themeName&&(themeName=config.theme),this.currentThemeName=themeName;var modulename=config.iconsystemmodule,ready=$.Deferred();return require([modulename],(function(System){var system=new System;system instanceof IconSystem?(iconSystem=system,system.init().then(ready.resolve).catch(notification.exception)):ready.reject("Invalid icon system specified"+config.iconsystem)})),ready.then(function(){return this.cachePartials(templateName)}.bind(this)).then(function(templateSource){return this.doRender(templateSource,context,themeName)}.bind(this))};return{render:function(templateName,context,themeName){return(new Renderer).render(templateName,context,themeName)},prefetchTemplates:function(templateNames,themeName){var renderer=new Renderer;return void 0===themeName&&(themeName=config.theme),renderer.prefetchTemplates(templateNames,themeName)},renderForPromise:function(templateName,context,themeName){return(new Renderer).render(templateName,context,themeName).then((function(html,js){return{html:html,js:js}}))},renderPix:function(key,component,title){return(new Renderer).renderIcon(key,getNormalisedComponent(component),title)},runTemplateJS:runTemplateJS,replaceNodeContents:function(element,newHTML,newJS){return domReplace(element,newHTML,newJS,!0)},replaceNode:function(element,newHTML,newJS){return domReplace(element,newHTML,newJS,!1)},prependNodeContents:function(element,html,js){return function(element,html,js){var node=$(element);if(node.length){var newContent=$(html);return node.prepend(newContent),runTemplateJS(js),filterEvents.notifyFilterContentUpdated(node),newContent.get()}return[]}(element,html,js)},appendNodeContents:function(element,html,js){return function(element,html,js){var node=$(element);if(node.length){var newContent=$(html);return node.append(newContent),runTemplateJS(js),filterEvents.notifyFilterContentUpdated(node),newContent.get()}return[]}(element,html,js)}}}));
11 //# sourceMappingURL=templates.min.js.map