Add non-submodule vendor directory.

Former-commit-id: 392cdade55487e774e959013d8d25ae0b1dfe93b
This commit is contained in:
John-David Dalton
2012-07-02 15:32:14 -04:00
parent 5048f0422d
commit 0ae7fe9df5
113 changed files with 77822 additions and 0 deletions

669
vendor/backbone/test/collection.js vendored Normal file
View File

@@ -0,0 +1,669 @@
$(document).ready(function() {
var lastRequest = null;
var sync = Backbone.sync;
var a, b, c, d, e, col, otherCol;
module("Backbone.Collection", {
setup: function() {
a = new Backbone.Model({id: 3, label: 'a'});
b = new Backbone.Model({id: 2, label: 'b'});
c = new Backbone.Model({id: 1, label: 'c'});
d = new Backbone.Model({id: 0, label: 'd'});
e = null;
col = new Backbone.Collection([a,b,c,d]);
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() {
equal(col.first(), a, "a should be first");
equal(col.last(), d, "d should be last");
col.comparator = function(a, b) {
return a.id > b.id ? -1 : 1;
};
col.sort();
equal(col.first(), a, "a should be first");
equal(col.last(), d, "d should be last");
col.comparator = function(model) { return model.id; };
col.sort();
equal(col.first(), d, "d should be first");
equal(col.last(), a, "a should be last");
equal(col.length, 4);
});
test("Collection: get, getByCid", 3, function() {
equal(col.get(0), d);
equal(col.get(2), b);
equal(col.getByCid(col.first().cid), col.first());
});
test("Collection: get with non-default ids", 2, function() {
var col = new Backbone.Collection();
var MongoModel = Backbone.Model.extend({
idAttribute: '_id'
});
var model = new MongoModel({_id: 100});
col.push(model);
equal(col.get(100), model);
model.set({_id: 101});
equal(col.get(101), model);
});
test("Collection: update index when id changes", 3, function() {
var col = new Backbone.Collection();
col.add([
{id : 0, name : 'one'},
{id : 1, name : 'two'}
]);
var one = col.get(0);
equal(one.get('name'), 'one');
one.set({id : 101});
equal(col.get(0), null);
equal(col.get(101).get('name'), 'one');
});
test("Collection: at", 1, function() {
equal(col.at(2), c);
});
test("Collection: pluck", 1, function() {
equal(col.pluck('label').join(' '), 'a b c d');
});
test("Collection: add", 11, function() {
var added, opts, secondAdded;
added = opts = secondAdded = null;
e = new Backbone.Model({id: 10, label : 'e'});
otherCol.add(e);
otherCol.on('add', function() {
secondAdded = true;
});
col.on('add', function(model, collection, options){
added = model.get('label');
equal(options.index, 4);
opts = options;
});
col.add(e, {amazing: true});
equal(added, 'e');
equal(col.length, 5);
equal(col.last(), e);
equal(otherCol.length, 1);
equal(secondAdded, null);
ok(opts.amazing);
var f = new Backbone.Model({id: 20, label : 'f'});
var g = new Backbone.Model({id: 21, label : 'g'});
var h = new Backbone.Model({id: 22, label : 'h'});
var atCol = new Backbone.Collection([f, g, h]);
equal(atCol.length, 3);
atCol.add(e, {at: 1});
equal(atCol.length, 4);
equal(atCol.at(1), e);
equal(atCol.last(), h);
});
test("Collection: add multiple models", 6, function() {
var col = new Backbone.Collection([{at: 0}, {at: 1}, {at: 9}]);
col.add([{at: 2}, {at: 3}, {at: 4}, {at: 5}, {at: 6}, {at: 7}, {at: 8}], {at: 2});
for (var i = 0; i <= 5; i++) {
equal(col.at(i).get('at'), i);
}
});
test("Collection: add; at should have preference over comparator", 1, function() {
var Col = Backbone.Collection.extend({
comparator: function(a,b) {
return a.id > b.id ? -1 : 1;
}
});
var col = new Col([{id: 2}, {id: 3}]);
col.add(new Backbone.Model({id: 1}), {at: 1});
equal(col.pluck('id').join(' '), '3 1 2');
});
test("Collection: can't add model to collection twice", function() {
var col = new Backbone.Collection([{id: 1}, {id: 2}, {id: 1}, {id: 2}, {id: 3}]);
equal(col.pluck('id').join(' '), '1 2 3');
});
test("Collection: can't add different model with same id to collection twice", 1, function() {
var col = new Backbone.Collection;
col.unshift({id: 101});
col.add({id: 101});
equal(col.length, 1);
});
test("Collection: merge in duplicate models with {merge: true}", 3, function() {
var col = new Backbone.Collection;
col.add([{id: 1, name: 'Moe'}, {id: 2, name: 'Curly'}, {id: 3, name: 'Larry'}]);
col.add({id: 1, name: 'Moses'});
equal(col.first().get('name'), 'Moe');
col.add({id: 1, name: 'Moses'}, {merge: true});
equal(col.first().get('name'), 'Moses');
col.add({id: 1, name: 'Tim'}, {merge: true, silent: true});
equal(col.first().get('name'), 'Tim');
});
test("Collection: add model to multiple collections", 10, function() {
var counter = 0;
var e = new Backbone.Model({id: 10, label : 'e'});
e.on('add', function(model, collection) {
counter++;
equal(e, model);
if (counter > 1) {
equal(collection, colF);
} else {
equal(collection, colE);
}
});
var colE = new Backbone.Collection([]);
colE.on('add', function(model, collection) {
equal(e, model);
equal(colE, collection);
});
var colF = new Backbone.Collection([]);
colF.on('add', function(model, collection) {
equal(e, model);
equal(colF, collection);
});
colE.add(e);
equal(e.collection, colE);
colF.add(e);
equal(e.collection, colE);
});
test("Collection: add model with parse", 1, function() {
var Model = Backbone.Model.extend({
parse: function(obj) {
obj.value += 1;
return obj;
}
});
var Col = Backbone.Collection.extend({model: Model});
var col = new Col;
col.add({value: 1}, {parse: true});
equal(col.at(0).get('value'), 2);
});
test("Collection: add model to collection with sort()-style comparator", 3, function() {
var col = new Backbone.Collection;
col.comparator = function(a, b) {
return a.get('name') < b.get('name') ? -1 : 1;
};
var tom = new Backbone.Model({name: 'Tom'});
var rob = new Backbone.Model({name: 'Rob'});
var tim = new Backbone.Model({name: 'Tim'});
col.add(tom);
col.add(rob);
col.add(tim);
equal(col.indexOf(rob), 0);
equal(col.indexOf(tim), 1);
equal(col.indexOf(tom), 2);
});
test("Collection: comparator that depends on `this`", 1, function() {
var col = new Backbone.Collection;
col.negative = function(num) {
return -num;
};
col.comparator = function(a) {
return this.negative(a.id);
};
col.add([{id: 1}, {id: 2}, {id: 3}]);
equal(col.pluck('id').join(' '), '3 2 1');
});
test("Collection: remove", 5, function() {
var removed = null;
var otherRemoved = null;
col.on('remove', function(model, col, options) {
removed = model.get('label');
equal(options.index, 3);
});
otherCol.on('remove', function(model, col, options) {
otherRemoved = true;
});
col.remove(d);
equal(removed, 'd');
equal(col.length, 3);
equal(col.first(), a);
equal(otherRemoved, null);
});
test("Collection: shift and pop", 2, function() {
var col = new Backbone.Collection([{a: 'a'}, {b: 'b'}, {c: 'c'}]);
equal(col.shift().get('a'), 'a');
equal(col.pop().get('c'), 'c');
});
test("Collection: slice", 2, function() {
var col = new Backbone.Collection([{a: 'a'}, {b: 'b'}, {c: 'c'}]);
var array = col.slice(1, 3);
equal(array.length, 2);
equal(array[0].get('b'), 'b');
});
test("Collection: events are unbound on remove", 3, function() {
var counter = 0;
var dj = new Backbone.Model();
var emcees = new Backbone.Collection([dj]);
emcees.on('change', function(){ counter++; });
dj.set({name : 'Kool'});
equal(counter, 1);
emcees.reset([]);
equal(dj.collection, undefined);
dj.set({name : 'Shadow'});
equal(counter, 1);
});
test("Collection: remove in multiple collections", 7, function() {
var modelData = {
id : 5,
title : 'Othello'
};
var passed = false;
var e = new Backbone.Model(modelData);
var f = new Backbone.Model(modelData);
f.on('remove', function() {
passed = true;
});
var colE = new Backbone.Collection([e]);
var colF = new Backbone.Collection([f]);
ok(e != f);
ok(colE.length == 1);
ok(colF.length == 1);
colE.remove(e);
equal(passed, false);
ok(colE.length == 0);
colF.remove(e);
ok(colF.length == 0);
equal(passed, true);
});
test("Collection: remove same model in multiple collection", 16, function() {
var counter = 0;
var e = new Backbone.Model({id: 5, title: 'Othello'});
e.on('remove', function(model, collection) {
counter++;
equal(e, model);
if (counter > 1) {
equal(collection, colE);
} else {
equal(collection, colF);
}
});
var colE = new Backbone.Collection([e]);
colE.on('remove', function(model, collection) {
equal(e, model);
equal(colE, collection);
});
var colF = new Backbone.Collection([e]);
colF.on('remove', function(model, collection) {
equal(e, model);
equal(colF, collection);
});
equal(colE, e.collection);
colF.remove(e);
ok(colF.length == 0);
ok(colE.length == 1);
equal(counter, 1);
equal(colE, e.collection);
colE.remove(e);
equal(null, e.collection);
ok(colE.length == 0);
equal(counter, 2);
});
test("Collection: model destroy removes from all collections", 3, function() {
var e = new Backbone.Model({id: 5, title: 'Othello'});
e.sync = function(method, model, options) { options.success({}); };
var colE = new Backbone.Collection([e]);
var colF = new Backbone.Collection([e]);
e.destroy();
ok(colE.length == 0);
ok(colF.length == 0);
equal(undefined, e.collection);
});
test("Colllection: non-persisted model destroy removes from all collections", 3, function() {
var e = new Backbone.Model({title: 'Othello'});
e.sync = function(method, model, options) { throw "should not be called"; };
var colE = new Backbone.Collection([e]);
var colF = new Backbone.Collection([e]);
e.destroy();
ok(colE.length == 0);
ok(colF.length == 0);
equal(undefined, e.collection);
});
test("Collection: fetch", 4, function() {
col.fetch();
equal(lastRequest.method, 'read');
equal(lastRequest.model, col);
equal(lastRequest.options.parse, true);
col.fetch({parse: false});
equal(lastRequest.options.parse, false);
});
test("Collection: create", 4, function() {
var model = col.create({label: 'f'}, {wait: true});
equal(lastRequest.method, 'create');
equal(lastRequest.model, model);
equal(model.get('label'), 'f');
equal(model.collection, col);
});
test("Collection: create enforces validation", 1, function() {
var ValidatingModel = Backbone.Model.extend({
validate: function(attrs) {
return "fail";
}
});
var ValidatingCollection = Backbone.Collection.extend({
model: ValidatingModel
});
var col = new ValidatingCollection();
equal(col.create({"foo":"bar"}), false);
});
test("Collection: a failing create runs the error callback", 1, function() {
var ValidatingModel = Backbone.Model.extend({
validate: function(attrs) {
return "fail";
}
});
var ValidatingCollection = Backbone.Collection.extend({
model: ValidatingModel
});
var flag = false;
var callback = function(model, error) { flag = true; };
var col = new ValidatingCollection();
col.create({"foo":"bar"}, { error: callback });
equal(flag, true);
});
test("collection: initialize", 1, function() {
var Collection = Backbone.Collection.extend({
initialize: function() {
this.one = 1;
}
});
var coll = new Collection;
equal(coll.one, 1);
});
test("Collection: toJSON", 1, function() {
equal(JSON.stringify(col), '[{"id":3,"label":"a"},{"id":2,"label":"b"},{"id":1,"label":"c"},{"id":0,"label":"d"}]');
});
test("Collection: where", 6, function() {
var coll = new Backbone.Collection([
{a: 1},
{a: 1},
{a: 1, b: 2},
{a: 2, b: 2},
{a: 3}
]);
equal(coll.where({a: 1}).length, 3);
equal(coll.where({a: 2}).length, 1);
equal(coll.where({a: 3}).length, 1);
equal(coll.where({b: 1}).length, 0);
equal(coll.where({b: 2}).length, 2);
equal(coll.where({a: 1, b: 2}).length, 1);
});
test("Collection: Underscore methods", 13, function() {
equal(col.map(function(model){ return model.get('label'); }).join(' '), 'a b c d');
equal(col.any(function(model){ return model.id === 100; }), false);
equal(col.any(function(model){ return model.id === 0; }), true);
equal(col.indexOf(b), 1);
equal(col.size(), 4);
equal(col.rest().length, 3);
ok(!_.include(col.rest()), a);
ok(!_.include(col.rest()), d);
ok(!col.isEmpty());
ok(!_.include(col.without(d)), d);
equal(col.max(function(model){ return model.id; }).id, 3);
equal(col.min(function(model){ return model.id; }).id, 0);
deepEqual(col.chain()
.filter(function(o){ return o.id % 2 === 0; })
.map(function(o){ return o.id * 2; })
.value(),
[4, 0]);
});
test("Collection: reset", 10, function() {
var resetCount = 0;
var models = col.models;
col.on('reset', function() { resetCount += 1; });
col.reset([]);
equal(resetCount, 1);
equal(col.length, 0);
equal(col.last(), null);
col.reset(models);
equal(resetCount, 2);
equal(col.length, 4);
equal(col.last(), d);
col.reset(_.map(models, function(m){ return m.attributes; }));
equal(resetCount, 3);
equal(col.length, 4);
ok(col.last() !== d);
ok(_.isEqual(col.last().attributes, d.attributes));
});
test("Collection: reset passes caller options", 3, function() {
var Model = Backbone.Model.extend({
initialize: function(attrs, options) {
this.model_parameter = options.model_parameter;
}
});
var col = new (Backbone.Collection.extend({ model: Model }))();
col.reset([{ astring: "green", anumber: 1 }, { astring: "blue", anumber: 2 }], { model_parameter: 'model parameter' });
equal(col.length, 2);
col.each(function(model) {
equal(model.model_parameter, 'model parameter');
});
});
test("Collection: trigger custom events on models", 1, function() {
var fired = null;
a.on("custom", function() { fired = true; });
a.trigger("custom");
equal(fired, true);
});
test("Collection: add does not alter arguments", 2, function(){
var attrs = {};
var models = [attrs];
new Backbone.Collection().add(models);
equal(models.length, 1);
ok(attrs === models[0]);
});
test("#714: access `model.collection` in a brand new model.", 2, function() {
var col = new Backbone.Collection;
var Model = Backbone.Model.extend({
set: function(attrs) {
equal(attrs.prop, 'value');
equal(this.collection, col);
return this;
}
});
col.model = Model;
col.create({prop: 'value'});
});
test("#574, remove its own reference to the .models array.", 2, function() {
var col = new Backbone.Collection([
{id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6}
]);
equal(col.length, 6);
col.remove(col.models);
equal(col.length, 0);
});
test("#861, adding models to a collection which do not pass validation", 1, function() {
raises(function() {
var Model = Backbone.Model.extend({
validate: function(attrs) {
if (attrs.id == 3) return "id can't be 3";
}
});
var Collection = Backbone.Collection.extend({
model: Model
});
var col = new Collection;
col.add([{id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6}]);
}, function(e) {
return e.message === "Can't add an invalid model to a collection";
});
});
test("Collection: index with comparator", 4, function() {
var counter = 0;
var col = new Backbone.Collection([{id: 2}, {id: 4}], {
comparator: function(model){ return model.id; }
}).on('add', function(model, colleciton, options){
if (model.id == 1) {
equal(options.index, 0);
equal(counter++, 0);
}
if (model.id == 3) {
equal(options.index, 2);
equal(counter++, 1);
}
});
col.add([{id: 3}, {id: 1}]);
});
test("Collection: throwing during add leaves consistent state", 4, function() {
var col = new Backbone.Collection();
col.on('test', function() { ok(false); });
col.model = Backbone.Model.extend({
validate: function(attrs){ if (!attrs.valid) return 'invalid'; }
});
var model = new col.model({id: 1, valid: true});
raises(function() { col.add([model, {id: 2}]); });
model.trigger('test');
ok(!col.getByCid(model.cid));
ok(!col.get(1));
equal(col.length, 0);
});
test("Collection: multiple copies of the same model", 3, function() {
var col = new Backbone.Collection();
var model = new Backbone.Model();
col.add([model, model]);
equal(col.length, 1);
col.add([{id: 1}, {id: 1}]);
equal(col.length, 2);
equal(col.last().id, 1);
});
test("#964 - collection.get return inconsistent", 2, function() {
var c = new Backbone.Collection();
ok(c.get(null) === undefined);
ok(c.get() === undefined);
});
test("#1112 - passing options.model sets collection.model", 2, function() {
var Model = Backbone.Model.extend({});
var c = new Backbone.Collection([{id: 1}], {model: Model});
ok(c.model === Model);
ok(c.at(0) instanceof Model);
});
test("null and undefined are invalid ids.", 2, function() {
var model = new Backbone.Model({id: 1});
var collection = new Backbone.Collection([model]);
model.set({id: null});
ok(!collection.get('null'));
model.set({id: 1});
model.set({id: undefined});
ok(!collection.get('undefined'));
});
test("Collection: falsy comparator", 4, function(){
var Col = Backbone.Collection.extend({
comparator: function(model){ return model.id; }
});
var col = new Col();
var colFalse = new Col(null, {comparator: false});
var colNull = new Col(null, {comparator: null});
var colUndefined = new Col(null, {comparator: undefined});
ok(col.comparator);
ok(!colFalse.comparator);
ok(!colNull.comparator);
ok(colUndefined.comparator);
});
test("#1355 - `options` is passed to success callbacks", 2, function(){
var m = new Backbone.Model({x:1});
var col = new Backbone.Collection();
var opts = {
success: function(collection, resp, options){
ok(options);
}
};
col.sync = m.sync = function( method, collection, options ){
options.success();
};
col.fetch(opts);
col.create(m, opts);
});
test("#1412 - Trigger 'sync' event.", 2, function() {
var collection = new Backbone.Collection([], {
model: Backbone.Model.extend({
sync: function(method, model, options) {
options.success();
}
})
});
collection.sync = function(method, model, options) { options.success(); };
collection.on('sync', function() { ok(true); });
collection.fetch();
collection.create({id: 1});
});
test("#1447 - create with wait adds model.", function() {
var collection = new Backbone.Collection;
var model = new Backbone.Model;
model.sync = function(method, model, options){ options.success(); };
collection.on('add', function(){ ok(true); });
collection.create(model, {wait: true});
});
test("#1448 - add sorts collection after merge.", function() {
var collection = new Backbone.Collection([
{id: 1, x: 1},
{id: 2, x: 2}
]);
collection.comparator = function(model){ return model.get('x'); };
collection.add({id: 1, x: 3}, {merge: true});
deepEqual(collection.pluck('id'), [2, 1]);
});
});

195
vendor/backbone/test/events.js vendored Normal file
View File

@@ -0,0 +1,195 @@
$(document).ready(function() {
module("Backbone.Events");
test("Events: on and trigger", 2, function() {
var obj = { counter: 0 };
_.extend(obj,Backbone.Events);
obj.on('event', function() { obj.counter += 1; });
obj.trigger('event');
equal(obj.counter,1,'counter should be incremented.');
obj.trigger('event');
obj.trigger('event');
obj.trigger('event');
obj.trigger('event');
equal(obj.counter, 5, 'counter should be incremented five times.');
});
test("Events: binding and triggering multiple events", 4, function() {
var obj = { counter: 0 };
_.extend(obj,Backbone.Events);
obj.on('a b c', function() { obj.counter += 1; });
obj.trigger('a');
equal(obj.counter, 1);
obj.trigger('a b');
equal(obj.counter, 3);
obj.trigger('c');
equal(obj.counter, 4);
obj.off('a c');
obj.trigger('a b c');
equal(obj.counter, 5);
});
test("Events: trigger all for each event", 3, function() {
var a, b, obj = { counter: 0 };
_.extend(obj, Backbone.Events);
obj.on('all', function(event) {
obj.counter++;
if (event == 'a') a = true;
if (event == 'b') b = true;
})
.trigger('a b');
ok(a);
ok(b);
equal(obj.counter, 2);
});
test("Events: on, then unbind all functions", 1, function() {
var obj = { counter: 0 };
_.extend(obj,Backbone.Events);
var callback = function() { obj.counter += 1; };
obj.on('event', callback);
obj.trigger('event');
obj.off('event');
obj.trigger('event');
equal(obj.counter, 1, 'counter should have only been incremented once.');
});
test("Events: bind two callbacks, unbind only one", 2, function() {
var obj = { counterA: 0, counterB: 0 };
_.extend(obj,Backbone.Events);
var callback = function() { obj.counterA += 1; };
obj.on('event', callback);
obj.on('event', function() { obj.counterB += 1; });
obj.trigger('event');
obj.off('event', callback);
obj.trigger('event');
equal(obj.counterA, 1, 'counterA should have only been incremented once.');
equal(obj.counterB, 2, 'counterB should have been incremented twice.');
});
test("Events: unbind a callback in the midst of it firing", 1, function() {
var obj = {counter: 0};
_.extend(obj, Backbone.Events);
var callback = function() {
obj.counter += 1;
obj.off('event', callback);
};
obj.on('event', callback);
obj.trigger('event');
obj.trigger('event');
obj.trigger('event');
equal(obj.counter, 1, 'the callback should have been unbound.');
});
test("Events: two binds that unbind themeselves", 2, function() {
var obj = { counterA: 0, counterB: 0 };
_.extend(obj,Backbone.Events);
var incrA = function(){ obj.counterA += 1; obj.off('event', incrA); };
var incrB = function(){ obj.counterB += 1; obj.off('event', incrB); };
obj.on('event', incrA);
obj.on('event', incrB);
obj.trigger('event');
obj.trigger('event');
obj.trigger('event');
equal(obj.counterA, 1, 'counterA should have only been incremented once.');
equal(obj.counterB, 1, 'counterB should have only been incremented once.');
});
test("Events: bind a callback with a supplied context", 1, function () {
var TestClass = function () {
return this;
};
TestClass.prototype.assertTrue = function () {
ok(true, '`this` was bound to the callback');
};
var obj = _.extend({},Backbone.Events);
obj.on('event', function () { this.assertTrue(); }, (new TestClass));
obj.trigger('event');
});
test("Events: nested trigger with unbind", 1, function () {
var obj = { counter: 0 };
_.extend(obj, Backbone.Events);
var incr1 = function(){ obj.counter += 1; obj.off('event', incr1); obj.trigger('event'); };
var incr2 = function(){ obj.counter += 1; };
obj.on('event', incr1);
obj.on('event', incr2);
obj.trigger('event');
equal(obj.counter, 3, 'counter should have been incremented three times');
});
test("Events: callback list is not altered during trigger", 2, function () {
var counter = 0, obj = _.extend({}, Backbone.Events);
var incr = function(){ counter++; };
obj.on('event', function(){ obj.on('event', incr).on('all', incr); })
.trigger('event');
equal(counter, 0, 'bind does not alter callback list');
obj.off()
.on('event', function(){ obj.off('event', incr).off('all', incr); })
.on('event', incr)
.on('all', incr)
.trigger('event');
equal(counter, 2, 'unbind does not alter callback list');
});
test("#1282 - 'all' callback list is retrieved after each event.", 1, function() {
var counter = 0;
var obj = _.extend({}, Backbone.Events);
var incr = function(){ counter++; };
obj.on('x', function() {
obj.on('y', incr).on('all', incr);
})
.trigger('x y');
strictEqual(counter, 2);
});
test("if no callback is provided, `on` is a noop", 0, function() {
_.extend({}, Backbone.Events).on('test').trigger('test');
});
test("remove all events for a specific context", 4, function() {
var obj = _.extend({}, Backbone.Events);
obj.on('x y all', function() { ok(true); });
obj.on('x y all', function() { ok(false); }, obj);
obj.off(null, null, obj);
obj.trigger('x y');
});
test("remove all events for a specific callback", 4, function() {
var obj = _.extend({}, Backbone.Events);
var success = function() { ok(true); };
var fail = function() { ok(false); };
obj.on('x y all', success);
obj.on('x y all', fail);
obj.off(null, fail);
obj.trigger('x y');
});
test("off is chainable", 3, function() {
var obj = _.extend({}, Backbone.Events);
// With no events
ok(obj.off() === obj);
// When removing all events
obj.on('event', function(){}, obj);
ok(obj.off() === obj);
// When removing some events
obj.on('event', function(){}, obj);
ok(obj.off('event') === obj);
});
test("#1310 - off does not skip consecutive events", 0, function() {
var obj = _.extend({}, Backbone.Events);
obj.on('event', function() { ok(false); }, obj);
obj.on('event', function() { ok(false); }, obj);
obj.off(null, null, obj);
obj.trigger('event');
});
});

855
vendor/backbone/test/model.js vendored Normal file
View File

@@ -0,0 +1,855 @@
$(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 klass = Backbone.Collection.extend({
url : function() { return '/collection'; }
});
var doc, collection;
module("Backbone.Model", {
setup: function() {
doc = new proxy({
id : '1-the-tempest',
title : "The Tempest",
author : "Bill Shakespeare",
length : 123
});
collection = new klass();
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() {
var Model = Backbone.Model.extend({
initialize: function() {
this.one = 1;
equal(this.collection, collection);
}
});
var model = new Model({}, {collection: collection});
equal(model.one, 1);
equal(model.collection, collection);
});
test("Model: initialize with attributes and options", 1, function() {
var Model = Backbone.Model.extend({
initialize: function(attributes, options) {
this.one = options.one;
}
});
var model = new Model({}, {one: 1});
equal(model.one, 1);
});
test("Model: initialize with parsed attributes", 1, function() {
var Model = Backbone.Model.extend({
parse: function(obj) {
obj.value += 1;
return obj;
}
});
var model = new Model({value: 1}, {parse: true});
equal(model.get('value'), 2);
});
test("Model: url", 3, function() {
doc.urlRoot = null;
equal(doc.url(), '/collection/1-the-tempest');
doc.collection.url = '/collection/';
equal(doc.url(), '/collection/1-the-tempest');
doc.collection = null;
raises(function() { doc.url(); });
doc.collection = collection;
});
test("Model: url when using urlRoot, and uri encoding", 2, function() {
var Model = Backbone.Model.extend({
urlRoot: '/collection'
});
var model = new Model();
equal(model.url(), '/collection');
model.set({id: '+1+'});
equal(model.url(), '/collection/%2B1%2B');
});
test("Model: url when using urlRoot as a function to determine urlRoot at runtime", 2, function() {
var Model = Backbone.Model.extend({
urlRoot: function() {
return '/nested/' + this.get('parent_id') + '/collection';
}
});
var model = new Model({parent_id: 1});
equal(model.url(), '/nested/1/collection');
model.set({id: 2});
equal(model.url(), '/nested/1/collection/2');
});
test("Model: clone", 8, function() {
var a = new Backbone.Model({ 'foo': 1, 'bar': 2, 'baz': 3});
var b = a.clone();
equal(a.get('foo'), 1);
equal(a.get('bar'), 2);
equal(a.get('baz'), 3);
equal(b.get('foo'), a.get('foo'), "Foo should be the same on the clone.");
equal(b.get('bar'), a.get('bar'), "Bar should be the same on the clone.");
equal(b.get('baz'), a.get('baz'), "Baz should be the same on the clone.");
a.set({foo : 100});
equal(a.get('foo'), 100);
equal(b.get('foo'), 1, "Changing a parent attribute does not change the clone.");
});
test("Model: isNew", 6, function() {
var a = new Backbone.Model({ 'foo': 1, 'bar': 2, 'baz': 3});
ok(a.isNew(), "it should be new");
a = new Backbone.Model({ 'foo': 1, 'bar': 2, 'baz': 3, 'id': -5 });
ok(!a.isNew(), "any defined ID is legal, negative or positive");
a = new Backbone.Model({ 'foo': 1, 'bar': 2, 'baz': 3, 'id': 0 });
ok(!a.isNew(), "any defined ID is legal, including zero");
ok( new Backbone.Model({ }).isNew(), "is true when there is no id");
ok(!new Backbone.Model({ 'id': 2 }).isNew(), "is false for a positive integer");
ok(!new Backbone.Model({ 'id': -5 }).isNew(), "is false for a negative integer");
});
test("Model: get", 2, function() {
equal(doc.get('title'), 'The Tempest');
equal(doc.get('author'), 'Bill Shakespeare');
});
test("Model: escape", 5, function() {
equal(doc.escape('title'), 'The Tempest');
doc.set({audience: 'Bill & Bob'});
equal(doc.escape('audience'), 'Bill &amp; Bob');
doc.set({audience: 'Tim > Joan'});
equal(doc.escape('audience'), 'Tim &gt; Joan');
doc.set({audience: 10101});
equal(doc.escape('audience'), '10101');
doc.unset('audience');
equal(doc.escape('audience'), '');
});
test("Model: has", 10, function() {
var model = new Backbone.Model();
strictEqual(model.has('name'), false);
model.set({
'0': 0,
'1': 1,
'true': true,
'false': false,
'empty': '',
'name': 'name',
'null': null,
'undefined': undefined
});
strictEqual(model.has('0'), true);
strictEqual(model.has('1'), true);
strictEqual(model.has('true'), true);
strictEqual(model.has('false'), true);
strictEqual(model.has('empty'), true);
strictEqual(model.has('name'), true);
model.unset('name');
strictEqual(model.has('name'), false);
strictEqual(model.has('null'), false);
strictEqual(model.has('undefined'), false);
});
test("Model: set and unset", 8, function() {
var a = new Backbone.Model({id: 'id', foo: 1, bar: 2, baz: 3});
var changeCount = 0;
a.on("change:foo", function() { changeCount += 1; });
a.set({'foo': 2});
ok(a.get('foo') == 2, "Foo should have changed.");
ok(changeCount == 1, "Change count should have incremented.");
a.set({'foo': 2}); // set with value that is not new shouldn't fire change event
ok(a.get('foo') == 2, "Foo should NOT have changed, still 2");
ok(changeCount == 1, "Change count should NOT have incremented.");
a.validate = function(attrs) {
equal(attrs.foo, void 0, "don't ignore values when unsetting");
};
a.unset('foo');
equal(a.get('foo'), void 0, "Foo should have changed");
delete a.validate;
ok(changeCount == 2, "Change count should have incremented for unset.");
a.unset('id');
equal(a.id, undefined, "Unsetting the id should remove the id property.");
});
test("Model: multiple unsets", 1, function() {
var i = 0;
var counter = function(){ i++; };
var model = new Backbone.Model({a: 1});
model.on("change:a", counter);
model.set({a: 2});
model.unset('a');
model.unset('a');
equal(i, 2, 'Unset does not fire an event for missing attributes.');
});
test("Model: unset and changedAttributes", 2, function() {
var model = new Backbone.Model({a: 1});
model.unset('a', {silent: true});
var changedAttributes = model.changedAttributes();
ok('a' in changedAttributes, 'changedAttributes should contain unset properties');
changedAttributes = model.changedAttributes();
ok('a' in changedAttributes, 'changedAttributes should contain unset properties when running changedAttributes again after an unset.');
});
test("Model: using a non-default id attribute.", 5, function() {
var MongoModel = Backbone.Model.extend({idAttribute : '_id'});
var model = new MongoModel({id: 'eye-dee', _id: 25, title: 'Model'});
equal(model.get('id'), 'eye-dee');
equal(model.id, 25);
equal(model.isNew(), false);
model.unset('_id');
equal(model.id, undefined);
equal(model.isNew(), true);
});
test("Model: set an empty string", 1, function() {
var model = new Backbone.Model({name : "Model"});
model.set({name : ''});
equal(model.get('name'), '');
});
test("Model: clear", 3, function() {
var changed;
var model = new Backbone.Model({id: 1, name : "Model"});
model.on("change:name", function(){ changed = true; });
model.on("change", function() {
var changedAttrs = model.changedAttributes();
ok('name' in changedAttrs);
});
model.clear();
equal(changed, true);
equal(model.get('name'), undefined);
});
test("Model: defaults", 4, function() {
var Defaulted = Backbone.Model.extend({
defaults: {
"one": 1,
"two": 2
}
});
var model = new Defaulted({two: null});
equal(model.get('one'), 1);
equal(model.get('two'), null);
Defaulted = Backbone.Model.extend({
defaults: function() {
return {
"one": 3,
"two": 4
};
}
});
var model = new Defaulted({two: null});
equal(model.get('one'), 3);
equal(model.get('two'), null);
});
test("Model: change, hasChanged, changedAttributes, previous, previousAttributes", 12, function() {
var model = new Backbone.Model({name : "Tim", age : 10});
equal(model.changedAttributes(), false);
model.on('change', function() {
ok(model.hasChanged('name'), 'name changed');
ok(!model.hasChanged('age'), 'age did not');
ok(_.isEqual(model.changedAttributes(), {name : 'Rob'}), 'changedAttributes returns the changed attrs');
equal(model.previous('name'), 'Tim');
ok(_.isEqual(model.previousAttributes(), {name : "Tim", age : 10}), 'previousAttributes is correct');
});
equal(model.hasChanged(), false);
equal(model.hasChanged(undefined), false);
model.set({name : 'Rob'}, {silent : true});
equal(model.hasChanged(), true);
equal(model.hasChanged(undefined), true);
equal(model.hasChanged('name'), true);
model.change();
equal(model.get('name'), 'Rob');
});
test("Model: changedAttributes", 3, function() {
var model = new Backbone.Model({a: 'a', b: 'b'});
equal(model.changedAttributes(), false);
equal(model.changedAttributes({a: 'a'}), false);
equal(model.changedAttributes({a: 'b'}).a, 'b');
});
test("Model: change with options", 2, function() {
var value;
var model = new Backbone.Model({name: 'Rob'});
model.on('change', function(model, options) {
value = options.prefix + model.get('name');
});
model.set({name: 'Bob'}, {silent: true});
model.change({prefix: 'Mr. '});
equal(value, 'Mr. Bob');
model.set({name: 'Sue'}, {prefix: 'Ms. '});
equal(value, 'Ms. Sue');
});
test("Model: change after initialize", 1, function () {
var changed = 0;
var attrs = {id: 1, label: 'c'};
var obj = new Backbone.Model(attrs);
obj.on('change', function() { changed += 1; });
obj.set(attrs);
equal(changed, 0);
});
test("Model: save within change event", 1, function () {
var model = new Backbone.Model({firstName : "Taylor", lastName: "Swift"});
model.on('change', function () {
model.save();
ok(_.isEqual(lastRequest.model, model));
});
model.set({lastName: 'Hicks'});
});
test("Model: validate after save", 1, function() {
var lastError, model = new Backbone.Model();
model.validate = function(attrs) {
if (attrs.admin) return "Can't change admin status.";
};
model.sync = function(method, model, options) {
options.success.call(this, {admin: true});
};
model.save(null, {error: function(model, error) {
lastError = error;
}});
equal(lastError, "Can't change admin status.");
});
test("Model: isValid", 5, function() {
var model = new Backbone.Model({valid: true});
model.validate = function(attrs) {
if (!attrs.valid) return "invalid";
};
equal(model.isValid(), true);
equal(model.set({valid: false}), false);
equal(model.isValid(), true);
ok(model.set('valid', false, {silent: true}));
equal(model.isValid(), false);
});
test("Model: save", 2, function() {
doc.save({title : "Henry V"});
equal(lastRequest.method, 'update');
ok(_.isEqual(lastRequest.model, doc));
});
test("Model: save in positional style", 1, function() {
var model = new Backbone.Model();
model.sync = function(method, model, options) {
options.success();
};
model.save('title', 'Twelfth Night');
equal(model.get('title'), 'Twelfth Night');
});
test("Model: fetch", 2, function() {
doc.fetch();
equal(lastRequest.method, 'read');
ok(_.isEqual(lastRequest.model, doc));
});
test("Model: destroy", 3, function() {
doc.destroy();
equal(lastRequest.method, 'delete');
ok(_.isEqual(lastRequest.model, doc));
var newModel = new Backbone.Model;
equal(newModel.destroy(), false);
});
test("Model: non-persisted destroy", 1, function() {
var a = new Backbone.Model({ 'foo': 1, 'bar': 2, 'baz': 3});
a.sync = function() { throw "should not be called"; };
a.destroy();
ok(true, "non-persisted model should not call sync");
});
test("Model: validate", 7, function() {
var lastError;
var model = new Backbone.Model();
model.validate = function(attrs) {
if (attrs.admin != this.get('admin')) return "Can't change admin status.";
};
model.on('error', function(model, error) {
lastError = error;
});
var result = model.set({a: 100});
equal(result, model);
equal(model.get('a'), 100);
equal(lastError, undefined);
result = model.set({admin: true}, {silent: true});
equal(model.get('admin'), true);
result = model.set({a: 200, admin: false});
equal(lastError, "Can't change admin status.");
equal(result, false);
equal(model.get('a'), 100);
});
test("Model: validate on unset and clear", 6, function() {
var error;
var model = new Backbone.Model({name: "One"});
model.validate = function(attrs) {
if (!attrs.name) {
error = true;
return "No thanks.";
}
};
model.set({name: "Two"});
equal(model.get('name'), 'Two');
equal(error, undefined);
model.unset('name');
equal(error, true);
equal(model.get('name'), 'Two');
model.clear();
equal(model.get('name'), 'Two');
delete model.validate;
model.clear();
equal(model.get('name'), undefined);
});
test("Model: validate with error callback", 8, function() {
var lastError, boundError;
var model = new Backbone.Model();
model.validate = function(attrs) {
if (attrs.admin) return "Can't change admin status.";
};
var callback = function(model, error) {
lastError = error;
};
model.on('error', function(model, error) {
boundError = true;
});
var result = model.set({a: 100}, {error: callback});
equal(result, model);
equal(model.get('a'), 100);
equal(lastError, undefined);
equal(boundError, undefined);
result = model.set({a: 200, admin: true}, {error: callback});
equal(result, false);
equal(model.get('a'), 100);
equal(lastError, "Can't change admin status.");
equal(boundError, undefined);
});
test("Model: defaults always extend attrs (#459)", 2, function() {
var Defaulted = Backbone.Model.extend({
defaults: {one: 1},
initialize : function(attrs, opts) {
equal(this.attributes.one, 1);
}
});
var providedattrs = new Defaulted({});
var emptyattrs = new Defaulted();
});
test("Model: Inherit class properties", 6, function() {
var Parent = Backbone.Model.extend({
instancePropSame: function() {},
instancePropDiff: function() {}
}, {
classProp: function() {}
});
var Child = Parent.extend({
instancePropDiff: function() {}
});
var adult = new Parent;
var kid = new Child;
equal(Child.classProp, Parent.classProp);
notEqual(Child.classProp, undefined);
equal(kid.instancePropSame, adult.instancePropSame);
notEqual(kid.instancePropSame, undefined);
notEqual(Child.prototype.instancePropDiff, Parent.prototype.instancePropDiff);
notEqual(Child.prototype.instancePropDiff, undefined);
});
test("Model: Nested change events don't clobber previous attributes", 4, function() {
new Backbone.Model()
.on('change:state', function(model, newState) {
equal(model.previous('state'), undefined);
equal(newState, 'hello');
// Fire a nested change event.
model.set({other: 'whatever'});
})
.on('change:state', function(model, newState) {
equal(model.previous('state'), undefined);
equal(newState, 'hello');
})
.set({state: 'hello'});
});
test("hasChanged/set should use same comparison", 2, function() {
var changed = 0, model = new Backbone.Model({a: null});
model.on('change', function() {
ok(this.hasChanged('a'));
})
.on('change:a', function() {
changed++;
})
.set({a: undefined});
equal(changed, 1);
});
test("#582, #425, change:attribute callbacks should fire after all changes have occurred", 9, function() {
var model = new Backbone.Model;
var assertion = function() {
equal(model.get('a'), 'a');
equal(model.get('b'), 'b');
equal(model.get('c'), 'c');
};
model.on('change:a', assertion);
model.on('change:b', assertion);
model.on('change:c', assertion);
model.set({a: 'a', b: 'b', c: 'c'});
});
test("#871, set with attributes property", 1, function() {
var model = new Backbone.Model();
model.set({attributes: true});
ok(model.has('attributes'));
});
test("set value regardless of equality/change", 1, function() {
var model = new Backbone.Model({x: []});
var a = [];
model.set({x: a});
ok(model.get('x') === a);
});
test("unset fires change for undefined attributes", 1, function() {
var model = new Backbone.Model({x: undefined});
model.on('change:x', function(){ ok(true); });
model.unset('x');
});
test("set: undefined values", 1, function() {
var model = new Backbone.Model({x: undefined});
ok('x' in model.attributes);
});
test("change fires change:attr", 1, function() {
var model = new Backbone.Model({x: 1});
model.set({x: 2}, {silent: true});
model.on('change:x', function(){ ok(true); });
model.change();
});
test("hasChanged is false after original values are set", 2, function() {
var model = new Backbone.Model({x: 1});
model.on('change:x', function(){ ok(false); });
model.set({x: 2}, {silent: true});
ok(model.hasChanged());
model.set({x: 1}, {silent: true});
ok(!model.hasChanged());
});
test("save with `wait` succeeds without `validate`", 1, function() {
var model = new Backbone.Model();
model.save({x: 1}, {wait: true});
ok(lastRequest.model === model);
});
test("`hasChanged` for falsey keys", 2, function() {
var model = new Backbone.Model();
model.set({x: true}, {silent: true});
ok(!model.hasChanged(0));
ok(!model.hasChanged(''));
});
test("`previous` for falsey keys", 2, function() {
var model = new Backbone.Model({0: true, '': true});
model.set({0: false, '': false}, {silent: true});
equal(model.previous(0), true);
equal(model.previous(''), true);
});
test("`save` with `wait` sends correct attributes", 5, function() {
var changed = 0;
var model = new Backbone.Model({x: 1, y: 2});
model.on('change:x', function() { changed++; });
model.save({x: 3}, {wait: true});
deepEqual(JSON.parse(ajaxParams.data), {x: 3, y: 2});
equal(model.get('x'), 1);
equal(changed, 0);
lastRequest.options.success({});
equal(model.get('x'), 3);
equal(changed, 1);
});
test("a failed `save` with `wait` doesn't leave attributes behind", 1, function() {
var model = new Backbone.Model;
model.save({x: 1}, {wait: true});
equal(model.get('x'), void 0);
});
test("#1030 - `save` with `wait` results in correct attributes if success is called during sync", 2, function() {
var model = new Backbone.Model({x: 1, y: 2});
model.sync = function(method, model, options) {
options.success();
};
model.on("change:x", function() { ok(true); });
model.save({x: 3}, {wait: true});
equal(model.get('x'), 3);
});
test("save with wait validates attributes", 1, function() {
var model = new Backbone.Model();
model.validate = function() { ok(true); };
model.save({x: 1}, {wait: true});
});
test("nested `set` during `'change:attr'`", 2, function() {
var events = [];
var model = new Backbone.Model();
model.on('all', function(event) { events.push(event); });
model.on('change', function() {
model.set({z: true}, {silent:true});
});
model.on('change:x', function() {
model.set({y: true});
});
model.set({x: true});
deepEqual(events, ['change:y', 'change:x', 'change']);
events = [];
model.change();
deepEqual(events, ['change:z', 'change']);
});
test("nested `change` only fires once", 1, function() {
var model = new Backbone.Model();
model.on('change', function() {
ok(true);
model.change();
});
model.set({x: true});
});
test("no `'change'` event if no changes", 0, function() {
var model = new Backbone.Model();
model.on('change', function() { ok(false); });
model.change();
});
test("nested `set` during `'change'`", 6, function() {
var count = 0;
var model = new Backbone.Model();
model.on('change', function() {
switch(count++) {
case 0:
deepEqual(this.changedAttributes(), {x: true});
equal(model.previous('x'), undefined);
model.set({y: true});
break;
case 1:
deepEqual(this.changedAttributes(), {y: true});
equal(model.previous('x'), true);
model.set({z: true});
break;
case 2:
deepEqual(this.changedAttributes(), {z: true});
equal(model.previous('y'), true);
break;
default:
ok(false);
}
});
model.set({x: true});
});
test("nested `'change'` with silent", 3, function() {
var count = 0;
var model = new Backbone.Model();
model.on('change:y', function() { ok(true); });
model.on('change', function() {
switch(count++) {
case 0:
deepEqual(this.changedAttributes(), {x: true});
model.set({y: true}, {silent: true});
break;
case 1:
deepEqual(this.changedAttributes(), {y: true, z: true});
break;
default:
ok(false);
}
});
model.set({x: true});
model.set({z: true});
});
test("nested `'change:attr'` with silent", 1, function() {
var model = new Backbone.Model();
model.on('change:y', function(){ ok(true); });
model.on('change', function() {
model.set({y: true}, {silent: true});
model.set({z: true});
});
model.set({x: true});
});
test("multiple nested changes with silent", 1, function() {
var model = new Backbone.Model();
model.on('change:x', function() {
model.set({y: 1}, {silent: true});
model.set({y: 2});
});
model.on('change:y', function(model, val) {
equal(val, 2);
});
model.set({x: true});
model.change();
});
test("multiple nested changes with silent", 2, function() {
var changes = [];
var model = new Backbone.Model();
model.on('change:b', function(model, val) { changes.push(val); });
model.on('change', function() {
model.set({b: 1});
model.set({b: 2}, {silent: true});
});
model.set({b: 0});
deepEqual(changes, [0, 1, 1]);
model.change();
deepEqual(changes, [0, 1, 1, 2, 1]);
});
test("nested set multiple times", 1, function() {
var model = new Backbone.Model();
model.on('change:b', function() {
ok(true);
});
model.on('change:a', function() {
model.set({b: true});
model.set({b: 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() {
var model = new Backbone.Model();
model.validate = null;
ok(model.isValid());
});
test("#1122 - clear does not alter options.", 1, function() {
var model = new Backbone.Model();
var options = {};
model.clear(options);
ok(!options.unset);
});
test("#1122 - unset does not alter options.", 1, function() {
var model = new Backbone.Model();
var options = {};
model.unset('x', options);
ok(!options.unset);
});
test("#1355 - `options` is passed to success callbacks", 3, function() {
var model = new Backbone.Model();
var opts = {
success: function( model, resp, options ) {
ok(options);
}
};
model.sync = function(method, model, options) {
options.success();
};
model.save({id: 1}, opts);
model.fetch(opts);
model.destroy(opts);
});
test("#1412 - Trigger 'sync' event.", 3, function() {
var model = new Backbone.Model({id: 1});
model.sync = function(method, model, options) { options.success(); };
model.on('sync', function() { ok(true); });
model.fetch();
model.save();
model.destroy();
});
test("#1365 - Destroy: New models execute success callback.", 2, function() {
new Backbone.Model()
.on('sync', function() { ok(false); })
.on('destroy', function(){ ok(true); })
.destroy({ success: function(){ ok(true); }});
});
test("#1433 - Save: An invalid model cannot be persisted.", 1, function() {
var model = new Backbone.Model;
model.validate = function(){ return 'invalid'; };
model.sync = function(){ ok(false); };
strictEqual(model.save(), false);
});
});

12
vendor/backbone/test/noconflict.js vendored Normal file
View File

@@ -0,0 +1,12 @@
$(document).ready(function() {
module("Backbone.noConflict");
test('Backbone.noConflict', 2, function() {
var noconflictBackbone = Backbone.noConflict();
equal(window.Backbone, undefined, 'Returned window.Backbone');
window.Backbone = noconflictBackbone;
equal(window.Backbone, noconflictBackbone, 'Backbone is still pointing to the original Backbone');
});
});

321
vendor/backbone/test/router.js vendored Normal file
View File

@@ -0,0 +1,321 @@
$(document).ready(function() {
var router = null;
var location = null;
var lastRoute = null;
var lastArgs = [];
function onRoute(router, route, args) {
lastRoute = route;
lastArgs = args;
}
var Location = function(href) {
this.replace(href);
};
_.extend(Location.prototype, {
replace: function(href) {
_.extend(this, _.pick($('<a></a>', {href: href})[0],
'href',
'hash',
'search',
'fragment',
'pathname'
));
// In IE, anchor.pathname does not contain a leading slash though
// window.location.pathname does.
if (!/^\//.test(this.pathname)) this.pathname = '/' + this.pathname;
},
toString: function() {
return this.href;
}
});
module("Backbone.Router", {
setup: function() {
location = new Location('http://example.com');
Backbone.history = new Backbone.History({location: location});
router = new Router({testing: 101});
Backbone.history.interval = 9;
Backbone.history.start({pushState: false});
lastRoute = null;
lastArgs = [];
Backbone.history.on('route', onRoute);
},
teardown: function() {
Backbone.history.stop();
Backbone.history.off('route', onRoute);
}
});
var Router = Backbone.Router.extend({
count: 0,
routes: {
"noCallback": "noCallback",
"counter": "counter",
"search/:query": "search",
"search/:query/p:page": "search",
"contacts": "contacts",
"contacts/new": "newContact",
"contacts/:id": "loadContact",
"splat/*args/end": "splat",
"*first/complex-:part/*rest": "complex",
":entity?*args": "query",
"*anything": "anything"
},
initialize : function(options) {
this.testing = options.testing;
this.route('implicit', 'implicit');
},
counter: function() {
this.count++;
},
implicit: function() {
this.count++;
},
search : function(query, page) {
this.query = query;
this.page = page;
},
contacts: function(){
this.contact = 'index';
},
newContact: function(){
this.contact = 'new';
},
loadContact: function(){
this.contact = 'load';
},
splat : function(args) {
this.args = args;
},
complex : function(first, part, rest) {
this.first = first;
this.part = part;
this.rest = rest;
},
query : function(entity, args) {
this.entity = entity;
this.queryArgs = args;
},
anything : function(whatever) {
this.anything = whatever;
}
});
test("Router: initialize", 1, function() {
equal(router.testing, 101);
});
test("Router: routes (simple)", 4, function() {
location.replace('http://example.com#search/news');
Backbone.history.checkUrl();
equal(router.query, 'news');
equal(router.page, undefined);
equal(lastRoute, 'search');
equal(lastArgs[0], 'news');
});
test("Router: routes (two part)", 2, function() {
location.replace('http://example.com#search/nyc/p10');
Backbone.history.checkUrl();
equal(router.query, 'nyc');
equal(router.page, '10');
});
test("Router: routes via navigate", 2, function() {
Backbone.history.navigate('search/manhattan/p20', {trigger: true});
equal(router.query, 'manhattan');
equal(router.page, '20');
});
test("Router: routes via navigate for backwards-compatibility", 2, function() {
Backbone.history.navigate('search/manhattan/p20', true);
equal(router.query, 'manhattan');
equal(router.page, '20');
});
test("Router: route precedence via navigate", 6, function(){
// check both 0.9.x and backwards-compatibility options
_.each([ { trigger: true }, true ], function( options ){
Backbone.history.navigate('contacts', options);
equal(router.contact, 'index');
Backbone.history.navigate('contacts/new', options);
equal(router.contact, 'new');
Backbone.history.navigate('contacts/foo', options);
equal(router.contact, 'load');
});
});
test("loadUrl is not called for identical routes.", 0, function() {
Backbone.history.loadUrl = function(){ ok(false); };
location.replace('http://example.com#route');
Backbone.history.navigate('route');
Backbone.history.navigate('/route');
Backbone.history.navigate('/route');
});
test("Router: use implicit callback if none provided", 1, function() {
router.count = 0;
router.navigate('implicit', {trigger: true});
equal(router.count, 1);
});
test("Router: routes via navigate with {replace: true}", 1, function() {
location.replace('http://example.com#start_here');
Backbone.history.checkUrl();
location.replace = function(href) {
strictEqual(href, new Location('http://example.com#end_here').href);
};
Backbone.history.navigate('end_here', {replace: true});
});
test("Router: routes (splats)", 1, function() {
location.replace('http://example.com#splat/long-list/of/splatted_99args/end');
Backbone.history.checkUrl();
equal(router.args, 'long-list/of/splatted_99args');
});
test("Router: routes (complex)", 3, function() {
location.replace('http://example.com#one/two/three/complex-part/four/five/six/seven');
Backbone.history.checkUrl();
equal(router.first, 'one/two/three');
equal(router.part, 'part');
equal(router.rest, 'four/five/six/seven');
});
test("Router: routes (query)", 5, function() {
location.replace('http://example.com#mandel?a=b&c=d');
Backbone.history.checkUrl();
equal(router.entity, 'mandel');
equal(router.queryArgs, 'a=b&c=d');
equal(lastRoute, 'query');
equal(lastArgs[0], 'mandel');
equal(lastArgs[1], 'a=b&c=d');
});
test("Router: routes (anything)", 1, function() {
location.replace('http://example.com#doesnt-match-a-route');
Backbone.history.checkUrl();
equal(router.anything, 'doesnt-match-a-route');
});
test("Router: fires event when router doesn't have callback on it", 1, function() {
router.on("route:noCallback", function(){ ok(true); });
location.replace('http://example.com#noCallback');
Backbone.history.checkUrl();
});
test("#933, #908 - leading slash", 2, function() {
location.replace('http://example.com/root/foo');
Backbone.history.stop();
Backbone.history = new Backbone.History({location: location});
Backbone.history.start({root: '/root', hashChange: false, silent: true});
strictEqual(Backbone.history.getFragment(), 'foo');
Backbone.history.stop();
Backbone.history = new Backbone.History({location: location});
Backbone.history.start({root: '/root/', hashChange: false, silent: true});
strictEqual(Backbone.history.getFragment(), 'foo');
});
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();
history.start();
// If this is not an old IE navigate will not be called.
if (!history.iframe) ok(true);
});
test("Router: route callback gets passed decoded values", 3, function() {
var route = 'has%2Fslash/complex-has%23hash/has%20space';
Backbone.history.navigate(route, {trigger: true});
equal(router.first, 'has/slash');
equal(router.part, 'has#hash');
equal(router.rest, 'has space');
});
test("Router: correctly handles URLs with % (#868)", 3, function() {
location.replace('http://example.com#search/fat%3A1.5%25');
Backbone.history.checkUrl();
location.replace('http://example.com#search/fat');
Backbone.history.checkUrl();
equal(router.query, 'fat');
equal(router.page, undefined);
equal(lastRoute, 'search');
});
test("#1185 - Use pathname when hashChange is not wanted.", 1, function() {
Backbone.history.stop();
location.replace('http://example.com/path/name#hash');
Backbone.history = new Backbone.History({location: location});
Backbone.history.start({hashChange: false});
var fragment = Backbone.history.getFragment();
strictEqual(fragment, location.pathname.replace(/^\//, ''));
});
test("#1206 - Strip leading slash before location.assign.", 1, function() {
Backbone.history.stop();
location.replace('http://example.com/root/');
Backbone.history = new Backbone.History({location: location});
Backbone.history.start({hashChange: false, root: '/root/'});
location.assign = function(pathname) {
strictEqual(pathname, '/root/fragment');
};
Backbone.history.navigate('/fragment');
});
test("#1387 - Root fragment without trailing slash.", 1, function() {
Backbone.history.stop();
location.replace('http://example.com/root');
Backbone.history = new Backbone.History({location: location});
Backbone.history.start({hashChange: false, root: '/root/', silent: true});
strictEqual(Backbone.history.getFragment(), '');
});
test("#1366 - History does not prepend root to fragment.", 2, function() {
Backbone.history.stop();
location.replace('http://example.com/root/');
Backbone.history = new Backbone.History({
location: location,
history: {
pushState: function(state, title, url) {
strictEqual(url, '/root/x');
}
}
});
Backbone.history.start({
root: '/root/',
pushState: true,
hashChange: false
});
Backbone.history.navigate('x');
strictEqual(Backbone.history.fragment, 'x');
});
});

160
vendor/backbone/test/sync.js vendored Normal file
View File

@@ -0,0 +1,160 @@
$(document).ready(function() {
var ajax = Backbone.ajax;
var lastRequest = null;
var Library = Backbone.Collection.extend({
url : function() { return '/library'; }
});
var library;
var attrs = {
title : "The Tempest",
author : "Bill Shakespeare",
length : 123
};
module("Backbone.sync", {
setup : function() {
library = new Library();
Backbone.ajax = function(obj) {
lastRequest = obj;
};
library.create(attrs, {wait: false});
},
teardown: function() {
Backbone.ajax = ajax;
}
});
test("sync: read", 4, function() {
library.fetch();
equal(lastRequest.url, '/library');
equal(lastRequest.type, 'GET');
equal(lastRequest.dataType, 'json');
ok(_.isEmpty(lastRequest.data));
});
test("sync: passing data", 3, function() {
library.fetch({data: {a: 'a', one: 1}});
equal(lastRequest.url, '/library');
equal(lastRequest.data.a, 'a');
equal(lastRequest.data.one, 1);
});
test("sync: create", 6, function() {
equal(lastRequest.url, '/library');
equal(lastRequest.type, 'POST');
equal(lastRequest.dataType, 'json');
var data = JSON.parse(lastRequest.data);
equal(data.title, 'The Tempest');
equal(data.author, 'Bill Shakespeare');
equal(data.length, 123);
});
test("sync: update", 7, function() {
library.first().save({id: '1-the-tempest', author: 'William Shakespeare'});
equal(lastRequest.url, '/library/1-the-tempest');
equal(lastRequest.type, 'PUT');
equal(lastRequest.dataType, 'json');
var data = JSON.parse(lastRequest.data);
equal(data.id, '1-the-tempest');
equal(data.title, 'The Tempest');
equal(data.author, 'William Shakespeare');
equal(data.length, 123);
});
test("sync: update with emulateHTTP and emulateJSON", 7, function() {
Backbone.emulateHTTP = Backbone.emulateJSON = true;
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
equal(lastRequest.url, '/library/2-the-tempest');
equal(lastRequest.type, 'POST');
equal(lastRequest.dataType, 'json');
equal(lastRequest.data._method, 'PUT');
var data = JSON.parse(lastRequest.data.model);
equal(data.id, '2-the-tempest');
equal(data.author, 'Tim Shakespeare');
equal(data.length, 123);
Backbone.emulateHTTP = Backbone.emulateJSON = false;
});
test("sync: update with just emulateHTTP", 6, function() {
Backbone.emulateHTTP = true;
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
equal(lastRequest.url, '/library/2-the-tempest');
equal(lastRequest.type, 'POST');
equal(lastRequest.contentType, 'application/json');
var data = JSON.parse(lastRequest.data);
equal(data.id, '2-the-tempest');
equal(data.author, 'Tim Shakespeare');
equal(data.length, 123);
Backbone.emulateHTTP = false;
});
test("sync: update with just emulateJSON", 6, function() {
Backbone.emulateJSON = true;
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
equal(lastRequest.url, '/library/2-the-tempest');
equal(lastRequest.type, 'PUT');
equal(lastRequest.contentType, 'application/x-www-form-urlencoded');
var data = JSON.parse(lastRequest.data.model);
equal(data.id, '2-the-tempest');
equal(data.author, 'Tim Shakespeare');
equal(data.length, 123);
Backbone.emulateJSON = false;
});
test("sync: read model", 3, function() {
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
library.first().fetch();
equal(lastRequest.url, '/library/2-the-tempest');
equal(lastRequest.type, 'GET');
ok(_.isEmpty(lastRequest.data));
});
test("sync: destroy", 3, function() {
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
library.first().destroy({wait: true});
equal(lastRequest.url, '/library/2-the-tempest');
equal(lastRequest.type, 'DELETE');
equal(lastRequest.data, null);
});
test("sync: destroy with emulateHTTP", 3, function() {
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
Backbone.emulateHTTP = Backbone.emulateJSON = true;
library.first().destroy();
equal(lastRequest.url, '/library/2-the-tempest');
equal(lastRequest.type, 'POST');
equal(JSON.stringify(lastRequest.data), '{"_method":"DELETE"}');
Backbone.emulateHTTP = Backbone.emulateJSON = false;
});
test("sync: urlError", 2, function() {
var model = new Backbone.Model();
raises(function() {
model.fetch();
});
model.fetch({url: '/one/two'});
equal(lastRequest.url, '/one/two');
});
test("#1052 - `options` is optional.", 0, function() {
var model = new Backbone.Model();
model.url = '/test';
Backbone.sync('create', model);
});
test("Backbone.ajax", 1, function() {
Backbone.ajax = function(settings){
strictEqual(settings.url, '/test');
};
var model = new Backbone.Model();
model.url = '/test';
Backbone.sync('create', model);
});
});

File diff suppressed because it is too large Load Diff

649
vendor/backbone/test/vendor/jslitmus.js vendored Normal file
View File

@@ -0,0 +1,649 @@
// JSLitmus.js
//
// Copyright (c) 2010, Robert Kieffer, http://broofa.com
// Available under MIT license (http://en.wikipedia.org/wiki/MIT_License)
(function() {
// Private methods and state
// Get platform info but don't go crazy trying to recognize everything
// that's out there. This is just for the major platforms and OSes.
var platform = 'unknown platform', ua = navigator.userAgent;
// Detect OS
var oses = ['Windows','iPhone OS','(Intel |PPC )?Mac OS X','Linux'].join('|');
var pOS = new RegExp('((' + oses + ') [^ \);]*)').test(ua) ? RegExp.$1 : null;
if (!pOS) pOS = new RegExp('((' + oses + ')[^ \);]*)').test(ua) ? RegExp.$1 : null;
// Detect browser
var pName = /(Chrome|MSIE|Safari|Opera|Firefox)/.test(ua) ? RegExp.$1 : null;
// Detect version
var vre = new RegExp('(Version|' + pName + ')[ \/]([^ ;]*)');
var pVersion = (pName && vre.test(ua)) ? RegExp.$2 : null;
var platform = (pOS && pName && pVersion) ? pName + ' ' + pVersion + ' on ' + pOS : 'unknown platform';
/**
* A smattering of methods that are needed to implement the JSLitmus testbed.
*/
var jsl = {
/**
* Enhanced version of escape()
*/
escape: function(s) {
s = s.replace(/,/g, '\\,');
s = escape(s);
s = s.replace(/\+/g, '%2b');
s = s.replace(/ /g, '+');
return s;
},
/**
* Get an element by ID.
*/
$: function(id) {
return document.getElementById(id);
},
/**
* Null function
*/
F: function() {},
/**
* Set the status shown in the UI
*/
status: function(msg) {
var el = jsl.$('jsl_status');
if (el) el.innerHTML = msg || '';
},
/**
* Convert a number to an abbreviated string like, "15K" or "10M"
*/
toLabel: function(n) {
if (n == Infinity) {
return 'Infinity';
} else if (n > 1e9) {
n = Math.round(n/1e8);
return n/10 + 'B';
} else if (n > 1e6) {
n = Math.round(n/1e5);
return n/10 + 'M';
} else if (n > 1e3) {
n = Math.round(n/1e2);
return n/10 + 'K';
}
return n;
},
/**
* Copy properties from src to dst
*/
extend: function(dst, src) {
for (var k in src) dst[k] = src[k]; return dst;
},
/**
* Like Array.join(), but for the key-value pairs in an object
*/
join: function(o, delimit1, delimit2) {
if (o.join) return o.join(delimit1); // If it's an array
var pairs = [];
for (var k in o) pairs.push(k + delimit1 + o[k]);
return pairs.join(delimit2);
},
/**
* Array#indexOf isn't supported in IE, so we use this as a cross-browser solution
*/
indexOf: function(arr, o) {
if (arr.indexOf) return arr.indexOf(o);
for (var i = 0; i < this.length; i++) if (arr[i] === o) return i;
return -1;
}
};
/**
* Test manages a single test (created with
* JSLitmus.test())
*
* @private
*/
var Test = function (name, f) {
if (!f) throw new Error('Undefined test function');
if (!/function[^\(]*\(([^,\)]*)/.test(f.toString())) {
throw new Error('"' + name + '" test: Test is not a valid Function object');
}
this.loopArg = RegExp.$1;
this.name = name;
this.f = f;
};
jsl.extend(Test, /** @lends Test */ {
/** Calibration tests for establishing iteration loop overhead */
CALIBRATIONS: [
new Test('calibrating loop', function(count) {while (count--);}),
new Test('calibrating function', jsl.F)
],
/**
* Run calibration tests. Returns true if calibrations are not yet
* complete (in which case calling code should run the tests yet again).
* onCalibrated - Callback to invoke when calibrations have finished
*/
calibrate: function(onCalibrated) {
for (var i = 0; i < Test.CALIBRATIONS.length; i++) {
var cal = Test.CALIBRATIONS[i];
if (cal.running) return true;
if (!cal.count) {
cal.isCalibration = true;
cal.onStop = onCalibrated;
//cal.MIN_TIME = .1; // Do calibrations quickly
cal.run(2e4);
return true;
}
}
return false;
}
});
jsl.extend(Test.prototype, {/** @lends Test.prototype */
/** Initial number of iterations */
INIT_COUNT: 10,
/** Max iterations allowed (i.e. used to detect bad looping functions) */
MAX_COUNT: 1e9,
/** Minimum time a test should take to get valid results (secs) */
MIN_TIME: .5,
/** Callback invoked when test state changes */
onChange: jsl.F,
/** Callback invoked when test is finished */
onStop: jsl.F,
/**
* Reset test state
*/
reset: function() {
delete this.count;
delete this.time;
delete this.running;
delete this.error;
},
/**
* Run the test (in a timeout). We use a timeout to make sure the browser
* has a chance to finish rendering any UI changes we've made, like
* updating the status message.
*/
run: function(count) {
count = count || this.INIT_COUNT;
jsl.status(this.name + ' x ' + count);
this.running = true;
var me = this;
setTimeout(function() {me._run(count);}, 200);
},
/**
* The nuts and bolts code that actually runs a test
*/
_run: function(count) {
var me = this;
// Make sure calibration tests have run
if (!me.isCalibration && Test.calibrate(function() {me.run(count);})) return;
this.error = null;
try {
var start, f = this.f, now, i = count;
// Start the timer
start = new Date();
// Now for the money shot. If this is a looping function ...
if (this.loopArg) {
// ... let it do the iteration itself
f(count);
} else {
// ... otherwise do the iteration for it
while (i--) f();
}
// Get time test took (in secs)
this.time = Math.max(1,new Date() - start)/1000;
// Store iteration count and per-operation time taken
this.count = count;
this.period = this.time/count;
// Do we need to do another run?
this.running = this.time <= this.MIN_TIME;
// ... if so, compute how many times we should iterate
if (this.running) {
// Bump the count to the nearest power of 2
var x = this.MIN_TIME/this.time;
var pow = Math.pow(2, Math.max(1, Math.ceil(Math.log(x)/Math.log(2))));
count *= pow;
if (count > this.MAX_COUNT) {
throw new Error('Max count exceeded. If this test uses a looping function, make sure the iteration loop is working properly.');
}
}
} catch (e) {
// Exceptions are caught and displayed in the test UI
this.reset();
this.error = e;
}
// Figure out what to do next
if (this.running) {
me.run(count);
} else {
jsl.status('');
me.onStop(me);
}
// Finish up
this.onChange(this);
},
/**
* Get the number of operations per second for this test.
*
* @param normalize if true, iteration loop overhead taken into account
*/
getHz: function(/**Boolean*/ normalize) {
var p = this.period;
// Adjust period based on the calibration test time
if (normalize && !this.isCalibration) {
var cal = Test.CALIBRATIONS[this.loopArg ? 0 : 1];
// If the period is within 20% of the calibration time, then zero the
// it out
p = p < cal.period*1.2 ? 0 : p - cal.period;
}
return Math.round(1/p);
},
/**
* Get a friendly string describing the test
*/
toString: function() {
return this.name + ' - ' + this.time/this.count + ' secs';
}
});
// CSS we need for the UI
var STYLESHEET = '<style> \
#jslitmus {font-family:sans-serif; font-size: 12px;} \
#jslitmus a {text-decoration: none;} \
#jslitmus a:hover {text-decoration: underline;} \
#jsl_status { \
margin-top: 10px; \
font-size: 10px; \
color: #888; \
} \
A IMG {border:none} \
#test_results { \
margin-top: 10px; \
font-size: 12px; \
font-family: sans-serif; \
border-collapse: collapse; \
border-spacing: 0px; \
} \
#test_results th, #test_results td { \
border: solid 1px #ccc; \
vertical-align: top; \
padding: 3px; \
} \
#test_results th { \
vertical-align: bottom; \
background-color: #ccc; \
padding: 1px; \
font-size: 10px; \
} \
#test_results #test_platform { \
color: #444; \
text-align:center; \
} \
#test_results .test_row { \
color: #006; \
cursor: pointer; \
} \
#test_results .test_nonlooping { \
border-left-style: dotted; \
border-left-width: 2px; \
} \
#test_results .test_looping { \
border-left-style: solid; \
border-left-width: 2px; \
} \
#test_results .test_name {white-space: nowrap;} \
#test_results .test_pending { \
} \
#test_results .test_running { \
font-style: italic; \
} \
#test_results .test_done {} \
#test_results .test_done { \
text-align: right; \
font-family: monospace; \
} \
#test_results .test_error {color: #600;} \
#test_results .test_error .error_head {font-weight:bold;} \
#test_results .test_error .error_body {font-size:85%;} \
#test_results .test_row:hover td { \
background-color: #ffc; \
text-decoration: underline; \
} \
#chart { \
margin: 10px 0px; \
width: 250px; \
} \
#chart img { \
border: solid 1px #ccc; \
margin-bottom: 5px; \
} \
#chart #tiny_url { \
height: 40px; \
width: 250px; \
} \
#jslitmus_credit { \
font-size: 10px; \
color: #888; \
margin-top: 8px; \
} \
</style>';
// HTML markup for the UI
var MARKUP = '<div id="jslitmus"> \
<button onclick="JSLitmus.runAll(event)">Run Tests</button> \
<button id="stop_button" disabled="disabled" onclick="JSLitmus.stop()">Stop Tests</button> \
<br \> \
<br \> \
<input type="checkbox" style="vertical-align: middle" id="test_normalize" checked="checked" onchange="JSLitmus.renderAll()""> Normalize results \
<table id="test_results"> \
<colgroup> \
<col /> \
<col width="100" /> \
</colgroup> \
<tr><th id="test_platform" colspan="2">' + platform + '</th></tr> \
<tr><th>Test</th><th>Ops/sec</th></tr> \
<tr id="test_row_template" class="test_row" style="display:none"> \
<td class="test_name"></td> \
<td class="test_result">Ready</td> \
</tr> \
</table> \
<div id="jsl_status"></div> \
<div id="chart" style="display:none"> \
<a id="chart_link" target="_blank"><img id="chart_image"></a> \
TinyURL (for chart): \
<iframe id="tiny_url" frameBorder="0" scrolling="no" src=""></iframe> \
</div> \
<a id="jslitmus_credit" title="JSLitmus home page" href="http://code.google.com/p/jslitmus" target="_blank">Powered by JSLitmus</a> \
</div>';
/**
* The public API for creating and running tests
*/
window.JSLitmus = {
/** The list of all tests that have been registered with JSLitmus.test */
_tests: [],
/** The queue of tests that need to be run */
_queue: [],
/**
* The parsed query parameters the current page URL. This is provided as a
* convenience for test functions - it's not used by JSLitmus proper
*/
params: {},
/**
* Initialize
*/
_init: function() {
// Parse query params into JSLitmus.params[] hash
var match = (location + '').match(/([^?#]*)(#.*)?$/);
if (match) {
var pairs = match[1].split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
if (pair.length > 1) {
var key = pair.shift();
var value = pair.length > 1 ? pair.join('=') : pair[0];
this.params[key] = value;
}
}
}
// Write out the stylesheet. We have to do this here because IE
// doesn't honor sheets written after the document has loaded.
document.write(STYLESHEET);
// Setup the rest of the UI once the document is loaded
if (window.addEventListener) {
window.addEventListener('load', this._setup, false);
} else if (document.addEventListener) {
document.addEventListener('load', this._setup, false);
} else if (window.attachEvent) {
window.attachEvent('onload', this._setup);
}
return this;
},
/**
* Set up the UI
*/
_setup: function() {
var el = jsl.$('jslitmus_container');
if (!el) document.body.appendChild(el = document.createElement('div'));
el.innerHTML = MARKUP;
// Render the UI for all our tests
for (var i=0; i < JSLitmus._tests.length; i++)
JSLitmus.renderTest(JSLitmus._tests[i]);
},
/**
* (Re)render all the test results
*/
renderAll: function() {
for (var i = 0; i < JSLitmus._tests.length; i++)
JSLitmus.renderTest(JSLitmus._tests[i]);
JSLitmus.renderChart();
},
/**
* (Re)render the chart graphics
*/
renderChart: function() {
var url = JSLitmus.chartUrl();
jsl.$('chart_link').href = url;
jsl.$('chart_image').src = url;
jsl.$('chart').style.display = '';
// Update the tiny URL
jsl.$('tiny_url').src = 'http://tinyurl.com/api-create.php?url='+escape(url);
},
/**
* (Re)render the results for a specific test
*/
renderTest: function(test) {
// Make a new row if needed
if (!test._row) {
var trow = jsl.$('test_row_template');
if (!trow) return;
test._row = trow.cloneNode(true);
test._row.style.display = '';
test._row.id = '';
test._row.onclick = function() {JSLitmus._queueTest(test);};
test._row.title = 'Run ' + test.name + ' test';
trow.parentNode.appendChild(test._row);
test._row.cells[0].innerHTML = test.name;
}
var cell = test._row.cells[1];
var cns = [test.loopArg ? 'test_looping' : 'test_nonlooping'];
if (test.error) {
cns.push('test_error');
cell.innerHTML =
'<div class="error_head">' + test.error + '</div>' +
'<ul class="error_body"><li>' +
jsl.join(test.error, ': ', '</li><li>') +
'</li></ul>';
} else {
if (test.running) {
cns.push('test_running');
cell.innerHTML = 'running';
} else if (jsl.indexOf(JSLitmus._queue, test) >= 0) {
cns.push('test_pending');
cell.innerHTML = 'pending';
} else if (test.count) {
cns.push('test_done');
var hz = test.getHz(jsl.$('test_normalize').checked);
cell.innerHTML = hz != Infinity ? hz : '&infin;';
cell.title = 'Looped ' + test.count + ' times in ' + test.time + ' seconds';
} else {
cell.innerHTML = 'ready';
}
}
cell.className = cns.join(' ');
},
/**
* Create a new test
*/
test: function(name, f) {
// Create the Test object
var test = new Test(name, f);
JSLitmus._tests.push(test);
// Re-render if the test state changes
test.onChange = JSLitmus.renderTest;
// Run the next test if this one finished
test.onStop = function(test) {
if (JSLitmus.onTestFinish) JSLitmus.onTestFinish(test);
JSLitmus.currentTest = null;
JSLitmus._nextTest();
};
// Render the new test
this.renderTest(test);
},
/**
* Add all tests to the run queue
*/
runAll: function(e) {
e = e || window.event;
var reverse = e && e.shiftKey, len = JSLitmus._tests.length;
for (var i = 0; i < len; i++) {
JSLitmus._queueTest(JSLitmus._tests[!reverse ? i : (len - i - 1)]);
}
},
/**
* Remove all tests from the run queue. The current test has to finish on
* it's own though
*/
stop: function() {
while (JSLitmus._queue.length) {
var test = JSLitmus._queue.shift();
JSLitmus.renderTest(test);
}
},
/**
* Run the next test in the run queue
*/
_nextTest: function() {
if (!JSLitmus.currentTest) {
var test = JSLitmus._queue.shift();
if (test) {
jsl.$('stop_button').disabled = false;
JSLitmus.currentTest = test;
test.run();
JSLitmus.renderTest(test);
if (JSLitmus.onTestStart) JSLitmus.onTestStart(test);
} else {
jsl.$('stop_button').disabled = true;
JSLitmus.renderChart();
}
}
},
/**
* Add a test to the run queue
*/
_queueTest: function(test) {
if (jsl.indexOf(JSLitmus._queue, test) >= 0) return;
JSLitmus._queue.push(test);
JSLitmus.renderTest(test);
JSLitmus._nextTest();
},
/**
* Generate a Google Chart URL that shows the data for all tests
*/
chartUrl: function() {
var n = JSLitmus._tests.length, markers = [], data = [];
var d, min = 0, max = -1e10;
var normalize = jsl.$('test_normalize').checked;
// Gather test data
for (var i=0; i < JSLitmus._tests.length; i++) {
var test = JSLitmus._tests[i];
if (test.count) {
var hz = test.getHz(normalize);
var v = hz != Infinity ? hz : 0;
data.push(v);
markers.push('t' + jsl.escape(test.name + '(' + jsl.toLabel(hz)+ ')') + ',000000,0,' +
markers.length + ',10');
max = Math.max(v, max);
}
}
if (markers.length <= 0) return null;
// Build chart title
var title = document.getElementsByTagName('title');
title = (title && title.length) ? title[0].innerHTML : null;
var chart_title = [];
if (title) chart_title.push(title);
chart_title.push('Ops/sec (' + platform + ')');
// Build labels
var labels = [jsl.toLabel(min), jsl.toLabel(max)];
var w = 250, bw = 15;
var bs = 5;
var h = markers.length*(bw + bs) + 30 + chart_title.length*20;
var params = {
chtt: escape(chart_title.join('|')),
chts: '000000,10',
cht: 'bhg', // chart type
chd: 't:' + data.join(','), // data set
chds: min + ',' + max, // max/min of data
chxt: 'x', // label axes
chxl: '0:|' + labels.join('|'), // labels
chsp: '0,1',
chm: markers.join('|'), // test names
chbh: [bw, 0, bs].join(','), // bar widths
// chf: 'bg,lg,0,eeeeee,0,eeeeee,.5,ffffff,1', // gradient
chs: w + 'x' + h
};
return 'http://chart.apis.google.com/chart?' + jsl.join(params, '=', '&');
}
};
JSLitmus._init();
})();

481
vendor/backbone/test/vendor/json2.js vendored Normal file
View File

@@ -0,0 +1,481 @@
/*
http://www.JSON.org/json2.js
2009-09-29
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
This file creates a global JSON object containing two methods: stringify
and parse.
JSON.stringify(value, replacer, space)
value any JavaScript value, usually an object or array.
replacer an optional parameter that determines how object
values are stringified for objects. It can be a
function or an array of strings.
space an optional parameter that specifies the indentation
of nested structures. If it is omitted, the text will
be packed without extra whitespace. If it is a number,
it will specify the number of spaces to indent at each
level. If it is a string (such as '\t' or '&nbsp;'),
it contains the characters used to indent at each level.
This method produces a JSON text from a JavaScript value.
When an object value is found, if the object contains a toJSON
method, its toJSON method will be called and the result will be
stringified. A toJSON method does not serialize: it returns the
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
bound to the value
For example, this would serialize Dates as ISO strings.
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
You can provide an optional replacer method. It will be passed the
key and value of each member, with this bound to the containing
object. The value that is returned from your method will be
serialized. If your method returns undefined, then the member will
be excluded from the serialization.
If the replacer parameter is an array of strings, then it will be
used to select the members to be serialized. It filters the results
such that only members with keys listed in the replacer array are
stringified.
Values that do not have JSON representations, such as undefined or
functions, will not be serialized. Such values in objects will be
dropped; in arrays they will be replaced with null. You can use
a replacer function to replace those with JSON values.
JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the
value that is filled with line breaks and indentation to make it
easier to read.
If the space parameter is a non-empty string, then that string will
be used for indentation. If the space parameter is a number, then
the indentation will be that many spaces.
Example:
text = JSON.stringify(['e', {pluribus: 'unum'}]);
// text is '["e",{"pluribus":"unum"}]'
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
text = JSON.stringify([new Date()], function (key, value) {
return this[key] instanceof Date ?
'Date(' + this[key] + ')' : value;
});
// text is '["Date(---current time---)"]'
JSON.parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = JSON.parse(text, function (key, value) {
var a;
if (typeof value === 'string') {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
var d;
if (typeof value === 'string' &&
value.slice(0, 5) === 'Date(' &&
value.slice(-1) === ')') {
d = new Date(value.slice(5, -1));
if (d) {
return d;
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
*/
/*jslint evil: true, strict: false */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
*/
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
if (!this.JSON) {
this.JSON = {};
}
(function () {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function (key) {
return isFinite(this.valueOf()) ?
this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z' : null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function (key) {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ?
'"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' :
'"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0 ? '[]' :
gap ? '[\n' + gap +
partial.join(',\n' + gap) + '\n' +
mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' :
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
mind + '}' : '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
}
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ?
walk({'': j}, '') : j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
};
}
}());

236
vendor/backbone/test/vendor/qunit.css vendored Executable file
View File

@@ -0,0 +1,236 @@
/**
* QUnit v1.8.0 - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
*
* Copyright (c) 2012 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* or GPL (GPL-LICENSE.txt) licenses.
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699a4;
background-color: #0d3349;
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
border-radius: 15px 15px 0 0;
-moz-border-radius: 15px 15px 0 0;
-webkit-border-top-right-radius: 15px;
-webkit-border-top-left-radius: 15px;
}
#qunit-header a {
text-decoration: none;
color: #c2ccd1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #fff;
}
#qunit-header label {
display: inline-block;
padding-left: 0.5em;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li a {
padding: 0.5em;
color: #c2ccd1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests ol {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
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 {
border-collapse: collapse;
margin-top: .2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 .5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #e0f2be;
color: #374e0c;
text-decoration: none;
}
#qunit-tests ins {
background-color: #ffcaca;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: black; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
margin: 0.5em;
padding: 0.4em 0.5em 0.4em 0.5em;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #5E740B;
background-color: #fff;
border-left: 26px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 26px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 15px 15px;
-moz-border-radius: 0 0 15px 15px;
-webkit-border-bottom-right-radius: 15px;
-webkit-border-bottom-left-radius: 15px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Result */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-bottom: 1px solid white;
}
#qunit-testresult .module-name {
font-weight: bold;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
width: 1000px;
height: 1000px;
}

1863
vendor/backbone/test/vendor/qunit.js vendored Executable file

File diff suppressed because it is too large Load Diff

230
vendor/backbone/test/view.js vendored Normal file
View File

@@ -0,0 +1,230 @@
$(document).ready(function() {
var view;
module("Backbone.View", {
setup: function() {
view = new Backbone.View({
id : 'test-view',
className : 'test-view'
});
}
});
test("View: constructor", 4, function() {
equal(view.el.id, 'test-view');
equal(view.el.className, 'test-view');
equal(view.options.id, 'test-view');
equal(view.options.className, 'test-view');
});
test("View: jQuery", 2, function() {
view.setElement(document.body);
ok(view.$('#qunit-header a').get(0).innerHTML.match(/Backbone Test Suite/));
ok(view.$('#qunit-header a').get(1).innerHTML.match(/Backbone Speed Suite/));
});
test("View: make", 3, function() {
var div = view.make('div', {id: 'test-div'}, "one two three");
equal(div.tagName.toLowerCase(), 'div');
equal(div.id, 'test-div');
equal($(div).text(), 'one two three');
});
test("View: make can take falsy values for content", 2, function() {
var div = view.make('div', {id: 'test-div'}, 0);
equal($(div).text(), '0');
var div = view.make('div', {id: 'test-div'}, '');
equal($(div).text(), '');
});
test("View: initialize", 1, function() {
var View = Backbone.View.extend({
initialize: function() {
this.one = 1;
}
});
var view = new View;
equal(view.one, 1);
});
test("View: delegateEvents", 6, function() {
var counter = 0;
var counter2 = 0;
view.setElement(document.body);
view.increment = function(){ counter++; };
view.$el.bind('click', function(){ counter2++; });
var events = {"click #qunit-banner": "increment"};
view.delegateEvents(events);
$('#qunit-banner').trigger('click');
equal(counter, 1);
equal(counter2, 1);
$('#qunit-banner').trigger('click');
equal(counter, 2);
equal(counter2, 2);
view.delegateEvents(events);
$('#qunit-banner').trigger('click');
equal(counter, 3);
equal(counter2, 3);
});
test("View: delegateEvents allows functions for callbacks", 3, function() {
view.counter = 0;
view.setElement("#qunit-banner");
var events = {"click": function() { this.counter++; }};
view.delegateEvents(events);
$('#qunit-banner').trigger('click');
equal(view.counter, 1);
$('#qunit-banner').trigger('click');
equal(view.counter, 2);
view.delegateEvents(events);
$('#qunit-banner').trigger('click');
equal(view.counter, 3);
});
test("View: undelegateEvents", 6, function() {
var counter = 0;
var counter2 = 0;
view.setElement(document.body);
view.increment = function(){ counter++; };
$(view.el).unbind('click');
$(view.el).bind('click', function(){ counter2++; });
var events = {"click #qunit-userAgent": "increment"};
view.delegateEvents(events);
$('#qunit-userAgent').trigger('click');
equal(counter, 1);
equal(counter2, 1);
view.undelegateEvents();
$('#qunit-userAgent').trigger('click');
equal(counter, 1);
equal(counter2, 2);
view.delegateEvents(events);
$('#qunit-userAgent').trigger('click');
equal(counter, 2);
equal(counter2, 3);
});
test("View: _ensureElement with DOM node el", 1, function() {
var ViewClass = Backbone.View.extend({
el: document.body
});
var view = new ViewClass;
equal(view.el, document.body);
});
test("View: _ensureElement with string el", 3, function() {
var ViewClass = Backbone.View.extend({
el: "body"
});
var view = new ViewClass;
strictEqual(view.el, document.body);
ViewClass = Backbone.View.extend({
el: "#testElement > h1"
});
view = new ViewClass;
strictEqual(view.el, $("#testElement > h1").get(0));
ViewClass = Backbone.View.extend({
el: "#nonexistent"
});
view = new ViewClass;
ok(!view.el);
});
test("View: with attributes", 2, function() {
var view = new Backbone.View({attributes : {'class': 'one', id: 'two'}});
equal(view.el.className, 'one');
equal(view.el.id, 'two');
});
test("View: with attributes as a function", 1, function() {
var viewClass = Backbone.View.extend({
attributes: function() {
return {'class': 'dynamic'};
}
});
equal((new viewClass).el.className, 'dynamic');
});
test("View: multiple views per element", 3, function() {
var count = 0, ViewClass = Backbone.View.extend({
el: $("body"),
events: {
"click": "click"
},
click: function() {
count++;
}
});
var view1 = new ViewClass;
$("body").trigger("click");
equal(1, count);
var view2 = new ViewClass;
$("body").trigger("click");
equal(3, count);
view1.delegateEvents();
$("body").trigger("click");
equal(5, count);
});
test("View: custom events, with namespaces", 2, function() {
var count = 0;
var ViewClass = Backbone.View.extend({
el: $('body'),
events: function() {
return {"fake$event.namespaced": "run"};
},
run: function() {
count++;
}
});
var view = new ViewClass;
$('body').trigger('fake$event').trigger('fake$event');
equal(count, 2);
$('body').unbind('.namespaced');
$('body').trigger('fake$event');
equal(count, 2);
});
test("#1048 - setElement uses provided object.", 2, function() {
var $el = $('body');
var view = new Backbone.View({el: $el});
ok(view.$el === $el);
view.setElement($el = $($el));
ok(view.$el === $el);
});
test("#986 - Undelegate before changing element.", 1, function() {
var a = $('<button></button>');
var b = $('<button></button>');
var View = Backbone.View.extend({
events: {click: function(e) { ok(view.el === e.target); }}
});
var view = new View({el: a});
view.setElement(b);
a.trigger('click');
b.trigger('click');
});
test("#1172 - Clone attributes object", 2, function() {
var View = Backbone.View.extend({attributes: {foo: 'bar'}});
var v1 = new View({id: 'foo'});
strictEqual(v1.el.id, 'foo');
var v2 = new View();
ok(!v2.el.id);
});
test("#1228 - tagName can be provided as a function", 1, function() {
var View = Backbone.View.extend({tagName: function(){ return 'p'; }});
ok(new View().$el.is('p'));
});
});