Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Hi, I updated the whole template plugin based on current text loader plugin implementation and underscore template plugin #8

Open
wants to merge 1 commit into from

7 participants

@eeroan

Now it works with latest r.js and require.js versions both with node and java

@eeroan eeroan Support latest require.js by using current implementation of text plu…
…gin and also latest verison of Underscore.template
5bc85bc
@eeroan

Here's a gentre reminder for accepting this pull requiest :). It works with latest require.js and r.js versions (2.1.2) and supports bundling with both node and rhino unlike the current one

@themouette

I now use it and it works great
:+1:

@MarcoPolo

I third the motion! lets merge this in!

@dawsontoth

What's the advantage of embedding text.js and underscore's templating in tpl.js? Why not load them in as external dependencies, and tell people they need them too? That'd make PRs like this unnecessary.

For example: https://github.com/dawsontoth/requirejs-tpl/blob/master/tpl.js

@eeroan
@dawsontoth

K! I'll look in to it. I bet I'll fail if you couldn't figure it out, but at least I'll have tons of fun in the process. ;)

@ingro

+1!

@dawsontoth

Fixed it. Now it works for me doing both: 1) dynamic inclusion, and 2) bundling. I also added some very verbose log output when attempting to compile invalid templates. https://github.com/dawsontoth/requirejs-tpl/blob/master/tpl.js Note that I set { variable: 'data' } as my default options in tpl.js (for performance of ease of falsy checking). If you want other settings, do it there.

Example bundled output (note that the text! is included too... not optimal, I know):

define('tpl!views/common/charts/templates/loader.html',[],function() { return function(data){
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
__p+='<div class="charts_container charts_container_'+
((__t=( data.type ))==null?'':_.escape(__t))+
'">\n</div>';
return __p;
} });
define('text!views/common/charts/templates/loader.html',[],function () { return '<div class="charts_container charts_container_<%- data.type %>">\n</div>';});
@eeroan
@aglemann
@dawsontoth

Nice points, @eeroan and @aglemann. I've just pushed some changes. They work in my project great. I've also changed it back to use the default settings for better compatibility (and then you can override via the module config).

@eeroan

Found something interesting: With this version where text plugin is included as dependency this is what happens for minified version:

define('text!app/navigation.html',[],function () { return '<header id="header" class="siteContent">\n  <h1 class="logo"><a href="#">Logo</a></h1>\n</header>\n';});

define('tpl!app/navigation.html',[],function() { return function(obj){
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
with(obj||{}){
__p+='<header id="header" class="siteContent">\n  <h1 class="logo"><a href="#">Logo</a></h1>\n</header>\n';
}
return __p;
} });

So each template is introduced twice: on version for text plugin and another for tpl.

@dawsontoth

Fixed.

@nuragic

Hi! Briefly... How the templateSettings can be overridden?

@eeroan

By using module config (see above) with this fork: https://github.com/dawsontoth/requirejs-tpl/blob/master/tpl.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 19, 2012
  1. @eeroan

    Support latest require.js by using current implementation of text plu…

    eeroan authored
    …gin and also latest verison of Underscore.template
This page is out of date. Refresh to see the latest.
Showing with 346 additions and 196 deletions.
  1. +346 −196 tpl.js
