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 2013/06/04 15:46:58 UTC

[2/4] git commit: updated refs/heads/master to df23be9

Create/edit and query views.

It is now also possible to preview views with pouch. Issue #1806


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

Branch: refs/heads/master
Commit: 90e4da6ce6bda6cb72b325d2702fe9832e20ff59
Parents: eb364ff
Author: Garren Smith <ga...@gmail.com>
Authored: Thu May 16 08:34:54 2013 +0200
Committer: Garren Smith <ga...@gmail.com>
Committed: Tue Jun 4 15:46:38 2013 +0200

----------------------------------------------------------------------
 src/fauxton/app/api.js                             |    8 +-
 src/fauxton/app/modules/databases/resources.js     |    2 +-
 src/fauxton/app/modules/documents/resources.js     |  114 +++-
 src/fauxton/app/modules/documents/routes.js        |  186 ++---
 src/fauxton/app/modules/documents/views.js         |  591 +++++++++------
 src/fauxton/app/modules/pouchdb/base.js            |   17 +-
 .../app/modules/pouchdb/pouchdb.mapreduce.js       |   50 ++-
 .../app/templates/documents/all_docs_list.html     |   93 +---
 src/fauxton/app/templates/documents/changes.html   |    4 +-
 .../app/templates/documents/view_editor.html       |  238 +++++--
 .../app/templates/layouts/with_tabs_sidebar.html   |    5 +-
 11 files changed, 790 insertions(+), 518 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/90e4da6c/src/fauxton/app/api.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/api.js b/src/fauxton/app/api.js
index c506b1a..e13a41d 100644
--- a/src/fauxton/app/api.js
+++ b/src/fauxton/app/api.js
@@ -32,7 +32,8 @@ function(app, Fauxton) {
   // List of JSHINT errors to ignore
   // Gets around problem of anonymous functions not being a valid statement
   FauxtonAPI.excludedViewErrors = [
-    "Missing name in function declaration."
+    "Missing name in function declaration.",
+    "['{a}'] is better written in dot notation."
   ];
 
   FauxtonAPI.isIgnorableError = function(msg) {
@@ -54,8 +55,9 @@ function(app, Fauxton) {
     }
   });
 
