resolving merge

This commit is contained in:
Jeremy Ashkenas
2012-04-02 13:06:44 -04:00
6 changed files with 197 additions and 120 deletions

View File

@@ -245,6 +245,7 @@
<li>- <a href="#mixin">mixin</a></li>
<li>- <a href="#uniqueId">uniqueId</a></li>
<li>- <a href="#escape">escape</a></li>
<li>- <a href="#result">result</a></li>
<li>- <a href="#template">template</a></li>
</ul>
@@ -1309,12 +1310,24 @@ _.uniqueId('contact_');
_.escape('Curly, Larry &amp; Moe');
=&gt; "Curly, Larry &amp;amp; Moe"</pre>
<p id="result">
<b class="header">result</b><code>_.result(object, property)</code>
<br />
If the value of the named property is a function then invoke it; otherwise, return it.
</p>
<pre>
var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
_.result(object, 'cheese');
=&gt; "crumpets"
_.result(object, 'stuff');
=&gt; "nonsense"</pre>
<p id="template">
<b class="header">template</b><code>_.template(templateString, [context])</code>
<br />
Compiles JavaScript templates into functions that can be evaluated
for rendering. Useful for rendering complicated bits of HTML from JSON
data sources. Template functions can both interpolate variables, using<br />
data sources. Template functions can both interpolate variables, using
<tt>&lt;%= &hellip; %&gt;</tt>, as well as execute arbitrary JavaScript code, with
<tt>&lt;% &hellip; %&gt;</tt>. If you wish to interpolate a value, and have
it be HTML-escaped, use <tt>&lt;%- &hellip; %&gt;</tt> When you evaluate a template function, pass in a
@@ -1368,6 +1381,18 @@ var template = _.template("Hello {{ name }}!");
template({name : "Mustache"});
=&gt; "Hello Mustache!"</pre>
<p>
Precompiling your templates can be a big help when debugging errors you can't
reproduce. This is because precompiled templates can provide line numbers and
a stack trace, something that is not possible when compiling templates on the client.
<b>template</b> provides the <b>source</b> property on the compiled template
function for easy precompilation.
</p>
<pre>&lt;script&gt;
JST.project = <%= _.template(jstText).source %>;
&lt;/script&gt;</pre>
<h2 id="chaining">Chaining</h2>

View File

@@ -275,6 +275,7 @@ $(document).ready(function() {
test('collections: size', function() {
equal(_.size({one : 1, two : 2, three : 3}), 3, 'can compute the size of an object');
equal(_.size([1, 2, 3]), 3, 'can compute the size of an array');
});
});

View File

