diff options
author | RaNaN <Mast3rRaNaN@hotmail.de> | 2012-12-17 20:23:18 +0100 |
---|---|---|
committer | RaNaN <Mast3rRaNaN@hotmail.de> | 2012-12-17 20:23:18 +0100 |
commit | f2cdd142f3f2d4c593f7004ae68e3f345e658334 (patch) | |
tree | 92859f8efc374d4d789d3cd5535c90b15acbf6bf /module/web/static/js | |
parent | fixes gzip middleware to do nothing when gzip is not available (diff) | |
download | pyload-f2cdd142f3f2d4c593f7004ae68e3f345e658334.tar.xz |
updated lots of libraries
Diffstat (limited to 'module/web/static/js')
-rw-r--r-- | module/web/static/js/default.js | 65 | ||||
-rw-r--r-- | module/web/static/js/libs/backbone-0.9.9.js (renamed from module/web/static/js/libs/backbone-0.9.2.js) | 986 | ||||
-rw-r--r-- | module/web/static/js/libs/jquery-1.8.3.js (renamed from module/web/static/js/libs/jquery-1.8.0.js) | 3437 | ||||
-rw-r--r-- | module/web/static/js/libs/jquery.peity-0.6.js | 184 | ||||
-rw-r--r-- | module/web/static/js/libs/jquery.peity-1.0.js | 246 | ||||
-rw-r--r-- | module/web/static/js/libs/jquery.transit-0.9.9.js (renamed from module/web/static/js/libs/jquery.transit-0.1.3.js) | 142 | ||||
-rw-r--r-- | module/web/static/js/libs/lodash-1.0.rc3.js (renamed from module/web/static/js/libs/lodash-0.7.0.js) | 2674 | ||||
-rw-r--r-- | module/web/static/js/libs/require-2.1.2.js (renamed from module/web/static/js/libs/require-2.0.6.js) | 788 | ||||
-rw-r--r-- | module/web/static/js/views/packageView.js | 1 |
9 files changed, 4595 insertions, 3928 deletions
diff --git a/module/web/static/js/default.js b/module/web/static/js/default.js index 3d02ba49e..8bd54cb25 100644 --- a/module/web/static/js/default.js +++ b/module/web/static/js/default.js @@ -3,41 +3,41 @@ require.config({ // XXX: To many dots in file breaks dependencies - paths:{ + paths: { - jquery:"libs/jquery-1.8.0", - jqueryui:"libs/jqueryui", - flot:"libs/jquery.flot-1.1", + jquery: "libs/jquery-1.8.3", + jqueryui: "libs/jqueryui", + flot: "libs/jquery.flot-1.1", flotpie: "libs/jquery.flot.pie", - peity: "libs/jquery.peity-0.6", - transit:"libs/jquery.transit-0.1.3", + peity: "libs/jquery.peity-1.0", + transit: "libs/jquery.transit-0.9.9", omniwindow: "libs/jquery.omniwindow", bootstrap: "libs/bootstrap-2.1.1", - underscore:"libs/lodash-0.7.0", - backbone:"libs/backbone-0.9.2", + underscore: "libs/lodash-1.0.rc3", + backbone: "libs/backbone-0.9.9", // handlebars: "libs/Handlebars-1.0rc1", // Plugins // hbs: "plugins/hbs-2.0.1", - text:"plugins/text-2.0.3", + text: "plugins/text-2.0.3", tpl: "../../templates" }, // Sets the configuration for your third party scripts that are not AMD compatible - shim:{ + shim: { - "backbone":{ - deps:["underscore", "jquery"], - exports:"Backbone" //attaches "Backbone" to the window object + "backbone": { + deps: ["underscore", "jquery"], + exports: "Backbone" //attaches "Backbone" to the window object }, - "flot" : ["jquery"], - "peity" : ["jquery"], - "flotpie" : ["flot"], - "transit" : ["jquery"], - "omniwindow" : ["jquery"], - "bootstrap" : ["jquery"] + "flot": ["jquery"], + "peity": ["jquery"], + "flotpie": ["flot"], + "transit": ["jquery"], + "omniwindow": ["jquery"], + "bootstrap": ["jquery"] } // end Shim Configuration // Handlebar Configuration @@ -49,20 +49,19 @@ require.config({ define('default', ['jquery', 'backbone', 'routers/defaultRouter', 'views/headerView', 'views/packageTreeView', 'utils/animations', 'bootstrap'], - function ($, Backbone, DefaultRouter, HeaderView, TreeView) { + function($, Backbone, DefaultRouter, HeaderView, TreeView) { + var init = function() { + var view = new HeaderView(); + view.render(); + }; - var init = function(){ - var view = new HeaderView(); - view.render(); - }; + var initPackageTree = function() { + $(function() { + var view = new TreeView(); + view.init(); + }); + }; - var initPackageTree = function() { - $(function() { - var view = new TreeView(); - view.init(); - }); - }; - - return {"init":init, "initPackageTree": initPackageTree}; -});
\ No newline at end of file + return {"init": init, "initPackageTree": initPackageTree}; + });
\ No newline at end of file diff --git a/module/web/static/js/libs/backbone-0.9.2.js b/module/web/static/js/libs/backbone-0.9.9.js index d0410b5c8..90e18d4ac 100644 --- a/module/web/static/js/libs/backbone-0.9.2.js +++ b/module/web/static/js/libs/backbone-0.9.9.js @@ -1,4 +1,4 @@ -// Backbone.js 0.9.2 +// Backbone.js 0.9.9 // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. // Backbone may be freely distributed under the MIT license. @@ -10,7 +10,7 @@ // Initial Setup // ------------- - // Save a reference to the global object (`window` in the browser, `global` + // Save a reference to the global object (`window` in the browser, `exports` // on the server). var root = this; @@ -18,9 +18,11 @@ // restored later on, if `noConflict` is used. var previousBackbone = root.Backbone; - // Create a local reference to slice/splice. - var slice = Array.prototype.slice; - var splice = Array.prototype.splice; + // Create a local reference to array methods. + var array = []; + var push = array.push; + var slice = array.slice; + var splice = array.splice; // The top-level namespace. All public Backbone classes and modules will // be attached to this. Exported for both CommonJS and the browser. @@ -32,23 +34,14 @@ } // Current version of the library. Keep in sync with `package.json`. - Backbone.VERSION = '0.9.2'; + Backbone.VERSION = '0.9.9'; // Require Underscore, if we're on the server, and it's not already present. var _ = root._; if (!_ && (typeof require !== 'undefined')) _ = require('underscore'); // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable. - var $ = root.jQuery || root.Zepto || root.ender; - - // Set the JavaScript library that will be used for DOM manipulation and - // Ajax calls (a.k.a. the `$` variable). By default Backbone will use: jQuery, - // Zepto, or Ender; but the `setDomLibrary()` method lets you inject an - // alternate JavaScript library (or a mock library for testing your views - // outside of a browser). - Backbone.setDomLibrary = function(lib) { - $ = lib; - }; + Backbone.$ = root.jQuery || root.Zepto || root.ender; // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable // to its previous owner. Returns a reference to this Backbone object. @@ -69,14 +62,51 @@ Backbone.emulateJSON = false; // Backbone.Events - // ----------------- + // --------------- - // Regular expression used to split event strings + // Regular expression used to split event strings. var eventSplitter = /\s+/; + // Implement fancy features of the Events API such as multiple event + // names `"change blur"` and jQuery-style event maps `{change: action}` + // in terms of the existing API. + var eventsApi = function(obj, action, name, rest) { + if (!name) return true; + if (typeof name === 'object') { + for (var key in name) { + obj[action].apply(obj, [key, name[key]].concat(rest)); + } + } else if (eventSplitter.test(name)) { + var names = name.split(eventSplitter); + for (var i = 0, l = names.length; i < l; i++) { + obj[action].apply(obj, [names[i]].concat(rest)); + } + } else { + return true; + } + }; + + // Optimized internal dispatch function for triggering events. Tries to + // keep the usual cases speedy (most Backbone events have 3 arguments). + var triggerEvents = function(obj, events, args) { + var ev, i = -1, l = events.length; + switch (args.length) { + case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); + return; + case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0]); + return; + case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1]); + return; + case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1], args[2]); + return; + default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); + } + }; + // A module that can be mixed in to *any object* in order to provide it with - // custom events. You may bind with `on` or remove with `off` callback functions - // to an event; trigger`-ing an event fires all callbacks in succession. + // custom events. You may bind with `on` or remove with `off` callback + // functions to an event; `trigger`-ing an event fires all callbacks in + // succession. // // var object = {}; // _.extend(object, Backbone.Events); @@ -85,58 +115,58 @@ // var Events = Backbone.Events = { - // Bind one or more space separated events, `events`, to a `callback` - // function. Passing `"all"` will bind the callback to all events fired. - on: function(events, callback, context) { - - var calls, event, node, tail, list; - if (!callback) return this; - events = events.split(eventSplitter); - calls = this._callbacks || (this._callbacks = {}); - - // Create an immutable callback list, allowing traversal during - // modification. The tail is an empty object that will always be used - // as the next node. - while (event = events.shift()) { - list = calls[event]; - node = list ? list.tail : {}; - node.next = tail = {}; - node.context = context; - node.callback = callback; - calls[event] = {tail: tail, next: list ? list.next : node}; - } - + // Bind one or more space separated events, or an events map, + // to a `callback` function. Passing `"all"` will bind the callback to + // all events fired. + on: function(name, callback, context) { + if (!(eventsApi(this, 'on', name, [callback, context]) && callback)) return this; + this._events || (this._events = {}); + var list = this._events[name] || (this._events[name] = []); + list.push({callback: callback, context: context, ctx: context || this}); return this; }, - // Remove one or many callbacks. If `context` is null, removes all callbacks - // with that function. If `callback` is null, removes all callbacks for the - // event. If `events` is null, removes all bound callbacks for all events. - off: function(events, callback, context) { - var event, calls, node, tail, cb, ctx; + // Bind events to only be triggered a single time. After the first time + // the callback is invoked, it will be removed. + once: function(name, callback, context) { + if (!(eventsApi(this, 'once', name, [callback, context]) && callback)) return this; + var self = this; + var once = _.once(function() { + self.off(name, once); + callback.apply(this, arguments); + }); + once._callback = callback; + this.on(name, once, context); + return this; + }, - // No events, or removing *all* events. - if (!(calls = this._callbacks)) return; - if (!(events || callback || context)) { - delete this._callbacks; + // Remove one or many callbacks. If `context` is null, removes all + // callbacks with that function. If `callback` is null, removes all + // callbacks for the event. If `events` is null, removes all bound + // callbacks for all events. + off: function(name, callback, context) { + var list, ev, events, names, i, l, j, k; + if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; + if (!name && !callback && !context) { + this._events = {}; return this; } - // Loop through the listed events and contexts, splicing them out of the - // linked list of callbacks if appropriate. - events = events ? events.split(eventSplitter) : _.keys(calls); - while (event = events.shift()) { - node = calls[event]; - delete calls[event]; - if (!node || !(callback || context)) continue; - // Create a new list, omitting the indicated callbacks. - tail = node.tail; - while ((node = node.next) !== tail) { - cb = node.callback; - ctx = node.context; - if ((callback && cb !== callback) || (context && ctx !== context)) { - this.on(event, cb, ctx); + names = name ? [name] : _.keys(this._events); + for (i = 0, l = names.length; i < l; i++) { + name = names[i]; + if (list = this._events[name]) { + events = []; + if (callback || context) { + for (j = 0, k = list.length; j < k; j++) { + ev = list[j]; + if ((callback && callback !== (ev.callback._callback || ev.callback)) || + (context && context !== ev.context)) { + events.push(ev); + } + } } + this._events[name] = events; } } @@ -147,40 +177,53 @@ // passed the same arguments as `trigger` is, apart from the event name // (unless you're listening on `"all"`, which will cause your callback to // receive the true name of the event as the first argument). - trigger: function(events) { - var event, node, calls, tail, args, all, rest; - if (!(calls = this._callbacks)) return this; - all = calls.all; - events = events.split(eventSplitter); - rest = slice.call(arguments, 1); - - // For each event, walk through the linked list of callbacks twice, - // first to trigger the event, then to trigger any `"all"` callbacks. - while (event = events.shift()) { - if (node = calls[event]) { - tail = node.tail; - while ((node = node.next) !== tail) { - node.callback.apply(node.context || this, rest); - } - } - if (node = all) { - tail = node.tail; - args = [event].concat(rest); - while ((node = node.next) !== tail) { - node.callback.apply(node.context || this, args); - } + trigger: function(name) { + if (!this._events) return this; + var args = slice.call(arguments, 1); + if (!eventsApi(this, 'trigger', name, args)) return this; + var events = this._events[name]; + var allEvents = this._events.all; + if (events) triggerEvents(this, events, args); + if (allEvents) triggerEvents(this, allEvents, arguments); + return this; + }, + + // An inversion-of-control version of `on`. Tell *this* object to listen to + // an event in another object ... keeping track of what it's listening to. + listenTo: function(object, events, callback) { + var listeners = this._listeners || (this._listeners = {}); + var id = object._listenerId || (object._listenerId = _.uniqueId('l')); + listeners[id] = object; + object.on(events, callback || this, this); + return this; + }, + + // Tell this object to stop listening to either specific events ... or + // to every object it's currently listening to. + stopListening: function(object, events, callback) { + var listeners = this._listeners; + if (!listeners) return; + if (object) { + object.off(events, callback, this); + if (!events && !callback) delete listeners[object._listenerId]; + } else { + for (var id in listeners) { + listeners[id].off(null, null, this); } + this._listeners = {}; } - return this; } - }; // Aliases for backwards compatibility. Events.bind = Events.on; Events.unbind = Events.off; + // Allow the `Backbone` object to serve as a global event bus, for folks who + // want global "pubsub" in a convenient place. + _.extend(Backbone, Events); + // Backbone.Model // -------------- @@ -188,23 +231,16 @@ // is automatically generated and assigned for you. var Model = Backbone.Model = function(attributes, options) { var defaults; - attributes || (attributes = {}); - if (options && options.parse) attributes = this.parse(attributes); - if (defaults = getValue(this, 'defaults')) { - attributes = _.extend({}, defaults, attributes); - } - if (options && options.collection) this.collection = options.collection; - this.attributes = {}; - this._escapedAttributes = {}; + var attrs = attributes || {}; this.cid = _.uniqueId('c'); this.changed = {}; - this._silent = {}; - this._pending = {}; - this.set(attributes, {silent: true}); - // Reset change tracking. - this.changed = {}; - this._silent = {}; - this._pending = {}; + this.attributes = {}; + this._changes = []; + if (options && options.collection) this.collection = options.collection; + if (options && options.parse) attrs = this.parse(attrs); + if (defaults = _.result(this, 'defaults')) _.defaults(attrs, defaults); + this.set(attrs, {silent: true}); + this._currentAttributes = _.clone(this.attributes); this._previousAttributes = _.clone(this.attributes); this.initialize.apply(this, arguments); }; @@ -215,14 +251,6 @@ // A hash of attributes whose current and previous value differ. changed: null, - // A hash of attributes that have silently changed since the last time - // `change` was called. Will become pending attributes on the next call. - _silent: null, - - // A hash of attributes that have changed since the last `'change'` event - // began. - _pending: null, - // The default name for the JSON `id` attribute is `"id"`. MongoDB and // CouchDB users may want to set this to `"_id"`. idAttribute: 'id', @@ -236,6 +264,11 @@ return _.clone(this.attributes); }, + // Proxy `Backbone.sync` by default. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + // Get the value of an attribute. get: function(attr) { return this.attributes[attr]; @@ -243,10 +276,7 @@ // Get the HTML-escaped value of an attribute. escape: function(attr) { - var html; - if (html = this._escapedAttributes[attr]) return html; - var val = this.get(attr); - return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val); + return _.escape(this.get(attr)); }, // Returns `true` if the attribute contains a value that is not null @@ -257,23 +287,21 @@ // Set a hash of model attributes on the object, firing `"change"` unless // you choose to silence it. - set: function(key, value, options) { - var attrs, attr, val; + set: function(key, val, options) { + var attr, attrs; + if (key == null) return this; // Handle both `"key", value` and `{key: value}` -style arguments. - if (_.isObject(key) || key == null) { + if (_.isObject(key)) { attrs = key; - options = value; + options = val; } else { - attrs = {}; - attrs[key] = value; + (attrs = {})[key] = val; } // Extract attributes and options. - options || (options = {}); - if (!attrs) return this; - if (attrs instanceof Model) attrs = attrs.attributes; - if (options.unset) for (attr in attrs) attrs[attr] = void 0; + var silent = options && options.silent; + var unset = options && options.unset; // Run validation. if (!this._validate(attrs, options)) return false; @@ -281,52 +309,38 @@ // Check for changes of `id`. if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; - var changes = options.changes = {}; var now = this.attributes; - var escaped = this._escapedAttributes; - var prev = this._previousAttributes || {}; // For each `set` attribute... for (attr in attrs) { val = attrs[attr]; - // If the new and current value differ, record the change. - if (!_.isEqual(now[attr], val) || (options.unset && _.has(now, attr))) { - delete escaped[attr]; - (options.silent ? this._silent : changes)[attr] = true; - } - - // Update or delete the current value. - options.unset ? delete now[attr] : now[attr] = val; - - // If the new and previous value differ, record the change. If not, - // then remove changes for this attribute. - if (!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) { - this.changed[attr] = val; - if (!options.silent) this._pending[attr] = true; - } else { - delete this.changed[attr]; - delete this._pending[attr]; - } + // Update or delete the current value, and track the change. + unset ? delete now[attr] : now[attr] = val; + this._changes.push(attr, val); } + // Signal that the model's state has potentially changed, and we need + // to recompute the actual changes. + this._hasComputed = false; + // Fire the `"change"` events. - if (!options.silent) this.change(options); + if (!silent) this.change(options); return this; }, // Remove an attribute from the model, firing `"change"` unless you choose // to silence it. `unset` is a noop if the attribute doesn't exist. unset: function(attr, options) { - (options || (options = {})).unset = true; - return this.set(attr, null, options); + return this.set(attr, void 0, _.extend({}, options, {unset: true})); }, // Clear all attributes on the model, firing `"change"` unless you choose // to silence it. clear: function(options) { - (options || (options = {})).unset = true; - return this.set(_.clone(this.attributes), options); + var attrs = {}; + for (var key in this.attributes) attrs[key] = void 0; + return this.set(attrs, _.extend({}, options, {unset: true})); }, // Fetch the model from the server. If the server's representation of the @@ -334,35 +348,34 @@ // triggering a `"change"` event. fetch: function(options) { options = options ? _.clone(options) : {}; + if (options.parse === void 0) options.parse = true; var model = this; var success = options.success; options.success = function(resp, status, xhr) { - if (!model.set(model.parse(resp, xhr), options)) return false; - if (success) success(model, resp); + if (!model.set(model.parse(resp), options)) return false; + if (success) success(model, resp, options); }; - options.error = Backbone.wrapError(options.error, model, options); - return (this.sync || Backbone.sync).call(this, 'read', this, options); + return this.sync('read', this, options); }, // Set a hash of model attributes, and sync the model to the server. // If the server returns an attributes hash that differs, the model's // state will be `set` again. - save: function(key, value, options) { - var attrs, current; + save: function(key, val, options) { + var attrs, current, done; - // Handle both `("key", value)` and `({key: value})` -style calls. - if (_.isObject(key) || key == null) { + // Handle both `"key", value` and `{key: value}` -style arguments. + if (key == null || _.isObject(key)) { attrs = key; - options = value; - } else { - attrs = {}; - attrs[key] = value; + options = val; + } else if (key != null) { + (attrs = {})[key] = val; } options = options ? _.clone(options) : {}; // If we're "wait"-ing to set changed attributes, validate early. if (options.wait) { - if (!this._validate(attrs, options)) return false; + if (attrs && !this._validate(attrs, options)) return false; current = _.clone(this.attributes); } @@ -372,29 +385,33 @@ return false; } + // Do not persist invalid models. + if (!attrs && !this._validate(null, options)) return false; + // After a successful server-side save, the client is (optionally) // updated with the server-side state. var model = this; var success = options.success; options.success = function(resp, status, xhr) { - var serverAttrs = model.parse(resp, xhr); - if (options.wait) { - delete options.wait; - serverAttrs = _.extend(attrs || {}, serverAttrs); - } + done = true; + var serverAttrs = model.parse(resp); + if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); if (!model.set(serverAttrs, options)) return false; - if (success) { - success(model, resp); - } else { - model.trigger('sync', model, resp, options); - } + if (success) success(model, resp, options); }; // Finish configuring and sending the Ajax request. - options.error = Backbone.wrapError(options.error, model, options); - var method = this.isNew() ? 'create' : 'update'; - var xhr = (this.sync || Backbone.sync).call(this, method, this, options); - if (options.wait) this.set(current, silentOptions); + var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); + if (method == 'patch') options.attrs = attrs; + var xhr = this.sync(method, this, options); + + // When using `wait`, reset attributes to original values unless + // `success` has been called already. + if (!done && options.wait) { + this.clear(silentOptions); + this.set(current, silentOptions); + } + return xhr; }, @@ -406,27 +423,22 @@ var model = this; var success = options.success; - var triggerDestroy = function() { + var destroy = function() { model.trigger('destroy', model, model.collection, options); }; + options.success = function(resp) { + if (options.wait || model.isNew()) destroy(); + if (success) success(model, resp, options); + }; + if (this.isNew()) { - triggerDestroy(); + options.success(); return false; } - options.success = function(resp) { - if (options.wait) triggerDestroy(); - if (success) { - success(model, resp); - } else { - model.trigger('sync', model, resp, options); - } - }; - - options.error = Backbone.wrapError(options.error, model, options); - var xhr = (this.sync || Backbone.sync).call(this, 'delete', this, options); - if (!options.wait) triggerDestroy(); + var xhr = this.sync('delete', this, options); + if (!options.wait) destroy(); return xhr; }, @@ -434,14 +446,14 @@ // using Backbone's restful methods, override this to change the endpoint // that will be called. url: function() { - var base = getValue(this, 'urlRoot') || getValue(this.collection, 'url') || urlError(); + var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError(); if (this.isNew()) return base; - return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id); + return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id); }, // **parse** converts a response into the hash of attributes to be `set` on // the model. The default implementation is just to pass the response along. - parse: function(resp, xhr) { + parse: function(resp) { return resp; }, @@ -459,30 +471,24 @@ // a `"change:attribute"` event for each changed attribute. // Calling this will cause all objects observing the model to update. change: function(options) { - options || (options = {}); var changing = this._changing; this._changing = true; - // Silent changes become pending changes. - for (var attr in this._silent) this._pending[attr] = true; + // Generate the changes to be triggered on the model. + var triggers = this._computeChanges(true); - // Silent changes are triggered. - var changes = _.extend({}, options.changes, this._silent); - this._silent = {}; - for (var attr in changes) { - this.trigger('change:' + attr, this, this.get(attr), options); + this._pending = !!triggers.length; + + for (var i = triggers.length - 2; i >= 0; i -= 2) { + this.trigger('change:' + triggers[i], this, triggers[i + 1], options); } + if (changing) return this; - // Continue firing `"change"` events while there are pending changes. - while (!_.isEmpty(this._pending)) { - this._pending = {}; + // Trigger a `change` while there have been changes. + while (this._pending) { + this._pending = false; this.trigger('change', this, options); - // Pending and silent changes still remain. - for (var attr in this.changed) { - if (this._pending[attr] || this._silent[attr]) continue; - delete this.changed[attr]; - } this._previousAttributes = _.clone(this.attributes); } @@ -493,7 +499,8 @@ // Determine if the model has changed since the last `"change"` event. // If you specify an attribute name, determine if that attribute has changed. hasChanged: function(attr) { - if (!arguments.length) return !_.isEmpty(this.changed); + if (!this._hasComputed) this._computeChanges(); + if (attr == null) return !_.isEmpty(this.changed); return _.has(this.changed, attr); }, @@ -513,10 +520,43 @@ return changed; }, + // Looking at the built up list of `set` attribute changes, compute how + // many of the attributes have actually changed. If `loud`, return a + // boiled-down list of only the real changes. + _computeChanges: function(loud) { + this.changed = {}; + var already = {}; + var triggers = []; + var current = this._currentAttributes; + var changes = this._changes; + + // Loop through the current queue of potential model changes. + for (var i = changes.length - 2; i >= 0; i -= 2) { + var key = changes[i], val = changes[i + 1]; + if (already[key]) continue; + already[key] = true; + + // Check if the attribute has been modified since the last change, + // and update `this.changed` accordingly. If we're inside of a `change` + // call, also add a trigger to the list. + if (current[key] !== val) { + this.changed[key] = val; + if (!loud) continue; + triggers.push(key, val); + current[key] = val; + } + } + if (loud) this._changes = []; + + // Signals `this.changed` is current to prevent duplicate calls from `this.hasChanged`. + this._hasComputed = true; + return triggers; + }, + // Get the previous value of an attribute, recorded at the time the last // `"change"` event was fired. previous: function(attr) { - if (!arguments.length || !this._previousAttributes) return null; + if (attr == null || !this._previousAttributes) return null; return this._previousAttributes[attr]; }, @@ -526,25 +566,16 @@ return _.clone(this._previousAttributes); }, - // Check if the model is currently in a valid state. It's only possible to - // get into an *invalid* state if you're using silent changes. - isValid: function() { - return !this.validate(this.attributes); - }, - // Run validation against the next complete set of model attributes, // returning `true` if all is well. If a specific `error` callback has // been passed, call that instead of firing the general `"error"` event. _validate: function(attrs, options) { - if (options.silent || !this.validate) return true; + if (!this.validate) return true; attrs = _.extend({}, this.attributes, attrs); var error = this.validate(attrs, options); if (!error) return true; - if (options && options.error) { - options.error(this, error, options); - } else { - this.trigger('error', this, error, options); - } + if (options && options.error) options.error(this, error, options); + this.trigger('error', this, error, options); return false; } @@ -559,10 +590,10 @@ var Collection = Backbone.Collection = function(models, options) { options || (options = {}); if (options.model) this.model = options.model; - if (options.comparator) this.comparator = options.comparator; + if (options.comparator !== void 0) this.comparator = options.comparator; this._reset(); this.initialize.apply(this, arguments); - if (models) this.reset(models, {silent: true, parse: options.parse}); + if (models) this.reset(models, _.extend({silent: true}, options)); }; // Define the Collection's inheritable methods. @@ -582,54 +613,65 @@ return this.map(function(model){ return model.toJSON(options); }); }, + // Proxy `Backbone.sync` by default. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + // Add a model, or list of models to the set. Pass **silent** to avoid // firing the `add` event for every new model. add: function(models, options) { - var i, index, length, model, cid, id, cids = {}, ids = {}, dups = []; - options || (options = {}); + var i, args, length, model, existing, needsSort; + var at = options && options.at; + var sort = ((options && options.sort) == null ? true : options.sort); models = _.isArray(models) ? models.slice() : [models]; - // Begin by turning bare objects into model references, and preventing - // invalid models or duplicate models from being added. - for (i = 0, length = models.length; i < length; i++) { - if (!(model = models[i] = this._prepareModel(models[i], options))) { - throw new Error("Can't add an invalid model to a collection"); + // Turn bare objects into model references, and prevent invalid models + // from being added. + for (i = models.length - 1; i >= 0; i--) { + if(!(model = this._prepareModel(models[i], options))) { + this.trigger("error", this, models[i], options); + models.splice(i, 1); + continue; } - cid = model.cid; - id = model.id; - if (cids[cid] || this._byCid[cid] || ((id != null) && (ids[id] || this._byId[id]))) { - dups.push(i); + models[i] = model; + + existing = model.id != null && this._byId[model.id]; + // If a duplicate is found, prevent it from being added and + // optionally merge it into the existing model. + if (existing || this._byCid[model.cid]) { + if (options && options.merge && existing) { + existing.set(model.attributes, options); + needsSort = sort; + } + models.splice(i, 1); continue; } - cids[cid] = ids[id] = model; - } - // Remove duplicates. - i = dups.length; - while (i--) { - models.splice(dups[i], 1); - } - - // Listen to added models' events, and index models for lookup by - // `id` and by `cid`. - for (i = 0, length = models.length; i < length; i++) { - (model = models[i]).on('all', this._onModelEvent, this); + // Listen to added models' events, and index models for lookup by + // `id` and by `cid`. + model.on('all', this._onModelEvent, this); this._byCid[model.cid] = model; if (model.id != null) this._byId[model.id] = model; } - // Insert models into the collection, re-sorting if needed, and triggering - // `add` events unless silenced. - this.length += length; - index = options.at != null ? options.at : this.models.length; - splice.apply(this.models, [index, 0].concat(models)); - if (this.comparator) this.sort({silent: true}); - if (options.silent) return this; - for (i = 0, length = this.models.length; i < length; i++) { - if (!cids[(model = this.models[i]).cid]) continue; - options.index = i; + // See if sorting is needed, update `length` and splice in new models. + if (models.length) needsSort = sort; + this.length += models.length; + args = [at != null ? at : this.models.length, 0]; + push.apply(args, models); + splice.apply(this.models, args); + + // Sort the collection if appropriate. + if (needsSort && this.comparator && at == null) this.sort({silent: true}); + + if (options && options.silent) return this; + + // Trigger `add` events. + while (model = models.shift()) { model.trigger('add', model, this, options); } + return this; }, @@ -640,7 +682,7 @@ options || (options = {}); models = _.isArray(models) ? models.slice() : [models]; for (i = 0, l = models.length; i < l; i++) { - model = this.getByCid(models[i]) || this.get(models[i]); + model = this.get(models[i]); if (!model) continue; delete this._byId[model.id]; delete this._byCid[model.cid]; @@ -659,7 +701,7 @@ // Add a model to the end of the collection. push: function(model, options) { model = this._prepareModel(model, options); - this.add(model, options); + this.add(model, _.extend({at: this.length}, options)); return model; }, @@ -684,15 +726,15 @@ return model; }, - // Get a model from the set by id. - get: function(id) { - if (id == null) return void 0; - return this._byId[id.id != null ? id.id : id]; + // Slice out a sub-array of models from the collection. + slice: function(begin, end) { + return this.models.slice(begin, end); }, - // Get a model from the set by client id. - getByCid: function(cid) { - return cid && this._byCid[cid.cid || cid]; + // Get a model from the set by id. + get: function(obj) { + if (obj == null) return void 0; + return this._byId[obj.id != null ? obj.id : obj] || this._byCid[obj.cid || obj]; }, // Get the model at the given index. @@ -715,34 +757,74 @@ // normal circumstances, as the set will maintain sort order as each item // is added. sort: function(options) { - options || (options = {}); - if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); - var boundComparator = _.bind(this.comparator, this); - if (this.comparator.length == 1) { - this.models = this.sortBy(boundComparator); + if (!this.comparator) { + throw new Error('Cannot sort a set without a comparator'); + } + + if (_.isString(this.comparator) || this.comparator.length === 1) { + this.models = this.sortBy(this.comparator, this); } else { - this.models.sort(boundComparator); + this.models.sort(_.bind(this.comparator, this)); } - if (!options.silent) this.trigger('reset', this, options); + + if (!options || !options.silent) this.trigger('sort', this, options); return this; }, // Pluck an attribute from each model in the collection. pluck: function(attr) { - return _.map(this.models, function(model){ return model.get(attr); }); + return _.invoke(this.models, 'get', attr); + }, + + // Smartly update a collection with a change set of models, adding, + // removing, and merging as necessary. + update: function(models, options) { + var model, i, l, existing; + var add = [], remove = [], modelMap = {}; + var idAttr = this.model.prototype.idAttribute; + options = _.extend({add: true, merge: true, remove: true}, options); + if (options.parse) models = this.parse(models); + + // Allow a single model (or no argument) to be passed. + if (!_.isArray(models)) models = models ? [models] : []; + + // Proxy to `add` for this case, no need to iterate... + if (options.add && !options.remove) return this.add(models, options); + + // Determine which models to add and merge, and which to remove. + for (i = 0, l = models.length; i < l; i++) { + model = models[i]; + existing = this.get(model.id || model.cid || model[idAttr]); + if (options.remove && existing) modelMap[existing.cid] = true; + if ((options.add && !existing) || (options.merge && existing)) { + add.push(model); + } + } + if (options.remove) { + for (i = 0, l = this.models.length; i < l; i++) { + model = this.models[i]; + if (!modelMap[model.cid]) remove.push(model); + } + } + + // Remove models (if applicable) before we add and merge the rest. + if (remove.length) this.remove(remove, options); + if (add.length) this.add(add, options); + return this; }, // When you have more items than you want to add or remove individually, // you can reset the entire set with a new list of models, without firing // any `add` or `remove` events. Fires `reset` when finished. reset: function(models, options) { - models || (models = []); options || (options = {}); + if (options.parse) models = this.parse(models); for (var i = 0, l = this.models.length; i < l; i++) { this._removeReference(this.models[i]); } + options.previousModels = this.models; this._reset(); - this.add(models, _.extend({silent: true}, options)); + if (models) this.add(models, _.extend({silent: true}, options)); if (!options.silent) this.trigger('reset', this, options); return this; }, @@ -752,34 +834,30 @@ // models to the collection instead of resetting. fetch: function(options) { options = options ? _.clone(options) : {}; - if (options.parse === undefined) options.parse = true; + if (options.parse === void 0) options.parse = true; var collection = this; var success = options.success; options.success = function(resp, status, xhr) { - collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options); - if (success) success(collection, resp); + var method = options.update ? 'update' : 'reset'; + collection[method](resp, options); + if (success) success(collection, resp, options); }; - options.error = Backbone.wrapError(options.error, collection, options); - return (this.sync || Backbone.sync).call(this, 'read', this, options); + return this.sync('read', this, options); }, // Create a new instance of a model in this collection. Add the model to the // collection immediately, unless `wait: true` is passed, in which case we // wait for the server to agree. create: function(model, options) { - var coll = this; + var collection = this; options = options ? _.clone(options) : {}; model = this._prepareModel(model, options); if (!model) return false; - if (!options.wait) coll.add(model, options); + if (!options.wait) collection.add(model, options); var success = options.success; - options.success = function(nextModel, resp, xhr) { - if (options.wait) coll.add(nextModel, options); - if (success) { - success(nextModel, resp); - } else { - nextModel.trigger('sync', model, resp, options); - } + options.success = function(model, resp, options) { + if (options.wait) collection.add(model, options); + if (success) success(model, resp, options); }; model.save(null, options); return model; @@ -787,19 +865,24 @@ // **parse** converts a response into a list of models to be added to the // collection. The default implementation is just to pass it through. - parse: function(resp, xhr) { + parse: function(resp) { return resp; }, + // Create a new collection with an identical list of models as this one. + clone: function() { + return new this.constructor(this.models); + }, + // Proxy to _'s chain. Can't be proxied the same way the rest of the // underscore methods are proxied because it relies on the underscore // constructor. - chain: function () { + chain: function() { return _(this.models).chain(); }, // Reset all internal state. Called when the collection is reset. - _reset: function(options) { + _reset: function() { this.length = 0; this.models = []; this._byId = {}; @@ -807,24 +890,21 @@ }, // Prepare a model or hash of attributes to be added to this collection. - _prepareModel: function(model, options) { - options || (options = {}); - if (!(model instanceof Model)) { - var attrs = model; - options.collection = this; - model = new this.model(attrs, options); - if (!model._validate(model.attributes, options)) model = false; - } else if (!model.collection) { - model.collection = this; + _prepareModel: function(attrs, options) { + if (attrs instanceof Model) { + if (!attrs.collection) attrs.collection = this; + return attrs; } + options || (options = {}); + options.collection = this; + var model = new this.model(attrs, options); + if (!model._validate(attrs, options)) return false; return model; }, // Internal method to remove a model's ties to a collection. _removeReference: function(model) { - if (this == model.collection) { - delete model.collection; - } + if (this === model.collection) delete model.collection; model.off('all', this._onModelEvent, this); }, @@ -833,13 +913,11 @@ // events simply proxy through. "add" and "remove" events that originate // in other collections are ignored. _onModelEvent: function(event, model, collection, options) { - if ((event == 'add' || event == 'remove') && collection != this) return; - if (event == 'destroy') { - this.remove(model, options); - } + if ((event === 'add' || event === 'remove') && collection !== this) return; + if (event === 'destroy') this.remove(model, options); if (model && event === 'change:' + model.idAttribute) { delete this._byId[model.previous(model.idAttribute)]; - this._byId[model.id] = model; + if (model.id != null) this._byId[model.id] = model; } this.trigger.apply(this, arguments); } @@ -847,21 +925,37 @@ }); // Underscore methods that we want to implement on the Collection. - var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', - 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', - 'include', 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', - 'toArray', 'size', 'first', 'initial', 'rest', 'last', 'without', 'indexOf', - 'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy']; + var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', + 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', + 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', + 'max', 'min', 'sortedIndex', 'toArray', 'size', 'first', 'head', 'take', + 'initial', 'rest', 'tail', 'last', 'without', 'indexOf', 'shuffle', + 'lastIndexOf', 'isEmpty']; // Mix in each Underscore method as a proxy to `Collection#models`. _.each(methods, function(method) { Collection.prototype[method] = function() { - return _[method].apply(_, [this.models].concat(_.toArray(arguments))); + var args = slice.call(arguments); + args.unshift(this.models); + return _[method].apply(_, args); + }; + }); + + // Underscore methods that take a property name as an argument. + var attributeMethods = ['groupBy', 'countBy', 'sortBy']; + + // Use attributes instead of properties. + _.each(attributeMethods, function(method) { + Collection.prototype[method] = function(value, context) { + var iterator = _.isFunction(value) ? value : function(model) { + return model.get(value); + }; + return _[method](this.models, iterator, context); }; }); // Backbone.Router - // ------------------- + // --------------- // Routers map faux-URLs to actions, and fire events when routes are // matched. Creating a new one sets its `routes` hash, if not set statically. @@ -874,9 +968,10 @@ // Cached regular expressions for matching named param parts and splatted // parts of route strings. + var optionalParam = /\((.*?)\)/g; var namedParam = /:\w+/g; var splatParam = /\*\w+/g; - var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g; + var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; // Set up all inheritable **Backbone.Router** properties and methods. _.extend(Router.prototype, Events, { @@ -892,7 +987,6 @@ // }); // route: function(route, name, callback) { - Backbone.history || (Backbone.history = new History); if (!_.isRegExp(route)) route = this._routeToRegExp(route); if (!callback) callback = this[name]; Backbone.history.route(route, _.bind(function(fragment) { @@ -907,6 +1001,7 @@ // Simple proxy to `Backbone.history` to save a fragment into the history. navigate: function(fragment, options) { Backbone.history.navigate(fragment, options); + return this; }, // Bind all defined routes to `Backbone.history`. We have to reverse the @@ -914,12 +1009,9 @@ // routes can be defined at the bottom of the route map. _bindRoutes: function() { if (!this.routes) return; - var routes = []; - for (var route in this.routes) { - routes.unshift([route, this.routes[route]]); - } - for (var i = 0, l = routes.length; i < l; i++) { - this.route(routes[i][0], routes[i][1], this[routes[i][1]]); + var route, routes = _.keys(this.routes); + while ((route = routes.pop()) != null) { + this.route(route, this.routes[route]); } }, @@ -927,6 +1019,7 @@ // against the current location hash. _routeToRegExp: function(route) { route = route.replace(escapeRegExp, '\\$&') + .replace(optionalParam, '(?:$1)?') .replace(namedParam, '([^\/]+)') .replace(splatParam, '(.*?)'); return new RegExp('^' + route + '$'); @@ -948,14 +1041,26 @@ var History = Backbone.History = function() { this.handlers = []; _.bindAll(this, 'checkUrl'); + + // Ensure that `History` can be used outside of the browser. + if (typeof window !== 'undefined') { + this.location = window.location; + this.history = window.history; + } }; - // Cached regex for cleaning leading hashes and slashes . - var routeStripper = /^[#\/]/; + // Cached regex for stripping a leading hash/slash and trailing space. + var routeStripper = /^[#\/]|\s+$/g; + + // Cached regex for stripping leading and trailing slashes. + var rootStripper = /^\/+|\/+$/g; // Cached regex for detecting MSIE. var isExplorer = /msie [\w.]+/; + // Cached regex for removing a trailing slash. + var trailingSlash = /\/$/; + // Has the history handling already been started? History.started = false; @@ -968,9 +1073,8 @@ // Gets the true hash value. Cannot use location.hash directly due to bug // in Firefox where location.hash will always be decoded. - getHash: function(windowOverride) { - var loc = windowOverride ? windowOverride.location : window.location; - var match = loc.href.match(/#(.*)$/); + getHash: function(window) { + var match = (window || this).location.href.match(/#(.*)$/); return match ? match[1] : ''; }, @@ -978,15 +1082,14 @@ // the hash, or the override. getFragment: function(fragment, forcePushState) { if (fragment == null) { - if (this._hasPushState || forcePushState) { - fragment = window.location.pathname; - var search = window.location.search; - if (search) fragment += search; + if (this._hasPushState || !this._wantsHashChange || forcePushState) { + fragment = this.location.pathname; + var root = this.root.replace(trailingSlash, ''); + if (!fragment.indexOf(root)) fragment = fragment.substr(root.length); } else { fragment = this.getHash(); } } - if (!fragment.indexOf(this.options.root)) fragment = fragment.substr(this.options.root.length); return fragment.replace(routeStripper, ''); }, @@ -999,24 +1102,28 @@ // Figure out the initial configuration. Do we need an iframe? // Is pushState desired ... is it available? this.options = _.extend({}, {root: '/'}, this.options, options); + this.root = this.options.root; this._wantsHashChange = this.options.hashChange !== false; this._wantsPushState = !!this.options.pushState; - this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState); + this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); var fragment = this.getFragment(); var docMode = document.documentMode; var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); - if (oldIE) { - this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow; + // Normalize root to always include a leading and trailing slash. + this.root = ('/' + this.root + '/').replace(rootStripper, '/'); + + if (oldIE && this._wantsHashChange) { + this.iframe = Backbone.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow; this.navigate(fragment); } // Depending on whether we're using pushState or hashes, and whether // 'onhashchange' is supported, determine how we check the URL state. if (this._hasPushState) { - $(window).bind('popstate', this.checkUrl); + Backbone.$(window).bind('popstate', this.checkUrl); } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) { - $(window).bind('hashchange', this.checkUrl); + Backbone.$(window).bind('hashchange', this.checkUrl); } else if (this._wantsHashChange) { this._checkUrlInterval = setInterval(this.checkUrl, this.interval); } @@ -1024,14 +1131,14 @@ // Determine if we need to change the base url, for a pushState link // opened by a non-pushState browser. this.fragment = fragment; - var loc = window.location; - var atRoot = loc.pathname == this.options.root; + var loc = this.location; + var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === this.root; // If we've started off with a route from a `pushState`-enabled browser, // but we're currently in a browser that doesn't support it... if (this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) { this.fragment = this.getFragment(null, true); - window.location.replace(this.options.root + '#' + this.fragment); + this.location.replace(this.root + this.location.search + '#' + this.fragment); // Return immediately as browser will do redirect to new url return true; @@ -1039,18 +1146,16 @@ // in a browser where it could be `pushState`-based instead... } else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) { this.fragment = this.getHash().replace(routeStripper, ''); - window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment); + this.history.replaceState({}, document.title, this.root + this.fragment + loc.search); } - if (!this.options.silent) { - return this.loadUrl(); - } + if (!this.options.silent) return this.loadUrl(); }, // Disable Backbone.history, perhaps temporarily. Not useful in a real app, // but possibly useful for unit testing Routers. stop: function() { - $(window).unbind('popstate', this.checkUrl).unbind('hashchange', this.checkUrl); + Backbone.$(window).unbind('popstate', this.checkUrl).unbind('hashchange', this.checkUrl); clearInterval(this._checkUrlInterval); History.started = false; }, @@ -1065,8 +1170,10 @@ // calls `loadUrl`, normalizing across the hidden iframe. checkUrl: function(e) { var current = this.getFragment(); - if (current == this.fragment && this.iframe) current = this.getFragment(this.getHash(this.iframe)); - if (current == this.fragment) return false; + if (current === this.fragment && this.iframe) { + current = this.getFragment(this.getHash(this.iframe)); + } + if (current === this.fragment) return false; if (this.iframe) this.navigate(current); this.loadUrl() || this.loadUrl(this.getHash()); }, @@ -1095,31 +1202,31 @@ navigate: function(fragment, options) { if (!History.started) return false; if (!options || options === true) options = {trigger: options}; - var frag = (fragment || '').replace(routeStripper, ''); - if (this.fragment == frag) return; + fragment = this.getFragment(fragment || ''); + if (this.fragment === fragment) return; + this.fragment = fragment; + var url = this.root + fragment; // If pushState is available, we use it to set the fragment as a real URL. if (this._hasPushState) { - if (frag.indexOf(this.options.root) != 0) frag = this.options.root + frag; - this.fragment = frag; - window.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, frag); + this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url); // If hash changes haven't been explicitly disabled, update the hash // fragment to store history. } else if (this._wantsHashChange) { - this.fragment = frag; - this._updateHash(window.location, frag, options.replace); - if (this.iframe && (frag != this.getFragment(this.getHash(this.iframe)))) { - // Opening and closing the iframe tricks IE7 and earlier to push a history entry on hash-tag change. - // When replace is true, we don't want this. + this._updateHash(this.location, fragment, options.replace); + if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) { + // Opening and closing the iframe tricks IE7 and earlier to push a + // history entry on hash-tag change. When replace is true, we don't + // want this. if(!options.replace) this.iframe.document.open().close(); - this._updateHash(this.iframe.location, frag, options.replace); + this._updateHash(this.iframe.location, fragment, options.replace); } // If you've told us that you explicitly don't want fallback hashchange- // based history, then `navigate` becomes a page refresh. } else { - window.location.assign(this.options.root + fragment); + return this.location.assign(url); } if (options.trigger) this.loadUrl(fragment); }, @@ -1128,13 +1235,19 @@ // a new one to the browser history. _updateHash: function(location, fragment, replace) { if (replace) { - location.replace(location.toString().replace(/(javascript:|#).*$/, '') + '#' + fragment); + var href = location.href.replace(/(javascript:|#).*$/, ''); + location.replace(href + '#' + fragment); } else { - location.hash = fragment; + // Some browsers require that `hash` contains a leading #. + location.hash = '#' + fragment; } } + }); + // Create the default Backbone.history. + Backbone.history = new History; + // Backbone.View // ------------- @@ -1152,7 +1265,7 @@ var delegateEventSplitter = /^(\S+)\s*(.*)$/; // List of view options to be merged as properties. - var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName']; + var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; // Set up all inheritable **Backbone.View** properties and methods. _.extend(View.prototype, Events, { @@ -1177,10 +1290,11 @@ return this; }, - // Remove this view from the DOM. Note that the view isn't present in the - // DOM by default, so calling this method may be a no-op. + // Remove this view by taking the element out of the DOM, and removing any + // applicable Backbone.Events listeners. remove: function() { this.$el.remove(); + this.stopListening(); return this; }, @@ -1191,8 +1305,8 @@ // make: function(tagName, attributes, content) { var el = document.createElement(tagName); - if (attributes) $(el).attr(attributes); - if (content) $(el).html(content); + if (attributes) Backbone.$(el).attr(attributes); + if (content != null) Backbone.$(el).html(content); return el; }, @@ -1200,7 +1314,7 @@ // re-delegation. setElement: function(element, delegate) { if (this.$el) this.undelegateEvents(); - this.$el = (element instanceof $) ? element : $(element); + this.$el = element instanceof Backbone.$ ? element : Backbone.$(element); this.el = this.$el[0]; if (delegate !== false) this.delegateEvents(); return this; @@ -1222,7 +1336,7 @@ // This only works for delegate-able events: not `focus`, `blur`, and // not `change`, `submit`, and `reset` in Internet Explorer. delegateEvents: function(events) { - if (!(events || (events = getValue(this, 'events')))) return; + if (!(events || (events = _.result(this, 'events')))) return; this.undelegateEvents(); for (var key in events) { var method = events[key]; @@ -1251,11 +1365,8 @@ // Keys with special meaning *(model, collection, id, className)*, are // attached directly to the view. _configure: function(options) { - if (this.options) options = _.extend({}, this.options, options); - for (var i = 0, l = viewOptions.length; i < l; i++) { - var attr = viewOptions[i]; - if (options[attr]) this[attr] = options[attr]; - } + if (this.options) options = _.extend({}, _.result(this, 'options'), options); + _.extend(this, _.pick(options, viewOptions)); this.options = options; }, @@ -1265,27 +1376,17 @@ // an element from the `id`, `className` and `tagName` properties. _ensureElement: function() { if (!this.el) { - var attrs = getValue(this, 'attributes') || {}; - if (this.id) attrs.id = this.id; - if (this.className) attrs['class'] = this.className; - this.setElement(this.make(this.tagName, attrs), false); + var attrs = _.extend({}, _.result(this, 'attributes')); + if (this.id) attrs.id = _.result(this, 'id'); + if (this.className) attrs['class'] = _.result(this, 'className'); + this.setElement(this.make(_.result(this, 'tagName'), attrs), false); } else { - this.setElement(this.el, false); + this.setElement(_.result(this, 'el'), false); } } }); - // The self-propagating extend function that Backbone classes use. - var extend = function (protoProps, classProps) { - var child = inherits(this, protoProps, classProps); - child.extend = this.extend; - return child; - }; - - // Set up inheritance for the model, collection, and view. - Model.extend = Collection.extend = Router.extend = View.extend = extend; - // Backbone.sync // ------------- @@ -1293,6 +1394,7 @@ var methodMap = { 'create': 'POST', 'update': 'PUT', + 'patch': 'PATCH', 'delete': 'DELETE', 'read': 'GET' }; @@ -1316,116 +1418,116 @@ var type = methodMap[method]; // Default options, unless specified. - options || (options = {}); + _.defaults(options || (options = {}), { + emulateHTTP: Backbone.emulateHTTP, + emulateJSON: Backbone.emulateJSON + }); // Default JSON-request options. var params = {type: type, dataType: 'json'}; // Ensure that we have a URL. if (!options.url) { - params.url = getValue(model, 'url') || urlError(); + params.url = _.result(model, 'url') || urlError(); } // Ensure that we have the appropriate request data. - if (!options.data && model && (method == 'create' || method == 'update')) { + if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { params.contentType = 'application/json'; - params.data = JSON.stringify(model.toJSON()); + params.data = JSON.stringify(options.attrs || model.toJSON(options)); } // For older servers, emulate JSON by encoding the request into an HTML-form. - if (Backbone.emulateJSON) { + if (options.emulateJSON) { params.contentType = 'application/x-www-form-urlencoded'; params.data = params.data ? {model: params.data} : {}; } // For older servers, emulate HTTP by mimicking the HTTP method with `_method` // And an `X-HTTP-Method-Override` header. - if (Backbone.emulateHTTP) { - if (type === 'PUT' || type === 'DELETE') { - if (Backbone.emulateJSON) params.data._method = type; - params.type = 'POST'; - params.beforeSend = function(xhr) { - xhr.setRequestHeader('X-HTTP-Method-Override', type); - }; - } + if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { + params.type = 'POST'; + if (options.emulateJSON) params.data._method = type; + var beforeSend = options.beforeSend; + options.beforeSend = function(xhr) { + xhr.setRequestHeader('X-HTTP-Method-Override', type); + if (beforeSend) return beforeSend.apply(this, arguments); + }; } // Don't process data on a non-GET request. - if (params.type !== 'GET' && !Backbone.emulateJSON) { + if (params.type !== 'GET' && !options.emulateJSON) { params.processData = false; } + var success = options.success; + options.success = function(resp, status, xhr) { + if (success) success(resp, status, xhr); + model.trigger('sync', model, resp, options); + }; + + var error = options.error; + options.error = function(xhr, status, thrown) { + if (error) error(model, xhr, options); + model.trigger('error', model, xhr, options); + }; + // Make the request, allowing the user to override any Ajax options. - return $.ajax(_.extend(params, options)); + var xhr = Backbone.ajax(_.extend(params, options)); + model.trigger('request', model, xhr, options); + return xhr; }; - // Wrap an optional error callback with a fallback error event. - Backbone.wrapError = function(onError, originalModel, options) { - return function(model, resp) { - resp = model === originalModel ? resp : model; - if (onError) { - onError(originalModel, resp, options); - } else { - originalModel.trigger('error', originalModel, resp, options); - } - }; + // Set the default implementation of `Backbone.ajax` to proxy through to `$`. + Backbone.ajax = function() { + return Backbone.$.ajax.apply(Backbone.$, arguments); }; // Helpers // ------- - // Shared empty constructor function to aid in prototype-chain creation. - var ctor = function(){}; - // Helper function to correctly set up the prototype chain, for subclasses. // Similar to `goog.inherits`, but uses a hash of prototype properties and // class properties to be extended. - var inherits = function(parent, protoProps, staticProps) { + var extend = function(protoProps, staticProps) { + var parent = this; var child; // The constructor function for the new subclass is either defined by you // (the "constructor" property in your `extend` definition), or defaulted // by us to simply call the parent's constructor. - if (protoProps && protoProps.hasOwnProperty('constructor')) { + if (protoProps && _.has(protoProps, 'constructor')) { child = protoProps.constructor; } else { child = function(){ parent.apply(this, arguments); }; } - // Inherit class (static) properties from parent. - _.extend(child, parent); + // Add static properties to the constructor function, if supplied. + _.extend(child, parent, staticProps); // Set the prototype chain to inherit from `parent`, without calling // `parent`'s constructor function. - ctor.prototype = parent.prototype; - child.prototype = new ctor(); + var Surrogate = function(){ this.constructor = child; }; + Surrogate.prototype = parent.prototype; + child.prototype = new Surrogate; // Add prototype properties (instance properties) to the subclass, // if supplied. if (protoProps) _.extend(child.prototype, protoProps); - // Add static properties to the constructor function, if supplied. - if (staticProps) _.extend(child, staticProps); - - // Correctly set child's `prototype.constructor`. - child.prototype.constructor = child; - - // Set a convenience property in case the parent's prototype is needed later. + // Set a convenience property in case the parent's prototype is needed + // later. child.__super__ = parent.prototype; return child; }; - // Helper function to get a value from a Backbone object as a property - // or as a function. - var getValue = function(object, prop) { - if (!(object && object[prop])) return null; - return _.isFunction(object[prop]) ? object[prop]() : object[prop]; - }; + // Set up inheritance for the model, collection, router, view and history. + Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend; // Throw an error when a URL is needed, and none is supplied. var urlError = function() { throw new Error('A "url" property or function must be specified'); }; -}).call(this);
\ No newline at end of file +}).call(this); diff --git a/module/web/static/js/libs/jquery-1.8.0.js b/module/web/static/js/libs/jquery-1.8.3.js index 43991b385..a86bf797a 100644 --- a/module/web/static/js/libs/jquery-1.8.0.js +++ b/module/web/static/js/libs/jquery-1.8.3.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v1.8.0 + * jQuery JavaScript Library v1.8.3 * http://jquery.com/ * * Includes Sizzle.js @@ -9,7 +9,7 @@ * Released under the MIT license * http://jquery.org/license * - * Date: Thu Aug 09 2012 16:24:48 GMT-0400 (Eastern Daylight Time) + * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time) */ (function( window, undefined ) { var @@ -51,8 +51,8 @@ var core_rnotwhite = /\S/, core_rspace = /\s+/, - // IE doesn't match non-breaking spaces with \s - rtrim = core_rnotwhite.test("\xA0") ? (/^[\s\xA0]+|[\s\xA0]+$/g) : /^\s+|\s+$/g, + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, // A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) @@ -186,7 +186,7 @@ jQuery.fn = jQuery.prototype = { selector: "", // The current version of jQuery being used - jquery: "1.8.0", + jquery: "1.8.3", // The default length of a jQuery object is 0 length: 0, @@ -573,7 +573,7 @@ jQuery.extend({ }, nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }, // args is for internal usage only @@ -619,7 +619,7 @@ jQuery.extend({ }, // Use native String.trim function wherever possible - trim: core_trim ? + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? function( text ) { return text == null ? "" : @@ -630,7 +630,7 @@ jQuery.extend({ function( text ) { return text == null ? "" : - text.toString().replace( rtrim, "" ); + ( text + "" ).replace( rtrim, "" ); }, // results is for internal usage only @@ -776,7 +776,7 @@ jQuery.extend({ }; // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy; }, @@ -844,9 +844,10 @@ jQuery.ready.promise = function( obj ) { readyList = jQuery.Deferred(); - // Catch cases where $(document).ready() is called after the - // browser event has already occurred. - if ( document.readyState === "complete" || ( document.readyState !== "loading" && document.addEventListener ) ) { + // 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 + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready setTimeout( jQuery.ready, 1 ); @@ -997,9 +998,12 @@ jQuery.Callbacks = function( options ) { var start = list.length; (function add( args ) { jQuery.each( args, function( _, arg ) { - if ( jQuery.isFunction( arg ) && ( !options.unique || !self.has( arg ) ) ) { - list.push( arg ); - } else if ( arg && arg.length ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { // Inspect recursively add( arg ); } @@ -1141,7 +1145,7 @@ jQuery.extend({ // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { - return typeof obj === "object" ? jQuery.extend( obj, promise ) : promise; + return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; @@ -1251,24 +1255,23 @@ jQuery.support = (function() { clickFn, div = document.createElement("div"); - // Preliminary tests + // Setup div.setAttribute( "className", "t" ); div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>"; + // Support tests won't run in some limited or non-browser environments all = div.getElementsByTagName("*"); a = div.getElementsByTagName("a")[ 0 ]; - a.style.cssText = "top:1px;float:left;opacity:.5"; - - // Can't get basic test support - if ( !all || !all.length || !a ) { + if ( !all || !a || !all.length ) { return {}; } - // First batch of supports tests + // First batch of tests select = document.createElement("select"); opt = select.appendChild( document.createElement("option") ); input = div.getElementsByTagName("input")[ 0 ]; + a.style.cssText = "top:1px;float:left;opacity:.5"; support = { // IE strips leading whitespace when .innerHTML is used leadingWhitespace: ( div.firstChild.nodeType === 3 ), @@ -1310,7 +1313,7 @@ jQuery.support = (function() { // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) getSetAttribute: div.className !== "t", - // Tests for enctype support on a form(#6743) + // Tests for enctype support on a form (#6743) enctype: !!document.createElement("form").enctype, // Makes sure cloning an html5 element does not cause problems @@ -1452,10 +1455,8 @@ jQuery.support = (function() { support.boxSizing = ( div.offsetWidth === 4 ); support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); - // NOTE: To any future maintainer, window.getComputedStyle was used here - // instead of getComputedStyle because it gave a better gzip size. - // The difference between window.getComputedStyle and getComputedStyle is - // 7 bytes + // NOTE: To any future maintainer, we've window.getComputedStyle + // because jsdom on node.js will break without it. if ( window.getComputedStyle ) { support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; @@ -1505,7 +1506,7 @@ jQuery.support = (function() { return support; })(); -var rbrace = /^(?:\{.*\}|\[.*\])$/, +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, rmultiDash = /([A-Z])/g; jQuery.extend({ @@ -1513,7 +1514,7 @@ jQuery.extend({ deletedIds: [], - // Please use with caution + // Remove at next major release (1.9/2.0) uuid: 0, // Unique for each copy of jQuery on the page @@ -1565,7 +1566,7 @@ jQuery.extend({ // Only DOM nodes need a new unique ID for each element since their data // ends up in the global cache if ( isNode ) { - elem[ internalKey ] = id = jQuery.deletedIds.pop() || ++jQuery.uuid; + elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++; } else { id = internalKey; } @@ -1739,7 +1740,7 @@ jQuery.fn.extend({ for ( l = attr.length; i < l; i++ ) { name = attr[i].name; - if ( name.indexOf( "data-" ) === 0 ) { + if ( !name.indexOf( "data-" ) ) { name = jQuery.camelCase( name.substring(5) ); dataAttr( elem, name, data[ name ] ); @@ -1868,6 +1869,7 @@ jQuery.extend({ type = type || "fx"; var queue = jQuery.queue( elem, type ), + startLength = queue.length, fn = queue.shift(), hooks = jQuery._queueHooks( elem, type ), next = function() { @@ -1877,6 +1879,7 @@ jQuery.extend({ // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { fn = queue.shift(); + startLength--; } if ( fn ) { @@ -1891,7 +1894,8 @@ jQuery.extend({ delete hooks.stop; fn.call( elem, next, hooks ); } - if ( !queue.length && hooks ) { + + if ( !startLength && hooks ) { hooks.empty.fire(); } }, @@ -1977,7 +1981,8 @@ jQuery.fn.extend({ type = type || "fx"; while( i-- ) { - if ( (tmp = jQuery._data( elements[ i ], type + "queueHooks" )) && tmp.empty ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { count++; tmp.empty.add( resolve ); } @@ -2045,7 +2050,7 @@ jQuery.fn.extend({ setClass = " " + elem.className + " "; for ( c = 0, cl = classNames.length; c < cl; c++ ) { - if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) { setClass += classNames[ c ] + " "; } } @@ -2078,7 +2083,7 @@ jQuery.fn.extend({ // loop over each item in the removal list for ( c = 0, cl = removes.length; c < cl; c++ ) { // Remove until there is nothing to remove, - while ( className.indexOf(" " + removes[ c ] + " ") > -1 ) { + while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) { className = className.replace( " " + removes[ c ] + " " , " " ); } } @@ -2132,7 +2137,7 @@ jQuery.fn.extend({ i = 0, l = this.length; for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { return true; } } @@ -2213,26 +2218,25 @@ jQuery.extend({ }, select: { get: function( elem ) { - var value, i, max, option, - index = elem.selectedIndex, - values = [], + var value, option, options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; // Loop through all the selected options - i = one ? index : 0; - max = one ? index + 1 : options.length; for ( ; i < max; i++ ) { option = options[ i ]; - // Don't return options that are disabled or in a disabled optgroup - if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && - (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { // Get the specific value for the option value = jQuery( option ).val(); @@ -2247,11 +2251,6 @@ jQuery.extend({ } } - // Fixes Bug #2551 -- select.val() broken in IE after form.reset() - if ( one && !values.length && options.length ) { - return jQuery( options[ index ] ).val(); - } - return values; }, @@ -2310,7 +2309,7 @@ jQuery.extend({ return ret; } else { - elem.setAttribute( name, "" + value ); + elem.setAttribute( name, value + "" ); return value; } @@ -2574,7 +2573,7 @@ if ( !jQuery.support.style ) { return elem.style.cssText.toLowerCase() || undefined; }, set: function( elem, value ) { - return ( elem.style.cssText = "" + value ); + return ( elem.style.cssText = value + "" ); } }; } @@ -2707,6 +2706,7 @@ jQuery.event = { handler: handler, guid: handler.guid, selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join(".") }, handleObjIn ); @@ -2942,7 +2942,7 @@ jQuery.event = { } // Note that this is a bare JS function and not a jQuery handler handle = ontype && cur[ ontype ]; - if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { event.preventDefault(); } } @@ -2987,10 +2987,10 @@ jQuery.event = { // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event || window.event ); - var i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related, + var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related, handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), delegateCount = handlers.delegateCount, - args = [].slice.call( arguments ), + args = core_slice.call( arguments ), run_all = !event.exclusive && !event.namespace, special = jQuery.event.special[ event.type ] || {}, handlerQueue = []; @@ -3008,23 +3008,20 @@ jQuery.event = { // Avoid non-left-click bubbling in Firefox (#3861) if ( delegateCount && !(event.button && event.type === "click") ) { - // Pregenerate a single jQuery object for reuse with .is() - jqcur = jQuery(this); - jqcur.context = this; - for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { - // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #xxxx) + // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764) if ( cur.disabled !== true || event.type !== "click" ) { selMatch = {}; matches = []; - jqcur[0] = cur; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; sel = handleObj.selector; if ( selMatch[ sel ] === undefined ) { - selMatch[ sel ] = jqcur.is( sel ); + selMatch[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; } if ( selMatch[ sel ] ) { matches.push( handleObj ); @@ -3165,11 +3162,6 @@ jQuery.event = { }, special: { - ready: { - // Make sure the ready event is setup - setup: jQuery.bindReady - }, - load: { // Prevent triggered image.load events from bubbling to window.load noBubble: true @@ -3236,7 +3228,7 @@ jQuery.removeEvent = document.removeEventListener ? if ( elem.detachEvent ) { - // #8545, #7054, preventing memory leaks for custom events in IE6-8 ââ¬â + // #8545, #7054, preventing memory leaks for custom events in IE6-8 // detachEvent needed property on element, by name of that event, to properly expose it to GC if ( typeof elem[ name ] === "undefined" ) { elem[ name ] = null; @@ -3458,7 +3450,7 @@ if ( !jQuery.support.changeBubbles ) { teardown: function() { jQuery.event.remove( this, "._change" ); - return rformElems.test( this.nodeName ); + return !rformElems.test( this.nodeName ); } }; } @@ -3599,7 +3591,7 @@ jQuery.fn.extend({ }, undelegate: function( selector, types, fn ) { // ( namespace ) or ( selector, types [, fn] ) - return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); + return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); }, trigger: function( type, data ) { @@ -3668,1463 +3660,1685 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; } }); -/*! - * Sizzle CSS Selector Engine - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license - * http://sizzlejs.com/ - */ -(function( window, undefined ) { - -var cachedruns, - dirruns, - sortOrder, - siblingCheck, - assertGetIdNotName, - - document = window.document, - docElem = document.documentElement, - - strundefined = "undefined", - hasDuplicate = false, - baseHasDuplicate = true, - done = 0, - slice = [].slice, - push = [].push, - - expando = ( "sizcache" + Math.random() ).replace( ".", "" ), - - // Regex - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), - - // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors - operators = "([*^$|!~]?=)", - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + - "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", - pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)", - pos = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)", - combinators = whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*", - groups = "(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|" + attributes + "|" + pseudos.replace( 2, 7 ) + "|[^\\\\(),])+", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcombinators = new RegExp( "^" + combinators ), - - // All simple (non-comma) selectors, excluding insignifant trailing whitespace - rgroups = new RegExp( groups + "?(?=" + whitespace + "*,|$)", "g" ), - - // A selector, or everything after leading whitespace - // Optionally followed in either case by a ")" for terminating sub-selectors - rselector = new RegExp( "^(?:(?!,)(?:(?:^|,)" + whitespace + "*" + groups + ")*?|" + whitespace + "*(.*?))(\\)|$)" ), - - // All combinators and selector components (attribute test, tag, pseudo, etc.), the latter appearing together when consecutive - rtokens = new RegExp( groups.slice( 19, -6 ) + "\\x20\\t\\r\\n\\f>+~])+|" + combinators, "g" ), - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, - - rsibling = /[\x20\t\r\n\f]*[+~]/, - rendsWithNot = /:not\($/, - - rheader = /h\d/i, - rinputs = /input|select|textarea|button/i, - - rbackslash = /\\(?!\\)/g, - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "[-", "[-\\*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|nth|last|first)-child(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "POS": new RegExp( pos, "ig" ), - // For use in libraries implementing .is() - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) - }, - - classCache = {}, - cachedClasses = [], - compilerCache = {}, - cachedSelectors = [], - - // Mark a function for use in filtering - markFunction = function( fn ) { - fn.sizzleFilter = true; - return fn; - }, - - // Returns a function to use in pseudos for input types - createInputFunction = function( type ) { - return function( elem ) { - // Check the input's nodeName and type - return elem.nodeName.toLowerCase() === "input" && elem.type === type; - }; - }, - - // Returns a function to use in pseudos for buttons - createButtonFunction = function( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; - }, - - // Used for testing something on an element - assert = function( fn ) { - var pass = false, - div = document.createElement("div"); - try { - pass = fn( div ); - } catch (e) {} - // release memory in IE - div = null; - return pass; - }, - - // Check if attributes should be retrieved by attribute nodes - assertAttributes = assert(function( div ) { - div.innerHTML = "<select></select>"; - var type = typeof div.lastChild.getAttribute("multiple"); - // IE8 returns a string for some attributes even when not present - return type !== "boolean" && type !== "string"; - }), - - // Check if getElementById returns elements by name - // Check if getElementsByName privileges form controls or returns elements by ID - assertUsableName = assert(function( div ) { - // Inject content - div.id = expando + 0; - div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>"; - docElem.insertBefore( div, docElem.firstChild ); - - // Test - var pass = document.getElementsByName && - // buggy browsers will return fewer than the correct 2 - document.getElementsByName( expando ).length === - // buggy browsers will return more than the correct 0 - 2 + document.getElementsByName( expando + 0 ).length; - assertGetIdNotName = !document.getElementById( expando ); - - // Cleanup - docElem.removeChild( div ); - - return pass; - }), - - // Check if the browser returns only elements - // when doing getElementsByTagName("*") - assertTagNameNoComments = assert(function( div ) { - div.appendChild( document.createComment("") ); - return div.getElementsByTagName("*").length === 0; - }), - - // Check if getAttribute returns normalized href attributes - assertHrefNotNormalized = assert(function( div ) { - div.innerHTML = "<a href='#'></a>"; - return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && - div.firstChild.getAttribute("href") === "#"; - }), - - // Check if getElementsByClassName can be trusted - assertUsableClassName = assert(function( div ) { - // Opera can't find a second classname (in 9.6) - div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>"; - if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { - return false; - } - - // Safari caches class attributes, doesn't catch changes (in 3.2) - div.lastChild.className = "e"; - return div.getElementsByClassName("e").length !== 1; - }); - -var Sizzle = function( selector, context, results, seed ) { - results = results || []; - context = context || document; - var match, elem, xml, m, - nodeType = context.nodeType; - - if ( nodeType !== 1 && nodeType !== 9 ) { - return []; - } - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - xml = isXML( context ); - - if ( !xml && !seed ) { - if ( (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { - push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); - return results; - } - } - } - - // All others - return select( selector, context, results, seed, xml ); -}; - -var Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - match: matchExpr, - - order: [ "ID", "TAG" ], - - attrHandle: {}, - - createPseudo: markFunction, - - find: { - "ID": assertGetIdNotName ? - function( id, context, xml ) { - if ( typeof context.getElementById !== strundefined && !xml ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - } : - function( id, context, xml ) { - if ( typeof context.getElementById !== strundefined && !xml ) { - var m = context.getElementById( id ); - - return m ? - m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? - [m] : - undefined : - []; - } - }, - - "TAG": assertTagNameNoComments ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - var elem, - tmp = [], - i = 0; - - for ( ; (elem = results[i]); i++ ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - } - }, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( rbackslash, "" ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr.CHILD - 1 type (only|nth|...) - 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 3 xn-component of xn+y argument ([+-]?\d*n|) - 4 sign of xn-component - 5 x of xn-component - 6 sign of y-component - 7 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1] === "nth" ) { - // nth-child requires argument - if ( !match[2] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); - match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); - - // other types prohibit arguments - } else if ( match[2] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var argument, - unquoted = match[4]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Relinquish our claim on characters in `unquoted` from a closing parenthesis on - if ( unquoted && (argument = rselector.exec( unquoted )) && argument.pop() ) { - - match[0] = match[0].slice( 0, argument[0].length - unquoted.length - 1 ); - unquoted = argument[0].slice( 0, -1 ); - } - - // Quoted or unquoted, we have the full argument - // Return only captures needed by the pseudo filter method (type and argument) - match.splice( 2, 3, unquoted || match[3] ); - return match; - } - }, - - filter: { - "ID": assertGetIdNotName ? - function( id ) { - id = id.replace( rbackslash, "" ); - return function( elem ) { - return elem.getAttribute("id") === id; - }; - } : - function( id ) { - id = id.replace( rbackslash, "" ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === id; - }; - }, - - "TAG": function( nodeName ) { - if ( nodeName === "*" ) { - return function() { return true; }; - } - nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); - - return function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className ]; - if ( !pattern ) { - pattern = classCache[ className ] = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" ); - cachedClasses.push( className ); - // Avoid too large of a cache - if ( cachedClasses.length > Expr.cacheLength ) { - delete classCache[ cachedClasses.shift() ]; - } - } - return function( elem ) { - return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); - }; - }, - - "ATTR": function( name, operator, check ) { - if ( !operator ) { - return function( elem ) { - return Sizzle.attr( elem, name ) != null; - }; - } - - return function( elem ) { - var result = Sizzle.attr( elem, name ), - value = result + ""; - - if ( result == null ) { - return operator === "!="; - } - - switch ( operator ) { - case "=": - return value === check; - case "!=": - return value !== check; - case "^=": - return check && value.indexOf( check ) === 0; - case "*=": - return check && value.indexOf( check ) > -1; - case "$=": - return check && value.substr( value.length - check.length ) === check; - case "~=": - return ( " " + value + " " ).indexOf( check ) > -1; - case "|=": - return value === check || value.substr( 0, check.length + 1 ) === check + "-"; - } - }; - }, - - "CHILD": function( type, argument, first, last ) { - - if ( type === "nth" ) { - var doneName = done++; - - return function( elem ) { - var parent, diff, - count = 0, - node = elem; - - if ( first === 1 && last === 0 ) { - return true; - } - - parent = elem.parentNode; - - if ( parent && (parent[ expando ] !== doneName || !elem.sizset) ) { - for ( node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType === 1 ) { - node.sizset = ++count; - if ( node === elem ) { - break; - } - } - } - - parent[ expando ] = doneName; - } - - diff = elem.sizset - last; - - if ( first === 0 ) { - return diff === 0; - - } else { - return ( diff % first === 0 && diff / first >= 0 ); - } - }; - } - - return function( elem ) { - var node = elem; - - switch ( type ) { - case "only": - case "first": - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - if ( type === "first" ) { - return true; - } - - node = elem; - - /* falls through */ - case "last": - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - return true; - } - }; - }, - - "PSEUDO": function( pseudo, argument, context, xml ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - var fn = Expr.pseudos[ pseudo ] || Expr.pseudos[ pseudo.toLowerCase() ]; - - if ( !fn ) { - Sizzle.error( "unsupported pseudo: " + pseudo ); - } - - // The user may set fn.sizzleFilter to indicate - // that arguments are needed to create the filter function - // just as Sizzle does - if ( !fn.sizzleFilter ) { - return fn; - } - - return fn( argument, context, xml ); - } - }, - - pseudos: { - "not": markFunction(function( selector, context, xml ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var matcher = compile( selector.replace( rtrim, "$1" ), context, xml ); - return function( elem ) { - return !matcher( elem ); - }; - }), - - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), - // not comment, processing instructions, or others - // Thanks to Diego Perini for the nodeName shortcut - // Greater than "@" means alpha characters (specifically not starting with "#" or "?") - var nodeType; - elem = elem.firstChild; - while ( elem ) { - if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { - return false; - } - elem = elem.nextSibling; - } - return true; - }, - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "text": function( elem ) { - var type, attr; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && - (type = elem.type) === "text" && - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); - }, - - // Input types - "radio": createInputFunction("radio"), - "checkbox": createInputFunction("checkbox"), - "file": createInputFunction("file"), - "password": createInputFunction("password"), - "image": createInputFunction("image"), - - "submit": createButtonFunction("submit"), - "reset": createButtonFunction("reset"), - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "focus": function( elem ) { - var doc = elem.ownerDocument; - return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href); - }, - - "active": function( elem ) { - return elem === elem.ownerDocument.activeElement; - } - }, - - setFilters: { - "first": function( elements, argument, not ) { - return not ? elements.slice( 1 ) : [ elements[0] ]; - }, - - "last": function( elements, argument, not ) { - var elem = elements.pop(); - return not ? elements : [ elem ]; - }, - - "even": function( elements, argument, not ) { - var results = [], - i = not ? 1 : 0, - len = elements.length; - for ( ; i < len; i = i + 2 ) { - results.push( elements[i] ); - } - return results; - }, - - "odd": function( elements, argument, not ) { - var results = [], - i = not ? 0 : 1, - len = elements.length; - for ( ; i < len; i = i + 2 ) { - results.push( elements[i] ); - } - return results; - }, - - "lt": function( elements, argument, not ) { - return not ? elements.slice( +argument ) : elements.slice( 0, +argument ); - }, - - "gt": function( elements, argument, not ) { - return not ? elements.slice( 0, +argument + 1 ) : elements.slice( +argument + 1 ); - }, - - "eq": function( elements, argument, not ) { - var elem = elements.splice( +argument, 1 ); - return not ? elements : elem; - } - } -}; - -// Deprecated -Expr.setFilters["nth"] = Expr.setFilters["eq"]; - -// Back-compat -Expr.filters = Expr.pseudos; - -// IE6/7 return a modified href -if ( !assertHrefNotNormalized ) { - Expr.attrHandle = { - "href": function( elem ) { - return elem.getAttribute( "href", 2 ); - }, - "type": function( elem ) { - return elem.getAttribute("type"); - } - }; -} - -// Add getElementsByName if usable -if ( assertUsableName ) { - Expr.order.push("NAME"); - Expr.find["NAME"] = function( name, context ) { - if ( typeof context.getElementsByName !== strundefined ) { - return context.getElementsByName( name ); - } - }; -} - -// Add getElementsByClassName if usable -if ( assertUsableClassName ) { - Expr.order.splice( 1, 0, "CLASS" ); - Expr.find["CLASS"] = function( className, context, xml ) { - if ( typeof context.getElementsByClassName !== strundefined && !xml ) { - return context.getElementsByClassName( className ); - } - }; -} - -// If slice is not available, provide a backup -try { - slice.call( docElem.childNodes, 0 )[0].nodeType; -} catch ( e ) { - slice = function( i ) { - var elem, results = []; - for ( ; (elem = this[i]); i++ ) { - results.push( elem ); - } - return results; - }; -} - -var isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -// Element contains another -var contains = Sizzle.contains = docElem.compareDocumentPosition ? - function( a, b ) { - return !!( a.compareDocumentPosition( b ) & 16 ); - } : - docElem.contains ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); - } : - function( a, b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - return false; - }; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -var getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( nodeType ) { - if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (see #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - } else { - - // If no nodeType, this is expected to be an array - for ( ; (node = elem[i]); i++ ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } - return ret; -}; - -Sizzle.attr = function( elem, name ) { - var attr, - xml = isXML( elem ); - - if ( !xml ) { - name = name.toLowerCase(); - } - if ( Expr.attrHandle[ name ] ) { - return Expr.attrHandle[ name ]( elem ); - } - if ( assertAttributes || xml ) { - return elem.getAttribute( name ); - } - attr = elem.getAttributeNode( name ); - return attr ? - typeof elem[ name ] === "boolean" ? - elem[ name ] ? name : null : - attr.specified ? attr.value : null : - null; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -// Check if the JavaScript engine is using some sort of -// optimization where it does not always call our comparision -// function. If that is the case, discard the hasDuplicate value. -// Thus far that includes Google Chrome. -[0, 0].sort(function() { - return (baseHasDuplicate = 0); -}); - - -if ( docElem.compareDocumentPosition ) { - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? - a.compareDocumentPosition : - a.compareDocumentPosition(b) & 4 - ) ? -1 : 1; - }; - -} else { - sortOrder = function( a, b ) { - // The nodes are identical, we can exit early - if ( a === b ) { - hasDuplicate = true; - return 0; - - // Fallback to using sourceIndex (in IE) if it's available on both nodes - } else if ( a.sourceIndex && b.sourceIndex ) { - return a.sourceIndex - b.sourceIndex; - } - - var al, bl, - ap = [], - bp = [], - aup = a.parentNode, - bup = b.parentNode, - cur = aup; - - // If the nodes are siblings (or identical) we can do a quick check - if ( aup === bup ) { - return siblingCheck( a, b ); - - // If no parents were found then the nodes are disconnected - } else if ( !aup ) { - return -1; - - } else if ( !bup ) { - return 1; - } - - // Otherwise they're somewhere else in the tree so we need - // to build up a full list of the parentNodes for comparison - while ( cur ) { - ap.unshift( cur ); - cur = cur.parentNode; - } - - cur = bup; - - while ( cur ) { - bp.unshift( cur ); - cur = cur.parentNode; - } - - al = ap.length; - bl = bp.length; - - // Start walking down the tree looking for a discrepancy - for ( var i = 0; i < al && i < bl; i++ ) { - if ( ap[i] !== bp[i] ) { - return siblingCheck( ap[i], bp[i] ); - } - } - - // We ended someplace up the tree so do a sibling check - return i === al ? - siblingCheck( a, bp[i], -1 ) : - siblingCheck( ap[i], b, 1 ); - }; - - siblingCheck = function( a, b, ret ) { - if ( a === b ) { - return ret; - } - - var cur = a.nextSibling; - - while ( cur ) { - if ( cur === b ) { - return -1; - } - - cur = cur.nextSibling; - } - - return 1; - }; -} - -// Document sorting and removing duplicates -Sizzle.uniqueSort = function( results ) { - var elem, - i = 1; - - if ( sortOrder ) { - hasDuplicate = baseHasDuplicate; - results.sort( sortOrder ); - - if ( hasDuplicate ) { - for ( ; (elem = results[i]); i++ ) { - if ( elem === results[ i - 1 ] ) { - results.splice( i--, 1 ); - } - } - } - } - - return results; -}; - -function multipleContexts( selector, contexts, results, seed ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results, seed ); - } -} - -function handlePOSGroup( selector, posfilter, argument, contexts, seed, not ) { - var results, - fn = Expr.setFilters[ posfilter.toLowerCase() ]; - - if ( !fn ) { - Sizzle.error( posfilter ); - } - - if ( selector || !(results = seed) ) { - multipleContexts( selector || "*", contexts, (results = []), seed ); - } - - return results.length > 0 ? fn( results, argument, not ) : []; -} - -function handlePOS( selector, context, results, seed, groups ) { - var match, not, anchor, ret, elements, currentContexts, part, lastIndex, - i = 0, - len = groups.length, - rpos = matchExpr["POS"], - // This is generated here in case matchExpr["POS"] is extended - rposgroups = new RegExp( "^" + rpos.source + "(?!" + whitespace + ")", "i" ), - // This is for making sure non-participating - // matching groups are represented cross-browser (IE6-8) - setUndefined = function() { - var i = 1, - len = arguments.length - 2; - for ( ; i < len; i++ ) { - if ( arguments[i] === undefined ) { - match[i] = undefined; - } - } - }; - - for ( ; i < len; i++ ) { - // Reset regex index to 0 - rpos.exec(""); - selector = groups[i]; - ret = []; - anchor = 0; - elements = seed; - while ( (match = rpos.exec( selector )) ) { - lastIndex = rpos.lastIndex = match.index + match[0].length; - if ( lastIndex > anchor ) { - part = selector.slice( anchor, match.index ); - anchor = lastIndex; - currentContexts = [ context ]; - - if ( rcombinators.test(part) ) { - if ( elements ) { - currentContexts = elements; - } - elements = seed; - } - - if ( (not = rendsWithNot.test( part )) ) { - part = part.slice( 0, -5 ).replace( rcombinators, "$&*" ); - } - - if ( match.length > 1 ) { - match[0].replace( rposgroups, setUndefined ); - } - elements = handlePOSGroup( part, match[1], match[2], currentContexts, elements, not ); - } - } - - if ( elements ) { - ret = ret.concat( elements ); - - if ( (part = selector.slice( anchor )) && part !== ")" ) { - if ( rcombinators.test(part) ) { - multipleContexts( part, ret, results, seed ); - } else { - Sizzle( part, context, results, seed ? seed.concat(elements) : elements ); - } - } else { - push.apply( results, ret ); - } - } else { - Sizzle( selector, context, results, seed ); - } - } - - // Do not sort if this is a single filter - return len === 1 ? results : Sizzle.uniqueSort( results ); -} - -function tokenize( selector, context, xml ) { - var tokens, soFar, type, - groups = [], - i = 0, - - // Catch obvious selector issues: terminal ")"; nonempty fallback match - // rselector never fails to match *something* - match = rselector.exec( selector ), - matched = !match.pop() && !match.pop(), - selectorGroups = matched && selector.match( rgroups ) || [""], - - preFilters = Expr.preFilter, - filters = Expr.filter, - checkContext = !xml && context !== document; - - for ( ; (soFar = selectorGroups[i]) != null && matched; i++ ) { - groups.push( tokens = [] ); - - // Need to make sure we're within a narrower context if necessary - // Adding a descendant combinator will generate what is needed - if ( checkContext ) { - soFar = " " + soFar; - } - - while ( soFar ) { - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - soFar = soFar.slice( match[0].length ); - - // Cast descendant combinators to space - matched = tokens.push({ part: match.pop().replace( rtrim, " " ), captures: match }); - } - - // Filters - for ( type in filters ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match, context, xml )) ) ) { - - soFar = soFar.slice( match.shift().length ); - matched = tokens.push({ part: type, captures: match }); - } - } - - if ( !matched ) { - break; - } - } - } - - if ( !matched ) { - Sizzle.error( selector ); - } - - return groups; -} - -function addCombinator( matcher, combinator, context ) { - var dir = combinator.dir, - doneName = done++; - - if ( !matcher ) { - // If there is no matcher to check, check against the context - matcher = function( elem ) { - return elem === context; - }; - } - return combinator.first ? - function( elem, context ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 ) { - return matcher( elem, context ) && elem; - } - } - } : - function( elem, context ) { - var cache, - dirkey = doneName + "." + dirruns, - cachedkey = dirkey + "." + cachedruns; - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 ) { - if ( (cache = elem[ expando ]) === cachedkey ) { - return elem.sizset; - } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { - if ( elem.sizset ) { - return elem; - } - } else { - elem[ expando ] = cachedkey; - if ( matcher( elem, context ) ) { - elem.sizset = true; - return elem; - } - elem.sizset = false; - } - } - } - }; -} - -function addMatcher( higher, deeper ) { - return higher ? - function( elem, context ) { - var result = deeper( elem, context ); - return result && higher( result === true ? elem : result, context ); - } : - deeper; -} - -// ["TAG", ">", "ID", " ", "CLASS"] -function matcherFromTokens( tokens, context, xml ) { - var token, matcher, - i = 0; - - for ( ; (token = tokens[i]); i++ ) { - if ( Expr.relative[ token.part ] ) { - matcher = addCombinator( matcher, Expr.relative[ token.part ], context ); - } else { - token.captures.push( context, xml ); - matcher = addMatcher( matcher, Expr.filter[ token.part ].apply( null, token.captures ) ); - } - } - - return matcher; -} - -function matcherFromGroupMatchers( matchers ) { - return function( elem, context ) { - var matcher, - j = 0; - for ( ; (matcher = matchers[j]); j++ ) { - if ( matcher(elem, context) ) { - return true; - } - } - return false; - }; -} - -var compile = Sizzle.compile = function( selector, context, xml ) { - var tokens, group, i, - cached = compilerCache[ selector ]; - - // Return a cached group function if already generated (context dependent) - if ( cached && cached.context === context ) { - return cached; - } - - // Generate a function of recursive functions that can be used to check each element - group = tokenize( selector, context, xml ); - for ( i = 0; (tokens = group[i]); i++ ) { - group[i] = matcherFromTokens( tokens, context, xml ); - } - - // Cache the compiled function - cached = compilerCache[ selector ] = matcherFromGroupMatchers( group ); - cached.context = context; - cached.runs = cached.dirruns = 0; - cachedSelectors.push( selector ); - // Ensure only the most recent are cached - if ( cachedSelectors.length > Expr.cacheLength ) { - delete compilerCache[ cachedSelectors.shift() ]; - } - return cached; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - return Sizzle( expr, null, null, [ elem ] ).length > 0; -}; - -var select = function( selector, context, results, seed, xml ) { - // Remove excessive whitespace - selector = selector.replace( rtrim, "$1" ); - var elements, matcher, i, len, elem, token, - type, findContext, notTokens, - match = selector.match( rgroups ), - tokens = selector.match( rtokens ), - contextNodeType = context.nodeType; - - // POS handling - if ( matchExpr["POS"].test(selector) ) { - return handlePOS( selector, context, results, seed, match ); - } - - if ( seed ) { - elements = slice.call( seed, 0 ); - - // To maintain document order, only narrow the - // set if there is one group - } else if ( match && match.length === 1 ) { - - // Take a shortcut and set the context if the root selector is an ID - if ( tokens.length > 1 && contextNodeType === 9 && !xml && - (match = matchExpr["ID"].exec( tokens[0] )) ) { - - context = Expr.find["ID"]( match[1], context, xml )[0]; - if ( !context ) { - return results; - } - - selector = selector.slice( tokens.shift().length ); - } - - findContext = ( (match = rsibling.exec( tokens[0] )) && !match.index && context.parentNode ) || context; - - // Get the last token, excluding :not - notTokens = tokens.pop(); - token = notTokens.split(":not")[0]; - - for ( i = 0, len = Expr.order.length; i < len; i++ ) { - type = Expr.order[i]; - - if ( (match = matchExpr[ type ].exec( token )) ) { - elements = Expr.find[ type ]( (match[1] || "").replace( rbackslash, "" ), findContext, xml ); - - if ( elements == null ) { - continue; - } - - if ( token === notTokens ) { - selector = selector.slice( 0, selector.length - notTokens.length ) + - token.replace( matchExpr[ type ], "" ); - - if ( !selector ) { - push.apply( results, slice.call(elements, 0) ); - } - } - break; - } - } - } - - // Only loop over the given elements once - // If selector is empty, we're already done - if ( selector ) { - matcher = compile( selector, context, xml ); - dirruns = matcher.dirruns++; - - if ( elements == null ) { - elements = Expr.find["TAG"]( "*", (rsibling.test( selector ) && context.parentNode) || context ); - } - for ( i = 0; (elem = elements[i]); i++ ) { - cachedruns = matcher.runs++; - if ( matcher(elem, context) ) { - results.push( elem ); - } - } - } - - return results; -}; - -if ( document.querySelectorAll ) { - (function() { - var disconnectedMatch, - oldSelect = select, - rescape = /'|\\/g, - rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, - rbuggyQSA = [], - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - // A support test would require too much code (would include document ready) - // just skip matchesSelector for :active - rbuggyMatches = [":active"], - matches = docElem.matchesSelector || - docElem.mozMatchesSelector || - docElem.webkitMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector; - - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - div.innerHTML = "<select><option selected></option></select>"; - - // IE8 - Some boolean attributes are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here (do not put tests after this one) - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function( div ) { - - // Opera 10-12/IE9 - ^= $= *= and empty values - // Should not select anything - div.innerHTML = "<p test=''></p>"; - if ( div.querySelectorAll("[test^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here (do not put tests after this one) - div.innerHTML = "<input type='hidden'>"; - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push(":enabled", ":disabled"); - } - }); - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - - select = function( selector, context, results, seed, xml ) { - // Only use querySelectorAll when not filtering, - // when this is not xml, - // and when no QSA bugs apply - if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - if ( context.nodeType === 9 ) { - try { - push.apply( results, slice.call(context.querySelectorAll( selector ), 0) ); - return results; - } catch(qsaError) {} - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - var old = context.getAttribute("id"), - nid = old || expando, - newContext = rsibling.test( selector ) && context.parentNode || context; - - if ( old ) { - nid = nid.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - - try { - push.apply( results, slice.call( newContext.querySelectorAll( - selector.replace( rgroups, "[id='" + nid + "'] $&" ) - ), 0 ) ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - - return oldSelect( selector, context, results, seed, xml ); - }; - - if ( matches ) { - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - disconnectedMatch = matches.call( div, "div" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - try { - matches.call( div, "[test!='']:sizzle" ); - rbuggyMatches.push( Expr.match.PSEUDO ); - } catch ( e ) {} - }); - - // rbuggyMatches always contains :active, so no need for a length check - rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); - - Sizzle.matchesSelector = function( elem, expr ) { - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - // rbuggyMatches always contains :active, so no need for an existence check - if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) { - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch(e) {} - } - - return Sizzle( expr, null, null, [ elem ] ).length > 0; - }; - } - })(); -} - +/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://sizzlejs.com/
+ */
+(function( window, undefined ) {
+
+var cachedruns,
+ assertGetIdNotName,
+ Expr,
+ getText,
+ isXML,
+ contains,
+ compile,
+ sortOrder,
+ hasDuplicate,
+ outermostContext,
+
+ baseHasDuplicate = true,
+ strundefined = "undefined",
+
+ expando = ( "sizcache" + Math.random() ).replace( ".", "" ),
+
+ Token = String,
+ document = window.document,
+ docElem = document.documentElement,
+ dirruns = 0,
+ done = 0,
+ pop = [].pop,
+ push = [].push,
+ slice = [].slice,
+ // Use a stripped-down indexOf if a native one is unavailable
+ indexOf = [].indexOf || function( elem ) {
+ var i = 0,
+ len = this.length;
+ for ( ; i < len; i++ ) {
+ if ( this[i] === elem ) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ // Augment a function for special use by Sizzle
+ markFunction = function( fn, value ) {
+ fn[ expando ] = value == null || value;
+ return fn;
+ },
+
+ createCache = function() {
+ var cache = {},
+ keys = [];
+
+ return markFunction(function( key, value ) {
+ // Only keep the most recent entries
+ if ( keys.push( key ) > Expr.cacheLength ) {
+ delete cache[ keys.shift() ];
+ }
+
+ // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157)
+ return (cache[ key + " " ] = value);
+ }, cache );
+ },
+
+ classCache = createCache(),
+ tokenCache = createCache(),
+ compilerCache = createCache(),
+
+ // Regex
+
+ // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+ whitespace = "[\\x20\\t\\r\\n\\f]",
+ // http://www.w3.org/TR/css3-syntax/#characters
+ characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",
+
+ // Loosely modeled on CSS identifier characters
+ // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors)
+ // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+ identifier = characterEncoding.replace( "w", "w#" ),
+
+ // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
+ operators = "([*^$|!~]?=)",
+ attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
+ "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
+
+ // Prefer arguments not in parens/brackets,
+ // then attribute selectors and non-pseudos (denoted by :),
+ // then anything else
+ // These preferences are here to reduce the number of selectors
+ // needing tokenize in the PSEUDO preFilter
+ pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)",
+
+ // For matchExpr.POS and matchExpr.needsContext
+ pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
+ "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)",
+
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+ rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
+ rpseudo = new RegExp( pseudos ),
+
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors
+ rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,
+
+ rnot = /^:not/,
+ rsibling = /[\x20\t\r\n\f]*[+~]/,
+ rendsWithNot = /:not\($/,
+
+ rheader = /h\d/i,
+ rinputs = /input|select|textarea|button/i,
+
+ rbackslash = /\\(?!\\)/g,
+
+ matchExpr = {
+ "ID": new RegExp( "^#(" + characterEncoding + ")" ),
+ "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+ "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
+ "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+ "ATTR": new RegExp( "^" + attributes ),
+ "PSEUDO": new RegExp( "^" + pseudos ),
+ "POS": new RegExp( pos, "i" ),
+ "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace +
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+ // For use in libraries implementing .is()
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" )
+ },
+
+ // Support
+
+ // Used for testing something on an element
+ assert = function( fn ) {
+ var div = document.createElement("div");
+
+ try {
+ return fn( div );
+ } catch (e) {
+ return false;
+ } finally {
+ // release memory in IE
+ div = null;
+ }
+ },
+
+ // Check if getElementsByTagName("*") returns only elements
+ assertTagNameNoComments = assert(function( div ) {
+ div.appendChild( document.createComment("") );
+ return !div.getElementsByTagName("*").length;
+ }),
+
+ // Check if getAttribute returns normalized href attributes
+ assertHrefNotNormalized = assert(function( div ) {
+ div.innerHTML = "<a href='#'></a>";
+ return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
+ div.firstChild.getAttribute("href") === "#";
+ }),
+
+ // Check if attributes should be retrieved by attribute nodes
+ assertAttributes = assert(function( div ) {
+ div.innerHTML = "<select></select>";
+ var type = typeof div.lastChild.getAttribute("multiple");
+ // IE8 returns a string for some attributes even when not present
+ return type !== "boolean" && type !== "string";
+ }),
+
+ // Check if getElementsByClassName can be trusted
+ assertUsableClassName = assert(function( div ) {
+ // Opera can't find a second classname (in 9.6)
+ div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
+ if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
+ return false;
+ }
+
+ // Safari 3.2 caches class attributes and doesn't catch changes
+ div.lastChild.className = "e";
+ return div.getElementsByClassName("e").length === 2;
+ }),
+
+ // Check if getElementById returns elements by name
+ // Check if getElementsByName privileges form controls or returns elements by ID
+ assertUsableName = assert(function( div ) {
+ // Inject content
+ div.id = expando + 0;
+ div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";
+ docElem.insertBefore( div, docElem.firstChild );
+
+ // Test
+ var pass = document.getElementsByName &&
+ // buggy browsers will return fewer than the correct 2
+ document.getElementsByName( expando ).length === 2 +
+ // buggy browsers will return more than the correct 0
+ document.getElementsByName( expando + 0 ).length;
+ assertGetIdNotName = !document.getElementById( expando );
+
+ // Cleanup
+ docElem.removeChild( div );
+
+ return pass;
+ });
+
+// If slice is not available, provide a backup
+try {
+ slice.call( docElem.childNodes, 0 )[0].nodeType;
+} catch ( e ) {
+ slice = function( i ) {
+ var elem,
+ results = [];
+ for ( ; (elem = this[i]); i++ ) {
+ results.push( elem );
+ }
+ return results;
+ };
+}
+
+function Sizzle( selector, context, results, seed ) {
+ results = results || [];
+ context = context || document;
+ var match, elem, xml, m,
+ nodeType = context.nodeType;
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ if ( nodeType !== 1 && nodeType !== 9 ) {
+ return [];
+ }
+
+ xml = isXML( context );
+
+ if ( !xml && !seed ) {
+ if ( (match = rquickExpr.exec( selector )) ) {
+ // Speed-up: Sizzle("#ID")
+ if ( (m = match[1]) ) {
+ if ( nodeType === 9 ) {
+ elem = context.getElementById( m );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE, Opera, and Webkit return items
+ // by name instead of ID
+ if ( elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ } else {
+ return results;
+ }
+ } else {
+ // Context is not a document
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+ contains( context, elem ) && elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ }
+
+ // Speed-up: Sizzle("TAG")
+ } else if ( match[2] ) {
+ push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
+ return results;
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) {
+ push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
+ return results;
+ }
+ }
+ }
+
+ // All others
+ return select( selector.replace( rtrim, "$1" ), context, results, seed, xml );
+}
+
+Sizzle.matches = function( expr, elements ) {
+ return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+ return Sizzle( expr, null, null, [ elem ] ).length > 0;
+};
+
+// Returns a function to use in pseudos for input types
+function createInputPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === type;
+ };
+}
+
+// Returns a function to use in pseudos for buttons
+function createButtonPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && elem.type === type;
+ };
+}
+
+// Returns a function to use in pseudos for positionals
+function createPositionalPseudo( fn ) {
+ return markFunction(function( argument ) {
+ argument = +argument;
+ return markFunction(function( seed, matches ) {
+ var j,
+ matchIndexes = fn( [], seed.length, argument ),
+ i = matchIndexes.length;
+
+ // Match elements found at the specified indexes
+ while ( i-- ) {
+ if ( seed[ (j = matchIndexes[i]) ] ) {
+ seed[j] = !(matches[j] = seed[j]);
+ }
+ }
+ });
+ });
+}
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+ var node,
+ ret = "",
+ i = 0,
+ nodeType = elem.nodeType;
+
+ if ( nodeType ) {
+ if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent for elements
+ // innerText usage removed for consistency of new lines (see #11153)
+ if ( typeof elem.textContent === "string" ) {
+ return elem.textContent;
+ } else {
+ // Traverse its children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ // Do not include comment or processing instruction nodes
+ } else {
+
+ // If no nodeType, this is expected to be an array
+ for ( ; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ ret += getText( node );
+ }
+ }
+ return ret;
+};
+
+isXML = Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+// Element contains another
+contains = Sizzle.contains = docElem.contains ?
+ function( a, b ) {
+ var adown = a.nodeType === 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
+ } :
+ docElem.compareDocumentPosition ?
+ function( a, b ) {
+ return b && !!( a.compareDocumentPosition( b ) & 16 );
+ } :
+ function( a, b ) {
+ while ( (b = b.parentNode) ) {
+ if ( b === a ) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+Sizzle.attr = function( elem, name ) {
+ var val,
+ xml = isXML( elem );
+
+ if ( !xml ) {
+ name = name.toLowerCase();
+ }
+ if ( (val = Expr.attrHandle[ name ]) ) {
+ return val( elem );
+ }
+ if ( xml || assertAttributes ) {
+ return elem.getAttribute( name );
+ }
+ val = elem.getAttributeNode( name );
+ return val ?
+ typeof elem[ name ] === "boolean" ?
+ elem[ name ] ? name : null :
+ val.specified ? val.value : null :
+ null;
+};
+
+Expr = Sizzle.selectors = {
+
+ // Can be adjusted by the user
+ cacheLength: 50,
+
+ createPseudo: markFunction,
+
+ match: matchExpr,
+
+ // IE6/7 return a modified href
+ attrHandle: assertHrefNotNormalized ?
+ {} :
+ {
+ "href": function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ },
+ "type": function( elem ) {
+ return elem.getAttribute("type");
+ }
+ },
+
+ find: {
+ "ID": assertGetIdNotName ?
+ function( id, context, xml ) {
+ if ( typeof context.getElementById !== strundefined && !xml ) {
+ var m = context.getElementById( id );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ } :
+ function( id, context, xml ) {
+ if ( typeof context.getElementById !== strundefined && !xml ) {
+ var m = context.getElementById( id );
+
+ return m ?
+ m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
+ [m] :
+ undefined :
+ [];
+ }
+ },
+
+ "TAG": assertTagNameNoComments ?
+ function( tag, context ) {
+ if ( typeof context.getElementsByTagName !== strundefined ) {
+ return context.getElementsByTagName( tag );
+ }
+ } :
+ function( tag, context ) {
+ var results = context.getElementsByTagName( tag );
+
+ // Filter out possible comments
+ if ( tag === "*" ) {
+ var elem,
+ tmp = [],
+ i = 0;
+
+ for ( ; (elem = results[i]); i++ ) {
+ if ( elem.nodeType === 1 ) {
+ tmp.push( elem );
+ }
+ }
+
+ return tmp;
+ }
+ return results;
+ },
+
+ "NAME": assertUsableName && function( tag, context ) {
+ if ( typeof context.getElementsByName !== strundefined ) {
+ return context.getElementsByName( name );
+ }
+ },
+
+ "CLASS": assertUsableClassName && function( className, context, xml ) {
+ if ( typeof context.getElementsByClassName !== strundefined && !xml ) {
+ return context.getElementsByClassName( className );
+ }
+ }
+ },
+
+ relative: {
+ ">": { dir: "parentNode", first: true },
+ " ": { dir: "parentNode" },
+ "+": { dir: "previousSibling", first: true },
+ "~": { dir: "previousSibling" }
+ },
+
+ preFilter: {
+ "ATTR": function( match ) {
+ match[1] = match[1].replace( rbackslash, "" );
+
+ // Move the given value to match[3] whether quoted or unquoted
+ match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" );
+
+ if ( match[2] === "~=" ) {
+ match[3] = " " + match[3] + " ";
+ }
+
+ return match.slice( 0, 4 );
+ },
+
+ "CHILD": function( match ) {
+ /* matches from matchExpr["CHILD"]
+ 1 type (only|nth|...)
+ 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+ 3 xn-component of xn+y argument ([+-]?\d*n|)
+ 4 sign of xn-component
+ 5 x of xn-component
+ 6 sign of y-component
+ 7 y of y-component
+ */
+ match[1] = match[1].toLowerCase();
+
+ if ( match[1] === "nth" ) {
+ // nth-child requires argument
+ if ( !match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // numeric x and y parameters for Expr.filter.CHILD
+ // remember that false/true cast respectively to 0/1
+ match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) );
+ match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" );
+
+ // other types prohibit arguments
+ } else if ( match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ return match;
+ },
+
+ "PSEUDO": function( match ) {
+ var unquoted, excess;
+ if ( matchExpr["CHILD"].test( match[0] ) ) {
+ return null;
+ }
+
+ if ( match[3] ) {
+ match[2] = match[3];
+ } else if ( (unquoted = match[4]) ) {
+ // Only check arguments that contain a pseudo
+ if ( rpseudo.test(unquoted) &&
+ // Get excess from tokenize (recursively)
+ (excess = tokenize( unquoted, true )) &&
+ // advance to the next closing parenthesis
+ (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+ // excess is a negative index
+ unquoted = unquoted.slice( 0, excess );
+ match[0] = match[0].slice( 0, excess );
+ }
+ match[2] = unquoted;
+ }
+
+ // Return only captures needed by the pseudo filter method (type and argument)
+ return match.slice( 0, 3 );
+ }
+ },
+
+ filter: {
+ "ID": assertGetIdNotName ?
+ function( id ) {
+ id = id.replace( rbackslash, "" );
+ return function( elem ) {
+ return elem.getAttribute("id") === id;
+ };
+ } :
+ function( id ) {
+ id = id.replace( rbackslash, "" );
+ return function( elem ) {
+ var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+ return node && node.value === id;
+ };
+ },
+
+ "TAG": function( nodeName ) {
+ if ( nodeName === "*" ) {
+ return function() { return true; };
+ }
+ nodeName = nodeName.replace( rbackslash, "" ).toLowerCase();
+
+ return function( elem ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+ };
+ },
+
+ "CLASS": function( className ) {
+ var pattern = classCache[ expando ][ className + " " ];
+
+ return pattern ||
+ (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+ classCache( className, function( elem ) {
+ return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
+ });
+ },
+
+ "ATTR": function( name, operator, check ) {
+ return function( elem, context ) {
+ var result = Sizzle.attr( elem, name );
+
+ if ( result == null ) {
+ return operator === "!=";
+ }
+ if ( !operator ) {
+ return true;
+ }
+
+ result += "";
+
+ return operator === "=" ? result === check :
+ operator === "!=" ? result !== check :
+ operator === "^=" ? check && result.indexOf( check ) === 0 :
+ operator === "*=" ? check && result.indexOf( check ) > -1 :
+ operator === "$=" ? check && result.substr( result.length - check.length ) === check :
+ operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+ operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" :
+ false;
+ };
+ },
+
+ "CHILD": function( type, argument, first, last ) {
+
+ if ( type === "nth" ) {
+ return function( elem ) {
+ var node, diff,
+ parent = elem.parentNode;
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ if ( parent ) {
+ diff = 0;
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ diff++;
+ if ( elem === node ) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Incorporate the offset (or cast to NaN), then check against cycle size
+ diff -= last;
+ return diff === first || ( diff % first === 0 && diff / first >= 0 );
+ };
+ }
+
+ return function( elem ) {
+ var node = elem;
+
+ switch ( type ) {
+ case "only":
+ case "first":
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ if ( type === "first" ) {
+ return true;
+ }
+
+ node = elem;
+
+ /* falls through */
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ };
+ },
+
+ "PSEUDO": function( pseudo, argument ) {
+ // pseudo-class names are case-insensitive
+ // http://www.w3.org/TR/selectors/#pseudo-classes
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+ // Remember that setFilters inherits from pseudos
+ var args,
+ fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+ Sizzle.error( "unsupported pseudo: " + pseudo );
+
+ // The user may use createPseudo to indicate that
+ // arguments are needed to create the filter function
+ // just as Sizzle does
+ if ( fn[ expando ] ) {
+ return fn( argument );
+ }
+
+ // But maintain support for old signatures
+ if ( fn.length > 1 ) {
+ args = [ pseudo, pseudo, "", argument ];
+ return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+ markFunction(function( seed, matches ) {
+ var idx,
+ matched = fn( seed, argument ),
+ i = matched.length;
+ while ( i-- ) {
+ idx = indexOf.call( seed, matched[i] );
+ seed[ idx ] = !( matches[ idx ] = matched[i] );
+ }
+ }) :
+ function( elem ) {
+ return fn( elem, 0, args );
+ };
+ }
+
+ return fn;
+ }
+ },
+
+ pseudos: {
+ "not": markFunction(function( selector ) {
+ // Trim the selector passed to compile
+ // to avoid treating leading and trailing
+ // spaces as combinators
+ var input = [],
+ results = [],
+ matcher = compile( selector.replace( rtrim, "$1" ) );
+
+ return matcher[ expando ] ?
+ markFunction(function( seed, matches, context, xml ) {
+ var elem,
+ unmatched = matcher( seed, null, xml, [] ),
+ i = seed.length;
+
+ // Match elements unmatched by `matcher`
+ while ( i-- ) {
+ if ( (elem = unmatched[i]) ) {
+ seed[i] = !(matches[i] = elem);
+ }
+ }
+ }) :
+ function( elem, context, xml ) {
+ input[0] = elem;
+ matcher( input, null, xml, results );
+ return !results.pop();
+ };
+ }),
+
+ "has": markFunction(function( selector ) {
+ return function( elem ) {
+ return Sizzle( selector, elem ).length > 0;
+ };
+ }),
+
+ "contains": markFunction(function( text ) {
+ return function( elem ) {
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+ };
+ }),
+
+ "enabled": function( elem ) {
+ return elem.disabled === false;
+ },
+
+ "disabled": function( elem ) {
+ return elem.disabled === true;
+ },
+
+ "checked": function( elem ) {
+ // In CSS3, :checked should return both checked and selected elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ var nodeName = elem.nodeName.toLowerCase();
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+ },
+
+ "selected": function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ "parent": function( elem ) {
+ return !Expr.pseudos["empty"]( elem );
+ },
+
+ "empty": function( elem ) {
+ // http://www.w3.org/TR/selectors/#empty-pseudo
+ // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
+ // not comment, processing instructions, or others
+ // Thanks to Diego Perini for the nodeName shortcut
+ // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
+ var nodeType;
+ elem = elem.firstChild;
+ while ( elem ) {
+ if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) {
+ return false;
+ }
+ elem = elem.nextSibling;
+ }
+ return true;
+ },
+
+ "header": function( elem ) {
+ return rheader.test( elem.nodeName );
+ },
+
+ "text": function( elem ) {
+ var type, attr;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" &&
+ (type = elem.type) === "text" &&
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type );
+ },
+
+ // Input types
+ "radio": createInputPseudo("radio"),
+ "checkbox": createInputPseudo("checkbox"),
+ "file": createInputPseudo("file"),
+ "password": createInputPseudo("password"),
+ "image": createInputPseudo("image"),
+
+ "submit": createButtonPseudo("submit"),
+ "reset": createButtonPseudo("reset"),
+
+ "button": function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === "button" || name === "button";
+ },
+
+ "input": function( elem ) {
+ return rinputs.test( elem.nodeName );
+ },
+
+ "focus": function( elem ) {
+ var doc = elem.ownerDocument;
+ return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+ },
+
+ "active": function( elem ) {
+ return elem === elem.ownerDocument.activeElement;
+ },
+
+ // Positional types
+ "first": createPositionalPseudo(function() {
+ return [ 0 ];
+ }),
+
+ "last": createPositionalPseudo(function( matchIndexes, length ) {
+ return [ length - 1 ];
+ }),
+
+ "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ return [ argument < 0 ? argument + length : argument ];
+ }),
+
+ "even": createPositionalPseudo(function( matchIndexes, length ) {
+ for ( var i = 0; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "odd": createPositionalPseudo(function( matchIndexes, length ) {
+ for ( var i = 1; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ })
+ }
+};
+
+function siblingCheck( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
+
+ var cur = a.nextSibling;
+
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
+
+ cur = cur.nextSibling;
+ }
+
+ return 1;
+}
+
+sortOrder = docElem.compareDocumentPosition ?
+ function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ return ( !a.compareDocumentPosition || !b.compareDocumentPosition ?
+ a.compareDocumentPosition :
+ a.compareDocumentPosition(b) & 4
+ ) ? -1 : 1;
+ } :
+ function( a, b ) {
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Fallback to using sourceIndex (in IE) if it's available on both nodes
+ } else if ( a.sourceIndex && b.sourceIndex ) {
+ return a.sourceIndex - b.sourceIndex;
+ }
+
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
+ }
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
+ };
+
+// Always assume the presence of duplicates if sort doesn't
+// pass them to our comparison function (as in Google Chrome).
+[0, 0].sort( sortOrder );
+baseHasDuplicate = !hasDuplicate;
+
+// Document sorting and removing duplicates
+Sizzle.uniqueSort = function( results ) {
+ var elem,
+ duplicates = [],
+ i = 1,
+ j = 0;
+
+ hasDuplicate = baseHasDuplicate;
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ for ( ; (elem = results[i]); i++ ) {
+ if ( elem === results[ i - 1 ] ) {
+ j = duplicates.push( i );
+ }
+ }
+ while ( j-- ) {
+ results.splice( duplicates[ j ], 1 );
+ }
+ }
+
+ return results;
+};
+
+Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+function tokenize( selector, parseOnly ) {
+ var matched, match, tokens, type,
+ soFar, groups, preFilters,
+ cached = tokenCache[ expando ][ selector + " " ];
+
+ if ( cached ) {
+ return parseOnly ? 0 : cached.slice( 0 );
+ }
+
+ soFar = selector;
+ groups = [];
+ preFilters = Expr.preFilter;
+
+ while ( soFar ) {
+
+ // Comma and first run
+ if ( !matched || (match = rcomma.exec( soFar )) ) {
+ if ( match ) {
+ // Don't consume trailing commas as valid
+ soFar = soFar.slice( match[0].length ) || soFar;
+ }
+ groups.push( tokens = [] );
+ }
+
+ matched = false;
+
+ // Combinators
+ if ( (match = rcombinators.exec( soFar )) ) {
+ tokens.push( matched = new Token( match.shift() ) );
+ soFar = soFar.slice( matched.length );
+
+ // Cast descendant combinators to space
+ matched.type = match[0].replace( rtrim, " " );
+ }
+
+ // Filters
+ for ( type in Expr.filter ) {
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+ (match = preFilters[ type ]( match ))) ) {
+
+ tokens.push( matched = new Token( match.shift() ) );
+ soFar = soFar.slice( matched.length );
+ matched.type = type;
+ matched.matches = match;
+ }
+ }
+
+ if ( !matched ) {
+ break;
+ }
+ }
+
+ // Return the length of the invalid excess
+ // if we're just parsing
+ // Otherwise, throw an error or return tokens
+ return parseOnly ?
+ soFar.length :
+ soFar ?
+ Sizzle.error( selector ) :
+ // Cache the tokens
+ tokenCache( selector, groups ).slice( 0 );
+}
+
+function addCombinator( matcher, combinator, base ) {
+ var dir = combinator.dir,
+ checkNonElements = base && combinator.dir === "parentNode",
+ doneName = done++;
+
+ return combinator.first ?
+ // Check against closest ancestor/preceding element
+ function( elem, context, xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( checkNonElements || elem.nodeType === 1 ) {
+ return matcher( elem, context, xml );
+ }
+ }
+ } :
+
+ // Check against all ancestor/preceding elements
+ function( elem, context, xml ) {
+ // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+ if ( !xml ) {
+ var cache,
+ dirkey = dirruns + " " + doneName + " ",
+ cachedkey = dirkey + cachedruns;
+ while ( (elem = elem[ dir ]) ) {
+ if ( checkNonElements || elem.nodeType === 1 ) {
+ if ( (cache = elem[ expando ]) === cachedkey ) {
+ return elem.sizset;
+ } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) {
+ if ( elem.sizset ) {
+ return elem;
+ }
+ } else {
+ elem[ expando ] = cachedkey;
+ if ( matcher( elem, context, xml ) ) {
+ elem.sizset = true;
+ return elem;
+ }
+ elem.sizset = false;
+ }
+ }
+ }
+ } else {
+ while ( (elem = elem[ dir ]) ) {
+ if ( checkNonElements || elem.nodeType === 1 ) {
+ if ( matcher( elem, context, xml ) ) {
+ return elem;
+ }
+ }
+ }
+ }
+ };
+}
+
+function elementMatcher( matchers ) {
+ return matchers.length > 1 ?
+ function( elem, context, xml ) {
+ var i = matchers.length;
+ while ( i-- ) {
+ if ( !matchers[i]( elem, context, xml ) ) {
+ return false;
+ }
+ }
+ return true;
+ } :
+ matchers[0];
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+ var elem,
+ newUnmatched = [],
+ i = 0,
+ len = unmatched.length,
+ mapped = map != null;
+
+ for ( ; i < len; i++ ) {
+ if ( (elem = unmatched[i]) ) {
+ if ( !filter || filter( elem, context, xml ) ) {
+ newUnmatched.push( elem );
+ if ( mapped ) {
+ map.push( i );
+ }
+ }
+ }
+ }
+
+ return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+ if ( postFilter && !postFilter[ expando ] ) {
+ postFilter = setMatcher( postFilter );
+ }
+ if ( postFinder && !postFinder[ expando ] ) {
+ postFinder = setMatcher( postFinder, postSelector );
+ }
+ return markFunction(function( seed, results, context, xml ) {
+ var temp, i, elem,
+ preMap = [],
+ postMap = [],
+ preexisting = results.length,
+
+ // Get initial elements from seed or context
+ elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+ // Prefilter to get matcher input, preserving a map for seed-results synchronization
+ matcherIn = preFilter && ( seed || !selector ) ?
+ condense( elems, preMap, preFilter, context, xml ) :
+ elems,
+
+ matcherOut = matcher ?
+ // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+ postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+ // ...intermediate processing is necessary
+ [] :
+
+ // ...otherwise use results directly
+ results :
+ matcherIn;
+
+ // Find primary matches
+ if ( matcher ) {
+ matcher( matcherIn, matcherOut, context, xml );
+ }
+
+ // Apply postFilter
+ if ( postFilter ) {
+ temp = condense( matcherOut, postMap );
+ postFilter( temp, [], context, xml );
+
+ // Un-match failing elements by moving them back to matcherIn
+ i = temp.length;
+ while ( i-- ) {
+ if ( (elem = temp[i]) ) {
+ matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+ }
+ }
+ }
+
+ if ( seed ) {
+ if ( postFinder || preFilter ) {
+ if ( postFinder ) {
+ // Get the final matcherOut by condensing this intermediate into postFinder contexts
+ temp = [];
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) ) {
+ // Restore matcherIn since elem is not yet a final match
+ temp.push( (matcherIn[i] = elem) );
+ }
+ }
+ postFinder( null, (matcherOut = []), temp, xml );
+ }
+
+ // Move matched elements from seed to results to keep them synchronized
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) &&
+ (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+ seed[temp] = !(results[temp] = elem);
+ }
+ }
+ }
+
+ // Add elements to results, through postFinder if defined
+ } else {
+ matcherOut = condense(
+ matcherOut === results ?
+ matcherOut.splice( preexisting, matcherOut.length ) :
+ matcherOut
+ );
+ if ( postFinder ) {
+ postFinder( null, results, matcherOut, xml );
+ } else {
+ push.apply( results, matcherOut );
+ }
+ }
+ });
+}
+
+function matcherFromTokens( tokens ) {
+ var checkContext, matcher, j,
+ len = tokens.length,
+ leadingRelative = Expr.relative[ tokens[0].type ],
+ implicitRelative = leadingRelative || Expr.relative[" "],
+ i = leadingRelative ? 1 : 0,
+
+ // The foundational matcher ensures that elements are reachable from top-level context(s)
+ matchContext = addCombinator( function( elem ) {
+ return elem === checkContext;
+ }, implicitRelative, true ),
+ matchAnyContext = addCombinator( function( elem ) {
+ return indexOf.call( checkContext, elem ) > -1;
+ }, implicitRelative, true ),
+ matchers = [ function( elem, context, xml ) {
+ return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ (checkContext = context).nodeType ?
+ matchContext( elem, context, xml ) :
+ matchAnyContext( elem, context, xml ) );
+ } ];
+
+ for ( ; i < len; i++ ) {
+ if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+ matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
+ } else {
+ matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+ // Return special upon seeing a positional matcher
+ if ( matcher[ expando ] ) {
+ // Find the next relative operator (if any) for proper handling
+ j = ++i;
+ for ( ; j < len; j++ ) {
+ if ( Expr.relative[ tokens[j].type ] ) {
+ break;
+ }
+ }
+ return setMatcher(
+ i > 1 && elementMatcher( matchers ),
+ i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ),
+ matcher,
+ i < j && matcherFromTokens( tokens.slice( i, j ) ),
+ j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+ j < len && tokens.join("")
+ );
+ }
+ matchers.push( matcher );
+ }
+ }
+
+ return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ var bySet = setMatchers.length > 0,
+ byElement = elementMatchers.length > 0,
+ superMatcher = function( seed, context, xml, results, expandContext ) {
+ var elem, j, matcher,
+ setMatched = [],
+ matchedCount = 0,
+ i = "0",
+ unmatched = seed && [],
+ outermost = expandContext != null,
+ contextBackup = outermostContext,
+ // We must always have either seed elements or context
+ elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
+ // Nested matchers should use non-integer dirruns
+ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E);
+
+ if ( outermost ) {
+ outermostContext = context !== document && context;
+ cachedruns = superMatcher.el;
+ }
+
+ // Add elements passing elementMatchers directly to results
+ for ( ; (elem = elems[i]) != null; i++ ) {
+ if ( byElement && elem ) {
+ for ( j = 0; (matcher = elementMatchers[j]); j++ ) {
+ if ( matcher( elem, context, xml ) ) {
+ results.push( elem );
+ break;
+ }
+ }
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ cachedruns = ++superMatcher.el;
+ }
+ }
+
+ // Track unmatched elements for set filters
+ if ( bySet ) {
+ // They will have gone through all possible matchers
+ if ( (elem = !matcher && elem) ) {
+ matchedCount--;
+ }
+
+ // Lengthen the array for every element, matched or not
+ if ( seed ) {
+ unmatched.push( elem );
+ }
+ }
+ }
+
+ // Apply set filters to unmatched elements
+ matchedCount += i;
+ if ( bySet && i !== matchedCount ) {
+ for ( j = 0; (matcher = setMatchers[j]); j++ ) {
+ matcher( unmatched, setMatched, context, xml );
+ }
+
+ if ( seed ) {
+ // Reintegrate element matches to eliminate the need for sorting
+ if ( matchedCount > 0 ) {
+ while ( i-- ) {
+ if ( !(unmatched[i] || setMatched[i]) ) {
+ setMatched[i] = pop.call( results );
+ }
+ }
+ }
+
+ // Discard index placeholder values to get only actual matches
+ setMatched = condense( setMatched );
+ }
+
+ // Add matches to results
+ push.apply( results, setMatched );
+
+ // Seedless set matches succeeding multiple successful matchers stipulate sorting
+ if ( outermost && !seed && setMatched.length > 0 &&
+ ( matchedCount + setMatchers.length ) > 1 ) {
+
+ Sizzle.uniqueSort( results );
+ }
+ }
+
+ // Override manipulation of globals by nested matchers
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ outermostContext = contextBackup;
+ }
+
+ return unmatched;
+ };
+
+ superMatcher.el = 0;
+ return bySet ?
+ markFunction( superMatcher ) :
+ superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
+ var i,
+ setMatchers = [],
+ elementMatchers = [],
+ cached = compilerCache[ expando ][ selector + " " ];
+
+ if ( !cached ) {
+ // Generate a function of recursive functions that can be used to check each element
+ if ( !group ) {
+ group = tokenize( selector );
+ }
+ i = group.length;
+ while ( i-- ) {
+ cached = matcherFromTokens( group[i] );
+ if ( cached[ expando ] ) {
+ setMatchers.push( cached );
+ } else {
+ elementMatchers.push( cached );
+ }
+ }
+
+ // Cache the compiled function
+ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+ }
+ return cached;
+};
+
+function multipleContexts( selector, contexts, results ) {
+ var i = 0,
+ len = contexts.length;
+ for ( ; i < len; i++ ) {
+ Sizzle( selector, contexts[i], results );
+ }
+ return results;
+}
+
+function select( selector, context, results, seed, xml ) {
+ var i, tokens, token, type, find,
+ match = tokenize( selector ),
+ j = match.length;
+
+ if ( !seed ) {
+ // 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
+ tokens = match[0] = match[0].slice( 0 );
+ if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+ context.nodeType === 9 && !xml &&
+ Expr.relative[ tokens[1].type ] ) {
+
+ context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0];
+ if ( !context ) {
+ return results;
+ }
+
+ selector = selector.slice( tokens.shift().length );
+ }
+
+ // Fetch a seed set for right-to-left matching
+ for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) {
+ token = tokens[i];
+
+ // 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( rbackslash, "" ),
+ rsibling.test( tokens[0].type ) && context.parentNode || context,
+ xml
+ )) ) {
+
+ // If seed is empty or no tokens remain, we can return early
+ tokens.splice( i, 1 );
+ selector = seed.length && tokens.join("");
+ if ( !selector ) {
+ push.apply( results, slice.call( seed, 0 ) );
+ return results;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Compile and execute a filtering function
+ // Provide `match` to avoid retokenization if we modified the selector above
+ compile( selector, match )(
+ seed,
+ context,
+ xml,
+ results,
+ rsibling.test( selector )
+ );
+ return results;
+}
+
+if ( document.querySelectorAll ) {
+ (function() {
+ var disconnectedMatch,
+ oldSelect = select,
+ rescape = /'|\\/g,
+ rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
+
+ // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA
+ // A support test would require too much code (would include document ready)
+ rbuggyQSA = [ ":focus" ],
+
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+ // A support test would require too much code (would include document ready)
+ // just skip matchesSelector for :active
+ rbuggyMatches = [ ":active" ],
+ matches = docElem.matchesSelector ||
+ docElem.mozMatchesSelector ||
+ docElem.webkitMatchesSelector ||
+ docElem.oMatchesSelector ||
+ docElem.msMatchesSelector;
+
+ // Build QSA regex
+ // Regex strategy adopted from Diego Perini
+ assert(function( div ) {
+ // Select is set to empty string on purpose
+ // This is to test IE's treatment of not explictly
+ // setting a boolean content attribute,
+ // since its presence should be enough
+ // http://bugs.jquery.com/ticket/12359
+ div.innerHTML = "<select><option selected=''></option></select>";
+
+ // IE8 - Some boolean attributes are not treated correctly
+ if ( !div.querySelectorAll("[selected]").length ) {
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
+ }
+
+ // Webkit/Opera - :checked should return selected option elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ // IE8 throws error here (do not put tests after this one)
+ if ( !div.querySelectorAll(":checked").length ) {
+ rbuggyQSA.push(":checked");
+ }
+ });
+
+ assert(function( div ) {
+
+ // Opera 10-12/IE9 - ^= $= *= and empty values
+ // Should not select anything
+ div.innerHTML = "<p test=''></p>";
+ if ( div.querySelectorAll("[test^='']").length ) {
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
+ }
+
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ // IE8 throws error here (do not put tests after this one)
+ div.innerHTML = "<input type='hidden'/>";
+ if ( !div.querySelectorAll(":enabled").length ) {
+ rbuggyQSA.push(":enabled", ":disabled");
+ }
+ });
+
+ // rbuggyQSA always contains :focus, so no need for a length check
+ rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") );
+
+ select = function( selector, context, results, seed, xml ) {
+ // Only use querySelectorAll when not filtering,
+ // when this is not xml,
+ // and when no QSA bugs apply
+ if ( !seed && !xml && !rbuggyQSA.test( selector ) ) {
+ var groups, i,
+ old = true,
+ nid = expando,
+ newContext = context,
+ newSelector = context.nodeType === 9 && selector;
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ groups = tokenize( selector );
+
+ if ( (old = context.getAttribute("id")) ) {
+ nid = old.replace( rescape, "\\$&" );
+ } else {
+ context.setAttribute( "id", nid );
+ }
+ nid = "[id='" + nid + "'] ";
+
+ i = groups.length;
+ while ( i-- ) {
+ groups[i] = nid + groups[i].join("");
+ }
+ newContext = rsibling.test( selector ) && context.parentNode || context;
+ newSelector = groups.join(",");
+ }
+
+ if ( newSelector ) {
+ try {
+ push.apply( results, slice.call( newContext.querySelectorAll(
+ newSelector
+ ), 0 ) );
+ return results;
+ } catch(qsaError) {
+ } finally {
+ if ( !old ) {
+ context.removeAttribute("id");
+ }
+ }
+ }
+ }
+
+ return oldSelect( selector, context, results, seed, xml );
+ };
+
+ if ( matches ) {
+ assert(function( div ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9)
+ disconnectedMatch = matches.call( div, "div" );
+
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ try {
+ matches.call( div, "[test!='']:sizzle" );
+ rbuggyMatches.push( "!=", pseudos );
+ } catch ( e ) {}
+ });
+
+ // rbuggyMatches always contains :active and :focus, so no need for a length check
+ rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") );
+
+ Sizzle.matchesSelector = function( elem, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace( rattributeQuotes, "='$1']" );
+
+ // rbuggyMatches always contains :active, so no need for an existence check
+ if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) {
+ try {
+ var ret = matches.call( elem, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9
+ elem.document && elem.document.nodeType !== 11 ) {
+ return ret;
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle( expr, null, null, [ elem ] ).length > 0;
+ };
+ }
+ })();
+}
+
+// Deprecated
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Back-compat
+function setFilters() {}
+Expr.filters = setFilters.prototype = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
// Override sizzle attribute retrieval Sizzle.attr = jQuery.attr; jQuery.find = Sizzle; @@ -5134,9 +5348,9 @@ jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; - - -})( window ); +
+
+})( window );
var runtil = /Until$/, rparentsprev = /^(?:parents|prev(?:Until|All))/, isSimple = /^.[^:#\[\.,]*$/, @@ -5923,15 +6137,11 @@ jQuery.buildFragment = function( args, context, scripts ) { first = args[ 0 ]; // Set context from what may come in as undefined or a jQuery collection or a node + // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 & + // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception context = context || document; - context = (context[0] || context).ownerDocument || context[0] || context; - - // Ensure that an attr object doesn't incorrectly stand in as a document object - // Chrome and Firefox seem to allow this to occur and will throw exception - // Fixes #8950 - if ( typeof context.createDocumentFragment === "undefined" ) { - context = document; - } + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; // Only cache "small" (1/2 KB) HTML strings that are associated with the main document // Cloning options loses the selected state, so don't cache them @@ -6076,8 +6286,8 @@ jQuery.extend({ }, clean: function( elems, context, fragment, scripts ) { - var j, safe, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, - i = 0, + var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, + safe = context === document && safeFragment, ret = []; // Ensure that context is a document @@ -6086,7 +6296,7 @@ jQuery.extend({ } // Use the already-created safe fragment if context permits - for ( safe = context === document && safeFragment; (elem = elems[i]) != null; i++ ) { + for ( i = 0; (elem = elems[i]) != null; i++ ) { if ( typeof elem === "number" ) { elem += ""; } @@ -6102,7 +6312,8 @@ jQuery.extend({ } else { // Ensure a safe container in which to render the html safe = safe || createSafeFragment( context ); - div = div || safe.appendChild( context.createElement("div") ); + div = context.createElement("div"); + safe.appendChild( div ); // Fix "XHTML"-style tags in all browsers elem = elem.replace(rxhtmlTag, "<$1></$2>"); @@ -6145,21 +6356,20 @@ jQuery.extend({ elem = div.childNodes; - // Remember the top-level container for proper cleanup - div = safe.lastChild; + // Take out of fragment container (we need a fresh div each time) + div.parentNode.removeChild( div ); } } if ( elem.nodeType ) { ret.push( elem ); } else { - ret = jQuery.merge( ret, elem ); + jQuery.merge( ret, elem ); } } // Fix #11356: Clear elements from safeFragment if ( div ) { - safe.removeChild( div ); elem = div = safe = null; } @@ -6294,9 +6504,10 @@ if ( matched.browser ) { browser.version = matched.version; } -// Deprecated, use jQuery.browser.webkit instead -// Maintained for back-compat only -if ( browser.webkit ) { +// Chrome is Webkit, but Webkit is also Safari. +if ( browser.chrome ) { + browser.webkit = true; +} else if ( browser.webkit ) { browser.safari = true; } @@ -6322,23 +6533,25 @@ jQuery.sub = function() { var rootjQuerySub = jQuerySub(document); return jQuerySub; }; - + })(); var curCSS, iframe, iframeDoc, ralpha = /alpha\([^)]*\)/i, ropacity = /opacity=([^)]*)/, rposition = /^(top|right|bottom|left)$/, + // 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 + rdisplayswap = /^(none|table(?!-c[ea]).+)/, rmargin = /^margin/, rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ), - elemdisplay = {}, + elemdisplay = { BODY: "block" }, cssShow = { position: "absolute", visibility: "hidden", display: "block" }, cssNormalTransform = { letterSpacing: 0, - fontWeight: 400, - lineHeight: 1 + fontWeight: 400 }, cssExpand = [ "Top", "Right", "Bottom", "Left" ], @@ -6604,18 +6817,20 @@ jQuery.extend({ } }); -// NOTE: To any future maintainer, we've used both window.getComputedStyle -// and getComputedStyle here to produce a better gzip size +// NOTE: To any future maintainer, we've window.getComputedStyle +// because jsdom on node.js will break without it. if ( window.getComputedStyle ) { curCSS = function( elem, name ) { var ret, width, minWidth, maxWidth, - computed = getComputedStyle( elem, null ), + computed = window.getComputedStyle( elem, null ), style = elem.style; if ( computed ) { - ret = computed[ name ]; - if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) { + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { ret = jQuery.style( elem, name ); } @@ -6738,7 +6953,10 @@ function getWidthOrHeight( elem, name, extra ) { valueIsBorderBox = true, isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"; - if ( val <= 0 ) { + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { // Fall back to computed then uncomputed css if necessary val = curCSS( elem, name ); if ( val < 0 || val == null ) { @@ -6817,12 +7035,14 @@ jQuery.each([ "height", "width" ], function( i, name ) { jQuery.cssHooks[ name ] = { get: function( elem, computed, extra ) { if ( computed ) { - if ( elem.offsetWidth !== 0 || curCSS( elem, "display" ) !== "none" ) { - return getWidthOrHeight( elem, name, extra ); - } else { + // certain elements can have dimension info if we invisibly show them + // however, it must have a current display style that would benefit from this + if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) { return jQuery.swap( elem, cssShow, function() { return getWidthOrHeight( elem, name, extra ); }); + } else { + return getWidthOrHeight( elem, name, extra ); } } }, @@ -7056,10 +7276,10 @@ function buildParams( prefix, obj, traditional, add ) { add( prefix, obj ); } } -var // Document location - ajaxLocation, - // Document location segments +var + // Document location ajaxLocParts, + ajaxLocation, rhash = /#.*$/, rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL @@ -7228,7 +7448,7 @@ jQuery.fn.load = function( url, params, callback ) { params = undefined; // Otherwise, build a param string - } else if ( typeof params === "object" ) { + } else if ( params && typeof params === "object" ) { type = "POST"; } @@ -7576,7 +7796,7 @@ jQuery.extend({ // Set data for the fake xhr object jqXHR.status = status; - jqXHR.statusText = "" + ( nativeStatusText || statusText ); + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; // Success/Error if ( isSuccess ) { @@ -7636,11 +7856,11 @@ jQuery.extend({ // Extract dataTypes list s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace ); - // Determine if a cross-domain request is in order + // A cross-domain request is in order when we have a protocol:host:port mismatch if ( s.crossDomain == null ) { parts = rurl.exec( s.url.toLowerCase() ); s.crossDomain = !!( parts && - ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] || + ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) ); @@ -8262,7 +8482,7 @@ if ( jQuery.support.ajax ) { // on any attempt to access responseText (#11426) try { responses.text = xhr.responseText; - } catch( _ ) { + } catch( e ) { } // Firefox throws an exception when accessing @@ -8338,12 +8558,13 @@ var fxNow, timerId, animationPrefilters = [ defaultPrefilter ], tweeners = { "*": [function( prop, value ) { - var end, unit, prevScale, + var end, unit, tween = this.createTween( prop, value ), parts = rfxnum.exec( value ), target = tween.cur(), start = +target || 0, - scale = 1; + scale = 1, + maxIterations = 20; if ( parts ) { end = +parts[2]; @@ -8359,17 +8580,15 @@ var fxNow, timerId, do { // 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 - prevScale = scale = scale || ".5"; + scale = scale || ".5"; // Adjust and apply start = start / scale; jQuery.style( tween.elem, prop, start + unit ); - // Update scale, tolerating zeroes from tween.cur() - scale = tween.cur() / target; - - // Stop looping if we've hit the mark or scale is unchanged - } while ( scale !== 1 && scale !== prevScale ); + // 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 + } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); } tween.unit = unit; @@ -8416,7 +8635,9 @@ function Animation( elem, properties, options ) { tick = function() { var currentTime = fxNow || createFxNow(), remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - percent = 1 - ( remaining / animation.duration || 0 ), + // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, index = 0, length = animation.tweens.length; @@ -8568,7 +8789,7 @@ jQuery.Animation = jQuery.extend( Animation, { }); function defaultPrefilter( elem, props, opts ) { - var index, prop, value, length, dataShow, tween, hooks, oldfire, + var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire, anim = this, style = elem.style, orig = {}, @@ -8642,6 +8863,7 @@ function defaultPrefilter( elem, props, opts ) { value = props[ index ]; if ( rfxtypes.exec( value ) ) { delete props[ index ]; + toggle = toggle || value === "toggle"; if ( value === ( hidden ? "hide" : "show" ) ) { continue; } @@ -8652,6 +8874,14 @@ function defaultPrefilter( elem, props, opts ) { length = handled.length; if ( length ) { dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} ); + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + + // store state if its toggle - enables .stop().toggle() to "reverse" + if ( toggle ) { + dataShow.hidden = !hidden; + } if ( hidden ) { jQuery( elem ).show(); } else { @@ -8709,7 +8939,13 @@ Tween.prototype = { var eased, hooks = Tween.propHooks[ this.prop ]; - this.pos = eased = jQuery.easing[ this.easing ]( percent, this.options.duration * percent, 0, 1, this.options.duration ); + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } this.now = ( this.end - this.start ) * eased + this.start; if ( this.options.step ) { @@ -8867,6 +9103,7 @@ function genFx( type, includeWidth ) { // 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 + includeWidth = includeWidth? 1 : 0; for( ; i < 4 ; i += 2 - includeWidth ) { which = cssExpand[ i ]; attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; @@ -8941,6 +9178,8 @@ jQuery.fx.tick = function() { timers = jQuery.timers, i = 0; + fxNow = jQuery.now(); + for ( ; i < timers.length; i++ ) { timer = timers[ i ]; // Checks the timer has not already been removed @@ -8952,6 +9191,7 @@ jQuery.fx.tick = function() { if ( !timers.length ) { jQuery.fx.stop(); } + fxNow = undefined; }; jQuery.fx.timer = function( timer ) { @@ -8995,7 +9235,8 @@ jQuery.fn.offset = function( options ) { }); } - var box, docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, top, left, + var docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, + box = { top: 0, left: 0 }, elem = this[ 0 ], doc = elem && elem.ownerDocument; @@ -9009,21 +9250,25 @@ jQuery.fn.offset = function( options ) { docElem = doc.documentElement; - // Make sure we're not dealing with a disconnected DOM node + // Make sure it's not a disconnected DOM node if ( !jQuery.contains( docElem, elem ) ) { - return { top: 0, left: 0 }; + return box; } - box = elem.getBoundingClientRect(); + // If we don't have gBCR, just use 0,0 rather than error + // BlackBerry 5, iOS 3 (original iPhone) + if ( typeof elem.getBoundingClientRect !== "undefined" ) { + box = elem.getBoundingClientRect(); + } win = getWindow( doc ); clientTop = docElem.clientTop || body.clientTop || 0; clientLeft = docElem.clientLeft || body.clientLeft || 0; scrollTop = win.pageYOffset || docElem.scrollTop; scrollLeft = win.pageXOffset || docElem.scrollLeft; - top = box.top + scrollTop - clientTop; - left = box.left + scrollLeft - clientLeft; - - return { top: top, left: left }; + return { + top: box.top + scrollTop - clientTop, + left: box.left + scrollLeft - clientLeft + }; }; jQuery.offset = { @@ -9201,7 +9446,7 @@ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { // Set width or height on the element jQuery.style( elem, type, value, extra ); - }, type, chainable ? margin : undefined, chainable ); + }, type, chainable ? margin : undefined, chainable, null ); }; }); }); @@ -9224,4 +9469,4 @@ if ( typeof define === "function" && define.amd && define.amd.jQuery ) { define( "jquery", [], function () { return jQuery; } ); } -})( window );
\ No newline at end of file +})( window ); diff --git a/module/web/static/js/libs/jquery.peity-0.6.js b/module/web/static/js/libs/jquery.peity-0.6.js deleted file mode 100644 index 323ba6fac..000000000 --- a/module/web/static/js/libs/jquery.peity-0.6.js +++ /dev/null @@ -1,184 +0,0 @@ -// Peity jQuery plugin version 0.6.0 -// (c) 2011 Ben Pickles -// -// http://benpickles.github.com/peity/ -// -// Released under MIT license. -(function($, document) { - var peity = $.fn.peity = function(type, options) { - if (document.createElement("canvas").getContext) { - this.each(function() { - $(this).change(function() { - var opts = $.extend({}, options) - var self = this - - $.each(opts, function(name, value) { - if ($.isFunction(value)) opts[name] = value.call(self) - }) - - var value = $(this).html(); - peity.graphers[type].call(this, $.extend({}, peity.defaults[type], opts)); - $(this).trigger("chart:changed", value); - }).trigger("change"); - }); - } - - return this; - }; - - peity.graphers = {}; - peity.defaults = {}; - - peity.add = function(type, defaults, grapher){ - peity.graphers[type] = grapher; - peity.defaults[type] = defaults; - }; - - var devicePixelRatio = window.devicePixelRatio || 1 - - function createCanvas(width, height) { - var canvas = document.createElement("canvas") - canvas.setAttribute("width", width * devicePixelRatio) - canvas.setAttribute("height", height * devicePixelRatio) - - if (devicePixelRatio != 1) { - var style = "width:" + width + "px;height:" + height + "px" - canvas.setAttribute("style", style) - } - - return canvas - } - - peity.add( - 'pie', - { - colours: ['#FFF4DD', '#FF9900'], - delimeter: '/', - diameter: 16 - }, - function(opts) { - var $this = $(this) - var values = $this.text().split(opts.delimeter) - var v1 = parseFloat(values[0]); - var v2 = parseFloat(values[1]); - var adjust = -Math.PI / 2; - var slice = (v1 / v2) * Math.PI * 2; - - var canvas = createCanvas(opts.diameter, opts.diameter) - var context = canvas.getContext("2d"); - var centre = canvas.width / 2; - - // Plate. - context.beginPath(); - context.moveTo(centre, centre); - context.arc(centre, centre, centre, slice + adjust, (slice == 0) ? Math.PI * 2 : adjust, false); - context.fillStyle = opts.colours[0]; - context.fill(); - - // Slice of pie. - context.beginPath(); - context.moveTo(centre, centre); - context.arc(centre, centre, centre, adjust, slice + adjust, false); - context.fillStyle = opts.colours[1]; - context.fill(); - - $this.wrapInner($("<span>").hide()).append(canvas) - }); - - peity.add( - "line", - { - colour: "#c6d9fd", - strokeColour: "#4d89f9", - strokeWidth: 1, - delimeter: ",", - height: 16, - max: null, - min: 0, - width: 32 - }, - function(opts) { - var $this = $(this) - var canvas = createCanvas(opts.width, opts.height) - var values = $this.text().split(opts.delimeter) - if (values.length == 1) values.push(values[0]) - var max = Math.max.apply(Math, values.concat([opts.max])); - var min = Math.min.apply(Math, values.concat([opts.min])) - - var context = canvas.getContext("2d"); - var width = canvas.width - var height = canvas.height - var xQuotient = width / (values.length - 1) - var yQuotient = height / (max - min) - - var coords = []; - var i; - - context.beginPath(); - context.moveTo(0, height + (min * yQuotient)) - - for (i = 0; i < values.length; i++) { - var x = i * xQuotient - var y = height - (yQuotient * (values[i] - min)) - - coords.push({ x: x, y: y }); - context.lineTo(x, y); - } - - context.lineTo(width, height + (min * yQuotient)) - context.fillStyle = opts.colour; - context.fill(); - - if (opts.strokeWidth) { - context.beginPath(); - context.moveTo(0, coords[0].y); - for (i = 0; i < coords.length; i++) { - context.lineTo(coords[i].x, coords[i].y); - } - context.lineWidth = opts.strokeWidth * devicePixelRatio; - context.strokeStyle = opts.strokeColour; - context.stroke(); - } - - $this.wrapInner($("<span>").hide()).append(canvas) - } - ); - - peity.add( - 'bar', - { - colour: "#4D89F9", - delimeter: ",", - height: 16, - max: null, - min: 0, - width: 32 - }, - function(opts) { - var $this = $(this) - var values = $this.text().split(opts.delimeter) - var max = Math.max.apply(Math, values.concat([opts.max])); - var min = Math.min.apply(Math, values.concat([opts.min])) - - var canvas = createCanvas(opts.width, opts.height) - var context = canvas.getContext("2d"); - - var width = canvas.width - var height = canvas.height - var yQuotient = height / (max - min) - var space = devicePixelRatio / 2 - var xQuotient = (width + space) / values.length - - context.fillStyle = opts.colour; - - for (var i = 0; i < values.length; i++) { - var x = i * xQuotient - var y = height - (yQuotient * (values[i] - min)) - - context.fillRect(x, y, xQuotient - space, yQuotient * values[i]) - } - - $this.wrapInner($("<span>").hide()).append(canvas) - } - ); -})(jQuery, document); diff --git a/module/web/static/js/libs/jquery.peity-1.0.js b/module/web/static/js/libs/jquery.peity-1.0.js new file mode 100644 index 000000000..8c2abc236 --- /dev/null +++ b/module/web/static/js/libs/jquery.peity-1.0.js @@ -0,0 +1,246 @@ +// Peity jQuery plugin version 1.0.0 +// (c) 2012 Ben Pickles +// +// http://benpickles.github.com/peity +// +// Released under MIT license. +(function($, document, Math, devicePixelRatio) { + var canvasSupported = document.createElement("canvas").getContext + + var peity = $.fn.peity = function(type, options) { + if (canvasSupported) { + this.each(function() { + var defaults = peity.defaults[type] + var data = {} + var $this = $(this) + + $.each($this.data(), function(name, value) { + if (name in defaults) data[name] = value + }) + + var opts = $.extend({}, defaults, data, options) + var chart = new Peity($this, type, opts) + chart.draw() + + $this.change(function() { + chart.draw() + }) + }); + } + + return this; + }; + + var Peity = function($elem, type, opts) { + this.$elem = $elem + this.type = type + this.opts = opts + } + + var PeityPrototype = Peity.prototype + + PeityPrototype.colours = function() { + var colours = this.opts.colours + var func = colours + + if (!$.isFunction(func)) { + func = function(_, i) { + return colours[i % colours.length] + } + } + + return func + } + + PeityPrototype.draw = function() { + peity.graphers[this.type].call(this, this.opts) + } + + PeityPrototype.prepareCanvas = function(width, height) { + var canvas = this.canvas + + if (canvas) { + this.context.clearRect(0, 0, canvas.width, canvas.height) + } else { + canvas = $("<canvas>").attr({ + height: height * devicePixelRatio, + width: width * devicePixelRatio + }) + + if (devicePixelRatio != 1) { + canvas.css({ + height: height, + width: width + }) + } + + this.canvas = canvas = canvas[0] + this.context = canvas.getContext("2d") + this.$elem.hide().before(canvas) + } + + return canvas + } + + PeityPrototype.values = function() { + return $.map(this.$elem.text().split(this.opts.delimiter), function(value) { + return parseFloat(value) + }) + } + + peity.defaults = {} + peity.graphers = {} + + peity.register = function(type, defaults, grapher) { + this.defaults[type] = defaults + this.graphers[type] = grapher + } + + peity.register( + 'pie', + { + colours: ["#ff9900", "#fff4dd", "#ffc66e"], + delimiter: null, + diameter: 16 + }, + function(opts) { + if (!opts.delimiter) { + var delimiter = this.$elem.text().match(/[^0-9\.]/) + opts.delimiter = delimiter ? delimiter[0] : "," + } + + var values = this.values() + + if (opts.delimiter == "/") { + var v1 = values[0] + var v2 = values[1] + values = [v1, v2 - v1] + } + + var i = 0 + var length = values.length + var sum = 0 + + for (; i < length; i++) { + sum += values[i] + } + + var canvas = this.prepareCanvas(opts.diameter, opts.diameter) + var context = this.context + var half = canvas.width / 2 + var pi = Math.PI + var colours = this.colours() + + context.save() + context.translate(half, half) + context.rotate(-pi / 2) + + for (i = 0; i < length; i++) { + var value = values[i] + var slice = (value / sum) * pi * 2 + + context.beginPath() + context.moveTo(0, 0) + context.arc(0, 0, half, 0, slice, false) + context.fillStyle = colours.call(this, value, i, values) + context.fill() + context.rotate(slice) + } + + context.restore() + } + ) + + peity.register( + "line", + { + colour: "#c6d9fd", + strokeColour: "#4d89f9", + strokeWidth: 1, + delimiter: ",", + height: 16, + max: null, + min: 0, + width: 32 + }, + function(opts) { + var values = this.values() + if (values.length == 1) values.push(values[0]) + var max = Math.max.apply(Math, values.concat([opts.max])); + var min = Math.min.apply(Math, values.concat([opts.min])) + + var canvas = this.prepareCanvas(opts.width, opts.height) + var context = this.context + var width = canvas.width + var height = canvas.height + var xQuotient = width / (values.length - 1) + var yQuotient = height / (max - min) + + var coords = []; + var i; + + context.beginPath(); + context.moveTo(0, height + (min * yQuotient)) + + for (i = 0; i < values.length; i++) { + var x = i * xQuotient + var y = height - (yQuotient * (values[i] - min)) + + coords.push({ x: x, y: y }); + context.lineTo(x, y); + } + + context.lineTo(width, height + (min * yQuotient)) + context.fillStyle = opts.colour; + context.fill(); + + if (opts.strokeWidth) { + context.beginPath(); + context.moveTo(0, coords[0].y); + for (i = 0; i < coords.length; i++) { + context.lineTo(coords[i].x, coords[i].y); + } + context.lineWidth = opts.strokeWidth * devicePixelRatio; + context.strokeStyle = opts.strokeColour; + context.stroke(); + } + } + ); + + peity.register( + 'bar', + { + colours: ["#4D89F9"], + delimiter: ",", + height: 16, + max: null, + min: 0, + spacing: devicePixelRatio, + width: 32 + }, + function(opts) { + var values = this.values() + var max = Math.max.apply(Math, values.concat([opts.max])); + var min = Math.min.apply(Math, values.concat([opts.min])) + + var canvas = this.prepareCanvas(opts.width, opts.height) + var context = this.context + + var width = canvas.width + var height = canvas.height + var yQuotient = height / (max - min) + var space = opts.spacing + var xQuotient = (width + space) / values.length + var colours = this.colours() + + for (var i = 0; i < values.length; i++) { + var value = values[i] + var x = i * xQuotient + var y = height - (yQuotient * (value - min)) + + context.fillStyle = colours.call(this, value, i, values) + context.fillRect(x, y, xQuotient - space, yQuotient * values[i]) + } + } + ); +})(jQuery, document, Math, window.devicePixelRatio || 1); diff --git a/module/web/static/js/libs/jquery.transit-0.1.3.js b/module/web/static/js/libs/jquery.transit-0.9.9.js index 2314f2ca2..f0d9719dd 100644 --- a/module/web/static/js/libs/jquery.transit-0.1.3.js +++ b/module/web/static/js/libs/jquery.transit-0.9.9.js @@ -1,6 +1,6 @@ /*! * jQuery Transit - CSS3 transitions and transformations - * Copyright(c) 2011 Rico Sta. Cruz <rico@ricostacruz.com> + * (c) 2011-2012 Rico Sta. Cruz <rico@ricostacruz.com> * MIT Licensed. * * http://ricostacruz.com/jquery.transit @@ -8,10 +8,8 @@ */ (function($) { - "use strict"; - $.transit = { - version: "0.1.3", + version: "0.9.9", // Map of $.css() keys to values for 'transitionProperty'. // See https://developer.mozilla.org/en/CSS/CSS_transitions#Properties_that_can_be_animated @@ -39,6 +37,9 @@ // Helper function to get the proper vendor property name. // (`transition` => `WebkitTransition`) function getVendorPropertyName(prop) { + // Handle unprefixed versions (FF16+, for example) + if (prop in div.style) return prop; + var prefixes = ['Moz', 'Webkit', 'O', 'ms']; var prop_ = prop.charAt(0).toUpperCase() + prop.substr(1); @@ -61,18 +62,14 @@ var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1; // Check for the browser's transitions support. - // You can access this in jQuery's `$.support.transition`. - // As per [jQuery's cssHooks documentation](http://api.jquery.com/jQuery.cssHooks/), - // we set $.support.transition to a string of the actual property name used. support.transition = getVendorPropertyName('transition'); support.transitionDelay = getVendorPropertyName('transitionDelay'); support.transform = getVendorPropertyName('transform'); support.transformOrigin = getVendorPropertyName('transformOrigin'); support.transform3d = checkTransform3dSupport(); - $.extend($.support, support); - var eventNames = { + 'transition': 'transitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd', 'WebkitTransition': 'webkitTransitionEnd', @@ -82,17 +79,50 @@ // Detect the 'transitionend' event needed. var transitionEnd = support.transitionEnd = eventNames[support.transition] || null; + // Populate jQuery's `$.support` with the vendor prefixes we know. + // As per [jQuery's cssHooks documentation](http://api.jquery.com/jQuery.cssHooks/), + // we set $.support.transition to a string of the actual property name used. + for (var key in support) { + if (support.hasOwnProperty(key) && typeof $.support[key] === 'undefined') { + $.support[key] = support[key]; + } + } + // Avoid memory leak in IE. div = null; // ## $.cssEase // List of easing aliases that you can use with `$.fn.transition`. $.cssEase = { - '_default': 'ease', - 'in': 'ease-in', - 'out': 'ease-out', - 'in-out': 'ease-in-out', - 'snap': 'cubic-bezier(0,1,.5,1)' + '_default': 'ease', + 'in': 'ease-in', + 'out': 'ease-out', + 'in-out': 'ease-in-out', + 'snap': 'cubic-bezier(0,1,.5,1)', + // Penner equations + 'easeOutCubic': 'cubic-bezier(.215,.61,.355,1)', + 'easeInOutCubic': 'cubic-bezier(.645,.045,.355,1)', + 'easeInCirc': 'cubic-bezier(.6,.04,.98,.335)', + 'easeOutCirc': 'cubic-bezier(.075,.82,.165,1)', + 'easeInOutCirc': 'cubic-bezier(.785,.135,.15,.86)', + 'easeInExpo': 'cubic-bezier(.95,.05,.795,.035)', + 'easeOutExpo': 'cubic-bezier(.19,1,.22,1)', + 'easeInOutExpo': 'cubic-bezier(1,0,0,1)', + 'easeInQuad': 'cubic-bezier(.55,.085,.68,.53)', + 'easeOutQuad': 'cubic-bezier(.25,.46,.45,.94)', + 'easeInOutQuad': 'cubic-bezier(.455,.03,.515,.955)', + 'easeInQuart': 'cubic-bezier(.895,.03,.685,.22)', + 'easeOutQuart': 'cubic-bezier(.165,.84,.44,1)', + 'easeInOutQuart': 'cubic-bezier(.77,0,.175,1)', + 'easeInQuint': 'cubic-bezier(.755,.05,.855,.06)', + 'easeOutQuint': 'cubic-bezier(.23,1,.32,1)', + 'easeInOutQuint': 'cubic-bezier(.86,0,.07,1)', + 'easeInSine': 'cubic-bezier(.47,0,.745,.715)', + 'easeOutSine': 'cubic-bezier(.39,.575,.565,1)', + 'easeInOutSine': 'cubic-bezier(.445,.05,.55,.95)', + 'easeInBack': 'cubic-bezier(.6,-.28,.735,.045)', + 'easeOutBack': 'cubic-bezier(.175, .885,.32,1.275)', + 'easeInOutBack': 'cubic-bezier(.68,-.55,.265,1.55)' }; // ## 'transform' CSS hook @@ -103,10 +133,10 @@ // $("#hello").css('transform'); // //=> { rotate: '90deg' } // - $.cssHooks.transform = { + $.cssHooks['transit:transform'] = { // The getter returns a `Transform` object. get: function(elem) { - return $(elem).data('transform'); + return $(elem).data('transform') || new Transform(); }, // The setter accepts a `Transform` object or a string. @@ -132,34 +162,45 @@ } }; - // ## 'transformOrigin' CSS hook - // Allows the use for `transformOrigin` to define where scaling and rotation - // is pivoted. - // - // $("#hello").css({ transformOrigin: '0 0' }); - // - $.cssHooks.transformOrigin = { - get: function(elem) { - return elem.style[support.transformOrigin]; - }, - set: function(elem, value) { - elem.style[support.transformOrigin] = value; - } + // Add a CSS hook for `.css({ transform: '...' })`. + // In jQuery 1.8+, this will intentionally override the default `transform` + // CSS hook so it'll play well with Transit. (see issue #62) + $.cssHooks.transform = { + set: $.cssHooks['transit:transform'].set }; - // ## 'transition' CSS hook - // Allows you to use the `transition` property in CSS. - // - // $("#hello").css({ transition: 'all 0 ease 0' }); - // - $.cssHooks.transition = { - get: function(elem) { - return elem.style[support.transition]; - }, - set: function(elem, value) { - elem.style[support.transition] = value; - } - }; + // jQuery 1.8+ supports prefix-free transitions, so these polyfills will not + // be necessary. + if ($.fn.jquery < "1.8") { + // ## 'transformOrigin' CSS hook + // Allows the use for `transformOrigin` to define where scaling and rotation + // is pivoted. + // + // $("#hello").css({ transformOrigin: '0 0' }); + // + $.cssHooks.transformOrigin = { + get: function(elem) { + return elem.style[support.transformOrigin]; + }, + set: function(elem, value) { + elem.style[support.transformOrigin] = value; + } + }; + + // ## 'transition' CSS hook + // Allows you to use the `transition` property in CSS. + // + // $("#hello").css({ transition: 'all 0 ease 0' }); + // + $.cssHooks.transition = { + get: function(elem) { + return elem.style[support.transition]; + }, + set: function(elem, value) { + elem.style[support.transition] = value; + } + }; + } // ## Other CSS hooks // Allows you to rotate, scale and translate. @@ -310,8 +351,8 @@ if (this._translateX === undefined) { this._translateX = 0; } if (this._translateY === undefined) { this._translateY = 0; } - if (x !== null) { this._translateX = unit(x, 'px'); } - if (y !== null) { this._translateY = unit(y, 'px'); } + if (x !== null && x !== undefined) { this._translateX = unit(x, 'px'); } + if (y !== null && y !== undefined) { this._translateY = unit(y, 'px'); } this.translate = this._translateX + "," + this._translateY; } @@ -405,7 +446,7 @@ $.each(props, function(key) { key = $.camelCase(key); // Convert "text-align" => "textAlign" - key = $.transit.propertyMap[key] || key; + key = $.transit.propertyMap[key] || $.cssProps[key] || key; key = uncamel(key); // Convert back to dasherized if ($.inArray(key, re) === -1) { re.push(key); } @@ -603,17 +644,18 @@ $.cssHooks[prop] = { get: function(elem) { - var t = $(elem).css('transform') || new Transform(); + var t = $(elem).css('transit:transform'); return t.get(prop); }, set: function(elem, value) { - var t = $(elem).css('transform'); - t = (typeof t === 'string' || t === null) ? new Transform() : t; - t.setFromString(prop, value); - $(elem).css({ transform: t }); + var t = $(elem).css('transit:transform'); + t.setFromString(prop, value); + + $(elem).css({ 'transit:transform': t }); } }; + } // ### uncamel(str) diff --git a/module/web/static/js/libs/lodash-0.7.0.js b/module/web/static/js/libs/lodash-1.0.rc3.js index 7843feb80..f4a8bb12d 100644 --- a/module/web/static/js/libs/lodash-0.7.0.js +++ b/module/web/static/js/libs/lodash-1.0.rc3.js @@ -1,34 +1,39 @@ /*! - * Lo-Dash v0.7.0 <http://lodash.com> + * Lo-Dash 1.0.0-rc.3 <http://lodash.com> * (c) 2012 John-David Dalton <http://allyoucanleet.com/> - * Based on Underscore.js 1.4.0 <http://underscorejs.org> + * Based on Underscore.js 1.4.3 <http://underscorejs.org> * (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. * Available under MIT license <http://lodash.com/license> */ ;(function(window, undefined) { - 'use strict'; /** Detect free variable `exports` */ - var freeExports = typeof exports == 'object' && exports && - (typeof global == 'object' && global && global == global.global && (window = global), exports); + var freeExports = typeof exports == 'object' && exports; - /** Native prototype shortcuts */ - var ArrayProto = Array.prototype, - BoolProto = Boolean.prototype, - ObjectProto = Object.prototype, - NumberProto = Number.prototype, - StringProto = String.prototype; + /** Detect free variable `global` and use it as `window` */ + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal) { + window = freeGlobal; + } + + /** Used for array and object method references */ + var arrayRef = [], + // avoid a Closure Compiler bug by creatively creating an object + objectRef = new function(){}; /** Used to generate unique IDs */ var idCounter = 0; + /** Used internally to indicate various things */ + var indicatorObject = objectRef; + /** Used by `cachedContains` as the default size when optimizations are enabled for large arrays */ var largeArraySize = 30; /** Used to restore the original `_` reference in `noConflict` */ var oldDash = window._; - /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ + /** Used to detect template delimiter values that require a with-statement */ var reComplexDelimiter = /[-?+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; /** Used to match HTML entities */ @@ -47,12 +52,21 @@ /** Used to detect if a method is native */ var reNative = RegExp('^' + - (ObjectProto.valueOf + '') + (objectRef.valueOf + '') .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') .replace(/valueOf|for [^\]]+/g, '.+?') + '$' ); - /** Used to ensure capturing order and avoid matches for undefined delimiters */ + /** + * Used to match ES6 template delimiters + * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.8.6 + */ + var reEsTemplate = /\$\{((?:(?=\\?)\\?[\s\S])*?)}/g; + + /** Used to match "interpolate" template delimiters */ + var reInterpolate = /<%=([\s\S]+?)%>/g; + + /** Used to ensure capturing order of template delimiters */ var reNoMatch = /($^)/; /** Used to match HTML characters */ @@ -71,19 +85,20 @@ var templateCounter = 0; /** Native method shortcuts */ - var concat = ArrayProto.concat, - hasOwnProperty = ObjectProto.hasOwnProperty, - push = ArrayProto.push, - propertyIsEnumerable = ObjectProto.propertyIsEnumerable, - slice = ArrayProto.slice, - toString = ObjectProto.toString; + var ceil = Math.ceil, + concat = arrayRef.concat, + floor = Math.floor, + getPrototypeOf = reNative.test(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, + hasOwnProperty = objectRef.hasOwnProperty, + push = arrayRef.push, + propertyIsEnumerable = objectRef.propertyIsEnumerable, + toString = objectRef.toString; /* Native method shortcuts for methods with the same name as other `lodash` methods */ var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, - nativeFloor = Math.floor, - nativeGetPrototypeOf = reNative.test(nativeGetPrototypeOf = Object.getPrototypeOf) && nativeGetPrototypeOf, nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, nativeIsFinite = window.isFinite, + nativeIsNaN = window.isNaN, nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys, nativeMax = Math.max, nativeMin = Math.min, @@ -100,9 +115,15 @@ regexpClass = '[object RegExp]', stringClass = '[object String]'; - /** Timer shortcuts */ - var clearTimeout = window.clearTimeout, - setTimeout = window.setTimeout; + /** Detect various environments */ + var isIeOpera = !!window.attachEvent, + isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera); + + /* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */ + var isBindFast = nativeBind && !isV8; + + /* Detect if `Object.keys` exists and is inferred to be fast (IE, Opera, V8) */ + var isKeysFast = nativeKeys && (isIeOpera || isV8); /** * Detect the JScript [[DontEnum]] bug: @@ -112,6 +133,9 @@ */ var hasDontEnumBug; + /** Detect if own properties are iterated after inherited properties (IE < 9) */ + var iteratesOwnLast; + /** * Detect if `Array#shift` and `Array#splice` augment array-like objects * incorrectly: @@ -122,33 +146,28 @@ * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. */ - var hasObjectSpliceBug; - - /** Detect if own properties are iterated after inherited properties (IE < 9) */ - var iteratesOwnLast; + var hasObjectSpliceBug = (hasObjectSpliceBug = { '0': 1, 'length': 1 }, + arrayRef.splice.call(hasObjectSpliceBug, 0, 1), hasObjectSpliceBug[0]); /** Detect if an `arguments` object's indexes are non-enumerable (IE < 9) */ - var noArgsEnum = true; + var nonEnumArgs = true; (function() { - var object = { '0': 1, 'length': 1 }, - props = []; - + var props = []; function ctor() { this.x = 1; } ctor.prototype = { 'valueOf': 1, 'y': 1 }; for (var prop in new ctor) { props.push(prop); } - for (prop in arguments) { noArgsEnum = !prop; } + for (prop in arguments) { nonEnumArgs = !prop; } - hasDontEnumBug = (props + '').length < 4; + hasDontEnumBug = !/valueOf/.test(props); iteratesOwnLast = props[0] != 'x'; - hasObjectSpliceBug = (props.splice.call(object, 0, 1), object[0]); }(1)); - /** Detect if an `arguments` object's [[Class]] is unresolvable (Firefox < 4, IE < 9) */ - var noArgsClass = !isArguments(arguments); + /** Detect if `arguments` objects are `Object` objects (all but Opera < 10.5) */ + var argsAreObjects = arguments.constructor == Object; - /** Detect if `Array#slice` cannot be used to convert strings to arrays (Opera < 10.52) */ - var noArraySliceOnStrings = slice.call('x')[0] != 'x'; + /** Detect if `arguments` objects [[Class]] is unresolvable (Firefox < 4, IE < 9) */ + var noArgsClass = !isArguments(arguments); /** * Detect lack of support for accessing string characters by index: @@ -164,23 +183,14 @@ * a string without a `toString` property value of `typeof` "function". */ try { - var noNodeClass = ({ 'toString': 0 } + '', toString.call(window.document || 0) == objectClass); + var noNodeClass = ({ 'toString': 0 } + '', toString.call(document) == objectClass); } catch(e) { } - /* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */ - var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); - - /* Detect if `Object.keys` exists and is inferred to be fast (IE, Opera, V8) */ - var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); - - /* Detect if strict mode, "use strict", is inferred to be fast (V8) */ - var isStrictFast = !isBindFast; - /** * Detect if sourceURL syntax is usable without erroring: * - * The JS engine in Adobe products, like InDesign, will throw a syntax error - * when it encounters a single line comment beginning with the `@` symbol. + * The JS engine embedded in Adobe products will throw a syntax error when + * it encounters a single line comment beginning with the `@` symbol. * * The JS engine in Narwhal will generate the function `function anonymous(){//}` * and throw a syntax error. @@ -190,21 +200,26 @@ * http://msdn.microsoft.com/en-us/library/121hztk3(v=vs.94).aspx */ try { - var useSourceURL = (Function('//@')(), !window.attachEvent); + var useSourceURL = (Function('//@')(), !isIeOpera); } catch(e) { } - /** Used to identify object classifications that are array-like */ - var arrayLikeClasses = {}; - arrayLikeClasses[boolClass] = arrayLikeClasses[dateClass] = arrayLikeClasses[funcClass] = - arrayLikeClasses[numberClass] = arrayLikeClasses[objectClass] = arrayLikeClasses[regexpClass] = false; - arrayLikeClasses[argsClass] = arrayLikeClasses[arrayClass] = arrayLikeClasses[stringClass] = true; - /** Used to identify object classifications that `_.clone` supports */ var cloneableClasses = {}; - cloneableClasses[argsClass] = cloneableClasses[funcClass] = false; - cloneableClasses[arrayClass] = cloneableClasses[boolClass] = cloneableClasses[dateClass] = - cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] = - cloneableClasses[stringClass] = true; + cloneableClasses[funcClass] = false; + cloneableClasses[argsClass] = cloneableClasses[arrayClass] = + cloneableClasses[boolClass] = cloneableClasses[dateClass] = + cloneableClasses[numberClass] = cloneableClasses[objectClass] = + cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; + + /** Used to lookup a built-in constructor by [[Class]] */ + var ctorByClass = {}; + ctorByClass[arrayClass] = Array; + ctorByClass[boolClass] = Boolean; + ctorByClass[dateClass] = Date; + ctorByClass[objectClass] = Object; + ctorByClass[numberClass] = Number; + ctorByClass[regexpClass] = RegExp; + ctorByClass[stringClass] = String; /** Used to determine if values are of the language type Object */ var objectTypes = { @@ -213,8 +228,7 @@ 'object': true, 'number': false, 'string': false, - 'undefined': false, - 'unknown': true + 'undefined': false }; /** Used to escape characters for inclusion in compiled string literals */ @@ -231,16 +245,39 @@ /*--------------------------------------------------------------------------*/ /** - * The `lodash` function. + * Creates a `lodash` object, that wraps the given `value`, to enable + * method chaining. + * + * The chainable wrapper functions are: + * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, `compose`, + * `concat`, `countBy`, `debounce`, `defaults`, `defer`, `delay`, `difference`, + * `filter`, `flatten`, `forEach`, `forIn`, `forOwn`, `functions`, `groupBy`, + * `initial`, `intersection`, `invert`, `invoke`, `keys`, `map`, `max`, `memoize`, + * `merge`, `min`, `object`, `omit`, `once`, `pairs`, `partial`, `pick`, `pluck`, + * `push`, `range`, `reject`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, + * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `union`, `uniq`, + * `unshift`, `values`, `where`, `without`, `wrap`, and `zip` + * + * The non-chainable wrapper functions are: + * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `has`, `identity`, + * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, + * `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, `isObject`, + * `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, `lastIndexOf`, + * `mixin`, `noConflict`, `pop`, `random`, `reduce`, `reduceRight`, `result`, + * `shift`, `size`, `some`, `sortedIndex`, `template`, `unescape`, and `uniqueId` + * + * The wrapper functions `first` and `last` return wrapped values when `n` is + * passed, otherwise they return unwrapped values. * * @name _ * @constructor + * @category Chaining * @param {Mixed} value The value to wrap in a `lodash` instance. * @returns {Object} Returns a `lodash` instance. */ function lodash(value) { - // exit early if already wrapped - if (value && value.__wrapped__) { + // exit early if already wrapped, even if wrapped by a different `lodash` constructor + if (value && typeof value == 'object' && value.__wrapped__) { return value; } // allow invoking `lodash` without the `new` operator @@ -286,7 +323,7 @@ * @memberOf _.templateSettings * @type RegExp */ - 'interpolate': /<%=([\s\S]+?)%>/g, + 'interpolate': reInterpolate, /** * Used to reference the data object in the template text. @@ -309,46 +346,44 @@ */ var iteratorTemplate = template( // conditional strict mode - '<% if (useStrict) { %>\'use strict\';\n<% } %>' + + "<% if (obj.useStrict) { %>'use strict';\n<% } %>" + // the `iteratee` may be reassigned by the `top` snippet - 'var index, value, iteratee = <%= firstArg %>, ' + + 'var index, iteratee = <%= firstArg %>, ' + // assign the `result` variable an initial value - 'result<% if (init) { %> = <%= init %><% } %>;\n' + + 'result = <%= firstArg %>;\n' + + // exit early if the first argument is falsey + 'if (!<%= firstArg %>) return result;\n' + // add code before the iteration branches '<%= top %>;\n' + - // the following branch is for iterating arrays and array-like objects - '<% if (arrayBranch) { %>' + - 'var length = iteratee.length; index = -1;' + - ' <% if (objectBranch) { %>\nif (length === +length) {<% } %>' + + // array-like iteration: + '<% if (arrayLoop) { %>' + + 'var length = iteratee.length; index = -1;\n' + + "if (typeof length == 'number') {" + // add support for accessing string characters by index if needed ' <% if (noCharByIndex) { %>\n' + - ' if (toString.call(iteratee) == stringClass) {\n' + - ' iteratee = iteratee.split(\'\')\n' + + ' if (isString(iteratee)) {\n' + + " iteratee = iteratee.split('')\n" + ' }' + ' <% } %>\n' + - ' <%= arrayBranch.beforeLoop %>;\n' + + // iterate over the array-like value ' while (++index < length) {\n' + - ' value = iteratee[index];\n' + - ' <%= arrayBranch.inLoop %>\n' + - ' }' + - ' <% if (objectBranch) { %>\n}<% } %>' + - '<% } %>' + - - // the following branch is for iterating an object's own/inherited properties - '<% if (objectBranch) { %>' + - ' <% if (arrayBranch) { %>\nelse {' + + ' <%= arrayLoop %>\n' + + ' }\n' + + '}\n' + + 'else {' + + // object iteration: // add support for iterating over `arguments` objects if needed - ' <% } else if (noArgsEnum) { %>\n' + + ' <% } else if (nonEnumArgs) { %>\n' + ' var length = iteratee.length; index = -1;\n' + ' if (length && isArguments(iteratee)) {\n' + ' while (++index < length) {\n' + - ' value = iteratee[index += \'\'];\n' + - ' <%= objectBranch.inLoop %>\n' + + " index += '';\n" + + ' <%= objectLoop %>\n' + ' }\n' + ' } else {' + ' <% } %>' + @@ -360,8 +395,8 @@ // the the `prototype` property of functions regardless of its // [[Enumerable]] value. ' <% if (!hasDontEnumBug) { %>\n' + - ' var skipProto = typeof iteratee == \'function\' && \n' + - ' propertyIsEnumerable.call(iteratee, \'prototype\');\n' + + " var skipProto = typeof iteratee == 'function' && \n" + + " propertyIsEnumerable.call(iteratee, 'prototype');\n" + ' <% } %>' + // iterate own properties using `Object.keys` if it's fast @@ -369,27 +404,23 @@ ' var ownIndex = -1,\n' + ' ownProps = objectTypes[typeof iteratee] ? nativeKeys(iteratee) : [],\n' + ' length = ownProps.length;\n\n' + - ' <%= objectBranch.beforeLoop %>;\n' + ' while (++ownIndex < length) {\n' + ' index = ownProps[ownIndex];\n' + - ' <% if (!hasDontEnumBug) { %>if (!(skipProto && index == \'prototype\')) {\n <% } %>' + - ' value = iteratee[index];\n' + - ' <%= objectBranch.inLoop %>\n' + + " <% if (!hasDontEnumBug) { %>if (!(skipProto && index == 'prototype')) {\n <% } %>" + + ' <%= objectLoop %>\n' + ' <% if (!hasDontEnumBug) { %>}\n<% } %>' + ' }' + // else using a for-in loop ' <% } else { %>\n' + - ' <%= objectBranch.beforeLoop %>;\n' + ' for (index in iteratee) {<%' + ' if (!hasDontEnumBug || useHas) { %>\n if (<%' + - ' if (!hasDontEnumBug) { %>!(skipProto && index == \'prototype\')<% }' + + " if (!hasDontEnumBug) { %>!(skipProto && index == 'prototype')<% }" + ' if (!hasDontEnumBug && useHas) { %> && <% }' + ' if (useHas) { %>hasOwnProperty.call(iteratee, index)<% }' + ' %>) {' + ' <% } %>\n' + - ' value = iteratee[index];\n' + - ' <%= objectBranch.inLoop %>;' + + ' <%= objectLoop %>;' + ' <% if (!hasDontEnumBug || useHas) { %>\n }<% } %>\n' + ' }' + ' <% } %>' + @@ -401,18 +432,16 @@ ' <% if (hasDontEnumBug) { %>\n\n' + ' var ctor = iteratee.constructor;\n' + ' <% for (var k = 0; k < 7; k++) { %>\n' + - ' index = \'<%= shadowed[k] %>\';\n' + + " index = '<%= shadowed[k] %>';\n" + ' if (<%' + - ' if (shadowed[k] == \'constructor\') {' + + " if (shadowed[k] == 'constructor') {" + ' %>!(ctor && ctor.prototype === iteratee) && <%' + ' } %>hasOwnProperty.call(iteratee, index)) {\n' + - ' value = iteratee[index];\n' + - ' <%= objectBranch.inLoop %>\n' + + ' <%= objectLoop %>\n' + ' }' + ' <% } %>' + ' <% } %>' + - ' <% if (arrayBranch || noArgsEnum) { %>\n}<% } %>' + - '<% } %>\n' + + ' <% if (arrayLoop || nonEnumArgs) { %>\n}<% } %>\n' + // add code to the bottom of the iteration function '<%= bottom %>;\n' + @@ -420,103 +449,41 @@ 'return result' ); - /** - * Reusable iterator options shared by - * `countBy`, `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, - * `map`, `reject`, `some`, and `sortBy`. - */ - var baseIteratorOptions = { - 'args': 'collection, callback, thisArg', - 'init': 'collection', - 'top': 'callback = createCallback(callback, thisArg)', - 'inLoop': 'if (callback(value, index, collection) === false) return result' - }; - - /** Reusable iterator options for `countBy`, `groupBy`, and `sortBy` */ - var countByIteratorOptions = { - 'init': '{}', - 'top': 'callback = createCallback(callback, thisArg)', - 'inLoop': - 'var prop = callback(value, index, collection);\n' + - '(hasOwnProperty.call(result, prop) ? result[prop]++ : result[prop] = 1)' - }; - - /** Reusable iterator options for `every` and `some` */ - var everyIteratorOptions = { - 'init': 'true', - 'inLoop': 'if (!callback(value, index, collection)) return !result' - }; - - /** Reusable iterator options for `defaults` and `extend` */ - var extendIteratorOptions = { - 'useHas': false, - 'useStrict': false, - 'args': 'object', - 'init': 'object', + /** Reusable iterator options for `assign` and `defaults` */ + var assignIteratorOptions = { + 'args': 'object, source, guard', 'top': - 'for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {\n' + - ' if (iteratee = arguments[argsIndex]) {', - 'inLoop': 'result[index] = value', + "for (var argsIndex = 1, argsLength = typeof guard == 'number' ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n" + + ' if ((iteratee = arguments[argsIndex])) {', + 'objectLoop': 'result[index] = iteratee[index]', 'bottom': ' }\n}' }; - /** Reusable iterator options for `filter`, `reject`, and `where` */ - var filterIteratorOptions = { - 'init': '[]', - 'inLoop': 'callback(value, index, collection) && result.push(value)' - }; - - /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ - var forEachIteratorOptions = { - 'top': 'callback = createCallback(callback, thisArg)' + /** + * Reusable iterator options shared by `each`, `forIn`, and `forOwn`. + */ + var eachIteratorOptions = { + 'args': 'collection, callback, thisArg', + 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg)", + 'arrayLoop': 'if (callback(iteratee[index], index, collection) === false) return result', + 'objectLoop': 'if (callback(iteratee[index], index, collection) === false) return result' }; /** Reusable iterator options for `forIn` and `forOwn` */ var forOwnIteratorOptions = { - 'inLoop': { - 'object': baseIteratorOptions.inLoop - } - }; - - /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */ - var mapIteratorOptions = { - 'init': '', - 'beforeLoop': { - 'array': 'result = Array(length)', - 'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') - }, - 'inLoop': { - 'array': 'result[index] = callback(value, index, collection)', - 'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '(callback(value, index, collection))' - } - }; - - /** Reusable iterator options for `omit` and `pick` */ - var omitIteratorOptions = { - 'useHas': false, - 'args': 'object, callback, thisArg', - 'init': '{}', - 'top': - 'var isFunc = typeof callback == \'function\';\n' + - 'if (isFunc) callback = createCallback(callback, thisArg);\n' + - 'else var props = concat.apply(ArrayProto, arguments)', - 'inLoop': - 'if (isFunc\n' + - ' ? !callback(value, index, object)\n' + - ' : indexOf(props, index) < 0\n' + - ') result[index] = value' + 'arrayLoop': null }; /*--------------------------------------------------------------------------*/ /** - * Creates a function optimized for searching large arrays for a given `value`, + * Creates a function optimized to search large arrays for a given `value`, * starting at `fromIndex`, using strict equality for comparisons, i.e. `===`. * * @private * @param {Array} array The array to search. * @param {Mixed} value The value to search for. - * @param {Number} [fromIndex=0] The index to start searching from. + * @param {Number} [fromIndex=0] The index to search from. * @param {Number} [largeSize=30] The length at which an array is considered large. * @returns {Boolean} Returns `true` if `value` is found, else `false`. */ @@ -524,18 +491,16 @@ fromIndex || (fromIndex = 0); var length = array.length, - isLarge = (length - fromIndex) >= (largeSize || largeArraySize), - cache = isLarge ? {} : array; + isLarge = (length - fromIndex) >= (largeSize || largeArraySize); if (isLarge) { - // init value cache - var key, + var cache = {}, index = fromIndex - 1; while (++index < length) { - // manually coerce `value` to string because `hasOwnProperty`, in some + // manually coerce `value` to a string because `hasOwnProperty`, in some // older versions of Firefox, coerces objects incorrectly - key = array[index] + ''; + var key = array[index] + ''; (hasOwnProperty.call(cache, key) ? cache[key] : (cache[key] = [])).push(array[index]); } } @@ -544,11 +509,23 @@ var key = value + ''; return hasOwnProperty.call(cache, key) && indexOf(cache[key], value) > -1; } - return indexOf(cache, value, fromIndex) > -1; + return indexOf(array, value, fromIndex) > -1; } } /** + * Used by `_.max` and `_.min` as the default `callback` when a given + * `collection` is a string value. + * + * @private + * @param {String} value The character to inspect. + * @returns {Number} Returns the code unit of given character. + */ + function charAtCallback(value) { + return value.charCodeAt(0); + } + + /** * Used by `sortBy` to compare transformed `collection` values, stable sorting * them in ascending order. * @@ -567,10 +544,10 @@ // ensure a stable sort in V8 and other engines // http://code.google.com/p/v8/issues/detail?id=90 if (a !== b) { - if (a > b || a === undefined) { + if (a > b || typeof a == 'undefined') { return 1; } - if (a < b || b === undefined) { + if (a < b || typeof b == 'undefined') { return -1; } } @@ -591,12 +568,15 @@ function createBound(func, thisArg, partialArgs) { var isFunc = isFunction(func), isPartial = !partialArgs, - methodName = func; + key = thisArg; // juggle arguments if (isPartial) { partialArgs = thisArg; } + if (!isFunc) { + thisArg = func; + } function bound() { // `Function#bind` spec @@ -605,24 +585,23 @@ thisBinding = isPartial ? this : thisArg; if (!isFunc) { - func = thisArg[methodName]; + func = thisArg[key]; } if (partialArgs.length) { args = args.length - ? partialArgs.concat(slice.call(args)) + ? partialArgs.concat(slice(args)) : partialArgs; } if (this instanceof bound) { - // get `func` instance if `bound` is invoked in a `new` expression + // ensure `new bound` is an instance of `bound` and `func` noop.prototype = func.prototype; thisBinding = new noop; + noop.prototype = null; // mimic the constructor's `return` behavior // http://es5.github.com/#x13.2.2 var result = func.apply(thisBinding, args); - return result && objectTypes[typeof result] - ? result - : thisBinding + return isObject(result) ? result : thisBinding; } return func.apply(thisBinding, args); } @@ -637,9 +616,11 @@ * @param {Function|String} [func=identity|property] The function called per * iteration or property name to query. * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @param {Object} [accumulating] Used to indicate that the callback should + * accept an `accumulator` argument. * @returns {Function} Returns a callback function. */ - function createCallback(func, thisArg) { + function createCallback(func, thisArg, accumulating) { if (!func) { return identity; } @@ -648,7 +629,12 @@ return object[func]; }; } - if (thisArg !== undefined) { + if (typeof thisArg != 'undefined') { + if (accumulating) { + return function(accumulator, value, index, object) { + return func.call(thisArg, accumulator, value, index, object); + }; + } return function(value, index, object) { return func.call(thisArg, value, index, object); }; @@ -657,108 +643,71 @@ } /** - * Creates compiled iteration functions. The iteration function will be created - * to iterate over only objects if the first argument of `options.args` is - * "object" or `options.inLoop.array` is falsey. + * Creates compiled iteration functions. * * @private - * @param {Object} [options1, options2, ...] The compile options objects. - * - * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks - * in the object loop. - * - * useStrict - A boolean to specify whether or not to include the ES5 - * "use strict" directive. - * - * args - A string of comma separated arguments the iteration function will - * accept. - * - * init - A string to specify the initial value of the `result` variable. - * + * @param {Object} [options1, options2, ...] The compile options object(s). + * useHas - A boolean to specify using `hasOwnProperty` checks in the object loop. + * args - A string of comma separated arguments the iteration function will accept. * top - A string of code to execute before the iteration branches. - * - * beforeLoop - A string or object containing an "array" or "object" property - * of code to execute before the array or object loops. - * - * inLoop - A string or object containing an "array" or "object" property - * of code to execute in the array or object loops. - * - * bottom - A string of code to execute after the iteration branches but - * before the `result` is returned. + * arrayLoop - A string of code to execute in the array loop. + * objectLoop - A string of code to execute in the object loop. + * bottom - A string of code to execute after the iteration branches. * * @returns {Function} Returns the compiled function. */ function createIterator() { - var object, - prop, - value, - index = -1, - length = arguments.length; - - // merge options into a template data object var data = { + 'arrayLoop': '', 'bottom': '', + 'hasDontEnumBug': hasDontEnumBug, + 'isKeysFast': isKeysFast, + 'objectLoop': '', + 'nonEnumArgs': nonEnumArgs, + 'noCharByIndex': noCharByIndex, + 'shadowed': shadowed, 'top': '', - 'arrayBranch': { 'beforeLoop': '' }, - 'objectBranch': { 'beforeLoop': '' } + 'useHas': true }; - while (++index < length) { - object = arguments[index]; - for (prop in object) { - value = (value = object[prop]) == null ? '' : value; - // keep this regexp explicit for the build pre-process - if (/beforeLoop|inLoop/.test(prop)) { - if (typeof value == 'string') { - value = { 'array': value, 'object': value }; - } - data.arrayBranch[prop] = value.array || ''; - data.objectBranch[prop] = value.object || ''; - } else { - data[prop] = value; - } + // merge options into a template data object + for (var object, index = 0; object = arguments[index]; index++) { + for (var key in object) { + data[key] = object[key]; } } - // set additional template `data` values - var args = data.args, - firstArg = /^[^,]+/.exec(args)[0], - init = data.init, - useStrict = data.useStrict; - - data.firstArg = firstArg; - data.hasDontEnumBug = hasDontEnumBug; - data.init = init == null ? firstArg : init; - data.isKeysFast = isKeysFast; - data.noArgsEnum = noArgsEnum; - data.shadowed = shadowed; - data.useHas = data.useHas !== false; - data.useStrict = useStrict == null ? isStrictFast : useStrict; - - if (data.noCharByIndex == null) { - data.noCharByIndex = noCharByIndex; - } - if (firstArg != 'collection' || !data.arrayBranch.inLoop) { - data.arrayBranch = null; - } + var args = data.args; + data.firstArg = /^[^,]+/.exec(args)[0]; + // create the function factory var factory = Function( - 'arrayLikeClasses, ArrayProto, bind, compareAscending, concat, createCallback, ' + - 'forIn, hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, ' + - 'isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, ' + - 'slice, stringClass, toString, undefined', - 'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' + - 'return callee' + 'createCallback, hasOwnProperty, isArguments, isString, objectTypes, ' + + 'nativeKeys, propertyIsEnumerable', + 'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' ); // return the compiled function return factory( - arrayLikeClasses, ArrayProto, bind, compareAscending, concat, createCallback, - forIn, hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, - isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, - slice, stringClass, toString + createCallback, hasOwnProperty, isArguments, isString, objectTypes, + nativeKeys, propertyIsEnumerable ); } /** + * A function compiled to iterate `arguments` objects, arrays, objects, and + * strings consistenly across environments, executing the `callback` for each + * element in the `collection`. The `callback` is bound to `thisArg` and invoked + * with three arguments; (value, index|key, collection). Callbacks may exit + * iteration early by explicitly returning `false`. + * + * @private + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array|Object|String} Returns `collection`. + */ + var each = createIterator(eachIteratorOptions); + + /** * Used by `template` to escape characters for inclusion in compiled * string literals. * @@ -782,6 +731,19 @@ } /** + * Checks if `value` is a DOM node in IE < 9. + * + * @private + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a DOM node, else `false`. + */ + function isNode(value) { + // IE < 9 presents DOM nodes as `Object` objects except they have `toString` + // methods that are `typeof` "string" and still can coerce nodes to strings + return typeof value.toString != 'function' && typeof (value + '') == 'string'; + } + + /** * A no-operation function. * * @private @@ -791,6 +753,34 @@ } /** + * Slices the `collection` from the `start` index up to, but not including, + * the `end` index. + * + * Note: This function is used, instead of `Array#slice`, to support node lists + * in IE < 9 and to ensure dense arrays are returned. + * + * @private + * @param {Array|Object|String} collection The collection to slice. + * @param {Number} start The start index. + * @param {Number} end The end index. + * @returns {Array} Returns the new array. + */ + function slice(array, start, end) { + start || (start = 0); + if (typeof end == 'undefined') { + end = array ? array.length : 0; + } + var index = -1, + length = end - start || 0, + result = Array(length < 0 ? 0 : length); + + while (++index < length) { + result[index] = array[start + index]; + } + return result; + } + + /** * Used by `unescape` to convert HTML entities to characters. * * @private @@ -804,23 +794,23 @@ /*--------------------------------------------------------------------------*/ /** - * Creates an object composed of the inverted keys and values of the given `object`. + * Assigns own enumerable properties of source object(s) to the `destination` + * object. Subsequent sources will overwrite propery assignments of previous + * sources. * * @static * @memberOf _ + * @alias extend * @category Objects - * @param {Object} object The object to invert. - * @returns {Object} Returns the created inverted object. + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @returns {Object} Returns the destination object. * @example * - * _.invert({ 'first': 'Moe', 'second': 'Larry', 'third': 'Curly' }); - * // => { 'Moe': 'first', 'Larry': 'second', 'Curly': 'third' } (order is not guaranteed) + * _.assign({ 'name': 'moe' }, { 'age': 40 }); + * // => { 'name': 'moe', 'age': 40 } */ - var invert = createIterator({ - 'args': 'object', - 'init': '{}', - 'inLoop': 'result[value] = index' - }); + var assign = createIterator(assignIteratorOptions); /** * Checks if `value` is an `arguments` object. @@ -844,88 +834,63 @@ // fallback for browsers that can't detect `arguments` objects by [[Class]] if (noArgsClass) { isArguments = function(value) { - return !!(value && hasOwnProperty.call(value, 'callee')); + return value ? hasOwnProperty.call(value, 'callee') : false; }; } /** - * Checks if `value` is an array. + * Iterates over `object`'s own and inherited enumerable properties, executing + * the `callback` for each property. The `callback` is bound to `thisArg` and + * invoked with three arguments; (value, key, object). Callbacks may exit iteration + * early by explicitly returning `false`. * * @static * @memberOf _ * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. + * @param {Object} object The object to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. * @example * - * (function() { return _.isArray(arguments); })(); - * // => false - * - * _.isArray([1, 2, 3]); - * // => true - */ - var isArray = nativeIsArray || function(value) { - return toString.call(value) == arrayClass; - }; - - /** - * Checks if `value` is a function. + * function Dog(name) { + * this.name = name; + * } * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. - * @example + * Dog.prototype.bark = function() { + * alert('Woof, woof!'); + * }; * - * _.isFunction(_); - * // => true + * _.forIn(new Dog('Dagny'), function(value, key) { + * alert(key); + * }); + * // => alerts 'name' and 'bark' (order is not guaranteed) */ - function isFunction(value) { - return typeof value == 'function'; - } - // fallback for older versions of Chrome and Safari - if (isFunction(/x/)) { - isFunction = function(value) { - return toString.call(value) == funcClass; - }; - } + var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, { + 'useHas': false + }); /** - * Checks if a given `value` is an object created by the `Object` constructor. + * Iterates over an object's own enumerable properties, executing the `callback` + * for each property. The `callback` is bound to `thisArg` and invoked with three + * arguments; (value, key, object). Callbacks may exit iteration early by explicitly + * returning `false`. * * @static * @memberOf _ * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. + * @param {Object} object The object to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. * @example * - * function Stooge(name, age) { - * this.name = name; - * this.age = age; - * } - * - * _.isPlainObject(new Stooge('moe', 40)); - * // false - * - * _.isPlainObject([1, 2, 3]); - * // false - * - * _.isPlainObject({ 'name': 'moe', 'age': 40 }); - * // => true + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * alert(key); + * }); + * // => alerts '0', '1', and 'length' (order is not guaranteed) */ - var isPlainObject = !nativeGetPrototypeOf ? isPlainFallback : function(value) { - if (!(value && typeof value == 'object')) { - return false; - } - var valueOf = value.valueOf, - objProto = typeof valueOf == 'function' && (objProto = nativeGetPrototypeOf(valueOf)) && nativeGetPrototypeOf(objProto); - - return objProto - ? value == objProto || (nativeGetPrototypeOf(value) == objProto && !isArguments(value)) - : isPlainFallback(value); - }; + var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions); /** * A fallback implementation of `isPlainObject` that checks if a given `value` @@ -937,18 +902,15 @@ * @param {Mixed} value The value to check. * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. */ - function isPlainFallback(value) { + function shimIsPlainObject(value) { // avoid non-objects and false positives for `arguments` objects var result = false; if (!(value && typeof value == 'object') || isArguments(value)) { return result; } - // IE < 9 presents DOM nodes as `Object` objects except they have `toString` - // methods that are `typeof` "string" and still can coerce nodes to strings. - // Also check that the constructor is `Object` (i.e. `Object instanceof Object`) + // check that the constructor is `Object` (i.e. `Object instanceof Object`) var ctor = value.constructor; - if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) && - (!isFunction(ctor) || ctor instanceof ctor)) { + if ((!isFunction(ctor) && (!noNodeClass || !isNode(value))) || ctor instanceof ctor) { // IE < 9 iterates inherited properties before own properties. If the first // iterated property is an object's own property then there are no inherited // enumerable properties. @@ -971,18 +933,20 @@ } /** - * A shim implementation of `Object.keys` that produces an array of the given - * object's own enumerable property names. + * A fallback implementation of `Object.keys` that produces an array of the + * given object's own enumerable property names. * * @private * @param {Object} object The object to inspect. * @returns {Array} Returns a new array of property names. */ - var shimKeys = createIterator({ - 'args': 'object', - 'init': '[]', - 'inLoop': 'result.push(index)' - }); + function shimKeys(object) { + var result = []; + forOwn(object, function(value, key) { + result.push(key); + }); + return result; + } /** * Used to convert characters to HTML entities: @@ -1006,10 +970,8 @@ /*--------------------------------------------------------------------------*/ /** - * Creates a clone of `value`. If `deep` is `true`, all nested objects will - * also be cloned otherwise they will be assigned by reference. Functions, DOM - * nodes, `arguments` objects, and objects created by constructors other than - * `Object` are **not** cloned. + * Creates a clone of `value`. If `deep` is `true`, nested objects will also + * be cloned, otherwise they will be assigned by reference. * * @static * @memberOf _ @@ -1030,15 +992,12 @@ * { 'name': 'curly', 'age': 60 } * ]; * - * _.clone({ 'name': 'moe' }); - * // => { 'name': 'moe' } - * * var shallow = _.clone(stooges); * shallow[0] === stooges[0]; * // => true * * var deep = _.clone(stooges, true); - * shallow[0] === stooges[0]; + * deep[0] === stooges[0]; * // => false */ function clone(value, deep, guard, stackA, stackB) { @@ -1049,29 +1008,23 @@ deep = false; } // inspect [[Class]] - var isObj = objectTypes[typeof value]; + var isObj = isObject(value); if (isObj) { - // don't clone `arguments` objects, functions, or non-object Objects var className = toString.call(value); - if (!cloneableClasses[className] || (noArgsClass && isArguments(value))) { + if (!cloneableClasses[className] || (noNodeClass && isNode(value))) { return value; } - var isArr = className == arrayClass; - isObj = isArr || (className == objectClass ? isPlainObject(value) : isObj); + var isArr = isArray(value); } // shallow clone if (!isObj || !deep) { - // don't clone functions return isObj - ? (isArr ? slice.call(value) : extend({}, value)) + ? (isArr ? slice(value) : assign({}, value)) : value; } - - var ctor = value.constructor; + var ctor = ctorByClass[className]; switch (className) { case boolClass: - return new ctor(value == true); - case dateClass: return new ctor(+value); @@ -1082,7 +1035,6 @@ case regexpClass: return ctor(value.source, reFlags.exec(value)); } - // check for circular references and return corresponding clone stackA || (stackA = []); stackB || (stackB = []); @@ -1093,9 +1045,8 @@ return stackB[length]; } } - // init cloned object - var result = isArr ? ctor(length = value.length) : {}; + var result = isArr ? ctor(value.length) : {}; // add the source value to the stack of traversed objects // and associate it with its clone @@ -1103,154 +1054,159 @@ stackB.push(result); // recursively populate clone (susceptible to call stack limits) + (isArr ? forEach : forOwn)(value, function(objValue, key) { + result[key] = clone(objValue, deep, null, stackA, stackB); + }); + + // add array properties assigned by `RegExp#exec` if (isArr) { - var index = -1; - while (++index < length) { - result[index] = clone(value[index], deep, null, stackA, stackB); + if (hasOwnProperty.call(value, 'index')) { + result.index = value.index; + } + if (hasOwnProperty.call(value, 'input')) { + result.input = value.input; } - } else { - forOwn(value, function(objValue, key) { - result[key] = clone(objValue, deep, null, stackA, stackB); - }); } return result; } /** - * Assigns enumerable properties of the default object(s) to the `destination` - * object for all `destination` properties that resolve to `null`/`undefined`. - * Once a property is set, additional defaults of the same property will be - * ignored. + * Creates a deep clone of `value`. Functions and DOM nodes are **not** cloned. + * The enumerable properties of `arguments` objects and objects created by + * constructors other than `Object` are cloned to plain `Object` objects. + * + * Note: This function is loosely based on the structured clone algorithm. + * See http://www.w3.org/TR/html5/common-dom-interfaces.html#internal-structured-cloning-algorithm. * * @static * @memberOf _ * @category Objects - * @param {Object} object The destination object. - * @param {Object} [default1, default2, ...] The default objects. - * @returns {Object} Returns the destination object. + * @param {Mixed} value The value to deep clone. + * @returns {Mixed} Returns the deep cloned `value`. * @example * - * var iceCream = { 'flavor': 'chocolate' }; - * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); - * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * var deep = _.cloneDeep(stooges); + * deep[0] === stooges[0]; + * // => false */ - var defaults = createIterator(extendIteratorOptions, { - 'inLoop': 'if (result[index] == null) ' + extendIteratorOptions.inLoop - }); + function cloneDeep(value) { + return clone(value, true); + } /** - * Assigns enumerable properties of the source object(s) to the `destination` - * object. Subsequent sources will overwrite propery assignments of previous - * sources. + * Assigns own enumerable properties of source object(s) to the `destination` + * object for all `destination` properties that resolve to `null`/`undefined`. + * Once a property is set, additional defaults of the same property will be + * ignored. * * @static * @memberOf _ * @category Objects * @param {Object} object The destination object. - * @param {Object} [source1, source2, ...] The source objects. + * @param {Object} [default1, default2, ...] The default objects. * @returns {Object} Returns the destination object. * @example * - * _.extend({ 'name': 'moe' }, { 'age': 40 }); - * // => { 'name': 'moe', 'age': 40 } + * var iceCream = { 'flavor': 'chocolate' }; + * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); + * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } */ - var extend = createIterator(extendIteratorOptions); + var defaults = createIterator(assignIteratorOptions, { + 'objectLoop': 'if (result[index] == null) ' + assignIteratorOptions.objectLoop + }); /** - * Iterates over `object`'s own and inherited enumerable properties, executing - * the `callback` for each property. The `callback` is bound to `thisArg` and - * invoked with three arguments; (value, key, object). Callbacks may exit iteration - * early by explicitly returning `false`. + * Creates a sorted array of all enumerable properties, own and inherited, + * of `object` that have function values. * * @static * @memberOf _ + * @alias methods * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns `object`. + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names that have function values. * @example * - * function Dog(name) { - * this.name = name; - * } - * - * Dog.prototype.bark = function() { - * alert('Woof, woof!'); - * }; - * - * _.forIn(new Dog('Dagny'), function(value, key) { - * alert(key); - * }); - * // => alerts 'name' and 'bark' (order is not guaranteed) + * _.functions(_); + * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] */ - var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { - 'useHas': false - }); + function functions(object) { + var result = []; + forIn(object, function(value, key) { + if (isFunction(value)) { + result.push(key); + } + }); + return result.sort(); + } /** - * Iterates over `object`'s own enumerable properties, executing the `callback` - * for each property. The `callback` is bound to `thisArg` and invoked with three - * arguments; (value, key, object). Callbacks may exit iteration early by explicitly - * returning `false`. + * Checks if the specified object `property` exists and is a direct property, + * instead of an inherited property. * * @static * @memberOf _ * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns `object`. + * @param {Object} object The object to check. + * @param {String} property The property to check for. + * @returns {Boolean} Returns `true` if key is a direct property, else `false`. * @example * - * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { - * alert(key); - * }); - * // => alerts '0', '1', and 'length' (order is not guaranteed) + * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); + * // => true */ - var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); + function has(object, property) { + return object ? hasOwnProperty.call(object, property) : false; + } /** - * Creates a sorted array of all enumerable properties, own and inherited, - * of `object` that have function values. + * Creates an object composed of the inverted keys and values of the given `object`. * * @static * @memberOf _ - * @alias methods * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names that have function values. + * @param {Object} object The object to invert. + * @returns {Object} Returns the created inverted object. * @example * - * _.functions(_); - * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + * _.invert({ 'first': 'Moe', 'second': 'Larry', 'third': 'Curly' }); + * // => { 'Moe': 'first', 'Larry': 'second', 'Curly': 'third' } (order is not guaranteed) */ - var functions = createIterator({ - 'useHas': false, - 'args': 'object', - 'init': '[]', - 'inLoop': 'if (isFunction(value)) result.push(index)', - 'bottom': 'result.sort()' - }); + function invert(object) { + var result = {}; + forOwn(object, function(value, key) { + result[value] = key; + }); + return result; + } /** - * Checks if the specified object `property` exists and is a direct property, - * instead of an inherited property. + * Checks if `value` is an array. * * @static * @memberOf _ * @category Objects - * @param {Object} object The object to check. - * @param {String} property The property to check for. - * @returns {Boolean} Returns `true` if key is a direct property, else `false`. + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. * @example * - * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); + * (function() { return _.isArray(arguments); })(); + * // => false + * + * _.isArray([1, 2, 3]); * // => true */ - function has(object, property) { - return hasOwnProperty.call(object, property); - } + var isArray = nativeIsArray || function(value) { + // `instanceof` may cause a memory leak in IE 7 if `value` is a host object + // http://ajaxian.com/archives/working-aroung-the-instanceof-memory-leak + return (argsAreObjects && value instanceof Array) || toString.call(value) == arrayClass; + }; /** * Checks if `value` is a boolean (`true` or `false`) value. @@ -1283,7 +1239,7 @@ * // => true */ function isDate(value) { - return toString.call(value) == dateClass; + return value instanceof Date || toString.call(value) == dateClass; } /** @@ -1324,22 +1280,24 @@ * _.isEmpty(''); * // => true */ - var isEmpty = createIterator({ - 'args': 'value', - 'init': 'true', - 'top': - 'if (!value) return result;\n' + - 'var className = toString.call(value),\n' + - ' length = value.length;\n' + - 'if (arrayLikeClasses[className]' + - (noArgsClass ? ' || isArguments(value)' : '') + ' ||\n' + - ' (className == objectClass && length === +length &&\n' + - ' isFunction(value.splice))' + - ') return !length', - 'inLoop': { - 'object': 'return false' + function isEmpty(value) { + var result = true; + if (!value) { + return result; } - }); + var className = toString.call(value), + length = value.length; + + if ((className == arrayClass || className == stringClass || + className == argsClass || (noArgsClass && isArguments(value))) || + (className == objectClass && typeof length == 'number' && isFunction(value.splice))) { + return !length; + } + forOwn(value, function() { + return (result = false); + }); + return result; + } /** * Performs a deep comparison between two values to determine if they are @@ -1365,23 +1323,26 @@ * // => true */ function isEqual(a, b, stackA, stackB) { - // a strict comparison is necessary because `null == undefined` - if (a == null || b == null) { - return a === b; - } // exit early for identical values if (a === b) { // treat `+0` vs. `-0` as not equal return a !== 0 || (1 / a == 1 / b); } - // unwrap any `lodash` wrapped values - if (objectTypes[typeof a] || objectTypes[typeof b]) { - a = a.__wrapped__ || a; - b = b.__wrapped__ || b; + // a strict comparison is necessary because `null == undefined` + if (a == null || b == null) { + return a === b; } // compare [[Class]] names - var className = toString.call(a); - if (className != toString.call(b)) { + var className = toString.call(a), + otherName = toString.call(b); + + if (className == argsClass) { + className = objectClass; + } + if (otherName == argsClass) { + otherName = objectClass; + } + if (className != otherName) { return false; } switch (className) { @@ -1404,18 +1365,28 @@ // treat string primitives and their corresponding object instances as equal return a == b + ''; } - // exit early, in older browsers, if `a` is array-like but not `b` - var isArr = arrayLikeClasses[className]; - if (noArgsClass && !isArr && (isArr = isArguments(a)) && !isArguments(b)) { - return false; - } - // exit for functions and DOM nodes - if (!isArr && (className != objectClass || (noNodeClass && ( - (typeof a.toString != 'function' && typeof (a + '') == 'string') || - (typeof b.toString != 'function' && typeof (b + '') == 'string'))))) { - return false; + var isArr = className == arrayClass; + if (!isArr) { + // unwrap any `lodash` wrapped values + if (a.__wrapped__ || b.__wrapped__) { + return isEqual(a.__wrapped__ || a, b.__wrapped__ || b); + } + // exit for functions and DOM nodes + if (className != objectClass || (noNodeClass && (isNode(a) || isNode(b)))) { + return false; + } + // in older versions of Opera, `arguments` objects have `Array` constructors + var ctorA = !argsAreObjects && isArguments(a) ? Object : a.constructor, + ctorB = !argsAreObjects && isArguments(b) ? Object : b.constructor; + + // non `Object` object instances with different constructors are not equal + if (ctorA != ctorB && !( + isFunction(ctorA) && ctorA instanceof ctorA && + isFunction(ctorB) && ctorB instanceof ctorB + )) { + return false; + } } - // assume cyclic structures are equal // the algorithm for detecting cyclic structures is adapted from ES 5.1 // section 15.12.3, abstract operation `JO` (http://es5.github.com/#x15.12.3) @@ -1428,7 +1399,6 @@ return stackB[length] == b; } } - var index = -1, result = true, size = 0; @@ -1453,58 +1423,35 @@ } return result; } - - var ctorA = a.constructor, - ctorB = b.constructor; - - // non `Object` object instances with different constructors are not equal - if (ctorA != ctorB && !( - isFunction(ctorA) && ctorA instanceof ctorA && - isFunction(ctorB) && ctorB instanceof ctorB - )) { - return false; - } - // deep compare objects - for (var prop in a) { - if (hasOwnProperty.call(a, prop)) { + // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys` + // which, in this case, is more costly + forIn(a, function(value, key, a) { + if (hasOwnProperty.call(a, key)) { // count the number of properties. size++; // deep compare each property value. - if (!(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stackA, stackB))) { - return false; - } - } - } - // ensure both objects have the same number of properties - for (prop in b) { - // The JS engine in Adobe products, like InDesign, has a bug that causes - // `!size--` to throw an error so it must be wrapped in parentheses. - // https://github.com/documentcloud/underscore/issues/355 - if (hasOwnProperty.call(b, prop) && !(size--)) { - // `size` will be `-1` if `b` has more properties than `a` - return false; + return (result = hasOwnProperty.call(b, key) && isEqual(value, b[key], stackA, stackB)); } - } - // handle JScript [[DontEnum]] bug - if (hasDontEnumBug) { - while (++index < 7) { - prop = shadowed[index]; - if (hasOwnProperty.call(a, prop) && - !(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stackA, stackB))) { - return false; + }); + + if (result) { + // ensure both objects have the same number of properties + forIn(b, function(value, key, b) { + if (hasOwnProperty.call(b, key)) { + // `size` will be `-1` if `b` has more properties than `a` + return (result = --size > -1); } - } + }); } - return true; + return result; } /** - * Checks if `value` is a finite number. + * Checks if `value` is, or can be coerced to, a finite number. * * Note: This is not the same as native `isFinite`, which will return true for - * booleans and other values. See http://es5.github.com/#x15.1.2.5. + * booleans and empty strings. See http://es5.github.com/#x15.1.2.5. * - * @deprecated * @static * @memberOf _ * @category Objects @@ -1516,13 +1463,42 @@ * // => true * * _.isFinite('10'); + * // => true + * + * _.isFinite(true); + * // => false + * + * _.isFinite(''); * // => false * * _.isFinite(Infinity); * // => false */ function isFinite(value) { - return nativeIsFinite(value) && toString.call(value) == numberClass; + return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value)); + } + + /** + * Checks if `value` is a function. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + */ + function isFunction(value) { + return typeof value == 'function'; + } + // fallback for older versions of Chrome and Safari + if (isFunction(/x/)) { + isFunction = function(value) { + return value instanceof Function || toString.call(value) == funcClass; + }; } /** @@ -1556,10 +1532,9 @@ /** * Checks if `value` is `NaN`. * - * Note: This is not the same as native `isNaN`, which will return true for + * Note: This is not the same as native `isNaN`, which will return `true` for * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. * - * @deprecated * @static * @memberOf _ * @category Objects @@ -1582,13 +1557,12 @@ function isNaN(value) { // `NaN` as a primitive is the only value that is not equal to itself // (perform the [[Class]] check first to avoid errors with some host objects in IE) - return toString.call(value) == numberClass && value != +value + return isNumber(value) && value != +value } /** * Checks if `value` is `null`. * - * @deprecated * @static * @memberOf _ * @category Objects @@ -1620,10 +1594,46 @@ * // => true */ function isNumber(value) { - return toString.call(value) == numberClass; + return typeof value == 'number' || toString.call(value) == numberClass; } /** + * Checks if a given `value` is an object created by the `Object` constructor. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Stooge(name, age) { + * this.name = name; + * this.age = age; + * } + * + * _.isPlainObject(new Stooge('moe', 40)); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'name': 'moe', 'age': 40 }); + * // => true + */ + var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { + if (!(value && typeof value == 'object')) { + return false; + } + var valueOf = value.valueOf, + objProto = typeof valueOf == 'function' && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto); + + return objProto + ? value == objProto || (getPrototypeOf(value) == objProto && !isArguments(value)) + : shimIsPlainObject(value); + }; + + /** * Checks if `value` is a regular expression. * * @static @@ -1637,7 +1647,7 @@ * // => true */ function isRegExp(value) { - return toString.call(value) == regexpClass; + return value instanceof RegExp || toString.call(value) == regexpClass; } /** @@ -1654,13 +1664,12 @@ * // => true */ function isString(value) { - return toString.call(value) == stringClass; + return typeof value == 'string' || toString.call(value) == stringClass; } /** * Checks if `value` is `undefined`. * - * @deprecated * @static * @memberOf _ * @category Objects @@ -1672,7 +1681,7 @@ * // => true */ function isUndefined(value) { - return value === undefined; + return typeof value == 'undefined'; } /** @@ -1692,7 +1701,7 @@ // avoid iterating over the `prototype` property return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype') ? shimKeys(object) - : nativeKeys(object); + : (isObject(object) ? nativeKeys(object) : []); }; /** @@ -1708,7 +1717,7 @@ * @param- {Object} [indicator] Internally used to indicate that the `stack` * argument is an array of traversed objects instead of another source object. * @param- {Array} [stackA=[]] Internally used to track traversed source objects. - * @param- {Array} [stackB=[]] Internally used to associate clones with their + * @param- {Array} [stackB=[]] Internally used to associate values with their * source counterparts. * @returns {Object} Returns the destination object. * @example @@ -1726,37 +1735,54 @@ * _.merge(stooges, ages); * // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }] */ - var merge = createIterator(extendIteratorOptions, { - 'args': 'object, source, indicator', - 'top': - 'var isArr, args = arguments, argsIndex = 0;\n' + - 'if (indicator == compareAscending) {\n' + - ' var argsLength = 2, stackA = args[3], stackB = args[4]\n' + - '} else {\n' + - ' var argsLength = args.length, stackA = [], stackB = []\n' + - '}\n' + - 'while (++argsIndex < argsLength) {\n' + - ' if (iteratee = args[argsIndex]) {', - 'inLoop': - 'if ((source = value) && ((isArr = isArray(source)) || isPlainObject(source))) {\n' + - ' var found = false, stackLength = stackA.length;\n' + - ' while (stackLength--) {\n' + - ' if (found = stackA[stackLength] == source) break\n' + - ' }\n' + - ' if (found) {\n' + - ' result[index] = stackB[stackLength]\n' + - ' } else {\n' + - ' stackA.push(source);\n' + - ' stackB.push(value = (value = result[index]) && isArr\n' + - ' ? (isArray(value) ? value : [])\n' + - ' : (isPlainObject(value) ? value : {})\n' + - ' );\n' + - ' result[index] = callee(value, source, compareAscending, stackA, stackB)\n' + - ' }\n' + - '} else if (source != null) {\n' + - ' result[index] = source\n' + - '}' - }); + function merge(object, source, indicator) { + var args = arguments, + index = 0, + length = 2, + stackA = args[3], + stackB = args[4]; + + if (indicator !== indicatorObject) { + stackA = []; + stackB = []; + + // work with `_.reduce` by only using its callback `accumulator` and `value` arguments + if (typeof indicator != 'number') { + length = args.length; + } + } + while (++index < length) { + forOwn(args[index], function(source, key) { + var found, isArr, value; + if (source && ((isArr = isArray(source)) || isPlainObject(source))) { + // avoid merging previously merged cyclic sources + var stackLength = stackA.length; + while (stackLength--) { + found = stackA[stackLength] == source; + if (found) { + break; + } + } + if (found) { + object[key] = stackB[stackLength]; + } + else { + // add `source` and associated `value` to the stack of traversed objects + stackA.push(source); + stackB.push(value = (value = object[key], isArr) + ? (isArray(value) ? value : []) + : (isPlainObject(value) ? value : {}) + ); + // recursively merge objects and arrays (susceptible to call stack limits) + object[key] = merge(value, source, indicatorObject, stackA, stackB); + } + } else if (source != null) { + object[key] = source; + } + }); + } + return object; + } /** * Creates a shallow clone of `object` excluding the specified properties. @@ -1783,7 +1809,25 @@ * }); * // => { 'name': 'moe' } */ - var omit = createIterator(omitIteratorOptions); + function omit(object, callback, thisArg) { + var isFunc = typeof callback == 'function', + result = {}; + + if (isFunc) { + callback = createCallback(callback, thisArg); + } else { + var props = concat.apply(arrayRef, arguments); + } + forIn(object, function(value, key, object) { + if (isFunc + ? !callback(value, key, object) + : indexOf(props, key, 1) < 0 + ) { + result[key] = value; + } + }); + return result; + } /** * Creates a two dimensional array of the given object's key-value pairs, @@ -1799,11 +1843,13 @@ * _.pairs({ 'moe': 30, 'larry': 40, 'curly': 50 }); * // => [['moe', 30], ['larry', 40], ['curly', 50]] (order is not guaranteed) */ - var pairs = createIterator({ - 'args': 'object', - 'init':'[]', - 'inLoop': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '([index, value])' - }); + function pairs(object) { + var result = []; + forOwn(object, function(value, key) { + result.push([key, value]); + }); + return result; + } /** * Creates a shallow clone of `object` composed of the specified properties. @@ -1830,22 +1876,29 @@ * }); * // => { 'name': 'moe' } */ - var pick = createIterator(omitIteratorOptions, { - 'top': - 'if (typeof callback != \'function\') {\n' + - ' var prop,\n' + - ' props = concat.apply(ArrayProto, arguments),\n' + - ' length = props.length;\n' + - ' for (index = 1; index < length; index++) {\n' + - ' prop = props[index];\n' + - ' if (prop in object) result[prop] = object[prop]\n' + - ' }\n' + - '} else {\n' + - ' callback = createCallback(callback, thisArg)', - 'inLoop': - 'if (callback(value, index, object)) result[index] = value', - 'bottom': '}' - }); + function pick(object, callback, thisArg) { + var result = {}; + if (typeof callback != 'function') { + var index = 0, + props = concat.apply(arrayRef, arguments), + length = props.length; + + while (++index < length) { + var key = props[index]; + if (key in object) { + result[key] = object[key]; + } + } + } else { + callback = createCallback(callback, thisArg); + forIn(object, function(value, key, object) { + if (callback(value, key, object)) { + result[key] = value; + } + }); + } + return result; + } /** * Creates an array composed of the own enumerable property values of `object`. @@ -1860,17 +1913,20 @@ * _.values({ 'one': 1, 'two': 2, 'three': 3 }); * // => [1, 2, 3] */ - var values = createIterator({ - 'args': 'object', - 'init': '[]', - 'inLoop': 'result.push(value)' - }); + function values(object) { + var result = []; + forOwn(object, function(value) { + result.push(value); + }); + return result; + } /*--------------------------------------------------------------------------*/ /** * Checks if a given `target` element is present in a `collection` using strict - * equality for comparisons, i.e. `===`. + * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used + * as the offset from the end of the collection. * * @static * @memberOf _ @@ -1878,27 +1934,42 @@ * @category Collections * @param {Array|Object|String} collection The collection to iterate over. * @param {Mixed} target The value to check for. + * @param {Number} [fromIndex=0] The index to search from. * @returns {Boolean} Returns `true` if the `target` element is found, else `false`. * @example * - * _.contains([1, 2, 3], 3); + * _.contains([1, 2, 3], 1); * // => true * + * _.contains([1, 2, 3], 1, 2); + * // => false + * * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); * // => true * * _.contains('curly', 'ur'); * // => true */ - var contains = createIterator({ - 'args': 'collection, target', - 'init': 'false', - 'noCharByIndex': false, - 'beforeLoop': { - 'array': 'if (toString.call(collection) == stringClass) return collection.indexOf(target) > -1' - }, - 'inLoop': 'if (value === target) return true' - }); + function contains(collection, target, fromIndex) { + var index = -1, + length = collection ? collection.length : 0, + result = false; + + fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0; + if (typeof length == 'number') { + result = (isString(collection) + ? collection.indexOf(target, fromIndex) + : indexOf(collection, target, fromIndex) + ) > -1; + } else { + each(collection, function(value) { + if (++index >= fromIndex) { + return !(result = value === target); + } + }); + } + return result; + } /** * Creates an object composed of keys returned from running each element of @@ -1926,7 +1997,16 @@ * _.countBy(['one', 'two', 'three'], 'length'); * // => { '3': 2, '5': 1 } */ - var countBy = createIterator(baseIteratorOptions, countByIteratorOptions); + function countBy(collection, callback, thisArg) { + var result = {}; + callback = createCallback(callback, thisArg); + + forEach(collection, function(value, key, collection) { + key = callback(value, key, collection); + (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1); + }); + return result; + } /** * Checks if the `callback` returns a truthy value for **all** elements of a @@ -1947,7 +2027,26 @@ * _.every([true, 1, null, 'yes'], Boolean); * // => false */ - var every = createIterator(baseIteratorOptions, everyIteratorOptions); + function every(collection, callback, thisArg) { + var result = true; + callback = createCallback(callback, thisArg); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + if (!(result = !!callback(collection[index], index, collection))) { + break; + } + } + } else { + each(collection, function(value, index, collection) { + return (result = !!callback(value, index, collection)); + }); + } + return result; + } /** * Examines each element in a `collection`, returning an array of all elements @@ -1967,7 +2066,29 @@ * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); * // => [2, 4, 6] */ - var filter = createIterator(baseIteratorOptions, filterIteratorOptions); + function filter(collection, callback, thisArg) { + var result = []; + callback = createCallback(callback, thisArg); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + var value = collection[index]; + if (callback(value, index, collection)) { + result.push(value); + } + } + } else { + each(collection, function(value, index, collection) { + if (callback(value, index, collection)) { + result.push(value); + } + }); + } + return result; + } /** * Examines each element in a `collection`, returning the first one the `callback` @@ -1980,7 +2101,7 @@ * @alias detect * @category Collections * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. + * @param {Function} [callback=identity] The function called per iteration. * @param {Mixed} [thisArg] The `this` binding of `callback`. * @returns {Mixed} Returns the element that passed the callback check, * else `undefined`. @@ -1989,10 +2110,18 @@ * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); * // => 2 */ - var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { - 'init': '', - 'inLoop': 'if (callback(value, index, collection)) return value' - }); + function find(collection, callback, thisArg) { + var result; + callback = createCallback(callback, thisArg); + + forEach(collection, function(value, index, collection) { + if (callback(value, index, collection)) { + result = value; + return false; + } + }); + return result; + } /** * Iterates over a `collection`, executing the `callback` for each element in @@ -2005,7 +2134,7 @@ * @alias each * @category Collections * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. + * @param {Function} [callback=identity] The function called per iteration. * @param {Mixed} [thisArg] The `this` binding of `callback`. * @returns {Array|Object|String} Returns `collection`. * @example @@ -2014,16 +2143,30 @@ * // => alerts each number and returns '1,2,3' * * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); - * // => alerts each number (order is not guaranteed) + * // => alerts each number value (order is not guaranteed) */ - var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); + function forEach(collection, callback, thisArg) { + if (callback && typeof thisArg == 'undefined' && isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + if (callback(collection[index], index, collection) === false) { + break; + } + } + } else { + each(collection, callback, thisArg); + } + return collection; + } /** * Creates an object composed of keys returned from running each element of * `collection` through a `callback`. The corresponding value of each key is an * array of elements passed to `callback` that returned the key. The `callback` * is bound to `thisArg` and invoked with three arguments; (value, index|key, collection). - * The `callback` argument may also be the name of a property to count by (e.g. 'length'). + * The `callback` argument may also be the name of a property to group by (e.g. 'length'). * * @static * @memberOf _ @@ -2044,11 +2187,16 @@ * _.groupBy(['one', 'two', 'three'], 'length'); * // => { '3': ['one', 'two'], '5': ['three'] } */ - var groupBy = createIterator(baseIteratorOptions, countByIteratorOptions, { - 'inLoop': - 'var prop = callback(value, index, collection);\n' + - '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(value)' - }); + function groupBy(collection, callback, thisArg) { + var result = {}; + callback = createCallback(callback, thisArg); + + forEach(collection, function(value, key, collection) { + key = callback(value, key, collection); + (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value); + }); + return result; + } /** * Invokes the method named by `methodName` on each element in the `collection`, @@ -2072,19 +2220,16 @@ * _.invoke([123, 456], String.prototype.split, ''); * // => [['1', '2', '3'], ['4', '5', '6']] */ - var invoke = createIterator(mapIteratorOptions, { - 'args': 'collection, methodName', - 'top': - 'var args = slice.call(arguments, 2),\n' + - ' isFunc = typeof methodName == \'function\'', - 'inLoop': { - 'array': - 'result[index] = (isFunc ? methodName : value[methodName]).apply(value, args)', - 'object': - 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + - '((isFunc ? methodName : value[methodName]).apply(value, args))' - } - }); + function invoke(collection, methodName) { + var args = slice(arguments, 2), + isFunc = typeof methodName == 'function', + result = []; + + forEach(collection, function(value) { + result.push((isFunc ? methodName : value[methodName]).apply(value, args)); + }); + return result; + } /** * Creates an array of values by running each element in the `collection` @@ -2107,7 +2252,121 @@ * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); * // => [3, 6, 9] (order is not guaranteed) */ - var map = createIterator(baseIteratorOptions, mapIteratorOptions); + function map(collection, callback, thisArg) { + var index = -1, + length = collection ? collection.length : 0, + result = Array(typeof length == 'number' ? length : 0); + + callback = createCallback(callback, thisArg); + if (isArray(collection)) { + while (++index < length) { + result[index] = callback(collection[index], index, collection); + } + } else { + each(collection, function(value, key, collection) { + result[++index] = callback(value, key, collection); + }); + } + return result; + } + + /** + * Retrieves the maximum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to + * `thisArg` and invoked with three arguments; (value, index, collection). + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Mixed} Returns the maximum value. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.max(stooges, function(stooge) { return stooge.age; }); + * // => { 'name': 'curly', 'age': 60 }; + */ + function max(collection, callback, thisArg) { + var computed = -Infinity, + index = -1, + length = collection ? collection.length : 0, + result = computed; + + if (callback || !isArray(collection)) { + callback = !callback && isString(collection) + ? charAtCallback + : createCallback(callback, thisArg); + + each(collection, function(value, index, collection) { + var current = callback(value, index, collection); + if (current > computed) { + computed = current; + result = value; + } + }); + } else { + while (++index < length) { + if (collection[index] > result) { + result = collection[index]; + } + } + } + return result; + } + + /** + * Retrieves the minimum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to `thisArg` + * and invoked with three arguments; (value, index, collection). + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Mixed} Returns the minimum value. + * @example + * + * _.min([10, 5, 100, 2, 1000]); + * // => 2 + */ + function min(collection, callback, thisArg) { + var computed = Infinity, + index = -1, + length = collection ? collection.length : 0, + result = computed; + + if (callback || !isArray(collection)) { + callback = !callback && isString(collection) + ? charAtCallback + : createCallback(callback, thisArg); + + each(collection, function(value, index, collection) { + var current = callback(value, index, collection); + if (current < computed) { + computed = current; + result = value; + } + }); + } else { + while (++index < length) { + if (collection[index] < result) { + result = collection[index]; + } + } + } + return result; + } /** * Retrieves the value of a specified property from all elements in @@ -2130,13 +2389,9 @@ * _.pluck(stooges, 'name'); * // => ['moe', 'larry', 'curly'] */ - var pluck = createIterator(mapIteratorOptions, { - 'args': 'collection, property', - 'inLoop': { - 'array': 'result[index] = value[property]', - 'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '(value[property])' - } - }); + function pluck(collection, property) { + return map(collection, property + ''); + } /** * Boils down a `collection` to a single value. The initial state of the @@ -2149,7 +2404,7 @@ * @alias foldl, inject * @category Collections * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. + * @param {Function} [callback=identity] The function called per iteration. * @param {Mixed} [accumulator] Initial value of the accumulator. * @param {Mixed} [thisArg] The `this` binding of `callback`. * @returns {Mixed} Returns the accumulated value. @@ -2158,24 +2413,29 @@ * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); * // => 6 */ - var reduce = createIterator({ - 'args': 'collection, callback, accumulator, thisArg', - 'init': 'accumulator', - 'top': - 'var noaccum = arguments.length < 3;\n' + - 'callback = createCallback(callback, thisArg)', - 'beforeLoop': { - 'array': 'if (noaccum) result = iteratee[++index]' - }, - 'inLoop': { - 'array': - 'result = callback(result, value, index, collection)', - 'object': - 'result = noaccum\n' + - ' ? (noaccum = false, value)\n' + - ' : callback(result, value, index, collection)' + function reduce(collection, callback, accumulator, thisArg) { + var noaccum = arguments.length < 3; + callback = createCallback(callback, thisArg, indicatorObject); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + if (noaccum) { + accumulator = collection[++index]; + } + while (++index < length) { + accumulator = callback(accumulator, collection[index], index, collection); + } + } else { + each(collection, function(value, index, collection) { + accumulator = noaccum + ? (noaccum = false, value) + : callback(accumulator, value, index, collection) + }); } - }); + return accumulator; + } /** * The right-associative version of `_.reduce`. @@ -2185,7 +2445,7 @@ * @alias foldr * @category Collections * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. + * @param {Function} [callback=identity] The function called per iteration. * @param {Mixed} [accumulator] Initial value of the accumulator. * @param {Mixed} [thisArg] The `this` binding of `callback`. * @returns {Mixed} Returns the accumulated value. @@ -2197,20 +2457,21 @@ */ function reduceRight(collection, callback, accumulator, thisArg) { var iteratee = collection, - length = collection.length, + length = collection ? collection.length : 0, noaccum = arguments.length < 3; - if (length !== +length) { + if (typeof length != 'number') { var props = keys(collection); length = props.length; - } else if (noCharByIndex && toString.call(collection) == stringClass) { + } else if (noCharByIndex && isString(collection)) { iteratee = collection.split(''); } - forEach(collection, function(value, index, object) { + callback = createCallback(callback, thisArg, indicatorObject); + forEach(collection, function(value, index, collection) { index = props ? props[--length] : --length; accumulator = noaccum ? (noaccum = false, iteratee[index]) - : callback.call(thisArg, accumulator, iteratee[index], index, object); + : callback(accumulator, iteratee[index], index, collection); }); return accumulator; } @@ -2232,9 +2493,38 @@ * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); * // => [1, 3, 5] */ - var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { - 'inLoop': '!' + filterIteratorOptions.inLoop - }); + function reject(collection, callback, thisArg) { + callback = createCallback(callback, thisArg); + return filter(collection, function(value, index, collection) { + return !callback(value, index, collection); + }); + } + + /** + * Creates an array of shuffled `array` values, using a version of the + * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to shuffle. + * @returns {Array} Returns a new shuffled collection. + * @example + * + * _.shuffle([1, 2, 3, 4, 5, 6]); + * // => [4, 1, 6, 3, 5, 2] + */ + function shuffle(collection) { + var index = -1, + result = Array(collection ? collection.length : 0); + + forEach(collection, function(value) { + var rand = floor(nativeRandom() * (++index + 1)); + result[index] = result[rand]; + result[rand] = value; + }); + return result; + } /** * Gets the size of the `collection` by returning `collection.length` for arrays @@ -2258,7 +2548,7 @@ */ function size(collection) { var length = collection ? collection.length : 0; - return length === +length ? length : keys(collection).length; + return typeof length == 'number' ? length : keys(collection).length; } /** @@ -2278,13 +2568,29 @@ * else `false`. * @example * - * _.some([null, 0, 'yes', false]); + * _.some([null, 0, 'yes', false], Boolean); * // => true */ - var some = createIterator(baseIteratorOptions, everyIteratorOptions, { - 'init': 'false', - 'inLoop': everyIteratorOptions.inLoop.replace('!', '') - }); + function some(collection, callback, thisArg) { + var result; + callback = createCallback(callback, thisArg); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + if ((result = callback(collection[index], index, collection))) { + break; + } + } + } else { + each(collection, function(value, index, collection) { + return !(result = callback(value, index, collection)); + }); + } + return !!result; + } /** * Creates an array, stable sorted in ascending order by the results of @@ -2311,31 +2617,28 @@ * _.sortBy(['larry', 'brendan', 'moe'], 'length'); * // => ['moe', 'larry', 'brendan'] */ - var sortBy = createIterator(baseIteratorOptions, countByIteratorOptions, mapIteratorOptions, { - 'inLoop': { - 'array': - 'result[index] = {\n' + - ' criteria: callback(value, index, collection),\n' + - ' index: index,\n' + - ' value: value\n' + - '}', - 'object': - 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '({\n' + - ' criteria: callback(value, index, collection),\n' + - ' index: index,\n' + - ' value: value\n' + - '})' - }, - 'bottom': - 'result.sort(compareAscending);\n' + - 'length = result.length;\n' + - 'while (length--) {\n' + - ' result[length] = result[length].value\n' + - '}' - }); + function sortBy(collection, callback, thisArg) { + var result = []; + callback = createCallback(callback, thisArg); + + forEach(collection, function(value, index, collection) { + result.push({ + 'criteria': callback(value, index, collection), + 'index': index, + 'value': value + }); + }); + + var length = result.length; + result.sort(compareAscending); + while (length--) { + result[length] = result[length].value; + } + return result; + } /** - * Converts the `collection`, to an array. + * Converts the `collection` to an array. * * @static * @memberOf _ @@ -2349,10 +2652,10 @@ */ function toArray(collection) { var length = collection ? collection.length : 0; - if (length === +length) { - return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') + if (typeof length == 'number') { + return noCharByIndex && isString(collection) ? collection.split('') - : slice.call(collection); + : slice(collection); } return values(collection); } @@ -2365,7 +2668,7 @@ * @memberOf _ * @category Collections * @param {Array|Object|String} collection The collection to iterate over. - * @param {Object} properties The object of properties/values to filter by. + * @param {Object} properties The object of property values to filter by. * @returns {Array} Returns a new array of elements that contain the given `properties`. * @example * @@ -2378,19 +2681,19 @@ * _.where(stooges, { 'age': 40 }); * // => [{ 'name': 'moe', 'age': 40 }] */ - var where = createIterator(filterIteratorOptions, { - 'args': 'collection, properties', - 'top': - 'var props = [];\n' + - 'forIn(properties, function(value, prop) { props.push(prop) });\n' + - 'var propsLength = props.length', - 'inLoop': - 'for (var prop, pass = true, propIndex = 0; propIndex < propsLength; propIndex++) {\n' + - ' prop = props[propIndex];\n' + - ' if (!(pass = value[prop] === properties[prop])) break\n' + - '}\n' + - 'pass && result.push(value)' - }); + function where(collection, properties) { + var props = keys(properties); + return filter(collection, function(object) { + var length = props.length; + while (length--) { + var result = object[props[length]] === properties[props[length]]; + if (!result) { + break; + } + } + return !!result; + }); + } /*--------------------------------------------------------------------------*/ @@ -2410,12 +2713,13 @@ */ function compact(array) { var index = -1, - length = array.length, + length = array ? array.length : 0, result = []; while (++index < length) { - if (array[index]) { - result.push(array[index]); + var value = array[index]; + if (value) { + result.push(value); } } return result; @@ -2439,14 +2743,15 @@ */ function difference(array) { var index = -1, - length = array.length, - flattened = concat.apply(ArrayProto, arguments), + length = array ? array.length : 0, + flattened = concat.apply(arrayRef, arguments), contains = cachedContains(flattened, length), result = []; while (++index < length) { - if (!contains(array[index])) { - result.push(array[index]); + var value = array[index]; + if (!contains(value)) { + result.push(value); } } return result; @@ -2464,15 +2769,20 @@ * @param {Number} [n] The number of elements to return. * @param- {Object} [guard] Internally used to allow this method to work with * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Mixed} Returns the first element or an array of the first `n` - * elements of `array`. + * @returns {Mixed} Returns the first element, or an array of the first `n` + * elements, of `array`. * @example * * _.first([5, 4, 3, 2, 1]); * // => 5 */ function first(array, n, guard) { - return (n == null || guard) ? array[0] : slice.call(array, 0, n); + if (array) { + var length = array.length; + return (n == null || guard) + ? array[0] + : slice(array, 0, nativeMin(nativeMax(0, n), length)); + } } /** @@ -2494,13 +2804,12 @@ * // => [1, 2, 3, [[4]]]; */ function flatten(array, shallow) { - var value, - index = -1, - length = array.length, + var index = -1, + length = array ? array.length : 0, result = []; while (++index < length) { - value = array[index]; + var value = array[index]; // recursively flatten arrays (susceptible to call stack limits) if (isArray(value)) { @@ -2515,15 +2824,15 @@ /** * Gets the index at which the first occurrence of `value` is found using * strict equality for comparisons, i.e. `===`. If the `array` is already - * sorted, passing `true` for `isSorted` will run a faster binary search. + * sorted, passing `true` for `fromIndex` will run a faster binary search. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to search. * @param {Mixed} value The value to search for. - * @param {Boolean|Number} [fromIndex=0] The index to start searching from or - * `true` to perform a binary search on a sorted `array`. + * @param {Boolean|Number} [fromIndex=0] The index to search from or `true` to + * perform a binary search on a sorted `array`. * @returns {Number} Returns the index of the matched value or `-1`. * @example * @@ -2538,15 +2847,13 @@ */ function indexOf(array, value, fromIndex) { var index = -1, - length = array.length; + length = array ? array.length : 0; - if (fromIndex) { - if (typeof fromIndex == 'number') { - index = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) - 1; - } else { - index = sortedIndex(array, value); - return array[index] === value ? index : -1; - } + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0) - 1; + } else if (fromIndex) { + index = sortedIndex(array, value); + return array[index] === value ? index : -1; } while (++index < length) { if (array[index] === value) { @@ -2564,17 +2871,22 @@ * @memberOf _ * @category Arrays * @param {Array} array The array to query. - * @param {Number} [n] The number of elements to return. + * @param {Number} [n=1] The number of elements to exclude. * @param- {Object} [guard] Internally used to allow this method to work with * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Array} Returns all but the last element or `n` elements of `array`. + * @returns {Array} Returns all but the last element, or `n` elements, of `array`. * @example * * _.initial([3, 2, 1]); * // => [3, 2] */ function initial(array, n, guard) { - return slice.call(array, 0, -((n == null || guard) ? 1 : n)); + if (!array) { + return []; + } + var length = array.length; + n = n == null || guard ? 1 : n || 0; + return slice(array, 0, nativeMin(nativeMax(0, length - n), length)); } /** @@ -2585,27 +2897,40 @@ * @memberOf _ * @category Arrays * @param {Array} [array1, array2, ...] Arrays to process. - * @returns {Array} Returns a new array of unique elements, in order, that are - * present in **all** of the arrays. + * @returns {Array} Returns a new array of unique elements that are present + * in **all** of the arrays. * @example * * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); * // => [1, 2] */ function intersection(array) { - var value, - argsLength = arguments.length, - cache = [], + var args = arguments, + argsLength = args.length, + cache = { '0': {} }, index = -1, - length = array.length, - result = []; + length = array ? array.length : 0, + isLarge = length >= 100, + result = [], + seen = result; - array: while (++index < length) { - value = array[index]; - if (indexOf(result, value) < 0) { - for (var argsIndex = 1; argsIndex < argsLength; argsIndex++) { - if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(arguments[argsIndex])))(value)) { - continue array; + outer: + while (++index < length) { + var value = array[index]; + if (isLarge) { + var key = value + ''; + var inited = hasOwnProperty.call(cache[0], key) + ? !(seen = cache[0][key]) + : (seen = cache[0][key] = []); + } + if (inited || indexOf(seen, value) < 0) { + if (isLarge) { + seen.push(value); + } + var argsIndex = argsLength; + while (--argsIndex) { + if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(args[argsIndex], 0, 100)))(value)) { + continue outer; } } result.push(value); @@ -2625,28 +2950,31 @@ * @param {Number} [n] The number of elements to return. * @param- {Object} [guard] Internally used to allow this method to work with * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Mixed} Returns the last element or an array of the last `n` - * elements of `array`. + * @returns {Mixed} Returns the last element, or an array of the last `n` + * elements, of `array`. * @example * * _.last([3, 2, 1]); * // => 1 */ function last(array, n, guard) { - var length = array.length; - return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); + if (array) { + var length = array.length; + return (n == null || guard) ? array[length - 1] : slice(array, nativeMax(0, length - n)); + } } /** - * Gets the index at which the last occurrence of `value` is found using - * strict equality for comparisons, i.e. `===`. + * Gets the index at which the last occurrence of `value` is found using strict + * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used + * as the offset from the end of the collection. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to search. * @param {Mixed} value The value to search for. - * @param {Number} [fromIndex=array.length-1] The index to start searching from. + * @param {Number} [fromIndex=array.length-1] The index to search from. * @returns {Number} Returns the index of the matched value or `-1`. * @example * @@ -2657,8 +2985,8 @@ * // => 1 */ function lastIndexOf(array, value, fromIndex) { - var index = array.length; - if (fromIndex && typeof fromIndex == 'number') { + var index = array ? array.length : 0; + if (typeof fromIndex == 'number') { index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1; } while (index--) { @@ -2670,84 +2998,6 @@ } /** - * Retrieves the maximum value of an `array`. If `callback` is passed, - * it will be executed for each value in the `array` to generate the - * criterion by which the value is ranked. The `callback` is bound to - * `thisArg` and invoked with three arguments; (value, index, array). - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Function} [callback] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Mixed} Returns the maximum value. - * @example - * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; - * - * _.max(stooges, function(stooge) { return stooge.age; }); - * // => { 'name': 'curly', 'age': 60 }; - */ - function max(array, callback, thisArg) { - var current, - computed = -Infinity, - index = -1, - length = array ? array.length : 0, - result = computed; - - callback = createCallback(callback, thisArg); - while (++index < length) { - current = callback(array[index], index, array); - if (current > computed) { - computed = current; - result = array[index]; - } - } - return result; - } - - /** - * Retrieves the minimum value of an `array`. If `callback` is passed, - * it will be executed for each value in the `array` to generate the - * criterion by which the value is ranked. The `callback` is bound to `thisArg` - * and invoked with three arguments; (value, index, array). - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Function} [callback] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Mixed} Returns the minimum value. - * @example - * - * _.min([10, 5, 100, 2, 1000]); - * // => 2 - */ - function min(array, callback, thisArg) { - var current, - computed = Infinity, - index = -1, - length = array ? array.length : 0, - result = computed; - - callback = createCallback(callback, thisArg); - while (++index < length) { - current = callback(array[index], index, array); - if (current < computed) { - computed = current; - result = array[index]; - } - } - return result; - } - - /** * Creates an object composed from arrays of `keys` and `values`. Pass either * a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`, or * two arrays, one of `keys` and one of corresponding `values`. @@ -2766,14 +3016,15 @@ */ function object(keys, values) { var index = -1, - length = keys.length, + length = keys ? keys.length : 0, result = {}; while (++index < length) { + var key = keys[index]; if (values) { - result[keys[index]] = values[index]; + result[key] = values[index]; } else { - result[keys[index][0]] = keys[index][1]; + result[key[0]] = key[1]; } } return result; @@ -2817,9 +3068,9 @@ start = 0; } // use `Array(length)` so V8 will avoid the slower "dictionary" mode - // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s + // http://youtu.be/XAqIpGU8ZZk#t=17m25s var index = -1, - length = nativeMax(0, Math.ceil((end - start) / step)), + length = nativeMax(0, ceil((end - start) / step)), result = Array(length); while (++index < length) { @@ -2838,45 +3089,17 @@ * @alias drop, tail * @category Arrays * @param {Array} array The array to query. - * @param {Number} [n] The number of elements to return. + * @param {Number} [n=1] The number of elements to exclude. * @param- {Object} [guard] Internally used to allow this method to work with * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Array} Returns all but the first value or `n` values of `array`. + * @returns {Array} Returns all but the first element, or `n` elements, of `array`. * @example * * _.rest([3, 2, 1]); * // => [2, 1] */ function rest(array, n, guard) { - return slice.call(array, (n == null || guard) ? 1 : n); - } - - /** - * Creates an array of shuffled `array` values, using a version of the - * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to shuffle. - * @returns {Array} Returns a new shuffled array. - * @example - * - * _.shuffle([1, 2, 3, 4, 5, 6]); - * // => [4, 1, 6, 3, 5, 2] - */ - function shuffle(array) { - var rand, - index = -1, - length = array.length, - result = Array(length); - - while (++index < length) { - rand = nativeFloor(nativeRandom() * (index + 1)); - result[index] = result[rand]; - result[rand] = array[index]; - } - return result; + return slice(array, (n == null || guard) ? 1 : nativeMax(0, n)); } /** @@ -2920,15 +3143,18 @@ * // => 2 */ function sortedIndex(array, value, callback, thisArg) { - var mid, - low = 0, - high = array.length; + var low = 0, + high = array ? array.length : low; - callback = createCallback(callback, thisArg); + // explicitly reference `identity` for better inlining in Firefox + callback = callback ? createCallback(callback, thisArg) : identity; value = callback(value); + while (low < high) { - mid = (low + high) >>> 1; - callback(array[mid]) < value ? low = mid + 1 : high = mid; + var mid = (low + high) >>> 1; + callback(array[mid]) < value + ? low = mid + 1 + : high = mid; } return low; } @@ -2949,17 +3175,7 @@ * // => [1, 2, 3, 101, 10] */ function union() { - var index = -1, - flattened = concat.apply(ArrayProto, arguments), - length = flattened.length, - result = []; - - while (++index < length) { - if (indexOf(result, flattened[index]) < 0) { - result.push(flattened[index]); - } - } - return result; + return uniq(concat.apply(arrayRef, arguments)); } /** @@ -2993,11 +3209,10 @@ * // => [1, 2, 3] */ function uniq(array, isSorted, callback, thisArg) { - var computed, - index = -1, - length = array.length, + var index = -1, + length = array ? array.length : 0, result = [], - seen = []; + seen = result; // juggle arguments if (typeof isSorted == 'function') { @@ -3005,15 +3220,33 @@ callback = isSorted; isSorted = false; } - callback = createCallback(callback, thisArg); + // init value cache for large arrays + var isLarge = !isSorted && length >= 75; + if (isLarge) { + var cache = {}; + } + if (callback) { + seen = []; + callback = createCallback(callback, thisArg); + } while (++index < length) { - computed = callback(array[index], index, array); + var value = array[index], + computed = callback ? callback(value, index, array) : value; + + if (isLarge) { + var key = computed + ''; + var inited = hasOwnProperty.call(cache, key) + ? !(seen = cache[key]) + : (seen = cache[key] = []); + } if (isSorted ? !index || seen[seen.length - 1] !== computed - : indexOf(seen, computed) < 0 + : inited || indexOf(seen, computed) < 0 ) { - seen.push(computed); - result.push(array[index]); + if (callback || isLarge) { + seen.push(computed); + } + result.push(value); } } return result; @@ -3036,13 +3269,14 @@ */ function without(array) { var index = -1, - length = array.length, + length = array ? array.length : 0, contains = cachedContains(arguments, 1, 20), result = []; while (++index < length) { - if (!contains(array[index])) { - result.push(array[index]); + var value = array[index]; + if (!contains(value)) { + result.push(value); } } return result; @@ -3066,7 +3300,7 @@ */ function zip(array) { var index = -1, - length = max(pluck(arguments, 'length')), + length = array ? max(pluck(arguments, 'length')) : 0, result = Array(length); while (++index < length) { @@ -3078,8 +3312,9 @@ /*--------------------------------------------------------------------------*/ /** - * Creates a function that is restricted to executing only after it is - * called `n` times. + * Creates a function that is restricted to executing `func` only after it is + * called `n` times. The `func` is executed with the `this` binding of the + * created function. * * @static * @memberOf _ @@ -3134,7 +3369,7 @@ // (in V8 `Function#bind` is slower except when partially applied) return isBindFast || (nativeBind && arguments.length > 2) ? nativeBind.call.apply(nativeBind, arguments) - : createBound(func, thisArg, slice.call(arguments, 2)); + : createBound(func, thisArg, slice(arguments, 2)); } /** @@ -3159,29 +3394,61 @@ * jQuery('#lodash_button').on('click', buttonView.onClick); * // => When the button is clicked, `this.label` will have the correct value */ - var bindAll = createIterator({ - 'useHas': false, - 'useStrict': false, - 'args': 'object', - 'top': - 'var funcs = arguments,\n' + - ' length = funcs.length;\n' + - 'if (length > 1) {\n' + - ' for (var index = 1; index < length; index++) {\n' + - ' result[funcs[index]] = bind(result[funcs[index]], result)\n' + - ' }\n' + - ' return result\n' + - '}', - 'inLoop': - 'if (isFunction(result[index])) {\n' + - ' result[index] = bind(result[index], result)\n' + - '}' - }); + function bindAll(object) { + var funcs = arguments, + index = funcs.length > 1 ? 0 : (funcs = functions(object), -1), + length = funcs.length; + + while (++index < length) { + var key = funcs[index]; + object[key] = bind(object[key], object); + } + return object; + } + + /** + * Creates a function that, when called, invokes the method at `object[key]` + * and prepends any additional `bindKey` arguments to those passed to the bound + * function. This method differs from `_.bind` by allowing bound functions to + * reference methods that will be redefined or don't yet exist. + * See http://michaux.ca/articles/lazy-function-definition-pattern. + * + * @static + * @memberOf _ + * @category Functions + * @param {Object} object The object the method belongs to. + * @param {String} key The key of the method. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var object = { + * 'name': 'moe', + * 'greet': function(greeting) { + * return greeting + ' ' + this.name; + * } + * }; + * + * var func = _.bindKey(object, 'greet', 'hi'); + * func(); + * // => 'hi moe' + * + * object.greet = function(greeting) { + * return greeting + ', ' + this.name + '!'; + * }; + * + * func(); + * // => 'hi, moe!' + */ + function bindKey(object, key) { + return createBound(object, key, slice(arguments, 2)); + } /** * Creates a function that is the composition of the passed functions, * where each function consumes the return value of the function that follows. * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. + * Each function is executed with the `this` binding of the composed function. * * @static * @memberOf _ @@ -3241,7 +3508,6 @@ result = func.apply(thisArg, args); } } - return function() { var isImmediate = immediate && !timeoutId; args = arguments; @@ -3275,8 +3541,8 @@ * // => 'logged later' (Appears after one second.) */ function delay(func, wait) { - var args = slice.call(arguments, 2); - return setTimeout(function() { return func.apply(undefined, args); }, wait); + var args = slice(arguments, 2); + return setTimeout(function() { func.apply(undefined, args); }, wait); } /** @@ -3295,51 +3561,16 @@ * // returns from the function before `alert` is called */ function defer(func) { - var args = slice.call(arguments, 1); - return setTimeout(function() { return func.apply(undefined, args); }, 1); - } - - /** - * Creates a function that, when called, invokes `object[methodName]` and - * prepends any additional `lateBind` arguments to those passed to the bound - * function. This method - * - * @static - * @memberOf _ - * @category Functions - * @param {Object} object The object the method belongs to. - * @param {String} methodName The method name. - * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. - * @returns {Function} Returns the new bound function. - * @example - * - * var object = { - * 'name': 'moe', - * 'greet': function(greeting) { - * return greeting + ' ' + this.name; - * } - * }; - * - * var func = _.bind(object, 'greet', 'hi'); - * func(); - * // => 'hi moe' - * - * object.greet = function(greeting) { - * return greeting + ', ' + this.name + '!'; - * }; - * - * func(); - * // => 'hi, moe!' - */ - function lateBind(object, methodName) { - return createBound(methodName, object, slice.call(arguments, 2)); + var args = slice(arguments, 1); + return setTimeout(function() { func.apply(undefined, args); }, 1); } /** * Creates a function that memoizes the result of `func`. If `resolver` is * passed, it will be used to determine the cache key for storing the result * based on the arguments passed to the memoized function. By default, the first - * argument passed to the memoized function is used as the cache key. + * argument passed to the memoized function is used as the cache key. The `func` + * is executed with the `this` binding of the memoized function. * * @static * @memberOf _ @@ -3356,16 +3587,17 @@ function memoize(func, resolver) { var cache = {}; return function() { - var prop = resolver ? resolver.apply(this, arguments) : arguments[0]; - return hasOwnProperty.call(cache, prop) - ? cache[prop] - : (cache[prop] = func.apply(this, arguments)); + var key = resolver ? resolver.apply(this, arguments) : arguments[0]; + return hasOwnProperty.call(cache, key) + ? cache[key] + : (cache[key] = func.apply(this, arguments)); }; } /** - * Creates a function that is restricted to one execution. Repeat calls to - * the function will return the value of the first call. + * Creates a function that is restricted to execute `func` once. Repeat calls to + * the function will return the value of the first call. The `func` is executed + * with the `this` binding of the created function. * * @static * @memberOf _ @@ -3398,8 +3630,8 @@ /** * Creates a function that, when called, invokes `func` with any additional - * `partial` arguments prepended to those passed to the new function. This method - * is similar to `bind`, except it does **not** alter the `this` binding. + * `partial` arguments prepended to those passed to the new function. This + * method is similar to `bind`, except it does **not** alter the `this` binding. * * @static * @memberOf _ @@ -3415,7 +3647,7 @@ * // => 'hi: moe' */ function partial(func) { - return createBound(func, slice.call(arguments, 1)); + return createBound(func, slice(arguments, 1)); } /** @@ -3448,20 +3680,21 @@ timeoutId = null; result = func.apply(thisArg, args); } - return function() { var now = new Date, - remain = wait - (now - lastCalled); + remaining = wait - (now - lastCalled); args = arguments; thisArg = this; - if (remain <= 0) { + if (remaining <= 0) { + clearTimeout(timeoutId); + timeoutId = null; lastCalled = now; result = func.apply(thisArg, args); } else if (!timeoutId) { - timeoutId = setTimeout(trailingCall, remain); + timeoutId = setTimeout(trailingCall, remaining); } return result; }; @@ -3469,8 +3702,9 @@ /** * Creates a function that passes `value` to the `wrapper` function as its - * first argument. Additional arguments passed to the new function are appended - * to those passed to the `wrapper` function. + * first argument. Additional arguments passed to the function are appended + * to those passed to the `wrapper` function. The `wrapper` is executed with + * the `this` binding of the created function. * * @static * @memberOf _ @@ -3480,19 +3714,17 @@ * @returns {Function} Returns the new function. * @example * - * var hello = function(name) { return 'hello: ' + name; }; + * var hello = function(name) { return 'hello ' + name; }; * hello = _.wrap(hello, function(func) { * return 'before, ' + func('moe') + ', after'; * }); * hello(); - * // => 'before, hello: moe, after' + * // => 'before, hello moe, after' */ function wrap(value, wrapper) { return function() { var args = [value]; - if (arguments.length) { - push.apply(args, arguments); - } + push.apply(args, arguments); return wrapper.apply(this, args); }; } @@ -3511,7 +3743,7 @@ * @example * * _.escape('Moe, Larry & Curly'); - * // => "Moe, Larry & Curly" + * // => 'Moe, Larry & Curly' */ function escape(string) { return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); @@ -3520,8 +3752,6 @@ /** * This function returns the first argument passed to it. * - * Note: It is used throughout Lo-Dash as a default callback. - * * @static * @memberOf _ * @category Utilities @@ -3565,15 +3795,10 @@ lodash.prototype[methodName] = function() { var args = [this.__wrapped__]; - if (arguments.length) { - push.apply(args, arguments); - } + push.apply(args, arguments); + var result = func.apply(lodash, args); - if (this.__chain__) { - result = new lodash(result); - result.__chain__ = true; - } - return result; + return new lodash(result); }; }); } @@ -3622,7 +3847,7 @@ max = min; min = 0; } - return min + nativeFloor(nativeRandom() * ((+max || 0) - min + 1)); + return min + floor(nativeRandom() * ((+max || 0) - min + 1)); } /** @@ -3630,7 +3855,6 @@ * it will be invoked and its result returned, else the property value is * returned. If `object` is falsey, then `null` is returned. * - * @deprecated * @static * @memberOf _ * @category Utilities @@ -3676,14 +3900,20 @@ * @param {String} text The template text. * @param {Obect} data The data object used to populate the text. * @param {Object} options The options object. + * escape - The "escape" delimiter regexp. + * evaluate - The "evaluate" delimiter regexp. + * interpolate - The "interpolate" delimiter regexp. + * sourceURL - The sourceURL of the template's compiled source. + * variable - The data object variable name. + * * @returns {Function|String} Returns a compiled function when no `data` object * is given, else it returns the interpolated text. * @example * * // using a compiled template - * var compiled = _.template('hello: <%= name %>'); + * var compiled = _.template('hello <%= name %>'); * compiled({ 'name': 'moe' }); - * // => 'hello: moe' + * // => 'hello moe' * * var list = '<% _.forEach(people, function(name) { %><li><%= name %></li><% }); %>'; * _.template(list, { 'people': ['moe', 'larry', 'curly'] }); @@ -3691,26 +3921,35 @@ * * // using the "escape" delimiter to escape HTML in data property values * _.template('<b><%- value %></b>', { 'value': '<script>' }); - * // => '<b><script></b>' + * // => '<b><script></b>' + * + * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter + * _.template('hello ${ name }', { 'name': 'curly' }); + * // => 'hello curly' * * // using the internal `print` function in "evaluate" delimiters - * _.template('<% print("Hello " + epithet); %>.', { 'epithet': 'stooge' }); - * // => 'Hello stooge.' + * _.template('<% print("hello " + epithet); %>!', { 'epithet': 'stooge' }); + * // => 'hello stooge!' * - * // using custom template delimiter settings + * // using custom template delimiters * _.templateSettings = { - * 'interpolate': /\{\{([\s\S]+?)\}\}/g + * 'interpolate': /{{([\s\S]+?)}}/g * }; * - * _.template('Hello {{ name }}!', { 'name': 'Mustache' }); - * // => 'Hello Mustache!' + * _.template('hello {{ name }}!', { 'name': 'mustache' }); + * // => 'hello mustache!' + * + * // using the `sourceURL` option to specify a custom sourceURL for the template + * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' }); + * compiled(data); + * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector * * // using the `variable` option to ensure a with-statement isn't used in the compiled template - * var compiled = _.template('hello: <%= data.name %>', null, { 'variable': 'data' }); + * var compiled = _.template('hello <%= data.name %>!', null, { 'variable': 'data' }); * compiled.source; * // => function(data) { * var __t, __p = '', __e = _.escape; - * __p += 'hello: ' + ((__t = ( data.name )) == null ? '' : __t); + * __p += 'hello ' + ((__t = ( data.name )) == null ? '' : __t) + '!'; * return __p; * } * @@ -3727,12 +3966,14 @@ // http://ejohn.org/blog/javascript-micro-templating/ // and Laura Doktorova's doT.js // https://github.com/olado/doT + text || (text = ''); options || (options = {}); var isEvaluating, result, - index = 0, settings = lodash.templateSettings, + index = 0, + interpolate = options.interpolate || settings.interpolate || reNoMatch, source = "__p += '", variable = options.variable || settings.variable, hasVariable = variable; @@ -3740,22 +3981,33 @@ // compile regexp to match each delimiter var reDelimiters = RegExp( (options.escape || settings.escape || reNoMatch).source + '|' + - (options.interpolate || settings.interpolate || reNoMatch).source + '|' + + interpolate.source + '|' + + (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' + (options.evaluate || settings.evaluate || reNoMatch).source + '|$' , 'g'); - text.replace(reDelimiters, function(match, escapeValue, interpolateValue, evaluateValue, offset) { + text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) { + interpolateValue || (interpolateValue = esTemplateValue); + // escape characters that cannot be included in string literals source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar); // replace delimiters with snippets - source += - escapeValue ? "' +\n__e(" + escapeValue + ") +\n'" : - evaluateValue ? "';\n" + evaluateValue + ";\n__p += '" : - interpolateValue ? "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'" : ''; - + if (escapeValue) { + source += "' +\n__e(" + escapeValue + ") +\n'"; + } + if (evaluateValue) { + source += "';\n" + evaluateValue + ";\n__p += '"; + } + if (interpolateValue) { + source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; + } isEvaluating || (isEvaluating = evaluateValue || reComplexDelimiter.test(escapeValue || interpolateValue)); index = offset + match.length; + + // the JS engine embedded in Adobe products requires returning the `match` + // string in order to produce the correct `offset` value + return match; }); source += "';\n"; @@ -3785,10 +4037,10 @@ // frame code as the function body source = 'function(' + variable + ') {\n' + (hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') + - 'var __t, __p = \'\', __e = _.escape' + + "var __t, __p = '', __e = _.escape" + (isEvaluating ? ', __j = Array.prototype.join;\n' + - 'function print() { __p += __j.call(arguments, \'\') }\n' + "function print() { __p += __j.call(arguments, '') }\n" : (hasVariable ? '' : ', __d = ' + variable + '.' + variable + ' || ' + variable) + ';\n' ) + source + @@ -3797,7 +4049,7 @@ // use a sourceURL for easier debugging // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl var sourceURL = useSourceURL - ? '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']' + ? '\n//@ sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') : ''; try { @@ -3852,8 +4104,9 @@ } /** - * Converts the HTML entities `&`, `<`, `>`, `"`, and `'` - * in `string` to their corresponding characters. + * The opposite of `_.escape`, this method converts the HTML entities + * `&`, `<`, `>`, `"`, and `'` in `string` to their + * corresponding characters. * * @static * @memberOf _ @@ -3863,63 +4116,35 @@ * @example * * _.unescape('Moe, Larry & Curly'); - * // => "Moe, Larry & Curly" + * // => 'Moe, Larry & Curly' */ function unescape(string) { return string == null ? '' : (string + '').replace(reEscapedHtml, unescapeHtmlChar); } /** - * Generates a unique id. If `prefix` is passed, the id will be appended to it. + * Generates a unique ID. If `prefix` is passed, the ID will be appended to it. * * @static * @memberOf _ * @category Utilities - * @param {String} [prefix] The value to prefix the id with. - * @returns {Number|String} Returns a numeric id if no prefix is passed, else - * a string id may be returned. + * @param {String} [prefix] The value to prefix the ID with. + * @returns {String} Returns the unique ID. * @example * * _.uniqueId('contact_'); * // => 'contact_104' + * + * _.uniqueId(); + * // => '105' */ function uniqueId(prefix) { - var id = idCounter++; - return prefix ? prefix + id : id; + return (prefix == null ? '' : prefix + '') + (++idCounter); } /*--------------------------------------------------------------------------*/ /** - * Wraps the value in a `lodash` wrapper object. - * - * @static - * @memberOf _ - * @category Chaining - * @param {Mixed} value The value to wrap. - * @returns {Object} Returns the wrapper object. - * @example - * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; - * - * var youngest = _.chain(stooges) - * .sortBy(function(stooge) { return stooge.age; }) - * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) - * .first() - * .value(); - * // => 'moe is 40' - */ - function chain(value) { - value = new lodash(value); - value.__chain__ = true; - return value; - } - - /** * Invokes `interceptor` with the `value` as the first argument, and then * returns `value`. The purpose of this method is to "tap into" a method chain, * in order to perform operations on intermediate results within the chain. @@ -3935,7 +4160,7 @@ * _.chain([1, 2, 3, 200]) * .filter(function(num) { return num % 2 == 0; }) * .tap(alert) - * .map(function(num) { return num * num }) + * .map(function(num) { return num * num; }) * .value(); * // => // [2, 200] (alerted) * // => [4, 40000] @@ -3946,84 +4171,120 @@ } /** - * Enables method chaining on the wrapper object. + * Produces the `toString` result of the wrapped value. * - * @name chain - * @deprecated + * @name toString * @memberOf _ * @category Chaining - * @returns {Mixed} Returns the wrapper object. + * @returns {String} Returns the string result. * @example * - * _([1, 2, 3]).value(); - * // => [1, 2, 3] + * _([1, 2, 3]).toString(); + * // => '1,2,3' */ - function wrapperChain() { - this.__chain__ = true; - return this; + function wrapperToString() { + return this.__wrapped__ + ''; } /** * Extracts the wrapped value. * - * @name value + * @name valueOf * @memberOf _ + * @alias value * @category Chaining * @returns {Mixed} Returns the wrapped value. * @example * - * _([1, 2, 3]).value(); + * _([1, 2, 3]).valueOf(); * // => [1, 2, 3] */ - function wrapperValue() { + function wrapperValueOf() { return this.__wrapped__; } /*--------------------------------------------------------------------------*/ - /** - * The semantic version number. - * - * @static - * @memberOf _ - * @type String - */ - lodash.VERSION = '0.7.0'; - - // assign static methods + // add functions that return wrapped values when chaining lodash.after = after; + lodash.assign = assign; lodash.bind = bind; lodash.bindAll = bindAll; - lodash.chain = chain; - lodash.clone = clone; + lodash.bindKey = bindKey; lodash.compact = compact; lodash.compose = compose; - lodash.contains = contains; lodash.countBy = countBy; lodash.debounce = debounce; lodash.defaults = defaults; lodash.defer = defer; lodash.delay = delay; lodash.difference = difference; - lodash.escape = escape; - lodash.every = every; - lodash.extend = extend; lodash.filter = filter; - lodash.find = find; - lodash.first = first; lodash.flatten = flatten; lodash.forEach = forEach; lodash.forIn = forIn; lodash.forOwn = forOwn; lodash.functions = functions; lodash.groupBy = groupBy; - lodash.has = has; - lodash.identity = identity; - lodash.indexOf = indexOf; lodash.initial = initial; lodash.intersection = intersection; lodash.invert = invert; lodash.invoke = invoke; + lodash.keys = keys; + lodash.map = map; + lodash.max = max; + lodash.memoize = memoize; + lodash.merge = merge; + lodash.min = min; + lodash.object = object; + lodash.omit = omit; + lodash.once = once; + lodash.pairs = pairs; + lodash.partial = partial; + lodash.pick = pick; + lodash.pluck = pluck; + lodash.range = range; + lodash.reject = reject; + lodash.rest = rest; + lodash.shuffle = shuffle; + lodash.sortBy = sortBy; + lodash.tap = tap; + lodash.throttle = throttle; + lodash.times = times; + lodash.toArray = toArray; + lodash.union = union; + lodash.uniq = uniq; + lodash.values = values; + lodash.where = where; + lodash.without = without; + lodash.wrap = wrap; + lodash.zip = zip; + + // add aliases + lodash.collect = map; + lodash.drop = rest; + lodash.each = forEach; + lodash.extend = assign; + lodash.methods = functions; + lodash.select = filter; + lodash.tail = rest; + lodash.unique = uniq; + + // add functions to `lodash.prototype` + mixin(lodash); + + /*--------------------------------------------------------------------------*/ + + // add functions that return unwrapped values when chaining + lodash.clone = clone; + lodash.cloneDeep = cloneDeep; + lodash.contains = contains; + lodash.escape = escape; + lodash.every = every; + lodash.find = find; + lodash.has = has; + lodash.identity = identity; + lodash.indexOf = indexOf; lodash.isArguments = isArguments; lodash.isArray = isArray; lodash.isBoolean = isBoolean; @@ -4041,120 +4302,123 @@ lodash.isRegExp = isRegExp; lodash.isString = isString; lodash.isUndefined = isUndefined; - lodash.keys = keys; - lodash.last = last; lodash.lastIndexOf = lastIndexOf; - lodash.lateBind = lateBind; - lodash.map = map; - lodash.max = max; - lodash.memoize = memoize; - lodash.merge = merge; - lodash.min = min; lodash.mixin = mixin; lodash.noConflict = noConflict; - lodash.object = object; - lodash.omit = omit; - lodash.once = once; - lodash.pairs = pairs; - lodash.partial = partial; - lodash.pick = pick; - lodash.pluck = pluck; lodash.random = random; - lodash.range = range; lodash.reduce = reduce; lodash.reduceRight = reduceRight; - lodash.reject = reject; - lodash.rest = rest; lodash.result = result; - lodash.shuffle = shuffle; lodash.size = size; lodash.some = some; - lodash.sortBy = sortBy; lodash.sortedIndex = sortedIndex; - lodash.tap = tap; lodash.template = template; - lodash.throttle = throttle; - lodash.times = times; - lodash.toArray = toArray; lodash.unescape = unescape; - lodash.union = union; - lodash.uniq = uniq; lodash.uniqueId = uniqueId; - lodash.values = values; - lodash.where = where; - lodash.without = without; - lodash.wrap = wrap; - lodash.zip = zip; - // assign aliases + // add aliases lodash.all = every; lodash.any = some; - lodash.collect = map; lodash.detect = find; - lodash.drop = rest; - lodash.each = forEach; lodash.foldl = reduce; lodash.foldr = reduceRight; - lodash.head = first; lodash.include = contains; lodash.inject = reduce; - lodash.methods = functions; - lodash.select = filter; - lodash.tail = rest; - lodash.take = first; - lodash.unique = uniq; - // add pseudo private properties used and removed during the build process - lodash._iteratorTemplate = iteratorTemplate; - lodash._shimKeys = shimKeys; + forOwn(lodash, function(func, methodName) { + if (!lodash.prototype[methodName]) { + lodash.prototype[methodName] = function() { + var args = [this.__wrapped__]; + push.apply(args, arguments); + return func.apply(lodash, args); + }; + } + }); /*--------------------------------------------------------------------------*/ - // add all static functions to `lodash.prototype` - mixin(lodash); + // add functions capable of returning wrapped and unwrapped values when chaining + lodash.first = first; + lodash.last = last; + + // add aliases + lodash.take = first; + lodash.head = first; - // add `lodash.prototype.chain` after calling `mixin()` to avoid overwriting - // it with the wrapped `lodash.chain` - lodash.prototype.chain = wrapperChain; - lodash.prototype.value = wrapperValue; + forOwn(lodash, function(func, methodName) { + if (!lodash.prototype[methodName]) { + lodash.prototype[methodName]= function(n, guard) { + var result = func(this.__wrapped__, n, guard); + return (n == null || guard) ? result : new lodash(result); + }; + } + }); - // add all mutator Array functions to the wrapper. - forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { - var func = ArrayProto[methodName]; + /*--------------------------------------------------------------------------*/ - lodash.prototype[methodName] = function() { - var value = this.__wrapped__; - func.apply(value, arguments); + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type String + */ + lodash.VERSION = '1.0.0-rc.3'; - // avoid array-like object bugs with `Array#shift` and `Array#splice` in - // Firefox < 10 and IE < 9 - if (hasObjectSpliceBug && value.length === 0) { - delete value[0]; - } - if (this.__chain__) { - value = new lodash(value); - value.__chain__ = true; - } - return value; + // add "Chaining" functions to the wrapper + lodash.prototype.toString = wrapperToString; + lodash.prototype.value = wrapperValueOf; + lodash.prototype.valueOf = wrapperValueOf; + + // add `Array` functions that return unwrapped values + each(['join', 'pop', 'shift'], function(methodName) { + var func = arrayRef[methodName]; + lodash.prototype[methodName] = function() { + return func.apply(this.__wrapped__, arguments); }; }); - // add all accessor Array functions to the wrapper. - forEach(['concat', 'join', 'slice'], function(methodName) { - var func = ArrayProto[methodName]; - + // add `Array` functions that return the wrapped value + each(['push', 'reverse', 'sort', 'unshift'], function(methodName) { + var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { - var value = this.__wrapped__, - result = func.apply(value, arguments); + func.apply(this.__wrapped__, arguments); + return this; + }; + }); - if (this.__chain__) { - result = new lodash(result); - result.__chain__ = true; - } - return result; + // add `Array` functions that return new wrapped values + each(['concat', 'slice', 'splice'], function(methodName) { + var func = arrayRef[methodName]; + lodash.prototype[methodName] = function() { + var result = func.apply(this.__wrapped__, arguments); + return new lodash(result); }; }); + // avoid array-like object bugs with `Array#shift` and `Array#splice` + // in Firefox < 10 and IE < 9 + if (hasObjectSpliceBug) { + each(['pop', 'shift', 'splice'], function(methodName) { + var func = arrayRef[methodName], + isSplice = methodName == 'splice'; + + lodash.prototype[methodName] = function() { + var value = this.__wrapped__, + result = func.apply(value, arguments); + + if (value.length === 0) { + delete value[0]; + } + return isSplice ? new lodash(result) : result; + }; + }); + } + + // add pseudo private property to be used and removed during the build process + lodash._each = each; + lodash._iteratorTemplate = iteratorTemplate; + /*--------------------------------------------------------------------------*/ // expose Lo-Dash diff --git a/module/web/static/js/libs/require-2.0.6.js b/module/web/static/js/libs/require-2.1.2.js index b592d5f22..0e7b81bc4 100644 --- a/module/web/static/js/libs/require-2.0.6.js +++ b/module/web/static/js/libs/require-2.1.2.js @@ -1,5 +1,5 @@ /** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 2.0.6 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. + * @license RequireJS 2.1.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -12,7 +12,7 @@ var requirejs, require, define; (function (global) { var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, - version = '2.0.6', + version = '2.1.2', commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, jsSuffixRegExp = /\.js$/, @@ -81,6 +81,10 @@ var requirejs, require, define; return hasOwn.call(obj, prop); } + function getOwn(obj, prop) { + return hasProp(obj, prop) && obj[prop]; + } + /** * Cycles over properties in an object and calls a function for each * property value. If the function returns a truthy value, then the @@ -89,7 +93,7 @@ var requirejs, require, define; function eachProp(obj, func) { var prop; for (prop in obj) { - if (obj.hasOwnProperty(prop)) { + if (hasProp(obj, prop)) { if (func(obj[prop], prop)) { break; } @@ -100,9 +104,6 @@ var requirejs, require, define; /** * Simple function to mix in properties from source into target, * but only if target does not already have a property of the same name. - * This is not robust in IE for transferring methods that match - * Object.prototype names, but the uses of mixin here seem unlikely to - * trigger a problem related to that. */ function mixin(target, source, force, deepStringMixin) { if (source) { @@ -147,41 +148,6 @@ var requirejs, require, define; return g; } - function makeContextModuleFunc(func, relMap, enableBuildCallback) { - return function () { - //A version of a require function that passes a moduleName - //value for items that may need to - //look up paths relative to the moduleName - var args = aps.call(arguments, 0), lastArg; - if (enableBuildCallback && - isFunction((lastArg = args[args.length - 1]))) { - lastArg.__requireJsBuild = true; - } - args.push(relMap); - return func.apply(null, args); - }; - } - - function addRequireMethods(req, context, relMap) { - each([ - ['toUrl'], - ['undef'], - ['defined', 'requireDefined'], - ['specified', 'requireSpecified'] - ], function (item) { - var prop = item[1] || item[0]; - req[item[0]] = context ? makeContextModuleFunc(context[prop], relMap) : - //If no context, then use default context. Reference from - //contexts instead of early binding to default context, so - //that during builds, the latest instance of the default - //context with its config gets used. - function () { - var ctx = contexts[defContextName]; - return ctx[prop].apply(ctx, arguments); - }; - }); - } - /** * Constructs an error with a pointer to an URL with more information. * @param {String} id the error ID that maps to an ID on a web page. @@ -230,7 +196,9 @@ var requirejs, require, define; baseUrl: './', paths: {}, pkgs: {}, - shim: {} + shim: {}, + map: {}, + config: {} }, registry = {}, undefEvents = {}, @@ -238,12 +206,7 @@ var requirejs, require, define; defined = {}, urlFetched = {}, requireCounter = 1, - unnormalizedCounter = 1, - //Used to track the order in which modules - //should be executed, by the order they - //load. Important for consistent cycle resolution - //behavior. - waitAry = []; + unnormalizedCounter = 1; /** * Trims the . and .. from an array of path segments. @@ -302,7 +265,7 @@ var requirejs, require, define; //otherwise, assume it is a top-level require that will //be relative to baseUrl in the end. if (baseName) { - if (config.pkgs[baseName]) { + if (getOwn(config.pkgs, baseName)) { //If the baseName is a package name, then just treat it as one //name to concat the name with. normalizedBaseParts = baseParts = [baseName]; @@ -320,7 +283,7 @@ var requirejs, require, define; //Some use of packages may use a . path to reference the //'main' module name, so normalize for that. - pkgConfig = config.pkgs[(pkgName = name[0])]; + pkgConfig = getOwn(config.pkgs, (pkgName = name[0])); name = name.join('/'); if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { name = pkgName; @@ -343,12 +306,12 @@ var requirejs, require, define; //Find the longest baseName segment match in the config. //So, do joins on the biggest to smallest lengths of baseParts. for (j = baseParts.length; j > 0; j -= 1) { - mapValue = map[baseParts.slice(0, j).join('/')]; + mapValue = getOwn(map, baseParts.slice(0, j).join('/')); //baseName segment has config, find if it has one for //this name. if (mapValue) { - mapValue = mapValue[nameSegment]; + mapValue = getOwn(mapValue, nameSegment); if (mapValue) { //Match, update name to the new value. foundMap = mapValue; @@ -366,8 +329,8 @@ var requirejs, require, define; //Check for a star map match, but just hold on to it, //if there is a shorter segment match later in a matching //config, then favor over this star map. - if (!foundStarMap && starMap && starMap[nameSegment]) { - foundStarMap = starMap[nameSegment]; + if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { + foundStarMap = getOwn(starMap, nameSegment); starI = i; } } @@ -399,18 +362,31 @@ var requirejs, require, define; } function hasPathFallback(id) { - var pathConfig = config.paths[id]; + var pathConfig = getOwn(config.paths, id); if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { removeScript(id); //Pop off the first array value, since it failed, and //retry pathConfig.shift(); - context.undef(id); + context.require.undef(id); context.require([id]); return true; } } + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + /** * Creates a module mapping that includes plugin prefix, module * name, and path. If parentModuleMap is provided it will @@ -427,8 +403,7 @@ var requirejs, require, define; * @returns {Object} */ function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { - var url, pluginModule, suffix, - index = name ? name.indexOf('!') : -1, + var url, pluginModule, suffix, nameParts, prefix = null, parentName = parentModuleMap ? parentModuleMap.name : null, originalName = name, @@ -442,14 +417,13 @@ var requirejs, require, define; name = '_@r' + (requireCounter += 1); } - if (index !== -1) { - prefix = name.substring(0, index); - name = name.substring(index + 1, name.length); - } + nameParts = splitPrefix(name); + prefix = nameParts[0]; + name = nameParts[1]; if (prefix) { prefix = normalize(prefix, parentName, applyMap); - pluginModule = defined[prefix]; + pluginModule = getOwn(defined, prefix); } //Account for relative paths if there is a base name. @@ -466,6 +440,15 @@ var requirejs, require, define; } else { //A regular module. normalizedName = normalize(name, parentName, applyMap); + + //Normalized name may be a plugin ID due to map config + //application in normalize. The map config values must + //already be normalized, so do not need to redo that part. + nameParts = splitPrefix(normalizedName); + prefix = nameParts[0]; + normalizedName = nameParts[1]; + isNormalized = true; + url = context.nameToUrl(normalizedName); } } @@ -493,7 +476,7 @@ var requirejs, require, define; function getModule(depMap) { var id = depMap.id, - mod = registry[id]; + mod = getOwn(registry, id); if (!mod) { mod = registry[id] = new context.Module(depMap); @@ -504,7 +487,7 @@ var requirejs, require, define; function on(depMap, name, fn) { var id = depMap.id, - mod = registry[id]; + mod = getOwn(registry, id); if (hasProp(defined, id) && (!mod || mod.defineEmitComplete)) { @@ -524,7 +507,7 @@ var requirejs, require, define; errback(err); } else { each(ids, function (id) { - var mod = registry[id]; + var mod = getOwn(registry, id); if (mod) { //Set error on module, so it skips timeout checks. mod.error = err; @@ -557,148 +540,71 @@ var requirejs, require, define; } } - /** - * Helper function that creates a require function object to give to - * modules that ask for it as a dependency. It needs to be specific - * per module because of the implication of path mappings that may - * need to be relative to the module name. - */ - function makeRequire(mod, enableBuildCallback, altRequire) { - var relMap = mod && mod.map, - modRequire = makeContextModuleFunc(altRequire || context.require, - relMap, - enableBuildCallback); - - addRequireMethods(modRequire, context, relMap); - modRequire.isBrowser = isBrowser; - - return modRequire; - } - handlers = { 'require': function (mod) { - return makeRequire(mod); + if (mod.require) { + return mod.require; + } else { + return (mod.require = context.makeRequire(mod.map)); + } }, 'exports': function (mod) { mod.usingExports = true; if (mod.map.isDefine) { - return (mod.exports = defined[mod.map.id] = {}); + if (mod.exports) { + return mod.exports; + } else { + return (mod.exports = defined[mod.map.id] = {}); + } } }, 'module': function (mod) { - return (mod.module = { - id: mod.map.id, - uri: mod.map.url, - config: function () { - return (config.config && config.config[mod.map.id]) || {}; - }, - exports: defined[mod.map.id] - }); + if (mod.module) { + return mod.module; + } else { + return (mod.module = { + id: mod.map.id, + uri: mod.map.url, + config: function () { + return (config.config && getOwn(config.config, mod.map.id)) || {}; + }, + exports: defined[mod.map.id] + }); + } } }; - function removeWaiting(id) { + function cleanRegistry(id) { //Clean up machinery used for waiting modules. delete registry[id]; - - each(waitAry, function (mod, i) { - if (mod.map.id === id) { - waitAry.splice(i, 1); - if (!mod.defined) { - context.waitCount -= 1; - } - return true; - } - }); } - function findCycle(mod, traced, processed) { - var id = mod.map.id, - depArray = mod.depMaps, - foundModule; - - //Do not bother with unitialized modules or not yet enabled - //modules. - if (!mod.inited) { - return; - } - - //Found the cycle. - if (traced[id]) { - return mod; - } - - traced[id] = true; - - //Trace through the dependencies. - each(depArray, function (depMap) { - var depId = depMap.id, - depMod = registry[depId]; + function breakCycle(mod, traced, processed) { + var id = mod.map.id; - if (!depMod || processed[depId] || - !depMod.inited || !depMod.enabled) { - return; - } - - return (foundModule = findCycle(depMod, traced, processed)); - }); - - processed[id] = true; - - return foundModule; - } - - function forceExec(mod, traced, uninited) { - var id = mod.map.id, - depArray = mod.depMaps; - - if (!mod.inited || !mod.map.isDefine) { - return; - } - - if (traced[id]) { - return defined[id]; - } - - traced[id] = mod; - - each(depArray, function (depMap) { - var depId = depMap.id, - depMod = registry[depId], - value; - - if (handlers[depId]) { - return; - } - - if (depMod) { - if (!depMod.inited || !depMod.enabled) { - //Dependency is not inited, - //so this module cannot be - //given a forced value yet. - uninited[id] = true; - return; - } - - //Get the value for the current dependency - value = forceExec(depMod, traced, uninited); - - //Even with forcing it may not be done, - //in particular if the module is waiting - //on a plugin resource. - if (!uninited[depId]) { - mod.defineDepById(depId, value); + if (mod.error) { + mod.emit('error', mod.error); + } else { + traced[id] = true; + each(mod.depMaps, function (depMap, i) { + var depId = depMap.id, + dep = getOwn(registry, depId); + + //Only force things that have not completed + //being defined, so still in the registry, + //and only if it has not been matched up + //in the module already. + if (dep && !mod.depMatched[i] && !processed[depId]) { + if (getOwn(traced, depId)) { + mod.defineDep(i, defined[depId]); + mod.check(); //pass false? + } else { + breakCycle(dep, traced, processed); + } } - } - }); - - mod.check(true); - - return defined[id]; - } - - function modCheck(mod) { - mod.check(); + }); + processed[id] = true; + } } function checkLoaded() { @@ -707,6 +613,7 @@ var requirejs, require, define; //It is possible to disable the wait interval by using waitSeconds of 0. expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), noLoads = [], + reqCalls = [], stillLoading = false, needCycleCheck = true; @@ -727,6 +634,10 @@ var requirejs, require, define; return; } + if (!map.isDefine) { + reqCalls.push(mod); + } + if (!mod.error) { //If the module should be executed, and it has not //been inited and time is up, remember it. @@ -761,31 +672,9 @@ var requirejs, require, define; //Not expired, check for a cycle. if (needCycleCheck) { - - each(waitAry, function (mod) { - if (mod.defined) { - return; - } - - var cycleMod = findCycle(mod, {}, {}), - traced = {}; - - if (cycleMod) { - forceExec(cycleMod, traced, {}); - - //traced modules may have been - //removed from the registry, but - //their listeners still need to - //be called. - eachProp(traced, modCheck); - } + each(reqCalls, function (mod) { + breakCycle(mod, {}, {}); }); - - //Now that dependencies have - //been satisfied, trigger the - //completion check that then - //notifies listeners. - eachProp(registry, modCheck); } //If still waiting on loads, and the waiting load is something @@ -806,9 +695,9 @@ var requirejs, require, define; } Module = function (map) { - this.events = undefEvents[map.id] || {}; + this.events = getOwn(undefEvents, map.id) || {}; this.map = map; - this.shim = config.shim[map.id]; + this.shim = getOwn(config.shim, map.id); this.depExports = []; this.depMaps = []; this.depMatched = []; @@ -851,7 +740,6 @@ var requirejs, require, define; //doing a direct modification of the depMaps array //would affect that config. this.depMaps = depMaps && depMaps.slice(0); - this.depMaps.rjsSkipMap = depMaps.rjsSkipMap; this.errback = errback; @@ -873,20 +761,6 @@ var requirejs, require, define; } }, - defineDepById: function (id, depExports) { - var i; - - //Find the index for this dependency. - each(this.depMaps, function (map, index) { - if (map.id === id) { - i = index; - return true; - } - }); - - return this.defineDep(i, depExports); - }, - defineDep: function (i, depExports) { //Because of cycles, defined callback for a given //export can be called more than once. @@ -910,7 +784,9 @@ var requirejs, require, define; //If the manager is for a plugin managed resource, //ask the plugin to load it now. if (this.shim) { - makeRequire(this, true)(this.shim.deps || [], bind(this, function () { + context.makeRequire(this.map, { + enableBuildCallback: true + })(this.shim.deps || [], bind(this, function () { return map.prefix ? this.callPlugin() : this.load(); })); } else { @@ -931,11 +807,9 @@ var requirejs, require, define; /** * Checks is the module is ready to define itself, and if so, - * define it. If the silent argument is true, then it will just - * define, but not notify listeners, and not ask for a context-wide - * check of all loaded modules. That is useful for cycle breaking. + * define it. */ - check: function (silent) { + check: function () { if (!this.enabled || this.enabling) { return; } @@ -1013,11 +887,6 @@ var requirejs, require, define; delete registry[id]; this.defined = true; - context.waitCount -= 1; - if (context.waitCount === 0) { - //Clear the wait array used for cycles. - waitAry = []; - } } //Finished the define stage. Allow calling check again @@ -1025,25 +894,33 @@ var requirejs, require, define; //cycle. this.defining = false; - if (!silent) { - if (this.defined && !this.defineEmitted) { - this.defineEmitted = true; - this.emit('defined', this.exports); - this.defineEmitComplete = true; - } + if (this.defined && !this.defineEmitted) { + this.defineEmitted = true; + this.emit('defined', this.exports); + this.defineEmitComplete = true; } + } }, callPlugin: function () { var map = this.map, id = map.id, - pluginMap = makeModuleMap(map.prefix, null, false, true); + //Map already normalized the prefix. + pluginMap = makeModuleMap(map.prefix); + + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(pluginMap); on(pluginMap, 'defined', bind(this, function (plugin) { var load, normalizedMap, normalizedMod, name = this.map.name, - parentName = this.map.parentMap ? this.map.parentMap.name : null; + parentName = this.map.parentMap ? this.map.parentMap.name : null, + localRequire = context.makeRequire(map.parentMap, { + enableBuildCallback: true, + skipMap: true + }); //If current map is not normalized, wait for that //normalized name to load instead of continuing. @@ -1055,10 +932,10 @@ var requirejs, require, define; }) || ''; } + //prefix and name should already be normalized, no need + //for applying map config again either. normalizedMap = makeModuleMap(map.prefix + '!' + name, - this.map.parentMap, - false, - true); + this.map.parentMap); on(normalizedMap, 'defined', bind(this, function (value) { this.init([], function () { return value; }, null, { @@ -1066,8 +943,13 @@ var requirejs, require, define; ignore: true }); })); - normalizedMod = registry[normalizedMap.id]; + + normalizedMod = getOwn(registry, normalizedMap.id); if (normalizedMod) { + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(normalizedMap); + if (this.events.error) { normalizedMod.on('error', bind(this, function (err) { this.emit('error', err); @@ -1094,7 +976,7 @@ var requirejs, require, define; //since they will never be resolved otherwise now. eachProp(registry, function (mod) { if (mod.map.id.indexOf(id + '_unnormalized') === 0) { - removeWaiting(mod.map.id); + cleanRegistry(mod.map.id); } }); @@ -1103,9 +985,19 @@ var requirejs, require, define; //Allow plugins to load other code without having to know the //context or how to 'complete' the load. - load.fromText = function (moduleName, text) { + load.fromText = bind(this, function (text, textAlt) { /*jslint evil: true */ - var hasInteractive = useInteractive; + var moduleName = map.name, + moduleMap = makeModuleMap(moduleName), + hasInteractive = useInteractive; + + //As of 2.1.0, support just passing the text, to reinforce + //fromText only being called once per resource. Still + //support old style of passing moduleName but discard + //that moduleName in favor of the internal ref. + if (textAlt) { + text = textAlt; + } //Turn off interactive script matching for IE for any define //calls in the text, then turn it back on at the end. @@ -1115,25 +1007,40 @@ var requirejs, require, define; //Prime the system by creating a module instance for //it. - getModule(makeModuleMap(moduleName)); + getModule(moduleMap); + + //Transfer any config to this other module. + if (hasProp(config.config, id)) { + config.config[moduleName] = config.config[id]; + } - req.exec(text); + try { + req.exec(text); + } catch (e) { + throw new Error('fromText eval for ' + moduleName + + ' failed: ' + e); + } if (hasInteractive) { useInteractive = true; } + //Mark this as a dependency for the plugin + //resource + this.depMaps.push(moduleMap); + //Support anonymous modules. context.completeLoad(moduleName); - }; + + //Bind the value of that module to the value for this + //resource ID. + localRequire([moduleName], load); + }); //Use parentName here since the plugin's name is not reliable, //could be some weird string with no path that actually wants to //reference the parentName's path. - plugin.load(map.name, makeRequire(map.parentMap, true, function (deps, cb, er) { - deps.rjsSkipMap = true; - return context.require(deps, cb, er); - }), load, config); + plugin.load(map.name, localRequire, load, config); })); context.enable(pluginMap, this); @@ -1143,12 +1050,6 @@ var requirejs, require, define; enable: function () { this.enabled = true; - if (!this.waitPushed) { - waitAry.push(this); - context.waitCount += 1; - this.waitPushed = true; - } - //Set flag mentioning that the module is enabling, //so that immediate calls to the defined callbacks //for dependencies do not trigger inadvertent load @@ -1165,10 +1066,10 @@ var requirejs, require, define; depMap = makeModuleMap(depMap, (this.map.isDefine ? this.map : this.map.parentMap), false, - !this.depMaps.rjsSkipMap); + !this.skipMap); this.depMaps[i] = depMap; - handler = handlers[depMap.id]; + handler = getOwn(handlers, depMap.id); if (handler) { this.depExports[i] = handler(this); @@ -1193,7 +1094,7 @@ var requirejs, require, define; //Skip special modules like 'require', 'exports', 'module' //Also, don't call enable if it is already enabled, //important in circular dependency cases. - if (!handlers[id] && mod && !mod.enabled) { + if (!hasProp(handlers, id) && mod && !mod.enabled) { context.enable(depMap, this); } })); @@ -1201,7 +1102,7 @@ var requirejs, require, define; //Enable each plugin that is used in //a dependency eachProp(this.pluginMaps, bind(this, function (pluginMap) { - var mod = registry[pluginMap.id]; + var mod = getOwn(registry, pluginMap.id); if (mod && !mod.enabled) { context.enable(pluginMap, this); } @@ -1227,14 +1128,17 @@ var requirejs, require, define; if (name === 'error') { //Now that the error handler was triggered, remove //the listeners, since this broken Module instance - //can stay around for a while in the registry/waitAry. + //can stay around for a while in the registry. delete this.events[name]; } } }; function callGetModule(args) { - getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); + //Skip modules already defined. + if (!hasProp(defined, args[0])) { + getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); + } } function removeListener(node, func, name, ieName) { @@ -1274,16 +1178,35 @@ var requirejs, require, define; }; } - return (context = { + function intakeDefines() { + var args; + + //Any defined modules in the global queue, intake them now. + takeGlobalQueue(); + + //Make sure any remaining defQueue items get properly processed. + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); + } else { + //args are id, deps, factory. Should be normalized by the + //define() function. + callGetModule(args); + } + } + } + + context = { config: config, contextName: contextName, registry: registry, defined: defined, urlFetched: urlFetched, - waitCount: 0, defQueue: defQueue, Module: Module, makeModuleMap: makeModuleMap, + nextTick: req.nextTick, /** * Set a configuration for the context. @@ -1301,20 +1224,23 @@ var requirejs, require, define; //they are additive. var pkgs = config.pkgs, shim = config.shim, - paths = config.paths, - map = config.map; - - //Mix in the config values, favoring the new values over - //existing ones in context.config. - mixin(config, cfg, true); - - //Merge paths. - config.paths = mixin(paths, cfg.paths, true); + objs = { + paths: true, + config: true, + map: true + }; - //Merge map - if (cfg.map) { - config.map = mixin(map || {}, cfg.map, true, true); - } + eachProp(cfg, function (value, prop) { + if (objs[prop]) { + if (prop === 'map') { + mixin(config[prop], value, true, true); + } else { + mixin(config[prop], value, true); + } + } else { + config[prop] = value; + } + }); //Merge shim if (cfg.shim) { @@ -1325,8 +1251,8 @@ var requirejs, require, define; deps: value }; } - if (value.exports && !value.exports.__buildReady) { - value.exports = context.makeShimExports(value.exports); + if ((value.exports || value.init) && !value.exportsFn) { + value.exportsFn = context.makeShimExports(value); } shim[id] = value; }); @@ -1381,125 +1307,144 @@ var requirejs, require, define; } }, - makeShimExports: function (exports) { - var func; - if (typeof exports === 'string') { - func = function () { - return getGlobal(exports); - }; - //Save the exports for use in nodefine checking. - func.exports = exports; - return func; - } else { - return function () { - return exports.apply(global, arguments); - }; + makeShimExports: function (value) { + function fn() { + var ret; + if (value.init) { + ret = value.init.apply(global, arguments); + } + return ret || (value.exports && getGlobal(value.exports)); } + return fn; }, - requireDefined: function (id, relMap) { - return hasProp(defined, makeModuleMap(id, relMap, false, true).id); - }, + makeRequire: function (relMap, options) { + options = options || {}; - requireSpecified: function (id, relMap) { - id = makeModuleMap(id, relMap, false, true).id; - return hasProp(defined, id) || hasProp(registry, id); - }, + function localRequire(deps, callback, errback) { + var id, map, requireMod; - require: function (deps, callback, errback, relMap) { - var moduleName, id, map, requireMod, args; - if (typeof deps === 'string') { - if (isFunction(callback)) { - //Invalid call - return onError(makeError('requireargs', 'Invalid require call'), errback); + if (options.enableBuildCallback && callback && isFunction(callback)) { + callback.__requireJsBuild = true; } - //Synchronous access to one module. If require.get is - //available (as in the Node adapter), prefer that. - //In this case deps is the moduleName and callback is - //the relMap - if (req.get) { - return req.get(context, deps, callback); - } + if (typeof deps === 'string') { + if (isFunction(callback)) { + //Invalid call + return onError(makeError('requireargs', 'Invalid require call'), errback); + } - //Just return the module wanted. In this scenario, the - //second arg (if passed) is just the relMap. - moduleName = deps; - relMap = callback; + //If require|exports|module are requested, get the + //value for them from the special handlers. Caveat: + //this only works while module is being defined. + if (relMap && hasProp(handlers, deps)) { + return handlers[deps](registry[relMap.id]); + } - //Normalize module name, if it contains . or .. - map = makeModuleMap(moduleName, relMap, false, true); - id = map.id; + //Synchronous access to one module. If require.get is + //available (as in the Node adapter), prefer that. + if (req.get) { + return req.get(context, deps, relMap); + } + + //Normalize module name, if it contains . or .. + map = makeModuleMap(deps, relMap, false, true); + id = map.id; - if (!hasProp(defined, id)) { - return onError(makeError('notloaded', 'Module name "' + - id + - '" has not been loaded yet for context: ' + - contextName)); + if (!hasProp(defined, id)) { + return onError(makeError('notloaded', 'Module name "' + + id + + '" has not been loaded yet for context: ' + + contextName + + (relMap ? '' : '. Use require([])'))); + } + return defined[id]; } - return defined[id]; - } - //Callback require. Normalize args. if callback or errback is - //not a function, it means it is a relMap. Test errback first. - if (errback && !isFunction(errback)) { - relMap = errback; - errback = undefined; - } - if (callback && !isFunction(callback)) { - relMap = callback; - callback = undefined; - } + //Grab defines waiting in the global queue. + intakeDefines(); - //Any defined modules in the global queue, intake them now. - takeGlobalQueue(); + //Mark all the dependencies as needing to be loaded. + context.nextTick(function () { + //Some defines could have been added since the + //require call, collect them. + intakeDefines(); - //Make sure any remaining defQueue items get properly processed. - while (defQueue.length) { - args = defQueue.shift(); - if (args[0] === null) { - return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); - } else { - //args are id, deps, factory. Should be normalized by the - //define() function. - callGetModule(args); - } - } + requireMod = getModule(makeModuleMap(null, relMap)); - //Mark all the dependencies as needing to be loaded. - requireMod = getModule(makeModuleMap(null, relMap)); + //Store if map config should be applied to this require + //call for dependencies. + requireMod.skipMap = options.skipMap; - requireMod.init(deps, callback, errback, { - enabled: true - }); + requireMod.init(deps, callback, errback, { + enabled: true + }); - checkLoaded(); + checkLoaded(); + }); - return context.require; - }, + return localRequire; + } - undef: function (id) { - //Bind any waiting define() calls to this context, - //fix for #408 - takeGlobalQueue(); + mixin(localRequire, { + isBrowser: isBrowser, + + /** + * Converts a module name + .extension into an URL path. + * *Requires* the use of a module name. It does not support using + * plain URLs like nameToUrl. + */ + toUrl: function (moduleNamePlusExt) { + var index = moduleNamePlusExt.lastIndexOf('.'), + ext = null; + + if (index !== -1) { + ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); + moduleNamePlusExt = moduleNamePlusExt.substring(0, index); + } - var map = makeModuleMap(id, null, true), - mod = registry[id]; + return context.nameToUrl(normalize(moduleNamePlusExt, + relMap && relMap.id, true), ext); + }, - delete defined[id]; - delete urlFetched[map.url]; - delete undefEvents[id]; + defined: function (id) { + return hasProp(defined, makeModuleMap(id, relMap, false, true).id); + }, - if (mod) { - //Hold on to listeners in case the - //module will be attempted to be reloaded - //using a different config. - if (mod.events.defined) { - undefEvents[id] = mod.events; + specified: function (id) { + id = makeModuleMap(id, relMap, false, true).id; + return hasProp(defined, id) || hasProp(registry, id); } + }); + + //Only allow undef on top level require calls + if (!relMap) { + localRequire.undef = function (id) { + //Bind any waiting define() calls to this context, + //fix for #408 + takeGlobalQueue(); + + var map = makeModuleMap(id, relMap, true), + mod = getOwn(registry, id); + + delete defined[id]; + delete urlFetched[map.url]; + delete undefEvents[id]; + + if (mod) { + //Hold on to listeners in case the + //module will be attempted to be reloaded + //using a different config. + if (mod.events.defined) { + undefEvents[id] = mod.events; + } - removeWaiting(id); + cleanRegistry(id); + } + }; } + + return localRequire; }, /** @@ -1508,7 +1453,7 @@ var requirejs, require, define; * used by the optimizer. */ enable: function (depMap, parent) { - var mod = registry[depMap.id]; + var mod = getOwn(registry, depMap.id); if (mod) { getModule(depMap).enable(); } @@ -1522,8 +1467,8 @@ var requirejs, require, define; */ completeLoad: function (moduleName) { var found, args, mod, - shim = config.shim[moduleName] || {}, - shExports = shim.exports && shim.exports.exports; + shim = getOwn(config.shim, moduleName) || {}, + shExports = shim.exports; takeGlobalQueue(); @@ -1548,9 +1493,9 @@ var requirejs, require, define; //Do this after the cycle of callGetModule in case the result //of those calls/init calls changes the registry. - mod = registry[moduleName]; + mod = getOwn(registry, moduleName); - if (!found && !defined[moduleName] && mod && !mod.inited) { + if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { if (hasPathFallback(moduleName)) { return; @@ -1563,7 +1508,7 @@ var requirejs, require, define; } else { //A script that does not call define(), so just simulate //the call for it. - callGetModule([moduleName, (shim.deps || []), shim.exports]); + callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); } } @@ -1571,24 +1516,6 @@ var requirejs, require, define; }, /** - * Converts a module name + .extension into an URL path. - * *Requires* the use of a module name. It does not support using - * plain URLs like nameToUrl. - */ - toUrl: function (moduleNamePlusExt, relModuleMap) { - var index = moduleNamePlusExt.lastIndexOf('.'), - ext = null; - - if (index !== -1) { - ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); - moduleNamePlusExt = moduleNamePlusExt.substring(0, index); - } - - return context.nameToUrl(normalize(moduleNamePlusExt, relModuleMap && relModuleMap.id, true), - ext); - }, - - /** * Converts a module name to a file path. Supports cases where * moduleName may actually be just an URL. * Note that it **does not** call normalize on the moduleName, @@ -1619,8 +1546,8 @@ var requirejs, require, define; //and work up from it. for (i = syms.length; i > 0; i -= 1) { parentModule = syms.slice(0, i).join('/'); - pkg = pkgs[parentModule]; - parentPath = paths[parentModule]; + pkg = getOwn(pkgs, parentModule); + parentPath = getOwn(paths, parentModule); if (parentPath) { //If an array, it means there are a few choices, //Choose the one that is desired @@ -1701,7 +1628,10 @@ var requirejs, require, define; return onError(makeError('scripterror', 'Script error', evt, [data.id])); } } - }); + }; + + context.require = context.makeRequire(); + return context; } /** @@ -1742,7 +1672,7 @@ var requirejs, require, define; contextName = config.context; } - context = contexts[contextName]; + context = getOwn(contexts, contextName); if (!context) { context = contexts[contextName] = req.s.newContext(contextName); } @@ -1763,6 +1693,16 @@ var requirejs, require, define; }; /** + * Execute something after the current tick + * of the event loop. Override for other envs + * that have a better solution than setTimeout. + * @param {Function} fn function to execute later. + */ + req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { + setTimeout(fn, 4); + } : function (fn) { fn(); }; + + /** * Export require as a global, but only if it does not already exist. */ if (!require) { @@ -1782,9 +1722,21 @@ var requirejs, require, define; //Create default context. req({}); - //Exports some context-sensitive methods on global require, using - //default context if no context specified. - addRequireMethods(req); + //Exports some context-sensitive methods on global require. + each([ + 'toUrl', + 'undef', + 'defined', + 'specified' + ], function (prop) { + //Reference from contexts instead of early binding to default context, + //so that during builds, the latest instance of the default context + //with its config gets used. + req[prop] = function () { + var ctx = contexts[defContextName]; + return ctx.require[prop].apply(ctx, arguments); + }; + }); if (isBrowser) { head = s.head = document.getElementsByTagName('head')[0]; @@ -1962,7 +1914,7 @@ var requirejs, require, define; define = function (name, deps, callback) { var node, context; - //Allow for anonymous functions + //Allow for anonymous modules if (typeof name !== 'string') { //Adjust args appropriately callback = deps; diff --git a/module/web/static/js/views/packageView.js b/module/web/static/js/views/packageView.js index a0de18827..5bc23a7b3 100644 --- a/module/web/static/js/views/packageView.js +++ b/module/web/static/js/views/packageView.js @@ -38,6 +38,7 @@ define(['jquery', 'views/abstract/itemView', 'underscore', 'views/fileView', 'ut ]; var pie = this.$('.package-graph'); pie.peity('pie'); + this.$('canvas').addClass('pull-right'); if (this.model.isLoaded()) { var ul = $('<ul></ul>'); |