Browse Source

merge with janl

tags/0.5.0-vsc
thegrandpoobah 16 years ago
parent
commit
1f0c6b6241
29 changed files with 229 additions and 417 deletions
  1. +1
    -0
      .gitignore
  2. +5
    -1
      CHANGES.md
  3. +34
    -32
      Rakefile
  4. +2
    -0
      THANKS.md
  5. +1
    -0
      examples/array_of_partials_implicit_partial.2.html
  6. +5
    -0
      examples/array_of_partials_implicit_partial.html
  7. +3
    -0
      examples/array_of_partials_implicit_partial.js
  8. +5
    -0
      examples/array_of_partials_implicit_partial.txt
  9. +1
    -0
      examples/array_of_partials_partial.2.html
  10. +4
    -0
      examples/array_of_partials_partial.html
  11. +3
    -0
      examples/array_of_partials_partial.js
  12. +5
    -0
      examples/array_of_partials_partial.txt
  13. +1
    -0
      examples/bug_11_eating_whitespace.html
  14. +3
    -0
      examples/bug_11_eating_whitespace.js
  15. +1
    -0
      examples/bug_11_eating_whitespace.txt
  16. +4
    -0
      examples/partial_recursion.2.html
  17. +4
    -0
      examples/partial_recursion.html
  18. +11
    -0
      examples/partial_recursion.js
  19. +3
    -0
      examples/partial_recursion.txt
  20. +5
    -0
      examples/whitespace_partial.2.html
  21. +3
    -0
      examples/whitespace_partial.html
  22. +19
    -0
      examples/whitespace_partial.js
  23. +6
    -0
      examples/whitespace_partial.txt
  24. +0
    -296
      lib/mustache.js
  25. +7
    -0
      mustache-commonjs/mustache.js.tpl.post
  26. +6
    -0
      mustache-commonjs/mustache.js.tpl.pre
  27. +7
    -0
      mustache-commonjs/package.json
  28. +80
    -81
      mustache.js
  29. +0
    -7
      package.json

+ 1
- 0
.gitignore View File

@@ -2,3 +2,4 @@ runner.js
jquery.mustache.js
dojox
yui3
commonjs.mustache.js

+ 5
- 1
CHANGES.md View File

@@ -2,6 +2,11 @@

## 0.3.0 (??-??-????)

* Fix Rhino compat.
* CommonJS packaging is no longer a special case.
* DRY Rakefile.
* Allow whitespace around tag names.
* Fix partial scope.
* Fix Comments.
* Added inverted sections.
* Avoid double encoding of entities.
@@ -9,7 +14,6 @@
* Added higher order sections.



## 0.2.3 (28-03-2010)

* Better error message for missing partials.


+ 34
- 32
Rakefile View File

@@ -11,44 +11,46 @@ end
desc "Run all specs"
task :spec

desc "Package for CommonJS"
task :commonjs do
puts "Packaging for CommonJS"
`mkdir lib`
`cp mustache.js lib/mustache.js`
puts "Done."
def templated_build(name, opts={})
# Create a rule that uses the .tmpl.{pre,post} stuff to make a final,
# wrapped, output file.
# There is some extra complexity because Dojo and YUI3 use different
# template files and final locations.
short = name.downcase
source = "mustache-#{short}"
dependencies = ["mustache.js"] + Dir.glob("#{source}/*.tpl.*")

desc "Package for #{name}"
task short.to_sym => dependencies do
target_js = opts[:location] ? "mustache.js" : "#{short}.mustache.js"

puts "Packaging for #{name}"
sh "mkdir -p #{opts[:location]}" if opts[:location]
sh "cat #{source}/#{target_js}.tpl.pre mustache.js \
#{source}/#{target_js}.tpl.post > #{opts[:location] || '.'}/#{target_js}"

# extra
if opts[:extra]
sh "sed -e 's/{{version}}/#{version}/' #{source}/#{opts[:extra]} \
> #{opts[:location]}/#{opts[:extra]}"
end

puts "Done, see #{opts[:location] || '.'}/#{target_js}"

end
end

