You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2014/03/06 12:51:56 UTC

[01/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Repository: couchdb
Updated Branches:
  refs/heads/Query-Options-UI 91e25b1fe -> dd98953ec (forced update)


Remove client-side password crypto from JS tests

This removes client-side password crypto from the JavaScript tests.

In some JavaScript tests, it has been assumed that SHA-1 is used for the
password hash in user docs.  Those tests should, however, not rely on
implementation details of the user authentication hash function, as it
isn't the goal of those tests to check these.  Furthermore, this causes
problems when a password scheme is changed, or a new one is introduced.


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/45e17e5f
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/45e17e5f
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/45e17e5f

Branch: refs/heads/Query-Options-UI
Commit: 45e17e5fbb3c5364e7f8d0e6bb4d79e2e291ecfa
Parents: 08cf09f
Author: Klaus Trainer <kl...@posteo.de>
Authored: Wed Feb 19 21:30:53 2014 +0100
Committer: Klaus Trainer <kl...@apache.org>
Committed: Sun Feb 23 18:59:21 2014 +0100

----------------------------------------------------------------------
 share/www/script/couch_test_runner.js |  4 +---
 share/www/script/test/auth_cache.js   | 12 ++----------
 share/www/script/test/cookie_auth.js  | 11 +++++------
 3 files changed, 8 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/45e17e5f/share/www/script/couch_test_runner.js
----------------------------------------------------------------------
diff --git a/share/www/script/couch_test_runner.js b/share/www/script/couch_test_runner.js
index c04e6b1..7f435bf 100644
--- a/share/www/script/couch_test_runner.js
+++ b/share/www/script/couch_test_runner.js
@@ -460,9 +460,7 @@ CouchDB.user_prefix = "org.couchdb.user:";
 CouchDB.prepareUserDoc = function(user_doc, new_password) {
   user_doc._id = user_doc._id || CouchDB.user_prefix + user_doc.name;
   if (new_password) {
-    // handle the password crypto
-    user_doc.salt = CouchDB.newUuids(1)[0];
-    user_doc.password_sha = hex_sha1(new_password + user_doc.salt);
+    user_doc.password = new_password;
   }
   user_doc.type = "user";
   if (!user_doc.roles) {

http://git-wip-us.apache.org/repos/asf/couchdb/blob/45e17e5f/share/www/script/test/auth_cache.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/auth_cache.js b/share/www/script/test/auth_cache.js
index 57e6a8d..2229c20 100644
--- a/share/www/script/test/auth_cache.js
+++ b/share/www/script/test/auth_cache.js
@@ -184,11 +184,7 @@ couchTests.auth_cache = function(debug) {
     hits_before = hits_after;
     misses_before = misses_after;
 
-    var new_salt = CouchDB.newUuids(1)[0];
-    var new_passwd = hex_sha1("foobar" + new_salt);
-    fdmanana.salt = new_salt;
-    fdmanana.password_sha = new_passwd;
-
+    fdmanana.password = "foobar";
     T(authDb.save(fdmanana).ok);
 
     // cache was refreshed
@@ -206,11 +202,7 @@ couchTests.auth_cache = function(debug) {
     misses_before = misses_after;
 
     // and yet another update
-    new_salt = CouchDB.newUuids(1)[0];
-    new_passwd = hex_sha1("javascript" + new_salt);
-    fdmanana.salt = new_salt;
-    fdmanana.password_sha = new_passwd;
-
+    fdmanana.password = "javascript";
     T(authDb.save(fdmanana).ok);
 
     // cache was refreshed

http://git-wip-us.apache.org/repos/asf/couchdb/blob/45e17e5f/share/www/script/test/cookie_auth.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/cookie_auth.js b/share/www/script/test/cookie_auth.js
index 40b633b..9b4bd64 100644
--- a/share/www/script/test/cookie_auth.js
+++ b/share/www/script/test/cookie_auth.js
@@ -115,7 +115,7 @@ couchTests.cookie_auth = function(debug) {
 
       // we can't create docs with malformed ids
       var badIdDoc = CouchDB.prepareUserDoc({
-        name: "foo"
+        name: "w00x"
       }, "bar");
 
       badIdDoc._id = "org.apache.couchdb:w00x";
@@ -153,8 +153,8 @@ couchTests.cookie_auth = function(debug) {
         usersDb.deleteDoc(jchrisUserDoc);
         T(false && "Can't delete other users docs. Should have thrown an error.");
       } catch (e) {
-        TEquals("forbidden", e.error);
-        TEquals(403, usersDb.last_req.status);
+        TEquals("not_found", e.error);
+        TEquals(404, usersDb.last_req.status);
       }
 
       // TODO should login() throw an exception here?
@@ -197,8 +197,8 @@ couchTests.cookie_auth = function(debug) {
         usersDb.save(jasonUserDoc);
         T(false && "Can't update someone else's user doc. Should have thrown an error.");
       } catch (e) {
-        T(e.error == "forbidden");
-        T(usersDb.last_req.status == 403);
+        T(e.error == "not_found");
+        T(usersDb.last_req.status == 404);
       }
 
       // test that you can't edit roles unless you are admin
@@ -272,7 +272,6 @@ couchTests.cookie_auth = function(debug) {
 
   var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
   usersDb.deleteDb();
-  usersDb.createDb();
 
   run_on_modified_server(
     [


[16/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Use $.inArray instead of indexOf for IE8 compatibility.

Signed-off-by: Alexander Shorin <kx...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/77724c94
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/77724c94
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/77724c94

Branch: refs/heads/Query-Options-UI
Commit: 77724c94b959ebe4feb124e21dfbad504f50f824
Parents: b22f2a4
Author: Keith Gable <kg...@decisionpt.com>
Authored: Mon Mar 3 14:00:11 2014 -0600
Committer: Alexander Shorin <kx...@apache.org>
Committed: Tue Mar 4 15:10:43 2014 +0400

----------------------------------------------------------------------
 share/www/script/futon.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/77724c94/share/www/script/futon.js
----------------------------------------------------------------------
diff --git a/share/www/script/futon.js b/share/www/script/futon.js
index e2e0aaf..409becc 100644
--- a/share/www/script/futon.js
+++ b/share/www/script/futon.js
@@ -240,7 +240,7 @@ function $$(node) {
           if (userCtx.name) {
             $("#userCtx .name").text(userCtx.name).attr({href : $.couch.urlPrefix + "/_utils/document.html?"+encodeURIComponent(r.info.authentication_db)+"/org.couchdb.user%3A"+encodeURIComponent(userCtx.name)});
 
-            if (userCtx.roles.indexOf("_admin") != -1) {
+            if ($.inArray("_admin", userCtx.roles) != -1) {
               $("#userCtx .loggedin").show();
               $("#userCtx .loggedinadmin").show();
               $(".serverAdmin").removeAttr('disabled'); // user is a server admin
@@ -266,7 +266,7 @@ function $$(node) {
                 }); 
               }
             }
-          } else if (userCtx.roles.indexOf("_admin") != -1) {
+          } else if ($.inArray("_admin", userCtx.roles) != -1) {
             $("#userCtx .adminparty").show();
             $(".serverAdmin").removeAttr('disabled');
           } else {


[04/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Move myself from THANKS to AUTHORS


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/358901a5
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/358901a5
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/358901a5

Branch: refs/heads/Query-Options-UI
Commit: 358901a50b83fd40fe8916b922e0737e96dbabeb
Parents: 8c86757
Author: Klaus Trainer <kl...@apache.org>
Authored: Sun Feb 23 19:11:51 2014 +0100
Committer: Klaus Trainer <kl...@apache.org>
Committed: Sun Feb 23 19:11:51 2014 +0100

----------------------------------------------------------------------
 AUTHORS   | 1 +
 THANKS.in | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/358901a5/AUTHORS
----------------------------------------------------------------------
diff --git a/AUTHORS b/AUTHORS
index 863e87b..dd0260a 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -27,5 +27,6 @@ documentation or developing software. Some of these people are:
  * Garren Smith <ga...@apache.org>
  * Sue Lockwood <de...@apache.org>
  * Andy Wenk <an...@apache.org>
+ * Klaus Trainer <kl...@apache.org>
 
 For a list of other credits see the `THANKS` file.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/358901a5/THANKS.in
----------------------------------------------------------------------
diff --git a/THANKS.in b/THANKS.in
index 67678f6..bdfb3cd 100644
--- a/THANKS.in
+++ b/THANKS.in
@@ -64,7 +64,6 @@ suggesting improvements or submitting changes. Some of these people are:
  * David Rose <do...@gmail.com>
  * Lim Yue Chuan <sh...@gmail.com>
  * David Davis <xa...@xantus.org>
- * Klaus Trainer <kl...@web.de>
  * Juuso Väänänen <ju...@vaananen.org>
  * Jeff Zellner <je...@gmail.com>
  * Benjamin Young <by...@bigbluehat.com>


[17/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
remove warning when running `grunt dev`

This removes the warning 'path.existsSync is now called
fs.existsSync'. Now having one require at the top of the file,
as module.require caches requires anyway.


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/0782a44c
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/0782a44c
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/0782a44c

Branch: refs/heads/Query-Options-UI
Commit: 0782a44ce6f54aa553e014acfa3bb11448c0682e
Parents: 77724c9
Author: Robert Kowalski <ro...@kowalski.gd>
Authored: Wed Mar 5 20:34:02 2014 +0100
Committer: Robert Kowalski <ro...@kowalski.gd>
Committed: Wed Mar 5 20:38:43 2014 +0100

----------------------------------------------------------------------
 src/fauxton/tasks/fauxton.js | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/0782a44c/src/fauxton/tasks/fauxton.js
----------------------------------------------------------------------
diff --git a/src/fauxton/tasks/fauxton.js b/src/fauxton/tasks/fauxton.js
index e54bab8..e06e7dc 100644
--- a/src/fauxton/tasks/fauxton.js
+++ b/src/fauxton/tasks/fauxton.js
@@ -11,7 +11,8 @@
 // the License.
 
 module.exports = function(grunt) {
-  var _ = grunt.util._;
+  var _ = grunt.util._,
+      fs = require('fs');
 
   grunt.registerMultiTask('template', 'generates an html file from a specified template', function(){
     var data = this.data,
@@ -24,11 +25,10 @@ module.exports = function(grunt) {
   grunt.registerMultiTask('get_deps', 'Fetch external dependencies', function(version) {
     grunt.log.writeln("Fetching external dependencies");
 
-    var path = require('path');
-        done = this.async(),
+    var done = this.async(),
         data = this.data,
         target = data.target || "app/addons/",
-        settingsFile = path.existsSync(data.src) ? data.src : "settings.json.default",
+        settingsFile = fs.existsSync(data.src) ? data.src : "settings.json.default",
         settings = grunt.file.readJSON(settingsFile),
         _ = grunt.util._;
 
@@ -76,10 +76,9 @@ module.exports = function(grunt) {
   });
 
   grunt.registerMultiTask('gen_load_addons', 'Generate the load_addons.js file', function() {
-    var path = require('path');
-        data = this.data,
+    var data = this.data,
         _ = grunt.util._,
-        settingsFile = path.existsSync(data.src) ? data.src : "settings.json.default",
+        settingsFile = fs.existsSync(data.src) ? data.src : "settings.json.default",
         settings = grunt.file.readJSON(settingsFile),
         template = "app/load_addons.js.underscore",
         dest = "app/load_addons.js",


[09/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Revert "fix right hand side scrolling over navigation"

This reverts commit e389a8ba2a79da2669b536ca242b5dfdee4faba3.

This is the commit that broke modals.

Reverting it fixes COUCHDB-2086

Fixing the right hand content overlapping the left
hand navigation will require a bigger fix...
This sadly was not it...


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/5989bb32
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/5989bb32
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/5989bb32

Branch: refs/heads/Query-Options-UI
Commit: 5989bb324a7998f931050b542f7e526309bdcc32
Parents: b4b6fe1
Author: BigBlueHat <by...@bigbluehat.com>
Authored: Mon Feb 24 15:39:26 2014 -0500
Committer: BigBlueHat <by...@bigbluehat.com>
Committed: Wed Feb 26 08:49:43 2014 -0500

----------------------------------------------------------------------
 src/fauxton/assets/less/fauxton.less | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/5989bb32/src/fauxton/assets/less/fauxton.less
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/less/fauxton.less b/src/fauxton/assets/less/fauxton.less
index 4ec502a..82c65bd 100644
--- a/src/fauxton/assets/less/fauxton.less
+++ b/src/fauxton/assets/less/fauxton.less
@@ -490,9 +490,8 @@ table.databases {
   max-width: 1500px;
   .box-shadow(-6px 0 rgba(0, 0, 0, 0.1));
   border-left: 1px solid #999;
-  position: fixed;
+  position: absolute;
   left: @navWidth;
-  right: 0;
   margin-left: 0;
   padding-left: 0;
   padding-right: 0;


[19/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Fauxton: Improved pagination

This is an improvement and fix on the current pagination. This fixes
pagination so that it works with all query options.

Pagination in Couchdb is quite complex
and there are plenty of situations to cater for. This new pagination
works quite differently to the previous way we had it working for a
user.

A user can set the number of documents they want to view on a page. This
is not related to limit options in the query options - the limit option
is an overall cap of how many documents to paginate too. A limit of none
is possible and is the default. If the limit option is set to 50 and a
user wants 10 docs per page, the would then be able to paginate 3 pages
before hitting the end.

Another change is that the api url and browser url does not change when
we paginate. That happens internally and hence the new addition of
urlParams and docParams. This allows Fauxton to keep track of the
parameters that the user sees and the parameters that the document needs
to paginate.

Fixes COUCHDB-2067


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/4e60f0b7
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/4e60f0b7
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/4e60f0b7

Branch: refs/heads/Query-Options-UI
Commit: 4e60f0b7a7b26662dde0ebc0a73c70df54e0f309
Parents: 0782a44
Author: Garren Smith <ga...@gmail.com>
Authored: Tue Jan 28 16:30:38 2014 +0200
Committer: Garren Smith <ga...@gmail.com>
Committed: Thu Mar 6 11:48:09 2014 +0200

----------------------------------------------------------------------
 src/fauxton/app/addons/databases/resources.js   |   2 +-
 .../app/addons/databases/templates/item.html    |   2 +-
 src/fauxton/app/addons/databases/views.js       |   3 +-
 .../addons/documents/assets/less/documents.less |   8 +
 src/fauxton/app/addons/documents/resources.js   | 211 +++--
 src/fauxton/app/addons/documents/routes.js      | 192 +++--
 .../documents/templates/advanced_options.html   |   8 +-
 .../documents/templates/all_docs_list.html      |  15 +-
 .../documents/templates/all_docs_number.html    |  24 +-
 .../app/addons/documents/templates/sidebar.html |   4 +-
 .../addons/documents/templates/view_editor.html |   4 +-
 .../app/addons/documents/tests/resourcesSpec.js |  27 -
 src/fauxton/app/addons/documents/views.js       | 241 +++---
 src/fauxton/app/addons/fauxton/base.js          |  14 +-
 src/fauxton/app/addons/fauxton/components.js    | 127 ++-
 .../app/addons/fauxton/tests/paginateSpec.js    |  18 -
 src/fauxton/test/mocha/chai.js                  | 804 ++++++++++++++-----
 src/fauxton/test/test.config.underscore         |   4 +
 18 files changed, 1153 insertions(+), 555 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/databases/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/databases/resources.js b/src/fauxton/app/addons/databases/resources.js
index ea1aed2..80cd533 100644
--- a/src/fauxton/app/addons/databases/resources.js
+++ b/src/fauxton/app/addons/databases/resources.js
@@ -22,7 +22,7 @@ define([
 function(app, FauxtonAPI, Documents) {
   var Databases = FauxtonAPI.addon();
 
-  Databases.DocLimit = 20;
+  Databases.DocLimit = 100;
 
   Databases.Model = FauxtonAPI.Model.extend({
     initialize: function(options) {

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/databases/templates/item.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/databases/templates/item.html b/src/fauxton/app/addons/databases/templates/item.html
index e2f8071..549f421 100644
--- a/src/fauxton/app/addons/databases/templates/item.html
+++ b/src/fauxton/app/addons/databases/templates/item.html
@@ -13,7 +13,7 @@ the License.
 -->
 
 <td>
-  <a href="#/database/<%=encoded%>/_all_docs?limit=<%=docLimit%>"><%= database.get("name") %></a>
+  <a href="#/database/<%=encoded%>/_all_docs"><%= database.get("name") %></a>
 </td>
 <td><%= database.status.humanSize() %></td>
 <td><%= database.status.numDocs() %></td>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/databases/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/databases/views.js b/src/fauxton/app/addons/databases/views.js
index 7f23d65..a56267f 100644
--- a/src/fauxton/app/addons/databases/views.js
+++ b/src/fauxton/app/addons/databases/views.js
@@ -31,8 +31,7 @@ function(app, Components, FauxtonAPI, Databases) {
       
       return {
         encoded: app.utils.safeURLName(this.model.get("name")),
-        database: this.model,
-        docLimit: Databases.DocLimit
+        database: this.model
       };
     }
   });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/documents/assets/less/documents.less
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/assets/less/documents.less b/src/fauxton/app/addons/documents/assets/less/documents.less
index 9dee85e..c30a9af 100644
--- a/src/fauxton/app/addons/documents/assets/less/documents.less
+++ b/src/fauxton/app/addons/documents/assets/less/documents.less
@@ -23,6 +23,14 @@ button.beautify {
 	margin-top: 20px;
 }
 
+#per-page {
+  float: right;
+
+  #select-per-page {
+    margin-top: 10px;
+  }
+  
+}
 
 /** used in all_docs_list.html **/
 .view {

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/documents/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/resources.js b/src/fauxton/app/addons/documents/resources.js
index adfee1f..c0b736f 100644
--- a/src/fauxton/app/addons/documents/resources.js
+++ b/src/fauxton/app/addons/documents/resources.js
@@ -18,6 +18,92 @@ define([
 function(app, FauxtonAPI) {
   var Documents = FauxtonAPI.addon();
 
+  Documents.QueryParams = (function () {
+    var _eachParams = function (params, action) {
+      _.each(['startkey', 'endkey', 'key'], function (key) {
+        if (_.has(params, key)) {
+          params[key] = action(params[key]);
+        }
+      });
+
+      return params;
+    };
+
+    return {
+      parse: function (params) {
+        return _eachParams(params, JSON.parse);
+      },
+
+      stringify: function (params) {
+        return _eachParams(params, JSON.stringify);
+      }
+    };
+  })();
+
+  Documents.paginate = {
+    history: [],
+    calculate: function (doc, defaultParams, currentParams, _isAllDocs) {
+      var docId = '',
+          lastId = '',
+          isView = !!!_isAllDocs,
+          key;
+
+      if (currentParams.keys) {
+        throw "Cannot paginate when keys is specfied";
+      }
+
+      if (_.isUndefined(doc)) {
+        throw "Require docs to paginate";
+      }
+
+      // defaultParams should always override the user-specified parameters
+      _.extend(currentParams, defaultParams);
+
+      lastId = doc.id || doc._id;
+
+      // If we are paginating on a view, we need to set a ``key`` and a ``docId``
+      // and expect that they are different values.
+      if (isView) {
+        key = doc.key;
+        docId = lastId;
+      } else {
+        docId = key = lastId;
+      }
+
+      // Set parameters to paginate
+      if (isView) {
+        currentParams.startkey_docid = docId;
+        currentParams.startkey = key;
+      } else if (currentParams.startkey) {
+        currentParams.startkey = key;
+      } else {
+        currentParams.startkey_docid = docId;
+      }
+
+      return currentParams;
+    },
+
+    next: function (docs, currentParams, perPage, _isAllDocs) {
+      var params = {limit: perPage, skip: 1},
+          doc = _.last(docs);
+          
+      this.history.push(_.clone(currentParams));
+      return this.calculate(doc, params, currentParams, _isAllDocs);
+    },
+
+    previous: function (docs, currentParams, perPage, _isAllDocs) {
+      var params = this.history.pop(),
+          doc = _.first(docs);
+
+      params.limit = perPage;
+      return params;
+    },
+
+    reset: function () {
+      this.history = [];
+    } 
+  };
+
   Documents.Doc = FauxtonAPI.Model.extend({
     idAttribute: "_id",
     documentation: function(){
@@ -274,20 +360,31 @@ function(app, FauxtonAPI) {
 
   Documents.AllDocs = FauxtonAPI.Collection.extend({
     model: Documents.Doc,
+    isAllDocs: true,
     documentation: function(){
       return "docs";
     },
     initialize: function(_models, options) {
       this.database = options.database;
-      this.params = options.params;
-      this.skipFirstItem = false;
-
+      this.params = _.clone(options.params);
       this.on("remove",this.decrementTotalRows , this);
+      this.perPageLimit = options.perPageLimit || 20;
+
+      if (!this.params.limit) {
+        this.params.limit = this.perPageLimit; 
+      }
     },
 
-    url: function(context) {
+    url: function(context, params) {
       var query = "";
-      if (this.params) {
+
+      if (params) {
+        if (!_.isEmpty(params)) {
+          query = "?" + $.param(params);
+        } else {
+          query = '';
+        }
+      } else if (this.params) {
         query = "?" + $.param(this.params);
       }
 
@@ -314,34 +411,13 @@ function(app, FauxtonAPI) {
       });
     },
 
-    urlNextPage: function (num, lastId) {
-      if (!lastId) {
-        var doc = this.last();
-
-        if (doc) {
-          lastId = doc.id;
-        } else {
-          lastId = '';
-        }
-      }
-
-      this.params.startkey_docid = '"' + lastId + '"';
-      this.params.startkey = '"' + lastId + '"';
-      // when paginating forward, fetch 21 and don't show
-      // the first item as it was the last item in the previous list
-      this.params.limit = num + 1;
-      return this.url('app');
+    updateLimit: function (limit) {
+      this.perPageLimit = limit;
+      this.params.limit = limit;
     },
 
-    urlPreviousPage: function (num, params) {
-      if (params) { 
-        this.params = params;
-      } else {
-        this.params = {reduce: false};
-      }
-
-      this.params.limit = num;
-      return this.url('app'); 
+    updateParams: function (params) {
+      this.params = params;
     },
 
     totalRows: function() {
@@ -359,18 +435,6 @@ function(app, FauxtonAPI) {
       return this.viewMeta.update_seq || false;
     },
 
-    recordStart: function () {
-      if (this.viewMeta.offset === 0) {
-        return 1;
-      }
-
-      if (this.skipFirstItem) {
-        return this.viewMeta.offset + 2;
-      }
-
-      return this.viewMeta.offset + 1;
-    },
-
     parse: function(resp) {
       var rows = resp.rows;
 
@@ -409,11 +473,23 @@ function(app, FauxtonAPI) {
       this.view = options.view;
       this.design = options.design.replace('_design/','');
       this.skipFirstItem = false;
+      this.perPageLimit = options.perPageLimit || 20;
+
+      if (!this.params.limit) {
+        this.params.limit = this.perPageLimit; 
+      }
+
     },
 
-    url: function(context) {
+    url: function(context, params) {
       var query = "";
-      if (this.params) {
+      if (params) {
+        if (!_.isEmpty(params)) {
+          query = "?" + $.param(params);
+        } else {
+          query = '';
+        }
+      } else if (this.params) {
         query = "?" + $.param(this.params);
       }
       
@@ -430,42 +506,18 @@ function(app, FauxtonAPI) {
       return url.join("/") + query;
     },
 
-    urlNextPage: function (num, lastId) {
-      if (!lastId) {
-        lastDoc = this.last();
-      }
-
-      var id = lastDoc.get("id");
-      if (id) {
-        this.params.startkey_docid = id;
-      }
-
-      this.params.startkey =  JSON.stringify(lastDoc.get('key'));
-      this.params.limit = num + 1;
-      return this.url('app');
+    updateParams: function (params) {
+      this.params = params;
     },
 
-     urlPreviousPage: function (num, params) {
-      if (params) { 
-        this.params = params;
-      } else {
-        this.params = {reduce: false};
+    updateLimit: function (limit) {
+      if (this.params.startkey_docid && this.params.startkey) {
+        //we are paginating so set limit + 1
+        this.params.limit = limit + 1;
+        return;
       }
 
-      this.params.limit = num;
-      return this.url('app');
-    },
-
-    recordStart: function () {
-      if (this.viewMeta.offset === 0) {
-        return 1;
-      }
-
-      if (this.skipFirstItem) {
-        return this.viewMeta.offset + 2;
-      }
-
-      return this.viewMeta.offset + 1;
+      this.params.limit = limit;
     },
 
     totalRows: function() {
@@ -565,6 +617,7 @@ function(app, FauxtonAPI) {
 
       return timeString;
     }
+
   });
 
   
@@ -619,10 +672,6 @@ function(app, FauxtonAPI) {
       return deferred;
     },
 
-    recordStart: function () {
-      return 1;
-    },
-
     totalRows: function() {
       return this.viewMeta.total_rows || "unknown";
     },

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/documents/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/routes.js b/src/fauxton/app/addons/documents/routes.js
index 1510485..f340195 100644
--- a/src/fauxton/app/addons/documents/routes.js
+++ b/src/fauxton/app/addons/documents/routes.js
@@ -155,13 +155,11 @@ function(app, FauxtonAPI, Documents, Databases) {
       "route:updateAllDocs": "updateAllDocsFromView",
       "route:updatePreviewDocs": "updateAllDocsFromPreview",
       "route:reloadDesignDocs": "reloadDesignDocs",
-      "route:paginate": "paginate"
+      "route:paginate": "paginate",
+      "route:perPageChange": "perPageChange"
     },
 
     initialize: function (route, masterLayout, options) {
-      var docOptions = app.getParams();
-      docOptions.include_docs = true;
-
       this.databaseName = options[0];
 
       this.data = {
@@ -170,9 +168,11 @@ function(app, FauxtonAPI, Documents, Databases) {
 
       this.data.designDocs = new Documents.AllDocs(null, {
         database: this.data.database,
-        params: {startkey: '"_design"',
+        params: {
+          startkey: '"_design"',
           endkey: '"_design1"',
-          include_docs: true}
+          include_docs: true
+        }
       });
 
       this.sidebar = this.setView("#sidebar-content", new Documents.Views.Sidebar({
@@ -185,12 +185,32 @@ function(app, FauxtonAPI, Documents, Databases) {
       return this.data.designDocs.fetch();
     },
 
+    createParams: function (options) {
+      var urlParams = app.getParams(options);
+      return {
+        urlParams: urlParams,
+        docParams: _.extend(_.clone(urlParams), {limit: this.getDocPerPageLimit(urlParams, 20)})
+      };
+    },
+
+    /*
+    * docParams are the options collection uses to fetch from the server 
+    * urlParams are what are shown in the url and to the user
+    * They are not the same when paginating
+    */
     allDocs: function(databaseName, options) {
-      var docOptions = app.getParams(options);
+      var params = this.createParams(options),
+          urlParams = params.urlParams,
+          docParams = params.docParams;
+
+      if (this.eventAllDocs) {
+        this.eventAllDocs = false;
+        return;
+      }
 
-      this.data.database.buildAllDocs(docOptions);
+      this.data.database.buildAllDocs(docParams);
 
-      if (docOptions.startkey && docOptions.startkey.indexOf('_design') > -1) {
+      if (docParams.startkey && docParams.startkey.indexOf('_design') > -1) {
         this.sidebar.setSelectedTab('design-docs');
       } else {
         this.sidebar.setSelectedTab('all-docs');
@@ -206,22 +226,29 @@ function(app, FauxtonAPI, Documents, Databases) {
       this.setView("#dashboard-upper-content", new Documents.Views.AllDocsLayout({
         database: this.data.database,
         collection: this.data.database.allDocs,
-        params: docOptions
+        params: urlParams,
+        docParams: docParams
       }));
 
       this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({
-        collection: this.data.database.allDocs
+        collection: this.data.database.allDocs,
+        docParams: docParams,
+        params: urlParams
       }));
 
       this.crumbs = [
         {"name": this.data.database.id, "link": Databases.databaseUrl(this.data.database)}
       ];
 
-      this.apiUrl = [this.data.database.allDocs.url("apiurl"), this.data.database.allDocs.documentation() ];
+      this.apiUrl = [this.data.database.allDocs.url("apiurl", urlParams), this.data.database.allDocs.documentation() ];
+      //reset the pagination history - the history is used for pagination.previous
+      Documents.paginate.reset();
     },
 
     viewFn: function (databaseName, ddoc, view) {
-      var params = app.getParams(),
+      var params = this.createParams(),
+          urlParams = params.urlParams,
+          docParams = params.docParams;
           decodeDdoc = decodeURIComponent(ddoc);
 
       view = view.replace(/\?.*$/,'');
@@ -230,7 +257,7 @@ function(app, FauxtonAPI, Documents, Databases) {
         database: this.data.database,
         design: decodeDdoc,
         view: view,
-        params: params
+        params: docParams
       });
 
       var ddocInfo = {
@@ -243,7 +270,7 @@ function(app, FauxtonAPI, Documents, Databases) {
         model: this.data.database,
         ddocs: this.data.designDocs,
         viewName: view,
-        params: params,
+        params: urlParams,
         newView: false,
         database: this.data.database,
         ddocInfo: ddocInfo
@@ -256,7 +283,9 @@ function(app, FauxtonAPI, Documents, Databases) {
         collection: this.data.indexedDocs,
         nestedView: Documents.Views.Row,
         viewList: true,
-        ddocInfo: ddocInfo
+        ddocInfo: ddocInfo,
+        docParams: docParams,
+        params: urlParams
       }));
 
       this.sidebar.setSelectedTab(app.utils.removeSpecialCharacters(ddoc) + '_' + app.utils.removeSpecialCharacters(view));
@@ -267,15 +296,15 @@ function(app, FauxtonAPI, Documents, Databases) {
         ];
       };
 
-      this.apiUrl = [this.data.indexedDocs.url("apiurl"), "docs"];
+      this.apiUrl = [this.data.indexedDocs.url("apiurl", urlParams), "docs"];
+      Documents.paginate.reset();
     },
 
     newViewEditor: function () {
       var params = app.getParams();
 
-      if (this.toolsView) {
-        this.toolsView.remove();
-      }
+      this.toolsView && this.toolsView.remove();
+      this.documentsView && this.documentsView.remove();
 
       this.viewEditor = this.setView("#dashboard-upper-content", new Documents.Views.ViewEditor({
         ddocs: this.data.designDocs,
@@ -290,39 +319,41 @@ function(app, FauxtonAPI, Documents, Databases) {
           {"name": this.data.database.id, "link": Databases.databaseUrl(this.data.database)},
         ];
       };
+
+      Documents.paginate.reset();
     },
 
     updateAllDocsFromView: function (event) {
       var view = event.view,
-          docOptions = app.getParams(),
-          ddoc = event.ddoc;
+          params = this.createParams(),
+          urlParams = params.urlParams,
+          docParams = params.docParams,
+          ddoc = event.ddoc,
+          collection;
 
-      this.documentsView && this.documentsView.remove();
+      docParams.limit = this.getDocPerPageLimit(urlParams, this.documentsView.perPage());
+      this.documentsView.forceRender();
 
       if (event.allDocs) {
-        this.data.database.buildAllDocs(docOptions);
-        this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({
-          collection: this.data.database.allDocs
-        }));
-        //this.apiUrl = [this.data.database.allDocs.url("apiurl"), this.data.database.allDocs.documentation() ];
-        return;
-      }
+        this.eventAllDocs = true; // this is horrible. But I cannot get the trigger not to fire the route!
+        this.data.database.buildAllDocs(docParams);
+        collection = this.data.database.allDocs;
 
-      this.data.indexedDocs = new Documents.IndexCollection(null, {
-        database: this.data.database,
-        design: ddoc,
-        view: view,
-        params: app.getParams()
-      });
+      } else {
+        collection = this.data.indexedDocs = new Documents.IndexCollection(null, {
+          database: this.data.database,
+          design: ddoc,
+          view: view,
+          params: docParams
+        });
 
-      this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({
-        database: this.data.database,
-        collection: this.data.indexedDocs,
-        nestedView: Documents.Views.Row,
-        viewList: true
-      }));
+      }
+
+      this.documentsView.setCollection(collection);
+      this.documentsView.setParams(docParams, urlParams);
 
-      this.apiUrl = [this.data.indexedDocs.url("apiurl"), "docs"];
+      this.apiUrl = [collection.url("apiurl", urlParams), "docs"];
+      Documents.paginate.reset();
     },
 
     updateAllDocsFromPreview: function (event) {
@@ -345,14 +376,49 @@ function(app, FauxtonAPI, Documents, Databases) {
       }));
     },
 
-    paginate: function (direction) {
-      _.extend(this.documentsView.collection.params, app.getParams());
+    perPageChange: function (perPage) {
+      var params = app.getParams();
+      this.perPage = perPage;
+      this.documentsView.updatePerPage(perPage);
+      this.documentsView.forceRender();
+      params.limit = perPage;
+      this.documentsView.collection.params = params;
+      this.setDocPerPageLimit(perPage);
+    },
+
+    paginate: function (options) {
+      var params = {},
+          urlParams = app.getParams(),
+          collection = this.documentsView.collection;
+
       this.documentsView.forceRender();
-      if (direction === 'next') {
-        this.documentsView.collection.skipFirstItem = true;
+
+      // this is really ugly. But we basically need to make sure that
+      // all parameters are in the correct state and have been parsed before we
+      // calculate how to paginate the collection
+      collection.params = Documents.QueryParams.parse(collection.params);
+      urlParams = Documents.QueryParams.parse(urlParams);
+
+      if (options.direction === 'next') {
+          params = Documents.paginate.next(collection.toJSON(), 
+                                           collection.params,
+                                           options.perPage, 
+                                           !!collection.isAllDocs);
       } else {
-        this.documentsView.collection.skipFirstItem = false;
+          params = Documents.paginate.previous(collection.toJSON(), 
+                                               collection.params, 
+                                               options.perPage, 
+                                               !!collection.isAllDocs);
       }
+
+      // use the perPage sent from IndexPagination as it calculates how many
+      // docs to fetch for next page
+      params.limit = options.perPage;
+
+      // again not pretty but need to make sure all the parameters can be correctly
+      // built into a query
+      params = Documents.QueryParams.stringify(params);
+      collection.updateParams(params);
     },
 
     reloadDesignDocs: function (event) {
@@ -361,6 +427,32 @@ function(app, FauxtonAPI, Documents, Databases) {
       if (event && event.selectedTab) {
         this.sidebar.setSelectedTab(event.selectedTab);
       }
+    },
+
+    setDocPerPageLimit: function (perPage) {
+      window.localStorage.setItem('fauxton:perpage', perPage);
+    },
+
+
+    getDocPerPageLimit: function (urlParams, perPage) {
+      var storedPerPage = perPage;
+
+      if (window.localStorage) {
+        storedPerPage = window.localStorage.getItem('fauxton:perpage');
+
+        if (!storedPerPage) {
+          this.setDocPerPageLimit(perPage);
+          storedPerPage = perPage;
+        } else {
+          storedPerPage = parseInt(storedPerPage, 10);
+        }
+      } 
+
+      if (!urlParams.limit || urlParams.limit > storedPerPage) {
+        return storedPerPage;
+      } else {
+        return urlParams.limit;
+      }
     }
 
   });
@@ -383,9 +475,9 @@ function(app, FauxtonAPI, Documents, Databases) {
       this.databaseName = options[0];
       this.database = new Databases.Model({id: this.databaseName});
 
-      var docOptions = app.getParams();
+      var docParams = app.getParams();
 
-      this.database.buildChanges(docOptions);
+      this.database.buildChanges(docParams);
 
       this.setView("#tabs", new Documents.Views.Tabs({
         collection: this.designDocs,

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/documents/templates/advanced_options.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/templates/advanced_options.html b/src/fauxton/app/addons/documents/templates/advanced_options.html
index bea256a..e282c62 100644
--- a/src/fauxton/app/addons/documents/templates/advanced_options.html
+++ b/src/fauxton/app/addons/documents/templates/advanced_options.html
@@ -12,7 +12,7 @@ License for the specific language governing permissions and limitations under
 the License.
 -->
 <div class="errors-container"></div>
-<form class="view-query-update custom-inputs">
+<form class="js-view-query-update custom-inputs">
   <div class="controls-group">
     <div class="row-fluid">
       <div class="controls controls-row">
@@ -68,10 +68,12 @@ the License.
           <select name="limit" class="input-small">
             <option>5</option>
             <option>10</option>
-            <option selected="selected">20</option>
+            <option >20</option>
             <option>30</option>
             <option>50</option>
-            <option>100</option>
+            <option >100</option>
+            <option>500</option>
+            <option selected="selected">None</option>
           </select>
         </label>
         <div class="checkbox inline">  

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/documents/templates/all_docs_list.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/templates/all_docs_list.html b/src/fauxton/app/addons/documents/templates/all_docs_list.html
index cdec81e..a521ff9 100644
--- a/src/fauxton/app/addons/documents/templates/all_docs_list.html
+++ b/src/fauxton/app/addons/documents/templates/all_docs_list.html
@@ -29,15 +29,16 @@ the License.
   <p>
 
   <div id="item-numbers"> </div>
-
-  <% if (requestDuration) { %>
-    <span class="view-request-duration pull-right">
-    View request duration: <strong> <%= requestDuration %> </strong> 
-    </span>
-  <% } %>
-  </p>
   <table class="all-docs table table-striped table-condensed">
     <tbody></tbody>
   </table>
+  
+  <% if (endOfResults) { %>  
+  <div class="text-center well">
+    <p class="muted">
+      End of results - <a id="js-end-results" href="#query" data-bypass="true" data-toggle="tab">edit query</a>
+    </p>
+    </div>
+  <% } %>
   <div id="documents-pagination"></div>
 </div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/documents/templates/all_docs_number.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/templates/all_docs_number.html b/src/fauxton/app/addons/documents/templates/all_docs_number.html
index df8fe07..0461a4b 100644
--- a/src/fauxton/app/addons/documents/templates/all_docs_number.html
+++ b/src/fauxton/app/addons/documents/templates/all_docs_number.html
@@ -11,13 +11,25 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 License for the specific language governing permissions and limitations under
 the License.
 -->
-<% if (totalRows === "unknown"){ %>
-  Showing 0 documents. <a href="#/database/<%=database%>/new"> Create your first document.</a>
-<% } else if (showNumbers) { %>
-  Showing <%=offset%> - <%= numModels %> of <%= totalRows %> rows
+<% if (totalRows === "unknown" || totalRows === 0){ %>
+Showing 0 documents. <a href="#/database/<%=database%>/new"> Create your first document.</a>
 <% } else { %>
-  Showing <%=pageStart%> - <%= pageEnd %>
+Showing <%=pageStart%> - <%= pageEnd %>
 <%}%>
 <% if (updateSeq) { %>
-  -- Update Sequence: <%= updateSeq %>
+-- Update Sequence: <%= updateSeq %>
 <% } %>
+
+<div id="per-page">
+  <label id="per-page" class="drop-down inline">
+    Per page:
+    <select id="select-per-page" name="per-page" class="input-small">
+      <option value="5">5</option>
+      <option value="10">10</option>
+      <option value="20">20</option>
+      <option value="30">30</option>
+      <option value="50">50</option>
+      <option value="100">100</option>
+    </select>
+  </label>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/documents/templates/sidebar.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/templates/sidebar.html b/src/fauxton/app/addons/documents/templates/sidebar.html
index 7358960..750cd30 100644
--- a/src/fauxton/app/addons/documents/templates/sidebar.html
+++ b/src/fauxton/app/addons/documents/templates/sidebar.html
@@ -55,8 +55,8 @@ the License.
 
   <nav>
     <ul class="nav nav-list">
-      <li class="active"><a id="all-docs" href="#<%= database.url('index') %>?limit=<%= docLimit %>" class="toggle-view"> All documents</a></li>
-      <li><a id="design-docs" href='#<%= database.url("index") %>?limit=<%= docLimit %>&startkey="_design"&endkey="_e"'  class="toggle-view"> All design docs</a></li>
+      <li class="active"><a id="all-docs" href="#<%= database.url('index') %>" class="toggle-view"> All documents</a></li>
+      <li><a id="design-docs" href='#<%= database.url("index") %>?startkey="_design"&endkey="_e"'  class="toggle-view"> All design docs</a></li>
     </ul>
     <ul class="nav nav-list views">
       <li class="nav-header">Secondary Indexes</li>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/documents/templates/view_editor.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/templates/view_editor.html b/src/fauxton/app/addons/documents/templates/view_editor.html
index 6a20849..e08e36e 100644
--- a/src/fauxton/app/addons/documents/templates/view_editor.html
+++ b/src/fauxton/app/addons/documents/templates/view_editor.html
@@ -17,12 +17,14 @@ the License.
       <% if (newView) { %>Create Index <% } else { %>Edit Index <% } %></a></li>
     <% if (!newView) { %>
     <li><a data-bypass="true" id="query-nav" href="#query" data-toggle="tab">
-      <i class="fonticon-plus fonticon"></i> Query Options</a></li>
+      <i class="fonticon-plus fonticon"></i> Query Options</a>
+    </li>
     <li><a data-bypass="true" id="meta-nav" href="#metadata" data-toggle="tab">Design Doc Metadata</a></li>
     <% } %>
   </ul>
   <div class="all-docs-list errors-container"></div>
   <div class="tab-content">
+	 <div id="query-options-wrapper"></div>
     <div class="tab-pane active" id="index">
       <div id="define-view" class="ddoc-alert well">
         <div class="errors-container"></div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/documents/tests/resourcesSpec.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/tests/resourcesSpec.js b/src/fauxton/app/addons/documents/tests/resourcesSpec.js
index 380a4e4..62506e6 100644
--- a/src/fauxton/app/addons/documents/tests/resourcesSpec.js
+++ b/src/fauxton/app/addons/documents/tests/resourcesSpec.js
@@ -32,20 +32,6 @@ define([
 
     });
 
-    it('Should return urlNext', function () {
-      var url = collection.urlNextPage(20);
-
-      assert.equal(url, 'database/databaseId/_design/myDoc/_view/?limit=21&reduce=false&startkey_docid=myId2&startkey=');
-
-    });
-
-    it('Should return urlPrevious', function () {
-      var url = collection.urlPreviousPage(20, {limit: 21, reduce: false,  startkey_docid: "myId1",startkey:"myId1"} );
-
-      assert.equal(url, 'database/databaseId/_design/myDoc/_view/?limit=20&reduce=false&startkey_docid=myId1&startkey=myId1');
-
-    });
-
   });
 
   describe('AllDocs', function () {
@@ -65,19 +51,6 @@ define([
 
     });
 
-    it('Should return urlNext', function () {
-      var url = collection.urlNextPage(20);
-
-      assert.equal(url, 'database/databaseId/_all_docs?limit=21&startkey_docid=%22myId2%22&startkey=%22myId2%22');
-
-    });
-
-     it('Should return urlPrevious', function () {
-      var url = collection.urlPreviousPage(20, {limit: 21, startkey_docid: "myId1",startkey:"myId1"} );
-      assert.equal(url, 'database/databaseId/_all_docs?limit=20&startkey_docid=myId1&startkey=myId1');
-    });
-
-
   });
 
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/documents/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/views.js b/src/fauxton/app/addons/documents/views.js
index 7282ed7..54ec261 100644
--- a/src/fauxton/app/addons/documents/views.js
+++ b/src/fauxton/app/addons/documents/views.js
@@ -26,8 +26,6 @@ define([
        // Plugins
        "plugins/beautify",
        "plugins/prettify",
-
-
 ],
 
 function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColumns, beautify) {
@@ -416,25 +414,38 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
 
     initialize: function (options) {
       this.newView = options.newView || false;
-      this.showNumbers = options.showNumbers;
       this.pagination = options.pagination;
-
+      _.bindAll(this);
+      
+      this._perPage = options.perPageDefault || 20;
       this.listenTo(this.collection, 'totalRows:decrement', this.render);
     },
 
+    events: {
+      'change #select-per-page': 'updatePerPage'
+    },
+
+    updatePerPage: function (event) {
+      this._perPage = parseInt(this.$('#select-per-page :selected').val(), 10);
+      this.pagination.updatePerPage(this.perPage());
+      FauxtonAPI.triggerRouteEvent('perPageChange', this.pagination.documentsLeftToFetch());
+    },
+
+    afterRender: function () {
+      this.$('option[value="' + this.perPage() + '"]').attr('selected', "selected");
+    },
+
     serialize: function () {
        var totalRows = 0,
-          recordStart = 0,
           updateSeq = false,
           pageStart = 0,
           pageEnd = 20;
 
       if (!this.newView) {
-        totalRows = this.collection.totalRows();
+        totalRows = this.collection.length;
         updateSeq = this.collection.updateSeq();
       }
 
-      recordStart = this.collection.recordStart();
       if (this.pagination) {
         pageStart = this.pagination.pageStart();
         pageEnd =  this.pagination.pageEnd();
@@ -443,13 +454,18 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       return {
         database: app.utils.safeURLName(this.collection.database.id),
         updateSeq: updateSeq,
-        offset: recordStart,
         totalRows: totalRows,
-        showNumbers: this.showNumbers,
-        numModels: this.collection.models.length + recordStart - 1,
         pageStart: pageStart,
         pageEnd: pageEnd
       };
+    },
+
+    perPage: function () {
+      return this._perPage;
+    },
+
+    setCollection: function (collection) {
+      this.collection = collection;
     }
 
   });
@@ -468,7 +484,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
 
     toggleQuery: function (event) {
       $('#dashboard-content').scrollTop(0);
-      this.$('#query').toggle('fast');
+      this.$('#query').toggle('slow');
     },
 
     beforeRender: function () {
@@ -477,17 +493,14 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
         previewFn: this.previewView,
         hasReduce: false,
         showPreview: false,
-        database: this.database
+        database: this.database,
       }));
-
-      this.$('#query').hide();
     },
 
     afterRender: function () {
       if (this.params) {
         this.advancedOptions.updateFromParams(this.params);
       }
-
     },
 
     updateAllDocs: function (event, paramInfo) {
@@ -518,9 +531,12 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       }
 
       var fragment = window.location.hash.replace(/\?.*$/, '');
-      fragment = fragment + '?' + $.param(params);
-      FauxtonAPI.navigate(fragment, {trigger: false});
 
+      if (!_.isEmpty(params)) {
+        fragment = fragment + '?' + $.param(params);
+      }
+
+      FauxtonAPI.navigate(fragment, {trigger: false});
       FauxtonAPI.triggerRouteEvent('updateAllDocs', {allDocs: true});
     },
 
@@ -537,7 +553,8 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       "click button.all": "selectAll",
       "click button.bulk-delete": "bulkDelete",
       "click #collapse": "collapse",
-      "change .row-select":"toggleTrash"
+      "change .row-select":"toggleTrash",
+      "click #js-end-results": "scrollToQuery"
     },
 
     toggleTrash: function () {
@@ -548,26 +565,45 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       }
     },
 
+    scrollToQuery: function () {
+      $('#dashboard-content').animate({ scrollTop: 0 }, 'slow');
+    },
+
     initialize: function(options){
       this.nestedView = options.nestedView || Views.Document;
       this.rows = {};
       this.viewList = !! options.viewList;
       this.database = options.database;
+
       if (options.ddocInfo) {
         this.designDocs = options.ddocInfo.designDocs;
         this.ddocID = options.ddocInfo.id;
       }
       this.newView = options.newView || false;
+      this.docParams = options.docParams;
+      this.params = options.params || {};
       this.expandDocs = true;
+      this.perPageDefault = this.docParams.limit || 20;
     },
 
     establish: function() {
       if (this.newView) { return null; }
 
-      return this.collection.fetch({reset: true}).fail(function() {
-        // TODO: handle error requests that slip through
-        // This should just throw a notification, not break the page
-        console.log("ERROR: ", arguments);
+      return this.collection.fetch({
+        reset: true,
+        success:  function() { },
+        error: function(model, xhr, options){
+          // TODO: handle error requests that slip through
+          // This should just throw a notification, not break the page
+          FauxtonAPI.addNotification({
+            msg: "Bad Request",
+            type: "error"
+          });
+
+          //now redirect back to alldocs
+          FauxtonAPI.navigate(model.database.url("index") + "?limit=100");
+          console.log("ERROR: ", arguments);
+        }
       });
     },
 
@@ -576,16 +612,10 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
     },
 
     serialize: function() {
-      var requestDuration = false;
-
-      if (this.collection.requestDurationInString) {
-        requestDuration = this.collection.requestDurationInString();
-      }
-
       return {
         viewList: this.viewList,
-        requestDuration: requestDuration,
-        expandDocs: this.expandDocs
+        expandDocs: this.expandDocs,
+        endOfResults: !this.pagination.canShowNextfn()
       };
     },
 
@@ -646,70 +676,41 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
 
     addPagination: function () {
       var collection = this.collection;
-      var perPage = function () {
-        if (collection.params.limit && collection.skipFirstItem) {
-          return parseInt(collection.params.limit, 10) - 1;
-        } else if (collection.params.limit) {
-          return parseInt(collection.params.limit, 10);
-        }
-
-        return 20;
-      };
 
       this.pagination = new Components.IndexPagination({
         collection: this.collection,
         scrollToSelector: '#dashboard-content',
-        previousUrlfn: function () {
-          return collection.urlPreviousPage(perPage(), this.previousParams.pop());
-        },
-        canShowPreviousfn: function () {
-          if (this.previousParams.length === 0) {
-            return false;
-          }
-
-          return true;
-        },
-        canShowNextfn: function () {
-          if (collection.length < (perPage() -1)) {
-            return false;
-          }
-
-          return true;
-        },
-
-        nextUrlfn: function () {
-          return collection.urlNextPage(perPage());
-        }
+        docLimit: this.params.limit,
+        perPage: this.perPageDefault
       });
     },
 
     cleanup: function () {
-      this.allDocsNumber.remove();
+      this.pagination && this.pagination.remove();
+      this.allDocsNumber && this.allDocsNumber.remove();
       _.each(this.rows, function (row) {row.remove();});
-
-      if (!this.pagination) { return; }
-      this.pagination.remove();
     },
 
     beforeRender: function() {
-      var showNumbers = true;
 
       if (!this.pagination) {
         this.addPagination();
       }
 
-      this.insertView('#documents-pagination', this.pagination);
+      if (!this.params.keys) { //cannot paginate with keys
+        this.insertView('#documents-pagination', this.pagination);
+      }
 
-      if (this.designDocs || this.collection.idxType === '_view' || this.collection.params.startkey === '"_design"') {
-        showNumbers = false;
+      if (!this.allDocsNumber) {
+        this.allDocsNumber = new Views.AllDocsNumber({
+          collection: this.collection,
+          newView: this.newView,
+          pagination: this.pagination,
+          perPageDefault: this.perPageDefault
+        });
       }
 
-      this.allDocsNumber = this.setView('#item-numbers', new Views.AllDocsNumber({
-        collection: this.collection,
-        newView: this.newView,
-        showNumbers: showNumbers,
-        pagination: this.pagination
-      }));
+      this.setView('#item-numbers', this.allDocsNumber);
 
       var docs = this.expandDocs ? this.collection : this.collection.simple();
 
@@ -720,8 +721,32 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       }, this);
     },
 
+    setCollection: function (collection) {
+      this.collection = collection;
+      this.pagination.setCollection(collection);
+      this.allDocsNumber.setCollection(collection);
+    },
+
+    setParams: function (docParams, urlParams) {
+      this.docParams = docParams;
+      this.params = urlParams;
+      this.perPageDefault = this.docParams.limit;
+
+      if (this.params.limit) {
+        this.pagination.docLimit = this.params.limit;
+      }
+    },
+
     afterRender: function(){
       prettyPrint();
+    },
+
+    perPage: function () {
+      return this.allDocsNumber.perPage();
+    },
+
+    updatePerPage: function (newPerPage) {
+      this.collection.updateLimit(newPerPage);
     }
   });
 
@@ -942,9 +967,6 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
           model = this.model;
 
       editor.editor.on("change", function (event) {
-        //if (event.data.action !== 'removeText') { return; }
-        //if (!event.data.text.match(/_id/) && !event.data.text.match(/_rev/)) { return; }
-
         var changedDoc;
         try {
           changedDoc = JSON.parse(editor.getValue());
@@ -1033,7 +1055,6 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       this.viewName = options.viewName;
       this.updateViewFn = options.updateViewFn;
       this.previewFn = options.previewFn;
-      //this.hadReduce = options.hasReduce || true;
 
       if (typeof(options.hasReduce) === 'undefined') {
         this.hasReduce = true;
@@ -1049,9 +1070,9 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
     },
 
     events: {
-      "change form.view-query-update input": "updateFilters",
-      "change form.view-query-update select": "updateFilters",
-      "submit form.view-query-update": "updateView",
+      "change form.js-view-query-update input": "updateFilters",
+      "change form.js-view-query-update select": "updateFilters",
+      "submit form.js-view-query-update": "updateView",
       "click button.preview": "previewView"
     },
 
@@ -1071,11 +1092,15 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
     },
 
     queryParams: function () {
-      var $form = this.$(".view-query-update");
+      var $form = this.$(".js-view-query-update");
       // Ignore params without a value
-      var params = _.filter($form.serializeArray(), function(param) {
-        return param.value;
-      });
+      var params = _.reduce($form.serializeArray(), function(params, param) {
+        if (!param.value) { return params; }
+        if (param.name === "limit" && param.value === 'None') { return params; }
+
+        params.push(param);
+        return params;
+      }, []);
 
       // Validate *key* params to ensure they're valid JSON
       var keyParams = ["key","keys","startkey","endkey"];
@@ -1103,7 +1128,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
     },
 
     updateFiltersFor: function(name, $ele) {
-      var $form = $ele.parents("form.view-query-update:first");
+      var $form = $ele.parents("form.js-view-query-update:first");
       switch (name) {
         // Reduce constraints
         //   - Can't include_docs for reduce=true
@@ -1131,12 +1156,13 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
     },
 
     updateFromParams: function (params) {
-      var $form = this.$el.find("form.view-query-update");
+      var $form = this.$el.find("form.js-view-query-update");
       _.each(params, function(val, key) {
         var $ele;
         switch (key) {
           case "limit":
-            case "group_level":
+          case "group_level":
+            if (!val) { return; }
             $form.find("select[name='"+key+"']").val(val);
           break;
           case "include_docs":
@@ -1247,7 +1273,8 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       "click button.preview": "previewView",
       "click #db-views-tabs-nav": 'toggleIndexNav',
       "click .beautify_map":  "beautifyCode",
-      "click .beautify_reduce":  "beautifyCode"
+      "click .beautify_reduce":  "beautifyCode",
+      "click #query-options-wrapper": 'toggleIndexNav'
     },
 
     langTemplates: {
@@ -1368,6 +1395,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
           that.mapEditor.editSaved();
           that.reduceEditor && that.reduceEditor.editSaved();
 
+
           FauxtonAPI.addNotification({
             msg: "View has been saved.",
             type: "success",
@@ -1448,9 +1476,11 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       }
 
        var fragment = window.location.hash.replace(/\?.*$/, '');
-       fragment = fragment + '?' + $.param(params);
-       FauxtonAPI.navigate(fragment, {trigger: false});
+       if (!_.isEmpty(params)) {
+        fragment = fragment + '?' + $.param(params);
+       }
 
+       FauxtonAPI.navigate(fragment, {trigger: false});
        FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: this.ddocID, view: this.viewName});
     },
 
@@ -1622,18 +1652,25 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
         database: this.database
       }));
 
-      this.advancedOptions = this.insertView('#query', new Views.AdvancedOptions({
-        updateViewFn: this.updateView,
-        previewFn: this.previewView,
-        database: this.database,
-        viewName: this.viewName,
-        ddocName: this.model.id,
-        hasReduce: this.hasReduce()
-      }));
+
+      if (!this.newView) {
+        this.eventer = _.extend({}, Backbone.Events);
+
+        this.advancedOptions = this.insertView('#query', new Views.AdvancedOptions({
+          updateViewFn: this.updateView,
+          previewFn: this.previewView,
+          database: this.database,
+          viewName: this.viewName,
+          ddocName: this.model.id,
+          hasReduce: this.hasReduce(),
+          eventer: this.eventer
+        }));
+      }
+
     },
 
     afterRender: function() {
-      if (this.params) {
+      if (this.params && !this.newView) {
         this.advancedOptions.updateFromParams(this.params);
       }
 
@@ -1646,7 +1683,6 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
         this.$('#index-nav').parent().removeClass('active');
       }
 
-
     },
 
     showEditors: function () {
@@ -1737,11 +1773,10 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       return {
         changes_url: '#' + this.database.url('changes'),
         permissions_url: '#' + this.database.url('app') + '/permissions',
-        db_url: '#' + this.database.url('index') + '?limit=' + Databases.DocLimit,
+        db_url: '#' + this.database.url('index'),
         database: this.collection.database,
         database_url: '#' + this.database.url('app'),
         docLinks: docLinks,
-        docLimit: Databases.DocLimit,
         addLinks: addLinks,
         newLinks: newLinks,
         extensionList: extensionList > 0

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/fauxton/base.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/fauxton/base.js b/src/fauxton/app/addons/fauxton/base.js
index 35babb5..a6e462a 100644
--- a/src/fauxton/app/addons/fauxton/base.js
+++ b/src/fauxton/app/addons/fauxton/base.js
@@ -45,7 +45,6 @@ function(app, FauxtonAPI, resizeColumns) {
     }
   });
 
-
   Fauxton.initialize = function () {
     app.footer = new Fauxton.Footer({el: "#footer-content"}),
     app.navBar = new Fauxton.NavBar();
@@ -93,7 +92,7 @@ function(app, FauxtonAPI, resizeColumns) {
     });
   };
 
-  Fauxton.Breadcrumbs = Backbone.View.extend({
+  Fauxton.Breadcrumbs = FauxtonAPI.View.extend({
     template: "templates/fauxton/breadcrumbs",
 
     serialize: function() {
@@ -114,10 +113,7 @@ function(app, FauxtonAPI, resizeColumns) {
     }
   });
 
-  // TODO: this View should extend from FauxtonApi.View.
-  // Chicken and egg problem, api.js extends fauxton/base.js.
-  // Need to sort the loading order.
-  Fauxton.Footer = Backbone.View.extend({
+  Fauxton.Footer = FauxtonAPI.View.extend({
     template: "templates/fauxton/footer",
 
     initialize: function() {
@@ -135,7 +131,7 @@ function(app, FauxtonAPI, resizeColumns) {
     }
   });
 
-  Fauxton.NavBar = Backbone.View.extend({
+  Fauxton.NavBar = FauxtonAPI.View.extend({
     className:"navbar",
     template: "templates/fauxton/nav_bar",
     // TODO: can we generate this list from the router?
@@ -260,7 +256,7 @@ function(app, FauxtonAPI, resizeColumns) {
     // TODO: ADD ACTIVE CLASS
   });
 
-  Fauxton.ApiBar = Backbone.View.extend({
+  Fauxton.ApiBar = FauxtonAPI.View.extend({
     template: "templates/fauxton/api_bar",
     endpoint: '_all_docs',
 
@@ -304,7 +300,7 @@ function(app, FauxtonAPI, resizeColumns) {
 
   });
 
-  Fauxton.Notification = Backbone.View.extend({
+  Fauxton.Notification = FauxtonAPI.View.extend({
     fadeTimer: 5000,
 
     initialize: function(options) {

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/fauxton/components.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/fauxton/components.js b/src/fauxton/app/addons/fauxton/components.js
index 0422b5a..7dcf2d7 100644
--- a/src/fauxton/app/addons/fauxton/components.js
+++ b/src/fauxton/app/addons/fauxton/components.js
@@ -69,19 +69,68 @@ function(app, FauxtonAPI, ace, spin) {
     initialize: function (options) {
       this.previousUrlfn = options.previousUrlfn;
       this.nextUrlfn = options.nextUrlfn;
-      this.canShowPreviousfn = options.canShowPreviousfn;
-      this.canShowNextfn = options.canShowNextfn;
       this.scrollToSelector = options.scrollToSelector;
       _.bindAll(this);
-      this.previousParams = [];
+      this.docLimit = options.docLimit || 1000000;
+      this.perPage = options.perPage || 20;
+      this.setDefaults();
+    },
+
+    setDefaults: function () {
+      this._pageNumber = [];
+      this._pageStart = 1;
+      this.enabled = true;
+      this.currentPage = 1;
+    },
+
+    canShowPreviousfn: function () {
+      if (this._pageStart === 1 || !this.enabled) {
+        return false;
+      }
+      return true;
+    },
+
+    canShowNextfn: function () {
+      if (!this.enabled) { return this.enabled; }
+
+      if (this.collection.length < (this.perPage -1)) {
+        return false;
+      }
+
+      if ((this.pageStart() + this.perPage) >= this.docLimit) {
+        return false;
+      }
+
+      if (this.collection.viewMeta && this.collection.viewMeta.total_rows <= this.pageStart() + this.perPage) {
+        return false;
+      }
+
+      return true;
     },
 
     previousClicked: function (event) {
       event.preventDefault();
       event.stopPropagation();
       if (!this.canShowPreviousfn()) { return; }
-      FauxtonAPI.navigate(this.previousUrlfn(), {trigger: false});
-      FauxtonAPI.triggerRouteEvent('paginate', 'previous');
+
+      this.decPageNumber();
+
+      FauxtonAPI.triggerRouteEvent('paginate', {
+       direction: 'previous',
+       perPage: this.perPage,
+       currentPage: this.currentPage
+      });
+    },
+
+    documentsLeftToFetch: function () {
+      var documentsLeftToFetch = this.docLimit - this.totalDocsViewed(),
+          limit = this.perPage;
+
+      if (documentsLeftToFetch < this.perPage ) {
+        limit = documentsLeftToFetch;
+      }
+
+      return limit;
     },
 
     nextClicked: function (event) {
@@ -89,14 +138,14 @@ function(app, FauxtonAPI, ace, spin) {
       event.stopPropagation();
       if (!this.canShowNextfn()) { return; }
 
-      var params = _.clone(this.collection.params);
+      this.incPageNumber();
 
-      if (params) {
-        this.previousParams.push(params);
-      }
+      FauxtonAPI.triggerRouteEvent('paginate', {
+       direction: 'next',
+       perPage: this.documentsLeftToFetch(),
+       currentPage: this.currentPage
+      });
 
-      FauxtonAPI.navigate(this.nextUrlfn(), {trigger: false});
-      FauxtonAPI.triggerRouteEvent('paginate', 'next');
     },
 
     serialize: function () {
@@ -106,30 +155,58 @@ function(app, FauxtonAPI, ace, spin) {
       };
     },
 
-    pageLimit: function () {
-      var limit = 20;
+    updatePerPage: function (newPerPage) {
+      this.setDefaults();
+      this.perPage = newPerPage;
+    },
+
+    page: function () {
+      return this._pageStart - 1;
+    },
+
+    incPageNumber: function () {
+      this.currentPage = this.currentPage + 1;
+      this._pageNumber.push({perPage: this.perPage});
+      this._pageStart = this._pageStart + this.perPage;
+    },
+
+    totalDocsViewed: function () {
+      return _.reduce(this._pageNumber, function (total, value) {
+        return total + value.perPage;
+      }, 0);
+    },
 
-      if (this.collection.params.limit && this.collection.skipFirstItem) {
-        limit = parseInt(this.collection.params.limit, 10) - 1;
-      } else if (this.collection.params.limit) {
-        limit = parseInt(this.collection.params.limit, 10);
+    decPageNumber: function () {
+      this.currentPage = this.currentPage - 1;
+      this._pageNumber.pop();
+      var val = this._pageStart - this.perPage;
+      if (val < 1) {
+        val = 1;
       }
 
-      return limit;
+      this._pageStart = val;
     },
 
     pageStart: function () {
-      return (this.previousParams.length * this.pageLimit()) + 1;
-
+      return this._pageStart;
     },
 
     pageEnd: function () {
-      if (this.collection.length < this.pageLimit()) {
-        return (this.previousParams.length * this.pageLimit()) + this.collection.length;
-      }
+      return this.page() + this.collection.length;
+    },
 
-      return (this.previousParams.length * this.pageLimit()) + this.pageLimit();
-    }
+    disable: function () {
+      this.enabled = false;
+    },
+
+    enable: function () {
+      this.enabled = true;
+    },
+
+    setCollection: function (collection) {
+      this.collection = collection;
+      this.setDefaults();
+    },
 
   });
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/app/addons/fauxton/tests/paginateSpec.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/fauxton/tests/paginateSpec.js b/src/fauxton/app/addons/fauxton/tests/paginateSpec.js
index 535e26f..8fc409a 100644
--- a/src/fauxton/app/addons/fauxton/tests/paginateSpec.js
+++ b/src/fauxton/app/addons/fauxton/tests/paginateSpec.js
@@ -58,15 +58,6 @@ define([
         //FauxtonAPI.navigate.restore && FauxtonAPI.navigate.restore(); 
       });
 
-      it('Should navigate', function () {
-        var navigateMock = sinon.spy(FauxtonAPI, 'navigate');
-
-        paginate.$('a#next').click();
-
-        assert.ok(navigateMock.calledOnce);
-        FauxtonAPI.navigate.restore();
-      });
-
       it('Should trigger routeEvent', function () {
         var navigateMock = sinon.spy(FauxtonAPI, 'triggerRouteEvent');
 
@@ -81,15 +72,6 @@ define([
 
     describe('#previous', function () {
 
-      it('Should navigate', function () {
-        var navigateMock = sinon.spy(FauxtonAPI, 'navigate');
-
-        paginate.$('a#previous').click();
-
-        assert.ok(navigateMock.calledOnce);
-        FauxtonAPI.navigate.restore();
-      });
-
       it('Should trigger routeEvent', function () {
         var navigateMock = sinon.spy(FauxtonAPI, 'triggerRouteEvent');
 


[06/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Fix formatting


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/17a97631
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/17a97631
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/17a97631

Branch: refs/heads/Query-Options-UI
Commit: 17a97631fdbb3c45dd82081b51e9cf26c39fb884
Parents: 8de6b6e
Author: Alexander Shorin <kx...@apache.org>
Authored: Tue Feb 25 20:27:33 2014 +0400
Committer: Alexander Shorin <kx...@apache.org>
Committed: Tue Feb 25 20:27:33 2014 +0400

----------------------------------------------------------------------
 share/doc/src/json-structure.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/17a97631/share/doc/src/json-structure.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/json-structure.rst b/share/doc/src/json-structure.rst
index 8eae7df..ab3ae45 100644
--- a/share/doc/src/json-structure.rst
+++ b/share/doc/src/json-structure.rst
@@ -227,7 +227,7 @@ List of Active Tasks
 +--------------------------------+---------------------------------------------+
 | Field                          | Description                                 |
 +================================+=============================================+
-| tasks [array]                  | Active Tasks                                 |
+| tasks [array]                  | Active Tasks                                |
 +--------------------------------+---------------------------------------------+
 |     pid                        | Process ID                                  |
 +--------------------------------+---------------------------------------------+


[14/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Turn workers back on for errors in the editors


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/72d61343
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/72d61343
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/72d61343

Branch: refs/heads/Query-Options-UI
Commit: 72d61343b8b6edbfea3c9412bcc9edb1c246bf34
Parents: 929b3a0
Author: suelockwood <de...@apache.org>
Authored: Fri Feb 28 15:38:17 2014 -0500
Committer: suelockwood <de...@apache.org>
Committed: Fri Feb 28 15:38:17 2014 -0500

----------------------------------------------------------------------
 src/fauxton/app/addons/fauxton/components.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/72d61343/src/fauxton/app/addons/fauxton/components.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/fauxton/components.js b/src/fauxton/app/addons/fauxton/components.js
index 71d78b1..0422b5a 100644
--- a/src/fauxton/app/addons/fauxton/components.js
+++ b/src/fauxton/app/addons/fauxton/components.js
@@ -284,7 +284,7 @@ function(app, FauxtonAPI, ace, spin) {
     afterRender: function () {
       this.editor = ace.edit(this.editorId);
       this.setHeightToLineCount();
-      this.editor.getSession().setUseWorker(false);
+
       this.editor.setTheme("ace/theme/" + this.theme);
 
       this.editor.getSession().setMode("ace/mode/" + this.mode);


[20/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Cleaning up query options UI


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/7573dc28
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/7573dc28
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/7573dc28

Branch: refs/heads/Query-Options-UI
Commit: 7573dc28e93029d00c1180ce792e5d98e970d119
Parents: 4e60f0b
Author: suelockwood <de...@apache.org>
Authored: Fri Feb 21 13:42:45 2014 -0500
Committer: Garren Smith <ga...@gmail.com>
Committed: Thu Mar 6 12:35:38 2014 +0200

----------------------------------------------------------------------
 .../addons/documents/assets/less/documents.less |   7 +
 .../documents/templates/advanced_options.html   | 156 +++++++++++--------
 src/fauxton/app/addons/documents/views.js       |  87 ++++++++++-
 3 files changed, 184 insertions(+), 66 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/7573dc28/src/fauxton/app/addons/documents/assets/less/documents.less
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/assets/less/documents.less b/src/fauxton/app/addons/documents/assets/less/documents.less
index c30a9af..61d864b 100644
--- a/src/fauxton/app/addons/documents/assets/less/documents.less
+++ b/src/fauxton/app/addons/documents/assets/less/documents.less
@@ -23,6 +23,12 @@ button.beautify {
 	margin-top: 20px;
 }
 
+.toggle-btns {
+    label{
+        margin-right: 0;
+    }
+}
+
 #per-page {
   float: right;
 
@@ -32,6 +38,7 @@ button.beautify {
   
 }
 
+
 /** used in all_docs_list.html **/
 .view {
     table td div {

http://git-wip-us.apache.org/repos/asf/couchdb/blob/7573dc28/src/fauxton/app/addons/documents/templates/advanced_options.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/templates/advanced_options.html b/src/fauxton/app/addons/documents/templates/advanced_options.html
index e282c62..274c107 100644
--- a/src/fauxton/app/addons/documents/templates/advanced_options.html
+++ b/src/fauxton/app/addons/documents/templates/advanced_options.html
@@ -12,79 +12,113 @@ License for the specific language governing permissions and limitations under
 the License.
 -->
 <div class="errors-container"></div>
-<form class="js-view-query-update custom-inputs">
-  <div class="controls-group">
-    <div class="row-fluid">
+<form class="view-query-update custom-inputs">
+
+
+<!-- tabs for choosing Keys or Start & end -->
+
+
+
+
+  <div class="btn-group toggle-btns">
+    <label for="showKeys" class="drop-down btn active">
+     Specific Keys 
+    </label>
+    <label for="showStartEnd" class="drop-down btn">
+      Bounded Queries
+    </label>
+  </div>
+
+  <div class="controls-group well">
+    <div class="row-fluid" id="js-showKeys">
       <div class="controls controls-row">
-        <input name="key" class="span6" type="text" placeholder="Key">
-        <input name="keys" class="span6" type="text" placeholder="Keys">
+        <input name="keys" class="input-xxlarge" type="text" placeholder="Enter a key, or list of keys seperated by a comma.">
       </div>
     </div>
-    <div class="row-fluid">
+    <div class="row-fluid hide" id="js-showStartEnd">
       <div class="controls controls-row">
-        <input name="startkey" class="span6" type="text" placeholder="Start Key">
+        <input name="startkey" class="span6" type="text" placeholder="Start Key" disabled>
         <input name="endkey" class="span6" type="text" placeholder="End Key">
       </div>
+      <div class="controls controls-row checkbox inline">  
+        <input id="check5" name="inclusive_end" type="checkbox" value="false" disabled>
+        <label for="check5">Disable Inclusive End</label>
+      </div>
     </div>
   </div>
+
+
+
+<!-- Limit and Skip are conditional -->
+
   <div class="controls-group">
-    <div class="row-fluid">
-      <div class="controls controls-row">
-        <div class="checkbox inline">  
-          <input id="check1" type="checkbox" name="include_docs" value="true">  
-          <label name="include_docs" for="check1">Include Docs</label>  
-          <% if (hasReduce) { %>
-          <input id="check2" name="reduce" type="checkbox" value="true">
-          <label for="check2">Reduce</label>  
-        </div> 
-        <label id="select1" class="drop-down inline">
-          Group Level:
-          <select id="select1" disabled name="group_level" class="input-small">
-            <option value="0">None</option>
-            <option value="1">1</option>
-            <option value="2">2</option>
-            <option value="3">3</option>
-            <option value="4">4</option>
-            <option value="5">5</option>
-            <option value="6">6</option>
-            <option value="7">7</option>
-            <option value="8">8</option>
-            <option value="9">9</option>
-            <option value="999" selected="selected">exact</option>
-          </select>
-        </label>
-        <% } else{ %>
-        </div>
-        <% } %>
+      <label class="drop-down inline">
+        Limit:
+        <select name="limit" class="input-small" disabled>
+          <option>5</option>
+          <option>10</option>
+          <option selected="selected">20</option>
+          <option>30</option>
+          <option>50</option>
+          <option>100</option>
+		  <option>500</option>
+        </select>
+      </label>
+      <label for="skipRows" class="inline drop-down">
+        Skip
+        <input name="skip" class="input-large" type="text" id="skipRows" disabled placeholder="Number of rows to skip">
+      </label>
+      <span class="js-disabled-message"> Limit &amp; and Skip are disabled when using Keys.</span>
 
-        <div class="checkbox inline">  
-          <input id="check3" name="stale" type="checkbox" value="ok">
-          <label for="check3">Stale</label>
-          <input id="check4" name="descending" type="checkbox" value="true">  
-          <label for="check4">Descending</label>  
-        </div> 
-        <label class="drop-down inline">
-          Limit:
-          <select name="limit" class="input-small">
-            <option>5</option>
-            <option>10</option>
-            <option >20</option>
-            <option>30</option>
-            <option>50</option>
-            <option >100</option>
-            <option>500</option>
-            <option selected="selected">None</option>
-          </select>
-        </label>
-        <div class="checkbox inline">  
-          <input id="check5" name="inclusive_end" type="checkbox" value="false">
-          <label for="check5">Disable Inclusive End</label>
-          <input id="check6" name="update_seq" type="checkbox" value="true">  
-          <label for="check6">Update Sequence</label>  
-        </div>
+      <div class="checkbox inline"> 
+        <input id="check1" type="checkbox" name="include_docs" value="true">  
+        <label name="include_docs" for="check1">Include Docs (show the entire doc body)</label>  
+
+        <input id="check3" name="stale" type="checkbox" value="ok">
+        <label for="check3">Stale</label>
       </div>
-    </div>
+
+      <% if (hasReduce) { %>
+      <div class="checkbox inline">  
+        <input id="check2" name="reduce" type="checkbox" value="true">
+        <label for="check2">Reduce</label>  
+      </div> 
+      <label id="select1" class="drop-down inline">
+        Group Level:
+        <select id="select1" disabled name="group_level" class="input-small">
+          <option value="0">None</option>
+          <option value="1">1</option>
+          <option value="2">2</option>
+          <option value="3">3</option>
+          <option value="4">4</option>
+          <option value="5">5</option>
+          <option value="6">6</option>
+          <option value="7">7</option>
+          <option value="8">8</option>
+          <option value="9">9</option>
+          <option value="999" selected="selected">exact</option>
+        </select>
+      </label>
+      <% } %>
+
+      <label id="select2" class="drop-down inline">
+        Order: 
+        <select id="select2" name="descending" class="input-large">
+          <option value="false">Accending</option>
+          <option value="true">Descending</option>
+        </select>
+      </label>
+
+      <div class="checkbox inline">  
+        <input id="check6" name="update_seq" type="checkbox" value="true">  
+        <label for="check6">Update Sequence</label>  
+      </div>
+
   </div>
+
+
+
+
   <div class="controls-group">
     <div class="row-fluid">
       <div id="button-options" class="controls controls-row">

http://git-wip-us.apache.org/repos/asf/couchdb/blob/7573dc28/src/fauxton/app/addons/documents/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/views.js b/src/fauxton/app/addons/documents/views.js
index 54ec261..e57071a 100644
--- a/src/fauxton/app/addons/documents/views.js
+++ b/src/fauxton/app/addons/documents/views.js
@@ -1073,7 +1073,34 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       "change form.js-view-query-update input": "updateFilters",
       "change form.js-view-query-update select": "updateFilters",
       "submit form.js-view-query-update": "updateView",
-      "click button.preview": "previewView"
+      "click button.preview": "previewView",
+      "click .toggle-btns > label":  "toggleQuery"
+    },
+
+    toggleQuery: function(e){
+      e.preventDefault();
+      var showFunctionName =this.$(e.currentTarget).attr("for");
+      //highlight current
+      this.$(".toggle-btns > label").removeClass('active');
+      this.$(e.currentTarget).addClass("active");
+      
+      this.$("[id^='js-show']").hide();
+
+      //show section & disable what needs to be disabled
+      this[showFunctionName]();
+    },
+
+    showKeys: function(){
+      this.$("#js-showKeys, .js-disabled-message").show();
+      this.$('[name="skip"],[name="startkey"],[name="limit"],[name="endkey"],[name="inclusive_end"]').attr("disabled","true");
+      this.$('[name="keys"]').removeAttr("disabled");
+    },
+
+    showStartEnd: function(){
+      this.$("#js-showStartEnd").show();
+      this.$('[name="skip"],[name="startkey"],[name="limit"],[name="endkey"],[name="inclusive_end"]').removeAttr("disabled");
+      this.$('.js-disabled-message').hide();
+      this.$('[name="keys"]').attr("disabled","true");
     },
 
     beforeRender: function () {
@@ -1090,10 +1117,25 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       this.hasReduce = hasReduce;
       this.render();
     },
+    getKeys: function(val){
+      var keys = val.value.replace(/\s/g,"").split(',');
 
+      if (keys.length > 1){
+        return {
+          name: "keys",
+          value:  JSON.stringify(keys)
+        };
+      } else if (keys.length === 1) {
+        return {
+          name: "key",
+          value: keys[0]
+        };
+      }
+
+    },
     queryParams: function () {
-      var $form = this.$(".js-view-query-update");
-      // Ignore params without a value
+      var $form = this.$(".view-query-update"),
+          getKeys = this.getKeys;
       var params = _.reduce($form.serializeArray(), function(params, param) {
         if (!param.value) { return params; }
         if (param.name === "limit" && param.value === 'None') { return params; }
@@ -1101,9 +1143,25 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
         params.push(param);
         return params;
       }, []);
+      });
+      var filteredParams = _.filter(params, function(param) {
+        return param.value;
+      });
+        
+
+
+      var params = _.map(filteredParams, function(param) {
+        if (param.name === "keys"){
+          return getKeys(param);
+        }else{
+          return param;
+        }
+      });
+
+
 
       // Validate *key* params to ensure they're valid JSON
-      var keyParams = ["key","keys","startkey","endkey"];
+      var keyParams = ["keys","startkey","endkey"];
       var errorParams = _.filter(params, function(param) {
         if (_.contains(keyParams, param.name)) {
           try {
@@ -1118,6 +1176,24 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
       });
 
       return {params: params, errorParams: errorParams};
+
+
+
+
+
+
+      // Ignore params without a value
+      _.map($form.serializeArray(), function(param) {
+        if (param.value){
+          if (param.name === "keys"){
+            var keys = getKeys(param.value);
+            data[keys.name] = keys.value;
+          }else{
+            data[param.name] = param.value;
+          }
+        }
+      });
+
     },
 
     updateFilters: function(event) {
@@ -1161,13 +1237,13 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
         var $ele;
         switch (key) {
           case "limit":
+          case "descending":
           case "group_level":
             if (!val) { return; }
             $form.find("select[name='"+key+"']").val(val);
           break;
           case "include_docs":
             case "stale":
-            case "descending":
             case "inclusive_end":
             $form.find("input[name='"+key+"']").prop('checked', true);
           break;
@@ -1186,6 +1262,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
     },
 
     updateView: function (event) {
+      event.preventDefault();
       this.updateViewFn(event, this.queryParams());
     },
 


[21/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
2183 (JIRA)  remove stale from query options


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/ed094a7c
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/ed094a7c
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/ed094a7c

Branch: refs/heads/Query-Options-UI
Commit: ed094a7c11957478e7e010ab72241b2512e8ce89
Parents: 7573dc2
Author: suelockwood <de...@apache.org>
Authored: Wed Mar 5 14:27:44 2014 -0500
Committer: Garren Smith <ga...@gmail.com>
Committed: Thu Mar 6 12:35:38 2014 +0200

----------------------------------------------------------------------
 src/fauxton/app/addons/documents/templates/advanced_options.html | 3 ---
 1 file changed, 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/ed094a7c/src/fauxton/app/addons/documents/templates/advanced_options.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/templates/advanced_options.html b/src/fauxton/app/addons/documents/templates/advanced_options.html
index 274c107..2eaff2a 100644
--- a/src/fauxton/app/addons/documents/templates/advanced_options.html
+++ b/src/fauxton/app/addons/documents/templates/advanced_options.html
@@ -73,9 +73,6 @@ the License.
       <div class="checkbox inline"> 
         <input id="check1" type="checkbox" name="include_docs" value="true">  
         <label name="include_docs" for="check1">Include Docs (show the entire doc body)</label>  
-
-        <input id="check3" name="stale" type="checkbox" value="ok">
-        <label for="check3">Stale</label>
       </div>
 
       <% if (hasReduce) { %>


[15/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Cancel event propagation on replication swap.

Previous implementation called e.preventDefault() but did not stop upwards
propagation - the click event would therefore trigger navigation to the main
database panel. jQuery calls preventDefault and stopPropagation
when false is returned from an event handler so just do that here.


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/b22f2a48
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/b22f2a48
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/b22f2a48

Branch: refs/heads/Query-Options-UI
Commit: b22f2a48c48f3cfa9b4c082cfee87b76e8d44c37
Parents: 72d6134
Author: Will Holley <wi...@gmail.com>
Authored: Fri Feb 28 15:07:13 2014 +0000
Committer: Will Holley <wi...@gmail.com>
Committed: Mon Mar 3 08:06:09 2014 +0000

----------------------------------------------------------------------
 src/fauxton/app/addons/replication/views.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/b22f2a48/src/fauxton/app/addons/replication/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/views.js b/src/fauxton/app/addons/replication/views.js
index 1b31a9d..a19e609 100644
--- a/src/fauxton/app/addons/replication/views.js
+++ b/src/fauxton/app/addons/replication/views.js
@@ -175,7 +175,6 @@ function(app, FauxtonAPI, Components, replication) {
       this.startReplication(formJSON);
     },	
     swapFields: function(e){
-      e.preventDefault();
       //WALL O' VARIABLES
       var $fromSelect = this.$('#from_name'),
           $toSelect = this.$('#to_name'),
@@ -191,6 +190,9 @@ function(app, FauxtonAPI, Components, replication) {
 
       $fromInput.val(toInputVal);
       $toInput.val(fromInputVal);
+
+      // prevent other click handlers from running
+      return false;
     }
   });
 


[12/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Added Highlight to the admin party to make it stand out more.


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/0df722e6
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/0df722e6
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/0df722e6

Branch: refs/heads/Query-Options-UI
Commit: 0df722e66b66bbad172a237b6fd1521b4a346676
Parents: 48f586f
Author: suelockwood <de...@apache.org>
Authored: Fri Feb 28 09:55:05 2014 -0500
Committer: suelockwood <de...@apache.org>
Committed: Fri Feb 28 09:55:05 2014 -0500

----------------------------------------------------------------------
 src/Makefile.am                                      |  1 +
 src/fauxton/app/addons/auth/assets/less/auth.less    | 15 +++++++++++++++
 .../app/addons/auth/templates/nav_link_title.html    |  2 +-
 3 files changed, 17 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/0df722e6/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 32a1a6a..59aa6bf 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,6 +52,7 @@ FAUXTON_FILES = \
     fauxton/app/addons/auth/base.js \
     fauxton/app/addons/auth/resources.js \
     fauxton/app/addons/auth/routes.js \
+    fauxton/app/addons/auth/assets/less/auth.less \
     fauxton/app/addons/auth/templates/change_password.html \
     fauxton/app/addons/auth/templates/create_admin.html \
     fauxton/app/addons/auth/templates/login.html \

http://git-wip-us.apache.org/repos/asf/couchdb/blob/0df722e6/src/fauxton/app/addons/auth/assets/less/auth.less
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/auth/assets/less/auth.less b/src/fauxton/app/addons/auth/assets/less/auth.less
new file mode 100644
index 0000000..8a71817
--- /dev/null
+++ b/src/fauxton/app/addons/auth/assets/less/auth.less
@@ -0,0 +1,15 @@
+/*  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.
+ */
+#primary-navbar .navbar nav .nav li a#user-create-admin{
+  background-color: #af2d24;
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/0df722e6/src/fauxton/app/addons/auth/templates/nav_link_title.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/auth/templates/nav_link_title.html b/src/fauxton/app/addons/auth/templates/nav_link_title.html
index 699ea80..863955c 100644
--- a/src/fauxton/app/addons/auth/templates/nav_link_title.html
+++ b/src/fauxton/app/addons/auth/templates/nav_link_title.html
@@ -12,7 +12,7 @@ License for the specific language governing permissions and limitations under
 the License.
 -->
 <% if (admin_party) { %>
-  <a id="user-create-admin" href="#createAdmin"> 
+  <a id="user-create-admin" class="alert_nav" href="#createAdmin"> 
   	<span class="fonticon-user fonticon"></span>
   	Admin Party! [FIX THIS] 
     <small>Everyone is an admin.</small>


[02/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Upgrade password hashes on authentication

We now upgrade user docs to the new PBKDF2 password scheme on successful
authentication if the password hash is still from the old days where we
only used plain SHA-1 for hashing salted passwords.

Closes COUCHDB-1780.


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/34888938
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/34888938
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/34888938

Branch: refs/heads/Query-Options-UI
Commit: 348889380c3b98d7f1a6c8963fc2eb4ee08c1db7
Parents: 45e17e5
Author: Klaus Trainer <kl...@posteo.de>
Authored: Wed Feb 19 23:17:02 2014 +0100
Committer: Klaus Trainer <kl...@apache.org>
Committed: Sun Feb 23 18:59:22 2014 +0100

----------------------------------------------------------------------
 share/www/script/test/users_db_security.js | 61 ++++++++++++++++++++++++-
 src/couchdb/couch_httpd_auth.erl           | 35 ++++++++++----
 2 files changed, 86 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/34888938/share/www/script/test/users_db_security.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/users_db_security.js b/share/www/script/test/users_db_security.js
index 9bf9b8a..2a2bb9d 100644
--- a/share/www/script/test/users_db_security.js
+++ b/share/www/script/test/users_db_security.js
@@ -151,6 +151,63 @@ couchTests.users_db_security = function(debug) {
       TEquals(true, userDoc.derived_key != jchrisDoc.derived_key,
         "should have new derived_key");
 
+      // SHA-1 password hashes are upgraded to PBKDF2 on successful
+      // authentication
+      var rnewsonDoc = {
+        _id: "org.couchdb.user:rnewson",
+        type: "user",
+        name: "rnewson",
+        // password: "plaintext_password",
+        password_sha: "e29dc3aeed5abf43185c33e479f8998558c59474",
+        salt: "24f1e0a87c2e374212bda1073107e8ae",
+        roles: []
+      };
+
+      var password_sha = rnewsonDoc.password_sha,
+        salt = rnewsonDoc.salt,
+        derived_key,
+        iterations;
+
+      usersDb.save(rnewsonDoc);
+      rnewsonDoc = open_as(usersDb, rnewsonDoc._id, "jan");
+      T(!rnewsonDoc.password_scheme);
+      T(!rnewsonDoc.derived_key);
+      T(!rnewsonDoc.iterations);
+
+      // check that we don't upgrade when the password is wrong
+      TEquals("unauthorized", CouchDB.login("rnewson", "wrong_password").error);
+      rnewsonDoc = open_as(usersDb, rnewsonDoc._id, "jan");
+      TEquals(salt, rnewsonDoc.salt);
+      TEquals(password_sha, rnewsonDoc.password_sha);
+      T(!rnewsonDoc.password_scheme);
+      T(!rnewsonDoc.derived_key);
+      T(!rnewsonDoc.iterations);
+
+      TEquals(true, CouchDB.login("rnewson", "plaintext_password").ok);
+      rnewsonDoc = usersDb.open(rnewsonDoc._id);
+      TEquals("pbkdf2", rnewsonDoc.password_scheme);
+      T(rnewsonDoc.salt != salt);
+      T(!rnewsonDoc.password_sha);
+      T(rnewsonDoc.derived_key);
+      T(rnewsonDoc.iterations);
+
+      salt = rnewsonDoc.salt,
+      derived_key = rnewsonDoc.derived_key,
+      iterations = rnewsonDoc.iterations;
+
+      // check that authentication is still working
+      // and everything is staying the same now
+      CouchDB.logout();
+      TEquals(true, CouchDB.login("rnewson", "plaintext_password").ok);
+      rnewsonDoc = usersDb.open(rnewsonDoc._id);
+      TEquals("pbkdf2", rnewsonDoc.password_scheme);
+      TEquals(salt, rnewsonDoc.salt);
+      T(!rnewsonDoc.password_sha);
+      TEquals(derived_key, rnewsonDoc.derived_key);
+      TEquals(iterations, rnewsonDoc.iterations);
+
+      CouchDB.logout();
+
       // user should not be able to read another user's user document
       var fdmananaDoc = {
         _id: "org.couchdb.user:fdmanana",
@@ -209,11 +266,11 @@ couchTests.users_db_security = function(debug) {
 
       // admin should be able to read from any view
       var result = view_as(usersDb, "user_db_auth/test", "jan");
-      TEquals(3, result.total_rows, "should allow access and list two users to admin");
+      TEquals(4, result.total_rows, "should allow access and list four users to admin");
 
       // db admin should be able to read from any view
       var result = view_as(usersDb, "user_db_auth/test", "benoitc");
-      TEquals(3, result.total_rows, "should allow access and list two users to db admin");
+      TEquals(4, result.total_rows, "should allow access and list four users to db admin");
 
 
       // non-admins can't read design docs

http://git-wip-us.apache.org/repos/asf/couchdb/blob/34888938/src/couchdb/couch_httpd_auth.erl
----------------------------------------------------------------------
diff --git a/src/couchdb/couch_httpd_auth.erl b/src/couchdb/couch_httpd_auth.erl
index b8c4e26..08841fb 100644
--- a/src/couchdb/couch_httpd_auth.erl
+++ b/src/couchdb/couch_httpd_auth.erl
@@ -68,11 +68,14 @@ default_authentication_handler(Req) ->
             nil ->
                 throw({unauthorized, <<"Name or password is incorrect.">>});
             UserProps ->
-                case authenticate(?l2b(Pass), UserProps) of
+                UserName = ?l2b(User),
+                Password = ?l2b(Pass),
+                case authenticate(Password, UserProps) of
                     true ->
+                        UserProps2 = maybe_upgrade_password_hash(UserName, Password, UserProps),
                         Req#httpd{user_ctx=#user_ctx{
-                            name=?l2b(User),
-                            roles=couch_util:get_value(<<"roles">>, UserProps, [])
+                            name=UserName,
+                            roles=couch_util:get_value(<<"roles">>, UserProps2, [])
                         }};
                     _Else ->
                         throw({unauthorized, <<"Name or password is incorrect.">>})
@@ -263,15 +266,16 @@ handle_session_req(#httpd{method='POST', mochi_req=MochiReq}=Req) ->
     UserName = ?l2b(couch_util:get_value("name", Form, "")),
     Password = ?l2b(couch_util:get_value("password", Form, "")),
     ?LOG_DEBUG("Attempt Login: ~s",[UserName]),
-    User = case couch_auth_cache:get_user_creds(UserName) of
+    UserProps = case couch_auth_cache:get_user_creds(UserName) of
         nil -> [];
         Result -> Result
     end,
-    UserSalt = couch_util:get_value(<<"salt">>, User, <<>>),
-    case authenticate(Password, User) of
+    case authenticate(Password, UserProps) of
         true ->
+            UserProps2 = maybe_upgrade_password_hash(UserName, Password, UserProps),
             % setup the session cookie
             Secret = ?l2b(ensure_cookie_auth_secret()),
+            UserSalt = couch_util:get_value(<<"salt">>, UserProps2),
             CurrentTime = make_cookie_time(),
             Cookie = cookie_auth_cookie(Req, ?b2l(UserName), <<Secret/binary, UserSalt/binary>>, CurrentTime),
             % TODO document the "next" feature in Futon
@@ -284,8 +288,8 @@ handle_session_req(#httpd{method='POST', mochi_req=MochiReq}=Req) ->
             send_json(Req#httpd{req_body=ReqBody}, Code, Headers,
                 {[
                     {ok, true},
-                    {name, couch_util:get_value(<<"name">>, User, null)},
-                    {roles, couch_util:get_value(<<"roles">>, User, [])}
+                    {name, couch_util:get_value(<<"name">>, UserProps2, null)},
+                    {roles, couch_util:get_value(<<"roles">>, UserProps2, [])}
                 ]});
         _Else ->
             % clear the session
@@ -340,6 +344,21 @@ maybe_value(_Key, undefined, _Fun) -> [];
 maybe_value(Key, Else, Fun) ->
     [{Key, Fun(Else)}].
 
+maybe_upgrade_password_hash(UserName, Password, UserProps) ->
+    case couch_util:get_value(<<"password_scheme">>, UserProps, <<"simple">>) of
+    <<"simple">> ->
+        DbName = ?l2b(couch_config:get("couch_httpd_auth", "authentication_db", "_users")),
+        couch_util:with_db(DbName, fun(UserDb) ->
+            UserProps2 = proplists:delete(<<"password_sha">>, UserProps),
+            UserProps3 = [{<<"password">>, Password} | UserProps2],
+            NewUserDoc = couch_doc:from_json_obj({UserProps3}),
+            {ok, _NewRev} = couch_db:update_doc(UserDb, NewUserDoc, []),
+            couch_auth_cache:get_user_creds(UserName)
+        end);
+    _ ->
+        UserProps
+    end.
+
 authenticate(Pass, UserProps) ->
     UserSalt = couch_util:get_value(<<"salt">>, UserProps, <<>>),
     {PasswordHash, ExpectedHash} =


[11/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Added the "everyone is an admin" & "fix this" note  and move the text around on the page.
Got rid of the extra header


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/48f586f8
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/48f586f8
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/48f586f8

Branch: refs/heads/Query-Options-UI
Commit: 48f586f850a4cc92e938d84bfc98ba33f125861e
Parents: 055219f
Author: suelockwood <de...@apache.org>
Authored: Thu Feb 27 15:33:48 2014 -0500
Committer: suelockwood <de...@apache.org>
Committed: Thu Feb 27 15:33:48 2014 -0500

----------------------------------------------------------------------
 .../app/addons/auth/templates/create_admin.html | 21 ++++++++++----------
 .../addons/auth/templates/nav_link_title.html   |  3 ++-
 src/fauxton/assets/less/fauxton.less            |  4 +++-
 3 files changed, 15 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/48f586f8/src/fauxton/app/addons/auth/templates/create_admin.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/auth/templates/create_admin.html b/src/fauxton/app/addons/auth/templates/create_admin.html
index 4715be5..8f4ed2c 100644
--- a/src/fauxton/app/addons/auth/templates/create_admin.html
+++ b/src/fauxton/app/addons/auth/templates/create_admin.html
@@ -13,21 +13,20 @@ the License.
 -->
 
 <div class="span12">
-  <h2> Add Admin </h2>
+  <p class="help-block">
+  Before a server admin is configured, all clients have admin privileges.
+  This is fine when HTTP access is restricted 
+  to trusted users. <strong>If end-users will be accessing this CouchDB, you must
+    create an admin account to prevent accidental (or malicious) data loss.</strong>
+  </p>
+  <p class="help-block">Server admins can create and destroy databases, install 
+    and update _design documents, run the test suite, and edit all aspects of CouchDB 
+    configuration.
+  </p>
   <form id="create-admin-form">
     <input id="username" type="text" name="name" placeholder= "Username:" size="24">
     <br/>
     <input id="password" type="password" name="password" placeholder= "Password" size="24">
-    <p class="help-block">
-    Before a server admin is configured, all clients have admin privileges.
-    This is fine when HTTP access is restricted 
-    to trusted users. <strong>If end-users will be accessing this CouchDB, you must
-      create an admin account to prevent accidental (or malicious) data loss.</strong>
-    </p>
-    <p class="help-block">Server admins can create and destroy databases, install 
-    and update _design documents, run the test suite, and edit all aspects of CouchDB 
-    configuration.
-    </p>
     <p class="help-block">Non-admin users have read and write access to all databases, which
     are controlled by validation functions. CouchDB can be configured to block all
     access to anonymous users.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/48f586f8/src/fauxton/app/addons/auth/templates/nav_link_title.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/auth/templates/nav_link_title.html b/src/fauxton/app/addons/auth/templates/nav_link_title.html
index b23157e..699ea80 100644
--- a/src/fauxton/app/addons/auth/templates/nav_link_title.html
+++ b/src/fauxton/app/addons/auth/templates/nav_link_title.html
@@ -14,7 +14,8 @@ the License.
 <% if (admin_party) { %>
   <a id="user-create-admin" href="#createAdmin"> 
   	<span class="fonticon-user fonticon"></span>
-  	Admin Party! 
+  	Admin Party! [FIX THIS] 
+    <small>Everyone is an admin.</small>
   </a>
 <% } else if (user) { %>
   <a  href="#changePassword" >

http://git-wip-us.apache.org/repos/asf/couchdb/blob/48f586f8/src/fauxton/assets/less/fauxton.less
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/less/fauxton.less b/src/fauxton/assets/less/fauxton.less
index 82c65bd..229075b 100644
--- a/src/fauxton/assets/less/fauxton.less
+++ b/src/fauxton/assets/less/fauxton.less
@@ -340,7 +340,9 @@ table.databases {
     bottom: 0;
     width: 100%;
   }
-
+  #user-create-admin{
+    font-size: 12px
+  }
   .navbar {
     .brand {
       .box-sizing(content-box);


[07/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Remove editor reference to go to line 2 to stop browsers from crashing on people who have minified index functions.


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/e0b860d0
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/e0b860d0
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/e0b860d0

Branch: refs/heads/Query-Options-UI
Commit: e0b860d0f1e024c1fd691a643dc65a4aa5d302c1
Parents: 17a9763
Author: suelockwood <de...@apache.org>
Authored: Tue Feb 25 14:58:26 2014 -0500
Committer: suelockwood <de...@apache.org>
Committed: Tue Feb 25 14:58:36 2014 -0500

----------------------------------------------------------------------
 src/fauxton/app/addons/fauxton/components.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/e0b860d0/src/fauxton/app/addons/fauxton/components.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/fauxton/components.js b/src/fauxton/app/addons/fauxton/components.js
index 8b21916..39c213d 100644
--- a/src/fauxton/app/addons/fauxton/components.js
+++ b/src/fauxton/app/addons/fauxton/components.js
@@ -284,11 +284,11 @@ function(app, FauxtonAPI, ace, spin) {
     afterRender: function () {
       this.editor = ace.edit(this.editorId);
       this.setHeightToLineCount();
+      this.editor.getSession().setUseWorker(false);
       this.editor.setTheme("ace/theme/" + this.theme);
+
       this.editor.getSession().setMode("ace/mode/" + this.mode);
-      this.editor.getSession().setUseWrapMode(true);
       this.editor.setShowPrintMargin(false);
-      this.editor.gotoLine(2);
       this.addCommands();
 
       if (this.couchJSHINT) {
@@ -329,7 +329,8 @@ function(app, FauxtonAPI, ace, spin) {
     },
 
     getLines: function(){
-      return this.editor.getSession().getDocument().getLength();
+      return 5;
+     // return this.editor.getSession().getDocument().getLength();
     },
 
     addCommands: function () {


[18/22] Fauxton: Improved pagination

Posted by ga...@apache.org.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/test/mocha/chai.js
----------------------------------------------------------------------
diff --git a/src/fauxton/test/mocha/chai.js b/src/fauxton/test/mocha/chai.js
index 2a67f98..9dd7b0a 100644
--- a/src/fauxton/test/mocha/chai.js
+++ b/src/fauxton/test/mocha/chai.js
@@ -27,10 +27,14 @@ function require(path, parent, orig) {
   // perform real require()
   // by invoking the module's
   // registered function
-  if (!module.exports) {
-    module.exports = {};
-    module.client = module.component = true;
-    module.call(this, module.exports, require.relative(resolved), module);
+  if (!module._resolving && !module.exports) {
+    var mod = {};
+    mod.exports = {};
+    mod.client = mod.component = true;
+    module._resolving = true;
+    module.call(this, mod.exports, require.relative(resolved), mod);
+    delete module._resolving;
+    module.exports = mod.exports;
   }
 
   return module.exports;
@@ -309,6 +313,411 @@ AssertionError.prototype.toJSON = function (stack) {
 };
 
 });
+require.register("chaijs-type-detect/lib/type.js", function(exports, require, module){
+/*!
+ * type-detect
+ * Copyright(c) 2013 jake luer <ja...@alogicalparadox.com>
+ * MIT Licensed
+ */
+
+/*!
+ * Primary Exports
+ */
+
+var exports = module.exports = getType;
+
+/*!
+ * Detectable javascript natives
+ */
+
+var natives = {
+    '[object Array]': 'array'
+  , '[object RegExp]': 'regexp'
+  , '[object Function]': 'function'
+  , '[object Arguments]': 'arguments'
+  , '[object Date]': 'date'
+};
+
+/**
+ * ### typeOf (obj)
+ *
+ * Use several different techniques to determine
+ * the type of object being tested.
+ *
+ *
+ * @param {Mixed} object
+ * @return {String} object type
+ * @api public
+ */
+
+function getType (obj) {
+  var str = Object.prototype.toString.call(obj);
+  if (natives[str]) return natives[str];
+  if (obj === null) return 'null';
+  if (obj === undefined) return 'undefined';
+  if (obj === Object(obj)) return 'object';
+  return typeof obj;
+}
+
+exports.Library = Library;
+
+/**
+ * ### Library
+ *
+ * Create a repository for custom type detection.
+ *
+ * ```js
+ * var lib = new type.Library;
+ * ```
+ *
+ */
+
+function Library () {
+  this.tests = {};
+}
+
+/**
+ * #### .of (obj)
+ *
+ * Expose replacement `typeof` detection to the library.
+ *
+ * ```js
+ * if ('string' === lib.of('hello world')) {
+ *   // ...
+ * }
+ * ```
+ *
+ * @param {Mixed} object to test
+ * @return {String} type
+ */
+
+Library.prototype.of = getType;
+
+/**
+ * #### .define (type, test)
+ *
+ * Add a test to for the `.test()` assertion.
+ *
+ * Can be defined as a regular expression:
+ *
+ * ```js
+ * lib.define('int', /^[0-9]+$/);
+ * ```
+ *
+ * ... or as a function:
+ *
+ * ```js
+ * lib.define('bln', function (obj) {
+ *   if ('boolean' === lib.of(obj)) return true;
+ *   var blns = [ 'yes', 'no', 'true', 'false', 1, 0 ];
+ *   if ('string' === lib.of(obj)) obj = obj.toLowerCase();
+ *   return !! ~blns.indexOf(obj);
+ * });
+ * ```
+ *
+ * @param {String} type
+ * @param {RegExp|Function} test
+ * @api public
+ */
+
+Library.prototype.define = function (type, test) {
+  if (arguments.length === 1) return this.tests[type];
+  this.tests[type] = test;
+  return this;
+};
+
+/**
+ * #### .test (obj, test)
+ *
+ * Assert that an object is of type. Will first
+ * check natives, and if that does not pass it will
+ * use the user defined custom tests.
+ *
+ * ```js
+ * assert(lib.test('1', 'int'));
+ * assert(lib.test('yes', 'bln'));
+ * ```
+ *
+ * @param {Mixed} object
+ * @param {String} type
+ * @return {Boolean} result
+ * @api public
+ */
+
+Library.prototype.test = function (obj, type) {
+  if (type === getType(obj)) return true;
+  var test = this.tests[type];
+
+  if (test && 'regexp' === getType(test)) {
+    return test.test(obj);
+  } else if (test && 'function' === getType(test)) {
+    return test(obj);
+  } else {
+    throw new ReferenceError('Type test "' + type + '" not defined or invalid.');
+  }
+};
+
+});
+require.register("chaijs-deep-eql/lib/eql.js", function(exports, require, module){
+/*!
+ * deep-eql
+ * Copyright(c) 2013 Jake Luer <ja...@alogicalparadox.com>
+ * MIT Licensed
+ */
+
+/*!
+ * Module dependencies
+ */
+
+var type = require('type-detect');
+
+/*!
+ * Buffer.isBuffer browser shim
+ */
+
+var Buffer;
+try { Buffer = require('buffer').Buffer; }
+catch(ex) {
+  Buffer = {};
+  Buffer.isBuffer = function() { return false; }
+}
+
+/*!
+ * Primary Export
+ */
+
+module.exports = deepEqual;
+
+/**
+ * Assert super-strict (egal) equality between
+ * two objects of any type.
+ *
+ * @param {Mixed} a
+ * @param {Mixed} b
+ * @param {Array} memoised (optional)
+ * @return {Boolean} equal match
+ */
+
+function deepEqual(a, b, m) {
+  if (sameValue(a, b)) {
+    return true;
+  } else if ('date' === type(a)) {
+    return dateEqual(a, b);
+  } else if ('regexp' === type(a)) {
+    return regexpEqual(a, b);
+  } else if (Buffer.isBuffer(a)) {
+    return bufferEqual(a, b);
+  } else if ('arguments' === type(a)) {
+    return argumentsEqual(a, b, m);
+  } else if (!typeEqual(a, b)) {
+    return false;
+  } else if (('object' !== type(a) && 'object' !== type(b))
+  && ('array' !== type(a) && 'array' !== type(b))) {
+    return sameValue(a, b);
+  } else {
+    return objectEqual(a, b, m);
+  }
+}
+
+/*!
+ * Strict (egal) equality test. Ensures that NaN always
+ * equals NaN and `-0` does not equal `+0`.
+ *
+ * @param {Mixed} a
+ * @param {Mixed} b
+ * @return {Boolean} equal match
+ */
+
+function sameValue(a, b) {
+  if (a === b) return a !== 0 || 1 / a === 1 / b;
+  return a !== a && b !== b;
+}
+
+/*!
+ * Compare the types of two given objects and
+ * return if they are equal. Note that an Array
+ * has a type of `array` (not `object`) and arguments
+ * have a type of `arguments` (not `array`/`object`).
+ *
+ * @param {Mixed} a
+ * @param {Mixed} b
+ * @return {Boolean} result
+ */
+
+function typeEqual(a, b) {
+  return type(a) === type(b);
+}
+
+/*!
+ * Compare two Date objects by asserting that
+ * the time values are equal using `saveValue`.
+ *
+ * @param {Date} a
+ * @param {Date} b
+ * @return {Boolean} result
+ */
+
+function dateEqual(a, b) {
+  if ('date' !== type(b)) return false;
+  return sameValue(a.getTime(), b.getTime());
+}
+
+/*!
+ * Compare two regular expressions by converting them
+ * to string and checking for `sameValue`.
+ *
+ * @param {RegExp} a
+ * @param {RegExp} b
+ * @return {Boolean} result
+ */
+
+function regexpEqual(a, b) {
+  if ('regexp' !== type(b)) return false;
+  return sameValue(a.toString(), b.toString());
+}
+
+/*!
+ * Assert deep equality of two `arguments` objects.
+ * Unfortunately, these must be sliced to arrays
+ * prior to test to ensure no bad behavior.
+ *
+ * @param {Arguments} a
+ * @param {Arguments} b
+ * @param {Array} memoize (optional)
+ * @return {Boolean} result
+ */
+
+function argumentsEqual(a, b, m) {
+  if ('arguments' !== type(b)) return false;
+  a = [].slice.call(a);
+  b = [].slice.call(b);
+  return deepEqual(a, b, m);
+}
+
+/*!
+ * Get enumerable properties of a given object.
+ *
+ * @param {Object} a
+ * @return {Array} property names
+ */
+
+function enumerable(a) {
+  var res = [];
+  for (var key in a) res.push(key);
+  return res;
+}
+
+/*!
+ * Simple equality for flat iterable objects
+ * such as Arrays or Node.js buffers.
+ *
+ * @param {Iterable} a
+ * @param {Iterable} b
+ * @return {Boolean} result
+ */
+
+function iterableEqual(a, b) {
+  if (a.length !==  b.length) return false;
+
+  var i = 0;
+  var match = true;
+
+  for (; i < a.length; i++) {
+    if (a[i] !== b[i]) {
+      match = false;
+      break;
+    }
+  }
+
+  return match;
+}
+
+/*!
+ * Extension to `iterableEqual` specifically
+ * for Node.js Buffers.
+ *
+ * @param {Buffer} a
+ * @param {Mixed} b
+ * @return {Boolean} result
+ */
+
+function bufferEqual(a, b) {
+  if (!Buffer.isBuffer(b)) return false;
+  return iterableEqual(a, b);
+}
+
+/*!
+ * Block for `objectEqual` ensuring non-existing
+ * values don't get in.
+ *
+ * @param {Mixed} object
+ * @return {Boolean} result
+ */
+
+function isValue(a) {
+  return a !== null && a !== undefined;
+}
+
+/*!
+ * Recursively check the equality of two objects.
+ * Once basic sameness has been established it will
+ * defer to `deepEqual` for each enumerable key
+ * in the object.
+ *
+ * @param {Mixed} a
+ * @param {Mixed} b
+ * @return {Boolean} result
+ */
+
+function objectEqual(a, b, m) {
+  if (!isValue(a) || !isValue(b)) {
+    return false;
+  }
+
+  if (a.prototype !== b.prototype) {
+    return false;
+  }
+
+  var i;
+  if (m) {
+    for (i = 0; i < m.length; i++) {
+      if ((m[i][0] === a && m[i][1] === b)
+      ||  (m[i][0] === b && m[i][1] === a)) {
+        return true;
+      }
+    }
+  } else {
+    m = [];
+  }
+
+  try {
+    var ka = enumerable(a);
+    var kb = enumerable(b);
+  } catch (ex) {
+    return false;
+  }
+
+  ka.sort();
+  kb.sort();
+
+  if (!iterableEqual(ka, kb)) {
+    return false;
+  }
+
+  m.push([ a, b ]);
+
+  var key;
+  for (i = ka.length - 1; i >= 0; i--) {
+    key = ka[i];
+    if (!deepEqual(a[key], b[key], m)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+});
 require.register("chai/index.js", function(exports, require, module){
 module.exports = require('./lib/chai');
 
@@ -316,7 +725,7 @@ module.exports = require('./lib/chai');
 require.register("chai/lib/chai.js", function(exports, require, module){
 /*!
  * chai
- * Copyright(c) 2011-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2011-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -327,7 +736,7 @@ var used = []
  * Chai version
  */
 
-exports.version = '1.7.2';
+exports.version = '1.8.1';
 
 /*!
  * Assertion Error
@@ -400,7 +809,7 @@ require.register("chai/lib/chai/assertion.js", function(exports, require, module
 /*!
  * chai
  * http://chaijs.com
- * Copyright(c) 2011-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2011-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -480,6 +889,10 @@ module.exports = function (_chai, util) {
     util.overwriteMethod(this.prototype, name, fn);
   };
 
+  Assertion.overwriteChainableMethod = function (name, fn, chainingBehavior) {
+    util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior);
+  };
+
   /*!
    * ### .assert(expression, message, negateMessage, expected, actual)
    *
@@ -533,7 +946,7 @@ require.register("chai/lib/chai/core/assertions.js", function(exports, require,
 /*!
  * chai
  * http://chaijs.com
- * Copyright(c) 2011-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2011-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -545,7 +958,7 @@ module.exports = function (chai, _) {
   /**
    * ### Language Chains
    *
-   * The following are provide as chainable getters to
+   * The following are provided as chainable getters to
    * improve the readability of your assertions. They
    * do not provide an testing capability unless they
    * have been overwritten by a plugin.
@@ -558,6 +971,7 @@ module.exports = function (chai, _) {
    * - is
    * - that
    * - and
+   * - has
    * - have
    * - with
    * - at
@@ -569,7 +983,7 @@ module.exports = function (chai, _) {
    */
 
   [ 'to', 'be', 'been'
-  , 'is', 'and', 'have'
+  , 'is', 'and', 'has', 'have'
   , 'with', 'that', 'at'
   , 'of', 'same' ].forEach(function (chain) {
     Assertion.addProperty(chain, function () {
@@ -677,9 +1091,21 @@ module.exports = function (chai, _) {
 
   function include (val, msg) {
     if (msg) flag(this, 'message', msg);
-    var obj = flag(this, 'object')
+    var obj = flag(this, 'object');
+
+    if (_.type(val) === 'object') {
+      if (!flag(this, 'negate')) {
+        for (var k in val) new Assertion(obj).property(k, val[k]);
+        return;
+      }
+      var subset = {}
+      for (var k in val) subset[k] = obj[k]
+      var expected = _.eql(subset, val);
+    } else {
+      var expected = obj && ~obj.indexOf(val)
+    }
     this.assert(
-        ~obj.indexOf(val)
+        expected
       , 'expected #{this} to include ' + _.inspect(val)
       , 'expected #{this} to not include ' + _.inspect(val));
   }
@@ -776,8 +1202,8 @@ module.exports = function (chai, _) {
    *
    * Asserts that the target is `undefined`.
    *
-   *      expect(undefined).to.be.undefined;
-   *      expect(null).to.not.be.undefined;
+   *     expect(undefined).to.be.undefined;
+   *     expect(null).to.not.be.undefined;
    *
    * @name undefined
    * @api public
@@ -1534,6 +1960,7 @@ module.exports = function (chai, _) {
    * @param {String|RegExp} expected error message
    * @param {String} message _optional_
    * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types
+   * @returns error for chaining (null if no error)
    * @api public
    */
 
@@ -1558,7 +1985,10 @@ module.exports = function (chai, _) {
       constructor = null;
       errMsg = null;
     } else if (typeof constructor === 'function') {
-      name = (new constructor()).name;
+      name = constructor.prototype.name || constructor.name;
+      if (name === 'Error' && constructor !== Error) {
+        name = (new constructor()).name;
+      }
     } else {
       constructor = null;
     }
@@ -1572,12 +2002,14 @@ module.exports = function (chai, _) {
             err === desiredError
           , 'expected #{this} to throw #{exp} but #{act} was thrown'
           , 'expected #{this} to not throw #{exp}'
-          , desiredError
-          , err
+          , (desiredError instanceof Error ? desiredError.toString() : desiredError)
+          , (err instanceof Error ? err.toString() : err)
         );
 
+        flag(this, 'object', err);
         return this;
       }
+
       // next, check constructor
       if (constructor) {
         this.assert(
@@ -1585,11 +2017,15 @@ module.exports = function (chai, _) {
           , 'expected #{this} to throw #{exp} but #{act} was thrown'
           , 'expected #{this} to not throw #{exp} but #{act} was thrown'
           , name
-          , err
+          , (err instanceof Error ? err.toString() : err)
         );
 
-        if (!errMsg) return this;
+        if (!errMsg) {
+          flag(this, 'object', err);
+          return this;
+        }
       }
+
       // next, check message
       var message = 'object' === _.type(err) && "message" in err
         ? err.message
@@ -1604,6 +2040,7 @@ module.exports = function (chai, _) {
           , message
         );
 
+        flag(this, 'object', err);
         return this;
       } else if ((message != null) && errMsg && 'string' === typeof errMsg) {
         this.assert(
@@ -1614,6 +2051,7 @@ module.exports = function (chai, _) {
           , message
         );
 
+        flag(this, 'object', err);
         return this;
       } else {
         thrown = true;
@@ -1636,9 +2074,11 @@ module.exports = function (chai, _) {
         thrown === true
       , 'expected #{this} to throw ' + expectedThrown + actuallyGot
       , 'expected #{this} to not throw ' + expectedThrown + actuallyGot
-      , desiredError
-      , thrownError
+      , (desiredError instanceof Error ? desiredError.toString() : desiredError)
+      , (thrownError instanceof Error ? thrownError.toString() : thrownError)
     );
+
+    flag(this, 'object', thrownError);
   };
 
   Assertion.addMethod('throw', assertThrows);
@@ -1657,8 +2097,8 @@ module.exports = function (chai, _) {
    * To check if a constructor will respond to a static function,
    * set the `itself` flag.
    *
-   *    Klass.baz = function(){};
-   *    expect(Klass).itself.to.respondTo('baz');
+   *     Klass.baz = function(){};
+   *     expect(Klass).itself.to.respondTo('baz');
    *
    * @name respondTo
    * @param {String} method
@@ -1686,12 +2126,12 @@ module.exports = function (chai, _) {
    *
    * Sets the `itself` flag, later used by the `respondTo` assertion.
    *
-   *    function Foo() {}
-   *    Foo.bar = function() {}
-   *    Foo.prototype.baz = function() {}
+   *     function Foo() {}
+   *     Foo.bar = function() {}
+   *     Foo.prototype.baz = function() {}
    *
-   *    expect(Foo).itself.to.respondTo('bar');
-   *    expect(Foo).itself.not.to.respondTo('baz');
+   *     expect(Foo).itself.to.respondTo('bar');
+   *     expect(Foo).itself.not.to.respondTo('baz');
    *
    * @name itself
    * @api public
@@ -1805,7 +2245,7 @@ module.exports = function (chai, _) {
 require.register("chai/lib/chai/interface/assert.js", function(exports, require, module){
 /*!
  * chai
- * Copyright(c) 2011-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2011-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -1860,13 +2300,12 @@ module.exports = function (chai, util) {
    */
 
   assert.fail = function (actual, expected, message, operator) {
-    throw new chai.AssertionError({
+    message = message || 'assert.fail()';
+    throw new chai.AssertionError(message, {
         actual: actual
       , expected: expected
-      , message: message
       , operator: operator
-      , stackStartFunction: assert.fail
-    });
+    }, assert.fail);
   };
 
   /**
@@ -2462,19 +2901,7 @@ module.exports = function (chai, util) {
    */
 
   assert.include = function (exp, inc, msg) {
-    var obj = new Assertion(exp, msg);
-
-    if (Array.isArray(exp)) {
-      obj.to.include(inc);
-    } else if ('string' === typeof exp) {
-      obj.to.contain.string(inc);
-    } else {
-      throw new chai.AssertionError(
-          'expected an array or string'
-        , null
-        , assert.include
-      );
-    }
+    new Assertion(exp, msg).include(inc);
   };
 
   /**
@@ -2494,19 +2921,7 @@ module.exports = function (chai, util) {
    */
 
   assert.notInclude = function (exp, inc, msg) {
-    var obj = new Assertion(exp, msg);
-
-    if (Array.isArray(exp)) {
-      obj.to.not.include(inc);
-    } else if ('string' === typeof exp) {
-      obj.to.not.contain.string(inc);
-    } else {
-      throw new chai.AssertionError(
-          'expected an array or string'
-        , null
-        , assert.notInclude
-      );
-    }
+    new Assertion(exp, msg).not.include(inc);
   };
 
   /**
@@ -2750,7 +3165,8 @@ module.exports = function (chai, util) {
       errt = null;
     }
 
-    new Assertion(fn, msg).to.Throw(errt, errs);
+    var assertErr = new Assertion(fn, msg).to.Throw(errt, errs);
+    return flag(assertErr, 'object');
   };
 
   /**
@@ -2888,7 +3304,7 @@ module.exports = function (chai, util) {
 require.register("chai/lib/chai/interface/expect.js", function(exports, require, module){
 /*!
  * chai
- * Copyright(c) 2011-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2011-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -2903,7 +3319,7 @@ module.exports = function (chai, util) {
 require.register("chai/lib/chai/interface/should.js", function(exports, require, module){
 /*!
  * chai
- * Copyright(c) 2011-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2011-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -2982,7 +3398,7 @@ module.exports = function (chai, util) {
 require.register("chai/lib/chai/utils/addChainableMethod.js", function(exports, require, module){
 /*!
  * Chai - addChainingMethod utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -3037,15 +3453,27 @@ var call  = Function.prototype.call,
  */
 
 module.exports = function (ctx, name, method, chainingBehavior) {
-  if (typeof chainingBehavior !== 'function')
+  if (typeof chainingBehavior !== 'function') {
     chainingBehavior = function () { };
+  }
+
+  var chainableBehavior = {
+      method: method
+    , chainingBehavior: chainingBehavior
+  };
+
+  // save the methods so we can overwrite them later, if we need to.
+  if (!ctx.__methods) {
+    ctx.__methods = {};
+  }
+  ctx.__methods[name] = chainableBehavior;
 
   Object.defineProperty(ctx, name,
     { get: function () {
-        chainingBehavior.call(this);
+        chainableBehavior.chainingBehavior.call(this);
 
         var assert = function () {
-          var result = method.apply(this, arguments);
+          var result = chainableBehavior.method.apply(this, arguments);
           return result === undefined ? this : result;
         };
 
@@ -3079,7 +3507,7 @@ module.exports = function (ctx, name, method, chainingBehavior) {
 require.register("chai/lib/chai/utils/addMethod.js", function(exports, require, module){
 /*!
  * Chai - addMethod utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -3119,7 +3547,7 @@ module.exports = function (ctx, name, method) {
 require.register("chai/lib/chai/utils/addProperty.js", function(exports, require, module){
 /*!
  * Chai - addProperty utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -3159,142 +3587,10 @@ module.exports = function (ctx, name, getter) {
 };
 
 });
-require.register("chai/lib/chai/utils/eql.js", function(exports, require, module){
-// This is (almost) directly from Node.js assert
-// https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/assert.js
-
-module.exports = _deepEqual;
-
-var getEnumerableProperties = require('./getEnumerableProperties');
-
-// for the browser
-var Buffer;
-try {
-  Buffer = require('buffer').Buffer;
-} catch (ex) {
-  Buffer = {
-    isBuffer: function () { return false; }
-  };
-}
-
-function _deepEqual(actual, expected, memos) {
-
-  // 7.1. All identical values are equivalent, as determined by ===.
-  if (actual === expected) {
-    return true;
-
-  } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
-    if (actual.length != expected.length) return false;
-
-    for (var i = 0; i < actual.length; i++) {
-      if (actual[i] !== expected[i]) return false;
-    }
-
-    return true;
-
-  // 7.2. If the expected value is a Date object, the actual value is
-  // equivalent if it is also a Date object that refers to the same time.
-  } else if (expected instanceof Date) {
-    if (!(actual instanceof Date)) return false;
-    return actual.getTime() === expected.getTime();
-
-  // 7.3. Other pairs that do not both pass typeof value == 'object',
-  // equivalence is determined by ==.
-  } else if (typeof actual != 'object' && typeof expected != 'object') {
-    return actual === expected;
-
-  } else if (expected instanceof RegExp) {
-    if (!(actual instanceof RegExp)) return false;
-    return actual.toString() === expected.toString();
-
-  // 7.4. For all other Object pairs, including Array objects, equivalence is
-  // determined by having the same number of owned properties (as verified
-  // with Object.prototype.hasOwnProperty.call), the same set of keys
-  // (although not necessarily the same order), equivalent values for every
-  // corresponding key, and an identical 'prototype' property. Note: this
-  // accounts for both named and indexed properties on Arrays.
-  } else {
-    return objEquiv(actual, expected, memos);
-  }
-}
-
-function isUndefinedOrNull(value) {
-  return value === null || value === undefined;
-}
-
-function isArguments(object) {
-  return Object.prototype.toString.call(object) == '[object Arguments]';
-}
-
-function objEquiv(a, b, memos) {
-  if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
-    return false;
-
-  // an identical 'prototype' property.
-  if (a.prototype !== b.prototype) return false;
-
-  // check if we have already compared a and b
-  var i;
-  if (memos) {
-    for(i = 0; i < memos.length; i++) {
-      if ((memos[i][0] === a && memos[i][1] === b) ||
-          (memos[i][0] === b && memos[i][1] === a))
-        return true;
-    }
-  } else {
-    memos = [];
-  }
-
-  //~~~I've managed to break Object.keys through screwy arguments passing.
-  //   Converting to array solves the problem.
-  if (isArguments(a)) {
-    if (!isArguments(b)) {
-      return false;
-    }
-    a = pSlice.call(a);
-    b = pSlice.call(b);
-    return _deepEqual(a, b, memos);
-  }
-  try {
-    var ka = getEnumerableProperties(a),
-        kb = getEnumerableProperties(b),
-        key;
-  } catch (e) {//happens when one is a string literal and the other isn't
-    return false;
-  }
-
-  // having the same number of owned properties (keys incorporates
-  // hasOwnProperty)
-  if (ka.length != kb.length)
-    return false;
-
-  //the same set of keys (although not necessarily the same order),
-  ka.sort();
-  kb.sort();
-  //~~~cheap key test
-  for (i = ka.length - 1; i >= 0; i--) {
-    if (ka[i] != kb[i])
-      return false;
-  }
-
-  // remember objects we have compared to guard against circular references
-  memos.push([ a, b ]);
-
-  //equivalent values for every corresponding key, and
-  //~~~possibly expensive deep test
-  for (i = ka.length - 1; i >= 0; i--) {
-    key = ka[i];
-    if (!_deepEqual(a[key], b[key], memos)) return false;
-  }
-
-  return true;
-}
-
-});
 require.register("chai/lib/chai/utils/flag.js", function(exports, require, module){
 /*!
  * Chai - flag utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -3329,7 +3625,7 @@ module.exports = function (obj, key, value) {
 require.register("chai/lib/chai/utils/getActual.js", function(exports, require, module){
 /*!
  * Chai - getActual utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -3351,7 +3647,7 @@ module.exports = function (obj, args) {
 require.register("chai/lib/chai/utils/getEnumerableProperties.js", function(exports, require, module){
 /*!
  * Chai - getEnumerableProperties utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -3379,7 +3675,7 @@ module.exports = function getEnumerableProperties(object) {
 require.register("chai/lib/chai/utils/getMessage.js", function(exports, require, module){
 /*!
  * Chai - message composition utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -3431,7 +3727,7 @@ module.exports = function (obj, args) {
 require.register("chai/lib/chai/utils/getName.js", function(exports, require, module){
 /*!
  * Chai - getName utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -3454,7 +3750,7 @@ module.exports = function (func) {
 require.register("chai/lib/chai/utils/getPathValue.js", function(exports, require, module){
 /*!
  * Chai - getPathValue utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * @see https://github.com/logicalparadox/filtr
  * MIT Licensed
  */
@@ -3559,7 +3855,7 @@ function _getPathValue (parsed, obj) {
 require.register("chai/lib/chai/utils/getProperties.js", function(exports, require, module){
 /*!
  * Chai - getProperties utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -3659,7 +3955,7 @@ exports.transferFlags = require('./transferFlags');
  * Deep equal utility
  */
 
-exports.eql = require('./eql');
+exports.eql = require('deep-eql');
 
 /*!
  * Deep path value
@@ -3703,6 +3999,12 @@ exports.overwriteMethod = require('./overwriteMethod');
 
 exports.addChainableMethod = require('./addChainableMethod');
 
+/*!
+ * Overwrite chainable method
+ */
+
+exports.overwriteChainableMethod = require('./overwriteChainableMethod');
+
 
 });
 require.register("chai/lib/chai/utils/inspect.js", function(exports, require, module){
@@ -4031,7 +4333,7 @@ function objectToString(o) {
 require.register("chai/lib/chai/utils/objDisplay.js", function(exports, require, module){
 /*!
  * Chai - flag utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -4082,7 +4384,7 @@ module.exports = function (obj) {
 require.register("chai/lib/chai/utils/overwriteMethod.js", function(exports, require, module){
 /*!
  * Chai - overwriteMethod utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -4136,7 +4438,7 @@ module.exports = function (ctx, name, method) {
 require.register("chai/lib/chai/utils/overwriteProperty.js", function(exports, require, module){
 /*!
  * Chai - overwriteProperty utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -4190,10 +4492,66 @@ module.exports = function (ctx, name, getter) {
 };
 
 });
+require.register("chai/lib/chai/utils/overwriteChainableMethod.js", function(exports, require, module){
+/*!
+ * Chai - overwriteChainableMethod utility
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
+ * MIT Licensed
+ */
+
+/**
+ * ### overwriteChainableMethod (ctx, name, fn)
+ *
+ * Overwites an already existing chainable method
+ * and provides access to the previous function or
+ * property.  Must return functions to be used for
+ * name.
+ *
+ *     utils.overwriteChainableMethod(chai.Assertion.prototype, 'length',
+ *       function (_super) {
+ *       }
+ *     , function (_super) {
+ *       }
+ *     );
+ *
+ * Can also be accessed directly from `chai.Assertion`.
+ *
+ *     chai.Assertion.overwriteChainableMethod('foo', fn, fn);
+ *
+ * Then can be used as any other assertion.
+ *
+ *     expect(myFoo).to.have.length(3);
+ *     expect(myFoo).to.have.length.above(3);
+ *
+ * @param {Object} ctx object whose method / property is to be overwritten
+ * @param {String} name of method / property to overwrite
+ * @param {Function} method function that returns a function to be used for name
+ * @param {Function} chainingBehavior function that returns a function to be used for property
+ * @name overwriteChainableMethod
+ * @api public
+ */
+
+module.exports = function (ctx, name, method, chainingBehavior) {
+  var chainableBehavior = ctx.__methods[name];
+
+  var _chainingBehavior = chainableBehavior.chainingBehavior;
+  chainableBehavior.chainingBehavior = function () {
+    var result = chainingBehavior(_chainingBehavior).call(this);
+    return result === undefined ? this : result;
+  };
+
+  var _method = chainableBehavior.method;
+  chainableBehavior.method = function () {
+    var result = method(_method).apply(this, arguments);
+    return result === undefined ? this : result;
+  };
+};
+
+});
 require.register("chai/lib/chai/utils/test.js", function(exports, require, module){
 /*!
  * Chai - test utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -4222,7 +4580,7 @@ module.exports = function (obj, args) {
 require.register("chai/lib/chai/utils/transferFlags.js", function(exports, require, module){
 /*!
  * Chai - transferFlags utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -4269,7 +4627,7 @@ module.exports = function (assertion, object, includeAll) {
 require.register("chai/lib/chai/utils/type.js", function(exports, require, module){
 /*!
  * Chai - type utility
- * Copyright(c) 2012-2013 Jake Luer <ja...@alogicalparadox.com>
+ * Copyright(c) 2012-2014 Jake Luer <ja...@alogicalparadox.com>
  * MIT Licensed
  */
 
@@ -4314,17 +4672,25 @@ module.exports = function (obj) {
 };
 
 });
+
+
+
+
 require.alias("chaijs-assertion-error/index.js", "chai/deps/assertion-error/index.js");
 require.alias("chaijs-assertion-error/index.js", "chai/deps/assertion-error/index.js");
 require.alias("chaijs-assertion-error/index.js", "assertion-error/index.js");
 require.alias("chaijs-assertion-error/index.js", "chaijs-assertion-error/index.js");
-
-require.alias("chai/index.js", "chai/index.js");
-
-if (typeof exports == "object") {
+require.alias("chaijs-deep-eql/lib/eql.js", "chai/deps/deep-eql/lib/eql.js");
+require.alias("chaijs-deep-eql/lib/eql.js", "chai/deps/deep-eql/index.js");
+require.alias("chaijs-deep-eql/lib/eql.js", "deep-eql/index.js");
+require.alias("chaijs-type-detect/lib/type.js", "chaijs-deep-eql/deps/type-detect/lib/type.js");
+require.alias("chaijs-type-detect/lib/type.js", "chaijs-deep-eql/deps/type-detect/index.js");
+require.alias("chaijs-type-detect/lib/type.js", "chaijs-type-detect/index.js");
+require.alias("chaijs-deep-eql/lib/eql.js", "chaijs-deep-eql/index.js");
+require.alias("chai/index.js", "chai/index.js");if (typeof exports == "object") {
   module.exports = require("chai");
 } else if (typeof define == "function" && define.amd) {
-  define(function(){ return require("chai"); });
+  define([], function(){ return require("chai"); });
 } else {
   this["chai"] = require("chai");
 }})();

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4e60f0b7/src/fauxton/test/test.config.underscore
----------------------------------------------------------------------
diff --git a/src/fauxton/test/test.config.underscore b/src/fauxton/test/test.config.underscore
index 5cebe78..95494a4 100644
--- a/src/fauxton/test/test.config.underscore
+++ b/src/fauxton/test/test.config.underscore
@@ -7,7 +7,11 @@ require.config(
 require([
         "app",
         <% _.each(testFiles, function (test) {%>
+           <% if (test[0] === '.') { %>
            '../<%= test %>',
+           <% } else { %>
+           '<%= test %>',
+           <% }  %>
         <% }) %>
 ], function() {
   if (window.mochaPhantomJS) { mochaPhantomJS.run(); }


[03/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Merge branch '1780-upgrade-password-hashes-on-authentication'


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/8c867577
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/8c867577
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/8c867577

Branch: refs/heads/Query-Options-UI
Commit: 8c867577cea2de7410eec33aa6358d1f20b5486d
Parents: 08cf09f 3488893
Author: Klaus Trainer <kl...@apache.org>
Authored: Sun Feb 23 18:59:35 2014 +0100
Committer: Klaus Trainer <kl...@apache.org>
Committed: Sun Feb 23 18:59:35 2014 +0100

----------------------------------------------------------------------
 share/www/script/couch_test_runner.js      |  4 +-
 share/www/script/test/auth_cache.js        | 12 +----
 share/www/script/test/cookie_auth.js       | 11 ++---
 share/www/script/test/users_db_security.js | 61 ++++++++++++++++++++++++-
 src/couchdb/couch_httpd_auth.erl           | 35 ++++++++++----
 5 files changed, 94 insertions(+), 29 deletions(-)
----------------------------------------------------------------------



[22/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Fix linting issue


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/dd98953e
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/dd98953e
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/dd98953e

Branch: refs/heads/Query-Options-UI
Commit: dd98953ecc8ee4e08ab30beb471df0701b2961d5
Parents: ed094a7
Author: Garren Smith <ga...@gmail.com>
Authored: Thu Mar 6 12:21:20 2014 +0200
Committer: Garren Smith <ga...@gmail.com>
Committed: Thu Mar 6 12:35:38 2014 +0200

----------------------------------------------------------------------
 src/fauxton/app/addons/documents/views.js | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/dd98953e/src/fauxton/app/addons/documents/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/views.js b/src/fauxton/app/addons/documents/views.js
index e57071a..444fcef 100644
--- a/src/fauxton/app/addons/documents/views.js
+++ b/src/fauxton/app/addons/documents/views.js
@@ -1177,13 +1177,8 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
 
       return {params: params, errorParams: errorParams};
 
-
-
-
-
-
       // Ignore params without a value
-      _.map($form.serializeArray(), function(param) {
+      /*_.map($form.serializeArray(), function(param) {
         if (param.value){
           if (param.name === "keys"){
             var keys = getKeys(param.value);
@@ -1192,7 +1187,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
             data[param.name] = param.value;
           }
         }
-      });
+      });(*/
 
     },
 


[13/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Added comparitor to the configuration collection for alphabetical sorting


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/929b3a08
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/929b3a08
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/929b3a08

Branch: refs/heads/Query-Options-UI
Commit: 929b3a08776674742353aab20d5f4adb331dd2a7
Parents: 0df722e
Author: suelockwood <de...@apache.org>
Authored: Thu Feb 27 16:15:51 2014 -0500
Committer: suelockwood <de...@apache.org>
Committed: Fri Feb 28 12:42:49 2014 -0500

----------------------------------------------------------------------
 src/fauxton/app/addons/config/resources.js | 5 +++++
 1 file changed, 5 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/929b3a08/src/fauxton/app/addons/config/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/config/resources.js b/src/fauxton/app/addons/config/resources.js
index 14d2474..227e80d 100644
--- a/src/fauxton/app/addons/config/resources.js
+++ b/src/fauxton/app/addons/config/resources.js
@@ -51,6 +51,11 @@ function (app, FauxtonAPI) {
   Config.Collection = Backbone.Collection.extend({
     model: Config.Model,
     documentation: "config",
+    comparator: function (OptionModel) {
+      if (OptionModel.get("section")) {
+        return OptionModel.get("section");
+      }
+    },
     url: function () {
       return app.host + '/_config';
     },


[08/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Accidentally committed debugging code. Return 5 ... (thanks kxepal!)


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/b4b6fe15
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/b4b6fe15
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/b4b6fe15

Branch: refs/heads/Query-Options-UI
Commit: b4b6fe15366c9b5ffa535df12c3060554d2af960
Parents: e0b860d
Author: suelockwood <de...@apache.org>
Authored: Tue Feb 25 15:05:07 2014 -0500
Committer: suelockwood <de...@apache.org>
Committed: Tue Feb 25 15:05:07 2014 -0500

----------------------------------------------------------------------
 src/fauxton/app/addons/fauxton/components.js | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/b4b6fe15/src/fauxton/app/addons/fauxton/components.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/fauxton/components.js b/src/fauxton/app/addons/fauxton/components.js
index 39c213d..71d78b1 100644
--- a/src/fauxton/app/addons/fauxton/components.js
+++ b/src/fauxton/app/addons/fauxton/components.js
@@ -329,8 +329,7 @@ function(app, FauxtonAPI, ace, spin) {
     },
 
     getLines: function(){
-      return 5;
-     // return this.editor.getSession().getDocument().getLength();
+     return this.editor.getSession().getDocument().getLength();
     },
 
     addCommands: function () {


[05/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
```validate_doc_update``` moved from views to root

Signed-off-by: Alexander Shorin <kx...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/8de6b6e1
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/8de6b6e1
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/8de6b6e1

Branch: refs/heads/Query-Options-UI
Commit: 8de6b6e1c36212dd6615633cd1fddae430da9775
Parents: 358901a
Author: Anthony Ananich <an...@inpun.com>
Authored: Tue Feb 25 18:17:44 2014 +0200
Committer: Alexander Shorin <kx...@apache.org>
Committed: Tue Feb 25 20:22:20 2014 +0400

----------------------------------------------------------------------
 share/doc/src/query-server/javascript.rst | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/8de6b6e1/share/doc/src/query-server/javascript.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/query-server/javascript.rst b/share/doc/src/query-server/javascript.rst
index 16590d6..386f9c5 100644
--- a/share/doc/src/query-server/javascript.rst
+++ b/share/doc/src/query-server/javascript.rst
@@ -273,12 +273,12 @@ The CommonJS module can be added to a design document, like so:
        "views": {
           "lib": {
              "security": "function user_context(userctx, secobj) { ... }"
-          },
-          "validate_doc_update": "function(newdoc, olddoc, userctx, secobj) {
-            user = require('lib/security').user(userctx, secobj);
-            return user.is_admin();
-          }"
+          }
        },
+       "validate_doc_update": "function(newdoc, olddoc, userctx, secobj) {
+          user = require('lib/security').user(userctx, secobj);
+          return user.is_admin();
+       }"
        "_id": "_design/test"
     }
 


[10/22] couchdb commit: updated refs/heads/Query-Options-UI to dd98953

Posted by ga...@apache.org.
Remove caching from design docs, so that edit revisions are updated


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/055219f1
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/055219f1
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/055219f1

Branch: refs/heads/Query-Options-UI
Commit: 055219f106b271dc17f0e469fb967489f4ef962e
Parents: 5989bb3
Author: suelockwood <de...@apache.org>
Authored: Wed Feb 26 17:00:07 2014 -0500
Committer: suelockwood <de...@apache.org>
Committed: Wed Feb 26 17:00:07 2014 -0500

----------------------------------------------------------------------
 src/fauxton/app/addons/documents/routes.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/055219f1/src/fauxton/app/addons/documents/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/documents/routes.js b/src/fauxton/app/addons/documents/routes.js
index be9ce2f..1510485 100644
--- a/src/fauxton/app/addons/documents/routes.js
+++ b/src/fauxton/app/addons/documents/routes.js
@@ -182,7 +182,7 @@ function(app, FauxtonAPI, Documents, Databases) {
     },
 
     establish: function () {
-      return this.data.designDocs.fetchOnce();
+      return this.data.designDocs.fetch();
     },
 
     allDocs: function(databaseName, options) {