Commit d2245d57 authored by Kostantinos  Margaritis's avatar Kostantinos Margaritis Committed by Sindre Sorhus

Close #1144 PR: React-backbone: New ui & upgrade to NPM. Fixes #1110

parent 1de82da0
node_modules/backbone/*
!node_modules/backbone/backbone.js
node_modules/backbone.localstorage/*
!node_modules/backbone.localstorage/backbone.localStorage.js
node_modules/jquery/*
!node_modules/jquery/dist
node_modules/jquery/dist/*
!node_modules/jquery/dist/jquery.js
node_modules/react/*
!node_modules/react/dist
node_modules/react/dist/*
!node_modules/react/dist/react-with-addons.js
!node_modules/react/dist/JSXTransformer.js
node_modules/todomvc-app-css/*
!node_modules/todomvc-app-css/index.css
node_modules/todomvc-common/*
!node_modules/todomvc-common/base.css
!node_modules/todomvc-common/base.js
node_modules/underscore/*
!node_modules/underscore/underscore.js
{
"name": "todomvc-react-backbone",
"version": "0.0.0",
"dependencies": {
"react": "~0.10.0",
"backbone": "~1.1.2",
"backbone.localstorage": "~1.1.7",
"jquery": "~2.1.0",
"todomvc-common": "~0.3.0",
"underscore": "~1.6.0"
}
}
...@@ -3,8 +3,9 @@ ...@@ -3,8 +3,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>React + Backbone • TodoMVC</title> <title>React _ Backbone • TodoMVC</title>
<link rel="stylesheet" href="bower_components/todomvc-common/base.css"> <link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
</head> </head>
<body> <body>
<section id="todoapp"></section> <section id="todoapp"></section>
...@@ -14,13 +15,13 @@ ...@@ -14,13 +15,13 @@
<p>Part of<a href="http://todomvc.com">TodoMVC</a></p> <p>Part of<a href="http://todomvc.com">TodoMVC</a></p>
</footer> </footer>
<script src="bower_components/todomvc-common/base.js"></script> <script src="node_modules/todomvc-common/base.js"></script>
<script src="bower_components/react/react-with-addons.js"></script> <script src="node_modules/react/dist/react-with-addons.js"></script>
<script src="bower_components/react/JSXTransformer.js"></script> <script src="node_modules/react/dist/JSXTransformer.js"></script>
<script src="bower_components/jquery/dist/jquery.js"></script> <script src="node_modules/jquery/dist/jquery.js"></script>
<script src="bower_components/underscore/underscore.js"></script> <script src="node_modules/underscore/underscore.js"></script>
<script src="bower_components/backbone/backbone.js"></script> <script src="node_modules/backbone/backbone.js"></script>
<script src="bower_components/backbone.localstorage/backbone.localStorage.js"></script> <script src="node_modules/backbone.localstorage/backbone.localStorage.js"></script>
<script src="js/todo.js"></script> <script src="js/todo.js"></script>
<script src="js/todos.js"></script> <script src="js/todos.js"></script>
......
/** /**
* Backbone localStorage Adapter * Backbone localStorage Adapter
* Version 1.1.7 * Version 1.1.16
* *
* https://github.com/jeromegn/Backbone.localStorage * https://github.com/jeromegn/Backbone.localStorage
*/ */
(function (root, factory) { (function (root, factory) {
if (typeof exports === 'object' && typeof require === 'function') { if (typeof exports === 'object' && typeof require === 'function') {
module.exports = factory(require("underscore"), require("backbone")); module.exports = factory(require("backbone"));
} else if (typeof define === "function" && define.amd) { } else if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module. // AMD. Register as an anonymous module.
define(["underscore","backbone"], function(_, Backbone) { define(["backbone"], function(Backbone) {
// Use global variables if the locals are undefined. // Use global variables if the locals are undefined.
return factory(_ || root._, Backbone || root.Backbone); return factory(Backbone || root.Backbone);
}); });
} else { } else {
// RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags factory(Backbone);
factory(_, Backbone); }
} }(this, function(Backbone) {
}(this, function(_, Backbone) {
// A simple module to replace `Backbone.sync` with *localStorage*-based // A simple module to replace `Backbone.sync` with *localStorage*-based
// persistence. Models are given GUIDS, and saved into a JSON object. Simple // persistence. Models are given GUIDS, and saved into a JSON object. Simple
// as that. // as that.
// Hold reference to Underscore.js and Backbone.js in the closure in order
// to make things work even if they are removed from the global namespace
// Generate four random hex digits. // Generate four random hex digits.
function S4() { function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1); return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
...@@ -35,19 +31,49 @@ function guid() { ...@@ -35,19 +31,49 @@ function guid() {
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}; };
function isObject(item) {
return item === Object(item);
}
function contains(array, item) {
var i = array.length;
while (i--) if (array[i] === item) return true;
return false;
}
function extend(obj, props) {
for (var key in props) obj[key] = props[key]
return obj;
}
function result(object, property) {
if (object == null) return void 0;
var value = object[property];
return (typeof value === 'function') ? object[property]() : value;
}
// Our Store is represented by a single JS object in *localStorage*. Create it // Our Store is represented by a single JS object in *localStorage*. Create it
// with a meaningful name, like the name you'd give a table. // with a meaningful name, like the name you'd give a table.
// window.Store is deprectated, use Backbone.LocalStorage instead // window.Store is deprectated, use Backbone.LocalStorage instead
Backbone.LocalStorage = window.Store = function(name) { Backbone.LocalStorage = window.Store = function(name, serializer) {
if( !this.localStorage ) { if( !this.localStorage ) {
throw "Backbone.localStorage: Environment does not support localStorage." throw "Backbone.localStorage: Environment does not support localStorage."
} }
this.name = name; this.name = name;
this.serializer = serializer || {
serialize: function(item) {
return isObject(item) ? JSON.stringify(item) : item;
},
// fix for "illegal access" error on Android when JSON.parse is passed null
deserialize: function (data) {
return data && JSON.parse(data);
}
};
var store = this.localStorage().getItem(this.name); var store = this.localStorage().getItem(this.name);
this.records = (store && store.split(",")) || []; this.records = (store && store.split(",")) || [];
}; };
_.extend(Backbone.LocalStorage.prototype, { extend(Backbone.LocalStorage.prototype, {
// Save the current state of the **Store** to *localStorage*. // Save the current state of the **Store** to *localStorage*.
save: function() { save: function() {
...@@ -57,11 +83,11 @@ _.extend(Backbone.LocalStorage.prototype, { ...@@ -57,11 +83,11 @@ _.extend(Backbone.LocalStorage.prototype, {
// Add a model, giving it a (hopefully)-unique GUID, if it doesn't already // Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
// have an id of it's own. // have an id of it's own.
create: function(model) { create: function(model) {
if (!model.id) { if (!model.id && model.id !== 0) {
model.id = guid(); model.id = guid();
model.set(model.idAttribute, model.id); model.set(model.idAttribute, model.id);
} }
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model)); this.localStorage().setItem(this._itemName(model.id), this.serializer.serialize(model));
this.records.push(model.id.toString()); this.records.push(model.id.toString());
this.save(); this.save();
return this.find(model); return this.find(model);
...@@ -69,36 +95,40 @@ _.extend(Backbone.LocalStorage.prototype, { ...@@ -69,36 +95,40 @@ _.extend(Backbone.LocalStorage.prototype, {
// Update a model by replacing its copy in `this.data`. // Update a model by replacing its copy in `this.data`.
update: function(model) { update: function(model) {
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model)); this.localStorage().setItem(this._itemName(model.id), this.serializer.serialize(model));
if (!_.include(this.records, model.id.toString())) var modelId = model.id.toString();
this.records.push(model.id.toString()); this.save(); if (!contains(this.records, modelId)) {
this.records.push(modelId);
this.save();
}
return this.find(model); return this.find(model);
}, },
// Retrieve a model from `this.data` by id. // Retrieve a model from `this.data` by id.
find: function(model) { find: function(model) {
return this.jsonData(this.localStorage().getItem(this.name+"-"+model.id)); return this.serializer.deserialize(this.localStorage().getItem(this._itemName(model.id)));
}, },
// Return the array of all models currently in storage. // Return the array of all models currently in storage.
findAll: function() { findAll: function() {
// Lodash removed _#chain in v1.0.0-rc.1 var result = [];
return (_.chain || _)(this.records) for (var i = 0, id, data; i < this.records.length; i++) {
.map(function(id){ id = this.records[i];
return this.jsonData(this.localStorage().getItem(this.name+"-"+id)); data = this.serializer.deserialize(this.localStorage().getItem(this._itemName(id)));
}, this) if (data != null) result.push(data);
.compact() }
.value(); return result;
}, },
// Delete a model from `this.data`, returning it. // Delete a model from `this.data`, returning it.
destroy: function(model) { destroy: function(model) {
if (model.isNew()) this.localStorage().removeItem(this._itemName(model.id));
return false var modelId = model.id.toString();
this.localStorage().removeItem(this.name+"-"+model.id); for (var i = 0, id; i < this.records.length; i++) {
this.records = _.reject(this.records, function(id){ if (this.records[i] === modelId) {
return id === model.id.toString(); this.records.splice(i, 1);
}); }
}
this.save(); this.save();
return model; return model;
}, },
...@@ -107,11 +137,6 @@ _.extend(Backbone.LocalStorage.prototype, { ...@@ -107,11 +137,6 @@ _.extend(Backbone.LocalStorage.prototype, {
return localStorage; return localStorage;
}, },
// fix for "illegal access" error on Android when JSON.parse is passed null
jsonData: function (data) {
return data && JSON.parse(data);
},
// Clear localStorage for specific collection. // Clear localStorage for specific collection.
_clear: function() { _clear: function() {
var local = this.localStorage(), var local = this.localStorage(),
...@@ -120,11 +145,12 @@ _.extend(Backbone.LocalStorage.prototype, { ...@@ -120,11 +145,12 @@ _.extend(Backbone.LocalStorage.prototype, {
// Remove id-tracking item (e.g., "foo"). // Remove id-tracking item (e.g., "foo").
local.removeItem(this.name); local.removeItem(this.name);
// Lodash removed _#chain in v1.0.0-rc.1
// Match all data items (e.g., "foo-ID") and remove. // Match all data items (e.g., "foo-ID") and remove.
(_.chain || _)(local).keys() for (var k in local) {
.filter(function (k) { return itemRe.test(k); }) if (itemRe.test(k)) {
.each(function (k) { local.removeItem(k); }); local.removeItem(k);
}
}
this.records.length = 0; this.records.length = 0;
}, },
...@@ -132,6 +158,10 @@ _.extend(Backbone.LocalStorage.prototype, { ...@@ -132,6 +158,10 @@ _.extend(Backbone.LocalStorage.prototype, {
// Size of localStorage. // Size of localStorage.
_storageSize: function() { _storageSize: function() {
return this.localStorage().length; return this.localStorage().length;
},
_itemName: function(id) {
return this.name+"-"+id;
} }
}); });
...@@ -140,9 +170,13 @@ _.extend(Backbone.LocalStorage.prototype, { ...@@ -140,9 +170,13 @@ _.extend(Backbone.LocalStorage.prototype, {
// *localStorage* property, which should be an instance of `Store`. // *localStorage* property, which should be an instance of `Store`.
// window.Store.sync and Backbone.localSync is deprecated, use Backbone.LocalStorage.sync instead // window.Store.sync and Backbone.localSync is deprecated, use Backbone.LocalStorage.sync instead
Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(method, model, options) { Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(method, model, options) {
var store = model.localStorage || model.collection.localStorage; var store = result(model, 'localStorage') || result(model.collection, 'localStorage');
var resp, errorMessage, syncDfd = Backbone.$.Deferred && Backbone.$.Deferred(); //If $ is having Deferred - use it. var resp, errorMessage;
//If $ is having Deferred - use it.
var syncDfd = Backbone.$ ?
(Backbone.$.Deferred && Backbone.$.Deferred()) :
(Backbone.Deferred && Backbone.Deferred());
try { try {
...@@ -204,8 +238,10 @@ Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(m ...@@ -204,8 +238,10 @@ Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(m
Backbone.ajaxSync = Backbone.sync; Backbone.ajaxSync = Backbone.sync;
Backbone.getSyncMethod = function(model) { Backbone.getSyncMethod = function(model, options) {
if(model.localStorage || (model.collection && model.collection.localStorage)) { var forceAjaxSync = options && options.ajaxSync;
if(!forceAjaxSync && (result(model, 'localStorage') || result(model.collection, 'localStorage'))) {
return Backbone.localSync; return Backbone.localSync;
} }
...@@ -215,7 +251,7 @@ Backbone.getSyncMethod = function(model) { ...@@ -215,7 +251,7 @@ Backbone.getSyncMethod = function(model) {
// Override 'Backbone.sync' to default to localSync, // Override 'Backbone.sync' to default to localSync,
// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync' // the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
Backbone.sync = function(method, model, options) { Backbone.sync = function(method, model, options) {
return Backbone.getSyncMethod(model).apply(this, [method, model, options]); return Backbone.getSyncMethod(model, options).apply(this, [method, model, options]);
}; };
return Backbone.LocalStorage; return Backbone.LocalStorage;
......
/*! /*!
* jQuery JavaScript Library v2.1.0 * jQuery JavaScript Library v2.1.3
* http://jquery.com/ * http://jquery.com/
* *
* Includes Sizzle.js * Includes Sizzle.js
...@@ -9,19 +9,19 @@ ...@@ -9,19 +9,19 @@
* Released under the MIT license * Released under the MIT license
* http://jquery.org/license * http://jquery.org/license
* *
* Date: 2014-01-23T21:10Z * Date: 2014-12-18T15:11Z
*/ */
(function( global, factory ) { (function( global, factory ) {
if ( typeof module === "object" && typeof module.exports === "object" ) { if ( typeof module === "object" && typeof module.exports === "object" ) {
// For CommonJS and CommonJS-like environments where a proper window is present, // For CommonJS and CommonJS-like environments where a proper `window`
// execute the factory and get jQuery // is present, execute the factory and get jQuery.
// For environments that do not inherently posses a window with a document // For environments that do not have a `window` with a `document`
// (such as Node.js), expose a jQuery-making factory as module.exports // (such as Node.js), expose a factory as module.exports.
// This accentuates the need for the creation of a real window // This accentuates the need for the creation of a real `window`.
// e.g. var jQuery = require("jquery")(window); // e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info // See ticket #14549 for more info.
module.exports = global.document ? module.exports = global.document ?
factory( global, true ) : factory( global, true ) :
function( w ) { function( w ) {
...@@ -37,10 +37,10 @@ ...@@ -37,10 +37,10 @@
// Pass this if window is not defined yet // Pass this if window is not defined yet
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
// Can't do this because several apps including ASP.NET trace // Support: Firefox 18+
// Can't be in strict mode, several libs including ASP.NET trace
// the stack via arguments.caller.callee and Firefox dies if // the stack via arguments.caller.callee and Firefox dies if
// you try to trace through "use strict" call chains. (#13335) // you try to trace through "use strict" call chains. (#13335)
// Support: Firefox 18+
// //
var arr = []; var arr = [];
...@@ -59,8 +59,6 @@ var toString = class2type.toString; ...@@ -59,8 +59,6 @@ var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty; var hasOwn = class2type.hasOwnProperty;
var trim = "".trim;
var support = {}; var support = {};
...@@ -69,7 +67,7 @@ var ...@@ -69,7 +67,7 @@ var
// Use the correct document accordingly with window argument (sandbox) // Use the correct document accordingly with window argument (sandbox)
document = window.document, document = window.document,
version = "2.1.0", version = "2.1.3",
// Define a local copy of jQuery // Define a local copy of jQuery
jQuery = function( selector, context ) { jQuery = function( selector, context ) {
...@@ -78,6 +76,10 @@ var ...@@ -78,6 +76,10 @@ var
return new jQuery.fn.init( selector, context ); return new jQuery.fn.init( selector, context );
}, },
// Support: Android<4.1
// Make sure we trim BOM and NBSP
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
// Matches dashed string for camelizing // Matches dashed string for camelizing
rmsPrefix = /^-ms-/, rmsPrefix = /^-ms-/,
rdashAlpha = /-([\da-z])/gi, rdashAlpha = /-([\da-z])/gi,
...@@ -108,10 +110,10 @@ jQuery.fn = jQuery.prototype = { ...@@ -108,10 +110,10 @@ jQuery.fn = jQuery.prototype = {
get: function( num ) { get: function( num ) {
return num != null ? return num != null ?
// Return a 'clean' array // Return just the one element from the set
( num < 0 ? this[ num + this.length ] : this[ num ] ) : ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
// Return just the object // Return all the elements in a clean array
slice.call( this ); slice.call( this );
}, },
...@@ -183,7 +185,7 @@ jQuery.extend = jQuery.fn.extend = function() { ...@@ -183,7 +185,7 @@ jQuery.extend = jQuery.fn.extend = function() {
if ( typeof target === "boolean" ) { if ( typeof target === "boolean" ) {
deep = target; deep = target;
// skip the boolean and the target // Skip the boolean and the target
target = arguments[ i ] || {}; target = arguments[ i ] || {};
i++; i++;
} }
...@@ -193,7 +195,7 @@ jQuery.extend = jQuery.fn.extend = function() { ...@@ -193,7 +195,7 @@ jQuery.extend = jQuery.fn.extend = function() {
target = {}; target = {};
} }
// extend jQuery itself if only one argument is passed // Extend jQuery itself if only one argument is passed
if ( i === length ) { if ( i === length ) {
target = this; target = this;
i--; i--;
...@@ -250,9 +252,6 @@ jQuery.extend({ ...@@ -250,9 +252,6 @@ jQuery.extend({
noop: function() {}, noop: function() {},
// See test/unit/core.js for details concerning isFunction.
// Since version 1.3, DOM methods and functions like alert
// aren't supported. They return false on IE (#2968).
isFunction: function( obj ) { isFunction: function( obj ) {
return jQuery.type(obj) === "function"; return jQuery.type(obj) === "function";
}, },
...@@ -267,7 +266,8 @@ jQuery.extend({ ...@@ -267,7 +266,8 @@ jQuery.extend({
// parseFloat NaNs numeric-cast false positives (null|true|false|"") // parseFloat NaNs numeric-cast false positives (null|true|false|"")
// ...but misinterprets leading-number strings, particularly hex literals ("0x...") // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
// subtraction forces infinities to NaN // subtraction forces infinities to NaN
return obj - parseFloat( obj ) >= 0; // adding 1 corrects loss of precision from parseFloat (#15100)
return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0;
}, },
isPlainObject: function( obj ) { isPlainObject: function( obj ) {
...@@ -279,16 +279,8 @@ jQuery.extend({ ...@@ -279,16 +279,8 @@ jQuery.extend({
return false; return false;
} }
// Support: Firefox <20 if ( obj.constructor &&
// The try/catch suppresses exceptions thrown when attempting to access !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
// the "constructor" property of certain host objects, ie. |window.location|
// https://bugzilla.mozilla.org/show_bug.cgi?id=814622
try {
if ( obj.constructor &&
!hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
return false;
}
} catch ( e ) {
return false; return false;
} }
...@@ -309,7 +301,7 @@ jQuery.extend({ ...@@ -309,7 +301,7 @@ jQuery.extend({
if ( obj == null ) { if ( obj == null ) {
return obj + ""; return obj + "";
} }
// Support: Android < 4.0, iOS < 6 (functionish RegExp) // Support: Android<4.0, iOS<6 (functionish RegExp)
return typeof obj === "object" || typeof obj === "function" ? return typeof obj === "object" || typeof obj === "function" ?
class2type[ toString.call(obj) ] || "object" : class2type[ toString.call(obj) ] || "object" :
typeof obj; typeof obj;
...@@ -339,6 +331,7 @@ jQuery.extend({ ...@@ -339,6 +331,7 @@ jQuery.extend({
}, },
// Convert dashed to camelCase; used by the css and data modules // Convert dashed to camelCase; used by the css and data modules
// Support: IE9-11+
// Microsoft forgot to hump their vendor prefix (#9572) // Microsoft forgot to hump their vendor prefix (#9572)
camelCase: function( string ) { camelCase: function( string ) {
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
...@@ -398,8 +391,11 @@ jQuery.extend({ ...@@ -398,8 +391,11 @@ jQuery.extend({
return obj; return obj;
}, },
// Support: Android<4.1
trim: function( text ) { trim: function( text ) {
return text == null ? "" : trim.call( text ); return text == null ?
"" :
( text + "" ).replace( rtrim, "" );
}, },
// results is for internal usage only // results is for internal usage only
...@@ -551,14 +547,14 @@ function isArraylike( obj ) { ...@@ -551,14 +547,14 @@ function isArraylike( obj ) {
} }
var Sizzle = var Sizzle =
/*! /*!
* Sizzle CSS Selector Engine v1.10.16 * Sizzle CSS Selector Engine v2.2.0-pre
* http://sizzlejs.com/ * http://sizzlejs.com/
* *
* Copyright 2013 jQuery Foundation, Inc. and other contributors * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors
* Released under the MIT license * Released under the MIT license
* http://jquery.org/license * http://jquery.org/license
* *
* Date: 2014-01-13 * Date: 2014-12-16
*/ */
(function( window ) { (function( window ) {
...@@ -567,7 +563,9 @@ var i, ...@@ -567,7 +563,9 @@ var i,
Expr, Expr,
getText, getText,
isXML, isXML,
tokenize,
compile, compile,
select,
outermostContext, outermostContext,
sortInput, sortInput,
hasDuplicate, hasDuplicate,
...@@ -583,7 +581,7 @@ var i, ...@@ -583,7 +581,7 @@ var i,
contains, contains,
// Instance-specific data // Instance-specific data
expando = "sizzle" + -(new Date()), expando = "sizzle" + 1 * new Date(),
preferredDoc = window.document, preferredDoc = window.document,
dirruns = 0, dirruns = 0,
done = 0, done = 0,
...@@ -598,7 +596,6 @@ var i, ...@@ -598,7 +596,6 @@ var i,
}, },
// General-purpose constants // General-purpose constants
strundefined = typeof undefined,
MAX_NEGATIVE = 1 << 31, MAX_NEGATIVE = 1 << 31,
// Instance methods // Instance methods
...@@ -608,12 +605,13 @@ var i, ...@@ -608,12 +605,13 @@ var i,
push_native = arr.push, push_native = arr.push,
push = arr.push, push = arr.push,
slice = arr.slice, slice = arr.slice,
// Use a stripped-down indexOf if we can't use a native one // Use a stripped-down indexOf as it's faster than native
indexOf = arr.indexOf || function( elem ) { // http://jsperf.com/thor-indexof-vs-for/5
indexOf = function( list, elem ) {
var i = 0, var i = 0,
len = this.length; len = list.length;
for ( ; i < len; i++ ) { for ( ; i < len; i++ ) {
if ( this[i] === elem ) { if ( list[i] === elem ) {
return i; return i;
} }
} }
...@@ -634,19 +632,26 @@ var i, ...@@ -634,19 +632,26 @@ var i,
// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
identifier = characterEncoding.replace( "w", "w#" ), identifier = characterEncoding.replace( "w", "w#" ),
// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
"*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", // Operator (capture 2)
"*([*^$|!~]?=)" + whitespace +
// Prefer arguments quoted, // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
// then not containing pseudos/brackets, "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
// then attribute selectors/non-parenthetical expressions, "*\\]",
// then anything else
// These preferences are here to reduce the number of selectors pseudos = ":(" + characterEncoding + ")(?:\\((" +
// needing tokenize in the PSEUDO preFilter // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", // 1. quoted (capture 3; capture 4 or capture 5)
"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
// 2. simple (capture 6)
"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
// 3. anything else (capture 2)
".*" +
")\\)|)",
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
rwhitespace = new RegExp( whitespace + "+", "g" ),
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
...@@ -689,7 +694,7 @@ var i, ...@@ -689,7 +694,7 @@ var i,
funescape = function( _, escaped, escapedWhitespace ) { funescape = function( _, escaped, escapedWhitespace ) {
var high = "0x" + escaped - 0x10000; var high = "0x" + escaped - 0x10000;
// NaN means non-codepoint // NaN means non-codepoint
// Support: Firefox // Support: Firefox<24
// Workaround erroneous numeric interpretation of +"0x" // Workaround erroneous numeric interpretation of +"0x"
return high !== high || escapedWhitespace ? return high !== high || escapedWhitespace ?
escaped : escaped :
...@@ -698,6 +703,14 @@ var i, ...@@ -698,6 +703,14 @@ var i,
String.fromCharCode( high + 0x10000 ) : String.fromCharCode( high + 0x10000 ) :
// Supplemental Plane codepoint (surrogate pair) // Supplemental Plane codepoint (surrogate pair)
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
},
// Used for iframes
// See setDocument()
// Removing the function wrapper causes a "Permission Denied"
// error in IE
unloadHandler = function() {
setDocument();
}; };
// Optimize for push.apply( _, NodeList ) // Optimize for push.apply( _, NodeList )
...@@ -740,19 +753,18 @@ function Sizzle( selector, context, results, seed ) { ...@@ -740,19 +753,18 @@ function Sizzle( selector, context, results, seed ) {
context = context || document; context = context || document;
results = results || []; results = results || [];
nodeType = context.nodeType;
if ( !selector || typeof selector !== "string" ) { if ( typeof selector !== "string" || !selector ||
return results; nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
}
if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { return results;
return [];
} }
if ( documentIsHTML && !seed ) { if ( !seed && documentIsHTML ) {
// Shortcuts // Try to shortcut find operations when possible (e.g., not under DocumentFragment)
if ( (match = rquickExpr.exec( selector )) ) { if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
// Speed-up: Sizzle("#ID") // Speed-up: Sizzle("#ID")
if ( (m = match[1]) ) { if ( (m = match[1]) ) {
if ( nodeType === 9 ) { if ( nodeType === 9 ) {
...@@ -784,7 +796,7 @@ function Sizzle( selector, context, results, seed ) { ...@@ -784,7 +796,7 @@ function Sizzle( selector, context, results, seed ) {
return results; return results;
// Speed-up: Sizzle(".CLASS") // Speed-up: Sizzle(".CLASS")
} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { } else if ( (m = match[3]) && support.getElementsByClassName ) {
push.apply( results, context.getElementsByClassName( m ) ); push.apply( results, context.getElementsByClassName( m ) );
return results; return results;
} }
...@@ -794,7 +806,7 @@ function Sizzle( selector, context, results, seed ) { ...@@ -794,7 +806,7 @@ function Sizzle( selector, context, results, seed ) {
if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
nid = old = expando; nid = old = expando;
newContext = context; newContext = context;
newSelector = nodeType === 9 && selector; newSelector = nodeType !== 1 && selector;
// qSA works strangely on Element-rooted queries // qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root // We can work around this by specifying an extra ID on the root
...@@ -981,7 +993,7 @@ function createPositionalPseudo( fn ) { ...@@ -981,7 +993,7 @@ function createPositionalPseudo( fn ) {
* @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
*/ */
function testContext( context ) { function testContext( context ) {
return context && typeof context.getElementsByTagName !== strundefined && context; return context && typeof context.getElementsByTagName !== "undefined" && context;
} }
// Expose support vars for convenience // Expose support vars for convenience
...@@ -1005,9 +1017,8 @@ isXML = Sizzle.isXML = function( elem ) { ...@@ -1005,9 +1017,8 @@ isXML = Sizzle.isXML = function( elem ) {
* @returns {Object} Returns the current document * @returns {Object} Returns the current document
*/ */
setDocument = Sizzle.setDocument = function( node ) { setDocument = Sizzle.setDocument = function( node ) {
var hasCompare, var hasCompare, parent,
doc = node ? node.ownerDocument || node : preferredDoc, doc = node ? node.ownerDocument || node : preferredDoc;
parent = doc.defaultView;
// If no document and documentElement is available, return // If no document and documentElement is available, return
if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
...@@ -1017,9 +1028,7 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1017,9 +1028,7 @@ setDocument = Sizzle.setDocument = function( node ) {
// Set our document // Set our document
document = doc; document = doc;
docElem = doc.documentElement; docElem = doc.documentElement;
parent = doc.defaultView;
// Support tests
documentIsHTML = !isXML( doc );
// Support: IE>8 // Support: IE>8
// If iframe document is assigned to "document" variable and if iframe has been reloaded, // If iframe document is assigned to "document" variable and if iframe has been reloaded,
...@@ -1028,21 +1037,22 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1028,21 +1037,22 @@ setDocument = Sizzle.setDocument = function( node ) {
if ( parent && parent !== parent.top ) { if ( parent && parent !== parent.top ) {
// IE11 does not have attachEvent, so all must suffer // IE11 does not have attachEvent, so all must suffer
if ( parent.addEventListener ) { if ( parent.addEventListener ) {
parent.addEventListener( "unload", function() { parent.addEventListener( "unload", unloadHandler, false );
setDocument();
}, false );
} else if ( parent.attachEvent ) { } else if ( parent.attachEvent ) {
parent.attachEvent( "onunload", function() { parent.attachEvent( "onunload", unloadHandler );
setDocument();
});
} }
} }
/* Support tests
---------------------------------------------------------------------- */
documentIsHTML = !isXML( doc );
/* Attributes /* Attributes
---------------------------------------------------------------------- */ ---------------------------------------------------------------------- */
// Support: IE<8 // Support: IE<8
// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) // Verify that getAttribute really returns attributes and not properties
// (excepting IE8 booleans)
support.attributes = assert(function( div ) { support.attributes = assert(function( div ) {
div.className = "i"; div.className = "i";
return !div.getAttribute("className"); return !div.getAttribute("className");
...@@ -1057,17 +1067,8 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1057,17 +1067,8 @@ setDocument = Sizzle.setDocument = function( node ) {
return !div.getElementsByTagName("*").length; return !div.getElementsByTagName("*").length;
}); });
// Check if getElementsByClassName can be trusted // Support: IE<9
support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { support.getElementsByClassName = rnative.test( doc.getElementsByClassName );
div.innerHTML = "<div class='a'></div><div class='a i'></div>";
// Support: Safari<4
// Catch class over-caching
div.firstChild.className = "i";
// Support: Opera<10
// Catch gEBCN failure to find non-leading classes
return div.getElementsByClassName("i").length === 2;
});
// Support: IE<10 // Support: IE<10
// Check if getElementById returns elements by name // Check if getElementById returns elements by name
...@@ -1081,11 +1082,11 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1081,11 +1082,11 @@ setDocument = Sizzle.setDocument = function( node ) {
// ID find and filter // ID find and filter
if ( support.getById ) { if ( support.getById ) {
Expr.find["ID"] = function( id, context ) { Expr.find["ID"] = function( id, context ) {
if ( typeof context.getElementById !== strundefined && documentIsHTML ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
var m = context.getElementById( id ); var m = context.getElementById( id );
// Check parentNode to catch when Blackberry 4.6 returns // Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963 // nodes that are no longer in the document #6963
return m && m.parentNode ? [m] : []; return m && m.parentNode ? [ m ] : [];
} }
}; };
Expr.filter["ID"] = function( id ) { Expr.filter["ID"] = function( id ) {
...@@ -1102,7 +1103,7 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1102,7 +1103,7 @@ setDocument = Sizzle.setDocument = function( node ) {
Expr.filter["ID"] = function( id ) { Expr.filter["ID"] = function( id ) {
var attrId = id.replace( runescape, funescape ); var attrId = id.replace( runescape, funescape );
return function( elem ) { return function( elem ) {
var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
return node && node.value === attrId; return node && node.value === attrId;
}; };
}; };
...@@ -1111,14 +1112,20 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1111,14 +1112,20 @@ setDocument = Sizzle.setDocument = function( node ) {
// Tag // Tag
Expr.find["TAG"] = support.getElementsByTagName ? Expr.find["TAG"] = support.getElementsByTagName ?
function( tag, context ) { function( tag, context ) {
if ( typeof context.getElementsByTagName !== strundefined ) { if ( typeof context.getElementsByTagName !== "undefined" ) {
return context.getElementsByTagName( tag ); return context.getElementsByTagName( tag );
// DocumentFragment nodes don't have gEBTN
} else if ( support.qsa ) {
return context.querySelectorAll( tag );
} }
} : } :
function( tag, context ) { function( tag, context ) {
var elem, var elem,
tmp = [], tmp = [],
i = 0, i = 0,
// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
results = context.getElementsByTagName( tag ); results = context.getElementsByTagName( tag );
// Filter out possible comments // Filter out possible comments
...@@ -1136,7 +1143,7 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1136,7 +1143,7 @@ setDocument = Sizzle.setDocument = function( node ) {
// Class // Class
Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { if ( documentIsHTML ) {
return context.getElementsByClassName( className ); return context.getElementsByClassName( className );
} }
}; };
...@@ -1165,11 +1172,15 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1165,11 +1172,15 @@ setDocument = Sizzle.setDocument = function( node ) {
// setting a boolean content attribute, // setting a boolean content attribute,
// since its presence should be enough // since its presence should be enough
// http://bugs.jquery.com/ticket/12359 // http://bugs.jquery.com/ticket/12359
div.innerHTML = "<select t=''><option selected=''></option></select>"; docElem.appendChild( div ).innerHTML = "<a id='" + expando + "'></a>" +
"<select id='" + expando + "-\f]' msallowcapture=''>" +
"<option selected=''></option></select>";
// Support: IE8, Opera 10-12 // Support: IE8, Opera 11-12.16
// Nothing should be selected when empty strings follow ^= or $= or *= // Nothing should be selected when empty strings follow ^= or $= or *=
if ( div.querySelectorAll("[t^='']").length ) { // The test attribute must be unknown in Opera but "safe" for WinRT
// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
if ( div.querySelectorAll("[msallowcapture^='']").length ) {
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
} }
...@@ -1179,12 +1190,24 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1179,12 +1190,24 @@ setDocument = Sizzle.setDocument = function( node ) {
rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
} }
// Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+
if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
rbuggyQSA.push("~=");
}
// Webkit/Opera - :checked should return selected option elements // Webkit/Opera - :checked should return selected option elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
// IE8 throws error here and will not see later tests // IE8 throws error here and will not see later tests
if ( !div.querySelectorAll(":checked").length ) { if ( !div.querySelectorAll(":checked").length ) {
rbuggyQSA.push(":checked"); rbuggyQSA.push(":checked");
} }
// Support: Safari 8+, iOS 8+
// https://bugs.webkit.org/show_bug.cgi?id=136851
// In-page `selector#id sibing-combinator selector` fails
if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) {
rbuggyQSA.push(".#.+[+~]");
}
}); });
assert(function( div ) { assert(function( div ) {
...@@ -1212,7 +1235,8 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1212,7 +1235,8 @@ setDocument = Sizzle.setDocument = function( node ) {
}); });
} }
if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
docElem.webkitMatchesSelector ||
docElem.mozMatchesSelector || docElem.mozMatchesSelector ||
docElem.oMatchesSelector || docElem.oMatchesSelector ||
docElem.msMatchesSelector) )) ) { docElem.msMatchesSelector) )) ) {
...@@ -1300,7 +1324,7 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1300,7 +1324,7 @@ setDocument = Sizzle.setDocument = function( node ) {
// Maintain original order // Maintain original order
return sortInput ? return sortInput ?
( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
0; 0;
} }
...@@ -1327,7 +1351,7 @@ setDocument = Sizzle.setDocument = function( node ) { ...@@ -1327,7 +1351,7 @@ setDocument = Sizzle.setDocument = function( node ) {
aup ? -1 : aup ? -1 :
bup ? 1 : bup ? 1 :
sortInput ? sortInput ?
( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
0; 0;
// If the nodes are siblings, we can do a quick check // If the nodes are siblings, we can do a quick check
...@@ -1390,10 +1414,10 @@ Sizzle.matchesSelector = function( elem, expr ) { ...@@ -1390,10 +1414,10 @@ Sizzle.matchesSelector = function( elem, expr ) {
elem.document && elem.document.nodeType !== 11 ) { elem.document && elem.document.nodeType !== 11 ) {
return ret; return ret;
} }
} catch(e) {} } catch (e) {}
} }
return Sizzle( expr, document, null, [elem] ).length > 0; return Sizzle( expr, document, null, [ elem ] ).length > 0;
}; };
Sizzle.contains = function( context, elem ) { Sizzle.contains = function( context, elem ) {
...@@ -1522,7 +1546,7 @@ Expr = Sizzle.selectors = { ...@@ -1522,7 +1546,7 @@ Expr = Sizzle.selectors = {
match[1] = match[1].replace( runescape, funescape ); match[1] = match[1].replace( runescape, funescape );
// Move the given value to match[3] whether quoted or unquoted // Move the given value to match[3] whether quoted or unquoted
match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
if ( match[2] === "~=" ) { if ( match[2] === "~=" ) {
match[3] = " " + match[3] + " "; match[3] = " " + match[3] + " ";
...@@ -1565,15 +1589,15 @@ Expr = Sizzle.selectors = { ...@@ -1565,15 +1589,15 @@ Expr = Sizzle.selectors = {
"PSEUDO": function( match ) { "PSEUDO": function( match ) {
var excess, var excess,
unquoted = !match[5] && match[2]; unquoted = !match[6] && match[2];
if ( matchExpr["CHILD"].test( match[0] ) ) { if ( matchExpr["CHILD"].test( match[0] ) ) {
return null; return null;
} }
// Accept quoted arguments as-is // Accept quoted arguments as-is
if ( match[3] && match[4] !== undefined ) { if ( match[3] ) {
match[2] = match[4]; match[2] = match[4] || match[5] || "";
// Strip excess characters from unquoted arguments // Strip excess characters from unquoted arguments
} else if ( unquoted && rpseudo.test( unquoted ) && } else if ( unquoted && rpseudo.test( unquoted ) &&
...@@ -1609,7 +1633,7 @@ Expr = Sizzle.selectors = { ...@@ -1609,7 +1633,7 @@ Expr = Sizzle.selectors = {
return pattern || return pattern ||
(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
classCache( className, function( elem ) { classCache( className, function( elem ) {
return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
}); });
}, },
...@@ -1631,7 +1655,7 @@ Expr = Sizzle.selectors = { ...@@ -1631,7 +1655,7 @@ Expr = Sizzle.selectors = {
operator === "^=" ? check && result.indexOf( check ) === 0 : operator === "^=" ? check && result.indexOf( check ) === 0 :
operator === "*=" ? check && result.indexOf( check ) > -1 : operator === "*=" ? check && result.indexOf( check ) > -1 :
operator === "$=" ? check && result.slice( -check.length ) === check : operator === "$=" ? check && result.slice( -check.length ) === check :
operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
false; false;
}; };
...@@ -1751,7 +1775,7 @@ Expr = Sizzle.selectors = { ...@@ -1751,7 +1775,7 @@ Expr = Sizzle.selectors = {
matched = fn( seed, argument ), matched = fn( seed, argument ),
i = matched.length; i = matched.length;
while ( i-- ) { while ( i-- ) {
idx = indexOf.call( seed, matched[i] ); idx = indexOf( seed, matched[i] );
seed[ idx ] = !( matches[ idx ] = matched[i] ); seed[ idx ] = !( matches[ idx ] = matched[i] );
} }
}) : }) :
...@@ -1790,6 +1814,8 @@ Expr = Sizzle.selectors = { ...@@ -1790,6 +1814,8 @@ Expr = Sizzle.selectors = {
function( elem, context, xml ) { function( elem, context, xml ) {
input[0] = elem; input[0] = elem;
matcher( input, null, xml, results ); matcher( input, null, xml, results );
// Don't keep the element (issue #299)
input[0] = null;
return !results.pop(); return !results.pop();
}; };
}), }),
...@@ -1801,6 +1827,7 @@ Expr = Sizzle.selectors = { ...@@ -1801,6 +1827,7 @@ Expr = Sizzle.selectors = {
}), }),
"contains": markFunction(function( text ) { "contains": markFunction(function( text ) {
text = text.replace( runescape, funescape );
return function( elem ) { return function( elem ) {
return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
}; };
...@@ -1978,7 +2005,7 @@ function setFilters() {} ...@@ -1978,7 +2005,7 @@ function setFilters() {}
setFilters.prototype = Expr.filters = Expr.pseudos; setFilters.prototype = Expr.filters = Expr.pseudos;
Expr.setFilters = new setFilters(); Expr.setFilters = new setFilters();
function tokenize( selector, parseOnly ) { tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
var matched, match, tokens, type, var matched, match, tokens, type,
soFar, groups, preFilters, soFar, groups, preFilters,
cached = tokenCache[ selector + " " ]; cached = tokenCache[ selector + " " ];
...@@ -2043,7 +2070,7 @@ function tokenize( selector, parseOnly ) { ...@@ -2043,7 +2070,7 @@ function tokenize( selector, parseOnly ) {
Sizzle.error( selector ) : Sizzle.error( selector ) :
// Cache the tokens // Cache the tokens
tokenCache( selector, groups ).slice( 0 ); tokenCache( selector, groups ).slice( 0 );
} };
function toSelector( tokens ) { function toSelector( tokens ) {
var i = 0, var i = 0,
...@@ -2122,6 +2149,15 @@ function elementMatcher( matchers ) { ...@@ -2122,6 +2149,15 @@ function elementMatcher( matchers ) {
matchers[0]; matchers[0];
} }
function multipleContexts( selector, contexts, results ) {
var i = 0,
len = contexts.length;
for ( ; i < len; i++ ) {
Sizzle( selector, contexts[i], results );
}
return results;
}
function condense( unmatched, map, filter, context, xml ) { function condense( unmatched, map, filter, context, xml ) {
var elem, var elem,
newUnmatched = [], newUnmatched = [],
...@@ -2213,7 +2249,7 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS ...@@ -2213,7 +2249,7 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS
i = matcherOut.length; i = matcherOut.length;
while ( i-- ) { while ( i-- ) {
if ( (elem = matcherOut[i]) && if ( (elem = matcherOut[i]) &&
(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
seed[temp] = !(results[temp] = elem); seed[temp] = !(results[temp] = elem);
} }
...@@ -2248,13 +2284,16 @@ function matcherFromTokens( tokens ) { ...@@ -2248,13 +2284,16 @@ function matcherFromTokens( tokens ) {
return elem === checkContext; return elem === checkContext;
}, implicitRelative, true ), }, implicitRelative, true ),
matchAnyContext = addCombinator( function( elem ) { matchAnyContext = addCombinator( function( elem ) {
return indexOf.call( checkContext, elem ) > -1; return indexOf( checkContext, elem ) > -1;
}, implicitRelative, true ), }, implicitRelative, true ),
matchers = [ function( elem, context, xml ) { matchers = [ function( elem, context, xml ) {
return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
(checkContext = context).nodeType ? (checkContext = context).nodeType ?
matchContext( elem, context, xml ) : matchContext( elem, context, xml ) :
matchAnyContext( elem, context, xml ) ); matchAnyContext( elem, context, xml ) );
// Avoid hanging onto element (issue #299)
checkContext = null;
return ret;
} ]; } ];
for ( ; i < len; i++ ) { for ( ; i < len; i++ ) {
...@@ -2390,7 +2429,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { ...@@ -2390,7 +2429,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
superMatcher; superMatcher;
} }
compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
var i, var i,
setMatchers = [], setMatchers = [],
elementMatchers = [], elementMatchers = [],
...@@ -2398,12 +2437,12 @@ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { ...@@ -2398,12 +2437,12 @@ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
if ( !cached ) { if ( !cached ) {
// Generate a function of recursive functions that can be used to check each element // Generate a function of recursive functions that can be used to check each element
if ( !group ) { if ( !match ) {
group = tokenize( selector ); match = tokenize( selector );
} }
i = group.length; i = match.length;
while ( i-- ) { while ( i-- ) {
cached = matcherFromTokens( group[i] ); cached = matcherFromTokens( match[i] );
if ( cached[ expando ] ) { if ( cached[ expando ] ) {
setMatchers.push( cached ); setMatchers.push( cached );
} else { } else {
...@@ -2413,74 +2452,83 @@ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { ...@@ -2413,74 +2452,83 @@ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
// Cache the compiled function // Cache the compiled function
cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
// Save selector and tokenization
cached.selector = selector;
} }
return cached; return cached;
}; };
function multipleContexts( selector, contexts, results ) { /**
var i = 0, * A low-level selection function that works with Sizzle's compiled
len = contexts.length; * selector functions
for ( ; i < len; i++ ) { * @param {String|Function} selector A selector or a pre-compiled
Sizzle( selector, contexts[i], results ); * selector function built with Sizzle.compile
} * @param {Element} context
return results; * @param {Array} [results]
} * @param {Array} [seed] A set of elements to match against
*/
function select( selector, context, results, seed ) { select = Sizzle.select = function( selector, context, results, seed ) {
var i, tokens, token, type, find, var i, tokens, token, type, find,
match = tokenize( selector ); compiled = typeof selector === "function" && selector,
match = !seed && tokenize( (selector = compiled.selector || selector) );
if ( !seed ) { results = results || [];
// Try to minimize operations if there is only one group
if ( match.length === 1 ) {
// Take a shortcut and set the context if the root selector is an ID // Try to minimize operations if there is no seed and only one group
tokens = match[0] = match[0].slice( 0 ); if ( match.length === 1 ) {
if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
support.getById && context.nodeType === 9 && documentIsHTML &&
Expr.relative[ tokens[1].type ] ) {
context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; // Take a shortcut and set the context if the root selector is an ID
if ( !context ) { tokens = match[0] = match[0].slice( 0 );
return results; if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
} support.getById && context.nodeType === 9 && documentIsHTML &&
selector = selector.slice( tokens.shift().value.length ); Expr.relative[ tokens[1].type ] ) {
context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
if ( !context ) {
return results;
// Precompiled matchers will still verify ancestry, so step up a level
} else if ( compiled ) {
context = context.parentNode;
} }
// Fetch a seed set for right-to-left matching selector = selector.slice( tokens.shift().value.length );
i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; }
while ( i-- ) {
token = tokens[i];
// Abort if we hit a combinator // Fetch a seed set for right-to-left matching
if ( Expr.relative[ (type = token.type) ] ) { i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
break; while ( i-- ) {
} token = tokens[i];
if ( (find = Expr.find[ type ]) ) {
// Search, expanding context for leading sibling combinators
if ( (seed = find(
token.matches[0].replace( runescape, funescape ),
rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
)) ) {
// If seed is empty or no tokens remain, we can return early
tokens.splice( i, 1 );
selector = seed.length && toSelector( tokens );
if ( !selector ) {
push.apply( results, seed );
return results;
}
break; // Abort if we hit a combinator
if ( Expr.relative[ (type = token.type) ] ) {
break;
}
if ( (find = Expr.find[ type ]) ) {
// Search, expanding context for leading sibling combinators
if ( (seed = find(
token.matches[0].replace( runescape, funescape ),
rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
)) ) {
// If seed is empty or no tokens remain, we can return early
tokens.splice( i, 1 );
selector = seed.length && toSelector( tokens );
if ( !selector ) {
push.apply( results, seed );
return results;
} }
break;
} }
} }
} }
} }
// Compile and execute a filtering function // Compile and execute a filtering function if one is not provided
// Provide `match` to avoid retokenization if we modified the selector above // Provide `match` to avoid retokenization if we modified the selector above
compile( selector, match )( ( compiled || compile( selector, match ) )(
seed, seed,
context, context,
!documentIsHTML, !documentIsHTML,
...@@ -2488,14 +2536,14 @@ function select( selector, context, results, seed ) { ...@@ -2488,14 +2536,14 @@ function select( selector, context, results, seed ) {
rsibling.test( selector ) && testContext( context.parentNode ) || context rsibling.test( selector ) && testContext( context.parentNode ) || context
); );
return results; return results;
} };
// One-time assignments // One-time assignments
// Sort stability // Sort stability
support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
// Support: Chrome<14 // Support: Chrome 14-35+
// Always assume duplicates if they aren't passed to the comparison function // Always assume duplicates if they aren't passed to the comparison function
support.detectDuplicates = !!hasDuplicate; support.detectDuplicates = !!hasDuplicate;
...@@ -2704,7 +2752,7 @@ var rootjQuery, ...@@ -2704,7 +2752,7 @@ var rootjQuery,
if ( match[1] ) { if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context; context = context instanceof jQuery ? context[0] : context;
// scripts is true for back-compat // Option to run scripts is true for back-compat
// Intentionally let the error be thrown if parseHTML is not present // Intentionally let the error be thrown if parseHTML is not present
jQuery.merge( this, jQuery.parseHTML( jQuery.merge( this, jQuery.parseHTML(
match[1], match[1],
...@@ -2732,8 +2780,8 @@ var rootjQuery, ...@@ -2732,8 +2780,8 @@ var rootjQuery,
} else { } else {
elem = document.getElementById( match[2] ); elem = document.getElementById( match[2] );
// Check parentNode to catch when Blackberry 4.6 returns // Support: Blackberry 4.6
// nodes that are no longer in the document #6963 // gEBID returns nodes no longer in the document (#6963)
if ( elem && elem.parentNode ) { if ( elem && elem.parentNode ) {
// Inject the element directly into the jQuery object // Inject the element directly into the jQuery object
this.length = 1; this.length = 1;
...@@ -2786,7 +2834,7 @@ rootjQuery = jQuery( document ); ...@@ -2786,7 +2834,7 @@ rootjQuery = jQuery( document );
var rparentsprev = /^(?:parents|prev(?:Until|All))/, var rparentsprev = /^(?:parents|prev(?:Until|All))/,
// methods guaranteed to produce a unique set when starting from a unique set // Methods guaranteed to produce a unique set when starting from a unique set
guaranteedUnique = { guaranteedUnique = {
children: true, children: true,
contents: true, contents: true,
...@@ -2866,8 +2914,7 @@ jQuery.fn.extend({ ...@@ -2866,8 +2914,7 @@ jQuery.fn.extend({
return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
}, },
// Determine the position of an element within // Determine the position of an element within the set
// the matched set of elements
index: function( elem ) { index: function( elem ) {
// No argument, return index in parent // No argument, return index in parent
...@@ -2875,7 +2922,7 @@ jQuery.fn.extend({ ...@@ -2875,7 +2922,7 @@ jQuery.fn.extend({
return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
} }
// index in selector // Index in selector
if ( typeof elem === "string" ) { if ( typeof elem === "string" ) {
return indexOf.call( jQuery( elem ), this[ 0 ] ); return indexOf.call( jQuery( elem ), this[ 0 ] );
} }
...@@ -3291,7 +3338,7 @@ jQuery.extend({ ...@@ -3291,7 +3338,7 @@ jQuery.extend({
progressValues, progressContexts, resolveContexts; progressValues, progressContexts, resolveContexts;
// add listeners to Deferred subordinates; treat others as resolved // Add listeners to Deferred subordinates; treat others as resolved
if ( length > 1 ) { if ( length > 1 ) {
progressValues = new Array( length ); progressValues = new Array( length );
progressContexts = new Array( length ); progressContexts = new Array( length );
...@@ -3308,7 +3355,7 @@ jQuery.extend({ ...@@ -3308,7 +3355,7 @@ jQuery.extend({
} }
} }
// if we're not waiting on anything, resolve the master // If we're not waiting on anything, resolve the master
if ( !remaining ) { if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues ); deferred.resolveWith( resolveContexts, resolveValues );
} }
...@@ -3365,8 +3412,9 @@ jQuery.extend({ ...@@ -3365,8 +3412,9 @@ jQuery.extend({
readyList.resolveWith( document, [ jQuery ] ); readyList.resolveWith( document, [ jQuery ] );
// Trigger any bound ready events // Trigger any bound ready events
if ( jQuery.fn.trigger ) { if ( jQuery.fn.triggerHandler ) {
jQuery( document ).trigger("ready").off("ready"); jQuery( document ).triggerHandler( "ready" );
jQuery( document ).off( "ready" );
} }
} }
}); });
...@@ -3386,7 +3434,7 @@ jQuery.ready.promise = function( obj ) { ...@@ -3386,7 +3434,7 @@ jQuery.ready.promise = function( obj ) {
readyList = jQuery.Deferred(); readyList = jQuery.Deferred();
// Catch cases where $(document).ready() is called after the browser event has already occurred. // Catch cases where $(document).ready() is called after the browser event has already occurred.
// we once tried to use readyState "interactive" here, but it caused issues like the one // We once tried to use readyState "interactive" here, but it caused issues like the one
// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
if ( document.readyState === "complete" ) { if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready // Handle it asynchronously to allow scripts the opportunity to delay ready
...@@ -3480,7 +3528,7 @@ jQuery.acceptData = function( owner ) { ...@@ -3480,7 +3528,7 @@ jQuery.acceptData = function( owner ) {
function Data() { function Data() {
// Support: Android < 4, // Support: Android<4,
// Old WebKit does not have Object.preventExtensions/freeze method, // Old WebKit does not have Object.preventExtensions/freeze method,
// return new empty object instead with no [[set]] accessor // return new empty object instead with no [[set]] accessor
Object.defineProperty( this.cache = {}, 0, { Object.defineProperty( this.cache = {}, 0, {
...@@ -3489,7 +3537,7 @@ function Data() { ...@@ -3489,7 +3537,7 @@ function Data() {
} }
}); });
this.expando = jQuery.expando + Math.random(); this.expando = jQuery.expando + Data.uid++;
} }
Data.uid = 1; Data.uid = 1;
...@@ -3517,7 +3565,7 @@ Data.prototype = { ...@@ -3517,7 +3565,7 @@ Data.prototype = {
descriptor[ this.expando ] = { value: unlock }; descriptor[ this.expando ] = { value: unlock };
Object.defineProperties( owner, descriptor ); Object.defineProperties( owner, descriptor );
// Support: Android < 4 // Support: Android<4
// Fallback to a less secure definition // Fallback to a less secure definition
} catch ( e ) { } catch ( e ) {
descriptor[ this.expando ] = unlock; descriptor[ this.expando ] = unlock;
...@@ -3657,17 +3705,16 @@ var data_user = new Data(); ...@@ -3657,17 +3705,16 @@ var data_user = new Data();
/* // Implementation Summary
Implementation Summary //
// 1. Enforce API surface and semantic compatibility with 1.9.x branch
1. Enforce API surface and semantic compatibility with 1.9.x branch // 2. Improve the module's maintainability by reducing the storage
2. Improve the module's maintainability by reducing the storage // paths to a single mechanism.
paths to a single mechanism. // 3. Use the same single mechanism to support "private" and "user" data.
3. Use the same single mechanism to support "private" and "user" data. // 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) // 5. Avoid exposing implementation details on user objects (eg. expando properties)
5. Avoid exposing implementation details on user objects (eg. expando properties) // 6. Provide a clear path for implementation upgrade to WeakMap in 2014
6. Provide a clear path for implementation upgrade to WeakMap in 2014
*/
var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
rmultiDash = /([A-Z])/g; rmultiDash = /([A-Z])/g;
...@@ -3738,11 +3785,15 @@ jQuery.fn.extend({ ...@@ -3738,11 +3785,15 @@ jQuery.fn.extend({
if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
i = attrs.length; i = attrs.length;
while ( i-- ) { while ( i-- ) {
name = attrs[ i ].name;
if ( name.indexOf( "data-" ) === 0 ) { // Support: IE11+
name = jQuery.camelCase( name.slice(5) ); // The attrs elements can be null (#14894)
dataAttr( elem, name, data[ name ] ); if ( attrs[ i ] ) {
name = attrs[ i ].name;
if ( name.indexOf( "data-" ) === 0 ) {
name = jQuery.camelCase( name.slice(5) );
dataAttr( elem, name, data[ name ] );
}
} }
} }
data_priv.set( elem, "hasDataAttrs", true ); data_priv.set( elem, "hasDataAttrs", true );
...@@ -3868,7 +3919,7 @@ jQuery.extend({ ...@@ -3868,7 +3919,7 @@ jQuery.extend({
queue.unshift( "inprogress" ); queue.unshift( "inprogress" );
} }
// clear up the last queue stop function // Clear up the last queue stop function
delete hooks.stop; delete hooks.stop;
fn.call( elem, next, hooks ); fn.call( elem, next, hooks );
} }
...@@ -3878,7 +3929,7 @@ jQuery.extend({ ...@@ -3878,7 +3929,7 @@ jQuery.extend({
} }
}, },
// not intended for public consumption - generates a queueHooks object, or returns the current one // Not public - generate a queueHooks object, or return the current one
_queueHooks: function( elem, type ) { _queueHooks: function( elem, type ) {
var key = type + "queueHooks"; var key = type + "queueHooks";
return data_priv.get( elem, key ) || data_priv.access( elem, key, { return data_priv.get( elem, key ) || data_priv.access( elem, key, {
...@@ -3908,7 +3959,7 @@ jQuery.fn.extend({ ...@@ -3908,7 +3959,7 @@ jQuery.fn.extend({
this.each(function() { this.each(function() {
var queue = jQuery.queue( this, type, data ); var queue = jQuery.queue( this, type, data );
// ensure a hooks for this queue // Ensure a hooks for this queue
jQuery._queueHooks( this, type ); jQuery._queueHooks( this, type );
if ( type === "fx" && queue[0] !== "inprogress" ) { if ( type === "fx" && queue[0] !== "inprogress" ) {
...@@ -3972,17 +4023,25 @@ var rcheckableType = (/^(?:checkbox|radio)$/i); ...@@ -3972,17 +4023,25 @@ var rcheckableType = (/^(?:checkbox|radio)$/i);
(function() { (function() {
var fragment = document.createDocumentFragment(), var fragment = document.createDocumentFragment(),
div = fragment.appendChild( document.createElement( "div" ) ); div = fragment.appendChild( document.createElement( "div" ) ),
input = document.createElement( "input" );
// #11217 - WebKit loses check when the name is after the checked attribute // Support: Safari<=5.1
div.innerHTML = "<input type='radio' checked='checked' name='t'/>"; // Check state lost if the name is set (#11217)
// Support: Windows Web Apps (WWA)
// `name` and `type` must use .setAttribute for WWA (#14901)
input.setAttribute( "type", "radio" );
input.setAttribute( "checked", "checked" );
input.setAttribute( "name", "t" );
// Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 div.appendChild( input );
// old WebKit doesn't clone checked state correctly in fragments
// Support: Safari<=5.1, Android<4.2
// Older WebKit doesn't clone checked state correctly in fragments
support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
// Support: IE<=11+
// Make sure textarea (and checkbox) defaultValue is properly cloned // Make sure textarea (and checkbox) defaultValue is properly cloned
// Support: IE9-IE11+
div.innerHTML = "<textarea>x</textarea>"; div.innerHTML = "<textarea>x</textarea>";
support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
})(); })();
...@@ -3995,7 +4054,7 @@ support.focusinBubbles = "onfocusin" in window; ...@@ -3995,7 +4054,7 @@ support.focusinBubbles = "onfocusin" in window;
var var
rkeyEvent = /^key/, rkeyEvent = /^key/,
rmouseEvent = /^(?:mouse|contextmenu)|click/, rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
...@@ -4360,8 +4419,8 @@ jQuery.event = { ...@@ -4360,8 +4419,8 @@ jQuery.event = {
j = 0; j = 0;
while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
// Triggered event must either 1) have no namespace, or // Triggered event must either 1) have no namespace, or 2) have namespace(s)
// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). // a subset or equal to those in the bound event (both can have no namespace).
if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
event.handleObj = handleObj; event.handleObj = handleObj;
...@@ -4511,7 +4570,7 @@ jQuery.event = { ...@@ -4511,7 +4570,7 @@ jQuery.event = {
event.target = document; event.target = document;
} }
// Support: Safari 6.0+, Chrome < 28 // Support: Safari 6.0+, Chrome<28
// Target should not be a text node (#504, #13143) // Target should not be a text node (#504, #13143)
if ( event.target.nodeType === 3 ) { if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode; event.target = event.target.parentNode;
...@@ -4564,7 +4623,7 @@ jQuery.event = { ...@@ -4564,7 +4623,7 @@ jQuery.event = {
// Support: Firefox 20+ // Support: Firefox 20+
// Firefox doesn't alert if the returnValue field is not set. // Firefox doesn't alert if the returnValue field is not set.
if ( event.result !== undefined ) { if ( event.result !== undefined && event.originalEvent ) {
event.originalEvent.returnValue = event.result; event.originalEvent.returnValue = event.result;
} }
} }
...@@ -4615,9 +4674,9 @@ jQuery.Event = function( src, props ) { ...@@ -4615,9 +4674,9 @@ jQuery.Event = function( src, props ) {
// Events bubbling up the document may have been marked as prevented // Events bubbling up the document may have been marked as prevented
// by a handler lower down the tree; reflect the correct value. // by a handler lower down the tree; reflect the correct value.
this.isDefaultPrevented = src.defaultPrevented || this.isDefaultPrevented = src.defaultPrevented ||
// Support: Android < 4.0
src.defaultPrevented === undefined && src.defaultPrevented === undefined &&
src.getPreventDefault && src.getPreventDefault() ? // Support: Android<4.0
src.returnValue === false ?
returnTrue : returnTrue :
returnFalse; returnFalse;
...@@ -4664,7 +4723,14 @@ jQuery.Event.prototype = { ...@@ -4664,7 +4723,14 @@ jQuery.Event.prototype = {
} }
}, },
stopImmediatePropagation: function() { stopImmediatePropagation: function() {
var e = this.originalEvent;
this.isImmediatePropagationStopped = returnTrue; this.isImmediatePropagationStopped = returnTrue;
if ( e && e.stopImmediatePropagation ) {
e.stopImmediatePropagation();
}
this.stopPropagation(); this.stopPropagation();
} }
}; };
...@@ -4673,7 +4739,9 @@ jQuery.Event.prototype = { ...@@ -4673,7 +4739,9 @@ jQuery.Event.prototype = {
// Support: Chrome 15+ // Support: Chrome 15+
jQuery.each({ jQuery.each({
mouseenter: "mouseover", mouseenter: "mouseover",
mouseleave: "mouseout" mouseleave: "mouseout",
pointerenter: "pointerover",
pointerleave: "pointerout"
}, function( orig, fix ) { }, function( orig, fix ) {
jQuery.event.special[ orig ] = { jQuery.event.special[ orig ] = {
delegateType: fix, delegateType: fix,
...@@ -4697,8 +4765,8 @@ jQuery.each({ ...@@ -4697,8 +4765,8 @@ jQuery.each({
}; };
}); });
// Create "bubbling" focus and blur events
// Support: Firefox, Chrome, Safari // Support: Firefox, Chrome, Safari
// Create "bubbling" focus and blur events
if ( !support.focusinBubbles ) { if ( !support.focusinBubbles ) {
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
...@@ -4851,7 +4919,7 @@ var ...@@ -4851,7 +4919,7 @@ var
// We have to close these tags to support XHTML (#13200) // We have to close these tags to support XHTML (#13200)
wrapMap = { wrapMap = {
// Support: IE 9 // Support: IE9
option: [ 1, "<select multiple='multiple'>", "</select>" ], option: [ 1, "<select multiple='multiple'>", "</select>" ],
thead: [ 1, "<table>", "</table>" ], thead: [ 1, "<table>", "</table>" ],
...@@ -4862,7 +4930,7 @@ var ...@@ -4862,7 +4930,7 @@ var
_default: [ 0, "", "" ] _default: [ 0, "", "" ]
}; };
// Support: IE 9 // Support: IE9
wrapMap.optgroup = wrapMap.option; wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
...@@ -4952,7 +5020,7 @@ function getAll( context, tag ) { ...@@ -4952,7 +5020,7 @@ function getAll( context, tag ) {
ret; ret;
} }
// Support: IE >= 9 // Fix IE bugs, see support tests
function fixInput( src, dest ) { function fixInput( src, dest ) {
var nodeName = dest.nodeName.toLowerCase(); var nodeName = dest.nodeName.toLowerCase();
...@@ -4972,8 +5040,7 @@ jQuery.extend({ ...@@ -4972,8 +5040,7 @@ jQuery.extend({
clone = elem.cloneNode( true ), clone = elem.cloneNode( true ),
inPage = jQuery.contains( elem.ownerDocument, elem ); inPage = jQuery.contains( elem.ownerDocument, elem );
// Support: IE >= 9 // Fix IE cloning issues
// Fix Cloning issues
if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
!jQuery.isXMLDoc( elem ) ) { !jQuery.isXMLDoc( elem ) ) {
...@@ -5024,8 +5091,8 @@ jQuery.extend({ ...@@ -5024,8 +5091,8 @@ jQuery.extend({
// Add nodes directly // Add nodes directly
if ( jQuery.type( elem ) === "object" ) { if ( jQuery.type( elem ) === "object" ) {
// Support: QtWebKit // Support: QtWebKit, PhantomJS
// jQuery.merge because push.apply(_, arraylike) throws // push.apply(_, arraylike) throws on ancient WebKit
jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
// Convert non-html into a text node // Convert non-html into a text node
...@@ -5047,15 +5114,14 @@ jQuery.extend({ ...@@ -5047,15 +5114,14 @@ jQuery.extend({
tmp = tmp.lastChild; tmp = tmp.lastChild;
} }
// Support: QtWebKit // Support: QtWebKit, PhantomJS
// jQuery.merge because push.apply(_, arraylike) throws // push.apply(_, arraylike) throws on ancient WebKit
jQuery.merge( nodes, tmp.childNodes ); jQuery.merge( nodes, tmp.childNodes );
// Remember the top-level container // Remember the top-level container
tmp = fragment.firstChild; tmp = fragment.firstChild;
// Fixes #12346 // Ensure the created nodes are orphaned (#12392)
// Support: Webkit, IE
tmp.textContent = ""; tmp.textContent = "";
} }
} }
...@@ -5098,7 +5164,7 @@ jQuery.extend({ ...@@ -5098,7 +5164,7 @@ jQuery.extend({
}, },
cleanData: function( elems ) { cleanData: function( elems ) {
var data, elem, events, type, key, j, var data, elem, type, key,
special = jQuery.event.special, special = jQuery.event.special,
i = 0; i = 0;
...@@ -5107,9 +5173,8 @@ jQuery.extend({ ...@@ -5107,9 +5173,8 @@ jQuery.extend({
key = elem[ data_priv.expando ]; key = elem[ data_priv.expando ];
if ( key && (data = data_priv.cache[ key ]) ) { if ( key && (data = data_priv.cache[ key ]) ) {
events = Object.keys( data.events || {} ); if ( data.events ) {
if ( events.length ) { for ( type in data.events ) {
for ( j = 0; (type = events[j]) !== undefined; j++ ) {
if ( special[ type ] ) { if ( special[ type ] ) {
jQuery.event.remove( elem, type ); jQuery.event.remove( elem, type );
...@@ -5412,14 +5477,15 @@ var iframe, ...@@ -5412,14 +5477,15 @@ var iframe,
*/ */
// Called only from within defaultDisplay // Called only from within defaultDisplay
function actualDisplay( name, doc ) { function actualDisplay( name, doc ) {
var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), var style,
elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
// getDefaultComputedStyle might be reliably used only on attached element // getDefaultComputedStyle might be reliably used only on attached element
display = window.getDefaultComputedStyle ? display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ?
// Use of this method is a temporary fix (more like optmization) until something better comes along, // Use of this method is a temporary fix (more like optimization) until something better comes along,
// since it was removed from specification and supported only in FF // since it was removed from specification and supported only in FF
window.getDefaultComputedStyle( elem[ 0 ] ).display : jQuery.css( elem[ 0 ], "display" ); style.display : jQuery.css( elem[ 0 ], "display" );
// We don't have any data stored on the element, // We don't have any data stored on the element,
// so use "detach" method as fast way to get rid of the element // so use "detach" method as fast way to get rid of the element
...@@ -5467,7 +5533,14 @@ var rmargin = (/^margin/); ...@@ -5467,7 +5533,14 @@ var rmargin = (/^margin/);
var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
var getStyles = function( elem ) { var getStyles = function( elem ) {
return elem.ownerDocument.defaultView.getComputedStyle( elem, null ); // Support: IE<=11+, Firefox<=30+ (#15098, #14150)
// IE throws on elements created in popups
// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
if ( elem.ownerDocument.defaultView.opener ) {
return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
}
return window.getComputedStyle( elem, null );
}; };
...@@ -5479,7 +5552,7 @@ function curCSS( elem, name, computed ) { ...@@ -5479,7 +5552,7 @@ function curCSS( elem, name, computed ) {
computed = computed || getStyles( elem ); computed = computed || getStyles( elem );
// Support: IE9 // Support: IE9
// getPropertyValue is only needed for .css('filter') in IE9, see #12537 // getPropertyValue is only needed for .css('filter') (#12537)
if ( computed ) { if ( computed ) {
ret = computed.getPropertyValue( name ) || computed[ name ]; ret = computed.getPropertyValue( name ) || computed[ name ];
} }
...@@ -5525,15 +5598,13 @@ function addGetHookIf( conditionFn, hookFn ) { ...@@ -5525,15 +5598,13 @@ function addGetHookIf( conditionFn, hookFn ) {
return { return {
get: function() { get: function() {
if ( conditionFn() ) { if ( conditionFn() ) {
// Hook not needed (or it's not possible to use it due to missing dependency), // Hook not needed (or it's not possible to use it due
// remove it. // to missing dependency), remove it.
// Since there are no other hooks for marginRight, remove the whole object.
delete this.get; delete this.get;
return; return;
} }
// Hook needed; redefine it so that the support test is not executed again. // Hook needed; redefine it so that the support test is not executed again.
return (this.get = hookFn).apply( this, arguments ); return (this.get = hookFn).apply( this, arguments );
} }
}; };
...@@ -5542,28 +5613,34 @@ function addGetHookIf( conditionFn, hookFn ) { ...@@ -5542,28 +5613,34 @@ function addGetHookIf( conditionFn, hookFn ) {
(function() { (function() {
var pixelPositionVal, boxSizingReliableVal, var pixelPositionVal, boxSizingReliableVal,
// Support: Firefox, Android 2.3 (Prefixed box-sizing versions).
divReset = "padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;" +
"-moz-box-sizing:content-box;box-sizing:content-box",
docElem = document.documentElement, docElem = document.documentElement,
container = document.createElement( "div" ), container = document.createElement( "div" ),
div = document.createElement( "div" ); div = document.createElement( "div" );
if ( !div.style ) {
return;
}
// Support: IE9-11+
// Style of cloned element affects source element cloned (#8908)
div.style.backgroundClip = "content-box"; div.style.backgroundClip = "content-box";
div.cloneNode( true ).style.backgroundClip = ""; div.cloneNode( true ).style.backgroundClip = "";
support.clearCloneStyle = div.style.backgroundClip === "content-box"; support.clearCloneStyle = div.style.backgroundClip === "content-box";
container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;" + container.style.cssText = "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;" +
"margin-top:1px"; "position:absolute";
container.appendChild( div ); container.appendChild( div );
// Executing both pixelPosition & boxSizingReliable tests require only one layout // Executing both pixelPosition & boxSizingReliable tests require only one layout
// so they're executed at the same time to save the second computation. // so they're executed at the same time to save the second computation.
function computePixelPositionAndBoxSizingReliable() { function computePixelPositionAndBoxSizingReliable() {
// Support: Firefox, Android 2.3 (Prefixed box-sizing versions). div.style.cssText =
div.style.cssText = "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" + // Support: Firefox<29, Android 2.3
"box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;" + // Vendor-prefix box-sizing
"position:absolute;top:1%"; "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" +
"box-sizing:border-box;display:block;margin-top:1%;top:1%;" +
"border:1px;padding:1px;width:4px;position:absolute";
div.innerHTML = "";
docElem.appendChild( container ); docElem.appendChild( container );
var divStyle = window.getComputedStyle( div, null ); var divStyle = window.getComputedStyle( div, null );
...@@ -5573,10 +5650,12 @@ function addGetHookIf( conditionFn, hookFn ) { ...@@ -5573,10 +5650,12 @@ function addGetHookIf( conditionFn, hookFn ) {
docElem.removeChild( container ); docElem.removeChild( container );
} }
// Use window.getComputedStyle because jsdom on node.js will break without it. // Support: node.js jsdom
// Don't assume that getComputedStyle is a property of the global object
if ( window.getComputedStyle ) { if ( window.getComputedStyle ) {
jQuery.extend(support, { jQuery.extend( support, {
pixelPosition: function() { pixelPosition: function() {
// This test is executed only once but we still do memoizing // This test is executed only once but we still do memoizing
// since we can use the boxSizingReliable pre-computing. // since we can use the boxSizingReliable pre-computing.
// No need to check if the test was already performed, though. // No need to check if the test was already performed, though.
...@@ -5590,6 +5669,7 @@ function addGetHookIf( conditionFn, hookFn ) { ...@@ -5590,6 +5669,7 @@ function addGetHookIf( conditionFn, hookFn ) {
return boxSizingReliableVal; return boxSizingReliableVal;
}, },
reliableMarginRight: function() { reliableMarginRight: function() {
// Support: Android 2.3 // Support: Android 2.3
// Check if div with explicit width and no margin-right incorrectly // Check if div with explicit width and no margin-right incorrectly
// gets computed margin-right based on width of container. (#3333) // gets computed margin-right based on width of container. (#3333)
...@@ -5597,7 +5677,13 @@ function addGetHookIf( conditionFn, hookFn ) { ...@@ -5597,7 +5677,13 @@ function addGetHookIf( conditionFn, hookFn ) {
// This support function is only executed once so no memoizing is needed. // This support function is only executed once so no memoizing is needed.
var ret, var ret,
marginDiv = div.appendChild( document.createElement( "div" ) ); marginDiv = div.appendChild( document.createElement( "div" ) );
marginDiv.style.cssText = div.style.cssText = divReset;
// Reset CSS: box-sizing; display; margin; border; padding
marginDiv.style.cssText = div.style.cssText =
// Support: Firefox<29, Android 2.3
// Vendor-prefix box-sizing
"-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
"box-sizing:content-box;display:block;margin:0;border:0;padding:0";
marginDiv.style.marginRight = marginDiv.style.width = "0"; marginDiv.style.marginRight = marginDiv.style.width = "0";
div.style.width = "1px"; div.style.width = "1px";
docElem.appendChild( container ); docElem.appendChild( container );
...@@ -5605,9 +5691,7 @@ function addGetHookIf( conditionFn, hookFn ) { ...@@ -5605,9 +5691,7 @@ function addGetHookIf( conditionFn, hookFn ) {
ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight ); ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight );
docElem.removeChild( container ); docElem.removeChild( container );
div.removeChild( marginDiv );
// Clean up the div for other support tests.
div.innerHTML = "";
return ret; return ret;
} }
...@@ -5639,29 +5723,29 @@ jQuery.swap = function( elem, options, callback, args ) { ...@@ -5639,29 +5723,29 @@ jQuery.swap = function( elem, options, callback, args ) {
var var
// swappable if display is none or starts with table except "table", "table-cell", or "table-caption" // Swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
rdisplayswap = /^(none|table(?!-c[ea]).+)/, rdisplayswap = /^(none|table(?!-c[ea]).+)/,
rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ), rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ), rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
cssShow = { position: "absolute", visibility: "hidden", display: "block" }, cssShow = { position: "absolute", visibility: "hidden", display: "block" },
cssNormalTransform = { cssNormalTransform = {
letterSpacing: 0, letterSpacing: "0",
fontWeight: 400 fontWeight: "400"
}, },
cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
// return a css property mapped to a potentially vendor prefixed property // Return a css property mapped to a potentially vendor prefixed property
function vendorPropName( style, name ) { function vendorPropName( style, name ) {
// shortcut for names that are not vendor prefixed // Shortcut for names that are not vendor prefixed
if ( name in style ) { if ( name in style ) {
return name; return name;
} }
// check for vendor prefixed names // Check for vendor prefixed names
var capName = name[0].toUpperCase() + name.slice(1), var capName = name[0].toUpperCase() + name.slice(1),
origName = name, origName = name,
i = cssPrefixes.length; i = cssPrefixes.length;
...@@ -5694,7 +5778,7 @@ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { ...@@ -5694,7 +5778,7 @@ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
val = 0; val = 0;
for ( ; i < 4; i += 2 ) { for ( ; i < 4; i += 2 ) {
// both box models exclude margin, so add it if we want it // Both box models exclude margin, so add it if we want it
if ( extra === "margin" ) { if ( extra === "margin" ) {
val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
} }
...@@ -5705,15 +5789,15 @@ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { ...@@ -5705,15 +5789,15 @@ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
} }
// at this point, extra isn't border nor margin, so remove border // At this point, extra isn't border nor margin, so remove border
if ( extra !== "margin" ) { if ( extra !== "margin" ) {
val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
} }
} else { } else {
// at this point, extra isn't content, so add padding // At this point, extra isn't content, so add padding
val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
// at this point, extra isn't content nor padding, so add border // At this point, extra isn't content nor padding, so add border
if ( extra !== "padding" ) { if ( extra !== "padding" ) {
val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
} }
...@@ -5731,7 +5815,7 @@ function getWidthOrHeight( elem, name, extra ) { ...@@ -5731,7 +5815,7 @@ function getWidthOrHeight( elem, name, extra ) {
styles = getStyles( elem ), styles = getStyles( elem ),
isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
// some non-html elements return undefined for offsetWidth, so check for null/undefined // Some non-html elements return undefined for offsetWidth, so check for null/undefined
// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
if ( val <= 0 || val == null ) { if ( val <= 0 || val == null ) {
...@@ -5746,7 +5830,7 @@ function getWidthOrHeight( elem, name, extra ) { ...@@ -5746,7 +5830,7 @@ function getWidthOrHeight( elem, name, extra ) {
return val; return val;
} }
// we need the check for style in case a browser which returns unreliable values // Check for style in case a browser which returns unreliable values
// for getComputedStyle silently falls back to the reliable elem.style // for getComputedStyle silently falls back to the reliable elem.style
valueIsBorderBox = isBorderBox && valueIsBorderBox = isBorderBox &&
( support.boxSizingReliable() || val === elem.style[ name ] ); ( support.boxSizingReliable() || val === elem.style[ name ] );
...@@ -5755,7 +5839,7 @@ function getWidthOrHeight( elem, name, extra ) { ...@@ -5755,7 +5839,7 @@ function getWidthOrHeight( elem, name, extra ) {
val = parseFloat( val ) || 0; val = parseFloat( val ) || 0;
} }
// use the active box-sizing model to add/subtract irrelevant styles // Use the active box-sizing model to add/subtract irrelevant styles
return ( val + return ( val +
augmentWidthOrHeight( augmentWidthOrHeight(
elem, elem,
...@@ -5795,13 +5879,10 @@ function showHide( elements, show ) { ...@@ -5795,13 +5879,10 @@ function showHide( elements, show ) {
values[ index ] = data_priv.access( elem, "olddisplay", defaultDisplay(elem.nodeName) ); values[ index ] = data_priv.access( elem, "olddisplay", defaultDisplay(elem.nodeName) );
} }
} else { } else {
hidden = isHidden( elem );
if ( !values[ index ] ) { if ( display !== "none" || !hidden ) {
hidden = isHidden( elem ); data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
if ( display && display !== "none" || !hidden ) {
data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css(elem, "display") );
}
} }
} }
} }
...@@ -5822,12 +5903,14 @@ function showHide( elements, show ) { ...@@ -5822,12 +5903,14 @@ function showHide( elements, show ) {
} }
jQuery.extend({ jQuery.extend({
// Add in style property hooks for overriding the default // Add in style property hooks for overriding the default
// behavior of getting and setting a style property // behavior of getting and setting a style property
cssHooks: { cssHooks: {
opacity: { opacity: {
get: function( elem, computed ) { get: function( elem, computed ) {
if ( computed ) { if ( computed ) {
// We should always get a number back from opacity // We should always get a number back from opacity
var ret = curCSS( elem, "opacity" ); var ret = curCSS( elem, "opacity" );
return ret === "" ? "1" : ret; return ret === "" ? "1" : ret;
...@@ -5840,6 +5923,8 @@ jQuery.extend({ ...@@ -5840,6 +5923,8 @@ jQuery.extend({
cssNumber: { cssNumber: {
"columnCount": true, "columnCount": true,
"fillOpacity": true, "fillOpacity": true,
"flexGrow": true,
"flexShrink": true,
"fontWeight": true, "fontWeight": true,
"lineHeight": true, "lineHeight": true,
"opacity": true, "opacity": true,
...@@ -5853,12 +5938,12 @@ jQuery.extend({ ...@@ -5853,12 +5938,12 @@ jQuery.extend({
// Add in properties whose names you wish to fix before // Add in properties whose names you wish to fix before
// setting or getting the value // setting or getting the value
cssProps: { cssProps: {
// normalize float css property
"float": "cssFloat" "float": "cssFloat"
}, },
// Get and set the style property on a DOM Node // Get and set the style property on a DOM Node
style: function( elem, name, value, extra ) { style: function( elem, name, value, extra ) {
// Don't set styles on text and comment nodes // Don't set styles on text and comment nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
return; return;
...@@ -5871,42 +5956,38 @@ jQuery.extend({ ...@@ -5871,42 +5956,38 @@ jQuery.extend({
name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
// gets hook for the prefixed version // Gets hook for the prefixed version, then unprefixed version
// followed by the unprefixed version
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
// Check if we're setting a value // Check if we're setting a value
if ( value !== undefined ) { if ( value !== undefined ) {
type = typeof value; type = typeof value;
// convert relative number strings (+= or -=) to relative numbers. #7345 // Convert "+=" or "-=" to relative numbers (#7345)
if ( type === "string" && (ret = rrelNum.exec( value )) ) { if ( type === "string" && (ret = rrelNum.exec( value )) ) {
value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
// Fixes bug #9237 // Fixes bug #9237
type = "number"; type = "number";
} }
// Make sure that null and NaN values aren't set. See: #7116 // Make sure that null and NaN values aren't set (#7116)
if ( value == null || value !== value ) { if ( value == null || value !== value ) {
return; return;
} }
// If a number was passed in, add 'px' to the (except for certain CSS properties) // If a number, add 'px' to the (except for certain CSS properties)
if ( type === "number" && !jQuery.cssNumber[ origName ] ) { if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
value += "px"; value += "px";
} }
// Fixes #8908, it can be done more correctly by specifying setters in cssHooks, // Support: IE9-11+
// but it would mean to define eight (for every problematic property) identical functions // background-* props affect original clone's values
if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
style[ name ] = "inherit"; style[ name ] = "inherit";
} }
// If a hook was provided, use that value, otherwise just set the specified value // If a hook was provided, use that value, otherwise just set the specified value
if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
// Support: Chrome, Safari
// Setting style to blank string required to delete "style: x !important;"
style[ name ] = "";
style[ name ] = value; style[ name ] = value;
} }
...@@ -5928,8 +6009,7 @@ jQuery.extend({ ...@@ -5928,8 +6009,7 @@ jQuery.extend({
// Make sure that we're working with the right name // Make sure that we're working with the right name
name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
// gets hook for the prefixed version // Try prefixed name followed by the unprefixed name
// followed by the unprefixed version
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
// If a hook was provided get the computed value from there // If a hook was provided get the computed value from there
...@@ -5942,12 +6022,12 @@ jQuery.extend({ ...@@ -5942,12 +6022,12 @@ jQuery.extend({
val = curCSS( elem, name, styles ); val = curCSS( elem, name, styles );
} }
//convert "normal" to computed value // Convert "normal" to computed value
if ( val === "normal" && name in cssNormalTransform ) { if ( val === "normal" && name in cssNormalTransform ) {
val = cssNormalTransform[ name ]; val = cssNormalTransform[ name ];
} }
// Return, converting to number if forced or a qualifier was provided and val looks numeric // Make numeric if forced or a qualifier was provided and val looks numeric
if ( extra === "" || extra ) { if ( extra === "" || extra ) {
num = parseFloat( val ); num = parseFloat( val );
return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
...@@ -5960,9 +6040,10 @@ jQuery.each([ "height", "width" ], function( i, name ) { ...@@ -5960,9 +6040,10 @@ jQuery.each([ "height", "width" ], function( i, name ) {
jQuery.cssHooks[ name ] = { jQuery.cssHooks[ name ] = {
get: function( elem, computed, extra ) { get: function( elem, computed, extra ) {
if ( computed ) { if ( computed ) {
// certain elements can have dimension info if we invisibly show them
// however, it must have a current display style that would benefit from this // Certain elements can have dimension info if we invisibly show them
return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ? // but it must have a current display style that would benefit
return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ?
jQuery.swap( elem, cssShow, function() { jQuery.swap( elem, cssShow, function() {
return getWidthOrHeight( elem, name, extra ); return getWidthOrHeight( elem, name, extra );
}) : }) :
...@@ -5989,8 +6070,6 @@ jQuery.each([ "height", "width" ], function( i, name ) { ...@@ -5989,8 +6070,6 @@ jQuery.each([ "height", "width" ], function( i, name ) {
jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight, jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
function( elem, computed ) { function( elem, computed ) {
if ( computed ) { if ( computed ) {
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
// Work around by temporarily setting element display to inline-block
return jQuery.swap( elem, { "display": "inline-block" }, return jQuery.swap( elem, { "display": "inline-block" },
curCSS, [ elem, "marginRight" ] ); curCSS, [ elem, "marginRight" ] );
} }
...@@ -6008,7 +6087,7 @@ jQuery.each({ ...@@ -6008,7 +6087,7 @@ jQuery.each({
var i = 0, var i = 0,
expanded = {}, expanded = {},
// assumes a single number if not a string // Assumes a single number if not a string
parts = typeof value === "string" ? value.split(" ") : [ value ]; parts = typeof value === "string" ? value.split(" ") : [ value ];
for ( ; i < 4; i++ ) { for ( ; i < 4; i++ ) {
...@@ -6131,17 +6210,18 @@ Tween.propHooks = { ...@@ -6131,17 +6210,18 @@ Tween.propHooks = {
return tween.elem[ tween.prop ]; return tween.elem[ tween.prop ];
} }
// passing an empty string as a 3rd parameter to .css will automatically // Passing an empty string as a 3rd parameter to .css will automatically
// attempt a parseFloat and fallback to a string if the parse fails // attempt a parseFloat and fallback to a string if the parse fails.
// so, simple values such as "10px" are parsed to Float. // Simple values such as "10px" are parsed to Float;
// complex values such as "rotate(1rad)" are returned as is. // complex values such as "rotate(1rad)" are returned as-is.
result = jQuery.css( tween.elem, tween.prop, "" ); result = jQuery.css( tween.elem, tween.prop, "" );
// Empty strings, null, undefined and "auto" are converted to 0. // Empty strings, null, undefined and "auto" are converted to 0.
return !result || result === "auto" ? 0 : result; return !result || result === "auto" ? 0 : result;
}, },
set: function( tween ) { set: function( tween ) {
// use step hook for back compat - use cssHook if its there - use .style if its // Use step hook for back compat.
// available and use plain properties where available // Use cssHook if its there.
// Use .style if available and use plain properties where available.
if ( jQuery.fx.step[ tween.prop ] ) { if ( jQuery.fx.step[ tween.prop ] ) {
jQuery.fx.step[ tween.prop ]( tween ); jQuery.fx.step[ tween.prop ]( tween );
} else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
...@@ -6155,7 +6235,6 @@ Tween.propHooks = { ...@@ -6155,7 +6235,6 @@ Tween.propHooks = {
// Support: IE9 // Support: IE9
// Panic based approach to setting things on disconnected nodes // Panic based approach to setting things on disconnected nodes
Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
set: function( tween ) { set: function( tween ) {
if ( tween.elem.nodeType && tween.elem.parentNode ) { if ( tween.elem.nodeType && tween.elem.parentNode ) {
...@@ -6211,16 +6290,16 @@ var ...@@ -6211,16 +6290,16 @@ var
start = +target || 1; start = +target || 1;
do { do {
// If previous iteration zeroed out, double until we get *something* // If previous iteration zeroed out, double until we get *something*.
// Use a string for doubling factor so we don't accidentally see scale as unchanged below // Use string for doubling so we don't accidentally see scale as unchanged below
scale = scale || ".5"; scale = scale || ".5";
// Adjust and apply // Adjust and apply
start = start / scale; start = start / scale;
jQuery.style( tween.elem, prop, start + unit ); jQuery.style( tween.elem, prop, start + unit );
// Update scale, tolerating zero or NaN from tween.cur() // Update scale, tolerating zero or NaN from tween.cur(),
// And breaking the loop if scale is unchanged or perfect, or if we've just had enough // break the loop if scale is unchanged or perfect, or if we've just had enough
} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
} }
...@@ -6252,8 +6331,8 @@ function genFx( type, includeWidth ) { ...@@ -6252,8 +6331,8 @@ function genFx( type, includeWidth ) {
i = 0, i = 0,
attrs = { height: type }; attrs = { height: type };
// if we include width, step value is 1 to do all cssExpand values, // If we include width, step value is 1 to do all cssExpand values,
// if we don't include width, step value is 2 to skip over Left and Right // otherwise step value is 2 to skip over Left and Right
includeWidth = includeWidth ? 1 : 0; includeWidth = includeWidth ? 1 : 0;
for ( ; i < 4 ; i += 2 - includeWidth ) { for ( ; i < 4 ; i += 2 - includeWidth ) {
which = cssExpand[ i ]; which = cssExpand[ i ];
...@@ -6275,7 +6354,7 @@ function createTween( value, prop, animation ) { ...@@ -6275,7 +6354,7 @@ function createTween( value, prop, animation ) {
for ( ; index < length; index++ ) { for ( ; index < length; index++ ) {
if ( (tween = collection[ index ].call( animation, prop, value )) ) { if ( (tween = collection[ index ].call( animation, prop, value )) ) {
// we're done with this property // We're done with this property
return tween; return tween;
} }
} }
...@@ -6283,14 +6362,14 @@ function createTween( value, prop, animation ) { ...@@ -6283,14 +6362,14 @@ function createTween( value, prop, animation ) {
function defaultPrefilter( elem, props, opts ) { function defaultPrefilter( elem, props, opts ) {
/* jshint validthis: true */ /* jshint validthis: true */
var prop, value, toggle, tween, hooks, oldfire, display, var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
anim = this, anim = this,
orig = {}, orig = {},
style = elem.style, style = elem.style,
hidden = elem.nodeType && isHidden( elem ), hidden = elem.nodeType && isHidden( elem ),
dataShow = data_priv.get( elem, "fxshow" ); dataShow = data_priv.get( elem, "fxshow" );
// handle queue: false promises // Handle queue: false promises
if ( !opts.queue ) { if ( !opts.queue ) {
hooks = jQuery._queueHooks( elem, "fx" ); hooks = jQuery._queueHooks( elem, "fx" );
if ( hooks.unqueued == null ) { if ( hooks.unqueued == null ) {
...@@ -6305,8 +6384,7 @@ function defaultPrefilter( elem, props, opts ) { ...@@ -6305,8 +6384,7 @@ function defaultPrefilter( elem, props, opts ) {
hooks.unqueued++; hooks.unqueued++;
anim.always(function() { anim.always(function() {
// doing this makes sure that the complete handler will be called // Ensure the complete handler is called before this completes
// before this completes
anim.always(function() { anim.always(function() {
hooks.unqueued--; hooks.unqueued--;
if ( !jQuery.queue( elem, "fx" ).length ) { if ( !jQuery.queue( elem, "fx" ).length ) {
...@@ -6316,7 +6394,7 @@ function defaultPrefilter( elem, props, opts ) { ...@@ -6316,7 +6394,7 @@ function defaultPrefilter( elem, props, opts ) {
}); });
} }
// height/width overflow pass // Height/width overflow pass
if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
// Make sure that nothing sneaks out // Make sure that nothing sneaks out
// Record all 3 overflow attributes because IE9-10 do not // Record all 3 overflow attributes because IE9-10 do not
...@@ -6327,13 +6405,12 @@ function defaultPrefilter( elem, props, opts ) { ...@@ -6327,13 +6405,12 @@ function defaultPrefilter( elem, props, opts ) {
// Set display property to inline-block for height/width // Set display property to inline-block for height/width
// animations on inline elements that are having width/height animated // animations on inline elements that are having width/height animated
display = jQuery.css( elem, "display" ); display = jQuery.css( elem, "display" );
// Get default display if display is currently "none"
if ( display === "none" ) {
display = defaultDisplay( elem.nodeName );
}
if ( display === "inline" &&
jQuery.css( elem, "float" ) === "none" ) {
// Test default display if display is currently "none"
checkDisplay = display === "none" ?
data_priv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
style.display = "inline-block"; style.display = "inline-block";
} }
} }
...@@ -6363,6 +6440,10 @@ function defaultPrefilter( elem, props, opts ) { ...@@ -6363,6 +6440,10 @@ function defaultPrefilter( elem, props, opts ) {
} }
} }
orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
// Any non-fx value stops us from restoring the original display value
} else {
display = undefined;
} }
} }
...@@ -6375,7 +6456,7 @@ function defaultPrefilter( elem, props, opts ) { ...@@ -6375,7 +6456,7 @@ function defaultPrefilter( elem, props, opts ) {
dataShow = data_priv.access( elem, "fxshow", {} ); dataShow = data_priv.access( elem, "fxshow", {} );
} }
// store state if its toggle - enables .stop().toggle() to "reverse" // Store state if its toggle - enables .stop().toggle() to "reverse"
if ( toggle ) { if ( toggle ) {
dataShow.hidden = !hidden; dataShow.hidden = !hidden;
} }
...@@ -6405,6 +6486,10 @@ function defaultPrefilter( elem, props, opts ) { ...@@ -6405,6 +6486,10 @@ function defaultPrefilter( elem, props, opts ) {
} }
} }
} }
// If this is a noop like .hide().hide(), restore an overwritten display value
} else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
style.display = display;
} }
} }
...@@ -6431,8 +6516,8 @@ function propFilter( props, specialEasing ) { ...@@ -6431,8 +6516,8 @@ function propFilter( props, specialEasing ) {
value = hooks.expand( value ); value = hooks.expand( value );
delete props[ name ]; delete props[ name ];
// not quite $.extend, this wont overwrite keys already present. // Not quite $.extend, this won't overwrite existing keys.
// also - reusing 'index' from above because we have the correct "name" // Reusing 'index' because we have the correct "name"
for ( index in value ) { for ( index in value ) {
if ( !( index in props ) ) { if ( !( index in props ) ) {
props[ index ] = value[ index ]; props[ index ] = value[ index ];
...@@ -6451,7 +6536,7 @@ function Animation( elem, properties, options ) { ...@@ -6451,7 +6536,7 @@ function Animation( elem, properties, options ) {
index = 0, index = 0,
length = animationPrefilters.length, length = animationPrefilters.length,
deferred = jQuery.Deferred().always( function() { deferred = jQuery.Deferred().always( function() {
// don't match elem in the :animated selector // Don't match elem in the :animated selector
delete tick.elem; delete tick.elem;
}), }),
tick = function() { tick = function() {
...@@ -6460,7 +6545,8 @@ function Animation( elem, properties, options ) { ...@@ -6460,7 +6545,8 @@ function Animation( elem, properties, options ) {
} }
var currentTime = fxNow || createFxNow(), var currentTime = fxNow || createFxNow(),
remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) // Support: Android 2.3
// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
temp = remaining / animation.duration || 0, temp = remaining / animation.duration || 0,
percent = 1 - temp, percent = 1 - temp,
index = 0, index = 0,
...@@ -6496,7 +6582,7 @@ function Animation( elem, properties, options ) { ...@@ -6496,7 +6582,7 @@ function Animation( elem, properties, options ) {
}, },
stop: function( gotoEnd ) { stop: function( gotoEnd ) {
var index = 0, var index = 0,
// if we are going to the end, we want to run all the tweens // If we are going to the end, we want to run all the tweens
// otherwise we skip this part // otherwise we skip this part
length = gotoEnd ? animation.tweens.length : 0; length = gotoEnd ? animation.tweens.length : 0;
if ( stopped ) { if ( stopped ) {
...@@ -6507,8 +6593,7 @@ function Animation( elem, properties, options ) { ...@@ -6507,8 +6593,7 @@ function Animation( elem, properties, options ) {
animation.tweens[ index ].run( 1 ); animation.tweens[ index ].run( 1 );
} }
// resolve when we played the last frame // Resolve when we played the last frame; otherwise, reject
// otherwise, reject
if ( gotoEnd ) { if ( gotoEnd ) {
deferred.resolveWith( elem, [ animation, gotoEnd ] ); deferred.resolveWith( elem, [ animation, gotoEnd ] );
} else { } else {
...@@ -6590,7 +6675,7 @@ jQuery.speed = function( speed, easing, fn ) { ...@@ -6590,7 +6675,7 @@ jQuery.speed = function( speed, easing, fn ) {
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
// normalize opt.queue - true/undefined/null -> "fx" // Normalize opt.queue - true/undefined/null -> "fx"
if ( opt.queue == null || opt.queue === true ) { if ( opt.queue == null || opt.queue === true ) {
opt.queue = "fx"; opt.queue = "fx";
} }
...@@ -6614,10 +6699,10 @@ jQuery.speed = function( speed, easing, fn ) { ...@@ -6614,10 +6699,10 @@ jQuery.speed = function( speed, easing, fn ) {
jQuery.fn.extend({ jQuery.fn.extend({
fadeTo: function( speed, to, easing, callback ) { fadeTo: function( speed, to, easing, callback ) {
// show any hidden elements after setting opacity to 0 // Show any hidden elements after setting opacity to 0
return this.filter( isHidden ).css( "opacity", 0 ).show() return this.filter( isHidden ).css( "opacity", 0 ).show()
// animate to the value specified // Animate to the value specified
.end().animate({ opacity: to }, speed, easing, callback ); .end().animate({ opacity: to }, speed, easing, callback );
}, },
animate: function( prop, speed, easing, callback ) { animate: function( prop, speed, easing, callback ) {
...@@ -6680,9 +6765,9 @@ jQuery.fn.extend({ ...@@ -6680,9 +6765,9 @@ jQuery.fn.extend({
} }
} }
// start the next in the queue if the last step wasn't forced // Start the next in the queue if the last step wasn't forced.
// timers currently will call their complete callbacks, which will dequeue // Timers currently will call their complete callbacks, which
// but only if they were gotoEnd // will dequeue but only if they were gotoEnd.
if ( dequeue || !gotoEnd ) { if ( dequeue || !gotoEnd ) {
jQuery.dequeue( this, type ); jQuery.dequeue( this, type );
} }
...@@ -6700,17 +6785,17 @@ jQuery.fn.extend({ ...@@ -6700,17 +6785,17 @@ jQuery.fn.extend({
timers = jQuery.timers, timers = jQuery.timers,
length = queue ? queue.length : 0; length = queue ? queue.length : 0;
// enable finishing flag on private data // Enable finishing flag on private data
data.finish = true; data.finish = true;
// empty the queue first // Empty the queue first
jQuery.queue( this, type, [] ); jQuery.queue( this, type, [] );
if ( hooks && hooks.stop ) { if ( hooks && hooks.stop ) {
hooks.stop.call( this, true ); hooks.stop.call( this, true );
} }
// look for any active animations, and finish them // Look for any active animations, and finish them
for ( index = timers.length; index--; ) { for ( index = timers.length; index--; ) {
if ( timers[ index ].elem === this && timers[ index ].queue === type ) { if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
timers[ index ].anim.stop( true ); timers[ index ].anim.stop( true );
...@@ -6718,14 +6803,14 @@ jQuery.fn.extend({ ...@@ -6718,14 +6803,14 @@ jQuery.fn.extend({
} }
} }
// look for any animations in the old queue and finish them // Look for any animations in the old queue and finish them
for ( index = 0; index < length; index++ ) { for ( index = 0; index < length; index++ ) {
if ( queue[ index ] && queue[ index ].finish ) { if ( queue[ index ] && queue[ index ].finish ) {
queue[ index ].finish.call( this ); queue[ index ].finish.call( this );
} }
} }
// turn off finishing flag // Turn off finishing flag
delete data.finish; delete data.finish;
}); });
} }
...@@ -6828,21 +6913,21 @@ jQuery.fn.delay = function( time, type ) { ...@@ -6828,21 +6913,21 @@ jQuery.fn.delay = function( time, type ) {
input.type = "checkbox"; input.type = "checkbox";
// Support: iOS 5.1, Android 4.x, Android 2.3 // Support: iOS<=5.1, Android<=4.2+
// Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere) // Default value for a checkbox should be "on"
support.checkOn = input.value !== ""; support.checkOn = input.value !== "";
// Must access the parent to make an option select properly // Support: IE<=11+
// Support: IE9, IE10 // Must access selectedIndex to make default options select
support.optSelected = opt.selected; support.optSelected = opt.selected;
// Make sure that the options inside disabled selects aren't marked as disabled // Support: Android<=2.3
// (WebKit marks them as disabled) // Options inside disabled selects are incorrectly marked as disabled
select.disabled = true; select.disabled = true;
support.optDisabled = !opt.disabled; support.optDisabled = !opt.disabled;
// Check if an input maintains its value after becoming a radio // Support: IE<=11+
// Support: IE9, IE10 // An input loses its value after becoming a radio
input = document.createElement( "input" ); input = document.createElement( "input" );
input.value = "t"; input.value = "t";
input.type = "radio"; input.type = "radio";
...@@ -6939,8 +7024,6 @@ jQuery.extend({ ...@@ -6939,8 +7024,6 @@ jQuery.extend({
set: function( elem, value ) { set: function( elem, value ) {
if ( !support.radioValue && value === "radio" && if ( !support.radioValue && value === "radio" &&
jQuery.nodeName( elem, "input" ) ) { jQuery.nodeName( elem, "input" ) ) {
// Setting the type on a radio button after the value resets the value in IE6-9
// Reset value to default in case type is set after value during creation
var val = elem.value; var val = elem.value;
elem.setAttribute( "type", value ); elem.setAttribute( "type", value );
if ( val ) { if ( val ) {
...@@ -7010,7 +7093,7 @@ jQuery.extend({ ...@@ -7010,7 +7093,7 @@ jQuery.extend({
var ret, hooks, notxml, var ret, hooks, notxml,
nType = elem.nodeType; nType = elem.nodeType;
// don't get/set properties on text, comment and attribute nodes // Don't get/set properties on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return; return;
} }
...@@ -7046,8 +7129,6 @@ jQuery.extend({ ...@@ -7046,8 +7129,6 @@ jQuery.extend({
} }
}); });
// Support: IE9+
// Selectedness for an option in an optgroup can be inaccurate
if ( !support.optSelected ) { if ( !support.optSelected ) {
jQuery.propHooks.selected = { jQuery.propHooks.selected = {
get: function( elem ) { get: function( elem ) {
...@@ -7155,7 +7236,7 @@ jQuery.fn.extend({ ...@@ -7155,7 +7236,7 @@ jQuery.fn.extend({
} }
} }
// only assign if different to avoid unneeded rendering. // Only assign if different to avoid unneeded rendering.
finalValue = value ? jQuery.trim( cur ) : ""; finalValue = value ? jQuery.trim( cur ) : "";
if ( elem.className !== finalValue ) { if ( elem.className !== finalValue ) {
elem.className = finalValue; elem.className = finalValue;
...@@ -7182,14 +7263,14 @@ jQuery.fn.extend({ ...@@ -7182,14 +7263,14 @@ jQuery.fn.extend({
return this.each(function() { return this.each(function() {
if ( type === "string" ) { if ( type === "string" ) {
// toggle individual class names // Toggle individual class names
var className, var className,
i = 0, i = 0,
self = jQuery( this ), self = jQuery( this ),
classNames = value.match( rnotwhite ) || []; classNames = value.match( rnotwhite ) || [];
while ( (className = classNames[ i++ ]) ) { while ( (className = classNames[ i++ ]) ) {
// check each className given, space separated list // Check each className given, space separated list
if ( self.hasClass( className ) ) { if ( self.hasClass( className ) ) {
self.removeClass( className ); self.removeClass( className );
} else { } else {
...@@ -7204,7 +7285,7 @@ jQuery.fn.extend({ ...@@ -7204,7 +7285,7 @@ jQuery.fn.extend({
data_priv.set( this, "__className__", this.className ); data_priv.set( this, "__className__", this.className );
} }
// If the element has a class name or if we're passed "false", // If the element has a class name or if we're passed `false`,
// then remove the whole classname (if there was one, the above saved it). // then remove the whole classname (if there was one, the above saved it).
// Otherwise bring back whatever was previously saved (if anything), // Otherwise bring back whatever was previously saved (if anything),
// falling back to the empty string if nothing was stored. // falling back to the empty string if nothing was stored.
...@@ -7248,9 +7329,9 @@ jQuery.fn.extend({ ...@@ -7248,9 +7329,9 @@ jQuery.fn.extend({
ret = elem.value; ret = elem.value;
return typeof ret === "string" ? return typeof ret === "string" ?
// handle most common string cases // Handle most common string cases
ret.replace(rreturn, "") : ret.replace(rreturn, "") :
// handle cases where value is null/undef or number // Handle cases where value is null/undef or number
ret == null ? "" : ret; ret == null ? "" : ret;
} }
...@@ -7297,6 +7378,16 @@ jQuery.fn.extend({ ...@@ -7297,6 +7378,16 @@ jQuery.fn.extend({
jQuery.extend({ jQuery.extend({
valHooks: { valHooks: {
option: {
get: function( elem ) {
var val = jQuery.find.attr( elem, "value" );
return val != null ?
val :
// Support: IE10-11+
// option.text throws exceptions (#14686, #14858)
jQuery.trim( jQuery.text( elem ) );
}
},
select: { select: {
get: function( elem ) { get: function( elem ) {
var value, option, var value, option,
...@@ -7343,12 +7434,12 @@ jQuery.extend({ ...@@ -7343,12 +7434,12 @@ jQuery.extend({
while ( i-- ) { while ( i-- ) {
option = options[ i ]; option = options[ i ];
if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { if ( (option.selected = jQuery.inArray( option.value, values ) >= 0) ) {
optionSet = true; optionSet = true;
} }
} }
// force browsers to behave consistently when non-matching value is set // Force browsers to behave consistently when non-matching value is set
if ( !optionSet ) { if ( !optionSet ) {
elem.selectedIndex = -1; elem.selectedIndex = -1;
} }
...@@ -7369,8 +7460,6 @@ jQuery.each([ "radio", "checkbox" ], function() { ...@@ -7369,8 +7460,6 @@ jQuery.each([ "radio", "checkbox" ], function() {
}; };
if ( !support.checkOn ) { if ( !support.checkOn ) {
jQuery.valHooks[ this ].get = function( elem ) { jQuery.valHooks[ this ].get = function( elem ) {
// Support: Webkit
// "" is returned instead of "on" if a value isn't specified
return elem.getAttribute("value") === null ? "on" : elem.value; return elem.getAttribute("value") === null ? "on" : elem.value;
}; };
} }
...@@ -7452,10 +7541,6 @@ jQuery.parseXML = function( data ) { ...@@ -7452,10 +7541,6 @@ jQuery.parseXML = function( data ) {
var var
// Document location
ajaxLocParts,
ajaxLocation,
rhash = /#.*$/, rhash = /#.*$/,
rts = /([?&])_=[^&]*/, rts = /([?&])_=[^&]*/,
rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
...@@ -7484,22 +7569,13 @@ var ...@@ -7484,22 +7569,13 @@ var
transports = {}, transports = {},
// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
allTypes = "*/".concat("*"); allTypes = "*/".concat( "*" ),
// #8138, IE may throw an exception when accessing // Document location
// a field from window.location if document.domain has been set ajaxLocation = window.location.href,
try {
ajaxLocation = location.href;
} catch( e ) {
// Use the href attribute of an A element
// since IE will modify it given document.location
ajaxLocation = document.createElement( "a" );
ajaxLocation.href = "";
ajaxLocation = ajaxLocation.href;
}
// Segment location into parts // Segment location into parts
ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
function addToPrefiltersOrTransports( structure ) { function addToPrefiltersOrTransports( structure ) {
...@@ -7978,7 +8054,8 @@ jQuery.extend({ ...@@ -7978,7 +8054,8 @@ jQuery.extend({
} }
// We can fire global events as of now if asked to // We can fire global events as of now if asked to
fireGlobals = s.global; // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
fireGlobals = jQuery.event && s.global;
// Watch for a new set of requests // Watch for a new set of requests
if ( fireGlobals && jQuery.active++ === 0 ) { if ( fireGlobals && jQuery.active++ === 0 ) {
...@@ -8051,7 +8128,7 @@ jQuery.extend({ ...@@ -8051,7 +8128,7 @@ jQuery.extend({
return jqXHR.abort(); return jqXHR.abort();
} }
// aborting is no longer a cancellation // Aborting is no longer a cancellation
strAbort = "abort"; strAbort = "abort";
// Install callbacks on deferreds // Install callbacks on deferreds
...@@ -8163,8 +8240,7 @@ jQuery.extend({ ...@@ -8163,8 +8240,7 @@ jQuery.extend({
isSuccess = !error; isSuccess = !error;
} }
} else { } else {
// We extract error from statusText // Extract error from statusText and normalize for non-aborts
// then normalize statusText and status for non-aborts
error = statusText; error = statusText;
if ( status || !statusText ) { if ( status || !statusText ) {
statusText = "error"; statusText = "error";
...@@ -8220,7 +8296,7 @@ jQuery.extend({ ...@@ -8220,7 +8296,7 @@ jQuery.extend({
jQuery.each( [ "get", "post" ], function( i, method ) { jQuery.each( [ "get", "post" ], function( i, method ) {
jQuery[ method ] = function( url, data, callback, type ) { jQuery[ method ] = function( url, data, callback, type ) {
// shift arguments if data argument was omitted // Shift arguments if data argument was omitted
if ( jQuery.isFunction( data ) ) { if ( jQuery.isFunction( data ) ) {
type = type || callback; type = type || callback;
callback = data; callback = data;
...@@ -8237,13 +8313,6 @@ jQuery.each( [ "get", "post" ], function( i, method ) { ...@@ -8237,13 +8313,6 @@ jQuery.each( [ "get", "post" ], function( i, method ) {
}; };
}); });
// Attach a bunch of functions for handling common AJAX events
jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
jQuery.fn[ type ] = function( fn ) {
return this.on( type, fn );
};
});
jQuery._evalUrl = function( url ) { jQuery._evalUrl = function( url ) {
return jQuery.ajax({ return jQuery.ajax({
...@@ -8461,8 +8530,9 @@ var xhrId = 0, ...@@ -8461,8 +8530,9 @@ var xhrId = 0,
// Support: IE9 // Support: IE9
// Open requests must be manually aborted on unload (#5280) // Open requests must be manually aborted on unload (#5280)
if ( window.ActiveXObject ) { // See https://support.microsoft.com/kb/2856746 for more info
jQuery( window ).on( "unload", function() { if ( window.attachEvent ) {
window.attachEvent( "onunload", function() {
for ( var key in xhrCallbacks ) { for ( var key in xhrCallbacks ) {
xhrCallbacks[ key ](); xhrCallbacks[ key ]();
} }
...@@ -8550,10 +8620,15 @@ jQuery.ajaxTransport(function( options ) { ...@@ -8550,10 +8620,15 @@ jQuery.ajaxTransport(function( options ) {
// Create the abort callback // Create the abort callback
callback = xhrCallbacks[ id ] = callback("abort"); callback = xhrCallbacks[ id ] = callback("abort");
// Do send the request try {
// This may raise an exception which is actually // Do send the request (this may raise an exception)
// handled in jQuery.ajax (so no try/catch here) xhr.send( options.hasContent && options.data || null );
xhr.send( options.hasContent && options.data || null ); } catch ( e ) {
// #14683: Only rethrow if this hasn't been notified as an error yet
if ( callback ) {
throw e;
}
}
}, },
abort: function() { abort: function() {
...@@ -8760,7 +8835,7 @@ jQuery.fn.load = function( url, params, callback ) { ...@@ -8760,7 +8835,7 @@ jQuery.fn.load = function( url, params, callback ) {
off = url.indexOf(" "); off = url.indexOf(" ");
if ( off >= 0 ) { if ( off >= 0 ) {
selector = url.slice( off ); selector = jQuery.trim( url.slice( off ) );
url = url.slice( 0, off ); url = url.slice( 0, off );
} }
...@@ -8810,6 +8885,16 @@ jQuery.fn.load = function( url, params, callback ) { ...@@ -8810,6 +8885,16 @@ jQuery.fn.load = function( url, params, callback ) {
// Attach a bunch of functions for handling common AJAX events
jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
jQuery.fn[ type ] = function( fn ) {
return this.on( type, fn );
};
});
jQuery.expr.filters.animated = function( elem ) { jQuery.expr.filters.animated = function( elem ) {
return jQuery.grep(jQuery.timers, function( fn ) { return jQuery.grep(jQuery.timers, function( fn ) {
return elem === fn.elem; return elem === fn.elem;
...@@ -8846,7 +8931,8 @@ jQuery.offset = { ...@@ -8846,7 +8931,8 @@ jQuery.offset = {
calculatePosition = ( position === "absolute" || position === "fixed" ) && calculatePosition = ( position === "absolute" || position === "fixed" ) &&
( curCSSTop + curCSSLeft ).indexOf("auto") > -1; ( curCSSTop + curCSSLeft ).indexOf("auto") > -1;
// Need to be able to calculate position if either top or left is auto and position is either absolute or fixed // Need to be able to calculate position if either
// top or left is auto and position is either absolute or fixed
if ( calculatePosition ) { if ( calculatePosition ) {
curPosition = curElem.position(); curPosition = curElem.position();
curTop = curPosition.top; curTop = curPosition.top;
...@@ -8903,8 +8989,8 @@ jQuery.fn.extend({ ...@@ -8903,8 +8989,8 @@ jQuery.fn.extend({
return box; return box;
} }
// Support: BlackBerry 5, iOS 3 (original iPhone)
// If we don't have gBCR, just use 0,0 rather than error // If we don't have gBCR, just use 0,0 rather than error
// BlackBerry 5, iOS 3 (original iPhone)
if ( typeof elem.getBoundingClientRect !== strundefined ) { if ( typeof elem.getBoundingClientRect !== strundefined ) {
box = elem.getBoundingClientRect(); box = elem.getBoundingClientRect();
} }
...@@ -8926,7 +9012,7 @@ jQuery.fn.extend({ ...@@ -8926,7 +9012,7 @@ jQuery.fn.extend({
// Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
if ( jQuery.css( elem, "position" ) === "fixed" ) { if ( jQuery.css( elem, "position" ) === "fixed" ) {
// We assume that getBoundingClientRect is available when computed position is fixed // Assume getBoundingClientRect is there when computed position is fixed
offset = elem.getBoundingClientRect(); offset = elem.getBoundingClientRect();
} else { } else {
...@@ -8989,16 +9075,18 @@ jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( ...@@ -8989,16 +9075,18 @@ jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function(
}; };
}); });
// Support: Safari<7+, Chrome<37+
// Add the top/left cssHooks using jQuery.fn.position // Add the top/left cssHooks using jQuery.fn.position
// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
// getComputedStyle returns percent when specified for top/left/bottom/right // Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280
// rather than make the css module depend on the offset module, we just check for it here // getComputedStyle returns percent when specified for top/left/bottom/right;
// rather than make the css module depend on the offset module, just check for it here
jQuery.each( [ "top", "left" ], function( i, prop ) { jQuery.each( [ "top", "left" ], function( i, prop ) {
jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
function( elem, computed ) { function( elem, computed ) {
if ( computed ) { if ( computed ) {
computed = curCSS( elem, prop ); computed = curCSS( elem, prop );
// if curCSS returns percentage, fallback to offset // If curCSS returns percentage, fallback to offset
return rnumnonpx.test( computed ) ? return rnumnonpx.test( computed ) ?
jQuery( elem ).position()[ prop ] + "px" : jQuery( elem ).position()[ prop ] + "px" :
computed; computed;
...@@ -9011,7 +9099,7 @@ jQuery.each( [ "top", "left" ], function( i, prop ) { ...@@ -9011,7 +9099,7 @@ jQuery.each( [ "top", "left" ], function( i, prop ) {
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
// margin is only for outerHeight, outerWidth // Margin is only for outerHeight, outerWidth
jQuery.fn[ funcName ] = function( margin, value ) { jQuery.fn[ funcName ] = function( margin, value ) {
var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
...@@ -9068,6 +9156,12 @@ jQuery.fn.andSelf = jQuery.fn.addBack; ...@@ -9068,6 +9156,12 @@ jQuery.fn.andSelf = jQuery.fn.addBack;
// derived from file names, and jQuery is normally delivered in a lowercase // derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants // file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work. // to call noConflict to hide this version of jQuery, it will work.
// Note that for maximum portability, libraries that are not jQuery should
// declare themselves as anonymous modules, and avoid setting a global if an
// AMD loader is present. jQuery is a special case. For more information, see
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
if ( typeof define === "function" && define.amd ) { if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function() { define( "jquery", [], function() {
return jQuery; return jQuery;
...@@ -9096,8 +9190,8 @@ jQuery.noConflict = function( deep ) { ...@@ -9096,8 +9190,8 @@ jQuery.noConflict = function( deep ) {
return jQuery; return jQuery;
}; };
// Expose jQuery and $ identifiers, even in // Expose jQuery and $ identifiers, even in AMD
// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557) // (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566) // and CommonJS for browser emulators (#13566)
if ( typeof noGlobal === strundefined ) { if ( typeof noGlobal === strundefined ) {
window.jQuery = window.$ = jQuery; window.jQuery = window.$ = jQuery;
......
...@@ -12,104 +12,81 @@ button { ...@@ -12,104 +12,81 @@ button {
font-size: 100%; font-size: 100%;
vertical-align: baseline; vertical-align: baseline;
font-family: inherit; font-family: inherit;
font-weight: inherit;
color: inherit; color: inherit;
-webkit-appearance: none; -webkit-appearance: none;
-ms-appearance: none; -ms-appearance: none;
-o-appearance: none;
appearance: none; appearance: none;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
font-smoothing: antialiased;
} }
body { body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em; line-height: 1.4em;
background: #eaeaea url('bg.png'); background: #f5f5f5;
color: #4d4d4d; color: #4d4d4d;
width: 550px; min-width: 230px;
max-width: 550px;
margin: 0 auto; margin: 0 auto;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased; -moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased; -ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased; font-smoothing: antialiased;
font-weight: 300;
} }
button, button,
input[type="checkbox"] { input[type="checkbox"] {
outline: none; outline: none;
}
.hidden {
display: none;
} }
#todoapp { #todoapp {
background: #fff; background: #fff;
background: rgba(255, 255, 255, 0.9);
margin: 130px 0 40px 0; margin: 130px 0 40px 0;
border: 1px solid #ccc;
position: relative; position: relative;
border-top-left-radius: 2px; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
border-top-right-radius: 2px; 0 25px 50px 0 rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.15);
}
#todoapp:before {
content: '';
border-left: 1px solid #f5d6d6;
border-right: 1px solid #f5d6d6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
} }
#todoapp input::-webkit-input-placeholder { #todoapp input::-webkit-input-placeholder {
font-style: italic; font-style: italic;
font-weight: 300;
color: #e6e6e6;
} }
#todoapp input::-moz-placeholder { #todoapp input::-moz-placeholder {
font-style: italic; font-style: italic;
color: #a9a9a9; font-weight: 300;
color: #e6e6e6;
}
#todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
} }
#todoapp h1 { #todoapp h1 {
position: absolute; position: absolute;
top: -120px; top: -155px;
width: 100%; width: 100%;
font-size: 70px; font-size: 100px;
font-weight: bold; font-weight: 100;
text-align: center; text-align: center;
color: #b3b3b3; color: rgba(175, 47, 47, 0.15);
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
-webkit-text-rendering: optimizeLegibility; -webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility; -moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility; -ms-text-rendering: optimizeLegibility;
-o-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
} }
#header {
padding-top: 15px;
border-radius: inherit;
}
#header:before {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
height: 15px;
z-index: 2;
border-bottom: 1px solid #6c615c;
background: #8d7d77;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
border-top-left-radius: 1px;
border-top-right-radius: 1px;
}
#new-todo, #new-todo,
.edit { .edit {
position: relative; position: relative;
...@@ -117,6 +94,7 @@ input[type="checkbox"] { ...@@ -117,6 +94,7 @@ input[type="checkbox"] {
width: 100%; width: 100%;
font-size: 24px; font-size: 24px;
font-family: inherit; font-family: inherit;
font-weight: inherit;
line-height: 1.4em; line-height: 1.4em;
border: 0; border: 0;
outline: none; outline: none;
...@@ -124,29 +102,25 @@ input[type="checkbox"] { ...@@ -124,29 +102,25 @@ input[type="checkbox"] {
padding: 6px; padding: 6px;
border: 1px solid #999; border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-moz-box-sizing: border-box;
-ms-box-sizing: border-box; -ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased; -moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased; -ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased; font-smoothing: antialiased;
} }
#new-todo { #new-todo {
padding: 16px 16px 16px 60px; padding: 16px 16px 16px 60px;
border: none; border: none;
background: rgba(0, 0, 0, 0.02); background: rgba(0, 0, 0, 0.003);
z-index: 2; box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
box-shadow: none;
} }
#main { #main {
position: relative; position: relative;
z-index: 2; z-index: 2;
border-top: 1px dotted #adadad; border-top: 1px solid #e6e6e6;
} }
label[for='toggle-all'] { label[for='toggle-all'] {
...@@ -155,19 +129,19 @@ label[for='toggle-all'] { ...@@ -155,19 +129,19 @@ label[for='toggle-all'] {
#toggle-all { #toggle-all {
position: absolute; position: absolute;
top: -42px; top: -55px;
left: -4px; left: -12px;
width: 40px; width: 60px;
height: 34px;
text-align: center; text-align: center;
/* Mobile Safari */ border: none; /* Mobile Safari */
border: none;
} }
#toggle-all:before { #toggle-all:before {
content: '»'; content: '';
font-size: 28px; font-size: 22px;
color: #d9d9d9; color: #e6e6e6;
padding: 0 25px 7px; padding: 10px 27px 10px 27px;
} }
#toggle-all:checked:before { #toggle-all:checked:before {
...@@ -183,7 +157,7 @@ label[for='toggle-all'] { ...@@ -183,7 +157,7 @@ label[for='toggle-all'] {
#todo-list li { #todo-list li {
position: relative; position: relative;
font-size: 24px; font-size: 24px;
border-bottom: 1px dotted #ccc; border-bottom: 1px solid #ededed;
} }
#todo-list li:last-child { #todo-list li:last-child {
...@@ -215,28 +189,18 @@ label[for='toggle-all'] { ...@@ -215,28 +189,18 @@ label[for='toggle-all'] {
top: 0; top: 0;
bottom: 0; bottom: 0;
margin: auto 0; margin: auto 0;
/* Mobile Safari */ border: none; /* Mobile Safari */
border: none;
-webkit-appearance: none; -webkit-appearance: none;
-ms-appearance: none; -ms-appearance: none;
-o-appearance: none;
appearance: none; appearance: none;
} }
#todo-list li .toggle:after { #todo-list li .toggle:after {
content: '✔'; content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
/* 40 + a couple of pixels visual adjustment */
line-height: 43px;
font-size: 20px;
color: #d9d9d9;
text-shadow: 0 -1px 0 #bfbfbf;
} }
#todo-list li .toggle:checked:after { #todo-list li .toggle:checked:after {
color: #85ada7; content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
} }
#todo-list li label { #todo-list li label {
...@@ -246,12 +210,11 @@ label[for='toggle-all'] { ...@@ -246,12 +210,11 @@ label[for='toggle-all'] {
margin-left: 45px; margin-left: 45px;
display: block; display: block;
line-height: 1.2; line-height: 1.2;
-webkit-transition: color 0.4s;
transition: color 0.4s; transition: color 0.4s;
} }
#todo-list li.completed label { #todo-list li.completed label {
color: #a9a9a9; color: #d9d9d9;
text-decoration: line-through; text-decoration: line-through;
} }
...@@ -264,21 +227,18 @@ label[for='toggle-all'] { ...@@ -264,21 +227,18 @@ label[for='toggle-all'] {
width: 40px; width: 40px;
height: 40px; height: 40px;
margin: auto 0; margin: auto 0;
font-size: 22px; font-size: 30px;
color: #a88a8a; color: #cc9a9a;
-webkit-transition: all 0.2s; margin-bottom: 11px;
transition: all 0.2s; transition: color 0.2s ease-out;
} }
#todo-list li .destroy:hover { #todo-list li .destroy:hover {
text-shadow: 0 0 1px #000, color: #af5b5e;
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
transform: scale(1.3);
} }
#todo-list li .destroy:after { #todo-list li .destroy:after {
content: ''; content: '×';
} }
#todo-list li:hover .destroy { #todo-list li:hover .destroy {
...@@ -295,29 +255,25 @@ label[for='toggle-all'] { ...@@ -295,29 +255,25 @@ label[for='toggle-all'] {
#footer { #footer {
color: #777; color: #777;
padding: 0 15px; padding: 10px 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
height: 20px; height: 20px;
z-index: 1;
text-align: center; text-align: center;
border-top: 1px solid #e6e6e6;
} }
#footer:before { #footer:before {
content: ''; content: '';
position: absolute; position: absolute;
right: 0; right: 0;
bottom: 31px; bottom: 0;
left: 0; left: 0;
height: 50px; height: 50px;
z-index: -1; overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 6px 0 -3px rgba(255, 255, 255, 0.8), 0 8px 0 -3px #f6f6f6,
0 7px 1px -3px rgba(0, 0, 0, 0.3), 0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 43px 0 -6px rgba(255, 255, 255, 0.8), 0 16px 0 -6px #f6f6f6,
0 44px 2px -6px rgba(0, 0, 0, 0.2); 0 17px 2px -6px rgba(0, 0, 0, 0.2);
} }
#todo-count { #todo-count {
...@@ -325,6 +281,10 @@ label[for='toggle-all'] { ...@@ -325,6 +281,10 @@ label[for='toggle-all'] {
text-align: left; text-align: left;
} }
#todo-count strong {
font-weight: 300;
}
#filters { #filters {
margin: 0; margin: 0;
padding: 0; padding: 0;
...@@ -339,49 +299,72 @@ label[for='toggle-all'] { ...@@ -339,49 +299,72 @@ label[for='toggle-all'] {
} }
#filters li a { #filters li a {
color: #83756f; color: inherit;
margin: 2px; margin: 3px;
padding: 3px 7px;
text-decoration: none; text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
#filters li a.selected,
#filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
} }
#filters li a.selected { #filters li a.selected {
font-weight: bold; border-color: rgba(175, 47, 47, 0.2);
} }
#clear-completed { #clear-completed,
html #clear-completed:active {
float: right; float: right;
position: relative; position: relative;
line-height: 20px; line-height: 20px;
text-decoration: none; text-decoration: none;
background: rgba(0, 0, 0, 0.1); cursor: pointer;
font-size: 11px; visibility: hidden;
padding: 0 10px; position: relative;
border-radius: 3px; }
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
#clear-completed::after {
visibility: visible;
content: 'Clear completed';
position: absolute;
right: 0;
white-space: nowrap;
} }
#clear-completed:hover { #clear-completed:hover::after {
background: rgba(0, 0, 0, 0.15); text-decoration: underline;
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
} }
#info { #info {
margin: 65px auto 0; margin: 65px auto 0;
color: #a6a6a6; color: #bfbfbf;
font-size: 12px; font-size: 10px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center; text-align: center;
} }
#info p {
line-height: 1;
}
#info a { #info a {
color: inherit; color: inherit;
text-decoration: none;
font-weight: 400;
}
#info a:hover {
text-decoration: underline;
} }
/* /*
Hack to remove background from Mobile Safari. Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera Can't use it globally since it destroys checkboxes in Firefox
*/ */
@media screen and (-webkit-min-device-pixel-ratio:0) { @media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all, #toggle-all,
#todo-list li .toggle { #todo-list li .toggle {
...@@ -393,10 +376,6 @@ label[for='toggle-all'] { ...@@ -393,10 +376,6 @@ label[for='toggle-all'] {
} }
#toggle-all { #toggle-all {
top: -56px;
left: -15px;
width: 65px;
height: 41px;
-webkit-transform: rotate(90deg); -webkit-transform: rotate(90deg);
transform: rotate(90deg); transform: rotate(90deg);
-webkit-appearance: none; -webkit-appearance: none;
...@@ -404,151 +383,12 @@ label[for='toggle-all'] { ...@@ -404,151 +383,12 @@ label[for='toggle-all'] {
} }
} }
.hidden { @media (max-width: 430px) {
display: none; #footer {
} height: 50px;
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #C5C5C5;
border-bottom: 1px dashed #F7F7F7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
-webkit-transition-property: left;
transition-property: left;
-webkit-transition-duration: 500ms;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
margin: 0 0 0 300px;
}
.learn-bar > .learn {
left: 8px;
} }
.learn-bar #todoapp { #filters {
width: 550px; bottom: 10px;
margin: 130px auto 40px auto;
} }
} }
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #c5c5c5;
border-bottom: 1px dashed #f7f7f7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
#issue-count {
display: none;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
transition-property: left;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
padding-left: 300px;
}
.learn-bar > .learn {
left: 8px;
}
}
/* global _ */
(function () { (function () {
'use strict'; 'use strict';
/* jshint ignore:start */
// Underscore's Template Module // Underscore's Template Module
// Courtesy of underscorejs.org // Courtesy of underscorejs.org
var _ = (function (_) { var _ = (function (_) {
...@@ -114,6 +116,7 @@ ...@@ -114,6 +116,7 @@
if (location.hostname === 'todomvc.com') { if (location.hostname === 'todomvc.com') {
window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script'));
} }
/* jshint ignore:end */
function redirect() { function redirect() {
if (location.hostname === 'tastejs.github.io') { if (location.hostname === 'tastejs.github.io') {
...@@ -175,13 +178,17 @@ ...@@ -175,13 +178,17 @@
if (learnJSON.backend) { if (learnJSON.backend) {
this.frameworkJSON = learnJSON.backend; this.frameworkJSON = learnJSON.backend;
this.frameworkJSON.issueLabel = framework;
this.append({ this.append({
backend: true backend: true
}); });
} else if (learnJSON[framework]) { } else if (learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework]; this.frameworkJSON = learnJSON[framework];
this.frameworkJSON.issueLabel = framework;
this.append(); this.append();
} }
this.fetchIssueCount();
} }
Learn.prototype.append = function (opts) { Learn.prototype.append = function (opts) {
...@@ -212,6 +219,26 @@ ...@@ -212,6 +219,26 @@
document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); document.body.insertAdjacentHTML('afterBegin', aside.outerHTML);
}; };
Learn.prototype.fetchIssueCount = function () {
var issueLink = document.getElementById('issue-count-link');
if (issueLink) {
var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos');
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function (e) {
var parsedResponse = JSON.parse(e.target.responseText);
if (parsedResponse instanceof Array) {
var count = parsedResponse.length
if (count !== 0) {
issueLink.innerHTML = 'This app has ' + count + ' open issues';
document.getElementById('issue-count').style.display = 'inline';
}
}
};
xhr.send();
}
};
redirect(); redirect();
getFile('learn.json', Learn); getFile('learn.json', Learn);
})(); })();
// Underscore.js 1.6.0 // Underscore.js 1.7.0
// http://underscorejs.org // http://underscorejs.org
// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license. // Underscore may be freely distributed under the MIT license.
...@@ -14,9 +14,6 @@ ...@@ -14,9 +14,6 @@
// Save the previous value of the `_` variable. // Save the previous value of the `_` variable.
var previousUnderscore = root._; var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version: // Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
...@@ -31,15 +28,6 @@ ...@@ -31,15 +28,6 @@
// All **ECMAScript 5** native function implementations that we hope to use // All **ECMAScript 5** native function implementations that we hope to use
// are declared here. // are declared here.
var var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray, nativeIsArray = Array.isArray,
nativeKeys = Object.keys, nativeKeys = Object.keys,
nativeBind = FuncProto.bind; nativeBind = FuncProto.bind;
...@@ -53,8 +41,7 @@ ...@@ -53,8 +41,7 @@
// Export the Underscore object for **Node.js**, with // Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in // backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier, // the browser, add `_` as a global object.
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') { if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) { if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _; exports = module.exports = _;
...@@ -65,98 +52,125 @@ ...@@ -65,98 +52,125 @@
} }
// Current version. // Current version.
_.VERSION = '1.6.0'; _.VERSION = '1.7.0';
// Internal function that returns an efficient (for current engines) version
// of the passed-in callback, to be repeatedly applied in other Underscore
// functions.
var createCallback = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
case 2: return function(value, other) {
return func.call(context, value, other);
};
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
// A mostly-internal function to generate callbacks that can be applied
// to each element in a collection, returning the desired result — either
// identity, an arbitrary callback, a property matcher, or a property accessor.
_.iteratee = function(value, context, argCount) {
if (value == null) return _.identity;
if (_.isFunction(value)) return createCallback(value, context, argCount);
if (_.isObject(value)) return _.matches(value);
return _.property(value);
};
// Collection Functions // Collection Functions
// -------------------- // --------------------
// The cornerstone, an `each` implementation, aka `forEach`. // The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects. // Handles raw objects in addition to array-likes. Treats all
// Delegates to **ECMAScript 5**'s native `forEach` if available. // sparse array-likes as if they were dense.
var each = _.each = _.forEach = function(obj, iterator, context) { _.each = _.forEach = function(obj, iteratee, context) {
if (obj == null) return obj; if (obj == null) return obj;
if (nativeForEach && obj.forEach === nativeForEach) { iteratee = createCallback(iteratee, context);
obj.forEach(iterator, context); var i, length = obj.length;
} else if (obj.length === +obj.length) { if (length === +length) {
for (var i = 0, length = obj.length; i < length; i++) { for (i = 0; i < length; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return; iteratee(obj[i], i, obj);
} }
} else { } else {
var keys = _.keys(obj); var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) { for (i = 0, length = keys.length; i < length; i++) {
if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; iteratee(obj[keys[i]], keys[i], obj);
} }
} }
return obj; return obj;
}; };
// Return the results of applying the iterator to each element. // Return the results of applying the iteratee to each element.
// Delegates to **ECMAScript 5**'s native `map` if available. _.map = _.collect = function(obj, iteratee, context) {
_.map = _.collect = function(obj, iterator, context) { if (obj == null) return [];
var results = []; iteratee = _.iteratee(iteratee, context);
if (obj == null) return results; var keys = obj.length !== +obj.length && _.keys(obj),
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); length = (keys || obj).length,
each(obj, function(value, index, list) { results = Array(length),
results.push(iterator.call(context, value, index, list)); currentKey;
}); for (var index = 0; index < length; index++) {
currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results; return results;
}; };
var reduceError = 'Reduce of empty array with no initial value'; var reduceError = 'Reduce of empty array with no initial value';
// **Reduce** builds up a single result from a list of values, aka `inject`, // **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. // or `foldl`.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { _.reduce = _.foldl = _.inject = function(obj, iteratee, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = []; if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) { iteratee = createCallback(iteratee, context, 4);
if (context) iterator = _.bind(iterator, context); var keys = obj.length !== +obj.length && _.keys(obj),
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); length = (keys || obj).length,
index = 0, currentKey;
if (arguments.length < 3) {
if (!length) throw new TypeError(reduceError);
memo = obj[keys ? keys[index++] : index++];
}
for (; index < length; index++) {
currentKey = keys ? keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
} }
each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
} else {
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo; return memo;
}; };
// The right-associative version of reduce, also known as `foldr`. // The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available. _.reduceRight = _.foldr = function(obj, iteratee, memo, context) {
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = []; if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { iteratee = createCallback(iteratee, context, 4);
if (context) iterator = _.bind(iterator, context); var keys = obj.length !== + obj.length && _.keys(obj),
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); index = (keys || obj).length,
currentKey;
if (arguments.length < 3) {
if (!index) throw new TypeError(reduceError);
memo = obj[keys ? keys[--index] : --index];
} }
var length = obj.length; while (index--) {
if (length !== +length) { currentKey = keys ? keys[index] : index;
var keys = _.keys(obj); memo = iteratee(memo, obj[currentKey], currentKey, obj);
length = keys.length;
} }
each(obj, function(value, index, list) {
index = keys ? keys[--length] : --length;
if (!initial) {
memo = obj[index];
initial = true;
} else {
memo = iterator.call(context, memo, obj[index], index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo; return memo;
}; };
// Return the first value which passes a truth test. Aliased as `detect`. // Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) { _.find = _.detect = function(obj, predicate, context) {
var result; var result;
any(obj, function(value, index, list) { predicate = _.iteratee(predicate, context);
if (predicate.call(context, value, index, list)) { _.some(obj, function(value, index, list) {
if (predicate(value, index, list)) {
result = value; result = value;
return true; return true;
} }
...@@ -165,61 +179,58 @@ ...@@ -165,61 +179,58 @@
}; };
// Return all the elements that pass a truth test. // Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`. // Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) { _.filter = _.select = function(obj, predicate, context) {
var results = []; var results = [];
if (obj == null) return results; if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context); predicate = _.iteratee(predicate, context);
each(obj, function(value, index, list) { _.each(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) results.push(value); if (predicate(value, index, list)) results.push(value);
}); });
return results; return results;
}; };
// Return all the elements for which a truth test fails. // Return all the elements for which a truth test fails.
_.reject = function(obj, predicate, context) { _.reject = function(obj, predicate, context) {
return _.filter(obj, function(value, index, list) { return _.filter(obj, _.negate(_.iteratee(predicate)), context);
return !predicate.call(context, value, index, list);
}, context);
}; };
// Determine whether all of the elements match a truth test. // Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`. // Aliased as `all`.
_.every = _.all = function(obj, predicate, context) { _.every = _.all = function(obj, predicate, context) {
predicate || (predicate = _.identity); if (obj == null) return true;
var result = true; predicate = _.iteratee(predicate, context);
if (obj == null) return result; var keys = obj.length !== +obj.length && _.keys(obj),
if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context); length = (keys || obj).length,
each(obj, function(value, index, list) { index, currentKey;
if (!(result = result && predicate.call(context, value, index, list))) return breaker; for (index = 0; index < length; index++) {
}); currentKey = keys ? keys[index] : index;
return !!result; if (!predicate(obj[currentKey], currentKey, obj)) return false;
}
return true;
}; };
// Determine if at least one element in the object matches a truth test. // Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`. // Aliased as `any`.
var any = _.some = _.any = function(obj, predicate, context) { _.some = _.any = function(obj, predicate, context) {
predicate || (predicate = _.identity); if (obj == null) return false;
var result = false; predicate = _.iteratee(predicate, context);
if (obj == null) return result; var keys = obj.length !== +obj.length && _.keys(obj),
if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context); length = (keys || obj).length,
each(obj, function(value, index, list) { index, currentKey;
if (result || (result = predicate.call(context, value, index, list))) return breaker; for (index = 0; index < length; index++) {
}); currentKey = keys ? keys[index] : index;
return !!result; if (predicate(obj[currentKey], currentKey, obj)) return true;
}
return false;
}; };
// Determine if the array or object contains a given value (using `===`). // Determine if the array or object contains a given value (using `===`).
// Aliased as `include`. // Aliased as `include`.
_.contains = _.include = function(obj, target) { _.contains = _.include = function(obj, target) {
if (obj == null) return false; if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; if (obj.length !== +obj.length) obj = _.values(obj);
return any(obj, function(value) { return _.indexOf(obj, target) >= 0;
return value === target;
});
}; };
// Invoke a method (with arguments) on every item in a collection. // Invoke a method (with arguments) on every item in a collection.
...@@ -248,51 +259,67 @@ ...@@ -248,51 +259,67 @@
return _.find(obj, _.matches(attrs)); return _.find(obj, _.matches(attrs));
}; };
// 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. _.max = function(obj, iteratee, context) {
// See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) var result = -Infinity, lastComputed = -Infinity,
_.max = function(obj, iterator, context) { value, computed;
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { if (iteratee == null && obj != null) {
return Math.max.apply(Math, obj); obj = obj.length === +obj.length ? obj : _.values(obj);
} for (var i = 0, length = obj.length; i < length; i++) {
var result = -Infinity, lastComputed = -Infinity; value = obj[i];
each(obj, function(value, index, list) { if (value > result) {
var computed = iterator ? iterator.call(context, value, index, list) : value; result = value;
if (computed > lastComputed) { }
result = value;
lastComputed = computed;
} }
}); } else {
iteratee = _.iteratee(iteratee, context);
_.each(obj, function(value, index, list) {
computed = iteratee(value, index, list);
if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
result = value;
lastComputed = computed;
}
});
}
return result; return result;
}; };
// Return the minimum element (or element-based computation). // Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) { _.min = function(obj, iteratee, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { var result = Infinity, lastComputed = Infinity,
return Math.min.apply(Math, obj); value, computed;
} if (iteratee == null && obj != null) {
var result = Infinity, lastComputed = Infinity; obj = obj.length === +obj.length ? obj : _.values(obj);
each(obj, function(value, index, list) { for (var i = 0, length = obj.length; i < length; i++) {
var computed = iterator ? iterator.call(context, value, index, list) : value; value = obj[i];
if (computed < lastComputed) { if (value < result) {
result = value; result = value;
lastComputed = computed; }
} }
}); } else {
iteratee = _.iteratee(iteratee, context);
_.each(obj, function(value, index, list) {
computed = iteratee(value, index, list);
if (computed < lastComputed || computed === Infinity && result === Infinity) {
result = value;
lastComputed = computed;
}
});
}
return result; return result;
}; };
// Shuffle an array, using the modern version of the // Shuffle a collection, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_.shuffle = function(obj) { _.shuffle = function(obj) {
var rand; var set = obj && obj.length === +obj.length ? obj : _.values(obj);
var index = 0; var length = set.length;
var shuffled = []; var shuffled = Array(length);
each(obj, function(value) { for (var index = 0, rand; index < length; index++) {
rand = _.random(index++); rand = _.random(0, index);
shuffled[index - 1] = shuffled[rand]; if (rand !== index) shuffled[index] = shuffled[rand];
shuffled[rand] = value; shuffled[rand] = set[index];
}); }
return shuffled; return shuffled;
}; };
...@@ -307,21 +334,14 @@ ...@@ -307,21 +334,14 @@
return _.shuffle(obj).slice(0, Math.max(0, n)); return _.shuffle(obj).slice(0, Math.max(0, n));
}; };
// An internal function to generate lookup iterators. // Sort the object's values by a criterion produced by an iteratee.
var lookupIterator = function(value) { _.sortBy = function(obj, iteratee, context) {
if (value == null) return _.identity; iteratee = _.iteratee(iteratee, context);
if (_.isFunction(value)) return value;
return _.property(value);
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, iterator, context) {
iterator = lookupIterator(iterator);
return _.pluck(_.map(obj, function(value, index, list) { return _.pluck(_.map(obj, function(value, index, list) {
return { return {
value: value, value: value,
index: index, index: index,
criteria: iterator.call(context, value, index, list) criteria: iteratee(value, index, list)
}; };
}).sort(function(left, right) { }).sort(function(left, right) {
var a = left.criteria; var a = left.criteria;
...@@ -336,12 +356,12 @@ ...@@ -336,12 +356,12 @@
// An internal function used for aggregate "group by" operations. // An internal function used for aggregate "group by" operations.
var group = function(behavior) { var group = function(behavior) {
return function(obj, iterator, context) { return function(obj, iteratee, context) {
var result = {}; var result = {};
iterator = lookupIterator(iterator); iteratee = _.iteratee(iteratee, context);
each(obj, function(value, index) { _.each(obj, function(value, index) {
var key = iterator.call(context, value, index, obj); var key = iteratee(value, index, obj);
behavior(result, key, value); behavior(result, value, key);
}); });
return result; return result;
}; };
...@@ -349,32 +369,32 @@ ...@@ -349,32 +369,32 @@
// Groups the object's values by a criterion. Pass either a string attribute // Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion. // to group by, or a function that returns the criterion.
_.groupBy = group(function(result, key, value) { _.groupBy = group(function(result, value, key) {
_.has(result, key) ? result[key].push(value) : result[key] = [value]; if (_.has(result, key)) result[key].push(value); else result[key] = [value];
}); });
// Indexes the object's values by a criterion, similar to `groupBy`, but for // Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique. // when you know that your index values will be unique.
_.indexBy = group(function(result, key, value) { _.indexBy = group(function(result, value, key) {
result[key] = value; result[key] = value;
}); });
// Counts instances of an object that group by a certain criterion. Pass // Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the // either a string attribute to count by, or a function that returns the
// criterion. // criterion.
_.countBy = group(function(result, key) { _.countBy = group(function(result, value, key) {
_.has(result, key) ? result[key]++ : result[key] = 1; if (_.has(result, key)) result[key]++; else result[key] = 1;
}); });
// Use a comparator function to figure out the smallest index at which // Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search. // an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator, context) { _.sortedIndex = function(array, obj, iteratee, context) {
iterator = lookupIterator(iterator); iteratee = _.iteratee(iteratee, context, 1);
var value = iterator.call(context, obj); var value = iteratee(obj);
var low = 0, high = array.length; var low = 0, high = array.length;
while (low < high) { while (low < high) {
var mid = (low + high) >>> 1; var mid = low + high >>> 1;
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
} }
return low; return low;
}; };
...@@ -390,7 +410,18 @@ ...@@ -390,7 +410,18 @@
// Return the number of elements in an object. // Return the number of elements in an object.
_.size = function(obj) { _.size = function(obj) {
if (obj == null) return 0; if (obj == null) return 0;
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; return obj.length === +obj.length ? obj.length : _.keys(obj).length;
};
// Split a collection into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_.partition = function(obj, predicate, context) {
predicate = _.iteratee(predicate, context);
var pass = [], fail = [];
_.each(obj, function(value, key, obj) {
(predicate(value, key, obj) ? pass : fail).push(value);
});
return [pass, fail];
}; };
// Array Functions // Array Functions
...@@ -401,7 +432,7 @@ ...@@ -401,7 +432,7 @@
// allows it to work with `_.map`. // allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) { _.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0; if (array == null) return void 0;
if ((n == null) || guard) return array[0]; if (n == null || guard) return array[0];
if (n < 0) return []; if (n < 0) return [];
return slice.call(array, 0, n); return slice.call(array, 0, n);
}; };
...@@ -411,14 +442,14 @@ ...@@ -411,14 +442,14 @@
// the array, excluding the last N. The **guard** check allows it to work with // the array, excluding the last N. The **guard** check allows it to work with
// `_.map`. // `_.map`.
_.initial = function(array, n, guard) { _.initial = function(array, n, guard) {
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
}; };
// Get the last element of an array. Passing **n** will return the last N // Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`. // values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) { _.last = function(array, n, guard) {
if (array == null) return void 0; if (array == null) return void 0;
if ((n == null) || guard) return array[array.length - 1]; if (n == null || guard) return array[array.length - 1];
return slice.call(array, Math.max(array.length - n, 0)); return slice.call(array, Math.max(array.length - n, 0));
}; };
...@@ -427,7 +458,7 @@ ...@@ -427,7 +458,7 @@
// the rest N values in the array. The **guard** // the rest N values in the array. The **guard**
// check allows it to work with `_.map`. // check allows it to work with `_.map`.
_.rest = _.tail = _.drop = function(array, n, guard) { _.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, (n == null) || guard ? 1 : n); return slice.call(array, n == null || guard ? 1 : n);
}; };
// Trim out all falsy values from an array. // Trim out all falsy values from an array.
...@@ -436,23 +467,26 @@ ...@@ -436,23 +467,26 @@
}; };
// Internal implementation of a recursive `flatten` function. // Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, output) { var flatten = function(input, shallow, strict, output) {
if (shallow && _.every(input, _.isArray)) { if (shallow && _.every(input, _.isArray)) {
return concat.apply(output, input); return concat.apply(output, input);
} }
each(input, function(value) { for (var i = 0, length = input.length; i < length; i++) {
if (_.isArray(value) || _.isArguments(value)) { var value = input[i];
shallow ? push.apply(output, value) : flatten(value, shallow, output); if (!_.isArray(value) && !_.isArguments(value)) {
if (!strict) output.push(value);
} else if (shallow) {
push.apply(output, value);
} else { } else {
output.push(value); flatten(value, shallow, strict, output);
} }
}); }
return output; return output;
}; };
// Flatten out an array, either recursively (by default), or just one level. // Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) { _.flatten = function(array, shallow) {
return flatten(array, shallow, []); return flatten(array, shallow, false, []);
}; };
// Return a version of the array that does not contain the specified value(s). // Return a version of the array that does not contain the specified value(s).
...@@ -460,68 +494,77 @@ ...@@ -460,68 +494,77 @@
return _.difference(array, slice.call(arguments, 1)); return _.difference(array, slice.call(arguments, 1));
}; };
// Split an array into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_.partition = function(array, predicate) {
var pass = [], fail = [];
each(array, function(elem) {
(predicate(elem) ? pass : fail).push(elem);
});
return [pass, fail];
};
// Produce a duplicate-free version of the array. If the array has already // Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm. // been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`. // Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator, context) { _.uniq = _.unique = function(array, isSorted, iteratee, context) {
if (_.isFunction(isSorted)) { if (array == null) return [];
context = iterator; if (!_.isBoolean(isSorted)) {
iterator = isSorted; context = iteratee;
iteratee = isSorted;
isSorted = false; isSorted = false;
} }
var initial = iterator ? _.map(array, iterator, context) : array; if (iteratee != null) iteratee = _.iteratee(iteratee, context);
var results = []; var result = [];
var seen = []; var seen = [];
each(initial, function(value, index) { for (var i = 0, length = array.length; i < length; i++) {
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { var value = array[i];
seen.push(value); if (isSorted) {
results.push(array[index]); if (!i || seen !== value) result.push(value);
seen = value;
} else if (iteratee) {
var computed = iteratee(value, i, array);
if (_.indexOf(seen, computed) < 0) {
seen.push(computed);
result.push(value);
}
} else if (_.indexOf(result, value) < 0) {
result.push(value);
} }
}); }
return results; return result;
}; };
// Produce an array that contains the union: each distinct element from all of // Produce an array that contains the union: each distinct element from all of
// the passed-in arrays. // the passed-in arrays.
_.union = function() { _.union = function() {
return _.uniq(_.flatten(arguments, true)); return _.uniq(flatten(arguments, true, true, []));
}; };
// Produce an array that contains every item shared between all the // Produce an array that contains every item shared between all the
// passed-in arrays. // passed-in arrays.
_.intersection = function(array) { _.intersection = function(array) {
var rest = slice.call(arguments, 1); if (array == null) return [];
return _.filter(_.uniq(array), function(item) { var result = [];
return _.every(rest, function(other) { var argsLength = arguments.length;
return _.contains(other, item); for (var i = 0, length = array.length; i < length; i++) {
}); var item = array[i];
}); if (_.contains(result, item)) continue;
for (var j = 1; j < argsLength; j++) {
if (!_.contains(arguments[j], item)) break;
}
if (j === argsLength) result.push(item);
}
return result;
}; };
// Take the difference between one array and a number of other arrays. // Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain. // Only the elements present in just the first array will remain.
_.difference = function(array) { _.difference = function(array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); var rest = flatten(slice.call(arguments, 1), true, true, []);
return _.filter(array, function(value){ return !_.contains(rest, value); }); return _.filter(array, function(value){
return !_.contains(rest, value);
});
}; };
// Zip together multiple lists into a single array -- elements that share // Zip together multiple lists into a single array -- elements that share
// an index go together. // an index go together.
_.zip = function() { _.zip = function(array) {
var length = _.max(_.pluck(arguments, 'length').concat(0)); if (array == null) return [];
var results = new Array(length); var length = _.max(arguments, 'length').length;
var results = Array(length);
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
results[i] = _.pluck(arguments, '' + i); results[i] = _.pluck(arguments, i);
} }
return results; return results;
}; };
...@@ -542,10 +585,8 @@ ...@@ -542,10 +585,8 @@
return result; return result;
}; };
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), // Return the position of the first occurrence of an item in an array,
// we need this function. Return the position of the first occurrence of an // or -1 if the item is not included in the array.
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true` // If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search. // for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) { _.indexOf = function(array, item, isSorted) {
...@@ -553,26 +594,23 @@ ...@@ -553,26 +594,23 @@
var i = 0, length = array.length; var i = 0, length = array.length;
if (isSorted) { if (isSorted) {
if (typeof isSorted == 'number') { if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted;
} else { } else {
i = _.sortedIndex(array, item); i = _.sortedIndex(array, item);
return array[i] === item ? i : -1; return array[i] === item ? i : -1;
} }
} }
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < length; i++) if (array[i] === item) return i; for (; i < length; i++) if (array[i] === item) return i;
return -1; return -1;
}; };
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item, from) { _.lastIndexOf = function(array, item, from) {
if (array == null) return -1; if (array == null) return -1;
var hasIndex = from != null; var idx = array.length;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { if (typeof from == 'number') {
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1);
} }
var i = (hasIndex ? from : array.length); while (--idx >= 0) if (array[idx] === item) return idx;
while (i--) if (array[i] === item) return i;
return -1; return -1;
}; };
...@@ -584,15 +622,13 @@ ...@@ -584,15 +622,13 @@
stop = start || 0; stop = start || 0;
start = 0; start = 0;
} }
step = arguments[2] || 1; step = step || 1;
var length = Math.max(Math.ceil((stop - start) / step), 0); var length = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0; var range = Array(length);
var range = new Array(length);
while(idx < length) { for (var idx = 0; idx < length; idx++, start += step) {
range[idx++] = start; range[idx] = start;
start += step;
} }
return range; return range;
...@@ -602,7 +638,7 @@ ...@@ -602,7 +638,7 @@
// ------------------ // ------------------
// Reusable constructor function for prototype setting. // Reusable constructor function for prototype setting.
var ctor = function(){}; 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). Delegates to **ECMAScript 5**'s native `Function.bind` if // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
...@@ -610,17 +646,18 @@ ...@@ -610,17 +646,18 @@
_.bind = function(func, context) { _.bind = function(func, context) {
var args, bound; var args, bound;
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError; if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
args = slice.call(arguments, 2); args = slice.call(arguments, 2);
return bound = function() { bound = function() {
if (!(this instanceof bound)) 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; Ctor.prototype = func.prototype;
var self = new ctor; var self = new Ctor;
ctor.prototype = null; Ctor.prototype = null;
var result = func.apply(self, args.concat(slice.call(arguments))); var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result; if (_.isObject(result)) return result;
return self; return self;
}; };
return bound;
}; };
// Partially apply a function by creating a version that has had some of its // Partially apply a function by creating a version that has had some of its
...@@ -643,27 +680,34 @@ ...@@ -643,27 +680,34 @@
// are the method names to be bound. Useful for ensuring that all callbacks // are the method names to be bound. Useful for ensuring that all callbacks
// defined on an object belong to it. // defined on an object belong to it.
_.bindAll = function(obj) { _.bindAll = function(obj) {
var funcs = slice.call(arguments, 1); var i, length = arguments.length, key;
if (funcs.length === 0) throw new Error('bindAll must be passed function names'); if (length <= 1) throw new Error('bindAll must be passed function names');
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); for (i = 1; i < length; i++) {
key = arguments[i];
obj[key] = _.bind(obj[key], obj);
}
return obj; return obj;
}; };
// Memoize an expensive function by storing its results. // Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) { _.memoize = function(func, hasher) {
var memo = {}; var memoize = function(key) {
hasher || (hasher = _.identity); var cache = memoize.cache;
return function() { var address = hasher ? hasher.apply(this, arguments) : key;
var key = hasher.apply(this, arguments); if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); return cache[address];
}; };
memoize.cache = {};
return memoize;
}; };
// Delays a function for the given number of milliseconds, and then calls // Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied. // it with the arguments supplied.
_.delay = function(func, wait) { _.delay = function(func, wait) {
var args = slice.call(arguments, 2); var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(null, args); }, wait); return setTimeout(function(){
return func.apply(null, args);
}, wait);
}; };
// Defers a function, scheduling it to run after the current call stack has // Defers a function, scheduling it to run after the current call stack has
...@@ -681,12 +725,12 @@ ...@@ -681,12 +725,12 @@
var context, args, result; var context, args, result;
var timeout = null; var timeout = null;
var previous = 0; var previous = 0;
options || (options = {}); if (!options) options = {};
var later = function() { var later = function() {
previous = options.leading === false ? 0 : _.now(); previous = options.leading === false ? 0 : _.now();
timeout = null; timeout = null;
result = func.apply(context, args); result = func.apply(context, args);
context = args = null; if (!timeout) context = args = null;
}; };
return function() { return function() {
var now = _.now(); var now = _.now();
...@@ -694,12 +738,12 @@ ...@@ -694,12 +738,12 @@
var remaining = wait - (now - previous); var remaining = wait - (now - previous);
context = this; context = this;
args = arguments; args = arguments;
if (remaining <= 0) { if (remaining <= 0 || remaining > wait) {
clearTimeout(timeout); clearTimeout(timeout);
timeout = null; timeout = null;
previous = now; previous = now;
result = func.apply(context, args); result = func.apply(context, args);
context = args = null; if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) { } else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining); timeout = setTimeout(later, remaining);
} }
...@@ -716,13 +760,14 @@ ...@@ -716,13 +760,14 @@
var later = function() { var later = function() {
var last = _.now() - timestamp; var last = _.now() - timestamp;
if (last < wait) {
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last); timeout = setTimeout(later, wait - last);
} else { } else {
timeout = null; timeout = null;
if (!immediate) { if (!immediate) {
result = func.apply(context, args); result = func.apply(context, args);
context = args = null; if (!timeout) context = args = null;
} }
} }
}; };
...@@ -732,9 +777,7 @@ ...@@ -732,9 +777,7 @@
args = arguments; args = arguments;
timestamp = _.now(); timestamp = _.now();
var callNow = immediate && !timeout; var callNow = immediate && !timeout;
if (!timeout) { if (!timeout) timeout = setTimeout(later, wait);
timeout = setTimeout(later, wait);
}
if (callNow) { if (callNow) {
result = func.apply(context, args); result = func.apply(context, args);
context = args = null; context = args = null;
...@@ -744,19 +787,6 @@ ...@@ -744,19 +787,6 @@
}; };
}; };
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
// Returns the first function passed as an argument to the second, // Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and // allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function. // conditionally execute the original function.
...@@ -764,16 +794,23 @@ ...@@ -764,16 +794,23 @@
return _.partial(wrapper, func); return _.partial(wrapper, func);
}; };
// Returns a negated version of the passed-in predicate.
_.negate = function(predicate) {
return function() {
return !predicate.apply(this, arguments);
};
};
// Returns a function that is the composition of a list of functions, each // Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows. // consuming the return value of the function that follows.
_.compose = function() { _.compose = function() {
var funcs = arguments; var args = arguments;
var start = args.length - 1;
return function() { return function() {
var args = arguments; var i = start;
for (var i = funcs.length - 1; i >= 0; i--) { var result = args[start].apply(this, arguments);
args = [funcs[i].apply(this, args)]; while (i--) result = args[i].call(this, result);
} return result;
return args[0];
}; };
}; };
...@@ -786,6 +823,23 @@ ...@@ -786,6 +823,23 @@
}; };
}; };
// Returns a function that will only be executed before being called N times.
_.before = function(times, func) {
var memo;
return function() {
if (--times > 0) {
memo = func.apply(this, arguments);
} else {
func = null;
}
return memo;
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = _.partial(_.before, 2);
// Object Functions // Object Functions
// ---------------- // ----------------
...@@ -803,7 +857,7 @@ ...@@ -803,7 +857,7 @@
_.values = function(obj) { _.values = function(obj) {
var keys = _.keys(obj); var keys = _.keys(obj);
var length = keys.length; var length = keys.length;
var values = new Array(length); var values = Array(length);
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]]; values[i] = obj[keys[i]];
} }
...@@ -814,7 +868,7 @@ ...@@ -814,7 +868,7 @@
_.pairs = function(obj) { _.pairs = function(obj) {
var keys = _.keys(obj); var keys = _.keys(obj);
var length = keys.length; var length = keys.length;
var pairs = new Array(length); var pairs = Array(length);
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
pairs[i] = [keys[i], obj[keys[i]]]; pairs[i] = [keys[i], obj[keys[i]]];
} }
...@@ -843,45 +897,62 @@ ...@@ -843,45 +897,62 @@
// Extend a given object with all the properties in passed-in object(s). // Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) { _.extend = function(obj) {
each(slice.call(arguments, 1), function(source) { if (!_.isObject(obj)) return obj;
if (source) { var source, prop;
for (var prop in source) { for (var i = 1, length = arguments.length; i < length; i++) {
obj[prop] = source[prop]; source = arguments[i];
for (prop in source) {
if (hasOwnProperty.call(source, prop)) {
obj[prop] = source[prop];
} }
} }
}); }
return obj; return obj;
}; };
// Return a copy of the object only containing the whitelisted properties. // Return a copy of the object only containing the whitelisted properties.
_.pick = function(obj) { _.pick = function(obj, iteratee, context) {
var copy = {}; var result = {}, key;
var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); if (obj == null) return result;
each(keys, function(key) { if (_.isFunction(iteratee)) {
if (key in obj) copy[key] = obj[key]; iteratee = createCallback(iteratee, context);
}); for (key in obj) {
return copy; var value = obj[key];
if (iteratee(value, key, obj)) result[key] = value;
}
} else {
var keys = concat.apply([], slice.call(arguments, 1));
obj = new Object(obj);
for (var i = 0, length = keys.length; i < length; i++) {
key = keys[i];
if (key in obj) result[key] = obj[key];
}
}
return result;
}; };
// Return a copy of the object without the blacklisted properties. // Return a copy of the object without the blacklisted properties.
_.omit = function(obj) { _.omit = function(obj, iteratee, context) {
var copy = {}; if (_.isFunction(iteratee)) {
var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); iteratee = _.negate(iteratee);
for (var key in obj) { } else {
if (!_.contains(keys, key)) copy[key] = obj[key]; var keys = _.map(concat.apply([], slice.call(arguments, 1)), String);
iteratee = function(value, key) {
return !_.contains(keys, key);
};
} }
return copy; return _.pick(obj, iteratee, context);
}; };
// Fill in a given object with default properties. // Fill in a given object with default properties.
_.defaults = function(obj) { _.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) { if (!_.isObject(obj)) return obj;
if (source) { for (var i = 1, length = arguments.length; i < length; i++) {
for (var prop in source) { var source = arguments[i];
if (obj[prop] === void 0) obj[prop] = source[prop]; for (var prop in source) {
} if (obj[prop] === void 0) obj[prop] = source[prop];
} }
}); }
return obj; return obj;
}; };
...@@ -903,7 +974,7 @@ ...@@ -903,7 +974,7 @@
var eq = function(a, b, aStack, bStack) { var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical. // Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) return a !== 0 || 1 / a == 1 / b; if (a === b) return a !== 0 || 1 / a === 1 / b;
// A strict comparison is necessary because `null == undefined`. // A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b; if (a == null || b == null) return a === b;
// Unwrap any wrapped objects. // Unwrap any wrapped objects.
...@@ -911,29 +982,27 @@ ...@@ -911,29 +982,27 @@
if (b instanceof _) b = b._wrapped; if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names. // Compare `[[Class]]` names.
var className = toString.call(a); var className = toString.call(a);
if (className != toString.call(b)) return false; if (className !== toString.call(b)) return false;
switch (className) { switch (className) {
// Strings, numbers, dates, and booleans are compared by value. // Strings, numbers, regular expressions, dates, and booleans are compared by value.
case '[object RegExp]':
// RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
case '[object String]': case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`. // equivalent to `new String("5")`.
return a == String(b); return '' + a === '' + b;
case '[object Number]': case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for // `NaN`s are equivalent, but non-reflexive.
// other numeric values. // Object(NaN) is equivalent to NaN
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); if (+a !== +a) return +b !== +b;
// An `egal` comparison is performed for other numeric values.
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
case '[object Date]': case '[object Date]':
case '[object Boolean]': case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their // Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations // millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent. // of `NaN` are not equivalent.
return +a == +b; return +a === +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
} }
if (typeof a != 'object' || typeof b != 'object') return false; if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic // Assume equality for cyclic structures. The algorithm for detecting cyclic
...@@ -942,25 +1011,29 @@ ...@@ -942,25 +1011,29 @@
while (length--) { while (length--) {
// Linear search. Performance is inversely proportional to the number of // Linear search. Performance is inversely proportional to the number of
// unique nested structures. // unique nested structures.
if (aStack[length] == a) return bStack[length] == b; if (aStack[length] === a) return bStack[length] === b;
} }
// Objects with different constructors are not equivalent, but `Object`s // Objects with different constructors are not equivalent, but `Object`s
// from different frames are. // from different frames are.
var aCtor = a.constructor, bCtor = b.constructor; var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && if (
_.isFunction(bCtor) && (bCtor instanceof bCtor)) aCtor !== bCtor &&
&& ('constructor' in a && 'constructor' in b)) { // Handle Object.create(x) cases
'constructor' in a && 'constructor' in b &&
!(_.isFunction(aCtor) && aCtor instanceof aCtor &&
_.isFunction(bCtor) && bCtor instanceof bCtor)
) {
return false; return false;
} }
// Add the first object to the stack of traversed objects. // Add the first object to the stack of traversed objects.
aStack.push(a); aStack.push(a);
bStack.push(b); bStack.push(b);
var size = 0, result = true; var size, result;
// Recursively compare objects and arrays. // Recursively compare objects and arrays.
if (className == '[object Array]') { if (className === '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary. // Compare array lengths to determine if a deep comparison is necessary.
size = a.length; size = a.length;
result = size == b.length; result = size === b.length;
if (result) { if (result) {
// Deep compare the contents, ignoring non-numeric properties. // Deep compare the contents, ignoring non-numeric properties.
while (size--) { while (size--) {
...@@ -969,20 +1042,16 @@ ...@@ -969,20 +1042,16 @@
} }
} else { } else {
// Deep compare objects. // Deep compare objects.
for (var key in a) { var keys = _.keys(a), key;
if (_.has(a, key)) { size = keys.length;
// Count the expected number of properties. // Ensure that both objects contain the same number of properties before comparing deep equality.
size++; result = _.keys(b).length === size;
// Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
}
}
// Ensure that both objects contain the same number of properties.
if (result) { if (result) {
for (key in b) { while (size--) {
if (_.has(b, key) && !(size--)) break; // Deep compare each member
key = keys[size];
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
} }
result = !size;
} }
} }
// Remove the first object from the stack of traversed objects. // Remove the first object from the stack of traversed objects.
...@@ -1000,7 +1069,7 @@ ...@@ -1000,7 +1069,7 @@
// An "empty" object has no enumerable own-properties. // An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) { _.isEmpty = function(obj) {
if (obj == null) return true; if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; if (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false; for (var key in obj) if (_.has(obj, key)) return false;
return true; return true;
}; };
...@@ -1013,18 +1082,19 @@ ...@@ -1013,18 +1082,19 @@
// Is a given value an array? // Is a given value an array?
// Delegates to ECMA5's native Array.isArray // Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) { _.isArray = nativeIsArray || function(obj) {
return toString.call(obj) == '[object Array]'; return toString.call(obj) === '[object Array]';
}; };
// Is a given variable an object? // Is a given variable an object?
_.isObject = function(obj) { _.isObject = function(obj) {
return obj === Object(obj); var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
}; };
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_['is' + name] = function(obj) { _['is' + name] = function(obj) {
return toString.call(obj) == '[object ' + name + ']'; return toString.call(obj) === '[object ' + name + ']';
}; };
}); });
...@@ -1032,14 +1102,14 @@ ...@@ -1032,14 +1102,14 @@
// there isn't any inspectable "Arguments" type. // there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) { if (!_.isArguments(arguments)) {
_.isArguments = function(obj) { _.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee')); return _.has(obj, 'callee');
}; };
} }
// Optimize `isFunction` if appropriate. // Optimize `isFunction` if appropriate. Work around an IE 11 bug.
if (typeof (/./) !== 'function') { if (typeof /./ !== 'function') {
_.isFunction = function(obj) { _.isFunction = function(obj) {
return typeof obj === 'function'; return typeof obj == 'function' || false;
}; };
} }
...@@ -1050,12 +1120,12 @@ ...@@ -1050,12 +1120,12 @@
// Is the given value `NaN`? (NaN is the only number which does not equal itself). // Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) { _.isNaN = function(obj) {
return _.isNumber(obj) && obj != +obj; return _.isNumber(obj) && obj !== +obj;
}; };
// Is a given value a boolean? // Is a given value a boolean?
_.isBoolean = function(obj) { _.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
}; };
// Is a given value equal to null? // Is a given value equal to null?
...@@ -1071,7 +1141,7 @@ ...@@ -1071,7 +1141,7 @@
// Shortcut function for checking if an object has a given property directly // Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype). // on itself (in other words, not on a prototype).
_.has = function(obj, key) { _.has = function(obj, key) {
return hasOwnProperty.call(obj, key); return obj != null && hasOwnProperty.call(obj, key);
}; };
// Utility Functions // Utility Functions
...@@ -1084,17 +1154,19 @@ ...@@ -1084,17 +1154,19 @@
return this; return this;
}; };
// Keep the identity function around for default iterators. // Keep the identity function around for default iteratees.
_.identity = function(value) { _.identity = function(value) {
return value; return value;
}; };
_.constant = function(value) { _.constant = function(value) {
return function () { return function() {
return value; return value;
}; };
}; };
_.noop = function(){};
_.property = function(key) { _.property = function(key) {
return function(obj) { return function(obj) {
return obj[key]; return obj[key];
...@@ -1103,20 +1175,23 @@ ...@@ -1103,20 +1175,23 @@
// Returns a predicate for checking whether an object has a given set of `key:value` pairs. // Returns a predicate for checking whether an object has a given set of `key:value` pairs.
_.matches = function(attrs) { _.matches = function(attrs) {
var pairs = _.pairs(attrs), length = pairs.length;
return function(obj) { return function(obj) {
if (obj === attrs) return true; //avoid comparing an object to itself. if (obj == null) return !length;
for (var key in attrs) { obj = new Object(obj);
if (attrs[key] !== obj[key]) for (var i = 0; i < length; i++) {
return false; var pair = pairs[i], key = pair[0];
if (pair[1] !== obj[key] || !(key in obj)) return false;
} }
return true; return true;
} };
}; };
// Run a function **n** times. // Run a function **n** times.
_.times = function(n, iterator, context) { _.times = function(n, iteratee, context) {
var accum = Array(Math.max(0, n)); var accum = Array(Math.max(0, n));
for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); iteratee = createCallback(iteratee, context, 1);
for (var i = 0; i < n; i++) accum[i] = iteratee(i);
return accum; return accum;
}; };
...@@ -1130,54 +1205,44 @@ ...@@ -1130,54 +1205,44 @@
}; };
// A (possibly faster) way to get the current timestamp as an integer. // A (possibly faster) way to get the current timestamp as an integer.
_.now = Date.now || function() { return new Date().getTime(); }; _.now = Date.now || function() {
return new Date().getTime();
// List of HTML entities for escaping.
var entityMap = {
escape: {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;'
}
}; };
entityMap.unescape = _.invert(entityMap.escape);
// Regexes containing the keys and values listed immediately above. // List of HTML entities for escaping.
var entityRegexes = { var escapeMap = {
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), '&': '&amp;',
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') '<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'`': '&#x60;'
}; };
var unescapeMap = _.invert(escapeMap);
// Functions for escaping and unescaping strings to/from HTML interpolation. // Functions for escaping and unescaping strings to/from HTML interpolation.
_.each(['escape', 'unescape'], function(method) { var createEscaper = function(map) {
_[method] = function(string) { var escaper = function(match) {
if (string == null) return ''; return map[match];
return ('' + string).replace(entityRegexes[method], function(match) {
return entityMap[method][match];
});
}; };
}); // Regexes for identifying a key that needs to be escaped
var source = '(?:' + _.keys(map).join('|') + ')';
var testRegexp = RegExp(source);
var replaceRegexp = RegExp(source, 'g');
return function(string) {
string = string == null ? '' : '' + string;
return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
};
};
_.escape = createEscaper(escapeMap);
_.unescape = createEscaper(unescapeMap);
// If the value of the named `property` is a function then invoke it with the // If the value of the named `property` is a function then invoke it with the
// `object` as context; otherwise, return it. // `object` as context; otherwise, return it.
_.result = function(object, property) { _.result = function(object, property) {
if (object == null) return void 0; if (object == null) return void 0;
var value = object[property]; var value = object[property];
return _.isFunction(value) ? value.call(object) : value; return _.isFunction(value) ? object[property]() : value;
};
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result.call(this, func.apply(_, args));
};
});
}; };
// Generate a unique integer id (unique within the entire client session). // Generate a unique integer id (unique within the entire client session).
...@@ -1208,22 +1273,26 @@ ...@@ -1208,22 +1273,26 @@
'\\': '\\', '\\': '\\',
'\r': 'r', '\r': 'r',
'\n': 'n', '\n': 'n',
'\t': 't',
'\u2028': 'u2028', '\u2028': 'u2028',
'\u2029': 'u2029' '\u2029': 'u2029'
}; };
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
var escapeChar = function(match) {
return '\\' + escapes[match];
};
// JavaScript micro-templating, similar to John Resig's implementation. // JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace, // Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code. // and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) { // NB: `oldSettings` only exists for backwards compatibility.
var render; _.template = function(text, settings, oldSettings) {
if (!settings && oldSettings) settings = oldSettings;
settings = _.defaults({}, settings, _.templateSettings); settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation. // Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([ var matcher = RegExp([
(settings.escape || noMatch).source, (settings.escape || noMatch).source,
(settings.interpolate || noMatch).source, (settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source (settings.evaluate || noMatch).source
...@@ -1233,19 +1302,18 @@ ...@@ -1233,19 +1302,18 @@
var index = 0; var index = 0;
var source = "__p+='"; var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset) source += text.slice(index, offset).replace(escaper, escapeChar);
.replace(escaper, function(match) { return '\\' + escapes[match]; }); index = offset + match.length;
if (escape) { if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
} } else if (interpolate) {
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
} } else if (evaluate) {
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='"; source += "';\n" + evaluate + "\n__p+='";
} }
index = offset + match.length;
// Adobe VMs need the match returned to produce the correct offest.
return match; return match;
}); });
source += "';\n"; source += "';\n";
...@@ -1255,29 +1323,31 @@ ...@@ -1255,29 +1323,31 @@
source = "var __t,__p='',__j=Array.prototype.join," + source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" + "print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n"; source + 'return __p;\n';
try { try {
render = new Function(settings.variable || 'obj', '_', source); var render = new Function(settings.variable || 'obj', '_', source);
} catch (e) { } catch (e) {
e.source = source; e.source = source;
throw e; throw e;
} }
if (data) return render(data, _);
var template = function(data) { var template = function(data) {
return render.call(this, data, _); return render.call(this, data, _);
}; };
// Provide the compiled function source as a convenience for precompilation. // Provide the compiled source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; var argument = settings.variable || 'obj';
template.source = 'function(' + argument + '){\n' + source + '}';
return template; return template;
}; };
// Add a "chain" function, which will delegate to the wrapper. // Add a "chain" function. Start chaining a wrapped Underscore object.
_.chain = function(obj) { _.chain = function(obj) {
return _(obj).chain(); var instance = _(obj);
instance._chain = true;
return instance;
}; };
// OOP // OOP
...@@ -1291,42 +1361,44 @@ ...@@ -1291,42 +1361,44 @@
return this._chain ? _(obj).chain() : obj; return this._chain ? _(obj).chain() : obj;
}; };
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
_.each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result.call(this, func.apply(_, args));
};
});
};
// Add all of the Underscore functions to the wrapper object. // Add all of the Underscore functions to the wrapper object.
_.mixin(_); _.mixin(_);
// Add all mutator Array functions to the wrapper. // Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name]; var method = ArrayProto[name];
_.prototype[name] = function() { _.prototype[name] = function() {
var obj = this._wrapped; var obj = this._wrapped;
method.apply(obj, arguments); method.apply(obj, arguments);
if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
return result.call(this, obj); return result.call(this, obj);
}; };
}); });
// Add all accessor Array functions to the wrapper. // Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) { _.each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name]; var method = ArrayProto[name];
_.prototype[name] = function() { _.prototype[name] = function() {
return result.call(this, method.apply(this._wrapped, arguments)); return result.call(this, method.apply(this._wrapped, arguments));
}; };
}); });
_.extend(_.prototype, { // Extracts the result from a wrapped and chained object.
_.prototype.value = function() {
// Start chaining a wrapped Underscore object. return this._wrapped;
chain: function() { };
this._chain = true;
return this;
},
// Extracts the result from a wrapped and chained object.
value: function() {
return this._wrapped;
}
});
// AMD registration happens at the end for compatibility with AMD loaders // AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general // that may not enforce next-turn semantics on modules. Even though general
...@@ -1340,4 +1412,4 @@ ...@@ -1340,4 +1412,4 @@
return _; return _;
}); });
} }
}).call(this); }.call(this));
{
"private": true,
"dependencies": {
"backbone": "^1.1.2",
"backbone.localstorage": "^1.1.7",
"jquery": "^2.1.0",
"react": "^0.12.0",
"todomvc-app-css": "^1.0.0",
"todomvc-common": "^1.0.1",
"underscore": "^1.6.0"
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment