Update vendors.

Former-commit-id: 55a30b9cbfe7c513dd13d709aa4552fe715c262b
This commit is contained in:
John-David Dalton
2013-01-29 22:43:56 -08:00
parent 904921d8c4
commit 17dff36b65
13 changed files with 519 additions and 264 deletions

View File

@@ -1,4 +1,4 @@
Copyright (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Copyright (c) 2010-2013 Jeremy Ashkenas, DocumentCloud
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@@ -19,4 +19,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE. OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,6 +1,6 @@
// Backbone.js 0.9.10 // Backbone.js 0.9.10
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. // (c) 2010-2013 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license. // Backbone may be freely distributed under the MIT license.
// For all details and documentation: // For all details and documentation:
// http://backbonejs.org // http://backbonejs.org
@@ -89,15 +89,15 @@
// Optimized internal dispatch function for triggering events. Tries to // Optimized internal dispatch function for triggering events. Tries to
// keep the usual cases speedy (most Backbone events have 3 arguments). // keep the usual cases speedy (most Backbone events have 3 arguments).
var triggerEvents = function(events, args) { var triggerEvents = function(events, args) {
var ev, i = -1, l = events.length; var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
switch (args.length) { switch (args.length) {
case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx);
return; return;
case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0]); case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1);
return; return;
case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1]); case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2);
return; return;
case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1], args[2]); case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3);
return; return;
default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args);
} }
@@ -119,25 +119,24 @@
// to a `callback` function. Passing `"all"` will bind the callback to // to a `callback` function. Passing `"all"` will bind the callback to
// all events fired. // all events fired.
on: function(name, callback, context) { on: function(name, callback, context) {
if (!(eventsApi(this, 'on', name, [callback, context]) && callback)) return this; if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
this._events || (this._events = {}); this._events || (this._events = {});
var list = this._events[name] || (this._events[name] = []); var events = this._events[name] || (this._events[name] = []);
list.push({callback: callback, context: context, ctx: context || this}); events.push({callback: callback, context: context, ctx: context || this});
return this; return this;
}, },
// Bind events to only be triggered a single time. After the first time // Bind events to only be triggered a single time. After the first time
// the callback is invoked, it will be removed. // the callback is invoked, it will be removed.
once: function(name, callback, context) { once: function(name, callback, context) {
if (!(eventsApi(this, 'once', name, [callback, context]) && callback)) return this; if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
var self = this; var self = this;
var once = _.once(function() { var once = _.once(function() {
self.off(name, once); self.off(name, once);
callback.apply(this, arguments); callback.apply(this, arguments);
}); });
once._callback = callback; once._callback = callback;
this.on(name, once, context); return this.on(name, once, context);
return this;
}, },
// Remove one or many callbacks. If `context` is null, removes all // Remove one or many callbacks. If `context` is null, removes all
@@ -145,7 +144,7 @@
// callbacks for the event. If `name` is null, removes all bound // callbacks for the event. If `name` is null, removes all bound
// callbacks for all events. // callbacks for all events.
off: function(name, callback, context) { off: function(name, callback, context) {
var list, ev, events, names, i, l, j, k; var retain, ev, events, names, i, l, j, k;
if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
if (!name && !callback && !context) { if (!name && !callback && !context) {
this._events = {}; this._events = {};
@@ -155,19 +154,19 @@
names = name ? [name] : _.keys(this._events); names = name ? [name] : _.keys(this._events);
for (i = 0, l = names.length; i < l; i++) { for (i = 0, l = names.length; i < l; i++) {
name = names[i]; name = names[i];
if (list = this._events[name]) { if (events = this._events[name]) {
events = []; this._events[name] = retain = [];
if (callback || context) { if (callback || context) {
for (j = 0, k = list.length; j < k; j++) { for (j = 0, k = events.length; j < k; j++) {
ev = list[j]; ev = events[j];
if ((callback && callback !== ev.callback && if ((callback && callback !== ev.callback &&
callback !== ev.callback._callback) || callback !== ev.callback._callback) ||
(context && context !== ev.context)) { (context && context !== ev.context)) {
events.push(ev); retain.push(ev);
} }
} }
} }
this._events[name] = events; if (!retain.length) delete this._events[name];
} }
} }
@@ -189,21 +188,11 @@
return this; return this;
}, },
// An inversion-of-control version of `on`. Tell *this* object to listen to
// an event in another object ... keeping track of what it's listening to.
listenTo: function(obj, name, callback) {
var listeners = this._listeners || (this._listeners = {});
var id = obj._listenerId || (obj._listenerId = _.uniqueId('l'));
listeners[id] = obj;
obj.on(name, typeof name === 'object' ? this : callback, this);
return this;
},
// Tell this object to stop listening to either specific events ... or // Tell this object to stop listening to either specific events ... or
// to every object it's currently listening to. // to every object it's currently listening to.
stopListening: function(obj, name, callback) { stopListening: function(obj, name, callback) {
var listeners = this._listeners; var listeners = this._listeners;
if (!listeners) return; if (!listeners) return this;
if (obj) { if (obj) {
obj.off(name, typeof name === 'object' ? this : callback, this); obj.off(name, typeof name === 'object' ? this : callback, this);
if (!name && !callback) delete listeners[obj._listenerId]; if (!name && !callback) delete listeners[obj._listenerId];
@@ -218,6 +207,20 @@
} }
}; };
var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
// An inversion-of-control versions of `on` and `once`. Tell *this* object to listen to
// an event in another object ... keeping track of what it's listening to.
_.each(listenMethods, function(implementation, method) {
Events[method] = function(obj, name, callback) {
var listeners = this._listeners || (this._listeners = {});
var id = obj._listenerId || (obj._listenerId = _.uniqueId('l'));
listeners[id] = obj;
obj[implementation](name, typeof name === 'object' ? this : callback, this);
return this;
};
});
// Aliases for backwards compatibility. // Aliases for backwards compatibility.
Events.bind = Events.on; Events.bind = Events.on;
Events.unbind = Events.off; Events.unbind = Events.off;
@@ -252,6 +255,9 @@
// A hash of attributes whose current and previous value differ. // A hash of attributes whose current and previous value differ.
changed: null, changed: null,
// The value returned during the last failed validation.
validationError: null,
// The default name for the JSON `id` attribute is `"id"`. MongoDB and // The default name for the JSON `id` attribute is `"id"`. MongoDB and
// CouchDB users may want to set this to `"_id"`. // CouchDB users may want to set this to `"_id"`.
idAttribute: 'id', idAttribute: 'id',
@@ -1299,7 +1305,7 @@
// This only works for delegate-able events: not `focus`, `blur`, and // This only works for delegate-able events: not `focus`, `blur`, and
// not `change`, `submit`, and `reset` in Internet Explorer. // not `change`, `submit`, and `reset` in Internet Explorer.
delegateEvents: function(events) { delegateEvents: function(events) {
if (!(events || (events = _.result(this, 'events')))) return; if (!(events || (events = _.result(this, 'events')))) return this;
this.undelegateEvents(); this.undelegateEvents();
for (var key in events) { for (var key in events) {
var method = events[key]; var method = events[key];
@@ -1315,6 +1321,7 @@
this.$el.on(eventName, selector, method); this.$el.on(eventName, selector, method);
} }
} }
return this;
}, },
// Clears all callbacks previously bound to the view with `delegateEvents`. // Clears all callbacks previously bound to the view with `delegateEvents`.
@@ -1322,6 +1329,7 @@
// Backbone views attached to the same DOM element. // Backbone views attached to the same DOM element.
undelegateEvents: function() { undelegateEvents: function() {
this.$el.off('.delegateEvents' + this.cid); this.$el.off('.delegateEvents' + this.cid);
return this;
}, },
// Performs the initial configuration of a View with a set of options. // Performs the initial configuration of a View with a set of options.