-  FauxtonAPI.navigate = function(url) {
-    Backbone.history.navigate(url, true);
+  FauxtonAPI.navigate = function(url, _opts) {
+    var options = _.extend({trigger: true}, _opts );
+    app.router.navigate(url,options);
   };
 
   FauxtonAPI.addHeaderLink = function(link) {

http://git-wip-us.apache.org/repos/asf/couchdb/blob/90e4da6c/src/fauxton/app/modules/databases/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/databases/resources.js b/src/fauxton/app/modules/databases/resources.js
index 6927fd5..04e6c1e 100644
--- a/src/fauxton/app/modules/databases/resources.js
+++ b/src/fauxton/app/modules/databases/resources.js
@@ -47,7 +47,7 @@ function(app, FauxtonAPI, Documents) {
       if (context === "index") {
         return "/database/" + this.id + "/_all_docs";
       } else if (context === "changes") {
-        return "/database/" + this.id + "/_changes?descending=true&limit=100";
+        return "/database/" + this.id + "/_changes?descending=true&limit=100&include_docs=true";
       } else if (context === "app") {
         return "/database/" + this.id;
       } else {

http://git-wip-us.apache.org/repos/asf/couchdb/blob/90e4da6c/src/fauxton/app/modules/documents/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/documents/resources.js b/src/fauxton/app/modules/documents/resources.js
index f1a07c3..e313378 100644
--- a/src/fauxton/app/modules/documents/resources.js
+++ b/src/fauxton/app/modules/documents/resources.js
@@ -13,15 +13,10 @@
 define([
   "app",
 
-  "api",
-
-  // Views
-  "modules/documents/views"
-
-  // Plugins
+  "api"
 ],
 
-function(app, FauxtonAPI, Views) {
+function(app, FauxtonAPI) {
   var Documents = app.module();
 
   Documents.Doc = Backbone.Model.extend({
@@ -64,14 +59,61 @@ function(app, FauxtonAPI, Views) {
     hasViews: function() {
       if (!this.isDdoc()) return false;
       var doc = this.get('doc');
-      return doc && doc.views && _.keys(doc.views).length > 0;
+      if (doc) {
+        return doc && doc.views && _.keys(doc.views).length > 0;
+      }
+
+      var views = this.get('views');
+      return views && _.keys(views).length > 0;
     },
 
     getDdocView: function(view) {
       if (!this.isDdoc() || !this.hasViews()) return false;
 
       var doc = this.get('doc');
-      return doc.views[view];
+      if (doc) {
+        return doc.views[view];
+      }
+
+      return this.get('views')[view];
+    },
+
+    setDdocView: function (view, map, reduce) {
+      if (!this.isDdoc()) return false;
+      var views = this.get('views');
+
+      if (reduce) {
+        views[view] = {
+          map: map,
+          reduce: reduce
+        }; 
+      } else {
+        views[view].map = map;
+      }
+
+      this.set({views: views});
+
+      return true;
+    },
+
+    removeDdocView: function (viewName) {
+      if (!this.isDdoc()) return false;
+      var views = this.get('views');
+
+      delete views[viewName];
+      this.set({views: views});
+    },
+
+    dDocModel: function () {
+      if (!this.isDdoc()) return false;
+      var doc = this.get('doc');
+
+      if (doc) {
+        console.log('DOC', doc);
+        return new Documents.Doc(doc, {database: this.database});
+      } 
+
+      return this;
     },
 
     viewHasReduce: function(viewName) {
@@ -208,10 +250,10 @@ function(app, FauxtonAPI, Views) {
 
     initialize: function(_models, options) {
       this.database = options.database;
-      this.view = options.view;
-      this.design = options.design;
       this.params = _.extend({limit: 10, reduce: false}, options.params);
       this.idxType = "_view";
+      this.view = options.view;
+      this.design = options.design.replace('_design/','');
     },
 
     url: function() {
@@ -256,8 +298,56 @@ function(app, FauxtonAPI, Views) {
       return this.models;
     }
   });
+  
+  Documents.PouchIndexCollection = Backbone.Collection.extend({
+    model: Documents.ViewRow,
+
+    initialize: function(_models, options) {
+      this.database = options.database;
+      this.rows = options.rows;
+      this.view = options.view;
+      this.design = options.design.replace('_design/','');
+      this.params = _.extend({limit: 10, reduce: false}, options.params);
+      this.idxType = "_view";
+    },
+
+    url: function () {
+      return '';
+    },
+
+    fetch: function() {
+      var deferred = FauxtonAPI.Deferred();
+      this.reset(this.rows, {silent: true});
+
+      this.viewMeta = {
+        total_rows: this.rows.length,
+        offest: 0,
+        update_seq: false
+      };
+
+      deferred.resolve();
+      return deferred;
+    },
+
+    totalRows: function() {
+      console.log('rows');
+      console.log(this);
+      return this.viewMeta.total_rows || "unknown";
+    },
+
+    updateSeq: function() {
+      return this.viewMeta.update_seq || false;
+    },
+
+    buildAllDocs: function(){
+      this.fetch();
+    },
+
+    allDocs: function(){
+      return this.models;
+    }
+  });
 
-  Documents.Views = Views;
 
   return Documents;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/90e4da6c/src/fauxton/app/modules/documents/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/documents/routes.js b/src/fauxton/app/modules/documents/routes.js
index 1a75559..e02ac93 100644
--- a/src/fauxton/app/modules/documents/routes.js
+++ b/src/fauxton/app/modules/documents/routes.js
@@ -16,7 +16,7 @@ define([
        "api",
 
        // Modules
-       "modules/documents/resources",
+       "modules/documents/views",
        "modules/databases/base"
 ],
 
@@ -32,7 +32,7 @@ function(app, FauxtonAPI, Documents, Databases) {
       var databaseName = options[0], docID = options[1];
 
       this.database = this.database || new Databases.Model({id: databaseName});
-      this.doc = this.doc || new Documents.Doc({
+      this.doc = new Documents.Doc({
         _id: docID
       }, {
         database: this.database
@@ -62,7 +62,8 @@ function(app, FauxtonAPI, Documents, Databases) {
     code_editor: function (event) {
       this.tabsView.updateSelected('code_editor');
       this.docView = this.setView("#dashboard-content", new Documents.Views.Doc({
-        model: this.doc
+        model: this.doc,
+        database: this.database
       }));
     },
 
@@ -78,96 +79,6 @@ function(app, FauxtonAPI, Documents, Databases) {
     }
   });
 
-  /*var newViewEditorCallback = function(databaseName) {
-    var data = {
-      database: new Databases.Model({id:databaseName})
-    };
-    data.designDocs = new Documents.AllDocs(null, {
-      database: data.database,
-      params: {startkey: '"_design"',
-        endkey: '"_design1"',
-        include_docs: true}
-    });
-
-    return {
-      layout: "with_tabs_sidebar",
-
-      data: data,
-
-      crumbs: [
-        {"name": "Databases", "link": "/_all_dbs"},
-        {"name": data.database.id, "link": data.database.url('app')}
-      ],
-
-      views: {
-        "#sidebar-content": new Documents.Views.Sidebar({
-          collection: data.designDocs
-        }),
-
-        "#tabs": new Documents.Views.Tabs({
-          collection: data.designDocs,
-          database: data.database
-        }),
-
-        "#dashboard-content": new Documents.Views.ViewEditor({
-          model: data.database,
-          ddocs: data.designDocs
-        })
-      },
-
-      apiUrl: data.database.url()
-    };
-  };*/
-
-  // HACK: this kind of works
-  // Basically need a way to share state between different routes, for
-  // instance making a new doc won't work for switching back and forth
-  // between code and field editors
-  /*var newDocCodeEditorCallback = function(databaseName) {
-    var data = {
-      database: new Databases.Model({id:databaseName}),
-      doc: new Documents.NewDoc(),
-      selected: "code_editor"
-    };
-    data.doc.database = data.database;
-    data.designDocs = new Documents.AllDocs(null, {
-      database: data.database,
-      params: {startkey: '"_design"',
-        endkey: '"_design1"',
-        include_docs: true}
-    });
-
-    var options = app.getParams();
-    options.include_docs = true;
-    data.database.buildAllDocs(options);
-
-    return {
-      layout: "one_pane",
-
-      data: data,
-
-      crumbs: [
-        {"name": "Databases", "link": "/_all_dbs"},
-        {"name": data.database.id, "link": Databases.databaseUrl(data.database)},
-        {"name": "new", "link": "#"}
-      ],
-
-      views: {
-        "#dashboard-content": new Documents.Views.Doc({
-          model: data.doc
-        }),
-
-        "#tabs": new Documents.Views.FieldEditorTabs({
-          selected: data.selected,
-          model: data.doc
-        })
-      },
-
-      apiUrl: data.doc.url()
-    };
-  };*/
-
-
   var DocumentsRouteObject = FauxtonAPI.RouteObject.extend({
     layout: "with_tabs_sidebar",
 
@@ -180,6 +91,12 @@ function(app, FauxtonAPI, Documents, Databases) {
       "database/:database/new_view": "newViewEditor"
     },
 
+    events: {
+      "route:updateAllDocs": "updateAllDocsFromView",
+      "route:updatePreviewDocs": "updateAllDocsFromPreview",
+      "route:reloadDesignDocs": "reloadDesignDocs"
+    },
+
     initialize: function (route, masterLayout, options) {
       var docOptions = app.getParams();
       docOptions.include_docs = true;
@@ -207,6 +124,9 @@ function(app, FauxtonAPI, Documents, Databases) {
       }));
     },
 
+    establish: function () {
+      return this.data.designDocs.fetch();
+    },
 
     allDocs: function(databaseName, options) {
       var docOptions = app.getParams(options);
@@ -220,7 +140,9 @@ function(app, FauxtonAPI, Documents, Databases) {
         this.sidebar.setSelectedTab('all-docs');
       }
 
-      this.documentsView = this.setView("#dashboard-content", new Documents.Views.AllDocsList({
+      if (this.viewEditor) { this.viewEditor.remove(); }
+
+      this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({
         collection: this.data.database.allDocs
       }));
 
@@ -250,12 +172,22 @@ function(app, FauxtonAPI, Documents, Databases) {
         designDocs: this.data.designDocs
       };
 
-      this.setView("#dashboard-content", new Documents.Views.AllDocsList({
+      this.viewEditor = this.setView("#dashboard-upper-content", new Documents.Views.ViewEditor({
+        model: this.data.database,
+        ddocs: this.data.designDocs,
+        viewName: view,
+        params: params,
+        newView: false,
+        database: this.data.database,
+        ddocInfo: ddocInfo
+      }));
+
+      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,
-        ddocInfo: ddocInfo,
-        params: params
+        ddocInfo: ddocInfo
       }));
 
       this.sidebar.setSelectedTab(ddoc + '_' + view);
@@ -271,17 +203,65 @@ function(app, FauxtonAPI, Documents, Databases) {
       this.apiUrl = this.data.indexedDocs.url();
     },
 
-    newViewEditor: function (event) {
-      // TODO: Get this working
-      this.setView("#dashboard-content", new Documents.Views.ViewEditor({
-        model: this.data.database,
-        ddocs: this.data.designDocs
+    newViewEditor: function () {
+      var params = app.getParams();
+
+      this.viewEditor = this.setView("#dashboard-upper-content", new Documents.Views.ViewEditor({
+        ddocs: this.data.designDocs,
+        params: params,
+        database: this.data.database,
+        newView: true
       }));
 
       this.sidebar.setSelectedTab('new-view');
+    },
 
-    }
+    updateAllDocsFromView: function (event) {
+      var view = event.view,
+          ddoc = event.ddoc;
+
+      this.data.indexedDocs = new Documents.IndexCollection(null, {
+        database: this.data.database,
+        design: ddoc,
+        view: view,
+        params: app.getParams()
+      });
+
+      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
+      }));
+    },
+
+    updateAllDocsFromPreview: function (event) {
+      var view = event.view,
+          rows = event.rows,
+          ddoc = event.ddoc;
 
+      this.data.indexedDocs = new Documents.PouchIndexCollection(null, {
+        database: this.data.database,
+        design: ddoc,
+        view: view,
+        rows: rows
+      });
+
+      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
+      }));
+    },
+
+    reloadDesignDocs: function (event) {
+      this.sidebar.forceRender();
+
+      if (event && event.selectedTab) {
+        this.sidebar.setSelectedTab(event.selectedTab);
+      }
+    }
   });
 
   var ChangesRouteObject = FauxtonAPI.RouteObject.extend({

http://git-wip-us.apache.org/repos/asf/couchdb/blob/90e4da6c/src/fauxton/app/modules/documents/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/documents/views.js b/src/fauxton/app/modules/documents/views.js
index 17bf9af..f5cd882 100644
--- a/src/fauxton/app/modules/documents/views.js
+++ b/src/fauxton/app/modules/documents/views.js
@@ -11,20 +11,24 @@
 // the License.
 
 define([
-  "app",
+       "app",
 
-  "api",
+       "api",
 
-  // Libs
-  "codemirror",
-  "jshint",
+       "modules/documents/resources",
+       "modules/pouchdb/base",
+
+       // Libs
+       "codemirror",
+       "jshint",
+
+       // Plugins
+       "plugins/codemirror-javascript",
+       "plugins/prettify"
 
-  // Plugins
-  "plugins/codemirror-javascript",
-  "plugins/prettify"
 ],
 
-function(app, FauxtonAPI, Codemirror, JSHint) {
+function(app, FauxtonAPI, Documents, pouchdb, Codemirror, JSHint) {
   var Views = {};
 
   Views.Tabs = FauxtonAPI.View.extend({
@@ -276,161 +280,50 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
     template: "templates/documents/all_docs_list",
     events: {
       "click button.all": "selectAll",
-      "click button.bulk-delete": "bulkDelete",
-      "change form.view-query-update input": "updateFilters",
-      "change form.view-query-update select": "updateFilters",
-      "submit form.view-query-update": "updateView"
+      "click button.bulk-delete": "bulkDelete"
     },
 
     initialize: function(options){
       this.nestedView = options.nestedView || Views.Document;
       this.rows = {};
       this.viewList = !! options.viewList;
-      this.params = options.params;
+      this.database = options.database;
       if (options.ddocInfo) {
         this.designDocs = options.ddocInfo.designDocs;
         this.ddocID = options.ddocInfo.id;
       }
+      this.newView = options.newView || false;
     },
 
     establish: function() {
-      var deferreds = [
-        this.collection.fetch().error(function() {
-          // TODO: handle error requests that slip through
-          // This should just throw a notification, not break the page
-          console.log("ERROR: ", arguments);
-        })
-      ];
-      if (this.designDocs) {
-        deferreds.push(this.designDocs.fetch());
-      }
-      return deferreds;
+      if (this.newView) { return null; }
+
+      return this.collection.fetch().fail(function() {
+        // TODO: handle error requests that slip through
+        // This should just throw a notification, not break the page
+        console.log("ERROR: ", arguments);
+      });
     },
 
     selectAll: function(evt){
       $("input:checkbox").attr('checked', !$(evt.target).hasClass('active'));
     },
 
-    // TODO:: HACK::
-    // Hack to grab info about the ddoc and current view to determine whether
-    // or not the view has a reduce function so we can display the advanced
-    // options appropriately.
-    //
-    // NOTE: we have this here temporarily because we have to wait for the
-    // design docs to be present.
-    //
-    // NOTE: We should probably refactor this View out into a separate View
-    // dedicated to displaying view query results.
-    // If nothing else, we should at least switch to something along the lines
-    // of fetchOnce to ensure we're not reloading the ddocs here in addition to
-    // the sidebar.
-    setDdocInfo: function() {
-      if (!this.ddoc && this.designDocs) {
-        this.ddoc = this.designDocs.get(this.ddocID);
-      }
-    },
-
     serialize: function() {
-      this.setDdocInfo();
-      var data = {
-        database: this.collection,
-        viewList: this.viewList,
-        hasReduce: false,
-        params: this.params,
-        ddocs: this.designDocs
-      };
-      if (this.ddoc) {
-        data.ddoc = this.ddoc;
-        data.hasReduce = this.ddoc.viewHasReduce(this.collection.view);
-      }
-      return data;
-    },
-
-    updateView: function(event) {
-      event.preventDefault();
-      var $form = $(event.currentTarget);
+      var totalRows = 0,
+      updateSeq = false;
 
-      // Ignore params without a value
-      var params = _.filter($form.serializeArray(), function(param) {
-        return param.value;
-      });
-
-      // Validate *key* params to ensure they're valid JSON
-      var keyParams = ["key","keys","startkey","endkey"];
-      var errorParams = _.filter(params, function(param) {
-        if (_.contains(keyParams, param.name)) {
-          try {
-            JSON.parse(param.value);
-            return false;
-          } catch(e) {
-            return true;
-          }
-        } else {
-          return false;
-        }
-      });
-
-      if (_.any(errorParams)) {
-        _.map(errorParams, function(param) {
-
-          // TODO: Where to add this error?
-          // bootstrap wants the error on a control-group div, but we're not using that
-          //$('form.view-query-update input[name='+param+'], form.view-query-update select[name='+param+']').addClass('error');
-
-          return FauxtonAPI.addNotification({
-            msg: "JSON Parse Error on field: "+param.name,
-            type: "error",
-            selector: ".view.show .all-docs-list.errors-container"
-          });
-        });
-
-        FauxtonAPI.addNotification({
-          msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
-          type: "warning",
-          selector: ".view.show .all-docs-list.errors-container"
-        });
-
-        return false;
+      if (!this.newView) {
+        totalRows = this.collection.totalRows();
+        updateSeq = this.collection.updateSeq();
       }
 
-      var fragment = window.location.hash.replace(/\?.*$/, '');
-      fragment = fragment + '?' + $.param(params);
-      FauxtonAPI.navigate(fragment);
-    },
-
-    updateFilters: function(event) {
-      event.preventDefault();
-      var $ele = $(event.currentTarget);
-      var name = $ele.attr('name');
-      this.updateFiltersFor(name, $ele);
-    },
-
-    updateFiltersFor: function(name, $ele) {
-      var $form = $ele.parents("form.view-query-update:first");
-      switch (name) {
-        // Reduce constraints
-        //   - Can't include_docs for reduce=true
-        //   - can't include group_level for reduce=false
-        case "reduce":
-          if ($ele.prop('checked') === true) {
-            if ($form.find("input[name=include_docs]").prop("checked") === true) {
-              $form.find("input[name=include_docs]").prop("checked", false);
-              var notification = FauxtonAPI.addNotification({
-                msg: "include_docs has been disabled as you cannot include docs on a reduced view",
-                type: "warn",
-                selector: ".view.show .all-docs-list.errors-container"
-              });
-            }
-            $form.find("input[name=include_docs]").prop("disabled", true);
-            $form.find("select[name=group_level]").prop("disabled", false);
-          } else {
-            $form.find("select[name=group_level]").prop("disabled", true);
-            $form.find("input[name=include_docs]").prop("disabled", false);
-          }
-          break;
-        case "include_docs":
-          break;
-      }
+      return {
+        updateSeq: updateSeq,
+        totalRows: totalRows,
+        numModels: this.collection.models.length,
+        viewList: this.viewList
+      };
     },
 
     /*
@@ -468,14 +361,6 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
     },
 
     beforeRender: function() {
-      this.setDdocInfo();
-      if (this.viewList) {
-        this.viewEditorView = this.insertView("#edit-index-container", new Views.ViewEditor({
-          model: this.ddoc,
-          ddocs: this.designDocs,
-          viewCollection: this.collection
-        }));
-      }
       this.collection.each(function(doc) {
         this.rows[doc.id] = this.insertView("table.all-docs tbody", new this.nestedView({
           model: doc
@@ -485,34 +370,6 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
 
     afterRender: function(){
       prettyPrint();
-      if (this.params) {
-        var $form = this.$el.find("form.view-query-update");
-        _.each(this.params, function(val, key) {
-          var $ele;
-          switch (key) {
-            case "limit":
-            case "group_level":
-              $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;
-            case "reduce":
-              $ele = $form.find("input[name='"+key+"']");
-              if (val == "true") {
-                $ele.prop('checked', true);
-              }
-              this.updateFiltersFor(key, $ele);
-              break;
-            default:
-              $form.find("input[name='"+key+"']").val(val);
-              break;
-          }
-        }, this);
-      }
     }
   });
 
@@ -523,6 +380,10 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
       "click button.save-doc": "saveDoc"
     },
 
+    initialize: function (options) {
+      this.database = options.database;
+    },
+
     updateValues: function() {
       var notification;
       if (this.model.changedAttributes()) {
@@ -540,13 +401,15 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
     },
 
     saveDoc: function(event) {
-      var json, notification;
+      var json, notification, that = this;
       if (this.hasValidCode()) {
         json = JSON.parse(this.editor.getValue());
         this.model.clear({silent:true});
         this.model.set(json);
         notification = FauxtonAPI.addNotification({msg: "Saving document."});
-        this.model.save().error(function(xhr) {
+        this.model.save().then(function () {
+          FauxtonAPI.navigate('/database/' + that.database.id + '/' + that.model.id);
+        }).fail(function(xhr) {
           var responseText = JSON.parse(xhr.responseText).reason;
           notification = FauxtonAPI.addNotification({
             msg: "Save failed: " + responseText,
@@ -607,7 +470,8 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
         matchBrackets: true,
         lineWrapping: true,
         onChange: function() {
-          that.runJSHint();
+          //throwing errors at the moment
+          //that.runJSHint();
         },
         extraKeys: {
           "Ctrl-S": function(instance) { that.saveDoc(); },
@@ -646,6 +510,7 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
     }
   });
 
+  //TODO split this into two smaller views, one for advance query options and other for index editing
   Views.ViewEditor = FauxtonAPI.View.extend({
     template: "templates/documents/view_editor",
     builtinReduces: ['_sum', '_count', '_stats'],
@@ -653,12 +518,17 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
     events: {
       "click button.save": "saveView",
       "click button.preview": "previewView",
-      "change select#reduce-function-selector": "updateReduce"
+      "click button.delete": "deleteView",
+      "change select#reduce-function-selector": "updateReduce",
+      "change form.view-query-update input": "updateFilters",
+      "change form.view-query-update select": "updateFilters",
+      "change select#ddoc": "updateDesignDoc",
+      "submit form.view-query-update": "updateView"
     },
 
     langTemplates: {
       "javascript": {
-        map: "function(doc) {\n  emit(null, doc);\n}",
+        map: "function(doc) {\n  emit(doc.id, 1);\n}",
         reduce: "function(keys, values, rereduce){\n  if (rereduce){\n    return sum(values);\n  } else {\n    return values.length;\n  }\n}"
       }
     },
@@ -666,10 +536,27 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
     defaultLang: "javascript",
 
     initialize: function(options) {
+      this.newView = options.newView || false;
       this.ddocs = options.ddocs;
-      this.viewCollection = options.viewCollection;
-      this.reduceFunStr = this.model.viewHasReduce(this.viewCollection.view);
-      this.newView = false;
+      this.params = options.params;
+      this.database = options.database;
+      if (this.newView) {
+        this.viewName = 'newView';
+      } else {
+        this.ddocID = options.ddocInfo.id;
+        this.viewName = options.viewName;
+      } 
+    },
+
+    updateDesignDoc: function () {
+
+      if (this.$('#ddoc :selected').prop('id') === 'new-doc') {
+        this.$('#new-ddoc-section').show();
+
+      } else {
+        this.$('#new-ddoc-section').hide();
+      }
+
     },
 
     updateValues: function() {
@@ -694,43 +581,211 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
       }
     },
 
-    establish: function() {
-      //return [this.ddocs.fetch(), this.model.fetch()];
-      return [];
+    queryParams: function () {
+      var $form = $(".view-query-update");
+      // Ignore params without a value
+      var params = _.filter($form.serializeArray(), function(param) {
+        return param.value;
+      });
+
+      // Validate *key* params to ensure they're valid JSON
+      var keyParams = ["key","keys","startkey","endkey"];
+      var errorParams = _.filter(params, function(param) {
+        if (_.contains(keyParams, param.name)) {
+          try {
+            JSON.parse(param.value);
+            return false;
+          } catch(e) {
+            return true;
+          }
+        } else {
+          return false;
+        }
+      });
+
+      return {params: params, errorParams: errorParams};
+    },
+
+    deleteView: function (event) {
+      event.preventDefault();
+
+      if (this.newView) { return alert('Cannot delete a new view.'); }
+      if (!confirm('Are you sure you want to delete this view?')) {return;}
+
+      var that = this,
+          promise,
+          viewName = this.$('#index-name').val();
+          ddocName = this.$('#ddoc :selected').val(),
+          ddoc = this.getCurrentDesignDoc();
+
+      ddoc.removeDdocView(viewName);
+
+      if (ddoc.hasViews()) {
+        promise = ddoc.save(); 
+      } else {
+        promise = ddoc.destroy();
+      }
+
+      promise.then(function () {
+        FauxtonAPI.navigate('/database/' + that.database.id + '/_all_docs?limit=100');
+        FauxtonAPI.triggerRouteEvent('reloadDesignDocs');
+      });
+    },
+
+    updateView: function(event) {
+      event.preventDefault();
+
+      if (this.newView) { return alert('Please save this new view before querying it.'); }
+
+      var paramInfo = this.queryParams(),
+          errorParams = paramInfo.errorParams,
+          params = paramInfo.params;
+
+      if (_.any(errorParams)) {
+        _.map(errorParams, function(param) {
+
+          // TODO: Where to add this error?
+          // bootstrap wants the error on a control-group div, but we're not using that
+          //$('form.view-query-update input[name='+param+'], form.view-query-update select[name='+param+']').addClass('error');
+
+          return FauxtonAPI.addNotification({
+            msg: "JSON Parse Error on field: "+param.name,
+            type: "error",
+            selector: ".view.show .all-docs-list.errors-container"
+          });
+        });
+
+        FauxtonAPI.addNotification({
+          msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
+          type: "warning",
+          selector: ".view.show .all-docs-list.errors-container"
+        });
+
+        return false;
+      }
+
+      var fragment = window.location.hash.replace(/\?.*$/, '');
+      fragment = fragment + '?' + $.param(params);
+      FauxtonAPI.navigate(fragment, {trigger: false});
+
+      FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: this.ddocID, view: this.viewName});
+    },
+
+    updateFilters: function(event) {
+      event.preventDefault();
+      var $ele = $(event.currentTarget);
+      var name = $ele.attr('name');
+      this.updateFiltersFor(name, $ele);
+    },
+
+    updateFiltersFor: function(name, $ele) {
+      var $form = $ele.parents("form.view-query-update:first");
+      switch (name) {
+        // Reduce constraints
+        //   - Can't include_docs for reduce=true
+        //   - can't include group_level for reduce=false
+        case "reduce":
+          if ($ele.prop('checked') === true) {
+          if ($form.find("input[name=include_docs]").prop("checked") === true) {
+            $form.find("input[name=include_docs]").prop("checked", false);
+            var notification = FauxtonAPI.addNotification({
+              msg: "include_docs has been disabled as you cannot include docs on a reduced view",
+              type: "warn",
+              selector: ".view.show .all-docs-list.errors-container"
+            });
+          }
+          $form.find("input[name=include_docs]").prop("disabled", true);
+          $form.find("select[name=group_level]").prop("disabled", false);
+        } else {
+          $form.find("select[name=group_level]").prop("disabled", true);
+          $form.find("input[name=include_docs]").prop("disabled", false);
+        }
+        break;
+        case "include_docs":
+          break;
+      }
     },
 
     previewView: function(event) {
+      var that = this,
+          mapVal = this.mapEditor.getValue(),
+          reduceVal = this.reduceVal(),
+          paramsArr = this.queryParams().params;
+
+      var params = _.reduce(paramsArr, function (params, param) {
+        params[param.name] = param.value;
+        return params;
+      }, {reduce: false});
+
+      event.preventDefault();
+
       FauxtonAPI.addNotification({
         msg: "<strong>Warning!</strong> Preview executes the Map/Reduce functions in your browser, and may behave differently from CouchDB.",
         type: "warning",
         selector: "#define-view .errors-container",
-        fade: false
+        fade: true
       });
-      FauxtonAPI.addNotification({
-        msg: "Preview Functionality Coming Soon",
-        type: "warning",
-        selector: "#define-view .errors-container"
+
+      var promise = FauxtonAPI.Deferred();
+
+      if (!this.database.allDocs) {
+        this.database.buildAllDocs({limit: "100", include_docs: true});
+        promise = this.database.allDocs.fetch();
+      } else {
+        promise.resolve();
+      }
+
+      promise.then(function () {
+        params.docs = that.database.allDocs.map(function (model) { return model.get('doc');}); 
+
+        var queryPromise = pouchdb.runViewQuery({map: mapVal, reduce: reduceVal}, params);
+        queryPromise.then(function (results) {
+          FauxtonAPI.triggerRouteEvent('updatePreviewDocs', {rows: results.rows, ddoc: that.getCurrentDesignDoc().id, view: that.viewName});
+        });
       });
     },
 
     saveView: function(event) {
-      var json, notification;
+      var json, notification,
+      that = this;
+
+      event.preventDefault();
+
       if (this.hasValidCode()) {
-        var mapVal = this.mapEditor.getValue();
-        var reduceVal = this.reduceEditor.getValue();
-        /*
+        var mapVal = this.mapEditor.getValue(), 
+            reduceVal = this.reduceVal(),
+            viewName = this.$('#index-name').val(),
+            ddoc = this.getCurrentDesignDoc(),
+            ddocName = ddoc.id;
+
+        this.viewName = viewName;
+
         notification = FauxtonAPI.addNotification({
           msg: "Saving document.",
           selector: "#define-view .errors-container"
         });
-        */
-        FauxtonAPI.addNotification({
-          msg: "Save Functionality Coming Soon",
-          type: "warning",
-          selector: "#define-view .errors-container"
-        });
-        /*
-        this.model.save().error(function(xhr) {
+
+        ddoc.setDdocView(viewName, mapVal, reduceVal);
+
+        ddoc.save().then(function () {
+          FauxtonAPI.addNotification({
+            msg: "View has been saved.",
+            type: "success",
+            selector: "#define-view .errors-container"
+          });
+
+          if (that.newView) {
+            var fragment = '/database/' + that.database.id +'/' + ddocName + '/_view/' + viewName; 
+
+            FauxtonAPI.navigate(fragment, {trigger: false});
+            FauxtonAPI.triggerRouteEvent('reloadDesignDocs',{selectedTab: ddocName.replace('_design/','') + '_' + viewName});
+
+            that.newView = false;
+          }
+
+          FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: ddocName, view: viewName});
+
+        }, function(xhr) {
           var responseText = JSON.parse(xhr.responseText).reason;
           notification = FauxtonAPI.addNotification({
             msg: "Save failed: " + responseText,
@@ -738,21 +793,51 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
             clear: true
           });
         });
-        */
       } else {
         notification = FauxtonAPI.addNotification({
-          msg: "Please fix the JSON errors and try again.",
+          msg: "Please fix the Javascript errors and try again.",
           type: "error",
           selector: "#define-view .errors-container"
         });
       }
     },
 
+    getCurrentDesignDoc: function () {
+      if (this.newDesignDoc()) {
+        var doc = {
+          _id: '_design/' + this.$('#new-ddoc').val(),
+          views: {},
+          language: "javascript"
+        };
+        return new Documents.Doc(doc, {database: this.database});
+      } else {
+        var ddocName = this.$('#ddoc').val();
+        return this.ddocs.find(function (ddoc) {
+          return ddoc.id === ddocName;
+        }).dDocModel();
+      }
+
+    },
+
+    newDesignDoc: function () {
+      return this.$('#ddoc :selected').prop('id') === 'new-doc';
+    },
+
     isCustomReduceEnabled: function() {
       return $("#reduce-function-selector").val() == "CUSTOM";
     },
 
     reduceVal: function() {
+      var reduceOption = this.$('#reduce-function-selector :selected').val(),
+      reduceVal = "";
+
+      if (reduceOption === 'CUSTOM') {
+        reduceVal = this.reduceEditor.getValue();
+      } else if ( reduceOption !== 'NONE') {
+        reduceVal = reduceOption;
+      }
+
+      return reduceVal;
     },
 
     hasValidCode: function() {
@@ -765,7 +850,7 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
         } else {
           // By default CouchDB view functions don't pass lint
           return _.every(JSHINT.errors, function(error) {
-            return FauxtonAPI.isIgnorableError(error.reason);
+            return FauxtonAPI.isIgnorableError(error.raw);
           });
         }
       }, this);
@@ -801,13 +886,15 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
 
     serialize: function() {
       return {
-        //database: this.model,
         ddocs: this.ddocs,
         ddoc: this.model,
-        viewCollection: this.viewCollection,
+        ddocName: this.model.id,
+        viewName: this.viewName,
         reduceFunStr: this.reduceFunStr,
+        hasReduce: this.reduceFunStr,
         isCustomReduce: this.hasCustomReduce(),
-        newView: this.newView
+        newView: this.newView,
+        langTemplates: this.langTemplates.javascript
       };
     },
 
@@ -815,6 +902,22 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
       return this.reduceFunStr && ! _.contains(this.builtinReduces, this.reduceFunStr);
     },
 
+    beforeRender: function () {
+
+      if (this.newView) {
+        this.reduceFunStr = '_sum';
+        if (this.ddocs.length === 0) {
+          this.model = new Documents.Doc(null, {database: this.database});
+        } else {
+          this.model = this.ddocs.first().dDocModel();
+        }
+        this.ddocID = this.model.id;
+      } else {
+        this.model = this.ddocs.get(this.ddocID).dDocModel();
+        this.reduceFunStr = this.model.viewHasReduce(this.viewName);
+      }
+    },
+
     afterRender: function() {
       var that = this;
       var mapFun = $("#map-function");
@@ -823,13 +926,16 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
         mapFun.val(this.langTemplates[this.defaultLang].map);
         reduceFun.val(this.langTemplates[this.defaultLang].reduce);
       }
+
+      this.updateDesignDoc();
+
       this.mapEditor = Codemirror.fromTextArea(mapFun.get()[0], {
         mode: "javascript",
         lineNumbers: true,
         matchBrackets: true,
         lineWrapping: true,
         onChange: function() {
-          that.runJSHint("mapEditor");
+          //that.runJSHint("mapEditor");
         },
         extraKeys: {
           "Ctrl-S": function(instance) { that.saveView(); },
@@ -842,7 +948,7 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
         matchBrackets: true,
         lineWrapping: true,
         onChange: function() {
-          that.runJSHint("reduceEditor");
+          //that.runJSHint("reduceEditor");
         },
         extraKeys: {
           "Ctrl-S": function(instance) { that.saveView(); },
@@ -855,6 +961,36 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
       if ( ! this.hasCustomReduce()) {
         $(".control-group.reduce-function").hide();
       }
+
+      if (this.params) {
+        var $form = this.$el.find("form.view-query-update");
+        _.each(this.params, function(val, key) {
+          var $ele;
+          switch (key) {
+            case "limit":
+              case "group_level":
+              $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;
+            case "reduce":
+              $ele = $form.find("input[name='"+key+"']");
+            if (val == "true") {
+              $ele.prop('checked', true);
+            }
+            this.updateFiltersFor(key, $ele);
+            break;
+            default:
+              $form.find("input[name='"+key+"']").val(val);
+            break;
+          }
+        }, this);
+      }
+
     }
   });
 
@@ -871,14 +1007,6 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
       }
     },
 
-    establish: function() {
-      if (this.collection) {
-        return [this.collection.fetch()];
-      } else {
-        return null;
-      }
-    },
-
     serialize: function() {
       return {
         index: [1,2,3],
@@ -929,7 +1057,14 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
       }, this);
     },
 
+    afterRender: function () {
+      if (this.selectedTab) {
+        this.setSelectedTab(this.selectedTab);
+      }
+    },
+
     setSelectedTab: function (selectedTab) {
+      this.selectedTab = selectedTab;
       this.$('li').removeClass('active');
       this.$('#' + selectedTab).parent().addClass('active');
     }
@@ -942,20 +1077,24 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
     template: "templates/documents/changes",
 
     establish: function() {
-      return [
-        this.model.changes.fetch()
-      ];
+      return [ this.model.changes.fetch()];
     },
 
     serialize: function () {
+      console.log('c', this.model.changes.toJSON());
       return {
         changes: this.model.changes.toJSON(),
         database: this.model
       };
+    },
+
+    afterRender: function(){
+      prettyPrint();
     }
 
-  });
 
+  });
 
-  return Views;
+  Documents.Views = Views;
+  return Documents;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/90e4da6c/src/fauxton/app/modules/pouchdb/base.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/pouchdb/base.js b/src/fauxton/app/modules/pouchdb/base.js
index 2b7cfc9..ddaf06d 100644
--- a/src/fauxton/app/modules/pouchdb/base.js
+++ b/src/fauxton/app/modules/pouchdb/base.js
@@ -31,8 +31,8 @@ function(app, FauxtonAPI, MapReduce) {
   var Pouch = {};
   Pouch.MapReduce = MapReduce;
 
-  Pouch.runViewQuery = function(fun, docs) {
-    docs = [
+  Pouch.runViewQuery = function(fun, opts) {
+    /*docs = [
       {_id: 'test_doc_1', foo: 'bar-1'},
       {_id: 'test_doc_2', foo: 'bar-2'},
       {_id: 'test_doc_3', foo: 'bar-3'},
@@ -43,15 +43,18 @@ function(app, FauxtonAPI, MapReduce) {
       {_id: 'test_doc_8', foo: 'bar-8'},
       {_id: 'test_doc_9', foo: 'bar-9'},
       {_id: 'test_doc_10', foo: 'bar-10'}
-    ];
+    ];*/
 
     var deferred = FauxtonAPI.Deferred();
-    var complete = function(resp) {
-      console.log("COMPLETE TRIGGERED", arguments);
+    var complete = function(resp, rows) {
+      deferred.resolve(rows);
     };
 
-    return Pouch.MapReduce.query(fun, {docs: docs, complete:complete});
-  };
+    var options = _.extend(opts, {complete: complete});
 
+    Pouch.MapReduce.query(fun, options);
+    return deferred;
+  };
+  //pdb.runViewQuery({map:function(doc) { emit(doc._id, doc.foo) }})
   return Pouch;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/90e4da6c/src/fauxton/app/modules/pouchdb/pouchdb.mapreduce.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/pouchdb/pouchdb.mapreduce.js b/src/fauxton/app/modules/pouchdb/pouchdb.mapreduce.js
index b97eb7f..a2d0b91 100644
--- a/src/fauxton/app/modules/pouchdb/pouchdb.mapreduce.js
+++ b/src/fauxton/app/modules/pouchdb/pouchdb.mapreduce.js
@@ -39,6 +39,36 @@ function(app, FauxtonAPI, Collate) {
   //var MapReduce = function(db) {
   var MapReduce = function() {
 
+    var builtInReduce = {
+      "_sum": function(keys, values){
+        return sum(values);
+      },
+
+      "_count": function(keys, values, rereduce){
+        if (rereduce){
+          return sum(values);
+        } else {
+          return values.length;
+        }
+      },
+
+      "_stats": function(keys, values, rereduce){
+        return {
+          'sum': sum(values),
+          'min': Math.min.apply(null, values),
+          'max': Math.max.apply(null, values),
+          'count': values.length,
+          'sumsqr': (function(){
+            _sumsqr = 0;
+            for(var idx in values){
+              _sumsqr += values[idx] * values[idx];
+            }
+            return _sumsqr;
+          })()
+        };
+      }
+    };
+
     function viewQuery(fun, options) {
       console.log("IN VIEW QUERY");
       if (!options.complete) {
@@ -55,13 +85,13 @@ function(app, FauxtonAPI, Collate) {
       var completed= false;
 
       var emit = function(key, val) {
-        console.log("IN EMIT: ", key, val, current);
+        //console.log("IN EMIT: ", key, val, current);
         var viewRow = {
           id: current.doc._id,
           key: key,
           value: val
         }; 
-        console.log("VIEW ROW: ", viewRow);
+        //console.log("VIEW ROW: ", viewRow);
 
         if (options.startkey && Pouch.collate(key, options.startkey) < 0) return;
         if (options.endkey && Pouch.collate(key, options.endkey) > 0) return;
@@ -95,7 +125,11 @@ function(app, FauxtonAPI, Collate) {
       // ugly way to make sure references to 'emit' in map/reduce bind to the
       // above emit
       eval('fun.map = ' + fun.map.toString() + ';');
-      if (fun.reduce) {
+      if (fun.reduce && options.reduce) {
+        if (builtInReduce[fun.reduce]) {
+          console.log('built in reduce');
+          fun.reduce = builtInReduce[fun.reduce];
+        }
         eval('fun.reduce = ' + fun.reduce.toString() + ';');
       }
 
@@ -105,6 +139,7 @@ function(app, FauxtonAPI, Collate) {
 
       //only proceed once all documents are mapped and joined
       var checkComplete= function(){
+        console.log('check');
         if (completed && results.length == num_started){
           results.sort(function(a, b) {
             return Pouch.collate(a.key, b.key);
@@ -116,6 +151,7 @@ function(app, FauxtonAPI, Collate) {
             return options.complete(null, {rows: results});
           }
 
+          console.log('reducing', options);
           var groups = [];
           results.forEach(function(e) {
             var last = groups[groups.length-1] || null;
@@ -130,19 +166,21 @@ function(app, FauxtonAPI, Collate) {
             e.value = fun.reduce(e.key, e.value) || null;
             e.key = e.key[0][0];
           });
+          console.log('GROUPs', groups);
           options.complete(null, {rows: groups});
         }
       };
 
       if (options.docs) {
-        console.log("RUNNING MR ON DOCS: ", options.docs);
+        //console.log("RUNNING MR ON DOCS: ", options.docs);
         _.each(options.docs, function(doc) {
           current = {doc: doc};
           fun.map.call(this, doc);
         }, this);
-        return options.complete(null, {rows: results});
+        completed = true;
+        return checkComplete();//options.complete(null, {rows: results});
       } else {
-        console.log("COULD NOT FIND DOCS");
+        //console.log("COULD NOT FIND DOCS");
         return false;
       }
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/90e4da6c/src/fauxton/app/templates/documents/all_docs_list.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/documents/all_docs_list.html b/src/fauxton/app/templates/documents/all_docs_list.html
index 2f63af0..9f4ffdd 100644
--- a/src/fauxton/app/templates/documents/all_docs_list.html
+++ b/src/fauxton/app/templates/documents/all_docs_list.html
@@ -28,97 +28,10 @@ the License.
     </div>
   <% } %>
 
-  <div class="row">
-    <div class="all-docs-list errors-container"></div>
-    <div id="edit-index-container"></div>
-    <% if (viewList) { %>
-      <div class="accordion" id="advanced-options-accordion">
-        <div class="accordion-group">
-          <div class="accordion-heading">
-            <a class="accordion-toggle" data-bypass="true" data-toggle="collapse" data-parent="#advanced-options-accordion" href="#collapse-advanced-options">
-              <i class="icon-plus"></i> Advanced Options
-            </a>
-          </div>
-          <div id="collapse-advanced-options" class="accordion-body collapse">
-            <div class="accordion-inner">
-              <form class="view-query-update">
-                <div class="controls controls-row">
-                  <label class="span3 inline">
-                    Limit:
-                    <select name="limit" class="input-small">
-                      <option>5</option>
-                      <option selected="selected">10</option>
-                      <option>25</option>
-                      <option>50</option>
-                      <option>100</option>
-                    </select>
-                  </label>
-                  <label class="span3 checkbox inline">
-                    <input name="include_docs" type="checkbox" value="true"> Include Docs
-                  </label>
-                  <% if (hasReduce) { %>
-                    <label class="span2 checkbox inline">
-                      <input name="reduce" type="checkbox" value="true"> Reduce
-                    </label>
-                    <label class="span4 inline">
-                      Group Level:
-                      <select 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>
-                  <% } %>
-                </div>
-
-                <div class="controls controls-row">
-                  <input name="key" class="span4" type="text" placeholder="Key">
-                  <input name="keys" class="span8" type="text" placeholder="Keys">
-                </div>
-                <div class="controls controls-row">
-                  <input name="startkey" class="span6" type="text" placeholder="Start Key">
-                  <input name="endkey" class="span6" type="text" placeholder="End Key">
-                </div>
-                <div class="controls controls-row">
-                  <label class="span2 checkbox inline">
-                    <input name="stale" type="checkbox" value="ok"> Stale
-                  </label>
-                  <label class="span2 checkbox inline">
-                    <input name="descending" type="checkbox" value="true"> Descending
-                  </label>
-                  <label class="span4 checkbox inline">
-                    <input name="inclusive_end" type="checkbox" value="false"> Disable Inclusive End
-                  </label>
-                  <label class="span4 checkbox inline">
-                    <input name="update_seq" type="checkbox" value="true"> Include Update Sequence
-                  </label>
-                </div>
-                <div class="controls controls-row">
-                  <button type="submit" class="btn btn-primary">Query</button>
-                </div>
-              </form>
-
-            </div>
-          </div>
-
-        </div>
-      </div>
-    <% } %>
-  </div>
-
-
   <p>
-    Showing 1-<%= database.models.length %> of <%= database.totalRows() %> rows
-    <% if (database.updateSeq()) { %>
-      -- Update Sequence: <%= database.updateSeq() %>
+    Showing 1-<%= numModels %> of <%= totalRows %> rows
+    <% if (updateSeq) { %>
+      -- Update Sequence: <%= updateSeq %>
     <% } %>
   </p>
   <table class="all-docs table table-striped table-condensed">

http://git-wip-us.apache.org/repos/asf/couchdb/blob/90e4da6c/src/fauxton/app/templates/documents/changes.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/documents/changes.html b/src/fauxton/app/templates/documents/changes.html
index 3e5009c..e528a1c 100644
--- a/src/fauxton/app/templates/documents/changes.html
+++ b/src/fauxton/app/templates/documents/changes.html
@@ -28,7 +28,9 @@ the License.
       <% } else { %>
         <td> <a href="#<%= database.url('app') %>/<%= change.id %>"><%= change.id %></a> </td>
       <% } %>
-      <td> <%= JSON.stringify(change.changes) %> </td>
+        <td> 
+          <pre class="prettyprint">  <%= JSON.stringify({changes: change.changes, doc: change.doc}, null, " ") %> </pre>
+      </td>
       <td><%= change.deleted ? "true" : "false" %></td>
     </tr>
   <% }); %>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/90e4da6c/src/fauxton/app/templates/documents/view_editor.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/documents/view_editor.html b/src/fauxton/app/templates/documents/view_editor.html
index 4a8668e..a34ff0d 100644
--- a/src/fauxton/app/templates/documents/view_editor.html
+++ b/src/fauxton/app/templates/documents/view_editor.html
@@ -11,82 +11,184 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 License for the specific language governing permissions and limitations under
 the License.
 -->
+<div class="row">
+  <div class="all-docs-list errors-container"></div>
+  <div id="edit-index-container">
 
-<div class="accordion" id="edit-index-accordion">
-  <div class="accordion-group">
-    <div class="accordion-heading">
-      <a class="accordion-toggle" data-bypass="true" data-toggle="collapse" data-parent="#edit-index-accordion" href="#collapse-edit-index">
-        <i class="icon-wrench"></i> Edit Index
-      </a>
-    </div>
-    <div id="collapse-edit-index" class="accordion-body collapse">
-      <div class="accordion-inner">
-        <div id="define-view" class="ddoc-alert well">
-          <div class="errors-container"></div>
-          <form class="form-horizontal">
-            <h3>Define your index</h3>
-            <div class="control-group">
-              <label class="control-label" for="ddoc">Design document <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#design-docs"><i class="icon-question-sign"></i></a></label>
-              <div class="controls">
-                <select id="ddoc">
-                  <optgroup label="Select a document">
-                    <option>New document</option>
-                    <% ddocs.each(function(ddoc) { %>
-                      <% if (ddoc.id == "_design/"+viewCollection.design) { %>
-                        <option selected="selected"><%= ddoc.id %></option>
-                      <% } else { %>
-                        <option><%= ddoc.id %></option>
-                      <% } %>
-                    <% }); %>
-                    <option selected="selected">_design/views101</option>
-                  </optgroup>
-                </select>
-              </div>
+    <div class="accordion" id="edit-index-accordion">
+      <div class="accordion-group">
+        <div class="accordion-heading">
+          <a class="accordion-toggle" data-bypass="true" data-toggle="collapse" data-parent="#edit-index-accordion" href="#collapse-edit-index">
+            <i class="icon-wrench"></i> <% if (newView) { %> Create Index <% } else { %> Edit Index <% } %>
+          </a>
+        </div>
+        <div id="collapse-edit-index" class="accordion-body <% if (!newView) { %> collapse <% } %>">
+          <div class="accordion-inner">
+            <div id="define-view" class="ddoc-alert well">
+              <div class="errors-container"></div>
+              <form class="form-horizontal">
+                <h3>Define your index</h3>
+                <div class="control-group">
+                  <div class="row" style="margin-left:10px">
+                    <div class="span3">
+                      <label class="control-label" for="ddoc">Design document <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#design-docs"><i class="icon-question-sign"></i></a></label>
+                      <div class="controls">
+                        <select id="ddoc">
+                          <optgroup label="Select a document">
+                            <option id="new-doc">New document</option>
+                            <% ddocs.each(function(ddoc) { %>
+                            <% if (ddoc.id === ddocName) { %>
+                            <option selected="selected"><%= ddoc.id %></option>
+                            <% } else { %>
+                            <option><%= ddoc.id %></option>
+                            <% } %>
+                            <% }); %>
+                          </optgroup>
+                        </select>
+                      </div>
+                    </div>
+                    <div id="new-ddoc-section" class="span5" style="display:none">
+                      <label class="control-label" for="new-ddoc"> _design/ </label>
+                      <div class="controls">
+                        <input type="text" id="new-ddoc" placeholder="newDesignDoc">
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div class="control-group">
+                  <label class="control-label" for="index-name">Index name <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#view-functions"><i class="icon-question-sign"></i></a></label>
+                  <div class="controls">
+                    <input type="text" id="index-name" value="<%= viewName %>" placeholder="Index name" />
+                  </div>
+                </div>
+                <div class="control-group">
+                  <label class="control-label" for="map-function">Map function <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#map-functions"><i class="icon-question-sign"></i></a></label>
+                  <div class="controls">
+                    <% if (newView) { %>
+                    <textarea class="js-editor" id="map-function"><%= langTemplates.map %></textarea>
+                    <% } else { %>
+                    <textarea class="js-editor" id="map-function"><%= ddoc.get('views')[viewName].map %></textarea>
+                    <% } %>
+                  </div>
+                </div>
+                <div class="control-group">
+                  <label class="control-label" for="reduce-function-selector">Reduce function <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs.html#reduce-and-rereduce-functions"><i class="icon-question-sign"></i></a></label>
+                  <div class="controls">
+                    <select id="reduce-function-selector">
+                      <option value="" <%= !reduceFunStr ? 'selected="selected"' : '' %>>None</option>
+                      <% _.each(["_sum", "_count", "_stats"], function(reduce) { %>
+                      <option value="<%= reduce %>" <% if (reduce == reduceFunStr) { %>selected<% } %>><%= reduce %></option>
+                      <% }) %>
+                      <option value="CUSTOM" <% if (isCustomReduce) { %>selected<% } %>>Custom reduce</option>
+                    </select>
+                    <span class="help-block">Reduce functions are optional.</span>
+                  </div>
+                </div>
+                <div class="control-group reduce-function">
+                  <label class="control-label" for="reduce-function">Custom Reduce</label>
+                  <div class="controls">
+                    <% if (newView) { %>
+                    <textarea class="js-editor" id="reduce-function"><%= langTemplates.reduce %></textarea>
+                    <% } else { %>
+                    <textarea class="js-editor" id="reduce-function"><%= ddoc.get('views')[viewName].reduce %></textarea>
+                    <% } %>
+                  </div>
+                </div>
+                <div class="control-group">
+                  <hr />
+                  <div class="controls">
+                    <% if (!this.newView) { %>
+                    <button class="btn btn-small btn-danger delete">Delete</button>
+                    <% } %>
+                    <button class="btn btn-small btn-info preview">Preview</button>
+                    <button class="btn btn-primary save">Save</button>
+                  </div>
+                </div>
+                <div class="clearfix"></div>
+              </form>
             </div>
-            <div class="control-group">
-              <label class="control-label" for="index-name">Index name <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#view-functions"><i class="icon-question-sign"></i></a></label>
-              <div class="controls">
-                <input type="text" id="index-name" value="<%= viewCollection.view %>" placeholder="Index name" />
+          </div>
+        </div>
+
+      </div>
+    </div>
+    <div class="accordion" id="advanced-options-accordion">
+      <div class="accordion-group">
+        <div class="accordion-heading">
+          <a class="accordion-toggle" data-bypass="true" data-toggle="collapse" data-parent="#advanced-options-accordion" href="#collapse-advanced-options">
+            <i class="icon-plus"></i> Advanced Options
+          </a>
+        </div>
+        <div id="collapse-advanced-options" class="accordion-body collapse">
+          <div class="accordion-inner">
+            <form class="view-query-update">
+              <div class="controls controls-row">
+                <label class="span3 inline">
+                  Limit:
+                  <select name="limit" class="input-small">
+                    <option>5</option>
+                    <option selected="selected">10</option>
+                    <option>25</option>
+                    <option>50</option>
+                    <option>100</option>
+                  </select>
+                </label>
+                <label class="span3 checkbox inline">
+                  <input name="include_docs" type="checkbox" value="true"> Include Docs
+                </label>
+                <% if (hasReduce) { %>
+                <label class="span2 checkbox inline">
+                  <input name="reduce" type="checkbox" value="true"> Reduce
+                </label>
+                <label class="span4 inline">
+                  Group Level:
+                  <select 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>
+                <% } %>
               </div>
-            </div>
-            <div class="control-group">
-              <label class="control-label" for="map-function">Map function <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#map-functions"><i class="icon-question-sign"></i></a></label>
-              <div class="controls">
-                <textarea class="js-editor" id="map-function"><%= ddoc.get('doc').views[viewCollection.view].map %></textarea>
+
+              <div class="controls controls-row">
+                <input name="key" class="span4" type="text" placeholder="Key">
+                <input name="keys" class="span8" type="text" placeholder="Keys">
               </div>
-            </div>
-            <div class="control-group">
-              <label class="control-label" for="reduce-function-selector">Reduce function <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#reduce-and-rereduce-functions"><i class="icon-question-sign"></i></a></label>
-              <div class="controls">
-                <select id="reduce-function-selector">
-                  <option value="" <%= !reduceFunStr ? 'selected="selected"' : '' %>>None</option>
-                  <% _.each(["_sum", "_count", "_stats"], function(reduce) { %>
-                    <option value="<%= reduce %>" <% if (reduce == reduceFunStr) { %>selected<% } %>><%= reduce %></option>
-                  <% }) %>
-                  <option value="CUSTOM" <% if (isCustomReduce) { %>selected<% } %>>Custom reduce</option>
-                </select>
-                <span class="help-block">Reduce functions are optional.</span>
+              <div class="controls controls-row">
+                <input name="startkey" class="span6" type="text" placeholder="Start Key">
+                <input name="endkey" class="span6" type="text" placeholder="End Key">
               </div>
-            </div>
-            <div class="control-group reduce-function">
-              <label class="control-label" for="reduce-function">Custom Reduce</label>
-              <div class="controls">
-                <textarea class="js-editor" id="reduce-function"><%= ddoc.get('doc').views[viewCollection.view].reduce %></textarea>
+              <div class="controls controls-row">
+                <label class="span2 checkbox inline">
+                  <input name="stale" type="checkbox" value="ok"> Stale
+                </label>
+                <label class="span2 checkbox inline">
+                  <input name="descending" type="checkbox" value="true"> Descending
+                </label>
+                <label class="span4 checkbox inline">
+                  <input name="inclusive_end" type="checkbox" value="false"> Disable Inclusive End
+                </label>
+                <label class="span4 checkbox inline">
+                  <input name="update_seq" type="checkbox" value="true"> Include Update Sequence
+                </label>
               </div>
-            </div>
-            <div class="control-group">
-              <hr />
-              <div class="controls">
-                <button class="btn btn-small btn-inverse cancel">Cancel</button>
-                <button class="btn btn-small btn-info preview">Preview</button>
-                <button class="btn btn-primary save">Save</button>
+              <div class="controls controls-row">
+                <button type="submit" class="btn btn-primary">Query</button>
               </div>
-            </div>
-            <div class="clearfix"></div>
-          </form>
+            </form>
+
+          </div>
         </div>
+
       </div>
     </div>
-
   </div>
-</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/90e4da6c/src/fauxton/app/templates/layouts/with_tabs_sidebar.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/layouts/with_tabs_sidebar.html b/src/fauxton/app/templates/layouts/with_tabs_sidebar.html
index f78832f..0b5f2c7 100644
--- a/src/fauxton/app/templates/layouts/with_tabs_sidebar.html
+++ b/src/fauxton/app/templates/layouts/with_tabs_sidebar.html
@@ -21,7 +21,10 @@ the License.
 
   <div class="row-fluid">
     <div id="sidebar-content" class="sidebar span4"></div>
-    <div id="dashboard-content" class="list span8 pull-right"></div>
+    <div id="dashboard-content" class="list span8 pull-right">
+      <div id="dashboard-upper-content"></div>
+      <div id="dashboard-lower-content"></div>
+    </div>
   </div>
 </div>