From 9bdcf579517e4f3722adc88a595eb5937dc4438e Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Sun, 22 Feb 2015 15:24:44 +0800 Subject: [PATCH] MDL-49152 output: Templates for renderers (JS and PHP) Mustache support for rendering templates from either php or js. --- lib/amd/build/ajax.min.js | 2 +- lib/amd/build/mustache.min.js | 1 + lib/amd/build/notification.min.js | 1 + lib/amd/build/str.min.js | 1 + lib/amd/build/templates.min.js | 1 + lib/amd/build/url.min.js | 1 + lib/amd/build/yui.min.js | 1 + lib/amd/src/ajax.js | 34 +- lib/amd/src/config.js | 2 + lib/amd/src/first.js | 1 + lib/amd/src/mustache.js | 608 ++++++++++++++ lib/amd/src/notification.js | 105 +++ lib/amd/src/str.js | 141 ++++ lib/amd/src/templates.js | 374 +++++++++ lib/amd/src/url.js | 91 ++ lib/amd/src/yui.js | 30 + lib/classes/output/external.php | 133 +++ .../output/mustache_filesystem_loader.php | 54 ++ .../output/mustache_javascript_helper.php | 59 ++ lib/classes/output/mustache_pix_helper.php | 78 ++ lib/classes/output/mustache_string_helper.php | 79 ++ lib/classes/output/mustache_uniqid_helper.php | 52 ++ lib/db/services.php | 7 + lib/mustache/CONTRIBUTING.md | 35 + lib/mustache/LICENSE | 21 + lib/mustache/README.md | 71 ++ lib/mustache/composer.json | 24 + lib/mustache/readme_moodle.txt | 11 + lib/mustache/src/Mustache/Autoloader.php | 74 ++ lib/mustache/src/Mustache/Cache.php | 38 + .../src/Mustache/Cache/AbstractCache.php | 60 ++ .../src/Mustache/Cache/FilesystemCache.php | 159 ++++ lib/mustache/src/Mustache/Cache/NoopCache.php | 49 ++ lib/mustache/src/Mustache/Compiler.php | 646 ++++++++++++++ lib/mustache/src/Mustache/Context.php | 206 +++++ lib/mustache/src/Mustache/Engine.php | 785 ++++++++++++++++++ lib/mustache/src/Mustache/Exception.php | 18 + .../Exception/InvalidArgumentException.php | 18 + .../src/Mustache/Exception/LogicException.php | 18 + .../Mustache/Exception/RuntimeException.php | 18 + .../Mustache/Exception/SyntaxException.php | 36 + .../Exception/UnknownFilterException.php | 32 + .../Exception/UnknownHelperException.php | 32 + .../Exception/UnknownTemplateException.php | 32 + .../src/Mustache/HelperCollection.php | 172 ++++ lib/mustache/src/Mustache/LambdaHelper.php | 49 ++ lib/mustache/src/Mustache/Loader.php | 27 + .../src/Mustache/Loader/ArrayLoader.php | 79 ++ .../src/Mustache/Loader/CascadingLoader.php | 69 ++ .../src/Mustache/Loader/FilesystemLoader.php | 124 +++ .../src/Mustache/Loader/InlineLoader.php | 123 +++ .../src/Mustache/Loader/MutableLoader.php | 35 + .../src/Mustache/Loader/StringLoader.php | 39 + lib/mustache/src/Mustache/Logger.php | 144 ++++ .../src/Mustache/Logger/AbstractLogger.php | 121 +++ .../src/Mustache/Logger/StreamLogger.php | 194 +++++ lib/mustache/src/Mustache/Parser.php | 317 +++++++ lib/mustache/src/Mustache/Template.php | 181 ++++ lib/mustache/src/Mustache/Tokenizer.php | 331 ++++++++ lib/outputcomponents.php | 32 + lib/outputfactories.php | 25 +- lib/outputrenderers.php | 162 +++- lib/outputrequirementslib.php | 58 +- lib/templates/pix_icon.mustache | 31 + lib/thirdpartylibs.xml | 12 + lib/upgrade.txt | 1 + 66 files changed, 6532 insertions(+), 33 deletions(-) create mode 100644 lib/amd/build/mustache.min.js create mode 100644 lib/amd/build/notification.min.js create mode 100644 lib/amd/build/str.min.js create mode 100644 lib/amd/build/templates.min.js create mode 100644 lib/amd/build/url.min.js create mode 100644 lib/amd/build/yui.min.js create mode 100644 lib/amd/src/mustache.js create mode 100644 lib/amd/src/notification.js create mode 100644 lib/amd/src/str.js create mode 100644 lib/amd/src/templates.js create mode 100644 lib/amd/src/url.js create mode 100644 lib/amd/src/yui.js create mode 100644 lib/classes/output/external.php create mode 100644 lib/classes/output/mustache_filesystem_loader.php create mode 100644 lib/classes/output/mustache_javascript_helper.php create mode 100644 lib/classes/output/mustache_pix_helper.php create mode 100644 lib/classes/output/mustache_string_helper.php create mode 100644 lib/classes/output/mustache_uniqid_helper.php create mode 100644 lib/mustache/CONTRIBUTING.md create mode 100644 lib/mustache/LICENSE create mode 100644 lib/mustache/README.md create mode 100644 lib/mustache/composer.json create mode 100644 lib/mustache/readme_moodle.txt create mode 100644 lib/mustache/src/Mustache/Autoloader.php create mode 100644 lib/mustache/src/Mustache/Cache.php create mode 100644 lib/mustache/src/Mustache/Cache/AbstractCache.php create mode 100644 lib/mustache/src/Mustache/Cache/FilesystemCache.php create mode 100644 lib/mustache/src/Mustache/Cache/NoopCache.php create mode 100644 lib/mustache/src/Mustache/Compiler.php create mode 100644 lib/mustache/src/Mustache/Context.php create mode 100644 lib/mustache/src/Mustache/Engine.php create mode 100644 lib/mustache/src/Mustache/Exception.php create mode 100644 lib/mustache/src/Mustache/Exception/InvalidArgumentException.php create mode 100644 lib/mustache/src/Mustache/Exception/LogicException.php create mode 100644 lib/mustache/src/Mustache/Exception/RuntimeException.php create mode 100644 lib/mustache/src/Mustache/Exception/SyntaxException.php create mode 100644 lib/mustache/src/Mustache/Exception/UnknownFilterException.php create mode 100644 lib/mustache/src/Mustache/Exception/UnknownHelperException.php create mode 100644 lib/mustache/src/Mustache/Exception/UnknownTemplateException.php create mode 100644 lib/mustache/src/Mustache/HelperCollection.php create mode 100644 lib/mustache/src/Mustache/LambdaHelper.php create mode 100644 lib/mustache/src/Mustache/Loader.php create mode 100644 lib/mustache/src/Mustache/Loader/ArrayLoader.php create mode 100644 lib/mustache/src/Mustache/Loader/CascadingLoader.php create mode 100644 lib/mustache/src/Mustache/Loader/FilesystemLoader.php create mode 100644 lib/mustache/src/Mustache/Loader/InlineLoader.php create mode 100644 lib/mustache/src/Mustache/Loader/MutableLoader.php create mode 100644 lib/mustache/src/Mustache/Loader/StringLoader.php create mode 100644 lib/mustache/src/Mustache/Logger.php create mode 100644 lib/mustache/src/Mustache/Logger/AbstractLogger.php create mode 100644 lib/mustache/src/Mustache/Logger/StreamLogger.php create mode 100644 lib/mustache/src/Mustache/Parser.php create mode 100644 lib/mustache/src/Mustache/Template.php create mode 100644 lib/mustache/src/Mustache/Tokenizer.php create mode 100644 lib/templates/pix_icon.mustache diff --git a/lib/amd/build/ajax.min.js b/lib/amd/build/ajax.min.js index bd2e6236024..72274b8eea6 100644 --- a/lib/amd/build/ajax.min.js +++ b/lib/amd/build/ajax.min.js @@ -1 +1 @@ -define(["jquery","core/config"],function(a,b){var c=function(a){var b,c,d=this,e=null,f=0;for(f=0;f"'\/]/g,function(a){return q[a]})}function g(b,d){function f(){if(w&&!x)for(;q.length;)delete p[q.pop()];else q=[];w=!1,x=!1}function g(a){if("string"==typeof a&&(a=a.split(s,2)),!n(a)||2!==a.length)throw new Error("Invalid tags: "+a);k=new RegExp(c(a[0])+"\\s*"),l=new RegExp("\\s*"+c(a[1])),m=new RegExp("\\s*"+c("}"+a[1]))}if(!b)return[];var k,l,m,o=[],p=[],q=[],w=!1,x=!1;g(d||a.tags);for(var y,z,A,B,C,D,E=new j(b);!E.eos();){if(y=E.pos,A=E.scanUntil(k))for(var F=0,G=A.length;G>F;++F)B=A.charAt(F),e(B)?q.push(p.length):x=!0,p.push(["text",B,y,y+1]),y+=1,"\n"===B&&f();if(!E.scan(k))break;if(w=!0,z=E.scan(v)||"name",E.scan(r),"="===z?(A=E.scanUntil(t),E.scan(t),E.scanUntil(l)):"{"===z?(A=E.scanUntil(m),E.scan(u),E.scanUntil(l),z="&"):A=E.scanUntil(l),!E.scan(l))throw new Error("Unclosed tag at "+E.pos);if(C=[z,A,y,E.pos],p.push(C),"#"===z||"^"===z)o.push(C);else if("/"===z){if(D=o.pop(),!D)throw new Error('Unopened section "'+A+'" at '+y);if(D[1]!==A)throw new Error('Unclosed section "'+D[1]+'" at '+y)}else"name"===z||"{"===z||"&"===z?x=!0:"="===z&&g(A)}if(D=o.pop())throw new Error('Unclosed section "'+D[1]+'" at '+E.pos);return i(h(p))}function h(a){for(var b,c,d=[],e=0,f=a.length;f>e;++e)b=a[e],b&&("text"===b[0]&&c&&"text"===c[0]?(c[1]+=b[1],c[3]=b[3]):(d.push(b),c=b));return d}function i(a){for(var b,c,d=[],e=d,f=[],g=0,h=a.length;h>g;++g)switch(b=a[g],b[0]){case"#":case"^":e.push(b),f.push(b),e=b[4]=[];break;case"/":c=f.pop(),c[5]=b[2],e=f.length>0?f[f.length-1][4]:d;break;default:e.push(b)}return d}function j(a){this.string=a,this.tail=a,this.pos=0}function k(a,b){this.view=null==a?{}:a,this.cache={".":this.view},this.parent=b}function l(){this.cache={}}var m=Object.prototype.toString,n=Array.isArray||function(a){return"[object Array]"===m.call(a)},o=RegExp.prototype.test,p=/\S/,q={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},r=/\s*/,s=/\s+/,t=/\s*=/,u=/\s*\}/,v=/#|\^|\/|>|\{|&|=|!/;j.prototype.eos=function(){return""===this.tail},j.prototype.scan=function(a){var b=this.tail.match(a);if(!b||0!==b.index)return"";var c=b[0];return this.tail=this.tail.substring(c.length),this.pos+=c.length,c},j.prototype.scanUntil=function(a){var b,c=this.tail.search(a);switch(c){case-1:b=this.tail,this.tail="";break;case 0:b="";break;default:b=this.tail.substring(0,c),this.tail=this.tail.substring(c)}return this.pos+=b.length,b},k.prototype.push=function(a){return new k(a,this)},k.prototype.lookup=function(a){var c,d=this.cache;if(a in d)c=d[a];else{for(var e,f,g=this;g;){if(a.indexOf(".")>0)for(c=g.view,e=a.split("."),f=0;null!=c&&fl;++l)switch(h=c[l],h[0]){case"#":if(i=d.lookup(h[1]),!i)continue;if(n(i))for(var o=0,p=i.length;p>o;++o)j+=this.renderTokens(h[4],d.push(i[o]),e,f);else if("object"==typeof i||"string"==typeof i)j+=this.renderTokens(h[4],d.push(i),e,f);else if(b(i)){if("string"!=typeof f)throw new Error("Cannot use higher-order sections without the original template");i=i.call(d.view,f.slice(h[3],h[5]),g),null!=i&&(j+=i)}else j+=this.renderTokens(h[4],d,e,f);break;case"^":i=d.lookup(h[1]),(!i||n(i)&&0===i.length)&&(j+=this.renderTokens(h[4],d,e,f));break;case">":if(!e)continue;i=b(e)?e(h[1]):e[h[1]],null!=i&&(j+=this.renderTokens(this.parse(i),d,e,i));break;case"&":i=d.lookup(h[1]),null!=i&&(j+=i);break;case"name":i=d.lookup(h[1]),null!=i&&(j+=a.escape(i));break;case"text":j+=h[1]}return j},a.name="mustache.js",a.version="1.0.0",a.tags=["{{","}}"];var w=new l;a.clearCache=function(){return w.clearCache()},a.parse=function(a,b){return w.parse(a,b)},a.render=function(a,b,c){return w.render(a,b,c)},a.to_html=function(c,d,e,f){var g=a.render(c,d,e);return b(f)?void f(g):g},a.escape=f,a.Scanner=j,a.Context=k,a.Writer=l}); \ No newline at end of file diff --git a/lib/amd/build/notification.min.js b/lib/amd/build/notification.min.js new file mode 100644 index 00000000000..37952ecce65 --- /dev/null +++ b/lib/amd/build/notification.min.js @@ -0,0 +1 @@ +define(["core/yui"],function(a){return{alert:function(b,c,d){a.use("moodle-core-notification-alert",function(){var a=new M.core.alert({title:b,message:c,yesLabel:d});a.show()})},confirm:function(b,c,d,e,f){a.use("moodle-core-notification-confirm",function(){var a=new M.core.confirm({title:b,question:c,yesLabel:d,noLabel:e});a.on("complete-yes",function(){f()}),a.show()})},exception:function(b){b.backtrace&&(b.lineNumber=b.backtrace[0].line,b.fileName=b.backtrace[0].file,b.fileName="..."+b.fileName.substr(b.fileName.length-20),b.stack=b.debuginfo,b.name=b.errorcode),a.use("moodle-core-notification-exception",function(){var a=new M.core.exception(b);a.show()})}}}); \ No newline at end of file diff --git a/lib/amd/build/str.min.js b/lib/amd/build/str.min.js new file mode 100644 index 00000000000..bfdb8260537 --- /dev/null +++ b/lib/amd/build/str.min.js @@ -0,0 +1 @@ +define(["jquery","core/ajax"],function(a,b){return{get_string:function(b,c,d,e){var f=a.Deferred(),g=this.get_strings([{key:b,component:c,param:d,lang:e}]);return g.done(function(a){f.resolve(a[0])}).fail(function(a){f.reject(a)}),f.promise()},get_strings:function(c){var d,e=a.Deferred(),f=[],g=0,h=!1;for(g=0;g0&&(e=d.shift().trim()),d.length>0&&(g=d.shift().trim()),d.length>0&&(i=d.join(",").trim());var j=f.imageUrl(e,g),k={attributes:[{name:"src",value:j},{name:"alt",value:i},{name:"class",value:"smallicon"}]},m=h[l+"/core/pix_icon"];return c=a.render(m,k,n),c.trim()},n=function(a){var b="";return t(a,!1).done(function(a){b=a}).fail(e.exception),b},o=function(a,b){return j.push(b(a,this)),""},p=function(a,b){var c=a.split(","),d="",e="",f="";c.length>0&&(d=c.shift().trim()),c.length>0&&(e=c.shift().trim()),c.length>0&&(f=c.join(",").trim()),""!==f&&(f=b(f,this)),0===f.indexOf("{")&&0!==f.indexOf("{{")&&(f=JSON.parse(f));var g=i.length;return i.push({key:d,component:e,param:f}),"{{_s"+g+"}}"},q=function(a,b){l=b,i=[],j=[],a.uniqid=k++,a.str=function(){return p},a.pix=function(){return m},a.js=function(){return o},a.globals={config:g},a.currentTheme=b},r=function(a){var b="";j.length>0&&(b=j.join(";\n"));var c=0;for(c=0;c0?d.get_strings(i).done(function(a){var c;for(c=0;c").attr("type","text/javascript").html(a);b("head").append(c)}}}); \ No newline at end of file diff --git a/lib/amd/build/url.min.js b/lib/amd/build/url.min.js new file mode 100644 index 00000000000..e287766ab7a --- /dev/null +++ b/lib/amd/build/url.min.js @@ -0,0 +1 @@ +define(["core/config"],function(a){return{fileUrl:function(b,c){var d=a.wwwroot+b;return"/"!=c.charAt(0)&&(c="/"+c),d+=a.slasharguments?c:"?file="+encodeURIComponent(c)},relativeUrl:function(b){if(0===b.indexOf("http:")||0===b.indexOf("https:")||b.indexOf("://"))throw new Error("relativeUrl function does not accept absolute urls");return"/"!=b.charAt(0)&&(b="/"+b),"admin"!==a.admin&&(b=b.replace(/^\/admin\//,"/"+a.admin+"/")),a.wwwroot+b},imageUrl:function(a,b){return M.util.image_url(a,b)}}}); \ No newline at end of file diff --git a/lib/amd/build/yui.min.js b/lib/amd/build/yui.min.js new file mode 100644 index 00000000000..c2bc6fae7ee --- /dev/null +++ b/lib/amd/build/yui.min.js @@ -0,0 +1 @@ +define(function(){return Y}); \ No newline at end of file diff --git a/lib/amd/src/ajax.js b/lib/amd/src/ajax.js index 05863ea8d54..4f2cb778702 100644 --- a/lib/amd/src/ajax.js +++ b/lib/amd/src/ajax.js @@ -19,9 +19,11 @@ * In addition, it can batch multiple requests and return multiple responses. * * @module core/ajax + * @class ajax * @package core * @copyright 2015 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since 2.9 */ define(['jquery', 'core/config'], function($, config) { @@ -29,6 +31,8 @@ define(['jquery', 'core/config'], function($, config) { * Success handler. Called when the ajax call succeeds. Checks each response and * resolves or rejects the deferred from that request. * + * @method requestSuccess + * @private * @param {Object[]} responses Array of responses containing error, exception and data attributes. */ var requestSuccess = function(responses) { @@ -70,6 +74,8 @@ define(['jquery', 'core/config'], function($, config) { /** * Fail handler. Called when the ajax call fails. Rejects all deferreds. * + * @method requestFail + * @private * @param {jqXHR} jqXHR The ajax object. * @param {string} textStatus The status string. */ @@ -91,15 +97,23 @@ define(['jquery', 'core/config'], function($, config) { // Public variables and functions. /** * Make a series of ajax requests and return all the responses. + * + * @method call * @param {Object[]} Array of requests with each containing methodname and args properties. * done and fail callbacks can be set for each element in the array, or the * can be attached to the promises returned by this function. - * @return {Promise{}} Array of promises that will be resolved when the ajax call returns. + * @param {Boolean} async Optional, defaults to true. + * If false - this function will not return until the promises are resolved. + * @return {Promise[]} Array of promises that will be resolved when the ajax call returns. */ - call: function(requests) { + call: function(requests, async) { var ajaxRequestData = [], i, promises = []; + + if (typeof async === "undefined") { + async = true; + } for (i = 0; i < requests.length; i++) { var request = requests[i]; ajaxRequestData.push({ @@ -126,12 +140,20 @@ define(['jquery', 'core/config'], function($, config) { data: ajaxRequestData, context: requests, dataType: 'json', - processData: false + processData: false, + async: async }; - $.ajax(config.wwwroot + '/lib/ajax/service.php', settings) - .done(requestSuccess) - .fail(requestFail); + // Jquery deprecated done and fail with async=false so we need to do this 2 ways. + if (async) { + $.ajax(config.wwwroot + '/lib/ajax/service.php', settings) + .done(requestSuccess) + .fail(requestFail); + } else { + settings.success = requestSuccess; + settings.error = requestFail; + $.ajax(config.wwwroot + '/lib/ajax/service.php', settings); + } return promises; } diff --git a/lib/amd/src/config.js b/lib/amd/src/config.js index 848567f8f11..203326b0ae9 100644 --- a/lib/amd/src/config.js +++ b/lib/amd/src/config.js @@ -17,9 +17,11 @@ * Expose the M.cfg global variable. * * @module core/config + * @class config * @package core * @copyright 2015 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since 2.9 */ define(function() { diff --git a/lib/amd/src/first.js b/lib/amd/src/first.js index 22188f304b9..704c0c58306 100644 --- a/lib/amd/src/first.js +++ b/lib/amd/src/first.js @@ -22,5 +22,6 @@ * @package core * @copyright 2015 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since 2.9 */ define(function() { }); diff --git a/lib/amd/src/mustache.js b/lib/amd/src/mustache.js new file mode 100644 index 00000000000..6c96ca4c639 --- /dev/null +++ b/lib/amd/src/mustache.js @@ -0,0 +1,608 @@ +// The MIT License +// +// Copyright (c) 2009 Chris Wanstrath (Ruby) +// Copyright (c) 2010-2014 Jan Lehnardt (JavaScript) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +// Description of import into Moodle: +// Download from https://github.com/janl/mustache.js/releases +// Copy mustache.js into lib/amd/src/ in Moodle folder. +// Add the license as a comment to the file and these instructions. + +/*! + * mustache.js - Logic-less {{mustache}} templates with JavaScript + * http://github.com/janl/mustache.js + */ +/* jshint ignore:start */ + +(function (global, factory) { + if (typeof exports === "object" && exports) { + factory(exports); // CommonJS + } else if (typeof define === "function" && define.amd) { + define(['exports'], factory); // AMD + } else { + factory(global.Mustache = {}); //