desc "Package for jQuery"
task :jquery do
puts "Packaging for jQuery"
source = "mustache-jquery"
target_jq = "jquery.mustache.js"
`cat #{source}/#{target_jq}.tpl.pre mustache.js #{source}/#{target_jq}.tpl.post > #{target_jq}`
puts "Done, see ./#{target_jq}"
end
templated_build "CommonJS", :location => "lib", :extra => "package.json"
templated_build "jQuery"
templated_build "Dojo", :location => "dojox/string"
templated_build "YUI3", :location => "yui3/mustache"

desc "Package for dojo"
task :dojo do
puts "Packaging for dojo"
source = "mustache-dojo"
target_js = "mustache.js"
`mkdir -p dojox; mkdir -p dojox/string`
`cat #{source}/#{target_js}.tpl.pre mustache.js #{source}/#{target_js}.tpl.post > dojox/string/#{target_js}`
puts "Done, see ./dojox/string/#{target_js} Include using dojo.require('dojox.string.mustache.'); "
def version
File.read("mustache.js").match('version: "([^\"]+)",$')[1]
end

desc "Package for YUI3"
task :yui3 do
puts "Packaging for YUI3"
source = "mustache-yui3"
target_js = "mustache.js"
`mkdir -p yui3; mkdir -p yui3/mustache`
`cat #{source}/#{target_js}.tpl.pre mustache.js #{source}/#{target_js}.tpl.post > yui3/mustache/#{target_js}`
puts "Done, see ./yui3/mustache/#{target_js}"
end

desc "Remove temporary files."
task :clean do
`git clean -fdx`
sh "git clean -fdx"
end

+ 2
- 0
THANKS.md View File

@@ -16,3 +16,5 @@ Mustache.js wouldn't kick ass if it weren't for these fine souls:
* Will Leinweber / will
* dpree
* Jason Smith / jhs
* Aaron Gibralter / agibralter
* Ross Boucher / boucher

+ 1
- 0
examples/array_of_partials_implicit_partial.2.html View File

@@ -0,0 +1 @@
{{.}}

+ 5
- 0
examples/array_of_partials_implicit_partial.html View File