View File

@@ -101,6 +101,38 @@ $(document).ready(function() {
b.trigger('event event2'); b.trigger('event event2');
}); });
test("listenToOnce and stopListening", 1, function() {
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenToOnce(b, 'all', function() { ok(true); });
b.trigger('anything');
b.trigger('anything');
a.listenToOnce(b, 'all', function() { ok(false); });
a.stopListening();
b.trigger('anything');
});
test("listenTo, listenToOnce and stopListening", 1, function() {
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenToOnce(b, 'all', function() { ok(true); });
b.trigger('anything');
b.trigger('anything');
a.listenTo(b, 'all', function() { ok(false); });
a.stopListening();
b.trigger('anything');
});
test("listenTo and stopListening with event maps", 1, function() {
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenTo(b, {change: function(){ ok(true); }});
b.trigger('change');
a.listenTo(b, {change: function(){ ok(false); }});
a.stopListening();
b.trigger('change');
});
test("listenTo yourself", 1, function(){ test("listenTo yourself", 1, function(){
var e = _.extend({}, Backbone.Events); var e = _.extend({}, Backbone.Events);
e.listenTo(e, "foo", function(){ ok(true); }); e.listenTo(e, "foo", function(){ ok(true); });
@@ -259,18 +291,6 @@ $(document).ready(function() {
obj.trigger('x y'); 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() { test("#1310 - off does not skip consecutive events", 0, function() {
var obj = _.extend({}, Backbone.Events); var obj = _.extend({}, Backbone.Events);
obj.on('event', function() { ok(false); }, obj); obj.on('event', function() { ok(false); }, obj);
@@ -400,4 +420,21 @@ $(document).ready(function() {
_.extend({}, Backbone.Events).once('event').trigger('event'); _.extend({}, Backbone.Events).once('event').trigger('event');
}); });
test("event functions are chainable", function() {
var obj = _.extend({}, Backbone.Events);
var obj2 = _.extend({}, Backbone.Events);
var fn = function() {};
equal(obj, obj.trigger('noeventssetyet'));
equal(obj, obj.off('noeventssetyet'));
equal(obj, obj.stopListening('noeventssetyet'));
equal(obj, obj.on('a', fn));
equal(obj, obj.once('c', fn));
equal(obj, obj.trigger('a'));
equal(obj, obj.listenTo(obj2, 'a', fn));
equal(obj, obj.listenToOnce(obj2, 'b', fn));
equal(obj, obj.off('a c'));
equal(obj, obj.stopListening(obj2, 'a'));
equal(obj, obj.stopListening());
});
}); });

View File

@@ -23,9 +23,6 @@ If you are interested in helping developing QUnit, you are in the right place.
For related discussions, visit the For related discussions, visit the
[QUnit and Testing forum](http://forum.jquery.com/qunit-and-testing). [QUnit and Testing forum](http://forum.jquery.com/qunit-and-testing).
Planning for a qunitjs.com site and other testing tools related work now happens
on the [jQuery Testing Team planning wiki](http://jquerytesting.pbworks.com/w/page/41556026/FrontPage).
Development Development
----------- -----------
@@ -49,11 +46,17 @@ tag, update them again to the next version, commit and push commits and tags
Put the 'v' in front of the tag, e.g. `v1.8.0`. Clean up the changelog, removing merge commits Put the 'v' in front of the tag, e.g. `v1.8.0`. Clean up the changelog, removing merge commits
or whitespace cleanups. or whitespace cleanups.
To upload to code.jquery.com (replace $version accordingly): To upload to code.jquery.com (replace $version accordingly), ssh to code.origin.jquery.com:
scp -q qunit/qunit.js jqadmin@code.origin.jquery.com:/var/www/html/code.jquery.com/qunit/qunit-$version.js cp qunit/qunit.js /var/www/html/code.jquery.com/qunit/qunit-$version.js
scp -q qunit/qunit.css jqadmin@code.origin.jquery.com:/var/www/html/code.jquery.com/qunit/qunit-$version.css cp qunit/qunit.css /var/www/html/code.jquery.com/qunit/qunit-$version.css
Then update /var/www/html/code.jquery.com/index.html and purge it with: Then update /var/www/html/code.jquery.com/index.html and purge it with:
curl -s http://code.origin.jquery.com/?reload curl -s http://code.origin.jquery.com/?reload
Update web-base-template to link to those files for qunitjs.com.
Publish to npm via
npm publish

View File

@@ -1,5 +1,5 @@
/** /**
* QUnit v1.10.0 - A JavaScript Unit Testing Framework * QUnit v1.11.0 - A JavaScript Unit Testing Framework
* *
* http://qunitjs.com * http://qunitjs.com
* *
@@ -20,7 +20,7 @@
/** Resets */ /** Resets */
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
@@ -111,7 +111,12 @@
color: #000; color: #000;
} }
#qunit-tests ol { #qunit-tests li .runtime {
float: right;
font-size: smaller;
}
.qunit-assert-list {
margin-top: 0.5em; margin-top: 0.5em;
padding: 0.5em; padding: 0.5em;
@@ -122,6 +127,10 @@
-webkit-border-radius: 5px; -webkit-border-radius: 5px;
} }
.qunit-collapsed {
display: none;
}
#qunit-tests table { #qunit-tests table {
border-collapse: collapse; border-collapse: collapse;
margin-top: .2em; margin-top: .2em;

View File

@@ -1,5 +1,5 @@
/** /**
* QUnit v1.10.0 - A JavaScript Unit Testing Framework * QUnit v1.11.0 - A JavaScript Unit Testing Framework
* *
* http://qunitjs.com * http://qunitjs.com
* *
@@ -11,6 +11,7 @@
(function( window ) { (function( window ) {
var QUnit, var QUnit,
assert,
config, config,
onErrorFnPrev, onErrorFnPrev,
testId = 0, testId = 0,
@@ -20,18 +21,67 @@ var QUnit,
// Keep a local reference to Date (GH-283) // Keep a local reference to Date (GH-283)
Date = window.Date, Date = window.Date,
defined = { defined = {
setTimeout: typeof window.setTimeout !== "undefined", setTimeout: typeof window.setTimeout !== "undefined",
sessionStorage: (function() { sessionStorage: (function() {
var x = "qunit-test-string"; var x = "qunit-test-string";
try { try {
sessionStorage.setItem( x, x ); sessionStorage.setItem( x, x );
sessionStorage.removeItem( x ); sessionStorage.removeItem( x );
return true; return true;
} catch( e ) { } catch( e ) {
return false; return false;
}
}())
},
/**
* Provides a normalized error string, correcting an issue
* with IE 7 (and prior) where Error.prototype.toString is
* not properly implemented
*
* Based on http://es5.github.com/#x15.11.4.4
*
* @param {String|Error} error
* @return {String} error message
*/
errorString = function( error ) {
var name, message,
errorString = error.toString();
if ( errorString.substring( 0, 7 ) === "[object" ) {
name = error.name ? error.name.toString() : "Error";
message = error.message ? error.message.toString() : "";
if ( name && message ) {
return name + ": " + message;
} else if ( name ) {
return name;
} else if ( message ) {
return message;
} else {
return "Error";
}
} else {
return errorString;
} }
}()) },
}; /**
* Makes a clone of an object using only Array or Object as base,
* and copies over the own enumerable properties.
*
* @param {Object} obj
* @return {Object} New object with only the own properties (recursively).
*/
objectValues = function( obj ) {
// Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.
/*jshint newcap: false */
var key, val,
vals = QUnit.is( "array", obj ) ? [] : {};
for ( key in obj ) {
if ( hasOwn.call( obj, key ) ) {
val = obj[key];
vals[key] = val === Object(val) ? objectValues(val) : val;
}
}
return vals;
};
function Test( settings ) { function Test( settings ) {
extend( this, settings ); extend( this, settings );
@@ -44,11 +94,11 @@ Test.count = 0;
Test.prototype = { Test.prototype = {
init: function() { init: function() {
var a, b, li, var a, b, li,
tests = id( "qunit-tests" ); tests = id( "qunit-tests" );
if ( tests ) { if ( tests ) {
b = document.createElement( "strong" ); b = document.createElement( "strong" );
b.innerHTML = this.name; b.innerHTML = this.nameHtml;
// `a` initialized at top of scope // `a` initialized at top of scope
a = document.createElement( "a" ); a = document.createElement( "a" );
@@ -92,6 +142,7 @@ Test.prototype = {
teardown: function() {} teardown: function() {}
}, this.moduleTestEnvironment ); }, this.moduleTestEnvironment );
this.started = +new Date();
runLoggingCallbacks( "testStart", QUnit, { runLoggingCallbacks( "testStart", QUnit, {
name: this.testName, name: this.testName,
module: this.module module: this.module
@@ -111,7 +162,7 @@ Test.prototype = {
try { try {
this.testEnvironment.setup.call( this.testEnvironment ); this.testEnvironment.setup.call( this.testEnvironment );
} catch( e ) { } catch( e ) {
QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) ); QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
} }
}, },
run: function() { run: function() {
@@ -120,22 +171,28 @@ Test.prototype = {
var running = id( "qunit-testresult" ); var running = id( "qunit-testresult" );
if ( running ) { if ( running ) {
running.innerHTML = "Running: <br/>" + this.name; running.innerHTML = "Running: <br/>" + this.nameHtml;
} }
if ( this.async ) { if ( this.async ) {
QUnit.stop(); QUnit.stop();
} }
this.callbackStarted = +new Date();
if ( config.notrycatch ) { if ( config.notrycatch ) {
this.callback.call( this.testEnvironment, QUnit.assert ); this.callback.call( this.testEnvironment, QUnit.assert );
this.callbackRuntime = +new Date() - this.callbackStarted;
return; return;
} }
try { try {
this.callback.call( this.testEnvironment, QUnit.assert ); this.callback.call( this.testEnvironment, QUnit.assert );
this.callbackRuntime = +new Date() - this.callbackStarted;
} catch( e ) { } catch( e ) {
QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace( e, 0 ) ); this.callbackRuntime = +new Date() - this.callbackStarted;
QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
// else next test will carry the responsibility // else next test will carry the responsibility
saveGlobal(); saveGlobal();
@@ -148,38 +205,43 @@ Test.prototype = {
teardown: function() { teardown: function() {
config.current = this; config.current = this;
if ( config.notrycatch ) { if ( config.notrycatch ) {
if ( typeof this.callbackRuntime === "undefined" ) {
this.callbackRuntime = +new Date() - this.callbackStarted;
}
this.testEnvironment.teardown.call( this.testEnvironment ); this.testEnvironment.teardown.call( this.testEnvironment );
return; return;
} else { } else {
try { try {
this.testEnvironment.teardown.call( this.testEnvironment ); this.testEnvironment.teardown.call( this.testEnvironment );
} catch( e ) { } catch( e ) {
QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) ); QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
} }
} }
checkPollution(); checkPollution();
}, },
finish: function() { finish: function() {
config.current = this; config.current = this;
if ( config.requireExpects && this.expected == null ) { if ( config.requireExpects && this.expected === null ) {
QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack ); QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
} else if ( this.expected != null && this.expected != this.assertions.length ) { } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
} else if ( this.expected == null && !this.assertions.length ) { } else if ( this.expected === null && !this.assertions.length ) {
QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack ); QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
} }
var assertion, a, b, i, li, ol, var i, assertion, a, b, time, li, ol,
test = this, test = this,
good = 0, good = 0,
bad = 0, bad = 0,
tests = id( "qunit-tests" ); tests = id( "qunit-tests" );
this.runtime = +new Date() - this.started;
config.stats.all += this.assertions.length; config.stats.all += this.assertions.length;
config.moduleStats.all += this.assertions.length; config.moduleStats.all += this.assertions.length;
if ( tests ) { if ( tests ) {
ol = document.createElement( "ol" ); ol = document.createElement( "ol" );
ol.className = "qunit-assert-list";
for ( i = 0; i < this.assertions.length; i++ ) { for ( i = 0; i < this.assertions.length; i++ ) {
assertion = this.assertions[i]; assertion = this.assertions[i];
@@ -208,22 +270,22 @@ Test.prototype = {
} }
if ( bad === 0 ) { if ( bad === 0 ) {
ol.style.display = "none"; addClass( ol, "qunit-collapsed" );
} }
// `b` initialized at top of scope // `b` initialized at top of scope
b = document.createElement( "strong" ); b = document.createElement( "strong" );
b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>"; b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
addEvent(b, "click", function() { addEvent(b, "click", function() {
var next = b.nextSibling.nextSibling, var next = b.parentNode.lastChild,
display = next.style.display; collapsed = hasClass( next, "qunit-collapsed" );
next.style.display = display === "none" ? "block" : "none"; ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );
}); });
addEvent(b, "dblclick", function( e ) { addEvent(b, "dblclick", function( e ) {
var target = e && e.target ? e.target : window.event.srcElement; var target = e && e.target ? e.target : window.event.srcElement;
if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
target = target.parentNode; target = target.parentNode;
} }
if ( window.location && target.nodeName.toLowerCase() === "strong" ) { if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
@@ -231,13 +293,19 @@ Test.prototype = {
} }
}); });
// `time` initialized at top of scope
time = document.createElement( "span" );
time.className = "runtime";
time.innerHTML = this.runtime + " ms";
// `li` initialized at top of scope // `li` initialized at top of scope
li = id( this.id ); li = id( this.id );
li.className = bad ? "fail" : "pass"; li.className = bad ? "fail" : "pass";
li.removeChild( li.firstChild ); li.removeChild( li.firstChild );
a = li.firstChild; a = li.firstChild;
li.appendChild( b ); li.appendChild( b );
li.appendChild ( a ); li.appendChild( a );
li.appendChild( time );
li.appendChild( ol ); li.appendChild( ol );
} else { } else {
@@ -255,7 +323,8 @@ Test.prototype = {
module: this.module, module: this.module,
failed: bad, failed: bad,
passed: this.assertions.length - bad, passed: this.assertions.length - bad,
total: this.assertions.length total: this.assertions.length,
duration: this.runtime
}); });
QUnit.reset(); QUnit.reset();
@@ -321,7 +390,7 @@ QUnit = {
test: function( testName, expected, callback, async ) { test: function( testName, expected, callback, async ) {
var test, var test,
name = "<span class='test-name'>" + escapeInnerText( testName ) + "</span>"; nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>";
if ( arguments.length === 2 ) { if ( arguments.length === 2 ) {
callback = expected; callback = expected;
@@ -329,11 +398,11 @@ QUnit = {
} }
if ( config.currentModule ) { if ( config.currentModule ) {
name = "<span class='module-name'>" + config.currentModule + "</span>: " + name; nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml;
} }
test = new Test({ test = new Test({
name: name, nameHtml: nameHtml,
testName: testName, testName: testName,
expected: expected, expected: expected,
async: async, async: async,
@@ -360,6 +429,18 @@ QUnit = {
}, },
start: function( count ) { start: function( count ) {
// QUnit hasn't been initialized yet.
// Note: RequireJS (et al) may delay onLoad
if ( config.semaphore === undefined ) {
QUnit.begin(function() {
// This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
setTimeout(function() {
QUnit.start( count );
});
});
return;
}
config.semaphore -= count || 1; config.semaphore -= count || 1;
// don't start until equal number of stop-calls // don't start until equal number of stop-calls
if ( config.semaphore > 0 ) { if ( config.semaphore > 0 ) {
@@ -368,6 +449,8 @@ QUnit = {
// ignore if start is called more often then stop // ignore if start is called more often then stop
if ( config.semaphore < 0 ) { if ( config.semaphore < 0 ) {
config.semaphore = 0; config.semaphore = 0;
QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );
return;
} }
// A slight delay, to avoid any current callbacks // A slight delay, to avoid any current callbacks
if ( defined.setTimeout ) { if ( defined.setTimeout ) {
@@ -403,11 +486,14 @@ QUnit = {
} }
}; };
// `assert` initialized at top of scope
// Asssert helpers // Asssert helpers
// All of these must call either QUnit.push() or manually do: // All of these must either call QUnit.push() or manually do:
// - runLoggingCallbacks( "log", .. ); // - runLoggingCallbacks( "log", .. );
// - config.current.assertions.push({ .. }); // - config.current.assertions.push({ .. });
QUnit.assert = { // We attach it to the QUnit object *after* we expose the public API,
// otherwise `assert` will become a global variable in browsers (#341).
assert = {
/** /**
* Asserts rough true-ish result. * Asserts rough true-ish result.
* @name ok * @name ok
@@ -428,14 +514,14 @@ QUnit.assert = {
message: msg message: msg
}; };
msg = escapeInnerText( msg || (result ? "okay" : "failed" ) ); msg = escapeText( msg || (result ? "okay" : "failed" ) );
msg = "<span class='test-message'>" + msg + "</span>"; msg = "<span class='test-message'>" + msg + "</span>";
if ( !result ) { if ( !result ) {
source = sourceFromStacktrace( 2 ); source = sourceFromStacktrace( 2 );
if ( source ) { if ( source ) {
details.source = source; details.source = source;
msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr></table>"; msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr></table>";
} }
} }
runLoggingCallbacks( "log", QUnit, details ); runLoggingCallbacks( "log", QUnit, details );
@@ -453,6 +539,7 @@ QUnit.assert = {
* @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" ); * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
*/ */
equal: function( actual, expected, message ) { equal: function( actual, expected, message ) {
/*jshint eqeqeq:false */
QUnit.push( expected == actual, actual, expected, message ); QUnit.push( expected == actual, actual, expected, message );
}, },
@@ -461,9 +548,30 @@ QUnit.assert = {
* @function * @function
*/ */
notEqual: function( actual, expected, message ) { notEqual: function( actual, expected, message ) {
/*jshint eqeqeq:false */
QUnit.push( expected != actual, actual, expected, message ); QUnit.push( expected != actual, actual, expected, message );
}, },
/**
* @name propEqual
* @function
*/
propEqual: function( actual, expected, message ) {
actual = objectValues(actual);
expected = objectValues(expected);
QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
},
/**
* @name notPropEqual
* @function
*/
notPropEqual: function( actual, expected, message ) {
actual = objectValues(actual);
expected = objectValues(expected);
QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
},
/** /**
* @name deepEqual * @name deepEqual
* @function * @function
@@ -496,8 +604,9 @@ QUnit.assert = {
QUnit.push( expected !== actual, actual, expected, message ); QUnit.push( expected !== actual, actual, expected, message );
}, },
throws: function( block, expected, message ) { "throws": function( block, expected, message ) {
var actual, var actual,
expectedOutput = expected,
ok = false; ok = false;
// 'expected' is optional // 'expected' is optional
@@ -518,18 +627,20 @@ QUnit.assert = {
// we don't want to validate thrown error // we don't want to validate thrown error
if ( !expected ) { if ( !expected ) {
ok = true; ok = true;
expectedOutput = null;
// expected is a regexp // expected is a regexp
} else if ( QUnit.objectType( expected ) === "regexp" ) { } else if ( QUnit.objectType( expected ) === "regexp" ) {
ok = expected.test( actual ); ok = expected.test( errorString( actual ) );
// expected is a constructor // expected is a constructor
} else if ( actual instanceof expected ) { } else if ( actual instanceof expected ) {
ok = true; ok = true;
// expected is a validation function which returns true is validation passed // expected is a validation function which returns true is validation passed
} else if ( expected.call( {}, actual ) === true ) { } else if ( expected.call( {}, actual ) === true ) {
expectedOutput = null;
ok = true; ok = true;
} }
QUnit.push( ok, actual, null, message ); QUnit.push( ok, actual, expectedOutput, message );
} else { } else {
QUnit.pushFailure( message, null, 'No exception was thrown.' ); QUnit.pushFailure( message, null, 'No exception was thrown.' );
} }
@@ -538,15 +649,16 @@ QUnit.assert = {
/** /**
* @deprecate since 1.8.0 * @deprecate since 1.8.0
* Kept assertion helpers in root for backwards compatibility * Kept assertion helpers in root for backwards compatibility.
*/ */
extend( QUnit, QUnit.assert ); extend( QUnit, assert );
/** /**
* @deprecated since 1.9.0 * @deprecated since 1.9.0
* Kept global "raises()" for backwards compatibility * Kept root "raises()" for backwards compatibility.
* (Note that we don't introduce assert.raises).
*/ */
QUnit.raises = QUnit.assert.throws; QUnit.raises = assert[ "throws" ];
/** /**
* @deprecated since 1.0.0, replaced with error pushes since 1.3.0 * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
@@ -622,6 +734,15 @@ config = {
moduleDone: [] moduleDone: []
}; };
// Export global variables, unless an 'exports' object exists,
// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
if ( typeof exports === "undefined" ) {
extend( window, QUnit );
// Expose QUnit object
window.QUnit = QUnit;
}
// Initialize more QUnit.config and QUnit.urlParams // Initialize more QUnit.config and QUnit.urlParams
(function() { (function() {
var i, var i,
@@ -655,18 +776,11 @@ config = {
QUnit.isLocal = location.protocol === "file:"; QUnit.isLocal = location.protocol === "file:";
}()); }());
// Export global variables, unless an 'exports' object exists,
// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
if ( typeof exports === "undefined" ) {
extend( window, QUnit );
// Expose QUnit object
window.QUnit = QUnit;
}
// Extend QUnit object, // Extend QUnit object,
// these after set here because they should not be exposed as global functions // these after set here because they should not be exposed as global functions
extend( QUnit, { extend( QUnit, {
assert: assert,
config: config, config: config,
// Initialize the configuration options // Initialize the configuration options
@@ -681,7 +795,7 @@ extend( QUnit, {
autorun: false, autorun: false,
filter: "", filter: "",
queue: [], queue: [],
semaphore: 0 semaphore: 1
}); });
var tests, banner, result, var tests, banner, result,
@@ -689,7 +803,7 @@ extend( QUnit, {
if ( qunit ) { if ( qunit ) {
qunit.innerHTML = qunit.innerHTML =
"<h1 id='qunit-header'>" + escapeInnerText( document.title ) + "</h1>" + "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
"<h2 id='qunit-banner'></h2>" + "<h2 id='qunit-banner'></h2>" +
"<div id='qunit-testrunner-toolbar'></div>" + "<div id='qunit-testrunner-toolbar'></div>" +
"<h2 id='qunit-userAgent'></h2>" + "<h2 id='qunit-userAgent'></h2>" +
@@ -745,7 +859,7 @@ extend( QUnit, {
// Safe object type checking // Safe object type checking
is: function( type, obj ) { is: function( type, obj ) {
return QUnit.objectType( obj ) == type; return QUnit.objectType( obj ) === type;
}, },
objectType: function( obj ) { objectType: function( obj ) {
@@ -757,7 +871,8 @@ extend( QUnit, {
return "null"; return "null";
} }
var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || ""; var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),
type = match && match[1] || "";
switch ( type ) { switch ( type ) {
case "Number": case "Number":
@@ -794,16 +909,16 @@ extend( QUnit, {
expected: expected expected: expected
}; };
message = escapeInnerText( message ) || ( result ? "okay" : "failed" ); message = escapeText( message ) || ( result ? "okay" : "failed" );
message = "<span class='test-message'>" + message + "</span>"; message = "<span class='test-message'>" + message + "</span>";
output = message; output = message;
if ( !result ) { if ( !result ) {
expected = escapeInnerText( QUnit.jsDump.parse(expected) ); expected = escapeText( QUnit.jsDump.parse(expected) );
actual = escapeInnerText( QUnit.jsDump.parse(actual) ); actual = escapeText( QUnit.jsDump.parse(actual) );
output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>"; output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
if ( actual != expected ) { if ( actual !== expected ) {
output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>"; output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>"; output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
} }
@@ -812,7 +927,7 @@ extend( QUnit, {
if ( source ) { if ( source ) {
details.source = source; details.source = source;
output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>"; output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
} }
output += "</table>"; output += "</table>";
@@ -839,19 +954,19 @@ extend( QUnit, {
message: message message: message
}; };
message = escapeInnerText( message ) || "error"; message = escapeText( message ) || "error";
message = "<span class='test-message'>" + message + "</span>"; message = "<span class='test-message'>" + message + "</span>";
output = message; output = message;
output += "<table>"; output += "<table>";
if ( actual ) { if ( actual ) {
output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeInnerText( actual ) + "</pre></td></tr>"; output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>";
} }
if ( source ) { if ( source ) {
details.source = source; details.source = source;
output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>"; output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
} }
output += "</table>"; output += "</table>";
@@ -876,7 +991,8 @@ extend( QUnit, {
querystring += encodeURIComponent( key ) + "=" + querystring += encodeURIComponent( key ) + "=" +
encodeURIComponent( params[ key ] ) + "&"; encodeURIComponent( params[ key ] ) + "&";
} }
return window.location.pathname + querystring.slice( 0, -1 ); return window.location.protocol + "//" + window.location.host +
window.location.pathname + querystring.slice( 0, -1 );
}, },
extend: extend, extend: extend,
@@ -907,7 +1023,7 @@ extend( QUnit.constructor.prototype, {
// testStart: { name } // testStart: { name }
testStart: registerLoggingCallback( "testStart" ), testStart: registerLoggingCallback( "testStart" ),
// testDone: { name, failed, passed, total } // testDone: { name, failed, passed, total, duration }
testDone: registerLoggingCallback( "testDone" ), testDone: registerLoggingCallback( "testDone" ),
// moduleStart: { name } // moduleStart: { name }
@@ -925,9 +1041,10 @@ QUnit.load = function() {
runLoggingCallbacks( "begin", QUnit, {} ); runLoggingCallbacks( "begin", QUnit, {} );
// Initialize the config, saving the execution queue // Initialize the config, saving the execution queue
var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxes, moduleFilter, var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,
numModules = 0, urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter,
moduleFilterHtml = "", numModules = 0,
moduleFilterHtml = "",
urlConfigHtml = "", urlConfigHtml = "",
oldconfig = extend( {}, config ); oldconfig = extend( {}, config );
@@ -948,14 +1065,24 @@ QUnit.load = function() {
}; };
} }
config[ val.id ] = QUnit.urlParams[ val.id ]; config[ val.id ] = QUnit.urlParams[ val.id ];
urlConfigHtml += "<input id='qunit-urlconfig-" + val.id + "' name='" + val.id + "' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) + " title='" + val.tooltip + "'><label for='qunit-urlconfig-" + val.id + "' title='" + val.tooltip + "'>" + val.label + "</label>"; urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) +
"' name='" + escapeText( val.id ) +
"' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) +
" title='" + escapeText( val.tooltip ) +
"'><label for='qunit-urlconfig-" + escapeText( val.id ) +
"' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>";
} }
moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " + ( config.module === undefined ? "selected" : "" ) + ">< All Modules ></option>"; moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " +
( config.module === undefined ? "selected='selected'" : "" ) +
">< All Modules ></option>";
for ( i in config.modules ) { for ( i in config.modules ) {
if ( config.modules.hasOwnProperty( i ) ) { if ( config.modules.hasOwnProperty( i ) ) {
numModules += 1; numModules += 1;
moduleFilterHtml += "<option value='" + encodeURIComponent(i) + "' " + ( config.module === i ? "selected" : "" ) + ">" + i + "</option>"; moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(i) ) + "' " +
( config.module === i ? "selected='selected'" : "" ) +
">" + escapeText(i) + "</option>";
} }
} }
moduleFilterHtml += "</select>"; moduleFilterHtml += "</select>";
@@ -1014,22 +1141,28 @@ QUnit.load = function() {
label.innerHTML = "Hide passed tests"; label.innerHTML = "Hide passed tests";
toolbar.appendChild( label ); toolbar.appendChild( label );
urlConfigCheckboxes = document.createElement( 'span' ); urlConfigCheckboxesContainer = document.createElement("span");
urlConfigCheckboxes.innerHTML = urlConfigHtml; urlConfigCheckboxesContainer.innerHTML = urlConfigHtml;
addEvent( urlConfigCheckboxes, "change", function( event ) { urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input");
var params = {}; // For oldIE support:
params[ event.target.name ] = event.target.checked ? true : undefined; // * Add handlers to the individual elements instead of the container
// * Use "click" instead of "change"
// * Fallback from event.target to event.srcElement
addEvents( urlConfigCheckboxes, "click", function( event ) {
var params = {},
target = event.target || event.srcElement;
params[ target.name ] = target.checked ? true : undefined;
window.location = QUnit.url( params ); window.location = QUnit.url( params );
}); });
toolbar.appendChild( urlConfigCheckboxes ); toolbar.appendChild( urlConfigCheckboxesContainer );
if (numModules > 1) { if (numModules > 1) {
moduleFilter = document.createElement( 'span' ); moduleFilter = document.createElement( 'span' );
moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' ); moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' );
moduleFilter.innerHTML = moduleFilterHtml; moduleFilter.innerHTML = moduleFilterHtml;
addEvent( moduleFilter, "change", function() { addEvent( moduleFilter.lastChild, "change", function() {
var selectBox = moduleFilter.getElementsByTagName("select")[0], var selectBox = moduleFilter.getElementsByTagName("select")[0],
selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value); selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } ); window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } );
}); });
@@ -1106,7 +1239,7 @@ function done() {
" milliseconds.<br/>", " milliseconds.<br/>",
"<span class='passed'>", "<span class='passed'>",
passed, passed,
"</span> tests of <span class='total'>", "</span> assertions of <span class='total'>",
config.stats.all, config.stats.all,
"</span> passed, <span class='failed'>", "</span> passed, <span class='failed'>",
config.stats.bad, config.stats.bad,
@@ -1199,7 +1332,7 @@ function validTest( test ) {
function extractStacktrace( e, offset ) { function extractStacktrace( e, offset ) {
offset = offset === undefined ? 3 : offset; offset = offset === undefined ? 3 : offset;
var stack, include, i, regex; var stack, include, i;
if ( e.stacktrace ) { if ( e.stacktrace ) {
// Opera // Opera
@@ -1213,7 +1346,7 @@ function extractStacktrace( e, offset ) {
if ( fileName ) { if ( fileName ) {
include = []; include = [];
for ( i = offset; i < stack.length; i++ ) { for ( i = offset; i < stack.length; i++ ) {
if ( stack[ i ].indexOf( fileName ) != -1 ) { if ( stack[ i ].indexOf( fileName ) !== -1 ) {
break; break;
} }
include.push( stack[ i ] ); include.push( stack[ i ] );
@@ -1242,17 +1375,27 @@ function sourceFromStacktrace( offset ) {
} }
} }
function escapeInnerText( s ) { /**
* Escape text for attribute or text content.
*/
function escapeText( s ) {
if ( !s ) { if ( !s ) {
return ""; return "";
} }
s = s + ""; s = s + "";
return s.replace( /[\&<>]/g, function( s ) { // Both single quotes and double quotes (for attributes)
return s.replace( /['"<>&]/g, function( s ) {
switch( s ) { switch( s ) {
case "&": return "&amp;"; case '\'':
case "<": return "&lt;"; return '&#039;';
case ">": return "&gt;"; case '"':
default: return s; return '&quot;';
case '<':
return '&lt;';
case '>':
return '&gt;';
case '&':
return '&amp;';
} }
}); });
} }
@@ -1300,7 +1443,7 @@ function saveGlobal() {
} }
} }
function checkPollution( name ) { function checkPollution() {
var newGlobals, var newGlobals,
deletedGlobals, deletedGlobals,
old = config.pollution; old = config.pollution;
@@ -1349,16 +1492,53 @@ function extend( a, b ) {
return a; return a;
} }
/**
* @param {HTMLElement} elem
* @param {string} type
* @param {Function} fn
*/
function addEvent( elem, type, fn ) { function addEvent( elem, type, fn ) {
// Standards-based browsers
if ( elem.addEventListener ) { if ( elem.addEventListener ) {
elem.addEventListener( type, fn, false ); elem.addEventListener( type, fn, false );
} else if ( elem.attachEvent ) { // IE
elem.attachEvent( "on" + type, fn );
} else { } else {
fn(); elem.attachEvent( "on" + type, fn );
} }
} }
/**
* @param {Array|NodeList} elems
* @param {string} type
* @param {Function} fn
*/
function addEvents( elems, type, fn ) {
var i = elems.length;
while ( i-- ) {
addEvent( elems[i], type, fn );
}
}
function hasClass( elem, name ) {
return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;
}
function addClass( elem, name ) {
if ( !hasClass( elem, name ) ) {
elem.className += (elem.className ? " " : "") + name;
}
}
function removeClass( elem, name ) {
var set = " " + elem.className + " ";
// Class name may appear multiple times
while ( set.indexOf(" " + name + " ") > -1 ) {
set = set.replace(" " + name + " " , " ");
}
// If possible, trim it for prettiness, but not neccecarily
elem.className = window.jQuery ? jQuery.trim( set ) : ( set.trim ? set.trim() : set );
}
function id( name ) { function id( name ) {
return !!( typeof document !== "undefined" && document && document.getElementById ) && return !!( typeof document !== "undefined" && document && document.getElementById ) &&
document.getElementById( name ); document.getElementById( name );
@@ -1372,7 +1552,6 @@ function registerLoggingCallback( key ) {
// Supports deprecated method of completely overwriting logging callbacks // Supports deprecated method of completely overwriting logging callbacks
function runLoggingCallbacks( key, scope, args ) { function runLoggingCallbacks( key, scope, args ) {
//debugger;
var i, callbacks; var i, callbacks;
if ( QUnit.hasOwnProperty( key ) ) { if ( QUnit.hasOwnProperty( key ) ) {
QUnit[ key ].call(scope, args ); QUnit[ key ].call(scope, args );
@@ -1414,6 +1593,7 @@ QUnit.equiv = (function() {
// for string, boolean, number and null // for string, boolean, number and null
function useStrictEquality( b, a ) { function useStrictEquality( b, a ) {
/*jshint eqeqeq:false */
if ( b instanceof a.constructor || a instanceof b.constructor ) { if ( b instanceof a.constructor || a instanceof b.constructor ) {
// to catch short annotaion VS 'new' annotation of a // to catch short annotaion VS 'new' annotation of a
// declaration // declaration
@@ -1610,7 +1790,8 @@ QUnit.jsDump = (function() {
var reName = /^function (\w+)/, var reName = /^function (\w+)/,
jsDump = { jsDump = {
parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance // type is used mostly internally, you can fix a (custom)type in advance
parse: function( obj, type, stack ) {
stack = stack || [ ]; stack = stack || [ ];
var inStack, res, var inStack, res,
parser = this.parsers[ type || this.typeOf(obj) ]; parser = this.parsers[ type || this.typeOf(obj) ];
@@ -1618,18 +1799,16 @@ QUnit.jsDump = (function() {
type = typeof parser; type = typeof parser;
inStack = inArray( obj, stack ); inStack = inArray( obj, stack );
if ( inStack != -1 ) { if ( inStack !== -1 ) {
return "recursion(" + (inStack - stack.length) + ")"; return "recursion(" + (inStack - stack.length) + ")";
} }
//else if ( type === "function" ) {
if ( type == "function" ) {
stack.push( obj ); stack.push( obj );
res = parser.call( this, obj, stack ); res = parser.call( this, obj, stack );
stack.pop(); stack.pop();
return res; return res;
} }
// else return ( type === "string" ) ? parser : this.parsers.error;
return ( type == "string" ) ? parser : this.parsers.error;
}, },
typeOf: function( obj ) { typeOf: function( obj ) {
var type; var type;
@@ -1656,6 +1835,8 @@ QUnit.jsDump = (function() {
( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
) { ) {
type = "array"; type = "array";
} else if ( obj.constructor === Error.prototype.constructor ) {
type = "error";
} else { } else {
type = typeof obj; type = typeof obj;
} }
@@ -1664,7 +1845,8 @@ QUnit.jsDump = (function() {
separator: function() { separator: function() {
return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " "; return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
}, },
indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing // extra can be a number, shortcut for increasing-calling-decreasing
indent: function( extra ) {
if ( !this.multiline ) { if ( !this.multiline ) {
return ""; return "";
} }
@@ -1693,13 +1875,16 @@ QUnit.jsDump = (function() {
parsers: { parsers: {
window: "[Window]", window: "[Window]",
document: "[Document]", document: "[Document]",
error: "[ERROR]", //when no parser is found, shouldn"t happen error: function(error) {
return "Error(\"" + error.message + "\")";
},
unknown: "[Unknown]", unknown: "[Unknown]",
"null": "null", "null": "null",
"undefined": "undefined", "undefined": "undefined",
"function": function( fn ) { "function": function( fn ) {
var ret = "function", var ret = "function",
name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE // functions never have name in IE
name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
if ( name ) { if ( name ) {
ret += " " + name; ret += " " + name;
@@ -1715,13 +1900,9 @@ QUnit.jsDump = (function() {
object: function( map, stack ) { object: function( map, stack ) {
var ret = [ ], keys, key, val, i; var ret = [ ], keys, key, val, i;
QUnit.jsDump.up(); QUnit.jsDump.up();
if ( Object.keys ) { keys = [];
keys = Object.keys( map ); for ( key in map ) {
} else { keys.push( key );
keys = [];
for ( key in map ) {
keys.push( key );
}
} }
keys.sort(); keys.sort();
for ( i = 0; i < keys.length; i++ ) { for ( i = 0; i < keys.length; i++ ) {
@@ -1733,21 +1914,34 @@ QUnit.jsDump = (function() {
return join( "{", ret, "}" ); return join( "{", ret, "}" );
}, },
node: function( node ) { node: function( node ) {
var a, val, var len, i, val,
open = QUnit.jsDump.HTML ? "&lt;" : "<", open = QUnit.jsDump.HTML ? "&lt;" : "<",
close = QUnit.jsDump.HTML ? "&gt;" : ">", close = QUnit.jsDump.HTML ? "&gt;" : ">",
tag = node.nodeName.toLowerCase(), tag = node.nodeName.toLowerCase(),
ret = open + tag; ret = open + tag,
attrs = node.attributes;
for ( a in QUnit.jsDump.DOMAttrs ) { if ( attrs ) {
val = node[ QUnit.jsDump.DOMAttrs[a] ]; for ( i = 0, len = attrs.length; i < len; i++ ) {
if ( val ) { val = attrs[i].nodeValue;
ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" ); // IE6 includes all attributes in .attributes, even ones not explicitly set.
// Those have values like undefined, null, 0, false, "" or "inherit".
if ( val && val !== "inherit" ) {
ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );
}
} }
} }
return ret + close + open + "/" + tag + close; ret += close;
// Show content of TextNode or CDATASection
if ( node.nodeType === 3 || node.nodeType === 4 ) {
ret += node.nodeValue;
}
return ret + open + "/" + tag + close;
}, },
functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function // function calls it internally, it's the arguments part of the function
functionArgs: function( fn ) {
var args, var args,
l = fn.length; l = fn.length;
@@ -1757,54 +1951,34 @@ QUnit.jsDump = (function() {
args = new Array(l); args = new Array(l);
while ( l-- ) { while ( l-- ) {
args[l] = String.fromCharCode(97+l);//97 is 'a' // 97 is 'a'
args[l] = String.fromCharCode(97+l);
} }
return " " + args.join( ", " ) + " "; return " " + args.join( ", " ) + " ";
}, },
key: quote, //object calls it internally, the key part of an item in a map // object calls it internally, the key part of an item in a map
functionCode: "[code]", //function calls it internally, it's the content of the function key: quote,
attribute: quote, //node calls it internally, it's an html attribute value // function calls it internally, it's the content of the function
functionCode: "[code]",
// node calls it internally, it's an html attribute value
attribute: quote,
string: quote, string: quote,
date: quote, date: quote,
regexp: literal, //regex regexp: literal,
number: literal, number: literal,
"boolean": literal "boolean": literal
}, },
DOMAttrs: { // if true, entities are escaped ( <, >, \t, space and \n )
//attributes to dump from nodes, name=>realName HTML: false,
id: "id", // indentation unit
name: "name", indentChar: " ",
"class": "className" // if true, items in a collection, are separated by a \n, else just a space.
}, multiline: true
HTML: false,//if true, entities are escaped ( <, >, \t, space and \n )
indentChar: " ",//indentation unit
multiline: true //if true, items in a collection, are separated by a \n, else just a space.
}; };
return jsDump; return jsDump;
}()); }());
// from Sizzle.js
function getText( elems ) {
var i, elem,
ret = "";
for ( i = 0; elems[i]; i++ ) {
elem = elems[i];
// Get the text from text nodes and CDATA nodes
if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
ret += elem.nodeValue;
// Traverse everything else, except comment nodes
} else if ( elem.nodeType !== 8 ) {
ret += getText( elem.childNodes );
}
}
return ret;
}
// from jquery.js // from jquery.js
function inArray( elem, array ) { function inArray( elem, array ) {
if ( array.indexOf ) { if ( array.indexOf ) {
@@ -1835,13 +2009,14 @@ function inArray( elem, array ) {
* QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over" * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
*/ */
QUnit.diff = (function() { QUnit.diff = (function() {
/*jshint eqeqeq:false, eqnull:true */
function diff( o, n ) { function diff( o, n ) {
var i, var i,
ns = {}, ns = {},
os = {}; os = {};
for ( i = 0; i < n.length; i++ ) { for ( i = 0; i < n.length; i++ ) {
if ( ns[ n[i] ] == null ) { if ( !hasOwn.call( ns, n[i] ) ) {
ns[ n[i] ] = { ns[ n[i] ] = {
rows: [], rows: [],
o: null o: null
@@ -1851,7 +2026,7 @@ QUnit.diff = (function() {
} }
for ( i = 0; i < o.length; i++ ) { for ( i = 0; i < o.length; i++ ) {
if ( os[ o[i] ] == null ) { if ( !hasOwn.call( os, o[i] ) ) {
os[ o[i] ] = { os[ o[i] ] = {
rows: [], rows: [],
n: null n: null
@@ -1864,7 +2039,7 @@ QUnit.diff = (function() {
if ( !hasOwn.call( ns, i ) ) { if ( !hasOwn.call( ns, i ) ) {
continue; continue;
} }
if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && os[i].rows.length == 1 ) { if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
n[ ns[i].rows[0] ] = { n[ ns[i].rows[0] ] = {
text: n[ ns[i].rows[0] ], text: n[ ns[i].rows[0] ],
row: os[i].rows[0] row: os[i].rows[0]
@@ -1970,7 +2145,7 @@ QUnit.diff = (function() {
// for CommonJS enviroments, export everything // for CommonJS enviroments, export everything
if ( typeof exports !== "undefined" ) { if ( typeof exports !== "undefined" ) {
extend(exports, QUnit); extend( exports, QUnit );
} }
// get at whatever the global object is, like window in browsers // get at whatever the global object is, like window in browsers

View File

@@ -1,18 +1,18 @@
/** vim: et:ts=4:sw=4:sts=4 /** vim: et:ts=4:sw=4:sts=4
* @license RequireJS 2.1.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. * @license RequireJS 2.1.4 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license. * Available via the MIT or new BSD license.
* see: http://github.com/jrburke/requirejs for details * see: http://github.com/jrburke/requirejs for details
*/ */
//Not using strict: uneven strict support in browsers, #392, and causes //Not using strict: uneven strict support in browsers, #392, and causes
//problems with requirejs.exec()/transpiler plugins that may not be strict. //problems with requirejs.exec()/transpiler plugins that may not be strict.
/*jslint regexp: true, nomen: true, sloppy: true */ /*jslint regexp: true, nomen: true, sloppy: true */
/*global window, navigator, document, importScripts, jQuery, setTimeout, opera */ /*global window, navigator, document, importScripts, setTimeout, opera */
var requirejs, require, define; var requirejs, require, define;
(function (global) { (function (global) {
var req, s, head, baseElement, dataMain, src, var req, s, head, baseElement, dataMain, src,
interactiveScript, currentlyAddingScript, mainScript, subPath, interactiveScript, currentlyAddingScript, mainScript, subPath,
version = '2.1.2', version = '2.1.4',
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/, jsSuffixRegExp = /\.js$/,
@@ -21,7 +21,6 @@ var requirejs, require, define;
ostring = op.toString, ostring = op.toString,
hasOwn = op.hasOwnProperty, hasOwn = op.hasOwnProperty,
ap = Array.prototype, ap = Array.prototype,
aps = ap.slice,
apsp = ap.splice, apsp = ap.splice,
isBrowser = !!(typeof window !== 'undefined' && navigator && document), isBrowser = !!(typeof window !== 'undefined' && navigator && document),
isWebWorker = !isBrowser && typeof importScripts !== 'undefined', isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
@@ -918,8 +917,7 @@ var requirejs, require, define;
name = this.map.name, name = this.map.name,
parentName = this.map.parentMap ? this.map.parentMap.name : null, parentName = this.map.parentMap ? this.map.parentMap.name : null,
localRequire = context.makeRequire(map.parentMap, { localRequire = context.makeRequire(map.parentMap, {
enableBuildCallback: true, enableBuildCallback: true
skipMap: true
}); });
//If current map is not normalized, wait for that //If current map is not normalized, wait for that
@@ -1017,8 +1015,11 @@ var requirejs, require, define;
try { try {
req.exec(text); req.exec(text);
} catch (e) { } catch (e) {
throw new Error('fromText eval for ' + moduleName + return onError(makeError('fromtexteval',
' failed: ' + e); 'fromText eval for ' + id +
' failed: ' + e,
e,
[id]));
} }
if (hasInteractive) { if (hasInteractive) {
@@ -1395,16 +1396,21 @@ var requirejs, require, define;
* plain URLs like nameToUrl. * plain URLs like nameToUrl.
*/ */
toUrl: function (moduleNamePlusExt) { toUrl: function (moduleNamePlusExt) {
var index = moduleNamePlusExt.lastIndexOf('.'), var ext, url,
ext = null; index = moduleNamePlusExt.lastIndexOf('.'),
segment = moduleNamePlusExt.split('/')[0],
isRelative = segment === '.' || segment === '..';
if (index !== -1) { //Have a file extension alias, and it is not the
//dots from a relative path.
if (index !== -1 && (!isRelative || index > 1)) {
ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
moduleNamePlusExt = moduleNamePlusExt.substring(0, index); moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
} }
return context.nameToUrl(normalize(moduleNamePlusExt, url = context.nameToUrl(normalize(moduleNamePlusExt,
relMap && relMap.id, true), ext); relMap && relMap.id, true), ext || '.fake');
return ext ? url : url.substring(0, url.length - 5);
}, },
defined: function (id) { defined: function (id) {
@@ -1449,10 +1455,11 @@ var requirejs, require, define;
/** /**
* Called to enable a module if it is still in the registry * Called to enable a module if it is still in the registry
* awaiting enablement. parent module is passed in for context, * awaiting enablement. A second arg, parent, the parent module,
* used by the optimizer. * is passed in for context, when this method is overriden by
* the optimizer. Not shown here to keep code compact.
*/ */
enable: function (depMap, parent) { enable: function (depMap) {
var mod = getOwn(registry, depMap.id); var mod = getOwn(registry, depMap.id);
if (mod) { if (mod) {
getModule(depMap).enable(); getModule(depMap).enable();

View File

@@ -1,4 +1,4 @@
Copyright (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Copyright (c) 2009-2013 Jeremy Ashkenas, DocumentCloud
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@@ -19,4 +19,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE. OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -260,6 +260,14 @@ $(document).ready(function() {
equal(result[0].a, 1); equal(result[0].a, 1);
}); });
test('findWhere', function() {
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}];
var result = _.findWhere(list, {a: 1});
deepEqual(result, {a: 1, b: 2});
result = _.findWhere(list, {b: 4});
deepEqual(result, {a: 1, b: 4});
});
test('max', function() { test('max', function() {
equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max');

View File

@@ -18,10 +18,10 @@ $(document).ready(function() {
func = _.bind(func, this, 'hello'); func = _.bind(func, this, 'hello');
equal(func('moe'), 'hello: moe', 'the function was partially applied in advance'); equal(func('moe'), 'hello: moe', 'the function was partially applied in advance');
var func = _.bind(func, this, 'curly'); func = _.bind(func, this, 'curly');
equal(func(), 'hello: curly', 'the function was completely applied in advance'); equal(func(), 'hello: curly', 'the function was completely applied in advance');
var func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; }; func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; };
func = _.bind(func, this, 'hello', 'moe', 'curly'); func = _.bind(func, this, 'hello', 'moe', 'curly');
equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
@@ -34,10 +34,15 @@ $(document).ready(function() {
// To test this with a modern browser, set underscore's nativeBind to undefined // To test this with a modern browser, set underscore's nativeBind to undefined
var F = function () { return this; }; var F = function () { return this; };
var Boundf = _.bind(F, {hello: "moe curly"}); var Boundf = _.bind(F, {hello: "moe curly"});
var newBoundf = new Boundf();
equal(newBoundf.hello, undefined, "function should not be bound to the context, to comply with ECMAScript 5");
equal(Boundf().hello, "moe curly", "When called without the new operator, it's OK to be bound to the context"); equal(Boundf().hello, "moe curly", "When called without the new operator, it's OK to be bound to the context");
ok(newBoundf instanceof F, "a bound instance is an instance of the original function"); });
test("partial", function() {
var obj = {name: 'moe'};
var func = function() { return this.name + ' ' + _.toArray(arguments).join(' '); };
obj.func = _.partial(func, 'a', 'b');
equal(obj.func('c', 'd'), 'moe a b c d', 'can partially apply');
}); });
test("bindAll", function() { test("bindAll", function() {

View File

@@ -207,7 +207,7 @@ $(document).ready(function() {
strictEqual(_.result(obj, 'x'), 'x'); strictEqual(_.result(obj, 'x'), 'x');
strictEqual(_.result(obj, 'y'), 'x'); strictEqual(_.result(obj, 'y'), 'x');
strictEqual(_.result(obj, 'z'), undefined); strictEqual(_.result(obj, 'z'), undefined);
strictEqual(_.result(null, 'x'), null); strictEqual(_.result(null, 'x'), undefined);
}); });
test('_.templateSettings.variable', function() { test('_.templateSettings.variable', function() {

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
// Underscore.js 1.4.3 // Underscore.js 1.4.4
// http://underscorejs.org // http://underscorejs.org
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore may be freely distributed under the MIT license. // Underscore may be freely distributed under the MIT license.
(function() { (function() {
@@ -64,7 +64,7 @@
} }
// Current version. // Current version.
_.VERSION = '1.4.3'; _.VERSION = '1.4.4';
// Collection Functions // Collection Functions
// -------------------- // --------------------
@@ -236,10 +236,10 @@
}; };
// Convenience version of a common use case of `filter`: selecting only objects // Convenience version of a common use case of `filter`: selecting only objects
// with specific `key:value` pairs. // containing specific `key:value` pairs.
_.where = function(obj, attrs) { _.where = function(obj, attrs, first) {
if (_.isEmpty(attrs)) return []; if (_.isEmpty(attrs)) return first ? void 0 : [];
return _.filter(obj, function(value) { return _[first ? 'find' : 'filter'](obj, function(value) {
for (var key in attrs) { for (var key in attrs) {
if (attrs[key] !== value[key]) return false; if (attrs[key] !== value[key]) return false;
} }
@@ -247,6 +247,12 @@
}); });
}; };
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
return _.where(obj, attrs, true);
};
// Return the maximum element or (element-based computation). // Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements. // Can't optimize arrays of integers longer than 65,535 elements.
// See: https://bugs.webkit.org/show_bug.cgi?id=80797 // See: https://bugs.webkit.org/show_bug.cgi?id=80797
@@ -568,26 +574,23 @@
// Function (ahem) Functions // Function (ahem) Functions
// ------------------ // ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments, // Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Binding with arguments is also known as `curry`. // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// Delegates to **ECMAScript 5**'s native `Function.bind` if available. // available.
// We check for `func.bind` first, to fail fast when `func` is undefined.
_.bind = function(func, context) { _.bind = function(func, context) {
var args, bound;
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError; var args = slice.call(arguments, 2);
args = slice.call(arguments, 2); return function() {
return bound = function() { return func.apply(context, args.concat(slice.call(arguments)));
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); };
ctor.prototype = func.prototype; };
var self = new ctor;
ctor.prototype = null; // Partially apply a function by creating a version that has had some of its
var result = func.apply(self, args.concat(slice.call(arguments))); // arguments pre-filled, without changing its dynamic `this` context.
if (Object(result) === result) return result; _.partial = function(func) {
return self; var args = slice.call(arguments, 1);
return function() {
return func.apply(this, args.concat(slice.call(arguments)));
}; };
}; };
@@ -1055,7 +1058,7 @@
// If the value of the named property is a function then invoke it; // If the value of the named property is a function then invoke it;
// otherwise, return it. // otherwise, return it.
_.result = function(object, property) { _.result = function(object, property) {
if (object == null) return null; if (object == null) return void 0;
var value = object[property]; var value = object[property];
return _.isFunction(value) ? value.call(object) : value; return _.isFunction(value) ? value.call(object) : value;
}; };