@@ -101,8 +101,8 @@ $(document).ready(function() {
setTimeout(throttledIncr, 190);
setTimeout(throttledIncr, 220);
setTimeout(throttledIncr, 240);
_.delay(function(){ ok(counter == 1, "incr was called immediately"); }, 30);
_.delay(function(){ ok(counter == 4, "incr was throttled"); start(); }, 400);
_.delay(function(){ equal(counter, 1, "incr was called immediately"); }, 30);
_.delay(function(){ equal(counter, 4, "incr was throttled"); start(); }, 400);
});
asyncTest("functions: throttle arguments", 2, function() {
@@ -122,7 +122,7 @@ $(document).ready(function() {
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 100);
throttledIncr();
_.delay(function(){ ok(counter == 1, "incr was called once"); start(); }, 220);
_.delay(function(){ equal(counter, 1, "incr was called once"); start(); }, 220);
});
asyncTest("functions: throttle twice", 1, function() {
@@ -130,7 +130,7 @@ $(document).ready(function() {
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 100);
throttledIncr(); throttledIncr();
_.delay(function(){ ok(counter == 2, "incr was called twice"); start(); }, 220);
_.delay(function(){ equal(counter, 2, "incr was called twice"); start(); }, 220);
});
asyncTest("functions: debounce", 1, function() {
@@ -143,7 +143,7 @@ $(document).ready(function() {
setTimeout(debouncedIncr, 90);
setTimeout(debouncedIncr, 120);
setTimeout(debouncedIncr, 150);
_.delay(function(){ ok(counter == 1, "incr was debounced"); start(); }, 220);
_.delay(function(){ equal(counter, 1, "incr was debounced"); start(); }, 220);
});
asyncTest("functions: debounce asap", 2, function() {

View File

@@ -18,6 +18,7 @@
<body>
<div class="underscore-test">
<h1 id="qunit-header">Underscore Test Suite</h1>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-banner"></h2>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
@@ -34,6 +35,7 @@
<script type="text/html" id="template">
<%
// a comment
if (data) { data += 12345; }; %>
<li><%= data %></li>
</script>

View File

@@ -1,6 +1,14 @@
$(document).ready(function() {
module("Utility");
var templateSettings = _.templateSettings;
module("Utility", {
teardown: function() {
_.templateSettings = templateSettings;
}
});
test("utility: noConflict", function() {
var underscore = _.noConflict();
@@ -152,4 +160,18 @@ $(document).ready(function() {
equal(templateWithNull({planet : "world"}), "a null undefined world", "can handle missing escape and evaluate settings");
});
test('_.template handles \\u2028 & \\u2029', function() {
var tmpl = _.template('<p>\u2028<%= "\\u2028\\u2029" %>\u2029</p>');
strictEqual(tmpl(), '<p>\u2028\u2028\u2029\u2029</p>');
});
test('result calls functions and returns primitives', function() {
var obj = {w: '', x: 'x', y: function(){ return this.x; }};
strictEqual(_.result(obj, 'w'), '');
strictEqual(_.result(obj, 'x'), 'x');
strictEqual(_.result(obj, 'y'), 'x');
strictEqual(_.result(obj, 'z'), undefined);
strictEqual(_.result(null, 'x'), null);
});
});

View File

@@ -309,7 +309,7 @@
// Return the number of elements in an object.
_.size = function(obj) {
return _.toArray(obj).length;
return _.isArray(obj) ? obj.length : _.keys(obj).length;
};
// Array Functions
@@ -873,6 +873,14 @@
return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
};
// If the value of the named property is a function then invoke it;
// otherwise, return it.
_.result = function(object, property) {
if (object == null) return null;
var value = object[property];
return _.isFunction(value) ? value.call(object) : value;
};
// Add your own custom functions to the Underscore object, ensuring that
// they're correctly added to the OOP wrapper as well.
_.mixin = function(obj) {
@@ -902,39 +910,58 @@
// 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(/\\\\/g, '\\').replace(/\\'/g, "'");
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.
_.template = function(str, data) {
var c = _.templateSettings;
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
var settings = _.templateSettings;
var source = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
'with(obj||{}){__p.push(\'' +
str.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(c.escape || noMatch, function(match, code) {
return "',_.escape(" + unescape(code) + "),'";
})
.replace(c.interpolate || noMatch, function(match, code) {
return "'," + unescape(code) + ",'";
})
.replace(c.evaluate || noMatch, function(match, code) {
return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
})
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n')
.replace(/\t/g, '\\t')
+ "');}return __p.join('');";
var func = new Function('obj', '_', tmpl);
if (data) return func(data, _);
return function(data) {
return func.call(this, data, _);
str
.replace(escaper, function(match) {
return '\\' + escapes[match];
})
.replace(settings.escape || noMatch, function(match, code) {
return "',\n_.escape(" + unescape(code) + "),\n'";
})
.replace(settings.interpolate || noMatch, function(match, code) {
return "',\n" + unescape(code) + ",\n'";
})
.replace(settings.evaluate || noMatch, function(match, code) {
return "');\n" + unescape(code) + "\n;__p.push('";
})
+ "');\n}\nreturn __p.join('');";
var render = new Function('obj', '_', source);
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
template.source = 'function(obj){\n' + source + '\n}';
return template;
};
// Add a "chain" function, which will delegate to the wrapper.