Update vendors.

Former-commit-id: 88e9746e94e8ec899227b1a925bea4ab4d373fb0
This commit is contained in:
John-David Dalton
2012-08-31 12:47:55 -07:00
parent 141c10f6fe
commit 9100db55b0
13 changed files with 474 additions and 355 deletions

View File

@@ -183,7 +183,7 @@
attributes || (attributes = {}); attributes || (attributes = {});
if (options && options.collection) this.collection = options.collection; if (options && options.collection) this.collection = options.collection;
if (options && options.parse) attributes = this.parse(attributes); if (options && options.parse) attributes = this.parse(attributes);
if (defaults = getValue(this, 'defaults')) { if (defaults = _.result(this, 'defaults')) {
attributes = _.extend({}, defaults, attributes); attributes = _.extend({}, defaults, attributes);
} }
this.attributes = {}; this.attributes = {};
@@ -336,9 +336,7 @@
options.success = function(resp, status, xhr) { options.success = function(resp, status, xhr) {
if (!model.set(model.parse(resp, xhr), options)) return false; if (!model.set(model.parse(resp, xhr), options)) return false;
if (success) success(model, resp, options); if (success) success(model, resp, options);
model.trigger('sync', model, resp, options);
}; };
options.error = Backbone.wrapError(options.error, model, options);
return this.sync('read', this, options); return this.sync('read', this, options);
}, },
@@ -383,11 +381,9 @@
if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
if (!model.set(serverAttrs, options)) return false; if (!model.set(serverAttrs, options)) return false;
if (success) success(model, resp, options); if (success) success(model, resp, options);
model.trigger('sync', model, resp, options);
}; };
// Finish configuring and sending the Ajax request. // Finish configuring and sending the Ajax request.
options.error = Backbone.wrapError(options.error, model, options);
var xhr = this.sync(this.isNew() ? 'create' : 'update', this, options); var xhr = this.sync(this.isNew() ? 'create' : 'update', this, options);
// When using `wait`, reset attributes to original values unless // When using `wait`, reset attributes to original values unless
@@ -415,7 +411,6 @@
options.success = function(resp) { options.success = function(resp) {
if (options.wait || model.isNew()) destroy(); if (options.wait || model.isNew()) destroy();
if (success) success(model, resp, options); if (success) success(model, resp, options);
if (!model.isNew()) model.trigger('sync', model, resp, options);
}; };
if (this.isNew()) { if (this.isNew()) {
@@ -423,7 +418,6 @@
return false; return false;
} }
options.error = Backbone.wrapError(options.error, model, options);
var xhr = this.sync('delete', this, options); var xhr = this.sync('delete', this, options);
if (!options.wait) destroy(); if (!options.wait) destroy();
return xhr; return xhr;
@@ -433,7 +427,7 @@
// using Backbone's restful methods, override this to change the endpoint // using Backbone's restful methods, override this to change the endpoint
// that will be called. // that will be called.
url: function() { url: function() {
var base = getValue(this, 'urlRoot') || getValue(this.collection, 'url') || urlError(); var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
if (this.isNew()) return base; if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id); return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);
}, },
@@ -527,8 +521,8 @@
// Check if the model is currently in a valid state. It's only possible to // Check if the model is currently in a valid state. It's only possible to
// get into an *invalid* state if you're using silent changes. // get into an *invalid* state if you're using silent changes.
isValid: function() { isValid: function(options) {
return !this.validate || !this.validate(this.attributes); return !this.validate || !this.validate(this.attributes, options);
}, },
// Run validation against the next complete set of model attributes, // Run validation against the next complete set of model attributes,
@@ -539,11 +533,8 @@
attrs = _.extend({}, this.attributes, attrs); attrs = _.extend({}, this.attributes, attrs);
var error = this.validate(attrs, options); var error = this.validate(attrs, options);
if (!error) return true; if (!error) return true;
if (options && options.error) { if (options && options.error) options.error(this, error, options);
options.error(this, error, options); this.trigger('error', this, error, options);
} else {
this.trigger('error', this, error, options);
}
return false; return false;
} }
@@ -781,9 +772,7 @@
options.success = function(resp, status, xhr) { options.success = function(resp, status, xhr) {
collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options); collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);
if (success) success(collection, resp, options); if (success) success(collection, resp, options);
collection.trigger('sync', collection, resp, options);
}; };
options.error = Backbone.wrapError(options.error, collection, options);
return this.sync('read', this, options); return this.sync('read', this, options);
}, },
@@ -913,7 +902,6 @@
// }); // });
// //
route: function(route, name, callback) { route: function(route, name, callback) {
Backbone.history || (Backbone.history = new History);
if (!_.isRegExp(route)) route = this._routeToRegExp(route); if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (!callback) callback = this[name]; if (!callback) callback = this[name];
Backbone.history.route(route, _.bind(function(fragment) { Backbone.history.route(route, _.bind(function(fragment) {
@@ -1163,6 +1151,9 @@
}); });
// Create the default Backbone.history.
Backbone.history = new History;
// Backbone.View // Backbone.View
// ------------- // -------------
@@ -1260,7 +1251,7 @@
// This only works for delegate-able events: not `focus`, `blur`, and // This only works for delegate-able events: not `focus`, `blur`, and
// not `change`, `submit`, and `reset` in Internet Explorer. // not `change`, `submit`, and `reset` in Internet Explorer.
delegateEvents: function(events) { delegateEvents: function(events) {
if (!(events || (events = getValue(this, 'events')))) return; if (!(events || (events = _.result(this, 'events')))) return;
this.undelegateEvents(); this.undelegateEvents();
for (var key in events) { for (var key in events) {
var method = events[key]; var method = events[key];
@@ -1303,10 +1294,10 @@
// an element from the `id`, `className` and `tagName` properties. // an element from the `id`, `className` and `tagName` properties.
_ensureElement: function() { _ensureElement: function() {
if (!this.el) { if (!this.el) {
var attrs = _.extend({}, getValue(this, 'attributes')); var attrs = _.extend({}, _.result(this, 'attributes'));
if (this.id) attrs.id = getValue(this, 'id'); if (this.id) attrs.id = _.result(this, 'id');
if (this.className) attrs['class'] = getValue(this, 'className'); if (this.className) attrs['class'] = _.result(this, 'className');
this.setElement(this.make(getValue(this, 'tagName'), attrs), false); this.setElement(this.make(_.result(this, 'tagName'), attrs), false);
} else { } else {
this.setElement(this.el, false); this.setElement(this.el, false);
} }
@@ -1351,7 +1342,7 @@
// Ensure that we have a URL. // Ensure that we have a URL.
if (!options.url) { if (!options.url) {
params.url = getValue(model, 'url') || urlError(); params.url = _.result(model, 'url') || urlError();
} }
// Ensure that we have the appropriate request data. // Ensure that we have the appropriate request data.
@@ -1383,6 +1374,18 @@
params.processData = false; params.processData = false;
} }
var success = options.success;
options.success = function(resp, status, xhr) {
if (success) success(resp, status, xhr);
model.trigger('sync', model, resp, options);
};
var error = options.error;
options.error = function(xhr, status, thrown) {
if (error) error(model, xhr, options);
model.trigger('error', model, xhr, options);
};
// Make the request, allowing the user to override any Ajax options. // Make the request, allowing the user to override any Ajax options.
return Backbone.ajax(_.extend(params, options)); return Backbone.ajax(_.extend(params, options));
}; };
@@ -1392,24 +1395,9 @@
return Backbone.$.ajax.apply(Backbone.$, arguments); return Backbone.$.ajax.apply(Backbone.$, arguments);
}; };
// Wrap an optional error callback with a fallback error event.
Backbone.wrapError = function(onError, originalModel, options) {
return function(model, resp) {
resp = model === originalModel ? resp : model;
if (onError) {
onError(originalModel, resp, options);
} else {
originalModel.trigger('error', originalModel, resp, options);
}
};
};
// Helpers // Helpers
// ------- // -------
// Shared empty constructor function to aid in prototype-chain creation.
var ctor = function(){};
// Helper function to correctly set up the prototype chain, for subclasses. // Helper function to correctly set up the prototype chain, for subclasses.
// Similar to `goog.inherits`, but uses a hash of prototype properties and // Similar to `goog.inherits`, but uses a hash of prototype properties and
// class properties to be extended. // class properties to be extended.
@@ -1420,31 +1408,27 @@
// The constructor function for the new subclass is either defined by you // The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted // (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call the parent's constructor. // by us to simply call the parent's constructor.
if (protoProps && protoProps.hasOwnProperty('constructor')) { if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor; child = protoProps.constructor;
} else { } else {
child = function(){ parent.apply(this, arguments); }; child = function(){ parent.apply(this, arguments); };
} }
// Inherit class (static) properties from parent.
_.extend(child, parent);
// Set the prototype chain to inherit from `parent`, without calling // Set the prototype chain to inherit from `parent`, without calling
// `parent`'s constructor function. // `parent`'s constructor function.
ctor.prototype = parent.prototype; function Surrogate(){ this.constructor = child; };
child.prototype = new ctor(); Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate;
// Add prototype properties (instance properties) to the subclass, // Add prototype properties (instance properties) to the subclass,
// if supplied. // if supplied.
if (protoProps) _.extend(child.prototype, protoProps); if (protoProps) _.extend(child.prototype, protoProps);
// Add static properties to the constructor function, if supplied. // Add static properties to the constructor function, if supplied.
if (staticProps) _.extend(child, staticProps); _.extend(child, parent, staticProps);
// Correctly set child's `prototype.constructor`. // Set a convenience property in case the parent's prototype is needed
child.prototype.constructor = child; // later.
// Set a convenience property in case the parent's prototype is needed later.
child.__super__ = parent.prototype; child.__super__ = parent.prototype;
return child; return child;
@@ -1453,13 +1437,6 @@
// Set up inheritance for the model, collection, and view. // Set up inheritance for the model, collection, and view.
Model.extend = Collection.extend = Router.extend = View.extend = extend; Model.extend = Collection.extend = Router.extend = View.extend = extend;
// Helper function to get a value from a Backbone object as a property
// or as a function.
var getValue = function(object, prop) {
if (!(object && object[prop])) return null;
return _.isFunction(object[prop]) ? object[prop]() : object[prop];
};
// Throw an error when a URL is needed, and none is supplied. // Throw an error when a URL is needed, and none is supplied.
var urlError = function() { var urlError = function() {
throw new Error('A "url" property or function must be specified'); throw new Error('A "url" property or function must be specified');

View File

@@ -1,13 +1,12 @@
$(document).ready(function() { $(document).ready(function() {
var lastRequest = null;
var sync = Backbone.sync;
var a, b, c, d, e, col, otherCol; var a, b, c, d, e, col, otherCol;
module("Backbone.Collection", { module("Backbone.Collection", _.extend(new Environment, {
setup: function() { setup: function() {
Environment.prototype.setup.apply(this, arguments);
a = new Backbone.Model({id: 3, label: 'a'}); a = new Backbone.Model({id: 3, label: 'a'});
b = new Backbone.Model({id: 2, label: 'b'}); b = new Backbone.Model({id: 2, label: 'b'});
c = new Backbone.Model({id: 1, label: 'c'}); c = new Backbone.Model({id: 1, label: 'c'});
@@ -15,21 +14,9 @@ $(document).ready(function() {
e = null; e = null;
col = new Backbone.Collection([a,b,c,d]); col = new Backbone.Collection([a,b,c,d]);
otherCol = new Backbone.Collection(); otherCol = new Backbone.Collection();
Backbone.sync = function(method, model, options) {
lastRequest = {
method: method,
model: model,
options: options
};
};
},
teardown: function() {
Backbone.sync = sync;
} }
}); }));
test("Collection: new and sort", 7, function() { test("Collection: new and sort", 7, function() {
equal(col.first(), a, "a should be first"); equal(col.first(), a, "a should be first");
@@ -48,26 +35,18 @@ $(document).ready(function() {
}); });
test("Collection: new and parse", 3, function() { test("Collection: new and parse", 3, function() {
var MyCol = Backbone.Collection.extend({ var Collection = Backbone.Collection.extend({
// only save the models that have an even value.
parse : function(data) { parse : function(data) {
var onlyEven = []; return _.filter(data, function(datum) {
_.each(data, function(datum) { return datum.a % 2 === 0;
if (datum.a % 2 === 0) {
onlyEven.push(datum);
}
}); });
return onlyEven;
} }
}); });
anotherCol = new MyCol([ var models = [{a: 1}, {a: 2}, {a: 3}, {a: 4}];
{ a : 1 },{ a : 2 },{ a : 3 },{ a : 4 } var collection = new Collection(models, {parse: true});
], { parse : true }); strictEqual(collection.length, 2);
strictEqual(collection.first().get('a'), 2);
equal(anotherCol.length, 2); strictEqual(collection.last().get('a'), 4);
equal(anotherCol.first().get('a'), 2)
equal(anotherCol.last().get('a'), 4);
}); });
test("Collection: get, getByCid", 3, function() { test("Collection: get, getByCid", 3, function() {
@@ -379,21 +358,25 @@ $(document).ready(function() {
}); });
test("Collection: fetch", 4, function() { test("Collection: fetch", 4, function() {
col.fetch(); var collection = new Backbone.Collection;
equal(lastRequest.method, 'read'); collection.url = '/test';
equal(lastRequest.model, col); collection.fetch();
equal(lastRequest.options.parse, true); equal(this.syncArgs.method, 'read');
equal(this.syncArgs.model, collection);
equal(this.syncArgs.options.parse, true);
col.fetch({parse: false}); collection.fetch({parse: false});
equal(lastRequest.options.parse, false); equal(this.syncArgs.options.parse, false);
}); });
test("Collection: create", 4, function() { test("Collection: create", 4, function() {
var model = col.create({label: 'f'}, {wait: true}); var collection = new Backbone.Collection;
equal(lastRequest.method, 'create'); collection.url = '/test';
equal(lastRequest.model, model); var model = collection.create({label: 'f'}, {wait: true});
equal(this.syncArgs.method, 'create');
equal(this.syncArgs.model, model);
equal(model.get('label'), 'f'); equal(model.get('label'), 'f');
equal(model.collection, col); equal(model.collection, collection);
}); });
test("Collection: create enforces validation", 1, function() { test("Collection: create enforces validation", 1, function() {
@@ -524,16 +507,17 @@ $(document).ready(function() {
}); });
test("#714: access `model.collection` in a brand new model.", 2, function() { test("#714: access `model.collection` in a brand new model.", 2, function() {
var col = new Backbone.Collection; var collection = new Backbone.Collection;
collection.url = '/test';
var Model = Backbone.Model.extend({ var Model = Backbone.Model.extend({
set: function(attrs) { set: function(attrs) {
equal(attrs.prop, 'value'); equal(attrs.prop, 'value');
equal(this.collection, col); equal(this.collection, collection);
return this; return this;
} }
}); });
col.model = Model; collection.model = Model;
col.create({prop: 'value'}); collection.create({prop: 'value'});
}); });
test("#574, remove its own reference to the .models array.", 2, function() { test("#574, remove its own reference to the .models array.", 2, function() {
@@ -659,15 +643,10 @@ $(document).ready(function() {
}); });
test("#1412 - Trigger 'sync' event.", 2, function() { test("#1412 - Trigger 'sync' event.", 2, function() {
var collection = new Backbone.Collection([], { var collection = new Backbone.Collection;
model: Backbone.Model.extend({ collection.url = '/test';
sync: function(method, model, options) {
options.success();
}
})
});
collection.sync = function(method, model, options) { options.success(); };
collection.on('sync', function() { ok(true); }); collection.on('sync', function() { ok(true); });
Backbone.ajax = function(settings){ settings.success(); };
collection.fetch(); collection.fetch();
collection.create({id: 1}); collection.create({id: 1});
}); });

View File

@@ -1,22 +1,15 @@
$(document).ready(function() { $(document).ready(function() {
// Variable to catch the last request.
var lastRequest = null;
// Variable to catch ajax params.
var ajaxParams = null;
var sync = Backbone.sync;
var ajax = Backbone.ajax;
var urlRoot = null;
var proxy = Backbone.Model.extend(); var proxy = Backbone.Model.extend();
var klass = Backbone.Collection.extend({ var klass = Backbone.Collection.extend({
url : function() { return '/collection'; } url : function() { return '/collection'; }
}); });
var doc, collection; var doc, collection;
module("Backbone.Model", { module("Backbone.Model", _.extend(new Environment, {
setup: function() { setup: function() {
Environment.prototype.setup.apply(this, arguments);
doc = new proxy({ doc = new proxy({
id : '1-the-tempest', id : '1-the-tempest',
title : "The Tempest", title : "The Tempest",
@@ -25,27 +18,9 @@ $(document).ready(function() {
}); });
collection = new klass(); collection = new klass();
collection.add(doc); collection.add(doc);
Backbone.sync = function(method, model, options) {
lastRequest = {
method: method,
model: model,
options: options
};
sync.apply(this, arguments);
};
Backbone.ajax = function(params) { ajaxParams = params; };
urlRoot = Backbone.Model.prototype.urlRoot;
Backbone.Model.prototype.urlRoot = '/';
},
teardown: function() {
Backbone.sync = sync;
Backbone.ajax = ajax;
Backbone.Model.prototype.urlRoot = urlRoot;
} }
}); }));
test("Model: initialize", 3, function() { test("Model: initialize", 3, function() {
var Model = Backbone.Model.extend({ var Model = Backbone.Model.extend({
@@ -334,10 +309,12 @@ $(document).ready(function() {
}); });
test("Model: save within change event", 1, function () { test("Model: save within change event", 1, function () {
var env = this;
var model = new Backbone.Model({firstName : "Taylor", lastName: "Swift"}); var model = new Backbone.Model({firstName : "Taylor", lastName: "Swift"});
model.url = '/test';
model.on('change', function () { model.on('change', function () {
model.save(); model.save();
ok(_.isEqual(lastRequest.model, model)); ok(_.isEqual(env.syncArgs.model, model));
}); });
model.set({lastName: 'Hicks'}); model.set({lastName: 'Hicks'});
}); });
@@ -371,8 +348,8 @@ $(document).ready(function() {
test("Model: save", 2, function() { test("Model: save", 2, function() {
doc.save({title : "Henry V"}); doc.save({title : "Henry V"});
equal(lastRequest.method, 'update'); equal(this.syncArgs.method, 'update');
ok(_.isEqual(lastRequest.model, doc)); ok(_.isEqual(this.syncArgs.model, doc));
}); });
test("Model: save in positional style", 1, function() { test("Model: save in positional style", 1, function() {
@@ -388,14 +365,14 @@ $(document).ready(function() {
test("Model: fetch", 2, function() { test("Model: fetch", 2, function() {
doc.fetch(); doc.fetch();
equal(lastRequest.method, 'read'); equal(this.syncArgs.method, 'read');
ok(_.isEqual(lastRequest.model, doc)); ok(_.isEqual(this.syncArgs.model, doc));
}); });
test("Model: destroy", 3, function() { test("Model: destroy", 3, function() {
doc.destroy(); doc.destroy();
equal(lastRequest.method, 'delete'); equal(this.syncArgs.method, 'delete');
ok(_.isEqual(lastRequest.model, doc)); ok(_.isEqual(this.syncArgs.model, doc));
var newModel = new Backbone.Model; var newModel = new Backbone.Model;
equal(newModel.destroy(), false); equal(newModel.destroy(), false);
@@ -472,7 +449,7 @@ $(document).ready(function() {
equal(result, false); equal(result, false);
equal(model.get('a'), 100); equal(model.get('a'), 100);
equal(lastError, "Can't change admin status."); equal(lastError, "Can't change admin status.");
equal(boundError, undefined); equal(boundError, true);
}); });
test("Model: defaults always extend attrs (#459)", 2, function() { test("Model: defaults always extend attrs (#459)", 2, function() {
@@ -595,8 +572,9 @@ $(document).ready(function() {
test("save with `wait` succeeds without `validate`", 1, function() { test("save with `wait` succeeds without `validate`", 1, function() {
var model = new Backbone.Model(); var model = new Backbone.Model();
model.url = '/test';
model.save({x: 1}, {wait: true}); model.save({x: 1}, {wait: true});
ok(lastRequest.model === model); ok(this.syncArgs.model === model);
}); });
test("`hasChanged` for falsey keys", 2, function() { test("`hasChanged` for falsey keys", 2, function() {
@@ -616,18 +594,20 @@ $(document).ready(function() {
test("`save` with `wait` sends correct attributes", 5, function() { test("`save` with `wait` sends correct attributes", 5, function() {
var changed = 0; var changed = 0;
var model = new Backbone.Model({x: 1, y: 2}); var model = new Backbone.Model({x: 1, y: 2});
model.url = '/test';
model.on('change:x', function() { changed++; }); model.on('change:x', function() { changed++; });
model.save({x: 3}, {wait: true}); model.save({x: 3}, {wait: true});
deepEqual(JSON.parse(ajaxParams.data), {x: 3, y: 2}); deepEqual(JSON.parse(this.ajaxSettings.data), {x: 3, y: 2});
equal(model.get('x'), 1); equal(model.get('x'), 1);
equal(changed, 0); equal(changed, 0);
lastRequest.options.success({}); this.syncArgs.options.success({});
equal(model.get('x'), 3); equal(model.get('x'), 3);
equal(changed, 1); equal(changed, 1);
}); });
test("a failed `save` with `wait` doesn't leave attributes behind", 1, function() { test("a failed `save` with `wait` doesn't leave attributes behind", 1, function() {
var model = new Backbone.Model; var model = new Backbone.Model;
model.url = '/test';
model.save({x: 1}, {wait: true}); model.save({x: 1}, {wait: true});
equal(model.get('x'), void 0); equal(model.get('x'), void 0);
}); });
@@ -644,6 +624,7 @@ $(document).ready(function() {
test("save with wait validates attributes", 1, function() { test("save with wait validates attributes", 1, function() {
var model = new Backbone.Model(); var model = new Backbone.Model();
model.url = '/test';
model.validate = function() { ok(true); }; model.validate = function() { ok(true); };
model.save({x: 1}, {wait: true}); model.save({x: 1}, {wait: true});
}); });
@@ -776,24 +757,6 @@ $(document).ready(function() {
model.set({a: true}); model.set({a: true});
}); });
test("Backbone.wrapError triggers `'error'`", 12, function() {
var resp = {};
var options = {};
var model = new Backbone.Model();
model.on('error', error);
var callback = Backbone.wrapError(null, model, options);
callback(model, resp);
callback(resp);
callback = Backbone.wrapError(error, model, options);
callback(model, resp);
callback(resp);
function error(_model, _resp, _options) {
ok(model === _model);
ok(resp === _resp);
ok(options === _options);
}
});
test("#1179 - isValid returns true in the absence of validate.", 1, function() { test("#1179 - isValid returns true in the absence of validate.", 1, function() {
var model = new Backbone.Model(); var model = new Backbone.Model();
model.validate = null; model.validate = null;
@@ -831,8 +794,9 @@ $(document).ready(function() {
test("#1412 - Trigger 'sync' event.", 3, function() { test("#1412 - Trigger 'sync' event.", 3, function() {
var model = new Backbone.Model({id: 1}); var model = new Backbone.Model({id: 1});
model.sync = function(method, model, options) { options.success(); }; model.url = '/test';
model.on('sync', function() { ok(true); }); model.on('sync', function(){ ok(true); });
Backbone.ajax = function(settings){ settings.success(); };
model.fetch(); model.fetch();
model.save(); model.save();
model.destroy(); model.destroy();

View File

@@ -244,14 +244,11 @@ $(document).ready(function() {
}); });
test("#1003 - History is started before navigate is called", 1, function() { test("#1003 - History is started before navigate is called", 1, function() {
var history = new Backbone.History();
history.navigate = function(){
ok(Backbone.History.started);
};
Backbone.history.stop(); Backbone.history.stop();
history.start(); Backbone.history.navigate = function(){ ok(Backbone.History.started); };
Backbone.history.start();
// If this is not an old IE navigate will not be called. // If this is not an old IE navigate will not be called.
if (!history.iframe) ok(true); if (!Backbone.history.iframe) ok(true);
}); });
test("Router: route callback gets passed decoded values", 3, function() { test("Router: route callback gets passed decoded values", 3, function() {

View File

@@ -1,8 +1,5 @@
$(document).ready(function() { $(document).ready(function() {
var ajax = Backbone.ajax;
var lastRequest = null;
var Library = Backbone.Collection.extend({ var Library = Backbone.Collection.extend({
url : function() { return '/library'; } url : function() { return '/library'; }
}); });
@@ -14,42 +11,36 @@ $(document).ready(function() {
length : 123 length : 123
}; };
module("Backbone.sync", { module("Backbone.sync", _.extend(new Environment, {
setup : function() { setup : function() {
library = new Library(); Environment.prototype.setup.apply(this, arguments);
Backbone.ajax = function(obj) { library = new Library;
lastRequest = obj;
};
library.create(attrs, {wait: false}); library.create(attrs, {wait: false});
},
teardown: function() {
Backbone.ajax = ajax;
} }
}); }));
test("sync: read", 4, function() { test("sync: read", 4, function() {
library.fetch(); library.fetch();
equal(lastRequest.url, '/library'); equal(this.ajaxSettings.url, '/library');
equal(lastRequest.type, 'GET'); equal(this.ajaxSettings.type, 'GET');
equal(lastRequest.dataType, 'json'); equal(this.ajaxSettings.dataType, 'json');
ok(_.isEmpty(lastRequest.data)); ok(_.isEmpty(this.ajaxSettings.data));
}); });
test("sync: passing data", 3, function() { test("sync: passing data", 3, function() {
library.fetch({data: {a: 'a', one: 1}}); library.fetch({data: {a: 'a', one: 1}});
equal(lastRequest.url, '/library'); equal(this.ajaxSettings.url, '/library');
equal(lastRequest.data.a, 'a'); equal(this.ajaxSettings.data.a, 'a');
equal(lastRequest.data.one, 1); equal(this.ajaxSettings.data.one, 1);
}); });
test("sync: create", 6, function() { test("sync: create", 6, function() {
equal(lastRequest.url, '/library'); equal(this.ajaxSettings.url, '/library');
equal(lastRequest.type, 'POST'); equal(this.ajaxSettings.type, 'POST');
equal(lastRequest.dataType, 'json'); equal(this.ajaxSettings.dataType, 'json');
var data = JSON.parse(lastRequest.data); var data = JSON.parse(this.ajaxSettings.data);
equal(data.title, 'The Tempest'); equal(data.title, 'The Tempest');
equal(data.author, 'Bill Shakespeare'); equal(data.author, 'Bill Shakespeare');
equal(data.length, 123); equal(data.length, 123);
@@ -57,10 +48,10 @@ $(document).ready(function() {
test("sync: update", 7, function() { test("sync: update", 7, function() {
library.first().save({id: '1-the-tempest', author: 'William Shakespeare'}); library.first().save({id: '1-the-tempest', author: 'William Shakespeare'});
equal(lastRequest.url, '/library/1-the-tempest'); equal(this.ajaxSettings.url, '/library/1-the-tempest');
equal(lastRequest.type, 'PUT'); equal(this.ajaxSettings.type, 'PUT');
equal(lastRequest.dataType, 'json'); equal(this.ajaxSettings.dataType, 'json');
var data = JSON.parse(lastRequest.data); var data = JSON.parse(this.ajaxSettings.data);
equal(data.id, '1-the-tempest'); equal(data.id, '1-the-tempest');
equal(data.title, 'The Tempest'); equal(data.title, 'The Tempest');
equal(data.author, 'William Shakespeare'); equal(data.author, 'William Shakespeare');
@@ -70,11 +61,11 @@ $(document).ready(function() {
test("sync: update with emulateHTTP and emulateJSON", 7, function() { test("sync: update with emulateHTTP and emulateJSON", 7, function() {
Backbone.emulateHTTP = Backbone.emulateJSON = true; Backbone.emulateHTTP = Backbone.emulateJSON = true;
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}); library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
equal(lastRequest.url, '/library/2-the-tempest'); equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(lastRequest.type, 'POST'); equal(this.ajaxSettings.type, 'POST');
equal(lastRequest.dataType, 'json'); equal(this.ajaxSettings.dataType, 'json');
equal(lastRequest.data._method, 'PUT'); equal(this.ajaxSettings.data._method, 'PUT');
var data = JSON.parse(lastRequest.data.model); var data = JSON.parse(this.ajaxSettings.data.model);
equal(data.id, '2-the-tempest'); equal(data.id, '2-the-tempest');
equal(data.author, 'Tim Shakespeare'); equal(data.author, 'Tim Shakespeare');
equal(data.length, 123); equal(data.length, 123);
@@ -84,10 +75,10 @@ $(document).ready(function() {
test("sync: update with just emulateHTTP", 6, function() { test("sync: update with just emulateHTTP", 6, function() {
Backbone.emulateHTTP = true; Backbone.emulateHTTP = true;
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}); library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
equal(lastRequest.url, '/library/2-the-tempest'); equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(lastRequest.type, 'POST'); equal(this.ajaxSettings.type, 'POST');
equal(lastRequest.contentType, 'application/json'); equal(this.ajaxSettings.contentType, 'application/json');
var data = JSON.parse(lastRequest.data); var data = JSON.parse(this.ajaxSettings.data);
equal(data.id, '2-the-tempest'); equal(data.id, '2-the-tempest');
equal(data.author, 'Tim Shakespeare'); equal(data.author, 'Tim Shakespeare');
equal(data.length, 123); equal(data.length, 123);
@@ -97,10 +88,10 @@ $(document).ready(function() {
test("sync: update with just emulateJSON", 6, function() { test("sync: update with just emulateJSON", 6, function() {
Backbone.emulateJSON = true; Backbone.emulateJSON = true;
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}); library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
equal(lastRequest.url, '/library/2-the-tempest'); equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(lastRequest.type, 'PUT'); equal(this.ajaxSettings.type, 'PUT');
equal(lastRequest.contentType, 'application/x-www-form-urlencoded'); equal(this.ajaxSettings.contentType, 'application/x-www-form-urlencoded');
var data = JSON.parse(lastRequest.data.model); var data = JSON.parse(this.ajaxSettings.data.model);
equal(data.id, '2-the-tempest'); equal(data.id, '2-the-tempest');
equal(data.author, 'Tim Shakespeare'); equal(data.author, 'Tim Shakespeare');
equal(data.length, 123); equal(data.length, 123);
@@ -110,26 +101,26 @@ $(document).ready(function() {
test("sync: read model", 3, function() { test("sync: read model", 3, function() {
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}); library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
library.first().fetch(); library.first().fetch();
equal(lastRequest.url, '/library/2-the-tempest'); equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(lastRequest.type, 'GET'); equal(this.ajaxSettings.type, 'GET');
ok(_.isEmpty(lastRequest.data)); ok(_.isEmpty(this.ajaxSettings.data));
}); });
test("sync: destroy", 3, function() { test("sync: destroy", 3, function() {
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}); library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
library.first().destroy({wait: true}); library.first().destroy({wait: true});
equal(lastRequest.url, '/library/2-the-tempest'); equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(lastRequest.type, 'DELETE'); equal(this.ajaxSettings.type, 'DELETE');
equal(lastRequest.data, null); equal(this.ajaxSettings.data, null);
}); });
test("sync: destroy with emulateHTTP", 3, function() { test("sync: destroy with emulateHTTP", 3, function() {
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}); library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
Backbone.emulateHTTP = Backbone.emulateJSON = true; Backbone.emulateHTTP = Backbone.emulateJSON = true;
library.first().destroy(); library.first().destroy();
equal(lastRequest.url, '/library/2-the-tempest'); equal(this.ajaxSettings.url, '/library/2-the-tempest');
equal(lastRequest.type, 'POST'); equal(this.ajaxSettings.type, 'POST');
equal(JSON.stringify(lastRequest.data), '{"_method":"DELETE"}'); equal(JSON.stringify(this.ajaxSettings.data), '{"_method":"DELETE"}');
Backbone.emulateHTTP = Backbone.emulateJSON = false; Backbone.emulateHTTP = Backbone.emulateJSON = false;
}); });
@@ -139,7 +130,7 @@ $(document).ready(function() {
model.fetch(); model.fetch();
}); });
model.fetch({url: '/one/two'}); model.fetch({url: '/one/two'});
equal(lastRequest.url, '/one/two'); equal(this.ajaxSettings.url, '/one/two');
}); });
test("#1052 - `options` is optional.", 0, function() { test("#1052 - `options` is optional.", 0, function() {
@@ -157,4 +148,13 @@ $(document).ready(function() {
Backbone.sync('create', model); Backbone.sync('create', model);
}); });
test("Call provided error callback on error.", 1, function() {
var model = new Backbone.Model;
model.url = '/test';
Backbone.sync('read', model, {
error: function() { ok(true); }
});
this.ajaxSettings.error();
});
}); });

View File

@@ -1,11 +1,11 @@
/** /**
* QUnit v1.8.0 - A JavaScript Unit Testing Framework * QUnit v1.10.0 - A JavaScript Unit Testing Framework
* *
* http://docs.jquery.com/QUnit * http://qunitjs.com
* *
* Copyright (c) 2012 John Resig, Jörn Zaefferer * Copyright 2012 jQuery Foundation and other contributors
* Dual licensed under the MIT (MIT-LICENSE.txt) * Released under the MIT license.
* or GPL (GPL-LICENSE.txt) licenses. * http://jquery.org/license
*/ */
/** Font Family and Sizes */ /** Font Family and Sizes */
@@ -20,7 +20,7 @@
/** Resets */ /** Resets */
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
@@ -38,10 +38,10 @@
line-height: 1em; line-height: 1em;
font-weight: normal; font-weight: normal;
border-radius: 15px 15px 0 0; border-radius: 5px 5px 0 0;
-moz-border-radius: 15px 15px 0 0; -moz-border-radius: 5px 5px 0 0;
-webkit-border-top-right-radius: 15px; -webkit-border-top-right-radius: 5px;
-webkit-border-top-left-radius: 15px; -webkit-border-top-left-radius: 5px;
} }
#qunit-header a { #qunit-header a {
@@ -54,9 +54,9 @@
color: #fff; color: #fff;
} }
#qunit-header label { #qunit-testrunner-toolbar label {
display: inline-block; display: inline-block;
padding-left: 0.5em; padding: 0 .5em 0 .1em;
} }
#qunit-banner { #qunit-banner {
@@ -67,6 +67,7 @@
padding: 0.5em 0 0.5em 2em; padding: 0.5em 0 0.5em 2em;
color: #5E740B; color: #5E740B;
background-color: #eee; background-color: #eee;
overflow: hidden;
} }
#qunit-userAgent { #qunit-userAgent {
@@ -76,6 +77,9 @@
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
} }
#qunit-modulefilter-container {
float: right;
}
/** Tests: Pass/Fail */ /** Tests: Pass/Fail */
@@ -113,13 +117,9 @@
background-color: #fff; background-color: #fff;
border-radius: 15px; border-radius: 5px;
-moz-border-radius: 15px; -moz-border-radius: 5px;
-webkit-border-radius: 15px; -webkit-border-radius: 5px;
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
} }
#qunit-tests table { #qunit-tests table {
@@ -162,8 +162,7 @@
#qunit-tests b.failed { color: #710909; } #qunit-tests b.failed { color: #710909; }
#qunit-tests li li { #qunit-tests li li {
margin: 0.5em; padding: 5px;
padding: 0.4em 0.5em 0.4em 0.5em;
background-color: #fff; background-color: #fff;
border-bottom: none; border-bottom: none;
list-style-position: inside; list-style-position: inside;
@@ -172,9 +171,9 @@
/*** Passing Styles */ /*** Passing Styles */
#qunit-tests li li.pass { #qunit-tests li li.pass {
color: #5E740B; color: #3c510c;
background-color: #fff; background-color: #fff;
border-left: 26px solid #C6E746; border-left: 10px solid #C6E746;
} }
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
@@ -190,15 +189,15 @@
#qunit-tests li li.fail { #qunit-tests li li.fail {
color: #710909; color: #710909;
background-color: #fff; background-color: #fff;
border-left: 26px solid #EE5757; border-left: 10px solid #EE5757;
white-space: pre; white-space: pre;
} }
#qunit-tests > li:last-child { #qunit-tests > li:last-child {
border-radius: 0 0 15px 15px; border-radius: 0 0 5px 5px;
-moz-border-radius: 0 0 15px 15px; -moz-border-radius: 0 0 5px 5px;
-webkit-border-bottom-right-radius: 15px; -webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 15px; -webkit-border-bottom-left-radius: 5px;
} }
#qunit-tests .fail { color: #000000; background-color: #EE5757; } #qunit-tests .fail { color: #000000; background-color: #EE5757; }

View File

@@ -1,11 +1,11 @@
/** /**
* QUnit v1.8.0 - A JavaScript Unit Testing Framework * QUnit v1.10.0 - A JavaScript Unit Testing Framework
* *
* http://docs.jquery.com/QUnit * http://qunitjs.com
* *
* Copyright (c) 2012 John Resig, Jörn Zaefferer * Copyright 2012 jQuery Foundation and other contributors
* Dual licensed under the MIT (MIT-LICENSE.txt) * Released under the MIT license.
* or GPL (GPL-LICENSE.txt) licenses. * http://jquery.org/license
*/ */
(function( window ) { (function( window ) {
@@ -17,6 +17,8 @@ var QUnit,
fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""), fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
toString = Object.prototype.toString, toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty, hasOwn = Object.prototype.hasOwnProperty,
// Keep a local reference to Date (GH-283)
Date = window.Date,
defined = { defined = {
setTimeout: typeof window.setTimeout !== "undefined", setTimeout: typeof window.setTimeout !== "undefined",
sessionStorage: (function() { sessionStorage: (function() {
@@ -304,7 +306,8 @@ QUnit = {
// call on start of module test to prepend name to all tests // call on start of module test to prepend name to all tests
module: function( name, testEnvironment ) { module: function( name, testEnvironment ) {
config.currentModule = name; config.currentModule = name;
config.currentModuleTestEnviroment = testEnvironment; config.currentModuleTestEnvironment = testEnvironment;
config.modules[name] = true;
}, },
asyncTest: function( testName, expected, callback ) { asyncTest: function( testName, expected, callback ) {
@@ -336,7 +339,7 @@ QUnit = {
async: async, async: async,
callback: callback, callback: callback,
module: config.currentModule, module: config.currentModule,
moduleTestEnvironment: config.currentModuleTestEnviroment, moduleTestEnvironment: config.currentModuleTestEnvironment,
stack: sourceFromStacktrace( 2 ) stack: sourceFromStacktrace( 2 )
}); });
@@ -349,7 +352,11 @@ QUnit = {
// Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
expect: function( asserts ) { expect: function( asserts ) {
config.current.expected = asserts; if (arguments.length === 1) {
config.current.expected = asserts;
} else {
return config.current.expected;
}
}, },
start: function( count ) { start: function( count ) {
@@ -403,6 +410,8 @@ QUnit = {
QUnit.assert = { QUnit.assert = {
/** /**
* Asserts rough true-ish result. * Asserts rough true-ish result.
* @name ok
* @function
* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
*/ */
ok: function( result, msg ) { ok: function( result, msg ) {
@@ -413,6 +422,8 @@ QUnit.assert = {
var source, var source,
details = { details = {
module: config.current.module,
name: config.current.testName,
result: result, result: result,
message: msg message: msg
}; };
@@ -437,36 +448,59 @@ QUnit.assert = {
/** /**
* Assert that the first two arguments are equal, with an optional message. * Assert that the first two arguments are equal, with an optional message.
* Prints out both actual and expected values. * Prints out both actual and expected values.
* @name equal
* @function
* @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" ); * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
*/ */
equal: function( actual, expected, message ) { equal: function( actual, expected, message ) {
QUnit.push( expected == actual, actual, expected, message ); QUnit.push( expected == actual, actual, expected, message );
}, },
/**
* @name notEqual
* @function
*/
notEqual: function( actual, expected, message ) { notEqual: function( actual, expected, message ) {
QUnit.push( expected != actual, actual, expected, message ); QUnit.push( expected != actual, actual, expected, message );
}, },
/**
* @name deepEqual
* @function
*/
deepEqual: function( actual, expected, message ) { deepEqual: function( actual, expected, message ) {
QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
}, },
/**
* @name notDeepEqual
* @function
*/
notDeepEqual: function( actual, expected, message ) { notDeepEqual: function( actual, expected, message ) {
QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
}, },
/**
* @name strictEqual
* @function
*/
strictEqual: function( actual, expected, message ) { strictEqual: function( actual, expected, message ) {
QUnit.push( expected === actual, actual, expected, message ); QUnit.push( expected === actual, actual, expected, message );
}, },
/**
* @name notStrictEqual
* @function
*/
notStrictEqual: function( actual, expected, message ) { notStrictEqual: function( actual, expected, message ) {
QUnit.push( expected !== actual, actual, expected, message ); QUnit.push( expected !== actual, actual, expected, message );
}, },
raises: function( block, expected, message ) { throws: function( block, expected, message ) {
var actual, var actual,
ok = false; ok = false;
// 'expected' is optional
if ( typeof expected === "string" ) { if ( typeof expected === "string" ) {
message = expected; message = expected;
expected = null; expected = null;
@@ -494,18 +528,29 @@ QUnit.assert = {
} else if ( expected.call( {}, actual ) === true ) { } else if ( expected.call( {}, actual ) === true ) {
ok = true; ok = true;
} }
}
QUnit.push( ok, actual, null, message ); QUnit.push( ok, actual, null, message );
} else {
QUnit.pushFailure( message, null, 'No exception was thrown.' );
}
} }
}; };
// @deprecated: Kept assertion helpers in root for backwards compatibility /**
* @deprecate since 1.8.0
* Kept assertion helpers in root for backwards compatibility
*/
extend( QUnit, QUnit.assert ); extend( QUnit, QUnit.assert );
/** /**
* @deprecated: Kept for backwards compatibility * @deprecated since 1.9.0
* next step: remove entirely * Kept global "raises()" for backwards compatibility
*/
QUnit.raises = QUnit.assert.throws;
/**
* @deprecated since 1.0.0, replaced with error pushes since 1.3.0
* Kept to avoid TypeErrors for undefined methods.
*/ */
QUnit.equals = function() { QUnit.equals = function() {
QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" ); QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
@@ -549,7 +594,23 @@ config = {
// when enabled, all tests must call expect() // when enabled, all tests must call expect()
requireExpects: false, requireExpects: false,
urlConfig: [ "noglobals", "notrycatch" ], // add checkboxes that are persisted in the query-string
// when enabled, the id is set to `true` as a `QUnit.config` property
urlConfig: [
{
id: "noglobals",
label: "Check for Globals",
tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
},
{
id: "notrycatch",
label: "No try-catch",
tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
}
],
// Set of all modules.
modules: {},
// logging callback queues // logging callback queues
begin: [], begin: [],
@@ -661,17 +722,10 @@ extend( QUnit, {
}, },
// Resets the test setup. Useful for tests that modify the DOM. // Resets the test setup. Useful for tests that modify the DOM.
// If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
reset: function() { reset: function() {
var fixture; var fixture = id( "qunit-fixture" );
if ( fixture ) {
if ( window.jQuery ) { fixture.innerHTML = config.fixture;
jQuery( "#qunit-fixture" ).html( config.fixture );
} else {
fixture = id( "qunit-fixture" );
if ( fixture ) {
fixture.innerHTML = config.fixture;
}
} }
}, },
@@ -732,6 +786,8 @@ extend( QUnit, {
var output, source, var output, source,
details = { details = {
module: config.current.module,
name: config.current.testName,
result: result, result: result,
message: message, message: message,
actual: actual, actual: actual,
@@ -770,26 +826,36 @@ extend( QUnit, {
}); });
}, },
pushFailure: function( message, source ) { pushFailure: function( message, source, actual ) {
if ( !config.current ) { if ( !config.current ) {
throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) ); throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
} }
var output, var output,
details = { details = {
module: config.current.module,
name: config.current.testName,
result: false, result: false,
message: message message: message
}; };
message = escapeInnerText(message ) || "error"; message = escapeInnerText( message ) || "error";
message = "<span class='test-message'>" + message + "</span>"; message = "<span class='test-message'>" + message + "</span>";
output = message; output = message;
output += "<table>";
if ( actual ) {
output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeInnerText( actual ) + "</pre></td></tr>";
}
if ( source ) { if ( source ) {
details.source = source; details.source = source;
output += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr></table>"; output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
} }
output += "</table>";
runLoggingCallbacks( "log", QUnit, details ); runLoggingCallbacks( "log", QUnit, details );
config.current.assertions.push({ config.current.assertions.push({
@@ -859,7 +925,9 @@ QUnit.load = function() {
runLoggingCallbacks( "begin", QUnit, {} ); runLoggingCallbacks( "begin", QUnit, {} );
// Initialize the config, saving the execution queue // Initialize the config, saving the execution queue
var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxes, moduleFilter,
numModules = 0,
moduleFilterHtml = "",
urlConfigHtml = "", urlConfigHtml = "",
oldconfig = extend( {}, config ); oldconfig = extend( {}, config );
@@ -872,10 +940,26 @@ QUnit.load = function() {
for ( i = 0; i < len; i++ ) { for ( i = 0; i < len; i++ ) {
val = config.urlConfig[i]; val = config.urlConfig[i];
config[val] = QUnit.urlParams[val]; if ( typeof val === "string" ) {
urlConfigHtml += "<label><input name='" + val + "' type='checkbox'" + ( config[val] ? " checked='checked'" : "" ) + ">" + val + "</label>"; val = {
id: val,
label: val,
tooltip: "[no tooltip available]"
};
}
config[ val.id ] = QUnit.urlParams[ val.id ];
urlConfigHtml += "<input id='qunit-urlconfig-" + val.id + "' name='" + val.id + "' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) + " title='" + val.tooltip + "'><label for='qunit-urlconfig-" + val.id + "' title='" + val.tooltip + "'>" + val.label + "</label>";
} }
moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " + ( config.module === undefined ? "selected" : "" ) + ">< All Modules ></option>";
for ( i in config.modules ) {
if ( config.modules.hasOwnProperty( i ) ) {
numModules += 1;
moduleFilterHtml += "<option value='" + encodeURIComponent(i) + "' " + ( config.module === i ? "selected" : "" ) + ">" + i + "</option>";
}
}
moduleFilterHtml += "</select>";
// `userAgent` initialized at top of scope // `userAgent` initialized at top of scope
userAgent = id( "qunit-userAgent" ); userAgent = id( "qunit-userAgent" );
if ( userAgent ) { if ( userAgent ) {
@@ -885,12 +969,7 @@ QUnit.load = function() {
// `banner` initialized at top of scope // `banner` initialized at top of scope
banner = id( "qunit-header" ); banner = id( "qunit-header" );
if ( banner ) { if ( banner ) {
banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined }) + "'>" + banner.innerHTML + "</a> " + urlConfigHtml; banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
addEvent( banner, "change", function( event ) {
var params = {};
params[ event.target.name ] = event.target.checked ? true : undefined;
window.location = QUnit.url( params );
});
} }
// `toolbar` initialized at top of scope // `toolbar` initialized at top of scope
@@ -931,8 +1010,31 @@ QUnit.load = function() {
// `label` initialized at top of scope // `label` initialized at top of scope
label = document.createElement( "label" ); label = document.createElement( "label" );
label.setAttribute( "for", "qunit-filter-pass" ); label.setAttribute( "for", "qunit-filter-pass" );
label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." );
label.innerHTML = "Hide passed tests"; label.innerHTML = "Hide passed tests";
toolbar.appendChild( label ); toolbar.appendChild( label );
urlConfigCheckboxes = document.createElement( 'span' );
urlConfigCheckboxes.innerHTML = urlConfigHtml;
addEvent( urlConfigCheckboxes, "change", function( event ) {
var params = {};
params[ event.target.name ] = event.target.checked ? true : undefined;
window.location = QUnit.url( params );
});
toolbar.appendChild( urlConfigCheckboxes );
if (numModules > 1) {
moduleFilter = document.createElement( 'span' );
moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' );
moduleFilter.innerHTML = moduleFilterHtml;
addEvent( moduleFilter, "change", function() {
var selectBox = moduleFilter.getElementsByTagName("select")[0],
selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } );
});
toolbar.appendChild(moduleFilter);
}
} }
// `main` initialized at top of scope // `main` initialized at top of scope
@@ -970,9 +1072,9 @@ window.onerror = function ( error, filePath, linerNr ) {
} }
QUnit.pushFailure( error, filePath + ":" + linerNr ); QUnit.pushFailure( error, filePath + ":" + linerNr );
} else { } else {
QUnit.test( "global failure", function() { QUnit.test( "global failure", extend( function() {
QUnit.pushFailure( error, filePath + ":" + linerNr ); QUnit.pushFailure( error, filePath + ":" + linerNr );
}); }, { validTest: validTest } ) );
} }
return false; return false;
} }
@@ -1039,6 +1141,11 @@ function done() {
} }
} }
// scroll back to top to show results
if ( window.scrollTo ) {
window.scrollTo(0, 0);
}
runLoggingCallbacks( "done", QUnit, { runLoggingCallbacks( "done", QUnit, {
failed: config.stats.bad, failed: config.stats.bad,
passed: passed, passed: passed,
@@ -1051,14 +1158,20 @@ function done() {
function validTest( test ) { function validTest( test ) {
var include, var include,
filter = config.filter && config.filter.toLowerCase(), filter = config.filter && config.filter.toLowerCase(),
module = config.module, module = config.module && config.module.toLowerCase(),
fullName = (test.module + ": " + test.testName).toLowerCase(); fullName = (test.module + ": " + test.testName).toLowerCase();
// Internally-generated tests are always valid
if ( test.callback && test.callback.validTest === validTest ) {
delete test.callback.validTest;
return true;
}
if ( config.testNumber ) { if ( config.testNumber ) {
return test.testNumber === config.testNumber; return test.testNumber === config.testNumber;
} }
if ( module && test.module !== module ) { if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
return false; return false;
} }
@@ -1335,7 +1448,8 @@ QUnit.equiv = (function() {
a.global === b.global && a.global === b.global &&
// (gmi) ... // (gmi) ...
a.ignoreCase === b.ignoreCase && a.ignoreCase === b.ignoreCase &&
a.multiline === b.multiline; a.multiline === b.multiline &&
a.sticky === b.sticky;
}, },
// - skip when the property is a method of an instance (OOP) // - skip when the property is a method of an instance (OOP)

View File

@@ -25,6 +25,8 @@ $(document).ready(function() {
equal(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object'); equal(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object');
result = _.map([[1,2,3],[1,2,3]], _.rest); result = _.map([[1,2,3],[1,2,3]], _.rest);
equal(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map'); equal(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map');
result = (function(){ return _(arguments).drop(); })(1, 2, 3, 4);
equal(result.join(', '), '2, 3, 4', 'aliased as drop and works on arguments object');
}); });
test("arrays: initial", function() { test("arrays: initial", function() {
@@ -123,10 +125,17 @@ $(document).ready(function() {
equal(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths'); equal(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths');
}); });
test('arrays: zipObject', function() { test('arrays: object', function() {
var result = _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]);
var shouldBe = {moe: 30, larry: 40, curly: 50}; var shouldBe = {moe: 30, larry: 40, curly: 50};
ok(_.isEqual(result, shouldBe), 'two arrays zipped together into an object'); ok(_.isEqual(result, shouldBe), 'two arrays zipped together into an object');
result = _.object([['one', 1], ['two', 2], ['three', 3]]);
shouldBe = {one: 1, two: 2, three: 3};
ok(_.isEqual(result, shouldBe), 'an array of pairs zipped together into an object');
var stooges = {moe: 30, larry: 40, curly: 50};
ok(_.isEqual(_.object(_.pairs(stooges)), stooges), 'an object converted to pairs and back to an object');
}); });
test("arrays: indexOf", function() { test("arrays: indexOf", function() {

View File

@@ -141,6 +141,7 @@ $(document).ready(function() {
ok(_.all([1], _.identity) === true, 'cast to boolean - true'); ok(_.all([1], _.identity) === true, 'cast to boolean - true');
ok(_.all([0], _.identity) === false, 'cast to boolean - false'); ok(_.all([0], _.identity) === false, 'cast to boolean - false');
ok(_.every([true, true, true], _.identity), 'aliased as "every"'); ok(_.every([true, true, true], _.identity), 'aliased as "every"');
ok(!_.all([undefined, undefined, undefined], _.identity), 'works with arrays of undefined');
}); });
test('collections: any', function() { test('collections: any', function() {
@@ -303,6 +304,14 @@ $(document).ready(function() {
test('collections: size', function() { test('collections: size', function() {
equal(_.size({one : 1, two : 2, three : 3}), 3, 'can compute the size of an object'); 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'); equal(_.size([1, 2, 3]), 3, 'can compute the size of an array');
var func = function() {
return _.size(arguments);
};
equal(func(1, 2, 3, 4), 4, 'can test the size of the arguments object');
equal(_.size('hello'), 5, 'can compute the size of a string');
}); });
}); });

View File

@@ -18,6 +18,16 @@ $(document).ready(function() {
equal(_.values({one : 1, two : 2}).join(', '), '1, 2', 'can extract the values from an object'); equal(_.values({one : 1, two : 2}).join(', '), '1, 2', 'can extract the values from an object');
}); });
test("objects: pairs", function() {
deepEqual(_.pairs({one: 1, two: 2}), [['one', 1], ['two', 2]], 'can convert an object into pairs');
});
test("objects: invert", function() {
var obj = {first: 'Moe', second: 'Larry', third: 'Curly'};
equal(_.keys(_.invert(obj)).join(' '), 'Moe Larry Curly', 'can invert an object');
ok(_.isEqual(_.invert(_.invert(obj)), obj), 'two inverts gets you back where you started');
});
test("objects: functions", function() { test("objects: functions", function() {
var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce}; var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce};
ok(_.isEqual(['b', 'd'], _.functions(obj)), 'can grab the function names of any passed-in object'); ok(_.isEqual(['b', 'd'], _.functions(obj)), 'can grab the function names of any passed-in object');

View File

@@ -48,6 +48,15 @@ $(document).ready(function() {
test("utility: _.escape", function() { test("utility: _.escape", function() {
equal(_.escape("Curly & Moe"), "Curly &amp; Moe"); equal(_.escape("Curly & Moe"), "Curly &amp; Moe");
equal(_.escape("Curly &amp; Moe"), "Curly &amp;amp; Moe"); equal(_.escape("Curly &amp; Moe"), "Curly &amp;amp; Moe");
equal(_.escape(null), '');
});
test("utility: _.unescape", function() {
var string = "Curly & Moe";
equal(_.unescape("Curly &amp; Moe"), string);
equal(_.unescape("Curly &amp;amp; Moe"), "Curly &amp; Moe");
equal(_.unescape(null), '');
equal(_.unescape(_.escape(string)), string);
}); });
test("utility: template", function() { test("utility: template", function() {
@@ -156,6 +165,14 @@ $(document).ready(function() {
equal(templateWithNull({planet : "world"}), "a null undefined world", "can handle missing escape and evaluate settings"); equal(templateWithNull({planet : "world"}), "a null undefined world", "can handle missing escape and evaluate settings");
}); });
test('utility: _.template provides the generated function source, when a SyntaxError occurs', function() {
try {
_.template('<b><%= if %></b>');
} catch (e) {
ok(e.source.indexOf('( if )') > 0);
}
});
test('_.template handles \\u2028 & \\u2029', function() { test('_.template handles \\u2028 & \\u2029', function() {
var tmpl = _.template('<p>\u2028<%= "\\u2028\\u2029" %>\u2029</p>'); var tmpl = _.template('<p>\u2028<%= "\\u2028\\u2029" %>\u2029</p>');
strictEqual(tmpl(), '<p>\u2028\u2028\u2029\u2029</p>'); strictEqual(tmpl(), '<p>\u2028\u2028\u2029\u2029</p>');

View File

@@ -5,28 +5,29 @@
// Oliver Steele's Functional, and John Resig's Micro-Templating. // Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation: // For all details and documentation:
// http://documentcloud.github.com/underscore // http://documentcloud.github.com/underscore
(function(){var s=this,L=s._,o={},k=Array.prototype,p=Object.prototype,M=k.push,h=k.slice,N=k.unshift,m=p.toString,O=p.hasOwnProperty,z=k.forEach,A=k.map,B=k.reduce,C=k.reduceRight,D=k.filter,E=k.every,F=k.some,q=k.indexOf,G=k.lastIndexOf,p=Array.isArray,P=Object.keys,t=Function.prototype.bind,b=function(a){return new l(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var i=b.each=b.forEach=function(a,c, (function(){var t=this,M=t._,p={},k=Array.prototype,q=Object.prototype,N=k.push,h=k.slice,O=k.unshift,m=q.toString,P=q.hasOwnProperty,A=k.forEach,B=k.map,C=k.reduce,D=k.reduceRight,E=k.filter,F=k.every,G=k.some,r=k.indexOf,H=k.lastIndexOf,q=Array.isArray,Q=Object.keys,u=Function.prototype.bind,b=function(a){return new l(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):t._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a,c,
d){if(a!=null)if(z&&a.forEach===z)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,d){var b=[];if(a==null)return b;if(A&&a.map===A)return a.map(c,d);i(a,function(a,g,j){b[b.length]=c.call(d,a,g,j)});return b};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduce===B){e&&(c=b.bind(c,e));return f?a.reduce(c, d){if(a!=null)if(A&&a.forEach===A)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(c.call(d,a[e],e,a)===p)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===p)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(B&&a.map===B)return a.map(c,b);j(a,function(a,g,i){e[e.length]=c.call(b,a,g,i)});return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(C&&a.reduce===C){e&&(c=b.bind(c,e));return f?a.reduce(c,
d):a.reduce(c)}i(a,function(a,b,h){if(f)d=c.call(e,d,a,b,h);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(C&&a.reduceRight===C){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,c,b){var e;H(a,function(a,g,j){if(c.call(b,a,g,j)){e= d):a.reduce(c)}j(a,function(a,b,h){if(f)d=c.call(e,d,a,b,h);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(D&&a.reduceRight===D){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,c,b){var e;I(a,function(a,g,i){if(c.call(b,a,g,i)){e=
a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(D&&a.filter===D)return a.filter(c,b);i(a,function(a,g,j){c.call(b,a,g,j)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;i(a,function(a,g,j){c.call(b,a,g,j)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(E&&a.every===E)return a.every(c,b);i(a,function(a,g,j){if(!(e=e&&c.call(b,a,g,j)))return o});return!!e};var H=b.some=b.any= a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(E&&a.filter===E)return a.filter(c,b);j(a,function(a,g,i){c.call(b,a,g,i)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,i){c.call(b,a,g,i)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,d){c||(c=b.identity);var e=true;if(a==null)return e;if(F&&a.every===F)return a.every(c,d);j(a,function(a,b,i){if(!(e=e&&c.call(d,a,b,i)))return p});return!!e};var I=
function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(F&&a.some===F)return a.some(c,d);i(a,function(a,b,j){if(e||(e=c.call(d,a,b,j)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=H(a,function(a){return a===c})};b.invoke=function(a,c){var d=h.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c:a[c]).apply(a,d)})};b.pluck=function(a,c){return b.map(a,function(a){return a[c]})}; b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(G&&a.some===G)return a.some(c,d);j(a,function(a,b,i){if(e||(e=c.call(d,a,b,i)))return p});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(r&&a.indexOf===r)return a.indexOf(c)!=-1;return b=I(a,function(a){return a===c})};b.invoke=function(a,c){var d=h.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c:a[c]).apply(a,d)})};b.pluck=function(a,c){return b.map(a,function(a){return a[c]})};
b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0]&&a.length<65535)return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};i(a,function(a,b,j){b=c?c.call(d,a,b,j):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0]&&a.length<65535)return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};i(a,function(a,b,j){b=c?c.call(d,a,b,j):a;b<e.computed&&(e={value:a,computed:b})}); b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0]&&a.length<65535)return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,i){b=c?c.call(d,a,b,i):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0]&&a.length<65535)return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,i){b=c?c.call(d,a,b,i):a;b<e.computed&&(e={value:a,computed:b})});
return e.value};b.shuffle=function(a){var c,b=0,e=[];i(a,function(a){c=Math.floor(Math.random()*++b);e[b-1]=e[c];e[c]=a});return e};b.sortBy=function(a,c,d){var e=I(a,c);return b.pluck(b.map(a,function(a,c,b){return{value:a,criteria:e.call(d,a,c,b)}}).sort(function(a,c){var b=a.criteria,d=c.criteria;return b===void 0?1:d===void 0?-1:b<d?-1:b>d?1:0}),"value")};var I=function(a,c){return b.isFunction(c)?c:function(a){return a[c]}},J=function(a,c,b){var e={},f=I(a,c);i(a,function(a,c){var h=f(a,c);b(e, return e.value};b.shuffle=function(a){var c,b=0,e=[];j(a,function(a){c=Math.floor(Math.random()*++b);e[b-1]=e[c];e[c]=a});return e};b.sortBy=function(a,c,d){var e=J(a,c);return b.pluck(b.map(a,function(a,c,b){return{value:a,criteria:e.call(d,a,c,b)}}).sort(function(a,c){var b=a.criteria,d=c.criteria;return b===void 0?1:d===void 0?-1:b<d?-1:b>d?1:0}),"value")};var J=function(a,c){return b.isFunction(c)?c:function(a){return a[c]}},K=function(a,c,b){var e={},f=J(a,c);j(a,function(a,c){var h=f(a,c);b(e,
h,a)});return e};b.groupBy=function(a,c){return J(a,c,function(a,c,b){(a[c]||(a[c]=[])).push(b)})};b.countBy=function(a,c){return J(a,c,function(a,c){a[c]||(a[c]=0);a[c]++})};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var c=d(c),e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<c?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?h.call(a):b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head= h,a)});return e};b.groupBy=function(a,c){return K(a,c,function(a,c,b){(a[c]||(a[c]=[])).push(b)})};b.countBy=function(a,c){return K(a,c,function(a,c){a[c]||(a[c]=0);a[c]++})};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var c=d(c),e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<c?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?h.call(a):b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return a.length===+a.length?a.length:b.keys(a).length};b.first=
b.take=function(a,c,b){return c!=null&&!b?h.call(a,0,c):a[0]};b.initial=function(a,c,b){return h.call(a,0,a.length-(c==null||b?1:c))};b.last=function(a,c,b){return c!=null&&!b?h.call(a,Math.max(a.length-c,0)):a[a.length-1]};b.rest=b.tail=function(a,c,b){return h.call(a,c==null||b?1:c)};b.compact=function(a){return b.filter(a,function(a){return!!a})};var r=function(a,c,d){i(a,function(a){b.isArray(a)?c?M.apply(d,a):r(a,c,d):d.push(a)});return d};b.flatten=function(a,c){return r(a,c,[])};b.without= b.head=b.take=function(a,c,b){return c!=null&&!b?h.call(a,0,c):a[0]};b.initial=function(a,c,b){return h.call(a,0,a.length-(c==null||b?1:c))};b.last=function(a,c,b){return c!=null&&!b?h.call(a,Math.max(a.length-c,0)):a[a.length-1]};b.rest=b.tail=b.drop=function(a,c,b){return h.call(a,c==null||b?1:c)};b.compact=function(a){return b.filter(a,function(a){return!!a})};var s=function(a,c,d){j(a,function(a){b.isArray(a)?c?N.apply(d,a):s(a,c,d):d.push(a)});return d};b.flatten=function(a,c){return s(a,c,[])};
function(a){return b.difference(a,h.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,j){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[j])}return d},[]);return e};b.union=function(){return b.uniq(r(arguments,true,[]))};b.intersection=function(a){var c=h.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=r(h.call(arguments,1),true,[]); b.without=function(a){return b.difference(a,h.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,i){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[i])}return d},[]);return e};b.union=function(){return b.uniq(s(arguments,true,[]))};b.intersection=function(a){var c=h.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=s(h.call(arguments,
return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=h.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.zipObject=function(a,c){for(var b={},e=0,f=a.length;e<f;e++)b[a[e]]=c[e];return b};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(a==null)return-1; 1),true,[]);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=h.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.object=function(a,c){for(var b={},e=0,f=a.length;e<f;e++)c?b[a[e]]=c[e]:b[a[e][0]]=a[e][1];return b};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(r&&a.indexOf===r)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=
if(G&&a.lastIndexOf===G)return a.lastIndexOf(c);for(var b=a.length;b--;)if(a[b]===c)return b;return-1};b.range=function(a,c,b){if(arguments.length<=1){c=a||0;a=0}for(var b=arguments[2]||1,e=Math.max(Math.ceil((c-a)/b),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+b}return g};var K=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,h.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=h.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(h.call(arguments))); function(a,c){if(a==null)return-1;if(H&&a.lastIndexOf===H)return a.lastIndexOf(c);for(var b=a.length;b--;)if(a[b]===c)return b;return-1};b.range=function(a,c,b){if(arguments.length<=1){c=a||0;a=0}for(var b=arguments[2]||1,e=Math.max(Math.ceil((c-a)/b),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+b}return g};var L=function(){};b.bind=function(a,c){var d,e;if(a.bind===u&&u)return u.apply(a,h.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=h.call(arguments,2);return d=function(){if(!(this instanceof
K.prototype=a.prototype;var b=new K,g=a.apply(b,e.concat(h.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=h.call(arguments,1);c.length==0&&(c=b.functions(a));i(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=h.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b, d))return a.apply(c,e.concat(h.call(arguments)));L.prototype=a.prototype;var b=new L,g=a.apply(b,e.concat(h.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=h.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,c){var b=h.call(arguments,2);return setTimeout(function(){return a.apply(null,
[a,1].concat(h.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,j,h,i=b.debounce(function(){j=g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;j&&a.apply(d,e);i()},c));if(g)j=true;else{g=true;h=a.apply(d,e)}i();return h}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments,h=d&&!e;clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b);h&&a.apply(f,g)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true; b)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(h.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,i,h,j=b.debounce(function(){i=g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;i&&a.apply(d,e);j()},c));if(g)i=true;else{g=true;h=a.apply(d,e)}j();return h}};b.debounce=function(a,c,b){var e;return function(){var f=this,g=arguments,i=b&&!e;clearTimeout(e);e=setTimeout(function(){e=null;b||a.apply(f,g)},c);i&&a.apply(f,g)}};b.once=function(a){var b=
return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(h.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=P||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values= false,d;return function(){if(b)return d;b=true;d=a.apply(this,arguments);a=null;return d}};b.wrap=function(a,b){return function(){var d=[a].concat(h.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=Q||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=
function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){i(h.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={},d=b.flatten(h.call(arguments,1));i(d,function(b){b in a&&(c[b]=a[b])});return c};b.omit=function(a){var c={},d=b.flatten(h.call(arguments,1)),e;for(e in a)b.include(d,e)||(c[e]=a[e]);return c};b.defaults=function(a){i(h.call(arguments,1), [],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.pairs=function(a){return b.map(a,function(a,b){return[b,a]})};b.invert=function(a){return b.reduce(a,function(a,b,e){a[b]=e;return a},{})};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(h.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={},d=b.flatten(h.call(arguments,1));j(d,
function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};var u=function(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=m.call(a);if(e!=m.call(c))return false;switch(e){case "[object String]":return a== function(b){b in a&&(c[b]=a[b])});return c};b.omit=function(a){var c={},d=b.flatten(h.call(arguments,1)),e;for(e in a)b.include(d,e)||(c[e]=a[e]);return c};b.defaults=function(a){j(h.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};var v=function(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=
""+c;case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){f=a.length;if(g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&u(a[f],c[f],d)))break}else{if("constructor"in c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=m.call(a);if(e!=m.call(c))return false;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;
a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)){f++;if(!(g=b.has(c,h)&&u(a[h],c[h],d)))break}if(g){for(h in c)if(b.has(c,h)&&!f--)break;g=!f}}d.pop();return g};b.isEqual=function(a,b){return u(a,b,[])};b.isEmpty=function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return m.call(a)=="[object Array]"}; for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){f=a.length;if(g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&v(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var i in a)if(b.has(a,i)){f++;if(!(g=b.has(c,i)&&v(a[i],c[i],d)))break}if(g){for(i in c)if(b.has(c,i)&&!f--)break;g=!f}}d.pop();return g};b.isEqual=function(a,b){return v(a,b,[])};b.isEmpty=function(a){if(a==null)return true;if(b.isArray(a)||
b.isObject=function(a){return a===Object(a)};i("Arguments,Function,String,Number,Date,RegExp".split(","),function(a){b["is"+a]=function(b){return m.call(b)=="[object "+a+"]"}});b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||m.call(a)=="[object Boolean]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a=== b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=q||function(a){return m.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};j("Arguments,Function,String,Number,Date,RegExp".split(","),function(a){b["is"+a]=function(b){return m.call(b)=="[object "+a+"]"}});b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFinite=function(a){return b.isNumber(a)&&
void 0};b.has=function(a,b){return O.call(a,b)};b.noConflict=function(){s._=L;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};var Q={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","/":"&#x2F;"},R=/[&<>"'\/]/g;b.escape=function(a){return(""+a).replace(R,function(a){return Q[a]})};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){i(b.functions(a),function(c){S(c,b[c]=a[c])})}; isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||m.call(a)=="[object Boolean]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return P.call(a,b)};b.noConflict=function(){t._=M;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.random=function(a,b){return a+(0|Math.random()*(b-a+1))};var n={escape:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;",
var T=0;b.uniqueId=function(a){var b=T++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var v=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},w;for(w in n)n[n[w]]=w;var U=/\\|'|\r|\n|\t|\u2028|\u2029/g,V=/\\(\\|'|r|n|t|u2028|u2029)/g,x=function(a){return a.replace(V,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(U,function(a){return"\\"+ "'":"&#x27;","/":"&#x2F;"}};n.unescape=b.invert(n.escape);var R={escape:RegExp("["+b.keys(n.escape).join("")+"]","g"),unescape:RegExp("("+b.keys(n.unescape).join("|")+")","g")};b.each(["escape","unescape"],function(a){b[a]=function(b){return b==null?"":(""+b).replace(R[a],function(b){return n[a][b]})}});b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){S(c,b[c]=a[c])})};var T=0;b.uniqueId=function(a){var b=
n[a]}).replace(d.escape||v,function(a,b){return"'+\n((__t=("+x(b)+"))==null?'':_.escape(__t))+\n'"}).replace(d.interpolate||v,function(a,b){return"'+\n((__t=("+x(b)+"))==null?'':__t)+\n'"}).replace(d.evaluate||v,function(a,b){return"';\n"+x(b)+"\n__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this, T++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var w=/.^/,o={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},x;for(x in o)o[o[x]]=x;var U=/\\|'|\r|\n|\t|\u2028|\u2029/g,V=/\\(\\|'|r|n|t|u2028|u2029)/g,y=function(a){return a.replace(V,function(a,b){return o[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(U,function(a){return"\\"+o[a]}).replace(d.escape||w,
a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};b.chain=function(a){return b(a).chain()};var l=function(a){this._wrapped=a};b.prototype=l.prototype;var y=function(a,c){return c?b(a).chain():a},S=function(a,c){l.prototype[a]=function(){var a=h.call(arguments);N.call(a,this._wrapped);return y(c.apply(b,a),this._chain)}};b.mixin(b);i("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];l.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments); function(a,b){return"'+\n((__t=("+y(b)+"))==null?'':_.escape(__t))+\n'"}).replace(d.interpolate||w,function(a,b){return"'+\n((__t=("+y(b)+"))==null?'':__t)+\n'"}).replace(d.evaluate||w,function(a,b){return"';\n"+y(b)+"\n__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");a="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{var e=new Function(d.variable||"obj","_",a)}catch(f){f.source=a;throw f;}if(c)return e(c,b);c=function(a){return e.call(this,
(a=="shift"||a=="splice")&&d.length===0&&delete d[0];return y(d,this._chain)}});i(["concat","join","slice"],function(a){var b=k[a];l.prototype[a]=function(){return y(b.apply(this._wrapped,arguments),this._chain)}});l.prototype.chain=function(){this._chain=true;return this};l.prototype.value=function(){return this._wrapped}}).call(this); a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};b.chain=function(a){return b(a).chain()};var l=function(a){this._wrapped=a};b.prototype=l.prototype;var z=function(a,c){return c?b(a).chain():a},S=function(a,c){l.prototype[a]=function(){var a=h.call(arguments);O.call(a,this._wrapped);return z(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];l.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);
(a=="shift"||a=="splice")&&d.length===0&&delete d[0];return z(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];l.prototype[a]=function(){return z(b.apply(this._wrapped,arguments),this._chain)}});l.prototype.chain=function(){this._chain=true;return this};l.prototype.value=function(){return this._wrapped}}).call(this);

View File

@@ -174,6 +174,7 @@
// Delegates to **ECMAScript 5**'s native `every` if available. // Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`. // Aliased as `all`.
_.every = _.all = function(obj, iterator, context) { _.every = _.all = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = true; var result = true;
if (obj == null) return result; if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
@@ -338,7 +339,7 @@
// Return the number of elements in an object. // Return the number of elements in an object.
_.size = function(obj) { _.size = function(obj) {
return _.isArray(obj) ? obj.length : _.keys(obj).length; return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
}; };
// Array Functions // Array Functions
@@ -369,12 +370,12 @@
} }
}; };
// Returns everything but the first entry of the array. Aliased as `tail`. // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **index** will return // Especially useful on the arguments object. Passing an **n** will return
// the rest of the values in the array from that index onward. The **guard** // the rest N values in the array. The **guard**
// check allows it to work with `_.map`. // check allows it to work with `_.map`.
_.rest = _.tail = function(array, index, guard) { _.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, (index == null) || guard ? 1 : index); return slice.call(array, (n == null) || guard ? 1 : n);
}; };
// Trim out all falsy values from an array. // Trim out all falsy values from an array.
@@ -456,12 +457,17 @@
return results; return results;
}; };
// Zip together two arrays -- an array of keys and an array of values -- into // Converts lists into objects. Pass either a single array of `[key, value]`
// a single object. // pairs, or two parallel arrays of the same length -- one of keys, and one of
_.zipObject = function(keys, values) { // the corresponding values.
_.object = function(list, values) {
var result = {}; var result = {};
for (var i = 0, l = keys.length; i < l; i++) { for (var i = 0, l = list.length; i < l; i++) {
result[keys[i]] = values[i]; if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
} }
return result; return result;
}; };
@@ -622,7 +628,9 @@
return function() { return function() {
if (ran) return memo; if (ran) return memo;
ran = true; ran = true;
return memo = func.apply(this, arguments); memo = func.apply(this, arguments);
func = null;
return memo;
}; };
}; };
@@ -676,6 +684,21 @@
return _.map(obj, _.identity); return _.map(obj, _.identity);
}; };
// Convert an object into a list of `[key, value]` pairs.
_.pairs = function(obj) {
return _.map(obj, function(value, key) {
return [key, value];
});
};
// Invert the keys and values of an object. The values must be serializable.
_.invert = function(obj) {
return _.reduce(obj, function(memo, value, key) {
memo[value] = key;
return memo;
}, {});
};
// Return a sorted list of the function names available on the object. // Return a sorted list of the function names available on the object.
// Aliased as `methods` // Aliased as `methods`
_.functions = _.methods = function(obj) { _.functions = _.methods = function(obj) {
@@ -925,25 +948,39 @@
for (var i = 0; i < n; i++) iterator.call(context, i); for (var i = 0; i < n; i++) iterator.call(context, i);
}; };
// Return a random integer between min and max (inclusive).
_.random = function(min, max) {
return min + (0 | Math.random() * (max - min + 1));
};
// List of HTML entities for escaping. // List of HTML entities for escaping.
var htmlEscapes = { var entityMap = {
'&': '&amp;', escape: {
'<': '&lt;', '&': '&amp;',
'>': '&gt;', '<': '&lt;',
'"': '&quot;', '>': '&gt;',
"'": '&#x27;', '"': '&quot;',
'/': '&#x2F;' "'": '&#x27;',
'/': '&#x2F;'
}
};
entityMap.unescape = _.invert(entityMap.escape);
// Regexes containing the keys and values listed immediately above.
var entityRegexes = {
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
}; };
// Regex containing the keys listed immediately above. // Functions for escaping and unescaping strings to/from HTML interpolation.
var htmlEscaper = /[&<>"'\/]/g; _.each(['escape', 'unescape'], function(method) {
_[method] = function(string) {
// Escape a string for HTML interpolation. if (string == null) return '';
_.escape = function(string) { return ('' + string).replace(entityRegexes[method], function(match) {
return ('' + string).replace(htmlEscaper, function(match) { return entityMap[method][match];
return htmlEscapes[match]; });
}); };
}; });
// If the value of the named property is a function then invoke it; // If the value of the named property is a function then invoke it;
// otherwise, return it. // otherwise, return it.
@@ -1033,10 +1070,16 @@
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," + source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'')};\n" + "print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n"; source + "return __p;\n";
var render = new Function(settings.variable || 'obj', '_', source); try {
var render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _); if (data) return render(data, _);
var template = function(data) { var template = function(data) {
return render.call(this, data, _); return render.call(this, data, _);