View
542 tpl.js
@@ -1,205 +1,355 @@
/**
- * Adapted from the official plugin text.js
- *
- * Uses UnderscoreJS micro-templates : http://documentcloud.github.com/underscore/#template
- * @author Julien Cabanès <julien@zeeagency.com>
- * @version 0.2
- *
- * @license RequireJS text 0.24.0 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
+ * @license RequireJS text 1.0.0 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license.
* see: http://github.com/jrburke/requirejs for details
*/
/*jslint regexp: false, nomen: false, plusplus: false, strict: false */
/*global require: false, XMLHttpRequest: false, ActiveXObject: false,
- define: false, window: false, process: false, Packages: false,
- java: false */
+ define: false, window: false, process: false, Packages: false,
+ java: false, location: false */
(function () {
-//>>excludeStart('excludeTpl', pragmas.excludeTpl)
- var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
-
- xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
-
- bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
-
- buildMap = [],
-
- templateSettings = {
- evaluate : /<%([\s\S]+?)%>/g,
- interpolate : /<%=([\s\S]+?)%>/g
- },
-
- /**
- * JavaScript micro-templating, similar to John Resig's implementation.
- * Underscore templating handles arbitrary delimiters, preserves whitespace,
- * and correctly escapes quotes within interpolated code.
- */
- template = function(str, data) {
- var c = templateSettings;
- var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
- 'with(obj||{}){__p.push(\'' +
- str.replace(/\\/g, '\\\\')
- .replace(/'/g, "\\'")
- .replace(c.interpolate, function(match, code) {
- return "'," + code.replace(/\\'/g, "'") + ",'";
- })
- .replace(c.evaluate || null, function(match, code) {
- return "');" + code.replace(/\\'/g, "'")
- .replace(/[\r\n\t]/g, ' ') + "; __p.push('";
- })
- .replace(/\r/g, '')
- .replace(/\n/g, '')
- .replace(/\t/g, '')
- + "');}return __p.join('');";
- return tmpl;
-
- /** /
- var func = new Function('obj', tmpl);
- return data ? func(data) : func;
- /**/
- };
-//>>excludeEnd('excludeTpl')
-
- define(function () {
-//>>excludeStart('excludeTpl', pragmas.excludeTpl)
- var tpl;
-
- var get, fs;
- if (typeof window !== "undefined" && window.navigator && window.document) {
- get = function (url, callback) {
-
- var xhr = tpl.createXhr();
- xhr.open('GET', url, true);
- xhr.onreadystatechange = function (evt) {
- //Do not explicitly handle errors, those should be
- //visible via console output in the browser.
- if (xhr.readyState === 4) {
- callback(xhr.responseText);
- }
- };
- xhr.send(null);
- };
- } else if (typeof process !== "undefined" &&
- process.versions &&
- !!process.versions.node) {
- //Using special require.nodeRequire, something added by r.js.
- fs = require.nodeRequire('fs');
-
- get = function (url, callback) {
-
- callback(fs.readFileSync(url, 'utf8'));
- };
- }
- return tpl = {
- version: '0.24.0',
- strip: function (content) {
- //Strips <?xml ...?> declarations so that external SVG and XML
- //documents can be added to a document without worry. Also, if the string
- //is an HTML document, only the part inside the body tag is returned.
- if (content) {
- content = content.replace(xmlRegExp, "");
- var matches = content.match(bodyRegExp);
- if (matches) {
- content = matches[1];
- }
- } else {
- content = "";
- }
-
- return content;
- },
-
- jsEscape: function (content) {
- return content.replace(/(['\\])/g, '\\$1')
- .replace(/[\f]/g, "\\f")
- .replace(/[\b]/g, "\\b")
- .replace(/[\n]/g, "")
- .replace(/[\t]/g, "")
- .replace(/[\r]/g, "");
- },
-
- createXhr: function () {
- //Would love to dump the ActiveX crap in here. Need IE 6 to die first.
- var xhr, i, progId;
- if (typeof XMLHttpRequest !== "undefined") {
- return new XMLHttpRequest();
- } else {
- for (i = 0; i < 3; i++) {
- progId = progIds[i];
- try {
- xhr = new ActiveXObject(progId);
- } catch (e) {}
-
- if (xhr) {
- progIds = [progId]; // so faster next time
- break;
- }
- }
- }
-
- if (!xhr) {
- throw new Error("require.getXhr(): XMLHttpRequest not available");
- }
-
- return xhr;
- },
-
- get: get,
-
- load: function (name, req, onLoad, config) {
-
- //Name has format: some.module.filext!strip
- //The strip part is optional.
- //if strip is present, then that means only get the string contents
- //inside a body tag in an HTML string. For XML/SVG content it means
- //removing the <?xml ...?> declarations so the content can be inserted
- //into the current doc without problems.
-
- var strip = false, url, index = name.indexOf("."),
- modName = name.substring(0, index),
- ext = name.substring(index + 1, name.length);
-
- index = ext.indexOf("!");
-
- if (index !== -1) {
- //Pull off the strip arg.
- strip = ext.substring(index + 1, ext.length);
- strip = strip === "strip";
- ext = ext.substring(0, index);
- }
-
- //Load the tpl.
- url = 'nameToUrl' in req ? req.nameToUrl(modName, "." + ext) : req.toUrl(modName + "." + ext);
-
- tpl.get(url, function (content) {
- content = template(content);
-
- if(!config.isBuild) {
- //if(typeof window !== "undefined" && window.navigator && window.document) {
- content = new Function('obj', content);
- }
- content = strip ? tpl.strip(content) : content;
-
- if (config.isBuild && config.inlineText) {
- buildMap[name] = content;
- }
- onLoad(content);
- });
-
- },
-
- write: function (pluginName, moduleName, write) {
- if (moduleName in buildMap) {
- var content = tpl.jsEscape(buildMap[moduleName]);
- write("define('" + pluginName + "!" + moduleName +
- "', function() {return function(obj) { " +
- content.replace(/(\\')/g, "'").replace(/(\\\\)/g, "\\")+
- "}});\n");
- }
- }
- };
-//>>excludeEnd('excludeTpl')
- return function() {};
- });
-//>>excludeEnd('excludeTpl')
+ var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
+ xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
+ bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
+ hasLocation = typeof location !== 'undefined' && location.href,
+ defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
+ defaultHostName = hasLocation && location.hostname,
+ defaultPort = hasLocation && (location.port || undefined),
+ buildMap = [];
+
+ var templateSettings = {
+ evaluate : /<%([\s\S]+?)%>/g,
+ interpolate : /<%=([\s\S]+?)%>/g,
+ escape : /<%-([\s\S]+?)%>/g
+ };
+
+ // When customizing `templateSettings`, if you don't want to define an
+ // interpolation, evaluation or escaping regex, we need one that is
+ // guaranteed not to match.
+ var noMatch = /.^/;
+
+ // Certain characters need to be escaped so that they can be put into a
+ // string literal.
+ var escapes = {
+ '\\': '\\',
+ "'": "'",
+ 'r': '\r',
+ 'n': '\n',
+ 't': '\t',
+ 'u2028': '\u2028',
+ 'u2029': '\u2029'
+ };
+
+ for (var p in escapes) escapes[escapes[p]] = p;
+ var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
+ var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
+
+ // Within an interpolation, evaluation, or escaping, remove HTML escaping
+ // that had been previously added.
+ var unescape = function(code) {
+ return code.replace(unescaper, function(match, escape) {
+ return escapes[escape];
+ });
+ };
+
+ // JavaScript micro-templating, similar to John Resig's implementation.
+ // Underscore templating handles arbitrary delimiters, preserves whitespace,
+ // and correctly escapes quotes within interpolated code.
+ function template(text, data, settings) {
+ settings = templateSettings;
+
+ // Compile the template source, taking care to escape characters that
+ // cannot be included in a string literal and then unescape them in code
+ // blocks.
+ var source = "__p+='" + text
+ .replace(escaper, function(match) {
+ return '\\' + escapes[match];
+ })
+ .replace(settings.escape || noMatch, function(match, code) {
+ return "'+_.escape(" + unescape(code) + ")+'";
+ })
+ .replace(settings.interpolate || noMatch, function(match, code) {
+ return "'+(" + unescape(code) + ")+'";
+ })
+ .replace(settings.evaluate || noMatch, function(match, code) {
+ return "';" + unescape(code) + ";__p+='";
+ }) + "';";
+
+ // If a variable is not specified, place data values in local scope.
+ if (!settings.variable) source = 'with(obj||{}){' + source + '}';
+
+ source = "var __p='';" +
+ "var print=function(){__p+=Array.prototype.join.call(arguments, '')};" +
+ source + "return __p;";
+
+ var render = new Function(settings.variable || 'obj', '_', source);
+ if (data) return render(data, _);
+ var template = function(data) {
+ return render.call(this, data, _);
+ };
+
+ // Provide the compiled function source as a convenience for build time
+ // precompilation.
+ template.source = 'function(' + (settings.variable || 'obj') + '){' +
+ source + '}';
+
+ return template;
+ };
+
+
+ define(function () {
+ var text, get, fs;
+
+ if (typeof window !== "undefined" && window.navigator && window.document) {
+ get = function (url, callback) {
+ var xhr = text.createXhr();
+ xhr.open('GET', url, true);
+ xhr.onreadystatechange = function (evt) {
+ //Do not explicitly handle errors, those should be
+ //visible via console output in the browser.
+ if (xhr.readyState === 4) {
+ callback(xhr.responseText);
+ }
+ };
+ xhr.send(null);
+ };
+ } else if (typeof process !== "undefined" &&
+ process.versions &&
+ !!process.versions.node) {
+ //Using special require.nodeRequire, something added by r.js.
+ fs = require.nodeRequire('fs');
+
+ get = function (url, callback) {
+ callback(fs.readFileSync(url, 'utf8'));
+ };
+ } else if (typeof Packages !== 'undefined') {
+ //Why Java, why is this so awkward?
+ get = function (url, callback) {
+ var encoding = "utf-8",
+ file = new java.io.File(url),
+ lineSeparator = java.lang.System.getProperty("line.separator"),
+ input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
+ stringBuffer, line,
+ content = '';
+ try {
+ stringBuffer = new java.lang.StringBuffer();
+ line = input.readLine();
+
+ // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
+ // http://www.unicode.org/faq/utf_bom.html
+
+ // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
+ // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
+ if (line && line.length() && line.charAt(0) === 0xfeff) {
+ // Eat the BOM, since we've already found the encoding on this file,
+ // and we plan to concatenating this buffer with others; the BOM should
+ // only appear at the top of a file.
+ line = line.substring(1);
+ }
+
+ stringBuffer.append(line);
+
+ while ((line = input.readLine()) !== null) {
+ stringBuffer.append(lineSeparator);
+ stringBuffer.append(line);
+ }
+ //Make sure we return a JavaScript string and not a Java string.
+ content = String(stringBuffer.toString()); //String
+ } finally {
+ input.close();
+ }
+ callback(content);
+ };
+ }
+
+ text = {
+ version: '1.0.0',
+
+ strip: function (content) {
+ //Strips <?xml ...?> declarations so that external SVG and XML
+ //documents can be added to a document without worry. Also, if the string
+ //is an HTML document, only the part inside the body tag is returned.
+ if (content) {
+ content = content.replace(xmlRegExp, "");
+ var matches = content.match(bodyRegExp);
+ if (matches) {
+ content = matches[1];
+ }
+ } else {
+ content = "";
+ }
+ return content;
+ },
+
+ jsEscape: function (content) {
+ return content.replace(/(['\\])/g, '\\$1')
+ .replace(/[\f]/g, "\\f")
+ .replace(/[\b]/g, "\\b")
+ .replace(/[\n]/g, "\\n")
+ .replace(/[\t]/g, "\\t")
+ .replace(/[\r]/g, "\\r");
+ },
+
+ createXhr: function () {
+ //Would love to dump the ActiveX crap in here. Need IE 6 to die first.
+ var xhr, i, progId;
+ if (typeof XMLHttpRequest !== "undefined") {
+ return new XMLHttpRequest();
+ } else {
+ for (i = 0; i < 3; i++) {
+ progId = progIds[i];
+ try {
+ xhr = new ActiveXObject(progId);
+ } catch (e) {}
+
+ if (xhr) {
+ progIds = [progId]; // so faster next time
+ break;
+ }
+ }
+ }
+
+ if (!xhr) {
+ throw new Error("createXhr(): XMLHttpRequest not available");
+ }
+
+ return xhr;
+ },
+
+ get: get,
+
+ /**
+ * Parses a resource name into its component parts. Resource names
+ * look like: module/name.ext!strip, where the !strip part is
+ * optional.
+ * @param {String} name the resource name
+ * @returns {Object} with properties "moduleName", "ext" and "strip"
+ * where strip is a boolean.
+ */
+ parseName: function (name) {
+ var strip = false, index = name.indexOf("."),
+ modName = name.substring(0, index),
+ ext = name.substring(index + 1, name.length);
+
+ index = ext.indexOf("!");
+ if (index !== -1) {
+ //Pull off the strip arg.
+ strip = ext.substring(index + 1, ext.length);
+ strip = strip === "strip";
+ ext = ext.substring(0, index);
+ }
+
+ return {
+ moduleName: modName,
+ ext: ext,
+ strip: strip
+ };
+ },
+
+ xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,
+
+ /**
+ * Is an URL on another domain. Only works for browser use, returns
+ * false in non-browser environments. Only used to know if an
+ * optimized .js version of a text resource should be loaded
+ * instead.
+ * @param {String} url
+ * @returns Boolean
+ */
+ useXhr: function (url, protocol, hostname, port) {
+ var match = text.xdRegExp.exec(url),
+ uProtocol, uHostName, uPort;
+ if (!match) {
+ return true;
+ }
+ uProtocol = match[2];
+ uHostName = match[3];
+
+ uHostName = uHostName.split(':');
+ uPort = uHostName[1];
+ uHostName = uHostName[0];
+
+ return (!uProtocol || uProtocol === protocol) &&
+ (!uHostName || uHostName === hostname) &&
+ ((!uPort && !uHostName) || uPort === port);
+ },
+
+ finishLoad: function (name, strip, content, onLoad, config) {
+ content = strip ? text.strip(content) : content;
+ var tpl = template(content);
+ var tplString = tpl.source;
+ if (config.isBuild && config.inlineText) {
+ buildMap[name] = tplString;
+ }
+ onLoad(tpl);
+ },
+
+ load: function (name, req, onLoad, config) {
+ //Name has format: some.module.filext!strip
+ //The strip part is optional.
+ //if strip is present, then that means only get the string contents
+ //inside a body tag in an HTML string. For XML/SVG content it means
+ //removing the <?xml ...?> declarations so the content can be inserted
+ //into the current doc without problems.
+
+ var parsed = text.parseName(name),
+ nonStripName = parsed.moduleName + '.' + parsed.ext,
+ url = req.toUrl(nonStripName),
+ useXhr = (config && config.text && config.text.useXhr) ||
+ text.useXhr;
+
+ //Load the text. Use XHR if possible and in a browser.
+ if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
+ text.get(url, function (content) {
+ text.finishLoad(name, parsed.strip, content, onLoad, config);
+ });
+ } else {
+ //Need to fetch the resource across domains. Assume
+ //the resource has been optimized into a JS module. Fetch
+ //by the module name + extension, but do not include the
+ //!strip part to avoid file system issues.
+ req([nonStripName], function (content) {
+ text.finishLoad(parsed.moduleName + '.' + parsed.ext,
+ parsed.strip, content, onLoad, config);
+ });
+ }
+ },
+
+ write: function (pluginName, moduleName, write, config) {
+ if (moduleName in buildMap) {
+ var content = buildMap[moduleName];
+ write('define("'+ pluginName +'!'+ moduleName +'", function() { return ' + content + '; });\n');
+ }
+ },
+
+ writeFile: function (pluginName, moduleName, req, write, config) {
+ var parsed = text.parseName(moduleName),
+ nonStripName = parsed.moduleName + '.' + parsed.ext,
+ //Use a '.js' file name so that it indicates it is a
+ //script that can be loaded across domains.
+ fileName = req.toUrl(parsed.moduleName + '.' +
+ parsed.ext) + '.js';
+
+ //Leverage own load() method to load plugin value, but only
+ //write out values that do not have the strip argument,
+ //to avoid any potential issues with ! in file names.
+ text.load(nonStripName, req, function (value) {
+ //Use own write() method to construct full module value.
+ //But need to create shell that translates writeFile's
+ //write() to the right interface.
+ var textWrite = function (contents) {
+ return write(fileName, contents);
+ };
+ textWrite.asModule = function (moduleName, contents) {
+ return write.asModule(moduleName, fileName, contents);
+ };
+
+ text.write(pluginName, nonStripName, textWrite, config);
+ }, config);
+ }
+ };
+
+ return text;
+ });
}());
Something went wrong with that request. Please try again.