@@ -0,0 +1,5 @@
Here is some stuff!
{{%IMPLICIT-ITERATOR}}
{{#numbers}}
{{>partial}}
{{/numbers}}

+ 3
- 0
examples/array_of_partials_implicit_partial.js View File

@@ -0,0 +1,3 @@
var partial_context = {
numbers: ['1', '2', '3', '4']
};

+ 5
- 0
examples/array_of_partials_implicit_partial.txt View File

@@ -0,0 +1,5 @@
Here is some stuff!
1
2
3
4

+ 1
- 0
examples/array_of_partials_partial.2.html View File

@@ -0,0 +1 @@
{{i}}

+ 4
- 0
examples/array_of_partials_partial.html View File

@@ -0,0 +1,4 @@
Here is some stuff!
{{#numbers}}
{{>partial}}
{{/numbers}}

+ 3
- 0
examples/array_of_partials_partial.js View File

@@ -0,0 +1,3 @@
var partial_context = {
numbers: [{i: '1'}, {i: '2'}, {i: '3'}, {i: '4'}]
};

+ 5
- 0
examples/array_of_partials_partial.txt View File

@@ -0,0 +1,5 @@
Here is some stuff!
1
2
3
4

+ 1
- 0
examples/bug_11_eating_whitespace.html View File

@@ -0,0 +1 @@
{{tag}} foo

+ 3
- 0
examples/bug_11_eating_whitespace.js View File

@@ -0,0 +1,3 @@
var bug_11_eating_whitespace = {
tag: "yo"
};

+ 1
- 0
examples/bug_11_eating_whitespace.txt View File

@@ -0,0 +1 @@
yo foo

+ 4
- 0
examples/partial_recursion.2.html View File

@@ -0,0 +1,4 @@
{{name}}
{{#children}}
{{>partial}}
{{/children}}

+ 4
- 0
examples/partial_recursion.html View File

@@ -0,0 +1,4 @@
{{name}}
{{#kids}}
{{>partial}}
{{/kids}}

+ 11
- 0
examples/partial_recursion.js View File

@@ -0,0 +1,11 @@
var partial_context = {
name: '1',
kids: [
{
name: '1.1',
children: [
{name: '1.1.1'}
]
}
]
};

+ 3
- 0
examples/partial_recursion.txt View File

@@ -0,0 +1,3 @@
1
1.1
1.1.1

+ 5
- 0
examples/whitespace_partial.2.html View File

@@ -0,0 +1,5 @@
Hello {{ name}}
You have just won ${{value }}!
{{# in_ca }}
Well, ${{ taxed_value }}, after taxes.
{{/ in_ca }}

+ 3
- 0
examples/whitespace_partial.html View File

@@ -0,0 +1,3 @@
<h1>{{ greeting }}</h1>
{{> partial }}
<h3>{{ farewell }}</h3>

+ 19
- 0
examples/whitespace_partial.js View File

@@ -0,0 +1,19 @@
var partial_context = {
greeting: function() {
return "Welcome";
},

farewell: function() {
return "Fair enough, right?";
},

partial: {
name: "Chris",
value: 10000,
taxed_value: function() {
return this.value - (this.value * 0.4);
},
in_ca: true
}
};


+ 6
- 0
examples/whitespace_partial.txt View File

@@ -0,0 +1,6 @@
<h1>Welcome</h1>
Hello Chris
You have just won $10000!
Well, $6000, after taxes.

<h3>Fair enough, right?</h3>

+ 0
- 296
lib/mustache.js View File

@@ -1,296 +0,0 @@
/*
mustache.js — Logic-less templates in JavaScript

See http://mustache.github.com/ for more info.
*/

var Mustache = function() {
var Renderer = function() {};

Renderer.prototype = {
otag: "{{",
ctag: "}}",
pragmas: {},
buffer: [],
pragmas_implemented: {
"IMPLICIT-ITERATOR": true
},

render: function(template, context, partials, in_recursion) {
// fail fast
if(template.indexOf(this.otag) == -1) {
if(in_recursion) {
return template;
} else {
this.send(template);
return;
}
}

if(!in_recursion) {
this.buffer = [];
}

template = this.render_pragmas(template);
var html = this.render_section(template, context, partials);
if(in_recursion) {
return this.render_tags(html, context, partials, in_recursion);
}

this.render_tags(html, context, partials, in_recursion);
},

/*
Sends parsed lines
*/
send: function(line) {
if(line != "") {
this.buffer.push(line);
}
},

/*
Looks for %PRAGMAS
*/
render_pragmas: function(template) {
// no pragmas
if(template.indexOf(this.otag + "%") == -1) {
return template;
}

var that = this;
var regex = new RegExp(this.otag + "%([\\w_-]+) ?([\\w]+=[\\w]+)?"
+ this.ctag);
return template.replace(regex, function(match, pragma, options) {
if(!that.pragmas_implemented[pragma]) {
throw({message: "This implementation of mustache doesn't understand the '"
+ pragma + "' pragma"});
}
that.pragmas[pragma] = {};
if(options) {
var opts = options.split("=");
that.pragmas[pragma][opts[0]] = opts[1];
}
return "";
// ignore unknown pragmas silently
});
},

/*
Tries to find a partial in the global scope and render it
*/
render_partial: function(name, context, partials) {
if(!partials || !partials[name]) {
throw({message: "unknown_partial '" + name + "'"});
}
if(typeof(context[name]) != "object") {
return partials[name];
}
return this.render(partials[name], context[name], partials, true);
},

/*
Renders boolean and enumerable sections
*/
render_section: function(template, context, partials) {
if(template.indexOf(this.otag + "#") == -1) {
return template;
}
var that = this;
// CSW - Added "+?" so it finds the tighest bound, not the widest
var regex = new RegExp(this.otag + "\\#(.+)" + this.ctag +
"\\s*([\\s\\S]+?)" + this.otag + "\\/\\1" + this.ctag + "\\s*", "mg");

// for each {{#foo}}{{/foo}} section do...
return template.replace(regex, function(match, name, content) {
var value = that.find(name, context);
if(that.is_array(value)) { // Enumerable, Let's loop!
return that.map(value, function(row) {
return that.render(content, that.merge(context,
that.create_context(row)), partials, true);
}).join("");
} else if(value) { // boolean section
return that.render(content, context, partials, true);
} else {
return "";
}
});
},

/*
Replace {{foo}} and friends with values from our view
*/
render_tags: function(template, context, partials, in_recursion) {
// tit for tat
var that = this;

var new_regex = function() {
return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\/#]+?)\\1?" +
that.ctag + "+", "g");
};

var regex = new_regex();
var lines = template.split("\n");
for (var i=0; i < lines.length; i++) {
lines[i] = lines[i].replace(regex, function(match, operator, name) {
switch(operator) {
case "!": // ignore comments
return match;
case "=": // set new delimiters, rebuild the replace regexp
that.set_delimiters(name);
regex = new_regex();
return "";
case ">": // render partial
return that.render_partial(name, context, partials);
case "{": // the triple mustache is unescaped
return that.find(name, context);
default: // escape the value
return that.escape(that.find(name, context));
}
}, this);
if(!in_recursion) {
this.send(lines[i]);
}
}

if(in_recursion) {
return lines.join("\n");
}
},

set_delimiters: function(delimiters) {
var dels = delimiters.split(" ");
this.otag = this.escape_regex(dels[0]);
this.ctag = this.escape_regex(dels[1]);
},

escape_regex: function(text) {
// thank you Simon Willison
if(!arguments.callee.sRE) {
var specials = [
'/', '.', '*', '+', '?', '|',
'(', ')', '[', ']', '{', '}', '\\'
];
arguments.callee.sRE = new RegExp(
'(\\' + specials.join('|\\') + ')', 'g'
);
}
return text.replace(arguments.callee.sRE, '\\$1');
},

/*
find `name` in current `context`. That is find me a value
from the view object
*/
find: function(name, context) {
name = this.trim(name);
if(typeof context[name] === "function") {
return context[name].apply(context);
}
if(context[name] !== undefined) {
return context[name];
}
// silently ignore unkown variables
return "";
},

// Utility methods

/*
Does away with nasty characters
*/
escape: function(s) {
return ((s == null) ? "" : s).toString().replace(/[&"<>\\]/g, function(s) {
switch(s) {
case "&": return "&amp;";
case "\\": return "\\\\";;
case '"': return '\"';;
case "<": return "&lt;";
case ">": return "&gt;";
default: return s;
}
});
},

/*
Merges all properties of object `b` into object `a`.
`b.property` overwrites a.property`
*/
merge: function(a, b) {
var _new = {};
for(var name in a) {
if(a.hasOwnProperty(name)) {
_new[name] = a[name];
}
};
for(var name in b) {
if(b.hasOwnProperty(name)) {
_new[name] = b[name];
}
};
return _new;
},

// by @langalex, support for arrays of strings
create_context: function(_context) {
if(this.is_object(_context)) {
return _context;
} else if(this.pragmas["IMPLICIT-ITERATOR"]) {
var iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator || ".";
var ctx = {};
ctx[iterator] = _context;
return ctx;
}
},

is_object: function(a) {
return a && typeof a == "object";
},

is_array: function(a) {
return Object.prototype.toString.call(a) === '[object Array]';
},

/*
Gets rid of leading and trailing whitespace
*/
trim: function(s) {
return s.replace(/^\s*|\s*$/g, "");
},

/*
Why, why, why? Because IE. Cry, cry cry.
*/
map: function(array, fn) {
if (typeof array.map == "function") {
return array.map(fn);
} else {
var r = [];
var l = array.length;
for(var i=0;i<l;i++) {
r.push(fn(array[i]));
}
return r;
}
}
};

return({
name: "mustache.js",
version: "0.2.3",

/*
Turns a template and view into HTML
*/
to_html: function(template, view, partials, send_fun) {
var renderer = new Renderer();
if(send_fun) {
renderer.send = send_fun;
}
renderer.render(template, view, partials);
if(!send_fun) {
return renderer.buffer.join("\n");
}
}
});
}();

+ 7
- 0
mustache-commonjs/mustache.js.tpl.post View File

@@ -0,0 +1,7 @@

exports.name = Mustache.name;
exports.version = Mustache.version;

exports.to_html = function() {
return Mustache.to_html.apply(this, arguments);
};

+ 6
- 0
mustache-commonjs/mustache.js.tpl.pre View File

@@ -0,0 +1,6 @@
/*
* CommonJS-compatible mustache.js module
*
* See http://github.com/janl/mustache.js for more info.
*/


+ 7
- 0
mustache-commonjs/package.json View File

@@ -0,0 +1,7 @@
{
"name": "mustache",
"author": "http://mustache.github.com/",
"description": "{{ mustache }} in JavaScript — Logic-less templates.",
"keywords": ["template"],
"version": "{{version}}"
}

+ 80
- 81
mustache.js View File

@@ -15,12 +15,15 @@ var Mustache = function() {
pragmas_implemented: {
"IMPLICIT-ITERATOR": true
},
context: {},

render: function(template, context, partials, in_recursion) {
// reset buffer
// TODO: make this non-lazy
if(!in_recursion)
this.buffer = [];
// reset buffer & set context
if(!in_recursion) {
this.context = context;
this.buffer = []; // TODO: make this non-lazy
}

// fail fast
if(!this.includes("", template)) {
if(in_recursion) {
@@ -31,10 +34,6 @@ var Mustache = function() {
}
}

if(!in_recursion) {
this.buffer = [];
}

template = this.render_pragmas(template);
var html = this.render_section(template, context, partials);
if(in_recursion) {
@@ -63,12 +62,13 @@ var Mustache = function() {
}

var that = this;
var regex = new RegExp(this.otag + "%([\\w_-]+) ?([\\w]+=[\\w]+)?"
+ this.ctag);
var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
this.ctag);
return template.replace(regex, function(match, pragma, options) {
if(!that.pragmas_implemented[pragma]) {
throw({message: "This implementation of mustache doesn't understand the '"
+ pragma + "' pragma"});
throw({message:
"This implementation of mustache doesn't understand the '" +
pragma + "' pragma"});
}
that.pragmas[pragma] = {};
if(options) {
@@ -81,14 +81,17 @@ var Mustache = function() {
},

/*
Tries to find a partial in the global scope and render it
Tries to find a partial in the curent scope and render it
*/
render_partial: function(name, context, partials) {
name = this.trim(name);
if(!partials || !partials[name]) {
throw({message: "unknown_partial '" + name + "'"});
}

return this.render(partials[name], context, partials, true);
if(typeof(context[name]) != "object") {
return this.render(partials[name], context, partials, true);
}
return this.render(partials[name], context[name], partials, true);
},

/*
@@ -101,15 +104,15 @@ var Mustache = function() {

var that = this;
// CSW - Added "+?" so it finds the tighest bound, not the widest
var regex = new RegExp(this.otag + "(\\^|\\#)(.+)" + this.ctag +
"\\s*([\\s\\S]+?)" + this.otag + "\\/\\2" + this.ctag +
var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
"\\s*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
"\\s*", "mg");

// for each {{#foo}}{{/foo}} section do...
return template.replace(regex, function(match, type, name, content) {
var value = that.find(name, context);
if(type == "^") { // inverted section
if(!value || that.is_array(value) && value.length == 0) {
if(!value || that.is_array(value) && value.length === 0) {
// false or empty list, render it
return that.render(content, context, partials, true);
} else {
@@ -118,17 +121,17 @@ var Mustache = function() {
} else if(type == "#") { // normal section
if(that.is_array(value)) { // Enumerable, Let's loop!
return that.map(value, function(row) {
return that.render(content, that.merge(context,
that.create_context(row)), partials, true);
return that.render(content, that.create_context(row),
partials, true);
}).join("");
} else if(that.is_object(value)) { // Object, Use it as subcontext!
return that.render(content,
that.merge(context, that.create_context(value)), partials, true);
return that.render(content, that.create_context(value),
partials, true);
} else if(typeof value === "function") {
// higher order section
return value.call(context, content, function(text) {
return that.render(text, context, partials, true);
})
});
} else if(value) { // boolean section
return that.render(content, context, partials, true);
} else {
@@ -146,37 +149,38 @@ var Mustache = function() {
var that = this;

var new_regex = function() {
return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\/#\^]+?)\\1?" +
return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
that.ctag + "+", "g");
};

var regex = new_regex();
var tag_replace_callback = function(match, operator, name) {
switch(operator) {
case "!": // ignore comments
return "";
case "=": // set new delimiters, rebuild the replace regexp
that.set_delimiters(name);
regex = new_regex();
return "";
case ">": // render partial
return that.render_partial(name, context, partials);
case "{": // the triple mustache is unescaped
return that.find(name, context);
default: // escape the value
return that.escape(that.find(name, context));
}
};
var lines = template.split("\n");
for (var i=0; i < lines.length; i++) {
lines[i] = lines[i].replace(regex, function(match, operator, name) {
switch(operator) {
case "!": // ignore comments
return "";
case "=": // set new delimiters, rebuild the replace regexp
that.set_delimiters(name);
regex = new_regex();
return "";
case ">": // render partial
return that.render_partial(name.replace(/^\s+/,""), context, partials);
case "{": // the triple mustache is unescaped
return that.find(name, context);
default: // escape the value
return that.escape(that.find(name, context));
}
}, this);
if(!in_recursion) {
this.send(lines[i]);
}
}

if(in_recursion) {
return lines.join("\n");
}
for(var i = 0; i < lines.length; i++) {
lines[i] = lines[i].replace(regex, tag_replace_callback, this);
if(!in_recursion) {
this.send(lines[i]);
}
}

if(in_recursion) {
return lines.join("\n");
}
},

set_delimiters: function(delimiters) {
@@ -196,7 +200,7 @@ var Mustache = function() {
'(\\' + specials.join('|\\') + ')', 'g'
);
}
return text.replace(arguments.callee.sRE, '\\$1');
return text.replace(arguments.callee.sRE, '\\$1');
},

/*
@@ -205,11 +209,24 @@ var Mustache = function() {
*/
find: function(name, context) {
name = this.trim(name);
if(typeof context[name] === "function") {
return context[name].apply(context);

// Checks whether a value is thruthy or false or 0
function is_kinda_truthy(bool) {
return bool === false || bool === 0 || bool;
}
if(context[name] !== undefined) {
return context[name];

var value;
if(is_kinda_truthy(context[name])) {
value = context[name];
} else if(is_kinda_truthy(this.context[name])) {
value = this.context[name];
}

if(typeof value === "function") {
return value.apply(context);
}
if(value !== undefined) {
return value;
}
// silently ignore unkown variables
return "";
@@ -226,37 +243,19 @@ var Mustache = function() {
Does away with nasty characters
*/
escape: function(s) {
return ((s == null) ? "" : s).toString().replace(/&(?!\w+;)|["<>\\]/g, function(s) {
s = String(s === null ? "" : s);
return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) {
switch(s) {
case "&": return "&amp;";
case "\\": return "\\\\";;
case '"': return '\"';;
case "<": return "&lt;";
case ">": return "&gt;";
default: return s;
case "&": return "&amp;";
case "\\": return "\\\\";
case '"': return '\"';
case "<": return "&lt;";
case ">": return "&gt;";
default: return s;
}
});
},

/*
Merges all properties of object `b` into object `a`.
`b.property` overwrites a.property`
*/
merge: function(a, b) {
var _new = {};
for(var name in a) {
if(a.hasOwnProperty(name)) {
_new[name] = a[name];
}
};
for(var name in b) {
if(b.hasOwnProperty(name)) {
_new[name] = b[name];
}
};
return _new;
},

// by @langalex, support for arrays of strings
create_context: function(_context) {
if(this.is_object(_context)) {
@@ -293,7 +292,7 @@ var Mustache = function() {
} else {
var r = [];
var l = array.length;
for(var i=0;i<l;i++) {
for(var i = 0; i < l; i++) {
r.push(fn(array[i]));
}
return r;


+ 0
- 7
package.json View File

@@ -1,7 +0,0 @@
{
"name": "mustache",
"author": "Jan Lehnardt",
"description": "{{mustaches}} in JavaScript — shameless port from @defunkt",
"keywords": ["template"],
"version": "0.2.3"
}

Loading…
Cancel
Save