You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by me...@apache.org on 2013/04/04 15:22:09 UTC
[01/10] git commit: updated refs/heads/master to 88c3171
Updated Branches:
refs/heads/master 0d01e365d -> 88c3171dc
update backbone.js to v1.0
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/4ed70fef
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/4ed70fef
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/4ed70fef
Branch: refs/heads/master
Commit: 4ed70fefd897e0927928ce2fb314596305099607
Parents: 0d01e36
Author: Simon Metson <si...@cloudant.com>
Authored: Thu Apr 4 13:08:18 2013 +0100
Committer: Simon Metson <si...@cloudant.com>
Committed: Thu Apr 4 13:08:18 2013 +0100
----------------------------------------------------------------------
src/fauxton/assets/js/libs/backbone.js | 1334 ++++++++++++++-------------
1 files changed, 686 insertions(+), 648 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/4ed70fef/src/fauxton/assets/js/libs/backbone.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/backbone.js b/src/fauxton/assets/js/libs/backbone.js
index 90e18d4..3512d42 100644
--- a/src/fauxton/assets/js/libs/backbone.js
+++ b/src/fauxton/assets/js/libs/backbone.js
@@ -1,6 +1,6 @@
-// Backbone.js 0.9.9
+// Backbone.js 1.0.0
-// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
+// (c) 2010-2013 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://backbonejs.org
@@ -18,14 +18,14 @@
// restored later on, if `noConflict` is used.
var previousBackbone = root.Backbone;
- // Create a local reference to array methods.
+ // Create local references to array methods we'll want to use later.
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.
+ // be attached to this. Exported for both the browser and the server.
var Backbone;
if (typeof exports !== 'undefined') {
Backbone = exports;
@@ -34,14 +34,15 @@
}
// Current version of the library. Keep in sync with `package.json`.
- Backbone.VERSION = '0.9.9';
+ Backbone.VERSION = '1.0.0';
// 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.
- Backbone.$ = root.jQuery || root.Zepto || root.ender;
+ // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
+ // the `$` variable.
+ Backbone.$ = root.jQuery || root.Zepto || root.ender || root.$;
// Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
// to its previous owner. Returns a reference to this Backbone object.
@@ -64,45 +65,6 @@
// Backbone.Events
// ---------------
- // 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
@@ -115,37 +77,35 @@
//
var Events = Backbone.Events = {
- // 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.
+ // Bind an event 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;
+ 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});
+ var events = this._events[name] || (this._events[name] = []);
+ events.push({callback: callback, context: context, ctx: context || this});
return this;
},
- // Bind events to only be triggered a single time. After the first time
+ // Bind an event 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;
+ 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;
+ return this.on(name, once, context);
},
// 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 the event. If `name` is null, removes all bound
// callbacks for all events.
off: function(name, callback, context) {
- var list, ev, events, names, i, l, j, k;
+ var retain, ev, events, names, i, l, j, k;
if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
if (!name && !callback && !context) {
this._events = {};
@@ -155,18 +115,18 @@
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 (events = this._events[name]) {
+ this._events[name] = retain = [];
if (callback || context) {
- for (j = 0, k = list.length; j < k; j++) {
- ev = list[j];
- if ((callback && callback !== (ev.callback._callback || ev.callback)) ||
+ for (j = 0, k = events.length; j < k; j++) {
+ ev = events[j];
+ if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
(context && context !== ev.context)) {
- events.push(ev);
+ retain.push(ev);
}
}
}
- this._events[name] = events;
+ if (!retain.length) delete this._events[name];
}
}
@@ -183,39 +143,87 @@
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);
+ if (events) triggerEvents(events, args);
+ if (allEvents) triggerEvents(allEvents, arguments);
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) {
+ stopListening: function(obj, name, 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 = {};
+ if (!listeners) return this;
+ var deleteListener = !name && !callback;
+ if (typeof name === 'object') callback = this;
+ if (obj) (listeners = {})[obj._listenerId] = obj;
+ for (var id in listeners) {
+ listeners[id].off(name, callback, this);
+ if (deleteListener) delete this._listeners[id];
}
return this;
}
+
+ };
+
+ // 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;
+
+ // Handle event maps.
+ if (typeof name === 'object') {
+ for (var key in name) {
+ obj[action].apply(obj, [key, name[key]].concat(rest));
+ }
+ return false;
+ }
+
+ // Handle space separated event names.
+ 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));
+ }
+ return false;
+ }
+
+ return true;
+ };
+
+ // A difficult-to-believe, but optimized internal dispatch function for
+ // triggering events. Tries to keep the usual cases speedy (most internal
+ // Backbone events have 3 arguments).
+ var triggerEvents = function(events, args) {
+ var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
+ 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, a1); return;
+ case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
+ case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
+ default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args);
+ }
};
+ var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
+
+ // Inversion-of-control versions of `on` and `once`. Tell *this* object to
+ // listen to an event in another object ... keeping track of what it's
+ // listening to.
+ _.each(listenMethods, function(implementation, method) {
+ Events[method] = function(obj, name, callback) {
+ var listeners = this._listeners || (this._listeners = {});
+ var id = obj._listenerId || (obj._listenerId = _.uniqueId('l'));
+ listeners[id] = obj;
+ if (typeof name === 'object') callback = this;
+ obj[implementation](name, callback, this);
+ return this;
+ };
+ });
+
// Aliases for backwards compatibility.
Events.bind = Events.on;
Events.unbind = Events.off;
@@ -227,30 +235,41 @@
// Backbone.Model
// --------------
- // Create a new model, with defined attributes. A client id (`cid`)
+ // Backbone **Models** are the basic data object in the framework --
+ // frequently representing a row in a table in a database on your server.
+ // A discrete chunk of data and a bunch of useful, related methods for
+ // performing computations and transformations on that data.
+
+ // Create a new model with the specified attributes. A client id (`cid`)
// is automatically generated and assigned for you.
var Model = Backbone.Model = function(attributes, options) {
var defaults;
var attrs = attributes || {};
+ options || (options = {});
this.cid = _.uniqueId('c');
- this.changed = {};
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);
+ _.extend(this, _.pick(options, modelOptions));
+ if (options.parse) attrs = this.parse(attrs, options) || {};
+ if (defaults = _.result(this, 'defaults')) {
+ attrs = _.defaults({}, attrs, defaults);
+ }
+ this.set(attrs, options);
+ this.changed = {};
this.initialize.apply(this, arguments);
};
+ // A list of options to be attached directly to the model, if provided.
+ var modelOptions = ['url', 'urlRoot', 'collection'];
+
// Attach all inheritable methods to the Model prototype.
_.extend(Model.prototype, Events, {
// A hash of attributes whose current and previous value differ.
changed: null,
+ // The value returned during the last failed validation.
+ validationError: null,
+
// The default name for the JSON `id` attribute is `"id"`. MongoDB and
// CouchDB users may want to set this to `"_id"`.
idAttribute: 'id',
@@ -264,7 +283,8 @@
return _.clone(this.attributes);
},
- // Proxy `Backbone.sync` by default.
+ // Proxy `Backbone.sync` by default -- but override this if you need
+ // custom syncing semantics for *this* particular model.
sync: function() {
return Backbone.sync.apply(this, arguments);
},
@@ -285,76 +305,140 @@
return this.get(attr) != null;
},
- // Set a hash of model attributes on the object, firing `"change"` unless
- // you choose to silence it.
+ // Set a hash of model attributes on the object, firing `"change"`. This is
+ // the core primitive operation of a model, updating the data and notifying
+ // anyone who needs to know about the change in state. The heart of the beast.
set: function(key, val, options) {
- var attr, attrs;
+ var attr, attrs, unset, changes, silent, changing, prev, current;
if (key == null) return this;
// Handle both `"key", value` and `{key: value}` -style arguments.
- if (_.isObject(key)) {
+ if (typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
- // Extract attributes and options.
- var silent = options && options.silent;
- var unset = options && options.unset;
+ options || (options = {});
// Run validation.
if (!this._validate(attrs, options)) return false;
+ // Extract attributes and options.
+ unset = options.unset;
+ silent = options.silent;
+ changes = [];
+ changing = this._changing;
+ this._changing = true;
+
+ if (!changing) {
+ this._previousAttributes = _.clone(this.attributes);
+ this.changed = {};
+ }
+ current = this.attributes, prev = this._previousAttributes;
+
// Check for changes of `id`.
if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
- var now = this.attributes;
-
- // For each `set` attribute...
+ // For each `set` attribute, update or delete the current value.
for (attr in attrs) {
val = attrs[attr];
-
- // Update or delete the current value, and track the change.
- unset ? delete now[attr] : now[attr] = val;
- this._changes.push(attr, val);
+ if (!_.isEqual(current[attr], val)) changes.push(attr);
+ if (!_.isEqual(prev[attr], val)) {
+ this.changed[attr] = val;
+ } else {
+ delete this.changed[attr];
+ }
+ unset ? delete current[attr] : current[attr] = val;
}
- // Signal that the model's state has potentially changed, and we need
- // to recompute the actual changes.
- this._hasComputed = false;
+ // Trigger all relevant attribute changes.
+ if (!silent) {
+ if (changes.length) this._pending = true;
+ for (var i = 0, l = changes.length; i < l; i++) {
+ this.trigger('change:' + changes[i], this, current[changes[i]], options);
+ }
+ }
- // Fire the `"change"` events.
- if (!silent) this.change(options);
+ // You might be wondering why there's a `while` loop here. Changes can
+ // be recursively nested within `"change"` events.
+ if (changing) return this;
+ if (!silent) {
+ while (this._pending) {
+ this._pending = false;
+ this.trigger('change', this, options);
+ }
+ }
+ this._pending = false;
+ this._changing = false;
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.
+ // Remove an attribute from the model, firing `"change"`. `unset` is a noop
+ // if the attribute doesn't exist.
unset: function(attr, 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 all attributes on the model, firing `"change"`.
clear: function(options) {
var attrs = {};
for (var key in this.attributes) attrs[key] = void 0;
return this.set(attrs, _.extend({}, options, {unset: true}));
},
+ // 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 (attr == null) return !_.isEmpty(this.changed);
+ return _.has(this.changed, attr);
+ },
+
+ // Return an object containing all the attributes that have changed, or
+ // false if there are no changed attributes. Useful for determining what
+ // parts of a view need to be updated and/or what attributes need to be
+ // persisted to the server. Unset attributes will be set to undefined.
+ // You can also pass an attributes object to diff against the model,
+ // determining if there *would be* a change.
+ changedAttributes: function(diff) {
+ if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
+ var val, changed = false;
+ var old = this._changing ? this._previousAttributes : this.attributes;
+ for (var attr in diff) {
+ if (_.isEqual(old[attr], (val = diff[attr]))) continue;
+ (changed || (changed = {}))[attr] = val;
+ }
+ return changed;
+ },
+
+ // Get the previous value of an attribute, recorded at the time the last
+ // `"change"` event was fired.
+ previous: function(attr) {
+ if (attr == null || !this._previousAttributes) return null;
+ return this._previousAttributes[attr];
+ },
+
+ // Get all of the attributes of the model at the time of the previous
+ // `"change"` event.
+ previousAttributes: function() {
+ return _.clone(this._previousAttributes);
+ },
+
// Fetch the model from the server. If the server's representation of the
- // model differs from its current attributes, they will be overriden,
+ // model differs from its current attributes, they will be overridden,
// 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), options)) return false;
+ options.success = function(resp) {
+ if (!model.set(model.parse(resp, options), options)) return false;
if (success) success(model, resp, options);
+ model.trigger('sync', model, resp, options);
};
+ wrapError(this, options);
return this.sync('read', this, options);
},
@@ -362,55 +446,53 @@
// If the server returns an attributes hash that differs, the model's
// state will be `set` again.
save: function(key, val, options) {
- var attrs, current, done;
+ var attrs, method, xhr, attributes = this.attributes;
// Handle both `"key", value` and `{key: value}` -style arguments.
- if (key == null || _.isObject(key)) {
+ if (key == null || typeof key === 'object') {
attrs = key;
options = val;
- } else if (key != null) {
+ } else {
(attrs = {})[key] = val;
}
- options = options ? _.clone(options) : {};
- // If we're "wait"-ing to set changed attributes, validate early.
- if (options.wait) {
- if (attrs && !this._validate(attrs, options)) return false;
- current = _.clone(this.attributes);
- }
+ // If we're not waiting and attributes exist, save acts as `set(attr).save(null, opts)`.
+ if (attrs && (!options || !options.wait) && !this.set(attrs, options)) return false;
- // Regular saves `set` attributes before persisting to the server.
- var silentOptions = _.extend({}, options, {silent: true});
- if (attrs && !this.set(attrs, options.wait ? silentOptions : options)) {
- return false;
- }
+ options = _.extend({validate: true}, options);
// Do not persist invalid models.
- if (!attrs && !this._validate(null, options)) return false;
+ if (!this._validate(attrs, options)) return false;
+
+ // Set temporary attributes if `{wait: true}`.
+ if (attrs && options.wait) {
+ this.attributes = _.extend({}, attributes, attrs);
+ }
// After a successful server-side save, the client is (optionally)
// updated with the server-side state.
+ if (options.parse === void 0) options.parse = true;
var model = this;
var success = options.success;
- options.success = function(resp, status, xhr) {
- done = true;
- var serverAttrs = model.parse(resp);
+ options.success = function(resp) {
+ // Ensure attributes are restored during synchronous saves.
+ model.attributes = attributes;
+ var serverAttrs = model.parse(resp, options);
if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
- if (!model.set(serverAttrs, options)) return false;
+ if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
+ return false;
+ }
if (success) success(model, resp, options);
+ model.trigger('sync', model, resp, options);
};
+ wrapError(this, options);
- // Finish configuring and sending the Ajax request.
- var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
- if (method == 'patch') options.attrs = attrs;
- var xhr = this.sync(method, this, options);
+ method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
+ if (method === 'patch') options.attrs = attrs;
+ 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);
- }
+ // Restore attributes.
+ if (attrs && options.wait) this.attributes = attributes;
return xhr;
},
@@ -430,12 +512,14 @@
options.success = function(resp) {
if (options.wait || model.isNew()) destroy();
if (success) success(model, resp, options);
+ if (!model.isNew()) model.trigger('sync', model, resp, options);
};
if (this.isNew()) {
options.success();
return false;
}
+ wrapError(this, options);
var xhr = this.sync('delete', this, options);
if (!options.wait) destroy();
@@ -453,7 +537,7 @@
// **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) {
+ parse: function(resp, options) {
return resp;
},
@@ -467,128 +551,52 @@
return this.id == null;
},
- // Call this method to manually fire a `"change"` event for this model and
- // a `"change:attribute"` event for each changed attribute.
- // Calling this will cause all objects observing the model to update.
- change: function(options) {
- var changing = this._changing;
- this._changing = true;
-
- // Generate the changes to be triggered on the model.
- var triggers = this._computeChanges(true);
-
- 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;
-
- // Trigger a `change` while there have been changes.
- while (this._pending) {
- this._pending = false;
- this.trigger('change', this, options);
- this._previousAttributes = _.clone(this.attributes);
- }
-
- this._changing = false;
- return this;
- },
-
- // 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 (!this._hasComputed) this._computeChanges();
- if (attr == null) return !_.isEmpty(this.changed);
- return _.has(this.changed, attr);
- },
-
- // Return an object containing all the attributes that have changed, or
- // false if there are no changed attributes. Useful for determining what
- // parts of a view need to be updated and/or what attributes need to be
- // persisted to the server. Unset attributes will be set to undefined.
- // You can also pass an attributes object to diff against the model,
- // determining if there *would be* a change.
- changedAttributes: function(diff) {
- if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
- var val, changed = false, old = this._previousAttributes;
- for (var attr in diff) {
- if (_.isEqual(old[attr], (val = diff[attr]))) continue;
- (changed || (changed = {}))[attr] = val;
- }
- 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 (attr == null || !this._previousAttributes) return null;
- return this._previousAttributes[attr];
- },
-
- // Get all of the attributes of the model at the time of the previous
- // `"change"` event.
- previousAttributes: function() {
- return _.clone(this._previousAttributes);
+ // Check if the model is currently in a valid state.
+ isValid: function(options) {
+ return this._validate({}, _.extend(options || {}, { validate: true }));
},
// 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.
+ // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
_validate: function(attrs, options) {
- if (!this.validate) return true;
+ if (!options.validate || !this.validate) return true;
attrs = _.extend({}, this.attributes, attrs);
- var error = this.validate(attrs, options);
+ var error = this.validationError = this.validate(attrs, options) || null;
if (!error) return true;
- if (options && options.error) options.error(this, error, options);
- this.trigger('error', this, error, options);
+ this.trigger('invalid', this, error, _.extend(options || {}, {validationError: error}));
return false;
}
});
+ // Underscore methods that we want to implement on the Model.
+ var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];
+
+ // Mix in each Underscore method as a proxy to `Model#attributes`.
+ _.each(modelMethods, function(method) {
+ Model.prototype[method] = function() {
+ var args = slice.call(arguments);
+ args.unshift(this.attributes);
+ return _[method].apply(_, args);
+ };
+ });
+
// Backbone.Collection
// -------------------
- // Provides a standard collection class for our sets of models, ordered
- // or unordered. If a `comparator` is specified, the Collection will maintain
+ // If models tend to represent a single row of data, a Backbone Collection is
+ // more analagous to a table full of data ... or a small slice or page of that
+ // table, or a collection of rows that belong together for a particular reason
+ // -- all of the messages in this particular folder, all of the documents
+ // belonging to this particular author, and so on. Collections maintain
+ // indexes of their models, both in order, and for lookup by `id`.
+
+ // Create a new **Collection**, perhaps to contain a specific type of `model`.
+ // If a `comparator` is specified, the Collection will maintain
// its models in sort order, as they're added and removed.
var Collection = Backbone.Collection = function(models, options) {
options || (options = {});
+ if (options.url) this.url = options.url;
if (options.model) this.model = options.model;
if (options.comparator !== void 0) this.comparator = options.comparator;
this._reset();
@@ -596,6 +604,10 @@
if (models) this.reset(models, _.extend({silent: true}, options));
};
+ // Default options for `Collection#set`.
+ var setOptions = {add: true, remove: true, merge: true};
+ var addOptions = {add: true, merge: false, remove: false};
+
// Define the Collection's inheritable methods.
_.extend(Collection.prototype, Events, {
@@ -618,83 +630,120 @@
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 a model, or list of models to the set.
add: function(models, options) {
- var i, args, length, model, existing, needsSort;
- var at = options && options.at;
- var sort = ((options && options.sort) == null ? true : options.sort);
+ return this.set(models, _.defaults(options || {}, addOptions));
+ },
+
+ // Remove a model, or a list of models from the set.
+ remove: function(models, options) {
models = _.isArray(models) ? models.slice() : [models];
+ options || (options = {});
+ var i, l, index, model;
+ for (i = 0, l = models.length; i < l; i++) {
+ model = this.get(models[i]);
+ if (!model) continue;
+ delete this._byId[model.id];
+ delete this._byId[model.cid];
+ index = this.indexOf(model);
+ this.models.splice(index, 1);
+ this.length--;
+ if (!options.silent) {
+ options.index = index;
+ model.trigger('remove', model, this, options);
+ }
+ this._removeReference(model);
+ }
+ return this;
+ },
+
+ // Update a collection by `set`-ing a new list of models, adding new ones,
+ // removing models that are no longer present, and merging models that
+ // already exist in the collection, as necessary. Similar to **Model#set**,
+ // the core operation for updating the data contained by the collection.
+ set: function(models, options) {
+ options = _.defaults(options || {}, setOptions);
+ if (options.parse) models = this.parse(models, options);
+ if (!_.isArray(models)) models = models ? [models] : [];
+ var i, l, model, attrs, existing, sort;
+ var at = options.at;
+ var sortable = this.comparator && (at == null) && options.sort !== false;
+ var sortAttr = _.isString(this.comparator) ? this.comparator : null;
+ var toAdd = [], toRemove = [], modelMap = {};
// 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;
- }
- models[i] = model;
+ for (i = 0, l = models.length; i < l; i++) {
+ if (!(model = this._prepareModel(models[i], options))) continue;
- 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) {
+ if (existing = this.get(model)) {
+ if (options.remove) modelMap[existing.cid] = true;
+ if (options.merge) {
existing.set(model.attributes, options);
- needsSort = sort;
+ if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
}
- models.splice(i, 1);
- continue;
+
+ // This is a new model, push it to the `toAdd` list.
+ } else if (options.add) {
+ toAdd.push(model);
+
+ // Listen to added models' events, and index models for lookup by
+ // `id` and by `cid`.
+ model.on('all', this._onModelEvent, this);
+ this._byId[model.cid] = model;
+ if (model.id != null) this._byId[model.id] = model;
}
+ }
- // 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;
+ // Remove nonexistent models if appropriate.
+ if (options.remove) {
+ for (i = 0, l = this.length; i < l; ++i) {
+ if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
+ }
+ if (toRemove.length) this.remove(toRemove, options);
}
// 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);
+ if (toAdd.length) {
+ if (sortable) sort = true;
+ this.length += toAdd.length;
+ if (at != null) {
+ splice.apply(this.models, [at, 0].concat(toAdd));
+ } else {
+ push.apply(this.models, toAdd);
+ }
+ }
- // Sort the collection if appropriate.
- if (needsSort && this.comparator && at == null) this.sort({silent: true});
+ // Silently sort the collection if appropriate.
+ if (sort) this.sort({silent: true});
- if (options && options.silent) return this;
+ if (options.silent) return this;
// Trigger `add` events.
- while (model = models.shift()) {
- model.trigger('add', model, this, options);
+ for (i = 0, l = toAdd.length; i < l; i++) {
+ (model = toAdd[i]).trigger('add', model, this, options);
}
+ // Trigger `sort` if the collection was sorted.
+ if (sort) this.trigger('sort', this, options);
return this;
},
- // Remove a model, or a list of models from the set. Pass silent to avoid
- // firing the `remove` event for every model removed.
- remove: function(models, options) {
- var i, l, index, model;
+ // 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 granular `add` or `remove` events. Fires `reset` when finished.
+ // Useful for bulk operations and optimizations.
+ reset: function(models, options) {
options || (options = {});
- models = _.isArray(models) ? models.slice() : [models];
- for (i = 0, l = models.length; i < l; i++) {
- model = this.get(models[i]);
- if (!model) continue;
- delete this._byId[model.id];
- delete this._byCid[model.cid];
- index = this.indexOf(model);
- this.models.splice(index, 1);
- this.length--;
- if (!options.silent) {
- options.index = index;
- model.trigger('remove', model, this, options);
- }
- this._removeReference(model);
+ 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 (!options.silent) this.trigger('reset', this, options);
return this;
},
@@ -734,7 +783,7 @@
// 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];
+ return this._byId[obj.id != null ? obj.id : obj.cid || obj];
},
// Get the model at the given index.
@@ -742,10 +791,11 @@
return this.models[index];
},
- // Return models with matching attributes. Useful for simple cases of `filter`.
- where: function(attrs) {
- if (_.isEmpty(attrs)) return [];
- return this.filter(function(model) {
+ // Return models with matching attributes. Useful for simple cases of
+ // `filter`.
+ where: function(attrs, first) {
+ if (_.isEmpty(attrs)) return first ? void 0 : [];
+ return this[first ? 'find' : 'filter'](function(model) {
for (var key in attrs) {
if (attrs[key] !== model.get(key)) return false;
}
@@ -753,95 +803,60 @@
});
},
+ // Return the first model with matching attributes. Useful for simple cases
+ // of `find`.
+ findWhere: function(attrs) {
+ return this.where(attrs, true);
+ },
+
// Force the collection to re-sort itself. You don't need to call this under
// normal circumstances, as the set will maintain sort order as each item
// is added.
sort: function(options) {
- if (!this.comparator) {
- throw new Error('Cannot sort a set without a comparator');
- }
+ if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
+ options || (options = {});
+ // Run sort based on type of `comparator`.
if (_.isString(this.comparator) || this.comparator.length === 1) {
this.models = this.sortBy(this.comparator, this);
} else {
this.models.sort(_.bind(this.comparator, this));
}
- if (!options || !options.silent) this.trigger('sort', this, options);
+ if (!options.silent) this.trigger('sort', this, options);
return this;
},
+ // Figure out the smallest index at which a model should be inserted so as
+ // to maintain order.
+ sortedIndex: function(model, value, context) {
+ value || (value = this.comparator);
+ var iterator = _.isFunction(value) ? value : function(model) {
+ return model.get(value);
+ };
+ return _.sortedIndex(this.models, model, iterator, context);
+ },
+
// Pluck an attribute from each model in the collection.
pluck: function(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) {
- 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();
- if (models) this.add(models, _.extend({silent: true}, options));
- if (!options.silent) this.trigger('reset', this, options);
- return this;
- },
-
// Fetch the default set of models for this collection, resetting the
- // collection when they arrive. If `add: true` is passed, appends the
- // models to the collection instead of resetting.
+ // collection when they arrive. If `reset: true` is passed, the response
+ // data will be passed through the `reset` method instead of `set`.
fetch: function(options) {
options = options ? _.clone(options) : {};
if (options.parse === void 0) options.parse = true;
- var collection = this;
var success = options.success;
- options.success = function(resp, status, xhr) {
- var method = options.update ? 'update' : 'reset';
+ var collection = this;
+ options.success = function(resp) {
+ var method = options.reset ? 'reset' : 'set';
collection[method](resp, options);
if (success) success(collection, resp, options);
+ collection.trigger('sync', collection, resp, options);
};
+ wrapError(this, options);
return this.sync('read', this, options);
},
@@ -849,13 +864,12 @@
// collection immediately, unless `wait: true` is passed, in which case we
// wait for the server to agree.
create: function(model, options) {
- var collection = this;
options = options ? _.clone(options) : {};
- model = this._prepareModel(model, options);
- if (!model) return false;
- if (!options.wait) collection.add(model, options);
+ if (!(model = this._prepareModel(model, options))) return false;
+ if (!options.wait) this.add(model, options);
+ var collection = this;
var success = options.success;
- options.success = function(model, resp, options) {
+ options.success = function(resp) {
if (options.wait) collection.add(model, options);
if (success) success(model, resp, options);
};
@@ -865,7 +879,7 @@
// **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) {
+ parse: function(resp, options) {
return resp;
},
@@ -874,22 +888,16 @@
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() {
- return _(this.models).chain();
- },
-
- // Reset all internal state. Called when the collection is reset.
+ // Private method to reset all internal state. Called when the collection
+ // is first initialized or reset.
_reset: function() {
this.length = 0;
this.models = [];
this._byId = {};
- this._byCid = {};
},
- // Prepare a model or hash of attributes to be added to this collection.
+ // Prepare a hash of attributes (or other model) to be added to this
+ // collection.
_prepareModel: function(attrs, options) {
if (attrs instanceof Model) {
if (!attrs.collection) attrs.collection = this;
@@ -898,11 +906,14 @@
options || (options = {});
options.collection = this;
var model = new this.model(attrs, options);
- if (!model._validate(attrs, options)) return false;
+ if (!model._validate(attrs, options)) {
+ this.trigger('invalid', this, attrs, options);
+ return false;
+ }
return model;
},
- // Internal method to remove a model's ties to a collection.
+ // Internal method to sever a model's ties to a collection.
_removeReference: function(model) {
if (this === model.collection) delete model.collection;
model.off('all', this._onModelEvent, this);
@@ -925,12 +936,14 @@
});
// Underscore methods that we want to implement on the Collection.
+ // 90% of the core usefulness of Backbone Collections is actually implemented
+ // right here:
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'];
+ 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
+ 'tail', 'drop', 'last', 'without', 'indexOf', 'shuffle', 'lastIndexOf',
+ 'isEmpty', 'chain'];
// Mix in each Underscore method as a proxy to `Collection#models`.
_.each(methods, function(method) {
@@ -954,6 +967,241 @@
};
});
+ // Backbone.View
+ // -------------
+
+ // Backbone Views are almost more convention than they are actual code. A View
+ // is simply a JavaScript object that represents a logical chunk of UI in the
+ // DOM. This might be a single item, an entire list, a sidebar or panel, or
+ // even the surrounding frame which wraps your whole app. Defining a chunk of
+ // UI as a **View** allows you to define your DOM events declaratively, without
+ // having to worry about render order ... and makes it easy for the view to
+ // react to specific changes in the state of your models.
+
+ // Creating a Backbone.View creates its initial element outside of the DOM,
+ // if an existing element is not provided...
+ var View = Backbone.View = function(options) {
+ this.cid = _.uniqueId('view');
+ this._configure(options || {});
+ this._ensureElement();
+ this.initialize.apply(this, arguments);
+ this.delegateEvents();
+ };
+
+ // Cached regex to split keys for `delegate`.
+ var delegateEventSplitter = /^(\S+)\s*(.*)$/;
+
+ // List of view options to be merged as properties.
+ var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
+
+ // Set up all inheritable **Backbone.View** properties and methods.
+ _.extend(View.prototype, Events, {
+
+ // The default `tagName` of a View's element is `"div"`.
+ tagName: 'div',
+
+ // jQuery delegate for element lookup, scoped to DOM elements within the
+ // current view. This should be prefered to global lookups where possible.
+ $: function(selector) {
+ return this.$el.find(selector);
+ },
+
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize: function(){},
+
+ // **render** is the core function that your view should override, in order
+ // to populate its element (`this.el`), with the appropriate HTML. The
+ // convention is for **render** to always return `this`.
+ render: function() {
+ return this;
+ },
+
+ // 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;
+ },
+
+ // Change the view's element (`this.el` property), including event
+ // re-delegation.
+ setElement: function(element, delegate) {
+ if (this.$el) this.undelegateEvents();
+ this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
+ this.el = this.$el[0];
+ if (delegate !== false) this.delegateEvents();
+ return this;
+ },
+
+ // Set callbacks, where `this.events` is a hash of
+ //
+ // *{"event selector": "callback"}*
+ //
+ // {
+ // 'mousedown .title': 'edit',
+ // 'click .button': 'save'
+ // 'click .open': function(e) { ... }
+ // }
+ //
+ // pairs. Callbacks will be bound to the view, with `this` set properly.
+ // Uses event delegation for efficiency.
+ // Omitting the selector binds the event to `this.el`.
+ // 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 = _.result(this, 'events')))) return this;
+ this.undelegateEvents();
+ for (var key in events) {
+ var method = events[key];
+ if (!_.isFunction(method)) method = this[events[key]];
+ if (!method) continue;
+
+ var match = key.match(delegateEventSplitter);
+ var eventName = match[1], selector = match[2];
+ method = _.bind(method, this);
+ eventName += '.delegateEvents' + this.cid;
+ if (selector === '') {
+ this.$el.on(eventName, method);
+ } else {
+ this.$el.on(eventName, selector, method);
+ }
+ }
+ return this;
+ },
+
+ // Clears all callbacks previously bound to the view with `delegateEvents`.
+ // You usually don't need to use this, but may wish to if you have multiple
+ // Backbone views attached to the same DOM element.
+ undelegateEvents: function() {
+ this.$el.off('.delegateEvents' + this.cid);
+ return this;
+ },
+
+ // Performs the initial configuration of a View with a set of options.
+ // Keys with special meaning *(e.g. model, collection, id, className)* are
+ // attached directly to the view. See `viewOptions` for an exhaustive
+ // list.
+ _configure: function(options) {
+ if (this.options) options = _.extend({}, _.result(this, 'options'), options);
+ _.extend(this, _.pick(options, viewOptions));
+ this.options = options;
+ },
+
+ // Ensure that the View has a DOM element to render into.
+ // If `this.el` is a string, pass it through `$()`, take the first
+ // matching element, and re-assign it to `el`. Otherwise, create
+ // an element from the `id`, `className` and `tagName` properties.
+ _ensureElement: function() {
+ if (!this.el) {
+ var attrs = _.extend({}, _.result(this, 'attributes'));
+ if (this.id) attrs.id = _.result(this, 'id');
+ if (this.className) attrs['class'] = _.result(this, 'className');
+ var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
+ this.setElement($el, false);
+ } else {
+ this.setElement(_.result(this, 'el'), false);
+ }
+ }
+
+ });
+
+ // Backbone.sync
+ // -------------
+
+ // Override this function to change the manner in which Backbone persists
+ // models to the server. You will be passed the type of request, and the
+ // model in question. By default, makes a RESTful Ajax request
+ // to the model's `url()`. Some possible customizations could be:
+ //
+ // * Use `setTimeout` to batch rapid-fire updates into a single request.
+ // * Send up the models as XML instead of JSON.
+ // * Persist models via WebSockets instead of Ajax.
+ //
+ // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
+ // as `POST`, with a `_method` parameter containing the true HTTP method,
+ // as well as all requests with the body as `application/x-www-form-urlencoded`
+ // instead of `application/json` with the model in a param named `model`.
+ // Useful when interfacing with server-side languages like **PHP** that make
+ // it difficult to read the body of `PUT` requests.
+ Backbone.sync = function(method, model, options) {
+ var type = methodMap[method];
+
+ // Default options, unless specified.
+ _.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 = _.result(model, 'url') || urlError();
+ }
+
+ // Ensure that we have the appropriate request data.
+ if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
+ params.contentType = 'application/json';
+ params.data = JSON.stringify(options.attrs || model.toJSON(options));
+ }
+
+ // For older servers, emulate JSON by encoding the request into an HTML-form.
+ 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 (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' && !options.emulateJSON) {
+ params.processData = false;
+ }
+
+ // If we're sending a `PATCH` request, and we're in an old Internet Explorer
+ // that still has ActiveX enabled by default, override jQuery to use that
+ // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
+ if (params.type === 'PATCH' && window.ActiveXObject &&
+ !(window.external && window.external.msActiveXFilteringEnabled)) {
+ params.xhr = function() {
+ return new ActiveXObject("Microsoft.XMLHTTP");
+ };
+ }
+
+ // Make the request, allowing the user to override any Ajax options.
+ var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
+ model.trigger('request', model, xhr, options);
+ return xhr;
+ };
+
+ // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
+ var methodMap = {
+ 'create': 'POST',
+ 'update': 'PUT',
+ 'patch': 'PATCH',
+ 'delete': 'DELETE',
+ 'read': 'GET'
+ };
+
+ // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
+ // Override this if you'd like to use a different library.
+ Backbone.ajax = function() {
+ return Backbone.$.ajax.apply(Backbone.$, arguments);
+ };
+
// Backbone.Router
// ---------------
@@ -969,7 +1217,7 @@
// Cached regular expressions for matching named param parts and splatted
// parts of route strings.
var optionalParam = /\((.*?)\)/g;
- var namedParam = /:\w+/g;
+ var namedParam = /(\(\?)?:\w+/g;
var splatParam = /\*\w+/g;
var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
@@ -988,13 +1236,19 @@
//
route: function(route, name, callback) {
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
+ if (_.isFunction(name)) {
+ callback = name;
+ name = '';
+ }
if (!callback) callback = this[name];
- Backbone.history.route(route, _.bind(function(fragment) {
- var args = this._extractParameters(route, fragment);
- callback && callback.apply(this, args);
- this.trigger.apply(this, ['route:' + name].concat(args));
- Backbone.history.trigger('route', this, name, args);
- }, this));
+ var router = this;
+ Backbone.history.route(route, function(fragment) {
+ var args = router._extractParameters(route, fragment);
+ callback && callback.apply(router, args);
+ router.trigger.apply(router, ['route:' + name].concat(args));
+ router.trigger('route', name, args);
+ Backbone.history.trigger('route', router, name, args);
+ });
return this;
},
@@ -1009,6 +1263,7 @@
// routes can be defined at the bottom of the route map.
_bindRoutes: function() {
if (!this.routes) return;
+ this.routes = _.result(this, 'routes');
var route, routes = _.keys(this.routes);
while ((route = routes.pop()) != null) {
this.route(route, this.routes[route]);
@@ -1020,15 +1275,21 @@
_routeToRegExp: function(route) {
route = route.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?')
- .replace(namedParam, '([^\/]+)')
+ .replace(namedParam, function(match, optional){
+ return optional ? match : '([^\/]+)';
+ })
.replace(splatParam, '(.*?)');
return new RegExp('^' + route + '$');
},
// Given a route, and a URL fragment that it matches, return the array of
- // extracted parameters.
+ // extracted decoded parameters. Empty or unmatched parameters will be
+ // treated as `null` to normalize cross-browser behavior.
_extractParameters: function(route, fragment) {
- return route.exec(fragment).slice(1);
+ var params = route.exec(fragment).slice(1);
+ return _.map(params, function(param) {
+ return param ? decodeURIComponent(param) : null;
+ });
}
});
@@ -1036,8 +1297,11 @@
// Backbone.History
// ----------------
- // Handles cross-browser history management, based on URL fragments. If the
- // browser does not support `onhashchange`, falls back to polling.
+ // Handles cross-browser history management, based on either
+ // [pushState](http://diveintohtml5.info/history.html) and real URLs, or
+ // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
+ // and URL fragments. If the browser supports neither (old IE, natch),
+ // falls back to polling.
var History = Backbone.History = function() {
this.handlers = [];
_.bindAll(this, 'checkUrl');
@@ -1121,9 +1385,9 @@
// Depending on whether we're using pushState or hashes, and whether
// 'onhashchange' is supported, determine how we check the URL state.
if (this._hasPushState) {
- Backbone.$(window).bind('popstate', this.checkUrl);
+ Backbone.$(window).on('popstate', this.checkUrl);
} else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
- Backbone.$(window).bind('hashchange', this.checkUrl);
+ Backbone.$(window).on('hashchange', this.checkUrl);
} else if (this._wantsHashChange) {
this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
}
@@ -1155,7 +1419,7 @@
// Disable Backbone.history, perhaps temporarily. Not useful in a real app,
// but possibly useful for unit testing Routers.
stop: function() {
- Backbone.$(window).unbind('popstate', this.checkUrl).unbind('hashchange', this.checkUrl);
+ Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
clearInterval(this._checkUrlInterval);
History.started = false;
},
@@ -1248,241 +1512,6 @@
// Create the default Backbone.history.
Backbone.history = new History;
- // Backbone.View
- // -------------
-
- // Creating a Backbone.View creates its initial element outside of the DOM,
- // if an existing element is not provided...
- var View = Backbone.View = function(options) {
- this.cid = _.uniqueId('view');
- this._configure(options || {});
- this._ensureElement();
- this.initialize.apply(this, arguments);
- this.delegateEvents();
- };
-
- // Cached regex to split keys for `delegate`.
- var delegateEventSplitter = /^(\S+)\s*(.*)$/;
-
- // List of view options to be merged as properties.
- var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
-
- // Set up all inheritable **Backbone.View** properties and methods.
- _.extend(View.prototype, Events, {
-
- // The default `tagName` of a View's element is `"div"`.
- tagName: 'div',
-
- // jQuery delegate for element lookup, scoped to DOM elements within the
- // current view. This should be prefered to global lookups where possible.
- $: function(selector) {
- return this.$el.find(selector);
- },
-
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize: function(){},
-
- // **render** is the core function that your view should override, in order
- // to populate its element (`this.el`), with the appropriate HTML. The
- // convention is for **render** to always return `this`.
- render: function() {
- return this;
- },
-
- // 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;
- },
-
- // For small amounts of DOM Elements, where a full-blown template isn't
- // needed, use **make** to manufacture elements, one at a time.
- //
- // var el = this.make('li', {'class': 'row'}, this.model.escape('title'));
- //
- make: function(tagName, attributes, content) {
- var el = document.createElement(tagName);
- if (attributes) Backbone.$(el).attr(attributes);
- if (content != null) Backbone.$(el).html(content);
- return el;
- },
-
- // Change the view's element (`this.el` property), including event
- // re-delegation.
- setElement: function(element, delegate) {
- if (this.$el) this.undelegateEvents();
- this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
- this.el = this.$el[0];
- if (delegate !== false) this.delegateEvents();
- return this;
- },
-
- // Set callbacks, where `this.events` is a hash of
- //
- // *{"event selector": "callback"}*
- //
- // {
- // 'mousedown .title': 'edit',
- // 'click .button': 'save'
- // 'click .open': function(e) { ... }
- // }
- //
- // pairs. Callbacks will be bound to the view, with `this` set properly.
- // Uses event delegation for efficiency.
- // Omitting the selector binds the event to `this.el`.
- // 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 = _.result(this, 'events')))) return;
- this.undelegateEvents();
- for (var key in events) {
- var method = events[key];
- if (!_.isFunction(method)) method = this[events[key]];
- if (!method) throw new Error('Method "' + events[key] + '" does not exist');
- var match = key.match(delegateEventSplitter);
- var eventName = match[1], selector = match[2];
- method = _.bind(method, this);
- eventName += '.delegateEvents' + this.cid;
- if (selector === '') {
- this.$el.bind(eventName, method);
- } else {
- this.$el.delegate(selector, eventName, method);
- }
- }
- },
-
- // Clears all callbacks previously bound to the view with `delegateEvents`.
- // You usually don't need to use this, but may wish to if you have multiple
- // Backbone views attached to the same DOM element.
- undelegateEvents: function() {
- this.$el.unbind('.delegateEvents' + this.cid);
- },
-
- // Performs the initial configuration of a View with a set of options.
- // Keys with special meaning *(model, collection, id, className)*, are
- // attached directly to the view.
- _configure: function(options) {
- if (this.options) options = _.extend({}, _.result(this, 'options'), options);
- _.extend(this, _.pick(options, viewOptions));
- this.options = options;
- },
-
- // Ensure that the View has a DOM element to render into.
- // If `this.el` is a string, pass it through `$()`, take the first
- // matching element, and re-assign it to `el`. Otherwise, create
- // an element from the `id`, `className` and `tagName` properties.
- _ensureElement: function() {
- if (!this.el) {
- 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(_.result(this, 'el'), false);
- }
- }
-
- });
-
- // Backbone.sync
- // -------------
-
- // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
- var methodMap = {
- 'create': 'POST',
- 'update': 'PUT',
- 'patch': 'PATCH',
- 'delete': 'DELETE',
- 'read': 'GET'
- };
-
- // Override this function to change the manner in which Backbone persists
- // models to the server. You will be passed the type of request, and the
- // model in question. By default, makes a RESTful Ajax request
- // to the model's `url()`. Some possible customizations could be:
- //
- // * Use `setTimeout` to batch rapid-fire updates into a single request.
- // * Send up the models as XML instead of JSON.
- // * Persist models via WebSockets instead of Ajax.
- //
- // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
- // as `POST`, with a `_method` parameter containing the true HTTP method,
- // as well as all requests with the body as `application/x-www-form-urlencoded`
- // instead of `application/json` with the model in a param named `model`.
- // Useful when interfacing with server-side languages like **PHP** that make
- // it difficult to read the body of `PUT` requests.
- Backbone.sync = function(method, model, options) {
- var type = methodMap[method];
-
- // Default options, unless specified.
- _.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 = _.result(model, 'url') || urlError();
- }
-
- // Ensure that we have the appropriate request data.
- if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
- params.contentType = 'application/json';
- params.data = JSON.stringify(options.attrs || model.toJSON(options));
- }
-
- // For older servers, emulate JSON by encoding the request into an HTML-form.
- 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 (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' && !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.
- var xhr = Backbone.ajax(_.extend(params, options));
- model.trigger('request', model, xhr, options);
- return xhr;
- };
-
- // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
- Backbone.ajax = function() {
- return Backbone.$.ajax.apply(Backbone.$, arguments);
- };
-
// Helpers
// -------
@@ -1499,7 +1528,7 @@
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
- child = function(){ parent.apply(this, arguments); };
+ child = function(){ return parent.apply(this, arguments); };
}
// Add static properties to the constructor function, if supplied.
@@ -1530,4 +1559,13 @@
throw new Error('A "url" property or function must be specified');
};
+ // Wrap an optional error callback with a fallback error event.
+ var wrapError = function (model, options) {
+ var error = options.error;
+ options.error = function(resp) {
+ if (error) error(model, resp, options);
+ model.trigger('error', model, resp, options);
+ };
+ };
+
}).call(this);
[05/10] git commit: updated refs/heads/master to 88c3171
Posted by me...@apache.org.
update backbone.layoutmanager.js to v0.8.7
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/cca20153
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/cca20153
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/cca20153
Branch: refs/heads/master
Commit: cca2015397c065d59e0cf99d64ad19deae91313f
Parents: 4ed70fe
Author: Simon Metson <si...@cloudant.com>
Authored: Thu Apr 4 13:08:19 2013 +0100
Committer: Simon Metson <si...@cloudant.com>
Committed: Thu Apr 4 13:08:19 2013 +0100
----------------------------------------------------------------------
.../assets/js/plugins/backbone.layoutmanager.js | 512 +++++++++------
1 files changed, 303 insertions(+), 209 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/cca20153/src/fauxton/assets/js/plugins/backbone.layoutmanager.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/plugins/backbone.layoutmanager.js b/src/fauxton/assets/js/plugins/backbone.layoutmanager.js
index 201895d..e578c94 100644
--- a/src/fauxton/assets/js/plugins/backbone.layoutmanager.js
+++ b/src/fauxton/assets/js/plugins/backbone.layoutmanager.js
@@ -1,6 +1,6 @@
/*!
- * backbone.layoutmanager.js v0.7.2
- * Copyright 2012, Tim Branyen (@tbranyen)
+ * backbone.layoutmanager.js v0.8.7
+ * Copyright 2013, Tim Branyen (@tbranyen)
* backbone.layoutmanager.js may be freely distributed under the MIT license.
*/
(function(window) {
@@ -14,7 +14,11 @@ var keys;
// Localize global dependency references.
var Backbone = window.Backbone;
var _ = window._;
-var $ = window.$;
+var $ = Backbone.$;
+
+// Used for issuing warnings and debugging.
+var warn = window.console && window.console.warn;
+var trace = window.console && window.console.trace;
// Maintain references to the two `Backbone.View` functions that are
// overwritten so that they can be proxied.
@@ -41,7 +45,7 @@ var LayoutManager = Backbone.View.extend({
Backbone.View.call(this, options);
},
- // Shorthand to `setView` function with the `append` flag set.
+ // Shorthand to `setView` function with the `insert` flag set.
insertView: function(selector, view) {
// If the `view` argument exists, then a selector was passed in. This code
// path will forward the selector on to `setView`.
@@ -55,12 +59,12 @@ var LayoutManager = Backbone.View.extend({
},
// Iterate over an object and ensure every value is wrapped in an array to
- // ensure they will be appended, then pass that object to `setViews`.
+ // ensure they will be inserted, then pass that object to `setViews`.
insertViews: function(views) {
// If an array of views was passed it should be inserted into the
- // root view. Much like calling insertView without a selector
+ // root view. Much like calling insertView without a selector.
if (_.isArray(views)) {
- return this.setViews({'': views});
+ return this.setViews({ "": views });
}
_.each(views, function(view, selector) {
@@ -72,11 +76,19 @@ var LayoutManager = Backbone.View.extend({
// Returns the View that matches the `getViews` filter function.
getView: function(fn) {
+ // If `getView` is invoked with undefined as the first argument, then the
+ // second argument will be used instead. This is to allow
+ // `getViews(undefined, fn)` to work as `getViews(fn)`. Useful for when
+ // you are allowing an optional selector.
+ if (fn == null) {
+ fn = arguments[1];
+ }
+
return this.getViews(fn).first().value();
},
// Provide a filter function to get a flattened array of all the subviews.
- // If the filter function is omitted it will return all subviews. If a
+ // If the filter function is omitted it will return all subviews. If a
// String is passed instead, it will return the Views for that selector.
getViews: function(fn) {
// Generate an array of all top level (no deeply nested) Views flattened.
@@ -90,11 +102,26 @@ var LayoutManager = Backbone.View.extend({
return _.chain([this.views[fn]]).flatten();
}
+ // If the argument passed is an Object, then pass it to `_.where`.
+ if (typeof fn === "object") {
+ return _.chain([_.where(views, fn)]).flatten();
+ }
+
// If a filter function is provided, run it on all Views and return a
// wrapped chain. Otherwise, simply return a wrapped chain of all Views.
return _.chain(typeof fn === "function" ? _.filter(views, fn) : views);
},
+ // Use this to remove Views, internally uses `getViews` so you can pass the
+ // same argument here as you would to that method.
+ removeView: function(fn) {
+ // Allow an optional selector or function to find the right model and
+ // remove nested Views based off the results of the selector or filter.
+ return this.getViews(fn).each(function(nestedView) {
+ nestedView.remove();
+ });
+ },
+
// This takes in a partial name and view instance and assigns them to
// the internal collection of views. If a view is not a LayoutManager
// instance, then mix in the LayoutManager prototype. This ensures
@@ -102,14 +129,14 @@ var LayoutManager = Backbone.View.extend({
//
// Must definitely wrap any render method passed in or defaults to a
// typical render function `return layout(this).render()`.
- setView: function(name, view, append) {
+ setView: function(name, view, insert) {
var manager, existing, options;
// Parent view, the one you are setting a View on.
var root = this;
// If no name was passed, use an empty string and shift all arguments.
if (typeof name !== "string") {
- append = view;
+ insert = view;
view = name;
name = "";
}
@@ -131,7 +158,7 @@ var LayoutManager = Backbone.View.extend({
}
// Assign options.
- options = view._options();
+ options = view.getAllOptions();
// Add reference to the parentView.
manager.parent = root;
@@ -139,13 +166,22 @@ var LayoutManager = Backbone.View.extend({
// Add reference to the placement selector used.
manager.selector = name;
- // Code path is less complex for Views that are not being appended. Simply
+ // Set up event bubbling, inspired by Backbone.ViewMaster. Do not bubble
+ // internal events that are triggered.
+ view.on("all", function(name) {
+ if (name !== "beforeRender" && name !== "afterRender") {
+ root.trigger.apply(root, arguments);
+ }
+ }, view);
+
+ // Code path is less complex for Views that are not being inserted. Simply
// remove existing Views and bail out with the assignment.
- if (!append) {
+ if (!insert) {
// If the View we are adding has already been rendered, simply inject it
// into the parent.
if (manager.hasRendered) {
- options.partial(root.el, manager.selector, view.el, manager.append);
+ // Apply the partial.
+ options.partial(root.$el, view.$el, root.__manager__, manager);
}
// Ensure remove is called when swapping View's.
@@ -163,8 +199,8 @@ var LayoutManager = Backbone.View.extend({
// Ensure this.views[name] is an array and push this View to the end.
this.views[name] = aConcat.call([], existing || [], view);
- // Put the view into `append` mode.
- manager.append = true;
+ // Put the view into `insert` mode.
+ manager.insert = true;
return view;
},
@@ -196,7 +232,7 @@ var LayoutManager = Backbone.View.extend({
// once all subviews and main view have been rendered into the view.el.
render: function() {
var root = this;
- var options = root._options();
+ var options = root.getAllOptions();
var manager = root.__manager__;
var parent = manager.parent;
var rentManager = parent && parent.__manager__;
@@ -204,13 +240,13 @@ var LayoutManager = Backbone.View.extend({
// Triggered once the render has succeeded.
function resolve() {
- var next;
+ var next, afterRender;
// If there is a parent, attach.
if (parent) {
if (!options.contains(parent.el, root.el)) {
- options.partial(parent.el, manager.selector, root.el,
- manager.append);
+ // Apply the partial.
+ options.partial(parent.$el, root.$el, rentManager, manager);
}
}
@@ -220,9 +256,6 @@ var LayoutManager = Backbone.View.extend({
// Set this View as successfully rendered.
manager.hasRendered = true;
- // Resolve the deferred.
- def.resolveWith(root, [root]);
-
// Only process the queue if it exists.
if (next = manager.queue.shift()) {
// Ensure that the next render is only called after all other
@@ -246,36 +279,46 @@ var LayoutManager = Backbone.View.extend({
// Always emit an afterRender event.
root.trigger("afterRender", root);
+
+ // If there are multiple top level elements and `el: false` is used,
+ // display a warning message and a stack trace.
+ if (manager.noel && root.$el.length > 1) {
+ // Do not display a warning while testing or if warning suppression
+ // is enabled.
+ if (warn && !options.suppressWarnings) {
+ window.console.warn("Using `el: false` with multiple top level " +
+ "elements is not supported.");
+
+ // Provide a stack trace if available to aid with debugging.
+ if (trace) { window.console.trace(); }
+ }
+ }
}
- // Special case for when a parent View that has not been rendered is
- // involved.
- if (rentManager && !rentManager.hasRendered) {
+ // If the parent is currently rendering, wait until it has completed
+ // until calling the nested View's `afterRender`.
+ if (rentManager && rentManager.queue) {
// Wait until the parent View has finished rendering, which could be
// asynchronous, and trigger afterRender on this View once it has
// compeleted.
- return parent.on("afterRender", function() {
- // Wish we had `once` for this...
- parent.off("afterRender", null, this);
-
- // Trigger the afterRender and set hasRendered.
- completeRender();
- }, root);
+ parent.once("afterRender", completeRender);
+ } else {
+ // This View and its parent have both rendered.
+ completeRender();
}
- // This View and its parent have both rendered.
- completeRender();
+ return def.resolveWith(root, [root]);
}
// Actually facilitate a render.
function actuallyRender() {
- var options = root._options();
+ var options = root.getAllOptions();
var manager = root.__manager__;
var parent = manager.parent;
var rentManager = parent && parent.__manager__;
// The `_viewRender` method is broken out to abstract away from having
- // too much code in `processRender`.
+ // too much code in `actuallyRender`.
root._render(LayoutManager._viewRender, options).done(function() {
// If there are no children to worry about, complete the render
// instantly.
@@ -285,44 +328,39 @@ var LayoutManager = Backbone.View.extend({
// Create a list of promises to wait on until rendering is done.
// Since this method will run on all children as well, its sufficient
- // for a full hierarchical.
+ // for a full hierarchical.
var promises = _.map(root.views, function(view) {
- var append = _.isArray(view);
+ var insert = _.isArray(view);
// If items are being inserted, they will be in a non-zero length
// Array.
- if (append && view.length) {
- // Only need to wait for the first View to complete, the rest
- // will be synchronous, by virtue of having the template cached.
- return view[0].render().pipe(function() {
- // Map over all the View's to be inserted and call render on
- // them all. Once they have all resolved, resolve the other
- // deferred.
- return options.when(_.map(view.slice(1), function(insertView) {
- return insertView.render();
- }));
- });
+ if (insert && view.length) {
+ // Schedule each view to be rendered in order and return a promise
+ // representing the result of the final rendering.
+ return _.reduce(view.slice(1), function(prevRender, view) {
+ return prevRender.then(function() {
+ return view.render();
+ });
+ // The first view should be rendered immediately, and the resulting
+ // promise used to initialize the reduction.
+ }, view[0].render());
}
// Only return the fetch deferred, resolve the main deferred after
// the element has been attached to it's parent.
- return !append ? view.render() : view;
+ return !insert ? view.render() : view;
});
// Once all nested Views have been rendered, resolve this View's
// deferred.
- options.when(promises).done(function() {
- resolve();
- });
+ options.when(promises).done(resolve);
});
}
// Another render is currently happening if there is an existing queue, so
// push a closure to render later into the queue.
if (manager.queue) {
- aPush.call(manager.queue, function() {
- actuallyRender();
- });
+ aPush.call(manager.queue, actuallyRender);
} else {
manager.queue = [];
@@ -334,7 +372,7 @@ var LayoutManager = Backbone.View.extend({
// Add the View to the deferred so that `view.render().view.el` is
// possible.
def.view = root;
-
+
// This is the promise that determines if the `render` function has
// completed or not.
return def;
@@ -350,12 +388,9 @@ var LayoutManager = Backbone.View.extend({
},
// Merge instance and global options.
- _options: function() {
- // Instance overrides take precedence, fallback to prototype options. In
- // Lo-Dash, `_.extend` will not copy over inherited properties, so the
- // `this.constructor.prototype` was added in to cover that case.
- return _.extend({}, this, this.constructor.prototype,
- LayoutManager.prototype.options, this.options);
+ getAllOptions: function() {
+ // Instance overrides take precedence, fallback to prototype options.
+ return _.extend({}, this, LayoutManager.prototype.options, this.options);
}
},
{
@@ -379,7 +414,7 @@ var LayoutManager = Backbone.View.extend({
// This gets passed to all _render methods. The `root` value here is passed
// from the `manage(this).render()` line in the `_render` function
_viewRender: function(root, options) {
- var url, contents, fetchAsync;
+ var url, contents, fetchAsync, renderedEl;
var manager = root.__manager__;
// This function is responsible for pairing the rendered template into
@@ -387,7 +422,27 @@ var LayoutManager = Backbone.View.extend({
function applyTemplate(rendered) {
// Actually put the rendered contents into the element.
if (rendered) {
- options.html(root.el, rendered);
+ // If no container is specified, we must replace the content.
+ if (manager.noel) {
+ // Trim off the whitespace, since the contents are passed into `$()`.
+ rendered = $.trim(rendered);
+
+ // Hold a reference to created element as replaceWith doesn't return
+ // new el.
+ renderedEl = $(rendered);
+
+ // Remove extra root elements.
+ root.$el.slice(1).remove();
+
+ // Swap out the View on the first top level element to avoid
+ // duplication.
+ root.$el.replaceWith(renderedEl);
+
+ // Don't delegate events here - we'll do that in resolve()
+ root.setElement(renderedEl, false);
+ } else {
+ options.html(root.$el, rendered);
+ }
}
// Resolve only after fetch and render have succeeded.
@@ -424,18 +479,17 @@ var LayoutManager = Backbone.View.extend({
// when `manage(this).render` is called. Returns a promise that can be
// used to know when the element has been rendered into its parent.
render: function() {
- var context;
- var data = options.serialize || options.data;
+ var context = root.serialize || options.serialize;
var template = root.template || options.template;
// If data is a function, immediately call it.
- if (_.isFunction(data)) {
- data = data.call(root);
+ if (_.isFunction(context)) {
+ context = context.call(root);
}
// This allows for `var done = this.async()` and then `done(contents)`.
fetchAsync = LayoutManager._makeAsync(options, function(contents) {
- done(data, contents);
+ done(context, contents);
});
// Set the url to the prefix + the view's template property.
@@ -446,7 +500,7 @@ var LayoutManager = Backbone.View.extend({
// Check if contents are already cached and if they are, simply process
// the template with the correct data.
if (contents = LayoutManager.cache(url)) {
- done(data, contents, url);
+ done(context, contents, url);
return fetchAsync;
}
@@ -454,14 +508,17 @@ var LayoutManager = Backbone.View.extend({
// Fetch layout and template contents.
if (typeof template === "string") {
contents = options.fetch.call(fetchAsync, options.prefix + template);
- // If its not a string just pass the object/function/whatever.
+ // If the template is already a function, simply call it.
+ } else if (typeof template === "function") {
+ contents = template;
+ // If its not a string and not undefined, pass the value to `fetch`.
} else if (template != null) {
contents = options.fetch.call(fetchAsync, template);
}
// If the function was synchronous, continue execution.
if (!fetchAsync._isAsync) {
- done(data, contents);
+ done(context, contents);
}
return fetchAsync;
@@ -471,6 +528,8 @@ var LayoutManager = Backbone.View.extend({
// Remove all nested Views.
_removeViews: function(root, force) {
+ var views;
+
// Shift arguments around.
if (typeof root === "boolean") {
force = root;
@@ -498,14 +557,14 @@ var LayoutManager = Backbone.View.extend({
var keep = typeof view.keep === "boolean" ? view.keep : view.options.keep;
// Only remove views that do not have `keep` attribute set, unless the
- // View is in `append` mode and the force flag is set.
- if (!keep && (manager.append === true || force)) {
+ // View is in `insert` mode and the force flag is set.
+ if ((!keep && manager.insert === true) || force) {
// Clean out the events.
LayoutManager.cleanViews(view);
// Since we are removing this view, force subviews to remove
- view._removeViews(true);
-
+ view._removeViews(true);
+
// Remove the View completely.
view.$el.remove();
@@ -532,6 +591,19 @@ var LayoutManager = Backbone.View.extend({
}
},
+ // Cache templates into LayoutManager._cache.
+ cache: function(path, contents) {
+ // If template path is found in the cache, return the contents.
+ if (path in this._cache && contents == null) {
+ return this._cache[path];
+ // Ensure path and contents aren't undefined.
+ } else if (path != null && contents != null) {
+ return this._cache[path] = contents;
+ }
+
+ // If the template is not in the cache, return undefined.
+ },
+
// Accept either a single view or an array of views to clean of all DOM
// events internal model and collection references and all Backbone.Events.
cleanViews: function(views) {
@@ -550,173 +622,185 @@ var LayoutManager = Backbone.View.extend({
view.collection.off(null, null, view);
}
+ // Automatically unbind events bound to this View.
+ view.stopListening();
+
// If a custom cleanup method was provided on the view, call it after
// the initial cleanup is done
- if (view.cleanup) {
- view.cleanup.call(view);
- }
+ _.result(view.getAllOptions(), "cleanup");
});
},
- // Cache templates into LayoutManager._cache.
- cache: function(path, contents) {
- // If template path is found in the cache, return the contents.
- if (path in this._cache) {
- return this._cache[path];
- // Ensure path and contents aren't undefined.
- } else if (path != null && contents != null) {
- return this._cache[path] = contents;
- }
-
- // If the template is not in the cache, return undefined.
- },
-
// This static method allows for global configuration of LayoutManager.
- configure: function(opts) {
- _.extend(LayoutManager.prototype.options, opts);
+ configure: function(options) {
+ _.extend(LayoutManager.prototype.options, options);
// Allow LayoutManager to manage Backbone.View.prototype.
- if (opts.manage) {
+ if (options.manage) {
Backbone.View.prototype.manage = true;
}
- },
- // Configure a View to work with the LayoutManager plugin.
- setupView: function(view, options) {
- // If the View has already been setup, no need to do it again.
- if (view.__manager__) {
- return;
+ // Disable the element globally.
+ if (options.el === false) {
+ Backbone.View.prototype.el = false;
}
- var views, declaredViews, viewOptions;
- var proto = Backbone.LayoutManager.prototype;
- var viewOverrides = _.pick(view, keys);
-
- // Ensure necessary properties are set.
- _.defaults(view, {
- // Ensure a view always has a views object.
- views: {},
-
- // Internal state object used to store whether or not a View has been
- // taken over by layout manager and if it has been rendered into the DOM.
- __manager__: {},
+ // Allow global configuration of `suppressWarnings`.
+ if (options.suppressWarnings === true) {
+ Backbone.View.prototype.suppressWarnings = true;
+ }
+ },
- // Add the ability to remove all Views.
- _removeViews: LayoutManager._removeViews,
+ // Configure a View to work with the LayoutManager plugin.
+ setupView: function(views, options) {
+ // Set up all Views passed.
+ _.each(aConcat.call([], views), function(view) {
+ // If the View has already been setup, no need to do it again.
+ if (view.__manager__) {
+ return;
+ }
- // Add the ability to remove itself.
- _removeView: LayoutManager._removeView
+ var views, declaredViews, viewOptions;
+ var proto = LayoutManager.prototype;
+ var viewOverrides = _.pick(view, keys);
- // Mix in all LayoutManager prototype properties as well.
- }, LayoutManager.prototype);
+ // Ensure necessary properties are set.
+ _.defaults(view, {
+ // Ensure a view always has a views object.
+ views: {},
- // Extend the options with the prototype and passed options.
- options = view.options = _.defaults(options || {}, view.options,
- proto.options);
+ // Internal state object used to store whether or not a View has been
+ // taken over by layout manager and if it has been rendered into the DOM.
+ __manager__: {},
- // Ensure view events are properly copied over.
- viewOptions = _.pick(options, aConcat.call(["events"],
- _.values(options.events)));
+ // Add the ability to remove all Views.
+ _removeViews: LayoutManager._removeViews,
- // Merge the View options into the View.
- _.extend(view, viewOptions);
+ // Add the ability to remove itself.
+ _removeView: LayoutManager._removeView
- // If the View still has the Backbone.View#render method, remove it. Don't
- // want it accidentally overriding the LM render.
- if (viewOverrides.render === LayoutManager.prototype.render ||
- viewOverrides.render === Backbone.View.prototype.render) {
- delete viewOverrides.render;
- }
+ // Mix in all LayoutManager prototype properties as well.
+ }, LayoutManager.prototype);
- // Pick out the specific properties that can be dynamically added at
- // runtime and ensure they are available on the view object.
- _.extend(options, viewOverrides);
+ // Extend the options with the prototype and passed options.
+ options = view.options = _.defaults(options || {}, view.options,
+ proto.options);
- // By default the original Remove function is the Backbone.View one.
- view._remove = Backbone.View.prototype.remove;
+ // Ensure view events are properly copied over.
+ viewOptions = _.pick(options, aConcat.call(["events"],
+ _.values(options.events)));
- // Always use this render function when using LayoutManager.
- view._render = function(manage, options) {
- // Keep the view consistent between callbacks and deferreds.
- var view = this;
- // Shorthand the manager.
- var manager = view.__manager__;
- // Cache these properties.
- var beforeRender = options.beforeRender;
+ // Merge the View options into the View.
+ _.extend(view, viewOptions);
- // Ensure all nested Views are properly scrubbed if re-rendering.
- if (manager.hasRendered) {
- this._removeViews();
+ // If the View still has the Backbone.View#render method, remove it.
+ // Don't want it accidentally overriding the LM render.
+ if (viewOverrides.render === LayoutManager.prototype.render ||
+ viewOverrides.render === Backbone.View.prototype.render) {
+ delete viewOverrides.render;
}
- // If a beforeRender function is defined, call it.
- if (beforeRender) {
- beforeRender.call(this, this);
- }
+ // Pick out the specific properties that can be dynamically added at
+ // runtime and ensure they are available on the view object.
+ _.extend(options, viewOverrides);
+
+ // By default the original Remove function is the Backbone.View one.
+ view._remove = Backbone.View.prototype.remove;
+
+ // Always use this render function when using LayoutManager.
+ view._render = function(manage, options) {
+ // Keep the view consistent between callbacks and deferreds.
+ var view = this;
+ // Shorthand the manager.
+ var manager = view.__manager__;
+ // Cache these properties.
+ var beforeRender = options.beforeRender;
+
+ // Ensure all nested Views are properly scrubbed if re-rendering.
+ if (manager.hasRendered) {
+ this._removeViews();
+ }
- // Always emit a beforeRender event.
- this.trigger("beforeRender", this);
+ // If a beforeRender function is defined, call it.
+ if (beforeRender) {
+ beforeRender.call(this, this);
+ }
- // Render!
- return manage(this, options).render();
- };
+ // Always emit a beforeRender event.
+ this.trigger("beforeRender", this);
- // Ensure the render is always set correctly.
- view.render = LayoutManager.prototype.render;
+ // Render!
+ return manage(this, options).render();
+ };
- // If the user provided their own remove override, use that instead of the
- // default.
- if (view.remove !== proto.remove) {
- view._remove = view.remove;
- view.remove = proto.remove;
- }
-
- // Normalize views to exist on either instance or options, default to
- // options.
- views = options.views || view.views;
+ // Ensure the render is always set correctly.
+ view.render = LayoutManager.prototype.render;
- // Set the internal views, only if selectors have been provided.
- if (_.keys(views).length) {
- // Keep original object declared containing Views.
- declaredViews = views;
+ // If the user provided their own remove override, use that instead of
+ // the default.
+ if (view.remove !== proto.remove) {
+ view._remove = view.remove;
+ view.remove = proto.remove;
+ }
- // Reset the property to avoid duplication or overwritting.
- view.views = {};
+ // Normalize views to exist on either instance or options, default to
+ // options.
+ views = options.views || view.views;
- // Set the declared Views.
- view.setViews(declaredViews);
- }
+ // Set the internal views, only if selectors have been provided.
+ if (_.keys(views).length) {
+ // Keep original object declared containing Views.
+ declaredViews = views;
- // If a template is passed use that instead.
- if (view.options.template) {
- view.options.template = options.template;
- // Ensure the template is mapped over.
- } else if (view.template) {
- options.template = view.template;
+ // Reset the property to avoid duplication or overwritting.
+ view.views = {};
- // Remove it from the instance.
- delete view.template;
- }
+ // Set the declared Views.
+ view.setViews(declaredViews);
+ }
+
+ // If a template is passed use that instead.
+ if (view.options.template) {
+ view.options.template = options.template;
+ // Ensure the template is mapped over.
+ } else if (view.template) {
+ options.template = view.template;
+ }
+ });
}
});
// Convenience assignment to make creating Layout's slightly shorter.
-Backbone.Layout = Backbone.LayoutView = Backbone.LayoutManager = LayoutManager;
+Backbone.Layout = LayoutManager;
// Tack on the version.
-LayoutManager.VERSION = "0.7.2";
+LayoutManager.VERSION = "0.8.7";
// Override _configure to provide extra functionality that is necessary in
// order for the render function reference to be bound during initialize.
-Backbone.View.prototype._configure = function() {
+Backbone.View.prototype._configure = function(options) {
+ var noel, retVal;
+
+ // Remove the container element provided by Backbone.
+ if ("el" in options ? options.el === false : this.el === false) {
+ noel = true;
+ }
+
// Run the original _configure.
- var retVal = _configure.apply(this, arguments);
+ retVal = _configure.apply(this, arguments);
// If manage is set, do it!
- if (this.manage) {
+ if (options.manage || this.manage) {
// Set up this View.
LayoutManager.setupView(this);
}
+ // Assign the `noel` property once we're sure the View we're working with is
+ // managed by LayoutManager.
+ if (this.__manager__) {
+ this.__manager__.noel = noel;
+ this.__manager__.suppressWarnings = options.suppressWarnings;
+ }
+
// Act like nothing happened.
return retVal;
};
@@ -739,23 +823,34 @@ LayoutManager.prototype.options = {
// This is the most common way you will want to partially apply a view into
// a layout.
- partial: function(root, name, el, append) {
- // If no selector is specified, assume the parent should be added to.
- var $root = name ? $(root).find(name) : $(root);
+ partial: function($root, $el, rentManager, manager) {
+ // If selector is specified, attempt to find it.
+ if (manager.selector) {
+ if (rentManager.noel) {
+ var $filtered = $root.filter(manager.selector);
+ $root = $filtered.length ? $filtered : $root.find(manager.selector);
+ } else {
+ $root = $root.find(manager.selector);
+ }
+ }
- // Use the append method if append argument is true.
- this[append ? "append" : "html"]($root, el);
+ // Use the insert method if insert argument is true.
+ if (manager.insert) {
+ this.insert($root, $el);
+ } else {
+ this.html($root, $el);
+ }
},
- // Override this with a custom HTML method, passed a root element and an
- // element to replace the innerHTML with.
- html: function(root, el) {
- $(root).html(el);
+ // Override this with a custom HTML method, passed a root element and content
+ // (a jQuery collection or a string) to replace the innerHTML with.
+ html: function($root, content) {
+ $root.html(content);
},
- // Very similar to HTML except this one will appendChild.
- append: function(root, el) {
- $(root).append(el);
+ // Very similar to HTML except this one will appendChild by default.
+ insert: function($root, $el) {
+ $root.append($el);
},
// Return a deferred for when all promises resolve/reject.
@@ -777,5 +872,4 @@ LayoutManager.prototype.options = {
// Maintain a list of the keys at define time.
keys = _.keys(LayoutManager.prototype.options);
-})(this);
-
+})(typeof global === "object" ? global : this);
[03/10] git commit: updated refs/heads/master to 88c3171
Posted by me...@apache.org.
update lodash
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/a46e87d7
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/a46e87d7
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/a46e87d7
Branch: refs/heads/master
Commit: a46e87d71a2821c79615dff9c0fefbdb5a140020
Parents: cca2015
Author: Simon Metson <me...@apache.org>
Authored: Thu Apr 4 13:08:19 2013 +0100
Committer: Simon Metson <me...@apache.org>
Committed: Thu Apr 4 13:08:19 2013 +0100
----------------------------------------------------------------------
src/fauxton/assets/js/libs/lodash.js | 5455 +++++++++++++++++------------
1 files changed, 3128 insertions(+), 2327 deletions(-)
----------------------------------------------------------------------
[10/10] git commit: updated refs/heads/master to 88c3171
Posted by me...@apache.org.
footer as view
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/88c3171d
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/88c3171d
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/88c3171d
Branch: refs/heads/master
Commit: 88c3171dc1d33979e51cdb18e96a2d333e440376
Parents: 203ac89
Author: Simon Metson <me...@apache.org>
Authored: Thu Apr 4 13:08:21 2013 +0100
Committer: Simon Metson <me...@apache.org>
Committed: Thu Apr 4 13:09:15 2013 +0100
----------------------------------------------------------------------
src/fauxton/app/app.js | 3 ++-
src/fauxton/app/modules/fauxton/base.js | 9 +++++++++
src/fauxton/app/router.js | 4 ++++
src/fauxton/app/templates/fauxton/footer.html | 1 +
src/fauxton/assets/index.underscore | 4 +---
5 files changed, 17 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/88c3171d/src/fauxton/app/app.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/app.js b/src/fauxton/app/app.js
index 5046096..e754c5f 100644
--- a/src/fauxton/app/app.js
+++ b/src/fauxton/app/app.js
@@ -23,7 +23,8 @@ function($, _, Backbone, Helpers) {
// creation.
var app = {
// The root path to run the application.
- root: "/"
+ root: "/",
+ version: "0.0.1"
};
// Localize or create a new JavaScript Template object.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/88c3171d/src/fauxton/app/modules/fauxton/base.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/fauxton/base.js b/src/fauxton/app/modules/fauxton/base.js
index a8bf430..0297dac 100644
--- a/src/fauxton/app/modules/fauxton/base.js
+++ b/src/fauxton/app/modules/fauxton/base.js
@@ -36,6 +36,15 @@ function(app, Backbone) {
}
});
+ Fauxton.Footer = Backbone.View.extend({
+ template: "templates/fauxton/footer",
+ serialize: function() {
+ return {
+ version: app.version
+ };
+ }
+ });
+
Fauxton.NavBar = Backbone.View.extend({
template: "templates/fauxton/nav_bar",
// TODO: can we generate this list from the router?
http://git-wip-us.apache.org/repos/asf/couchdb/blob/88c3171d/src/fauxton/app/router.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/router.js b/src/fauxton/app/router.js
index 2e994e5..77cc36b 100644
--- a/src/fauxton/app/router.js
+++ b/src/fauxton/app/router.js
@@ -140,10 +140,12 @@ function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, Databases, Documents
},
initialize: function() {
+ //TODO: It would be nice to handle this with a router
this.navBar = app.navBar = new Fauxton.NavBar();
this.apiBar = app.apiBar = new Fauxton.ApiBar();
app.masterLayout = this.masterLayout = new Layout(this.navBar, this.apiBar);
+ app.footer = new Fauxton.Footer({el: "#footer-content"});
// NOTE: This must be below creation of the layout
// FauxtonAPI header links and others depend on existence of the layout
@@ -152,6 +154,8 @@ function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, Databases, Documents
$("#app-container").html(this.masterLayout.el);
this.masterLayout.render();
+
+ app.footer.render();
}
});
http://git-wip-us.apache.org/repos/asf/couchdb/blob/88c3171d/src/fauxton/app/templates/fauxton/footer.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/fauxton/footer.html b/src/fauxton/app/templates/fauxton/footer.html
new file mode 100644
index 0000000..6af1bcf
--- /dev/null
+++ b/src/fauxton/app/templates/fauxton/footer.html
@@ -0,0 +1 @@
+<p>Fauxton <%=version%> on <a href="http://couchdb.apache.org/">Apache CouchDB</a></p>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/couchdb/blob/88c3171d/src/fauxton/assets/index.underscore
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/index.underscore b/src/fauxton/assets/index.underscore
index 8a62006..2b5c009 100644
--- a/src/fauxton/assets/index.underscore
+++ b/src/fauxton/assets/index.underscore
@@ -43,9 +43,7 @@
<hr>
<footer>
- <div id="footer-content" class="container">
- <p>© Project Fauxton 2012</p>
- </div>
+ <div id="footer-content" class="container"></div>
</footer>
</div>
[06/10] git commit: updated refs/heads/master to 88c3171
Posted by me...@apache.org.
get logs working with backbone v1
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/e14abfde
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/e14abfde
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/e14abfde
Branch: refs/heads/master
Commit: e14abfdecc8f5050336a3aba74986e7043fae480
Parents: 7bafe85
Author: Simon Metson <si...@cloudant.com>
Authored: Thu Apr 4 13:08:20 2013 +0100
Committer: Simon Metson <me...@apache.org>
Committed: Thu Apr 4 13:08:20 2013 +0100
----------------------------------------------------------------------
src/fauxton/app/addons/logs/resources.js | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/e14abfde/src/fauxton/app/addons/logs/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/logs/resources.js b/src/fauxton/app/addons/logs/resources.js
index 382fafe..d1e6d20 100644
--- a/src/fauxton/app/addons/logs/resources.js
+++ b/src/fauxton/app/addons/logs/resources.js
@@ -12,10 +12,11 @@
define([
"app",
- "api"
+ "api",
+ "backbone"
],
-function (app, FauxtonAPI) {
+function (app, FauxtonAPI, Backbone) {
var Log = FauxtonAPI.addon();
@@ -66,7 +67,6 @@ function (app, FauxtonAPI) {
parse: function (resp) {
var lines = resp.split(/\n/);
-
return _.foldr(lines, function (acc, logLine) {
var match = logLine.match(/^\[(.*?)\]\s\[(.*?)\]\s\[(.*?)\]\s(.*)/);
@@ -98,7 +98,7 @@ function (app, FauxtonAPI) {
this.filters = [];
this.filteredCollection = new Log.Collection(this.collection.toJSON());
- this.collection.on("reset", function () {
+ this.collection.on("add", function () {
this.createFilteredCollection();
}, this);
},
[09/10] git commit: updated refs/heads/master to 88c3171
Posted by me...@apache.org.
twitter feed as news
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/203ac897
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/203ac897
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/203ac897
Branch: refs/heads/master
Commit: 203ac897ec4b3e34dc795cb2ab2cec6146b7810b
Parents: 1fb2259
Author: Simon Metson <si...@cloudant.com>
Authored: Thu Apr 4 13:08:20 2013 +0100
Committer: Simon Metson <me...@apache.org>
Committed: Thu Apr 4 13:09:15 2013 +0100
----------------------------------------------------------------------
src/fauxton/app/templates/databases/sidebar.html | 12 ++++++++----
1 files changed, 8 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/203ac897/src/fauxton/app/templates/databases/sidebar.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/databases/sidebar.html b/src/fauxton/app/templates/databases/sidebar.html
index 1b78aba..a8bbd89 100644
--- a/src/fauxton/app/templates/databases/sidebar.html
+++ b/src/fauxton/app/templates/databases/sidebar.html
@@ -13,15 +13,19 @@ the License.
-->
<div class="row-fluid">
-<a class="btn new" id="new"><i class="icon-plus"></i> New database</a>
+ <a href="http://couchdb.org" target="_blank"><img src="img/couchdblogo.png"/></a>
+ <br/>
</div>
<hr>
<ul class="nav nav-list">
<!-- <li class="nav-header">Database types</li> -->
<li class="active"><a class="toggle-view" id="owned">Your databases</a></li>
+ <li><a class="btn new" id="new"><i class="icon-plus"></i> New database</a></li>
</ul>
<hr>
-<div class="well">
- <p>Here be news.</p>
+
+<div>
+ <a class="twitter-timeline" data-dnt="true" href="https://twitter.com/CouchDB" data-widget-id="314360971646869505">Tweets by @CouchDB</a>
+<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
+
</div>
-<a href="http://couchdb.org" target="_blank"><img src="img/couchdblogo.png"/></a>
[07/10] git commit: updated refs/heads/master to 88c3171
Posted by me...@apache.org.
fix Databases.Status.updateSeq for the case when a database has received no
updates (e.g. is newly created).
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/7bafe858
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/7bafe858
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/7bafe858
Branch: refs/heads/master
Commit: 7bafe8582d9f3dbebd286582400560e0e1bbbd6d
Parents: b420843
Author: Simon Metson <si...@cloudant.com>
Authored: Thu Apr 4 13:08:20 2013 +0100
Committer: Simon Metson <me...@apache.org>
Committed: Thu Apr 4 13:08:20 2013 +0100
----------------------------------------------------------------------
src/fauxton/app/modules/databases/resources.js | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/7bafe858/src/fauxton/app/modules/databases/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/databases/resources.js b/src/fauxton/app/modules/databases/resources.js
index e5d3fae..59dd179 100644
--- a/src/fauxton/app/modules/databases/resources.js
+++ b/src/fauxton/app/modules/databases/resources.js
@@ -104,8 +104,10 @@ function(app, FauxtonAPI, Documents) {
var updateSeq = this.get("update_seq");
if (full || (typeof(updateSeq) === 'number')) {
return updateSeq;
- } else {
+ } else if (updateSeq) {
return updateSeq.split('-')[0];
+ } else {
+ return 0;
}
},
[02/10] update lodash
Posted by me...@apache.org.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/a46e87d7/src/fauxton/assets/js/libs/lodash.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/libs/lodash.js b/src/fauxton/assets/js/libs/lodash.js
index 43ddcb2..ccf632d 100644
--- a/src/fauxton/assets/js/libs/lodash.js
+++ b/src/fauxton/assets/js/libs/lodash.js
@@ -1,77 +1,77 @@
-/*!
- * Lo-Dash v0.3.1 <http://lodash.com>
- * Copyright 2012 John-David Dalton <http://allyoucanleet.com/>
- * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
- * <http://documentcloud.github.com/underscore>
+/**
+ * @license
+ * Lo-Dash 1.1.1 (Custom Build) <http://lodash.com/>
+ * Build: `lodash underscore exports="amd,commonjs,global,node" -o ./dist/lodash.underscore.js`
+ * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
+ * Based on Underscore.js 1.4.4 <http://underscorejs.org/>
+ * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
* Available under MIT license <http://lodash.com/license>
*/
-;(function(window, undefined) {
- 'use strict';
+;(function(window) {
+
+ /** Used as a safe reference for `undefined` in pre ES5 environments */
+ var undefined;
/** 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;
- /**
- * Detect the JScript [[DontEnum]] bug:
- * In IE < 9 an objects own properties, shadowing non-enumerable ones, are
- * made non-enumerable as well.
- */
- var hasDontEnumBug = !{ 'valueOf': 0 }.propertyIsEnumerable('valueOf');
+ /** Detect free variable `module` */
+ var freeModule = typeof module == 'object' && module && module.exports == freeExports && module;
+
+ /** Detect free variable `global` and use it as `window` */
+ var freeGlobal = typeof global == 'object' && global;
+ if (freeGlobal.global === freeGlobal) {
+ window = freeGlobal;
+ }
/** Used to generate unique IDs */
var idCounter = 0;
- /** Used to restore the original `_` reference in `noConflict` */
- var oldDash = window._;
+ /** Used internally to indicate various things */
+ var indicatorObject = {};
- /** Used to detect if a method is native */
- var reNative = RegExp('^' + ({}.valueOf + '')
- .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&')
- .replace(/valueOf|for [^\]]+/g, '.+?') + '$');
+ /** Used to match empty string literals in compiled template source */
+ var reEmptyStringLeading = /\b__p \+= '';/g,
+ reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+ reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
- /** Used to match tokens in template text */
- var reToken = /__token__(\d+)/g;
+ /** Used to match HTML entities */
+ var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g;
- /** Used to match unescaped characters in HTML */
- var reUnescapedHtml = /[&<"']/g;
+ /**
+ * Used to match ES6 template delimiters
+ * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.8.6
+ */
+ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
- /** Used to match unescaped characters in compiled string literals */
- var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
+ /** Used to match regexp flags from their coerced string values */
+ var reFlags = /\w*$/;
- /** Used to fix the JScript [[DontEnum]] bug */
- var shadowed = [
- 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
- 'toLocaleString', 'toString', 'valueOf'
- ];
+ /** Used to match "interpolate" template delimiters */
+ var reInterpolate = /<%=([\s\S]+?)%>/g;
- /** Used to make template sourceURLs easier to identify */
- var templateCounter = 0;
+ /** Used to ensure capturing order of template delimiters */
+ var reNoMatch = /($^)/;
- /** Used to replace template delimiters */
- var token = '__token__';
+ /** Used to match HTML characters */
+ var reUnescapedHtml = /[&<>"']/g;
- /** Used to store tokenized template text snippets */
- var tokenized = [];
+ /** Used to match unescaped characters in compiled string literals */
+ var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
- /** Detect if sourceURL syntax is usable without erroring */
- try {
- // Adobe's and Narwhal's JS engines will error
- var useSourceURL = (Function('//@')(), true);
- } catch(e){ }
+ /** Used to make template sourceURLs easier to identify */
+ var templateCounter = 0;
- /**
- * Used to escape characters for inclusion in HTML.
- * The `>` and `/` characters don't require escaping in HTML and have no
- * special meaning unless they're part of a tag or an unquoted attribute value
- * http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact)
- */
- var htmlEscapes = {
- '&': '&',
- '<': '<',
- '"': '"',
- "'": '''
- };
+ /** `Object#toString` result shortcuts */
+ var argsClass = '[object Arguments]',
+ arrayClass = '[object Array]',
+ boolClass = '[object Boolean]',
+ dateClass = '[object Date]',
+ funcClass = '[object Function]',
+ numberClass = '[object Number]',
+ objectClass = '[object Object]',
+ regexpClass = '[object RegExp]',
+ stringClass = '[object String]';
/** Used to determine if values are of the language type Object */
var objectTypes = {
@@ -94,72 +94,142 @@
'\u2029': 'u2029'
};
- /** Object#toString result shortcuts */
- var arrayClass = '[object Array]',
- boolClass = '[object Boolean]',
- dateClass = '[object Date]',
- funcClass = '[object Function]',
- numberClass = '[object Number]',
- regexpClass = '[object RegExp]',
- stringClass = '[object String]';
+ /*--------------------------------------------------------------------------*/
- /** Native prototype shortcuts */
- var ArrayProto = Array.prototype,
- ObjectProto = Object.prototype;
+ /** Used for `Array` and `Object` method references */
+ var arrayRef = Array(),
+ objectRef = Object();
- /** Native method shortcuts */
- var concat = ArrayProto.concat,
- hasOwnProperty = ObjectProto.hasOwnProperty,
- push = ArrayProto.push,
- slice = ArrayProto.slice,
- toString = ObjectProto.toString;
+ /** Used to restore the original `_` reference in `noConflict` */
+ var oldDash = window._;
+
+ /** Used to detect if a method is native */
+ var reNative = RegExp('^' +
+ String(objectRef.valueOf)
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ .replace(/valueOf|for [^\]]+/g, '.+?') + '$'
+ );
- /* Used if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */
- var nativeBind = reNative.test(nativeBind = slice.bind) &&
- /\n|Opera/.test(nativeBind + toString.call(window.opera)) && nativeBind;
+ /** Native method shortcuts */
+ var ceil = Math.ceil,
+ clearTimeout = window.clearTimeout,
+ concat = arrayRef.concat,
+ floor = Math.floor,
+ hasOwnProperty = objectRef.hasOwnProperty,
+ push = arrayRef.push,
+ setTimeout = window.setTimeout,
+ slice = arrayRef.slice,
+ toString = objectRef.toString;
/* Native method shortcuts for methods with the same name as other `lodash` methods */
- var nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
+ var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind,
+ nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
nativeIsFinite = window.isFinite,
- nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys;
+ nativeIsNaN = window.isNaN,
+ nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys,
+ nativeMax = Math.max,
+ nativeMin = Math.min,
+ nativeRandom = Math.random;
- /** Timer shortcuts */
- var clearTimeout = window.clearTimeout,
- setTimeout = window.setTimeout;
+ /** Detect various environments */
+ var isIeOpera = reNative.test(window.attachEvent),
+ isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera);
/*--------------------------------------------------------------------------*/
/**
- * The `lodash` function.
+ * Creates a `lodash` object, that wraps the given `value`, to enable method
+ * chaining.
+ *
+ * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
+ * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
+ * and `unshift`
+ *
+ * Chaining is supported in custom builds as long as the `value` method is
+ * implicitly or explicitly included in the build.
+ *
+ * The chainable wrapper functions are:
+ * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
+ * `compose`, `concat`, `countBy`, `createCallback`, `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`, `partialRight`, `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`, `parseInt`,
+ * `pop`, `random`, `reduce`, `reduceRight`, `result`, `shift`, `size`, `some`,
+ * `sortedIndex`, `runInContext`, `template`, `unescape`, `uniqueId`, and `value`
+ *
+ * The wrapper functions `first` and `last` return wrapped values when `n` is
+ * passed, otherwise they return unwrapped values.
*
* @name _
* @constructor
- * @param {Mixed} value The value to wrap in a `LoDash` instance.
- * @returns {Object} Returns a `LoDash` instance.
+ * @category Chaining
+ * @param {Mixed} value The value to wrap in a `lodash` instance.
+ * @returns {Object} Returns a `lodash` instance.
*/
function lodash(value) {
- // allow invoking `lodash` without the `new` operator
- return new LoDash(value);
+ return (value instanceof lodash)
+ ? value
+ : new lodashWrapper(value);
}
/**
- * Creates a `LoDash` instance that wraps a value to allow chaining.
+ * An object used to flag environments features.
*
- * @private
- * @constructor
- * @param {Mixed} value The value to wrap.
+ * @static
+ * @memberOf _
+ * @type Object
*/
- function LoDash(value) {
- // exit early if already wrapped
- if (value && value._wrapped) {
- return value;
- }
- this._wrapped = value;
- }
+ var support = {};
+
+ (function() {
+ var object = { '0': 1, 'length': 1 };
+
+ /**
+ * Detect if `arguments` objects are `Object` objects (all but Opera < 10.5).
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ support.argsObject = arguments.constructor == Object;
+
+ /**
+ * Detect if `Function#bind` exists and is inferred to be fast (all but V8).
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ support.fastBind = nativeBind && !isV8;
+
+ /**
+ * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly.
+ *
+ * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()`
+ * and `splice()` functions that fail to remove the last element, `value[0]`,
+ * of array-like objects even though the `length` property is set to `0`.
+ * 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.
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]);
+ }(1));
/**
- * By default, Lo-Dash uses ERB-style template delimiters, change the
- * following template settings to use alternative delimiters.
+ * By default, the template delimiters used by Lo-Dash are similar to those in
+ * embedded Ruby (ERB). Change the following template settings to use alternative
+ * delimiters.
*
* @static
* @memberOf _
@@ -170,7 +240,6 @@
/**
* Used to detect `data` property values to be HTML-escaped.
*
- * @static
* @memberOf _.templateSettings
* @type RegExp
*/
@@ -179,7 +248,6 @@
/**
* Used to detect code to be evaluated.
*
- * @static
* @memberOf _.templateSettings
* @type RegExp
*/
@@ -188,279 +256,125 @@
/**
* Used to detect `data` property values to inject.
*
- * @static
* @memberOf _.templateSettings
* @type RegExp
*/
- 'interpolate': /<%=([\s\S]+?)%>/g,
+ 'interpolate': reInterpolate,
/**
* Used to reference the data object in the template text.
*
- * @static
* @memberOf _.templateSettings
* @type String
*/
- 'variable': 'obj'
+ 'variable': ''
};
/*--------------------------------------------------------------------------*/
/**
- * The template used to create iterator functions.
+ * Used by `_.max` and `_.min` as the default `callback` when a given
+ * `collection` is a string value.
*
* @private
- * @param {Obect} data The data object used to populate the text.
- * @returns {String} Returns the interpolated text.
- */
- var iteratorTemplate = template(
- // assign the `result` variable an initial value
- 'var index, result<% if (init) { %> = <%= init %><% } %>;\n' +
- // add code to exit early or do so if the first argument is falsey
- '<%= exit %>;\n' +
- // add code after the exit snippet but before the iteration branches
- '<%= top %>;\n' +
-
- // the following branch is for iterating arrays and array-like objects
- '<% if (arrayBranch) { %>' +
- 'var length = <%= firstArg %>.length; index = -1;' +
- ' <% if (objectBranch) { %>\nif (length === length >>> 0) {<% } %>\n' +
- ' <%= arrayBranch.beforeLoop %>;\n' +
- ' while (<%= arrayBranch.loopExp %>) {\n' +
- ' <%= arrayBranch.inLoop %>;\n' +
- ' }' +
- ' <% if (objectBranch) { %>\n}\n<% }' +
- '}' +
-
- // the following branch is for iterating an object's own/inherited properties
- 'if (objectBranch) {' +
- ' if (arrayBranch) { %>else {\n<% }' +
- ' if (!hasDontEnumBug) { %> var skipProto = typeof <%= iteratedObject %> == \'function\';\n<% } %>' +
- ' <%= objectBranch.beforeLoop %>;\n' +
- ' for (<%= objectBranch.loopExp %>) {' +
- ' \n<%' +
- ' if (hasDontEnumBug) {' +
- ' if (useHas) { %> if (<%= hasExp %>) {\n <% } %>' +
- ' <%= objectBranch.inLoop %>;<%' +
- ' if (useHas) { %>\n }<% }' +
- ' }' +
- ' else {' +
- ' %>' +
-
- // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
- // (if the prototype or a property on the prototype has been set)
- // incorrectly sets a function's `prototype` property [[Enumerable]]
- // value to `true`. Because of this Lo-Dash standardizes on skipping
- // the the `prototype` property of functions regardless of its
- // [[Enumerable]] value.
- ' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> && <%= hasExp %><% } %>) {\n' +
- ' <%= objectBranch.inLoop %>;\n' +
- ' }' +
- ' <% } %>\n' +
- ' }' +
-
- // Because IE < 9 can't set the `[[Enumerable]]` attribute of an
- // existing property and the `constructor` property of a prototype
- // defaults to non-enumerable, Lo-Dash skips the `constructor`
- // property when it infers it's iterating over a `prototype` object.
- ' <% if (hasDontEnumBug) { %>\n' +
- ' var ctor = <%= iteratedObject %>.constructor;\n' +
- ' <% for (var k = 0; k < 7; k++) { %>\n' +
- ' index = \'<%= shadowed[k] %>\';\n' +
- ' if (<%' +
- ' if (shadowed[k] == \'constructor\') {' +
- ' %>!(ctor && ctor.prototype === <%= iteratedObject %>) && <%' +
- ' } %><%= hasExp %>) {\n' +
- ' <%= objectBranch.inLoop %>;\n' +
- ' }<%' +
- ' }' +
- ' }' +
- ' if (arrayBranch) { %>\n}<% }' +
- '} %>\n' +
-
- // add code to the bottom of the iteration function
- '<%= bottom %>;\n' +
- // finally, return the `result`
- 'return result'
- );
+ * @param {String} value The character to inspect.
+ * @returns {Number} Returns the code unit of given character.
+ */
+ function charAtCallback(value) {
+ return value.charCodeAt(0);
+ }
/**
- * Reusable iterator options shared by
- * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `map`, `reject`, and `some`.
- */
- var baseIteratorOptions = {
- 'args': 'collection, callback, thisArg',
- 'init': 'collection',
- 'top':
- 'if (!callback) {\n' +
- ' callback = identity\n' +
- '}\n' +
- 'else if (thisArg) {\n' +
- ' callback = iteratorBind(callback, thisArg)\n' +
- '}',
- 'inLoop': 'callback(collection[index], index, collection)'
- };
-
- /** Reusable iterator options for `every` and `some` */
- var everyIteratorOptions = {
- 'init': 'true',
- 'inLoop': 'if (!callback(collection[index], index, collection)) return !result'
- };
-
- /** Reusable iterator options for `defaults` and `extend` */
- var extendIteratorOptions = {
- 'args': 'object',
- 'init': 'object',
- 'top':
- 'for (var source, sourceIndex = 1, length = arguments.length; sourceIndex < length; sourceIndex++) {\n' +
- ' source = arguments[sourceIndex];\n' +
- (hasDontEnumBug ? ' if (source) {' : ''),
- 'loopExp': 'index in source',
- 'useHas': false,
- 'inLoop': 'object[index] = source[index]',
- 'bottom': (hasDontEnumBug ? ' }\n' : '') + '}'
- };
-
- /** Reusable iterator options for `filter` and `reject` */
- var filterIteratorOptions = {
- 'init': '[]',
- 'inLoop': 'callback(collection[index], index, collection) && result.push(collection[index])'
- };
+ * Used by `sortBy` to compare transformed `collection` values, stable sorting
+ * them in ascending order.
+ *
+ * @private
+ * @param {Object} a The object to compare to `b`.
+ * @param {Object} b The object to compare to `a`.
+ * @returns {Number} Returns the sort order indicator of `1` or `-1`.
+ */
+ function compareAscending(a, b) {
+ var ai = a.index,
+ bi = b.index;
- /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */
- var forEachIteratorOptions = {
- 'top': 'if (thisArg) callback = iteratorBind(callback, thisArg)'
- };
+ a = a.criteria;
+ b = b.criteria;
- /** Reusable iterator options for `forIn` and `forOwn` */
- var forOwnIteratorOptions = {
- 'inLoop': {
- 'object': baseIteratorOptions.inLoop
+ // 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 || typeof a == 'undefined') {
+ return 1;
+ }
+ if (a < b || typeof b == 'undefined') {
+ return -1;
+ }
}
- };
-
- /*--------------------------------------------------------------------------*/
+ return ai < bi ? -1 : 1;
+ }
/**
- * 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 a function that, when called, invokes `func` with the `this` binding
+ * of `thisArg` and prepends any `partialArgs` to the arguments passed to the
+ * bound function.
*
* @private
- * @param {Object} [options1, options2, ...] The compile options objects.
- *
- * args - A string of comma separated arguments the iteration function will
- * accept.
- *
- * init - A string to specify the initial value of the `result` variable.
- *
- * exit - A string of code to use in place of the default exit-early check
- * of `if (!arguments[0]) return result`.
- *
- * top - A string of code to execute after the exit-early check but 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.
- *
- * loopExp - A string or object containing an "array" or "object" property
- * of code to execute as the array or object loop expression.
- *
- * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks
- * in the object loop.
- *
- * 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.
- *
- * @returns {Function} Returns the compiled function.
+ * @param {Function|String} func The function to bind or the method name.
+ * @param {Mixed} [thisArg] The `this` binding of `func`.
+ * @param {Array} partialArgs An array of arguments to be partially applied.
+ * @param {Object} [idicator] Used to indicate binding by key or partially
+ * applying arguments from the right.
+ * @returns {Function} Returns the new bound function.
*/
- function createIterator() {
- var object,
- prop,
- value,
- index = -1,
- length = arguments.length;
-
- // merge options into a template data object
- var data = {
- 'bottom': '',
- 'exit': '',
- 'init': '',
- 'top': '',
- 'arrayBranch': { 'beforeLoop': '', 'loopExp': '++index < length' },
- 'objectBranch': { 'beforeLoop': '' }
- };
+ function createBound(func, thisArg, partialArgs, indicator) {
+ var isFunc = isFunction(func),
+ isPartial = !partialArgs,
+ key = thisArg;
- 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|loopExp|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;
- }
+ // juggle arguments
+ if (isPartial) {
+ var rightIndicator = indicator;
+ partialArgs = thisArg;
+ }
+ else if (!isFunc) {
+ if (!indicator) {
+ throw new TypeError;
}
+ thisArg = func;
}
- // set additional template data values
- var args = data.args,
- arrayBranch = data.arrayBranch,
- objectBranch = data.objectBranch,
- firstArg = /^[^,]+/.exec(args)[0],
- loopExp = objectBranch.loopExp,
- iteratedObject = /\S+$/.exec(loopExp || firstArg)[0];
- data.firstArg = firstArg;
- data.hasDontEnumBug = hasDontEnumBug;
- data.hasExp = 'hasOwnProperty.call(' + iteratedObject + ', index)';
- data.iteratedObject = iteratedObject;
- data.shadowed = shadowed;
- data.useHas = data.useHas !== false;
+ function bound() {
+ // `Function#bind` spec
+ // http://es5.github.com/#x15.3.4.5
+ var args = arguments,
+ thisBinding = isPartial ? this : thisArg;
- if (!data.exit) {
- data.exit = 'if (!' + firstArg + ') return result';
- }
- if (firstArg == 'object' || !arrayBranch.inLoop) {
- data.arrayBranch = null;
- }
- if (!loopExp) {
- objectBranch.loopExp = 'index in ' + iteratedObject;
- }
- // create the function factory
- var factory = Function(
- 'arrayClass, funcClass, hasOwnProperty, identity, iteratorBind, objectTypes, ' +
- 'stringClass, toString, undefined',
- '"use strict"; return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
- );
- // return the compiled function
- return factory(
- arrayClass, funcClass, hasOwnProperty, identity, iteratorBind, objectTypes,
- stringClass, toString
- );
- }
+ if (!isFunc) {
+ func = thisArg[key];
+ }
+ if (partialArgs.length) {
+ args = args.length
+ ? (args = slice.call(args), rightIndicator ? args.concat(partialArgs) : partialArgs.concat(args))
+ : partialArgs;
+ }
+ if (this instanceof bound) {
+ // ensure `new bound` is an instance of `func`
+ noop.prototype = func.prototype;
+ thisBinding = new noop;
+ noop.prototype = null;
- /**
- * Used by `template()` to replace tokens with their corresponding code snippets.
- *
- * @private
- * @param {String} match The matched token.
- * @param {String} index The `tokenized` index of the code snippet.
- * @returns {String} Returns the code snippet.
- */
- function detokenize(match, index) {
- return tokenized[index];
+ // mimic the constructor's `return` behavior
+ // http://es5.github.com/#x13.2.2
+ var result = func.apply(thisBinding, args);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisBinding, args);
+ }
+ return bound;
}
/**
- * Used by `template()` to escape characters for inclusion in compiled
+ * Used by `template` to escape characters for inclusion in compiled
* string literals.
*
* @private
@@ -472,7 +386,7 @@
}
/**
- * Used by `escape()` to escape characters for inclusion in HTML.
+ * Used by `escape` to convert characters to HTML entities.
*
* @private
* @param {String} match The matched character to escape.
@@ -483,19 +397,17 @@
}
/**
- * Creates a new function that, when called, invokes `func` with the `this`
- * binding of `thisArg` and the arguments (value, index, object).
+ * A fast path for creating `lodash` wrapper objects.
*
* @private
- * @param {Function} func The function to bind.
- * @param {Mixed} [thisArg] The `this` binding of `func`.
- * @returns {Function} Returns the new bound function.
+ * @param {Mixed} value The value to wrap in a `lodash` instance.
+ * @returns {Object} Returns a `lodash` instance.
*/
- function iteratorBind(func, thisArg) {
- return function(value, index, object) {
- return func.call(thisArg, value, index, object);
- };
+ function lodashWrapper(value) {
+ this.__wrapped__ = value;
}
+ // ensure `new lodashWrapper` is an instance of `lodash`
+ lodashWrapper.prototype = lodash.prototype;
/**
* A no-operation function.
@@ -507,2484 +419,3264 @@
}
/**
- * A shim 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',
- 'exit': 'if (!objectTypes[typeof object] || object === null) throw TypeError()',
- 'init': '[]',
- 'inLoop': 'result.push(index)'
- });
-
- /**
- * Used by `template()` to replace "escape" template delimiters with tokens.
- *
- * @private
- * @param {String} match The matched template delimiter.
- * @param {String} value The delimiter value.
- * @returns {String} Returns a token.
- */
- function tokenizeEscape(match, value) {
- var index = tokenized.length;
- tokenized[index] = "'+\n_.escape(" + value + ") +\n'";
- return token + index;
- }
-
- /**
- * Used by `template()` to replace "interpolate" template delimiters with tokens.
- *
- * @private
- * @param {String} match The matched template delimiter.
- * @param {String} value The delimiter value.
- * @returns {String} Returns a token.
- */
- function tokenizeInterpolate(match, value) {
- var index = tokenized.length;
- tokenized[index] = "'+\n((__t = (" + value + ")) == null ? '' : __t) +\n'";
- return token + index;
- }
-
- /**
- * Used by `template()` to replace "evaluate" template delimiters with tokens.
+ * Used by `unescape` to convert HTML entities to characters.
*
* @private
- * @param {String} match The matched template delimiter.
- * @param {String} value The delimiter value.
- * @returns {String} Returns a token.
+ * @param {String} match The matched character to unescape.
+ * @returns {String} Returns the unescaped character.
*/
- function tokenizeEvaluate(match, value) {
- var index = tokenized.length;
- tokenized[index] = "';\n" + value + ";\n__p += '";
- return token + index;
+ function unescapeHtmlChar(match) {
+ return htmlUnescapes[match];
}
/*--------------------------------------------------------------------------*/
/**
- * Checks if a given `target` value is present in a `collection` using strict
- * equality for comparisons, i.e. `===`.
+ * Checks if `value` is an `arguments` object.
*
* @static
* @memberOf _
- * @alias include
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Mixed} target The value to check for.
- * @returns {Boolean} Returns `true` if `target` value is found, else `false`.
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is an `arguments` object, else `false`.
* @example
*
- * _.contains([1, 2, 3], 3);
+ * (function() { return _.isArguments(arguments); })(1, 2, 3);
* // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
*/
- var contains = createIterator({
- 'args': 'collection, target',
- 'init': 'false',
- 'inLoop': 'if (collection[index] === target) return true'
- });
+ function isArguments(value) {
+ return toString.call(value) == argsClass;
+ }
+ // fallback for browsers that can't detect `arguments` objects by [[Class]]
+ if (!isArguments(arguments)) {
+ isArguments = function(value) {
+ return value ? hasOwnProperty.call(value, 'callee') : false;
+ };
+ }
/**
- * Checks if the `callback` returns a truthy value for **all** elements of a
- * `collection`. The `callback` is bound to `thisArg` and invoked with 3
- * arguments; for arrays they are (value, index, array) and for objects they
- * are (value, key, object).
+ * Checks if `value` is an array.
*
* @static
* @memberOf _
- * @alias all
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [callback=identity] The function called per iteration.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Boolean} Returns `true` if all values pass the callback check, else `false`.
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is an array, else `false`.
* @example
*
- * _.every([true, 1, null, 'yes'], Boolean);
+ * (function() { return _.isArray(arguments); })();
* // => false
+ *
+ * _.isArray([1, 2, 3]);
+ * // => true
*/
- var every = createIterator(baseIteratorOptions, everyIteratorOptions);
+ 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 (support.argsObject && value instanceof Array) || toString.call(value) == arrayClass;
+ };
/**
- * Examines each value in a `collection`, returning an array of all values the
- * `callback` returns truthy for. The `callback` is bound to `thisArg` and
- * invoked with 3 arguments; for arrays they are (value, index, array) and for
- * objects they are (value, key, object).
- *
- * @static
- * @memberOf _
- * @alias select
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [callback=identity] The function called per iteration.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Array} Returns a new array of values that passed callback check.
- * @example
+ * A fallback implementation of `Object.keys` that produces an array of the
+ * given object's own enumerable property names.
*
- * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
- * // => [2, 4, 6]
+ * @private
+ * @type Function
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns a new array of property names.
*/
- var filter = createIterator(baseIteratorOptions, filterIteratorOptions);
+ var shimKeys = function (object) {
+ var index, iterable = object, result = [];
+ if (!iterable) return result;
+ if (!(objectTypes[typeof object])) return result;
+
+ for (index in iterable) {
+ if (hasOwnProperty.call(iterable, index)) {
+ result.push(index);
+ }
+ }
+ return result
+ };
/**
- * Examines each value in a `collection`, returning the first one the `callback`
- * returns truthy for. The function returns as soon as it finds an acceptable
- * value, and does not iterate over the entire `collection`. The `callback` is
- * bound to `thisArg` and invoked with 3 arguments; for arrays they are
- * (value, index, array) and for objects they are (value, key, object).
+ * Creates an array composed of the own enumerable property names of `object`.
*
* @static
* @memberOf _
- * @alias detect
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Mixed} Returns the value that passed the callback check, else `undefined`.
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns a new array of property names.
* @example
*
- * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
- * // => 2
+ * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => ['one', 'two', 'three'] (order is not guaranteed)
*/
- var find = createIterator(baseIteratorOptions, forEachIteratorOptions, {
- 'init': '',
- 'inLoop': 'if (callback(collection[index], index, collection)) return collection[index]'
- });
+ var keys = !nativeKeys ? shimKeys : function(object) {
+ if (!isObject(object)) {
+ return [];
+ }
+ return nativeKeys(object);
+ };
/**
- * Iterates over a `collection`, executing the `callback` for each value in the
- * `collection`. The `callback` is bound to `thisArg` and invoked with 3
- * arguments; for arrays they are (value, index, array) and for objects they
- * are (value, key, object).
- *
- * @static
- * @memberOf _
- * @alias each
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Array|Object} Returns the `collection`.
- * @example
- *
- * _([1, 2, 3]).forEach(alert).join(',');
- * // => alerts each number and returns '1,2,3'
+ * Used to convert characters to HTML entities:
*
- * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert);
- * // => alerts each number (order is not guaranteed)
+ * Though the `>` character is escaped for symmetry, characters like `>` and `/`
+ * don't require escaping in HTML and have no special meaning unless they're part
+ * of a tag or an unquoted attribute value.
+ * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
*/
- var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions);
+ var htmlEscapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ };
+
+ /** Used to convert HTML entities to characters */
+ var htmlUnescapes = invert(htmlEscapes);
+
+ /*--------------------------------------------------------------------------*/
/**
- * Produces a new array of values by mapping each value in the `collection`
- * through a transformation `callback`. The `callback` is bound to `thisArg`
- * and invoked with 3 arguments; for arrays they are (value, index, array)
- * and for objects they are (value, key, object).
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object. Subsequent sources will overwrite property assignments of previous
+ * sources. If a `callback` function is passed, it will be executed to produce
+ * the assigned values. The `callback` is bound to `thisArg` and invoked with
+ * two arguments; (objectValue, sourceValue).
*
* @static
* @memberOf _
- * @alias collect
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [callback=identity] The function called per iteration.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Array} Returns a new array of values returned by the callback.
+ * @type Function
+ * @alias extend
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {Object} [source1, source2, ...] The source objects.
+ * @param {Function} [callback] The function to customize assigning values.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the destination object.
* @example
*
- * _.map([1, 2, 3], function(num) { return num * 3; });
- * // => [3, 6, 9]
+ * _.assign({ 'name': 'moe' }, { 'age': 40 });
+ * // => { 'name': 'moe', 'age': 40 }
*
- * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
- * // => [3, 6, 9] (order is not guaranteed)
+ * var defaults = _.partialRight(_.assign, function(a, b) {
+ * return typeof a == 'undefined' ? b : a;
+ * });
+ *
+ * var food = { 'name': 'apple' };
+ * defaults(food, { 'name': 'banana', 'type': 'fruit' });
+ * // => { 'name': 'apple', 'type': 'fruit' }
*/
- var map = createIterator(baseIteratorOptions, {
- 'init': '',
- 'exit': 'if (!collection) return []',
- 'beforeLoop': {
- 'array': 'result = Array(length)',
- 'object': 'result = []'
- },
- 'inLoop': {
- 'array': 'result[index] = callback(collection[index], index, collection)',
- 'object': 'result.push(callback(collection[index], index, collection))'
+ function assign(object) {
+ if (!object) {
+ return object;
}
- });
-
- /**
- * Boils down a `collection` to a single value. The initial state of the
- * reduction is `accumulator` and each successive step of it should be returned
- * by the `callback`. The `callback` is bound to `thisArg` and invoked with 4
- * arguments; for arrays they are (accumulator, value, index, array) and for
- * objects they are (accumulator, value, key, object).
+ for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {
+ var iterable = arguments[argsIndex];
+ if (iterable) {
+ for (var key in iterable) {
+ object[key] = iterable[key];
+ }
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Creates a clone of `value`. If `deep` is `true`, nested objects will also
+ * be cloned, otherwise they will be assigned by reference. If a `callback`
+ * function is passed, it will be executed to produce the cloned values. If
+ * `callback` returns `undefined`, cloning will be handled by the method instead.
+ * The `callback` is bound to `thisArg` and invoked with one argument; (value).
*
* @static
* @memberOf _
- * @alias foldl, inject
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} [accumulator] Initial value of the accumulator.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Mixed} Returns the accumulated value.
+ * @category Objects
+ * @param {Mixed} value The value to clone.
+ * @param {Boolean} [deep=false] A flag to indicate a deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @param- {Array} [stackA=[]] Tracks traversed source objects.
+ * @param- {Array} [stackB=[]] Associates clones with source counterparts.
+ * @returns {Mixed} Returns the cloned `value`.
* @example
*
- * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; });
- * // => 6
+ * var stooges = [
+ * { 'name': 'moe', 'age': 40 },
+ * { 'name': 'larry', 'age': 50 }
+ * ];
+ *
+ * var shallow = _.clone(stooges);
+ * shallow[0] === stooges[0];
+ * // => true
+ *
+ * var deep = _.clone(stooges, true);
+ * deep[0] === stooges[0];
+ * // => false
+ *
+ * _.mixin({
+ * 'clone': _.partialRight(_.clone, function(value) {
+ * return _.isElement(value) ? value.cloneNode(false) : undefined;
+ * })
+ * });
+ *
+ * var clone = _.clone(document.body);
+ * clone.childNodes.length;
+ * // => 0
*/
- var reduce = createIterator({
- 'args': 'collection, callback, accumulator, thisArg',
- 'init': 'accumulator',
- 'top':
- 'var noaccum = arguments.length < 3;\n' +
- 'if (thisArg) callback = iteratorBind(callback, thisArg)',
- 'beforeLoop': {
- 'array': 'if (noaccum) result = collection[++index]'
- },
- 'inLoop': {
- 'array':
- 'result = callback(result, collection[index], index, collection)',
- 'object':
- 'result = noaccum\n' +
- ' ? (noaccum = false, collection[index])\n' +
- ' : callback(result, collection[index], index, collection)'
- }
- });
+ function clone(value) {
+ return isObject(value)
+ ? (isArray(value) ? slice.call(value) : assign({}, value))
+ : value;
+ }
/**
- * The right-associative version of `_.reduce`.
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object for all destination properties that resolve to `undefined`. Once a
+ * property is set, additional defaults of the same property will be ignored.
*
* @static
* @memberOf _
- * @alias foldr
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} [accumulator] Initial value of the accumulator.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Mixed} Returns the accumulated value.
+ * @type Function
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {Object} [source1, source2, ...] The source objects.
+ * @param- {Object} [guard] Allows working with `_.reduce` without using its
+ * callback's `key` and `object` arguments as sources.
+ * @returns {Object} Returns the destination object.
* @example
*
- * var list = [[0, 1], [2, 3], [4, 5]];
- * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
- * // => [4, 5, 2, 3, 0, 1]
+ * var food = { 'name': 'apple' };
+ * _.defaults(food, { 'name': 'banana', 'type': 'fruit' });
+ * // => { 'name': 'apple', 'type': 'fruit' }
*/
- function reduceRight(collection, callback, accumulator, thisArg) {
- if (!collection) {
- return accumulator;
- }
-
- var length = collection.length,
- noaccum = arguments.length < 3;
-
- if(thisArg) {
- callback = iteratorBind(callback, thisArg);
+ function defaults(object) {
+ if (!object) {
+ return object;
}
- if (length === length >>> 0) {
- if (length && noaccum) {
- accumulator = collection[--length];
- }
- while (length--) {
- accumulator = callback(accumulator, collection[length], length, collection);
+ for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {
+ var iterable = arguments[argsIndex];
+ if (iterable) {
+ for (var key in iterable) {
+ if (object[key] == null) {
+ object[key] = iterable[key];
+ }
+ }
}
- return accumulator;
- }
-
- var prop,
- props = keys(collection);
-
- length = props.length;
- if (length && noaccum) {
- accumulator = collection[props[--length]];
- }
- while (length--) {
- prop = props[length];
- accumulator = callback(accumulator, collection[prop], prop, collection);
}
- return accumulator;
+ return object;
}
/**
- * The opposite of `_.filter`, this method returns the values of a `collection`
- * that `callback` does **not** return truthy for.
+ * 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 Collections
- * @param {Array|Object} collection The collection to iterate over.
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Array} Returns a new array of values that did **not** pass the callback check.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
* @example
*
- * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
- * // => [1, 3, 5]
+ * 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)
*/
- var reject = createIterator(baseIteratorOptions, filterIteratorOptions, {
- 'inLoop': '!' + filterIteratorOptions.inLoop
- });
+ var forIn = function (collection, callback) {
+ var index, iterable = collection, result = iterable;
+ if (!iterable) return result;
+ if (!objectTypes[typeof iterable]) return result;
+
+ for (index in iterable) {
+ if (callback(iterable[index], index, collection) === indicatorObject) return result;
+ }
+ return result
+ };
/**
- * Checks if the `callback` returns a truthy value for **any** element of a
- * `collection`. The function returns as soon as it finds passing value, and
- * does not iterate over the entire `collection`. The `callback` is bound to
- * `thisArg` and invoked with 3 arguments; for arrays they are
- * (value, index, array) and for objects they are (value, key, object).
+ * 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 _
- * @alias any
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Boolean} Returns `true` if any value passes the callback check, else `false`.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
* @example
*
- * _.some([null, 0, 'yes', false]);
- * // => true
+ * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+ * alert(key);
+ * });
+ * // => alerts '0', '1', and 'length' (order is not guaranteed)
*/
- var some = createIterator(baseIteratorOptions, everyIteratorOptions, {
- 'init': 'false',
- 'inLoop': everyIteratorOptions.inLoop.replace('!', '')
- });
+ var forOwn = function (collection, callback) {
+ var index, iterable = collection, result = iterable;
+ if (!iterable) return result;
+ if (!objectTypes[typeof iterable]) return result;
+
+ for (index in iterable) {
+ if (hasOwnProperty.call(iterable, index)) {
+ if (callback(iterable[index], index, collection) === indicatorObject) return result;
+ }
+ }
+ return result
+ };
/**
- * Converts the `collection`, into an array. Useful for converting the
- * `arguments` object.
+ * Creates a sorted array of all enumerable properties, own and inherited,
+ * of `object` that have function values.
*
* @static
* @memberOf _
- * @category Collections
- * @param {Array|Object} collection The collection to convert.
- * @returns {Array} Returns the new converted array.
+ * @alias methods
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns a new array of property names that have function values.
* @example
*
- * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
- * // => [2, 3, 4]
+ * _.functions(_);
+ * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
*/
- function toArray(collection) {
- if (!collection) {
- return [];
- }
- if (toString.call(collection.toArray) == funcClass) {
- return collection.toArray();
- }
- var length = collection.length;
- if (length === length >>> 0) {
- return slice.call(collection);
- }
- return values(collection);
+ function functions(object) {
+ var result = [];
+ forIn(object, function(value, key) {
+ if (isFunction(value)) {
+ result.push(key);
+ }
+ });
+ return result.sort();
}
- /*--------------------------------------------------------------------------*/
-
/**
- * Produces a new array with all falsey values of `array` removed. The values
- * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey.
+ * Checks if the specified object `property` exists and is a direct property,
+ * instead of an inherited property.
*
* @static
* @memberOf _
- * @category Arrays
- * @param {Array} array The array to compact.
- * @returns {Array} Returns a new filtered array.
+ * @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`.
* @example
*
- * _.compact([0, 1, false, 2, '', 3]);
- * // => [1, 2, 3]
+ * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
+ * // => true
*/
- function compact(array) {
- var result = [];
- if (!array) {
- return result;
- }
- var index = -1,
- length = array.length;
-
- while (++index < length) {
- if (array[index]) {
- result.push(array[index]);
- }
- }
- return result;
+ function has(object, property) {
+ return object ? hasOwnProperty.call(object, property) : false;
}
/**
- * Produces a new array of `array` values not present in the other arrays
- * using strict equality for comparisons, i.e. `===`.
+ * Creates an object composed of the inverted keys and values of the given `object`.
*
* @static
* @memberOf _
- * @category Arrays
- * @param {Array} array The array to process.
- * @param {Array} [array1, array2, ...] Arrays to check.
- * @returns {Array} Returns a new array of `array` values not present in the
- * other arrays.
+ * @category Objects
+ * @param {Object} object The object to invert.
+ * @returns {Object} Returns the created inverted object.
* @example
*
- * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
- * // => [1, 3, 4]
+ * _.invert({ 'first': 'moe', 'second': 'larry' });
+ * // => { 'moe': 'first', 'larry': 'second' }
*/
- function difference(array) {
- var result = [];
- if (!array) {
- return result;
- }
+ function invert(object) {
var index = -1,
- length = array.length,
- flattened = concat.apply(result, slice.call(arguments, 1));
+ props = keys(object),
+ length = props.length,
+ result = {};
while (++index < length) {
- if (indexOf(flattened, array[index]) < 0) {
- result.push(array[index]);
- }
+ var key = props[index];
+ result[object[key]] = key;
}
return result;
}
/**
- * Gets the first value of the `array`. Pass `n` to return the first `n` values
- * of the `array`.
+ * Checks if `value` is a boolean value.
*
* @static
* @memberOf _
- * @alias head, take
- * @category Arrays
- * @param {Array} array The array to query.
- * @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 value or an array of the first `n` values
- * of `array`.
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is a boolean value, else `false`.
* @example
*
- * _.first([5, 4, 3, 2, 1]);
- * // => 5
+ * _.isBoolean(null);
+ * // => false
*/
- function first(array, n, guard) {
- if (array) {
- return (n == undefined || guard) ? array[0] : slice.call(array, 0, n);
- }
+ function isBoolean(value) {
+ return value === true || value === false || toString.call(value) == boolClass;
}
/**
- * Flattens a nested array (the nesting can be to any depth). If `shallow` is
- * truthy, `array` will only be flattened a single level.
+ * Checks if `value` is a date.
*
* @static
* @memberOf _
- * @category Arrays
- * @param {Array} array The array to compact.
- * @param {Boolean} shallow A flag to indicate only flattening a single level.
- * @returns {Array} Returns a new flattened array.
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is a date, else `false`.
* @example
*
- * _.flatten([1, [2], [3, [[4]]]]);
- * // => [1, 2, 3, 4];
- *
- * _.flatten([1, [2], [3, [[4]]]], true);
- * // => [1, 2, 3, [[4]]];
+ * _.isDate(new Date);
+ * // => true
*/
- function flatten(array, shallow) {
- var result = [];
- if (!array) {
- return result;
- }
- var value,
- index = -1,
- length = array.length;
+ function isDate(value) {
+ return value instanceof Date || toString.call(value) == dateClass;
+ }
- while (++index < length) {
- value = array[index];
- if (isArray(value)) {
- push.apply(result, shallow ? value : flatten(value));
- } else {
- result.push(value);
- }
- }
- return result;
+ /**
+ * Checks if `value` is a DOM element.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is a DOM element, else `false`.
+ * @example
+ *
+ * _.isElement(document.body);
+ * // => true
+ */
+ function isElement(value) {
+ return value ? value.nodeType === 1 : false;
}
/**
- * Splits `array` into sets, grouped by the result of running each value
- * through `callback`. The `callback` is bound to `thisArg` and invoked with 3
- * arguments; (value, index, array). The `callback` argument may also be the
- * name of a property to group by.
+ * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
+ * length of `0` and objects with no own enumerable properties are considered
+ * "empty".
*
* @static
* @memberOf _
- * @category Arrays
- * @param {Array} array The array to iterate over.
- * @param {Function|String} callback The function called per iteration or
- * property name to group by.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Object} Returns an object of grouped values.
+ * @category Objects
+ * @param {Array|Object|String} value The value to inspect.
+ * @returns {Boolean} Returns `true`, if the `value` is empty, else `false`.
* @example
*
- * _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); });
- * // => { '1': [1.3], '2': [2.1, 2.4] }
+ * _.isEmpty([1, 2, 3]);
+ * // => false
*
- * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math);
- * // => { '1': [1.3], '2': [2.1, 2.4] }
+ * _.isEmpty({});
+ * // => true
*
- * _.groupBy(['one', 'two', 'three'], 'length');
- * // => { '3': ['one', 'two'], '5': ['three'] }
+ * _.isEmpty('');
+ * // => true
*/
- function groupBy(array, callback, thisArg) {
- var result = {};
- if (!array) {
- return result;
+ function isEmpty(value) {
+ if (!value) {
+ return true;
}
- var prop,
- value,
- index = -1,
- isFunc = typeof callback == 'function',
- length = array.length;
-
- if (isFunc && thisArg) {
- callback = iteratorBind(callback, thisArg);
+ if (isArray(value) || isString(value)) {
+ return !value.length;
}
- while (++index < length) {
- value = array[index];
- prop = isFunc ? callback(value, index, array) : value[callback];
- (hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(value);
+ for (var key in value) {
+ if (hasOwnProperty.call(value, key)) {
+ return false;
+ }
}
- return result;
+ return true;
}
/**
- * 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.
+ * Performs a deep comparison between two values to determine if they are
+ * equivalent to each other. If `callback` is passed, it will be executed to
+ * compare values. If `callback` returns `undefined`, comparisons will be handled
+ * by the method instead. The `callback` is bound to `thisArg` and invoked with
+ * two arguments; (a, b).
*
* @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`.
- * @returns {Number} Returns the index of the matched value or `-1`.
+ * @category Objects
+ * @param {Mixed} a The value to compare.
+ * @param {Mixed} b The other value to compare.
+ * @param {Function} [callback] The function to customize comparing values.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @param- {Array} [stackA=[]] Tracks traversed `a` objects.
+ * @param- {Array} [stackB=[]] Tracks traversed `b` objects.
+ * @returns {Boolean} Returns `true`, if the values are equivalent, else `false`.
* @example
*
- * _.indexOf([1, 2, 3, 1, 2, 3], 2);
- * // => 1
+ * var moe = { 'name': 'moe', 'age': 40 };
+ * var copy = { 'name': 'moe', 'age': 40 };
*
- * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
- * // => 4
+ * moe == copy;
+ * // => false
*
- * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
- * // => 2
- */
- function indexOf(array, value, fromIndex) {
- if (!array) {
- return -1;
+ * _.isEqual(moe, copy);
+ * // => true
+ *
+ * var words = ['hello', 'goodbye'];
+ * var otherWords = ['hi', 'goodbye'];
+ *
+ * _.isEqual(words, otherWords, function(a, b) {
+ * var reGreet = /^(?:hello|hi)$/i,
+ * aGreet = _.isString(a) && reGreet.test(a),
+ * bGreet = _.isString(b) && reGreet.test(b);
+ *
+ * return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
+ * });
+ * // => true
+ */
+ function isEqual(a, b, stackA, stackB) {
+ if (a === b) {
+ return a !== 0 || (1 / a == 1 / b);
}
- var index = -1,
- length = array.length;
+ var type = typeof a,
+ otherType = typeof b;
- if (fromIndex) {
- if (typeof fromIndex == 'number') {
- index = (fromIndex < 0 ? Math.max(0, length + fromIndex) : fromIndex) - 1;
- } else {
- index = sortedIndex(array, value);
- return array[index] === value ? index : -1;
+ if (a === a &&
+ (!a || (type != 'function' && type != 'object')) &&
+ (!b || (otherType != 'function' && otherType != 'object'))) {
+ return false;
+ }
+ if (a == null || b == null) {
+ return a === b;
+ }
+ var className = toString.call(a),
+ otherClass = toString.call(b);
+
+ if (className != otherClass) {
+ return false;
+ }
+ switch (className) {
+ case boolClass:
+ case dateClass:
+ return +a == +b;
+
+ case numberClass:
+ return a != +a
+ ? b != +b
+ : (a == 0 ? (1 / a == 1 / b) : a == +b);
+
+ case regexpClass:
+ case stringClass:
+ return a == String(b);
+ }
+ var isArr = className == arrayClass;
+ if (!isArr) {
+ if (a instanceof lodash || b instanceof lodash) {
+ return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, stackA, stackB);
+ }
+ if (className != objectClass) {
+ return false;
+ }
+ var ctorA = a.constructor,
+ ctorB = b.constructor;
+
+ if (ctorA != ctorB && !(
+ isFunction(ctorA) && ctorA instanceof ctorA &&
+ isFunction(ctorB) && ctorB instanceof ctorB
+ )) {
+ return false;
}
}
- while (++index < length) {
- if (array[index] === value) {
- return index;
+ stackA || (stackA = []);
+ stackB || (stackB = []);
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == a) {
+ return stackB[length] == b;
}
}
- return -1;
+ var result = true,
+ size = 0;
+
+ stackA.push(a);
+ stackB.push(b);
+
+ if (isArr) {
+ size = b.length;
+ result = size == a.length;
+
+ if (result) {
+ while (size--) {
+ if (!(result = isEqual(a[size], b[size], stackA, stackB))) {
+ break;
+ }
+ }
+ }
+ return result;
+ }
+ forIn(b, function(value, key, b) {
+ if (hasOwnProperty.call(b, key)) {
+ size++;
+ return !(result = hasOwnProperty.call(a, key) && isEqual(a[key], value, stackA, stackB)) && indicatorObject;
+ }
+ });
+
+ if (result) {
+ forIn(a, function(value, key, a) {
+ if (hasOwnProperty.call(a, key)) {
+ return !(result = --size > -1) && indicatorObject;
+ }
+ });
+ }
+ return result;
}
/**
- * Gets all but the last value of `array`. Pass `n` to exclude the last `n`
- * values from the result.
+ * 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 empty strings. See http://es5.github.com/#x15.1.2.5.
*
* @static
* @memberOf _
- * @category Arrays
- * @param {Array} array The array to query.
- * @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 {Array} Returns all but the last value or `n` values of `array`.
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is finite, else `false`.
+ * @example
+ *
+ * _.isFinite(-101);
+ * // => true
+ *
+ * _.isFinite('10');
+ * // => true
+ *
+ * _.isFinite(true);
+ * // => false
+ *
+ * _.isFinite('');
+ * // => false
+ *
+ * _.isFinite(Infinity);
+ * // => false
+ */
+ function isFinite(value) {
+ 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
*
- * _.initial([3, 2, 1]);
- * // => [3, 2]
+ * _.isFunction(_);
+ * // => true
*/
- function initial(array, n, guard) {
- if (!array) {
- return [];
+ 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;
+ };
+ }
+
+ /**
+ * Checks if `value` is the language type of Object.
+ * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(1);
+ * // => false
+ */
+ function isObject(value) {
+ // check if the value is the ECMAScript language type of Object
+ // http://es5.github.com/#x8
+ // and avoid a V8 bug
+ // http://code.google.com/p/v8/issues/detail?id=2291
+ return value ? objectTypes[typeof value] : false;
+ }
+
+ /**
+ * Checks if `value` is `NaN`.
+ *
+ * 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.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is `NaN`, else `false`.
+ * @example
+ *
+ * _.isNaN(NaN);
+ * // => true
+ *
+ * _.isNaN(new Number(NaN));
+ * // => true
+ *
+ * isNaN(undefined);
+ * // => true
+ *
+ * _.isNaN(undefined);
+ * // => false
+ */
+ 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 isNumber(value) && value != +value
+ }
+
+ /**
+ * Checks if `value` is `null`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is `null`, else `false`.
+ * @example
+ *
+ * _.isNull(null);
+ * // => true
+ *
+ * _.isNull(undefined);
+ * // => false
+ */
+ function isNull(value) {
+ return value === null;
+ }
+
+ /**
+ * Checks if `value` is a number.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is a number, else `false`.
+ * @example
+ *
+ * _.isNumber(8.4 * 5);
+ * // => true
+ */
+ function isNumber(value) {
+ return typeof value == 'number' || toString.call(value) == numberClass;
+ }
+
+ /**
+ * Checks if `value` is a regular expression.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is a regular expression, else `false`.
+ * @example
+ *
+ * _.isRegExp(/moe/);
+ * // => true
+ */
+ function isRegExp(value) {
+ return value instanceof RegExp || toString.call(value) == regexpClass;
+ }
+
+ /**
+ * Checks if `value` is a string.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is a string, else `false`.
+ * @example
+ *
+ * _.isString('moe');
+ * // => true
+ */
+ function isString(value) {
+ return typeof value == 'string' || toString.call(value) == stringClass;
+ }
+
+ /**
+ * Checks if `value` is `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is `undefined`, else `false`.
+ * @example
+ *
+ * _.isUndefined(void 0);
+ * // => true
+ */
+ function isUndefined(value) {
+ return typeof value == 'undefined';
+ }
+
+ /**
+ * Creates a shallow clone of `object` excluding the specified properties.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If a `callback` function is passed, it will be executed
+ * for each property in the `object`, omitting the properties `callback`
+ * returns truthy for. The `callback` is bound to `thisArg` and invoked
+ * with three arguments; (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Function|String} callback|[prop1, prop2, ...] The properties to omit
+ * or the function called per iteration.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object without the omitted properties.
+ * @example
+ *
+ * _.omit({ 'name': 'moe', 'age': 40 }, 'age');
+ * // => { 'name': 'moe' }
+ *
+ * _.omit({ 'name': 'moe', 'age': 40 }, function(value) {
+ * return typeof value == 'number';
+ * });
+ * // => { 'name': 'moe' }
+ */
+ function omit(object) {
+ var props = concat.apply(arrayRef, arguments),
+ result = {};
+
+ forIn(object, function(value, key) {
+ if (indexOf(props, key, 1) < 0) {
+ result[key] = value;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Creates a two dimensional array of the given object's key-value pairs,
+ * i.e. `[[key1, value1], [key2, value2]]`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns new array of key-value pairs.
+ * @example
+ *
+ * _.pairs({ 'moe': 30, 'larry': 40 });
+ * // => [['moe', 30], ['larry', 40]] (order is not guaranteed)
+ */
+ function pairs(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ var key = props[index];
+ result[index] = [key, object[key]];
}
- return slice.call(array, 0, -((n == undefined || guard) ? 1 : n));
+ return result;
}
/**
- * Computes the intersection of all the passed-in arrays.
+ * Creates a shallow clone of `object` composed of the specified properties.
+ * Property names may be specified as individual arguments or as arrays of property
+ * names. If `callback` is passed, it will be executed for each property in the
+ * `object`, picking the properties `callback` returns truthy for. The `callback`
+ * is bound to `thisArg` and invoked with three arguments; (value, key, object).
*
* @static
* @memberOf _
- * @category Arrays
- * @param {Array} [array1, array2, ...] Arrays to process.
- * @returns {Array} Returns a new array of unique values, in order, that are
- * present in **all** of the arrays.
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Array|Function|String} callback|[prop1, prop2, ...] The function called
+ * per iteration or properties to pick, either as individual arguments or arrays.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object composed of the picked properties.
* @example
*
- * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
- * // => [1, 2]
+ * _.pick({ 'name': 'moe', '_userid': 'moe1' }, 'name');
+ * // => { 'name': 'moe' }
+ *
+ * _.pick({ 'name': 'moe', '_userid': 'moe1' }, function(value, key) {
+ * return key.charAt(0) != '_';
+ * });
+ * // => { 'name': 'moe' }
*/
- function intersection(array) {
- var result = [];
- if (!array) {
- return result;
+ function pick(object) {
+ var index = 0,
+ props = concat.apply(arrayRef, arguments),
+ length = props.length,
+ result = {};
+
+ while (++index < length) {
+ var prop = props[index];
+ if (prop in object) {
+ result[prop] = object[prop];
+ }
}
- var value,
- index = -1,
- length = array.length,
- others = slice.call(arguments, 1);
+ return result;
+ }
+
+ /**
+ * Creates an array composed of the own enumerable property values of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns a new array of property values.
+ * @example
+ *
+ * _.values({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => [1, 2, 3] (order is not guaranteed)
+ */
+ function values(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
while (++index < length) {
- value = array[index];
- if (indexOf(result, value) < 0 &&
- every(others, function(other) { return indexOf(other, value) > -1; })) {
- result.push(value);
- }
+ result[index] = object[props[index]];
}
return result;
}
+ /*--------------------------------------------------------------------------*/
+
/**
- * Invokes the method named by `methodName` on each element of `array`.
- * Additional arguments will be passed to each invoked method. If `methodName`
- * is a function it will be invoked for, and `this` bound to, each element
- * of `array`.
+ * Checks if a given `target` element is present in a `collection` 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 iterate over.
- * @param {Function|String} methodName The name of the method to invoke or
- * the function invoked per iteration.
- * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
- * @returns {Array} Returns a new array of values returned from each invoked method.
+ * @alias include
+ * @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
*
- * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
- * // => [[1, 5, 7], [1, 2, 3]]
+ * _.contains([1, 2, 3], 1);
+ * // => true
*
- * _.invoke([123, 456], String.prototype.split, '');
- * // => [['1', '2', '3'], ['4', '5', '6']]
+ * _.contains([1, 2, 3], 1, 2);
+ * // => false
+ *
+ * _.contains({ 'name': 'moe', 'age': 40 }, 'moe');
+ * // => true
+ *
+ * _.contains('curly', 'ur');
+ * // => true
*/
- function invoke(array, methodName) {
- var result = [];
- if (!array) {
- return result;
- }
- var args = slice.call(arguments, 2),
- index = -1,
- length = array.length,
- isFunc = typeof methodName == 'function';
-
- while (++index < length) {
- result[index] = (isFunc ? methodName : array[index][methodName]).apply(array[index], args);
+ function contains(collection, target) {
+ var length = collection ? collection.length : 0,
+ result = false;
+ if (typeof length == 'number') {
+ result = indexOf(collection, target) > -1;
+ } else {
+ forOwn(collection, function(value) {
+ return (result = value === target) && indicatorObject;
+ });
}
return result;
}
/**
- * Gets the last value of the `array`. Pass `n` to return the lasy `n` values
- * of the `array`.
+ * Creates an object composed of keys returned from running each element of the
+ * `collection` through the given `callback`. The corresponding value of each key
+ * is the number of times the key was returned by the `callback`. The `callback`
+ * is bound to `thisArg` and invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
*
* @static
* @memberOf _
- * @category Arrays
- * @param {Array} array The array to query.
- * @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 value or an array of the last `n` values
- * of `array`.
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to iterate over.
+ * @param {Function|Object|String} [callback=identity] The function called per
+ * iteration. If a property name or object is passed, it will be used to create
+ * a "_.pluck" or "_.where" style callback, respectively.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
* @example
*
- * _.last([3, 2, 1]);
- * // => 1
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy(['one', 'two', 'three'], 'length');
+ * // => { '3': 2, '5': 1 }
*/
- function last(array, n, guard) {
- if (array) {
- var length = array.length;
- return (n == undefined || guard) ? array[length - 1] : slice.call(array, -n || length);
- }
+ function countBy(collection, callback, thisArg) {
+ var result = {};
+ callback = createCallback(callback, thisArg);
+
+ forEach(collection, function(value, key, collection) {
+ key = String(callback(value, key, collection));
+ (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
+ });
+ return result;
}
/**
- * Gets the index at which the last occurrence of `value` is found using
- * strict equality for comparisons, i.e. `===`.
+ * Checks if the `callback` returns a truthy value for **all** elements of a
+ * `collection`. The `callback` is bound to `thisArg` and invoked with three
+ * arguments; (value, index|key, collection).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
*
* @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.
- * @returns {Number} Returns the index of the matched value or `-1`.
+ * @alias all
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to iterate over.
+ * @param {Function|Object|String} [callback=identity] The function called per
+ * iteration. If a property name or object is passed, it will be used to create
+ * a "_.pluck" or "_.where" style callback, respectively.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Boolean} Returns `true` if all elements pass the callback check,
+ * else `false`.
* @example
*
- * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
- * // => 4
+ * _.every([true, 1, null, 'yes'], Boolean);
+ * // => false
*
- * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
- * // => 1
+ * var stooges = [
+ * { 'name': 'moe', 'age': 40 },
+ * { 'name': 'larry', 'age': 50 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.every(stooges, 'age');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.every(stooges, { 'age': 50 });
+ * // => false
*/
- function lastIndexOf(array, value, fromIndex) {
- if (!array) {
- return -1;
- }
- var index = array.length;
- if (fromIndex && typeof fromIndex == 'number') {
- index = (fromIndex < 0 ? Math.max(0, index + fromIndex) : Math.min(fromIndex, index - 1)) + 1;
- }
- while (index--) {
- if (array[index] === value) {
- return index;
+ function every(collection, callback, thisArg) {
+ var result = true;
+ callback = createCallback(callback, thisArg);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ if (!(result = !!callback(collection[index], index, collection))) {
+ break;
+ }
}
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ return !(result = !!callback(value, index, collection)) && indicatorObject;
+ });
}
- return -1;
+ 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 3 arguments; (value, index, array).
+ * Examines each element in a `collection`, returning an array of all elements
+ * the `callback` returns truthy for. The `callback` is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
*
* @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 for the callback.
- * @returns {Mixed} Returns the maximum value.
+ * @alias select
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to iterate over.
+ * @param {Function|Object|String} [callback=identity] The function called per
+ * iteration. If a property name or object is passed, it will be used to create
+ * a "_.pluck" or "_.where" style callback, respectively.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that passed the callback check.
* @example
*
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 },
- * { 'name': 'curly', 'age': 60 }
+ * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [2, 4, 6]
+ *
+ * var food = [
+ * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
+ * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
* ];
*
- * _.max(stooges, function(stooge) { return stooge.age; });
- * // => { 'name': 'curly', 'age': 60 };
+ * // using "_.pluck" callback shorthand
+ * _.filter(food, 'organic');
+ * // => [{ 'name': 'carrot', 'organic': true, 'type': 'vegetable' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.filter(food, { 'type': 'fruit' });
+ * // => [{ 'name': 'apple', 'organic': false, 'type': 'fruit' }]
*/
- function max(array, callback, thisArg) {
- var computed = -Infinity,
- result = computed;
+ function filter(collection, callback, thisArg) {
+ var result = [];
+ callback = createCallback(callback, thisArg);
- if (!array) {
- return result;
- }
- var current,
- index = -1,
- length = array.length;
+ var index = -1,
+ length = collection ? collection.length : 0;
- if (!callback) {
+ if (typeof length == 'number') {
while (++index < length) {
- if (array[index] > result) {
- result = array[index];
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ result.push(value);
}
}
- return result;
- }
- if (thisArg) {
- callback = iteratorBind(callback, thisArg);
- }
- while (++index < length) {
- current = callback(array[index], index, array);
- if (current > computed) {
- computed = current;
- result = array[index];
- }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ });
}
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 3 arguments; (value, index, array).
+ * Examines each element in a `collection`, returning the first that the `callback`
+ * returns truthy for. The `callback` is bound to `thisArg` and invoked with three
+ * arguments; (value, index|key, collection).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
*
* @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 for the callback.
- * @returns {Mixed} Returns the minimum value.
+ * @alias detect
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to iterate over.
+ * @param {Function|Object|String} [callback=identity] The function called per
+ * iteration. If a property name or object is passed, it will be used to create
+ * a "_.pluck" or "_.where" style callback, respectively.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Mixed} Returns the found element, else `undefined`.
* @example
*
- * _.min([10, 5, 100, 2, 1000]);
+ * _.find([1, 2, 3, 4], function(num) { return num % 2 == 0; });
* // => 2
+ *
+ * var food = [
+ * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
+ * { 'name': 'banana', 'organic': true, 'type': 'fruit' },
+ * { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
+ * ];
+ *
+ * // using "_.where" callback shorthand
+ * _.find(food, { 'type': 'vegetable' });
+ * // => { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.find(food, 'organic');
+ * // => { 'name': 'banana', 'organic': true, 'type': 'fruit' }
*/
- function min(array, callback, thisArg) {
- var computed = Infinity,
- result = computed;
+ function find(col
<TRUNCATED>
[08/10] git commit: updated refs/heads/master to 88c3171
Posted by me...@apache.org.
fix broken addon task
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/1fb22598
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/1fb22598
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/1fb22598
Branch: refs/heads/master
Commit: 1fb225985dfd3c83ac4eebfc6aaf2d06ce46800c
Parents: e14abfd
Author: Simon Metson <me...@apache.org>
Authored: Thu Apr 4 13:08:21 2013 +0100
Committer: Simon Metson <me...@apache.org>
Committed: Thu Apr 4 13:09:15 2013 +0100
----------------------------------------------------------------------
license.skip | 1 +
src/fauxton/package.json | 49 ++--------
src/fauxton/tasks/addon/rename.json | 5 +
src/fauxton/tasks/addon/root/base.js.underscore | 21 ++++
.../tasks/addon/root/resources.js.underscore | 21 ++++
src/fauxton/tasks/addon/root/route.js.underscore | 21 ++++
src/fauxton/tasks/addon/template.js | 70 +++++++++++++
src/fauxton/tasks/fauxton.js | 78 ---------------
src/fauxton/tasks/templates/base.js.underscore | 21 ----
.../tasks/templates/resources.js.underscore | 21 ----
src/fauxton/tasks/templates/route.js.underscore | 21 ----
src/fauxton/writing_addons.md | 14 ++--
12 files changed, 154 insertions(+), 189 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/license.skip
----------------------------------------------------------------------
diff --git a/license.skip b/license.skip
index 7c1f783..291e51d 100644
--- a/license.skip
+++ b/license.skip
@@ -113,6 +113,7 @@
^src/fauxton/TODO.md
^src/fauxton/settings.json.*
^src/fauxton/test/.*
+^src/fauxton/tasks/rename.json
^src/ibrowse/.*
^src/mochiweb/.*
^src/snappy/.*
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/src/fauxton/package.json
----------------------------------------------------------------------
diff --git a/src/fauxton/package.json b/src/fauxton/package.json
index 75d7c63..a6d746c 100644
--- a/src/fauxton/package.json
+++ b/src/fauxton/package.json
@@ -1,42 +1,9 @@
{
- "name": "fauxton",
- "version": "0.1.0",
- "description": "Fauxton is a modular CouchDB dashboard and Futon replacement.",
- "main": "grunt.js",
- "directories": {
- "test": "test"
- },
- "dependencies": {
- "async": "~0.2.6",
- "grunt": "~0.4.1",
- "grunt-cli": "~0.1.6",
- "couchapp": "~0.9.1",
- "grunt-contrib": "~0.5.0",
- "grunt-contrib-cssmin": "~0.5.0",
- "grunt-contrib-uglify": "~0.2.0",
- "grunt-couchapp": "~0.1.0",
- "grunt-exec": "~0.4.0",
- "grunt-jasmine-task": "~0.2.3",
- "grunt-requirejs": "~0.3.3",
- "underscore": "~1.4.2",
- "url": "~0.7.9",
- "urls": "~0.0.3",
- "express": "~3.0.6",
- "http-proxy": "~0.9.1"
- },
- "devDependencies": {},
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "repository": {
- "type": "git",
- "url": "https://git-wip-us.apache.org/repos/asf/couchdb.git"
- },
- "keywords": [
- "couchdb",
- "futon",
- "fauxton"
- ],
- "author": "",
- "license": "Apache V2"
-}
+ "name": "WickedCool",
+ "licenses": [
+ {
+ "type": "Apache",
+ "url": "undefined/blob/master/LICENSE-Apache"
+ }
+ ]
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/src/fauxton/tasks/addon/rename.json
----------------------------------------------------------------------
diff --git a/src/fauxton/tasks/addon/rename.json b/src/fauxton/tasks/addon/rename.json
new file mode 100644
index 0000000..1f326e9
--- /dev/null
+++ b/src/fauxton/tasks/addon/rename.json
@@ -0,0 +1,5 @@
+{
+ "route.js.underscore": "{%= path %}/{%= name.toLowerCase() %}/route.js",
+ "resources.js.underscore": "{%= path %}/{%= name.toLowerCase() %}/resources.js",
+ "base.js.underscore": "{%= path %}/{%= name.toLowerCase() %}/base.js"
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/src/fauxton/tasks/addon/root/base.js.underscore
----------------------------------------------------------------------
diff --git a/src/fauxton/tasks/addon/root/base.js.underscore b/src/fauxton/tasks/addon/root/base.js.underscore
new file mode 100644
index 0000000..d002cd5
--- /dev/null
+++ b/src/fauxton/tasks/addon/root/base.js.underscore
@@ -0,0 +1,21 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+ "app",
+ "api",
+ "addons/{%= name.toLowerCase() %}/routes"
+],
+
+function(app, FauxtonAPI, {%= name %}) {
+ return {%= name %};
+});
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/src/fauxton/tasks/addon/root/resources.js.underscore
----------------------------------------------------------------------
diff --git a/src/fauxton/tasks/addon/root/resources.js.underscore b/src/fauxton/tasks/addon/root/resources.js.underscore
new file mode 100644
index 0000000..8d0ef32
--- /dev/null
+++ b/src/fauxton/tasks/addon/root/resources.js.underscore
@@ -0,0 +1,21 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+ "app",
+ "api"
+],
+
+function (app, FauxtonAPI) {
+ var {%= name %} = FauxtonAPI.addon();
+ return {%= name %};
+});
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/src/fauxton/tasks/addon/root/route.js.underscore
----------------------------------------------------------------------
diff --git a/src/fauxton/tasks/addon/root/route.js.underscore b/src/fauxton/tasks/addon/root/route.js.underscore
new file mode 100644
index 0000000..859e5bb
--- /dev/null
+++ b/src/fauxton/tasks/addon/root/route.js.underscore
@@ -0,0 +1,21 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+ "app",
+ "api",
+ "addons/{%= name.toLowerCase() %}/resources"
+],
+function(app, FauxtonAPI, {%= name %}) {
+ {%= name %}.Routes = {};
+ return {%= name %};
+});
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/src/fauxton/tasks/addon/template.js
----------------------------------------------------------------------
diff --git a/src/fauxton/tasks/addon/template.js b/src/fauxton/tasks/addon/template.js
new file mode 100644
index 0000000..459ff34
--- /dev/null
+++ b/src/fauxton/tasks/addon/template.js
@@ -0,0 +1,70 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+'use strict';
+
+exports.description = 'Generate a skeleton for an addon.';
+
+exports.notes = '';
+
+exports.after = "Created your addon! Don't forget to update your"+
+ " settings.json for it to be compiled and deployed";
+
+// Any existing file or directory matching this wildcard will cause a warning.
+// exports.warnOn = '*';
+
+// The actual init template.
+exports.template = function(grunt, init, done) {
+
+ // destpath
+ init.process(
+ {},
+ [
+ {
+ name: "name",
+ message: "Add on Name",
+ validator: /^[\w\-\.]+$/,
+ default: "WickedCool"
+ },
+ {
+ name: "path",
+ message: "Location of add ons",
+ default: "app/addons"
+ },
+ {
+ name: "assets",
+ message: "Do you need an assets folder? (for .less)",
+ default: 'y/N'
+ }
+ ],
+ function (err, props) {
+ // Files to copy (and process).
+ var files = init.filesToCopy(props);
+
+ // Actually copy and process (apply the template props) files.
+ init.copyAndProcess(files, props);
+
+ // Make the assets dir if requested
+ if (props.assets == "y"){
+ var asspath = props.path + "/" + props.name.toLowerCase() + "/assets";
+ grunt.file.mkdir(asspath);
+ grunt.log.writeln("Created " + asspath);
+ }
+
+ var tmplpath = props.path + "/" + props.name.toLowerCase() + "/templates";
+ grunt.file.mkdir(tmplpath);
+ grunt.log.writeln("Created " + tmplpath);
+ // All done!
+ done();
+ }
+ )
+};
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/src/fauxton/tasks/fauxton.js
----------------------------------------------------------------------
diff --git a/src/fauxton/tasks/fauxton.js b/src/fauxton/tasks/fauxton.js
index b718fea..a26f9f8 100644
--- a/src/fauxton/tasks/fauxton.js
+++ b/src/fauxton/tasks/fauxton.js
@@ -10,86 +10,8 @@
// License for the specific language governing permissions and limitations under
// the License.
-// var prompt = require('prompt');
-
module.exports = function(grunt) {
var _ = grunt.util._;
- var prompts = [
- {
- name: "name",
- message: "Add on Name",
- validator: /^[\w\-\.]+$/,
- default: "WickedCool"
- },
- {
- name: "path",
- message: "Location of add ons",
- default: "app/addons"
- },
- {
- name: "assets",
- message: "Do you need an assets folder? (for .less)",
- default: 'y/N'
- }
- ];
-
- addonTemplates = [
- {
- name: 'base',
- filename: 'base.js',
- template: grunt.file.read('./tasks/templates/base.js.underscore')
- },
- {
- name: 'resources',
- filename: 'resources.js',
- template: grunt.file.read('./tasks/templates/resources.js.underscore')
- },
- {
- name: 'routes',
- filename: 'routes.js',
- template: grunt.file.read('./tasks/templates/route.js.underscore')
- }
- ];
-
- // Create a new task.
- grunt.registerInitTask('addon', 'Generate a skeleton for an addon"', function() {
- var done = this.async();
- grunt.helper('prompt', {}, prompts, function (err, result) {
- if (err) { return onErr(err); }
- grunt.log.writeln(result.assets);
- var module = result.name,
- assets = result.assets;
- if (assets == 'y') {
- //if you need an assets folder
- filepath = result.path + '/' + module.toLowerCase() + '/assets/less';
- grunt.file.mkdir(filepath);
- lessfile = {
- name: 'less',
- filename: module.toLowerCase()+'.less',
- template: '//<%= module %> styles'
- };
- lessfile.module = module.charAt(0).toUpperCase() + module.substr(1);
- var content = grunt.template.process(lessfile.template, lessfile);
- grunt.file.write(filepath + '/' + lessfile.filename, content);
- }
- filepath = result.path + '/' + module.toLowerCase() + '/templates';
- grunt.file.mkdir(filepath);
- filepath = result.path + '/' + module.toLowerCase();
- _.each(addonTemplates, function(file){
- file.module = module.charAt(0).toUpperCase() + module.substr(1);
- var content = grunt.template.process(file.template, file);
- grunt.file.write(filepath + '/' + file.filename, content);
- });
- grunt.log.writeln('Created addon ' + result.name + ' in ' + result.path);
- grunt.log.writeln('\n\nAdd ' + result.name + ' to settings.json for it to be compiled and deployed');
- done();
- });
- function onErr(err) {
- grunt.log.writeln(err);
- done();
- return 1;
- }
- });
grunt.registerMultiTask('template', 'generates an html file from a specified template', function(){
var data = this.data;
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/src/fauxton/tasks/templates/base.js.underscore
----------------------------------------------------------------------
diff --git a/src/fauxton/tasks/templates/base.js.underscore b/src/fauxton/tasks/templates/base.js.underscore
deleted file mode 100644
index cf7afa7..0000000
--- a/src/fauxton/tasks/templates/base.js.underscore
+++ /dev/null
@@ -1,21 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-define([
- "app",
- "api",
- "addons/<%= module.toLowerCase() %>/routes"
-],
-
-function(app, FauxtonAPI, <%= module %>) {
- return <%= module %>;
-});
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/src/fauxton/tasks/templates/resources.js.underscore
----------------------------------------------------------------------
diff --git a/src/fauxton/tasks/templates/resources.js.underscore b/src/fauxton/tasks/templates/resources.js.underscore
deleted file mode 100644
index 0aa37f8..0000000
--- a/src/fauxton/tasks/templates/resources.js.underscore
+++ /dev/null
@@ -1,21 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-define([
- "app",
- "api"
-],
-
-function (app, FauxtonAPI) {
- var <%= module %> = FauxtonAPI.addon();
- return <%= module %>;
-});
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/src/fauxton/tasks/templates/route.js.underscore
----------------------------------------------------------------------
diff --git a/src/fauxton/tasks/templates/route.js.underscore b/src/fauxton/tasks/templates/route.js.underscore
deleted file mode 100644
index 9256766..0000000
--- a/src/fauxton/tasks/templates/route.js.underscore
+++ /dev/null
@@ -1,21 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-define([
- "app",
- "api",
- "addons/<%= module.toLowerCase() %>/resources"
-],
-function(app, FauxtonAPI, <%= module %>) {
- <%= module %>.Routes = {};
- return <%= module %>;
-});
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1fb22598/src/fauxton/writing_addons.md
----------------------------------------------------------------------
diff --git a/src/fauxton/writing_addons.md b/src/fauxton/writing_addons.md
index ccd09af..e0a85fa 100644
--- a/src/fauxton/writing_addons.md
+++ b/src/fauxton/writing_addons.md
@@ -11,14 +11,14 @@ have the following structure:
[optional]
* assets/less
- * my_addon.less
+ * my_addon.less
## Generating an addon
-We have a grunt task that lets you create a skeleton addon, including all the
-boiler plate code. Run `bbb addon` and answer the questions it asks to create
-an addon:
+We have a `grunt-init` template that lets you create a skeleton addon,
+including all the boiler plate code. Run `grunt-init tasks/addon` and answer
+the questions it asks to create an addon:
- ± bbb addon
+ ± grunt-init tasks/addon
path.existsSync is now called `fs.existsSync`.
Running "addon" task
@@ -32,8 +32,8 @@ an addon:
Done, without errors.
-Once the addon is created add the name to the settings.json file to get it compiled
-and added on the next install.
+Once the addon is created add the name to the settings.json file to get it
+compiled and added on the next install.
## Routes and hooks
An addon can insert itself into fauxton in two ways; via a route or via a hook.
[04/10] git commit: updated refs/heads/master to 88c3171
Posted by me...@apache.org.
layout manager migration
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/b420843f
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/b420843f
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/b420843f
Branch: refs/heads/master
Commit: b420843f28e27f61428293e9cb85803161967179
Parents: a46e87d
Author: Simon Metson <si...@cloudant.com>
Authored: Thu Apr 4 13:08:19 2013 +0100
Committer: Simon Metson <me...@apache.org>
Committed: Thu Apr 4 13:08:19 2013 +0100
----------------------------------------------------------------------
src/fauxton/app/app.js | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/b420843f/src/fauxton/app/app.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/app.js b/src/fauxton/app/app.js
index ffd2227..5046096 100644
--- a/src/fauxton/app/app.js
+++ b/src/fauxton/app/app.js
@@ -30,7 +30,7 @@ function($, _, Backbone, Helpers) {
var JST = window.JST = window.JST || {};
// Configure LayoutManager with Backbone Boilerplate defaults.
- Backbone.LayoutManager.configure({
+ Backbone.Layout.configure({
// Allow LayoutManager to augment Backbone.View.prototype.
manage: true,