couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From robertkowal...@apache.org
Subject [3/8] fauxton commit: updated refs/heads/port-views to f114d07
Date Wed, 15 Oct 2014 12:33:38 GMT
import index module


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

Branch: refs/heads/port-views
Commit: fc1eb5c8a7907e4fe9622b439b7052adc530ec95
Parents: b74df89
Author: Robert Kowalski <robertkowalski@apache.org>
Authored: Tue Oct 14 16:12:33 2014 +0200
Committer: Robert Kowalski <robertkowalski@apache.org>
Committed: Wed Oct 15 14:33:09 2014 +0200

----------------------------------------------------------------------
 .gitignore                                      |   1 +
 app/addons/indexes/assets/less/doc-item.less    |  95 +++
 app/addons/indexes/assets/less/index-form.less  |  64 ++
 app/addons/indexes/assets/less/indexes.less     |  17 +
 .../assets/less/new-index-placeholder.less      |  26 +
 app/addons/indexes/base.js                      |  78 +++
 app/addons/indexes/resources.js                 | 216 ++++++
 app/addons/indexes/routes-core.js               | 224 +++++++
 app/addons/indexes/routes-list.js               | 108 +++
 app/addons/indexes/routes-show.js               | 103 +++
 app/addons/indexes/routes-viewindexes.js        | 144 ++++
 app/addons/indexes/routes.js                    |  24 +
 .../indexes/templates/design_doc_selector.html  |  34 +
 app/addons/indexes/templates/header_right.html  |  23 +
 .../indexes/templates/index_row_docular.html    |  35 +
 app/addons/indexes/templates/list_editor.html   |  18 +
 .../indexes/templates/preview_screen.html       |  18 +
 app/addons/indexes/templates/show_editor.html   |  41 ++
 app/addons/indexes/templates/view_editor.html   |  80 +++
 app/addons/indexes/views.js                     | 653 +++++++++++++++++++
 20 files changed, 2002 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 1baaaf1..f7cb6c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ app/addons/*
 !app/addons/databases
 !app/addons/documents
 !app/addons/styletests
+!app/addons/indexes
 settings.json*
 !settings.json.default
 !assets/js/plugins/zeroclipboard/ZeroClipboard.swf

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/assets/less/doc-item.less
----------------------------------------------------------------------
diff --git a/app/addons/indexes/assets/less/doc-item.less b/app/addons/indexes/assets/less/doc-item.less
new file mode 100644
index 0000000..3d3e3ad
--- /dev/null
+++ b/app/addons/indexes/assets/less/doc-item.less
@@ -0,0 +1,95 @@
+// 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.
+.view table td div.doc-menu-item div.dropdown{
+  display: block;
+}
+
+
+#doc-list{
+  padding-top: 30px;
+  .all-docs-item {
+    .select {
+      display:none;
+      float: left;
+      vertical-align: top;
+    }
+    &.showSelect{
+      .select {
+        display:block;
+        width: 10%;
+      }
+      .doc-item {
+        width:90%;
+      }
+    }
+  }
+  div.doc-row {
+    margin-bottom:20px;
+    .doc-item {
+      float:left;
+      width:100%;
+      vertical-align: top;
+      position: relative;
+      .border-radius(5px 5px 5px 5px);
+      .box-shadow(3px 4px 0 rgba(0, 0, 0, 0.3));
+      border: 1px solid #000;
+      header {
+        font-weight: bold;
+        position: relative;
+        padding: 20px;
+        background-color: #777;
+        color: #3a2c2b;
+        border-bottom: 1px solid #000;
+        .header-id-number{
+          color: #fff;
+          margin-left: 10px;
+        }
+      }
+
+      .doc-data{
+        pre.prettyprint{
+        }
+      }
+
+      &:hover{
+        .btn-group{
+          display:block;
+        }
+      }
+      //temporary
+      .btn-group{
+        position:absolute;
+        top:10px;
+        right:10px;
+        display:none;
+      }
+
+      .doc-menu {
+        position:absolute;
+        right: 0px;
+        top: 0px;
+        .doc-menu-item{
+          a {
+            text-decoration: none;
+          }
+          .dropdown-menu {
+            left:auto;
+            right: 0px;
+          }
+          display: inline-block;
+          width: 30px;
+          height: 30px;
+        }
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/assets/less/index-form.less
----------------------------------------------------------------------
diff --git a/app/addons/indexes/assets/less/index-form.less b/app/addons/indexes/assets/less/index-form.less
new file mode 100644
index 0000000..c07163b
--- /dev/null
+++ b/app/addons/indexes/assets/less/index-form.less
@@ -0,0 +1,64 @@
+// 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.
+.index-form{
+  .control-group {
+    padding:15px 25px;
+    border-bottom: 1px solid #D9D9D9;
+    border-top: 1px solid #fff;
+    margin: 0 0 0 1px;
+    > input[type=text]{
+      width: 100%;
+      .border-radius(5px 5px 5px 5px);
+    }
+  }
+  .js-editor {
+    border: 1px solid #ccc;
+    .border-radius(5px 5px 5px 5px);
+  }
+}
+.input-prepend {
+  margin-left:0;
+}
+.new-ddoc {
+  .select-wrapper {
+    .border-radius(5px 0 0 5px);
+    margin-left:0;
+    overflow: hidden;
+    border: 1px solid #d0cdc6;
+    position: relative;
+    select {
+      border: 0;
+      color: #000;
+      background: transparent;
+      font-size: 14px;
+      padding: 5px 10px;
+      -webkit-appearance: none;
+      -moz-appearance: window;
+      height: 44px;
+    }
+    i {
+      position: absolute;
+      right: 20px;
+      top: 16px;
+      display: block;
+      color: #000;
+      width: 0;
+      height: 0;
+      border-style: solid;
+      border-width: 10px 7.5px 0 7.5px;
+      border-color: #000000 transparent transparent transparent;
+    }
+  }
+  input[type=text]{
+    .border-radius(0px 5px 5px 0);
+  }
+}

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/assets/less/indexes.less
----------------------------------------------------------------------
diff --git a/app/addons/indexes/assets/less/indexes.less b/app/addons/indexes/assets/less/indexes.less
new file mode 100644
index 0000000..5795cce
--- /dev/null
+++ b/app/addons/indexes/assets/less/indexes.less
@@ -0,0 +1,17 @@
+// 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.
+@import "../../../../../assets/less/variables.less";
+@import "../../../../../assets/less/bootstrap/variables.less";
+@import "../../../../../assets/less/bootstrap/mixins.less";
+@import "new-index-placeholder.less";
+@import "index-form.less";
+@import "doc-item.less";

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/assets/less/new-index-placeholder.less
----------------------------------------------------------------------
diff --git a/app/addons/indexes/assets/less/new-index-placeholder.less b/app/addons/indexes/assets/less/new-index-placeholder.less
new file mode 100644
index 0000000..b5ca506
--- /dev/null
+++ b/app/addons/indexes/assets/less/new-index-placeholder.less
@@ -0,0 +1,26 @@
+// 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.
+.watermark-logo {
+  background: transparent url('../img/couchWatermark.png') no-repeat 50% 50%;
+  min-height: 400px;
+  padding-top: 60%;
+  text-align: center;
+  margin: 0 20%;
+  h3 {
+    border-bottom: 1px solid #ccc;
+    padding-bottom:10px;
+    margin-bottom:20px;
+  }
+  .preview {
+    margin: 0 10px;
+  }
+}

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/base.js
----------------------------------------------------------------------
diff --git a/app/addons/indexes/base.js b/app/addons/indexes/base.js
new file mode 100644
index 0000000..f2220fa
--- /dev/null
+++ b/app/addons/indexes/base.js
@@ -0,0 +1,78 @@
+// 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.
+
+
+/*  View Indexes  */
+define([
+  "app",
+  "api",
+  "addons/indexes/routes"
+],
+function(app, FauxtonAPI, Routes) {
+  Routes.initialize = function() {
+
+    /*
+      Example of an extension:
+      An extension is just like an event that may or may not have listener in another view.
+      In this case the listener is in the documents addon in the sidebar.
+
+      If there are is a view that you want rendered in another route, this is the way to do it.
+
+      Each Secondary index is going to trigger this event with the views it's passing.
+      The views will be rendered and have links to the routes defined in this addon. Then it's just business as usual.
+
+      FauxtonAPI.registerExtension('sidebar:list', new Views.IndexMenu({}));
+      FauxtonAPI.registerExtension('sidebar:links',{
+        title: "New View",
+        url: "new_view"
+      });
+
+
+    */
+    //Show in the sidebar
+    FauxtonAPI.registerExtension('sidebar:list', {
+      selector:'views',
+      ddocType:'view',
+      icon: "fonticon-sidenav-map-reduce"
+    });
+
+    //Commented out since they are just POCs
+    // FauxtonAPI.registerExtension('sidebar:list', {
+    //   selector:'list',
+    //   icon: "fonticon-sidenav-list-function"
+    // });
+    // FauxtonAPI.registerExtension('sidebar:list', {
+    //   selector:'show',
+    //   icon: "fonticon-sidenav-show-function"
+    // });
+
+
+    /* show in the add new menu dropdown */
+    FauxtonAPI.registerExtension('sidebar:links', {
+      title: "Secondary View",
+      url: "new_view"
+    });
+
+    //Commented out since they are just POCs
+    // FauxtonAPI.registerExtension('sidebar:links', {
+    //   title: "List Function",
+    //   url: "new_list"
+    // });
+
+    // FauxtonAPI.registerExtension('sidebar:links', {
+    //   url: 'new_show',
+    //   title: 'Show Index'
+    // });
+
+  };
+  return Routes;
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/resources.js
----------------------------------------------------------------------
diff --git a/app/addons/indexes/resources.js b/app/addons/indexes/resources.js
new file mode 100644
index 0000000..2b5fbe7
--- /dev/null
+++ b/app/addons/indexes/resources.js
@@ -0,0 +1,216 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+define([
+  "app",
+  "api",
+  "cloudant.pagingcollection"
+],
+
+function(app, FauxtonAPI, PagingCollection) {
+ var Resources = {};
+
+
+   Resources.QueryParams = (function () {
+    var _eachParams = function (params, action) {
+      // clone to avoid in-place modification
+      var result = _.clone(params);
+
+      _.each(['startkey', 'endkey', 'key'], function (key) {
+        if (_.has(result, key)) {
+          result[key] = action(result[key]);
+        }
+      });
+
+      return result;
+    };
+
+    return {
+      parse: function (params) {
+        return _eachParams(params, JSON.parse);
+      },
+
+      stringify: function (params) {
+        return _eachParams(params, JSON.stringify);
+      }
+    };
+  })();
+
+  Resources.ViewRow = FauxtonAPI.Model.extend({
+    // this is a hack so that backbone.collections doesn't group
+    // these by id and reduce the number of items returned.
+    idAttribute: "_id",
+
+    docType: function() {
+      if (!this.id) return "reduction";
+
+      return this.id.match(/^_design/) ? "design doc" : "doc";
+    },
+    documentation: function(){
+      return "docs";
+    },
+    url: function(context) {
+      return this.collection.database.url(context) + "/" + this.safeID();
+    },
+
+    isEditable: function() {
+      return this.docType() != "reduction";
+    },
+    safeID: function() {
+      var id = this.id || this.get("id");
+
+      return app.utils.safeURLName(id);
+    },
+
+    prettyJSON: function() {
+      //var data = this.get("doc") ? this.get("doc") : this;
+      return JSON.stringify(this, null, "  ");
+    }
+  });
+
+
+
+  Resources.IndexCollection = PagingCollection.extend({
+    model: Resources.ViewRow,
+    documentation: function(){
+      return "docs";
+    },
+    initialize: function(_models, options) {
+      this.database = options.database;
+      this.params = _.extend({limit: 20, reduce: false}, options.params);
+
+      this.idxType = "_view";
+      this.view = options.view;
+      this.design = options.design.replace('_design/','');
+      this.perPageLimit = options.perPageLimit || 20;
+
+      if (!this.params.limit) {
+        this.params.limit = this.perPageLimit;
+      }
+    },
+
+    urlRef: function(context, params) {
+      var query = "";
+
+      if (params) {
+        if (!_.isEmpty(params)) {
+          query = "?" + $.param(params);
+        } else {
+          query = '';
+        }
+      } else if (this.params) {
+        var parsedParam = Resources.QueryParams.stringify(this.params);
+        query = "?" + $.param(parsedParam);
+      }
+
+      var startOfUrl = app.host;
+      if (context === 'app') {
+        startOfUrl = 'database';
+      } else if (context === "apiurl"){
+        startOfUrl = window.location.origin;
+      }
+      var design = app.utils.safeURLName(this.design),
+          view = app.utils.safeURLName(this.view);
+
+      var url = [startOfUrl, this.database.safeID(), "_design", design, this.idxType, view];
+      return url.join("/") + query;
+    },
+
+    url: function () {
+      return this.urlRef.apply(this, arguments);
+    },
+
+    totalRows: function() {
+      if (this.params.reduce) { return "unknown_reduce";}
+
+      return this.viewMeta.total_rows || "unknown";
+    },
+
+    updateSeq: function() {
+      return this.viewMeta.update_seq || false;
+    },
+
+    simple: function () {
+      var docs = this.map(function (item) {
+        return {
+          _id: item.id,
+          key: item.get('key'),
+          value: item.get('value')
+        };
+      });
+
+      return new Resources.IndexCollection(docs, {
+        database: this.database,
+        params: this.params,
+        view: this.view,
+        design: this.design
+      });
+    },
+
+    parse: function(resp) {
+      var rows = resp.rows;
+      this.endTime = new Date().getTime();
+      this.requestDuration = (this.endTime - this.startTime);
+
+      return PagingCollection.prototype.parse.apply(this, arguments);
+    },
+
+    buildAllDocs: function(){
+      this.fetch();
+    },
+
+    // We implement our own fetch to store the starttime so we that
+    // we can get the request duration
+    fetch: function () {
+      this.startTime = new Date().getTime();
+      return PagingCollection.prototype.fetch.call(this);
+    },
+
+    allDocs: function(){
+      return this.models;
+    },
+
+    // This is taken from futon.browse.js $.timeString
+    requestDurationInString: function () {
+      var ms, sec, min, h, timeString, milliseconds = this.requestDuration;
+
+      sec = Math.floor(milliseconds / 1000.0);
+      min = Math.floor(sec / 60.0);
+      sec = (sec % 60.0).toString();
+      if (sec.length < 2) {
+         sec = "0" + sec;
+      }
+
+      h = (Math.floor(min / 60.0)).toString();
+      if (h.length < 2) {
+        h = "0" + h;
+      }
+
+      min = (min % 60.0).toString();
+      if (min.length < 2) {
+        min = "0" + min;
+      }
+
+      timeString = h + ":" + min + ":" + sec;
+
+      ms = (milliseconds % 1000.0).toString();
+      while (ms.length < 3) {
+        ms = "0" + ms;
+      }
+      timeString += "." + ms;
+
+      return timeString;
+    }
+
+  });
+
+  return Resources;
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/routes-core.js
----------------------------------------------------------------------
diff --git a/app/addons/indexes/routes-core.js b/app/addons/indexes/routes-core.js
new file mode 100644
index 0000000..1277c6b
--- /dev/null
+++ b/app/addons/indexes/routes-core.js
@@ -0,0 +1,224 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+define([
+  "app",
+  "api",
+  "addons/databases/base",
+  "addons/indexes/views",
+  "addons/documents/views",
+  "addons/indexes/resources"
+],
+
+function (app, FauxtonAPI, Databases, Views, Documents, Resources) {
+
+  var CoreIndexRouteObj =  FauxtonAPI.RouteObject.extend({
+    layout: "two_pane",
+
+    initialize: function (route, masterLayout, options) {
+      _.bindAll(this);
+      var params = this.createParams(),
+      urlParams = params.urlParams,
+      docParams = params.docParams;
+
+      this.databaseName = options[0];
+
+      this.data = {
+        database: new Databases.Model({id:this.databaseName})
+      };
+
+      this.data.designDocs = new Documents.AllDocs(null, {
+        database: this.data.database,
+        paging: {
+          pageSize: 500
+        },
+        params: {
+          startkey: '_design',
+          endkey: '_design1',
+          include_docs: true,
+          limit: 500
+        }
+      });
+
+
+      /* --------------------------------------------------
+        Set up right header
+      ----------------------------------------------------*/
+
+      this.rightHeader = this.setView("#api-navbar", new Views.RightHeader({
+        database: this.data.database,
+        model: this.data.database,
+        endpoint: this.data.designDocs.urlRef("apiurl", urlParams),
+        documentation: "docs"
+      }));
+
+    },
+
+    events: {
+      "route:perPageChange": "perPageChange",
+      "route:paginate": "paginate",
+      "route:updateAllDocs": "updateAllDocsFromView"
+    },
+
+    /* --------------------------------------------------
+      Called when you change the # of items to show in the pagination footer
+    ----------------------------------------------------*/
+    perPageChange: function (perPage) {
+      // We need to restore the collection parameters to the defaults (1st page)
+      // and update the page size
+      this.perPage = perPage;
+      this.documentsView.forceRender();
+      this.documentsView.collection.pageSizeReset(perPage, {fetch: false});
+      this.setDocPerPageLimit(perPage);
+    },
+
+    /* --------------------------------------------------
+      Store the docs to show per page in local storage
+    ----------------------------------------------------*/
+    setDocPerPageLimit: function (perPage) {
+      window.localStorage.setItem('fauxton:perpage', perPage);
+    },
+
+    /* --------------------------------------------------
+      Triggers when you hit the paginate forward and backwards buttons
+    ----------------------------------------------------*/
+
+    paginate: function (options) {
+      var collection = this.documentsView.collection;
+
+      this.documentsView.forceRender();
+      collection.paging.pageSize = options.perPage;
+      var promise = collection[options.direction]({fetch: false});
+    },
+
+    /* --------------------------------------------------
+     Get Design doc info
+    ----------------------------------------------------*/
+    ddocInfo: function (designDoc, designDocs, view) {
+      return {
+        id: "_design/" + designDoc,
+        currView: view,
+        designDocs: designDocs
+      };
+    },
+
+    /* --------------------------------------------------
+      URL params from Advanced/ Query options
+    ----------------------------------------------------*/
+    createParams: function (options) {
+      var urlParams = app.getParams(options);
+      var params = Documents.QueryParams.parse(urlParams);
+
+      return {
+        urlParams: urlParams,
+        docParams: _.extend(params, {limit: this.getDocPerPageLimit(params, 20)})
+      };
+    },
+
+    /* --------------------------------------------------
+      determines how many docs to display for the request
+    ----------------------------------------------------*/
+    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 parseInt(storedPerPage, 10);
+      } else {
+        return parseInt(urlParams.limit, 10);
+      }
+    },
+
+    establish: function () {
+      return this.data.designDocs.fetch({reset: true});
+    },
+
+
+    /* --------------------------------------------------
+        Reload preview docs
+    -----------------------------------------------------*/
+    updateAllDocsFromView: function (event) {
+      var view = event.view,
+          params = this.createParams(),
+          urlParams = params.urlParams,
+          docParams = params.docParams,
+          ddoc = event.ddoc,
+          pageSize,
+          collection;
+
+      var defaultPageSize = _.isUndefined(this.documentsView) ? 20 : this.documentsView.perPage();
+      docParams.limit = pageSize = this.getDocPerPageLimit(urlParams, defaultPageSize);
+
+      if (event.allDocs) {
+        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;
+        collection.paging.pageSize = pageSize;
+
+      } else {
+        collection = this.data.indexedDocs = new Resources.IndexCollection(null, {
+          database: this.data.database,
+          design: ddoc,
+          view: view,
+          params: docParams,
+          paging: {
+            pageSize: pageSize
+          }
+        });
+
+        if (!this.documentsView) {
+          this.documentsView = this.createViewDocumentsView({
+            designDoc: ddoc,
+            docParams: docParams,
+            urlParams: urlParams,
+            database: this.data.database,
+            indexedDocs: this.indexedDocs,
+            designDocs: this.data.designDocs,
+            view: view
+          });
+        }
+      }
+
+      this.documentsView.setCollection(collection);
+      this.documentsView.setParams(docParams, urlParams);
+
+      this.documentsView.forceRender();
+    },
+
+    /* --------------------------------------------------
+      Docs that are returned from a view
+    ----------------------------------------------------*/
+    createViewDocumentsView: function (options) {
+      return this.setView("#right-content", new Documents.Views.AllDocsList({
+        database: options.database,
+        collection: options.indexedDocs,
+        nestedView: Views.Row,
+        viewList: true,
+        ddocInfo: this.ddocInfo(options.designDoc, options.designDocs, options.view),
+        docParams: options.docParams,
+        params: options.urlParams
+      }));
+    }
+  });
+
+  return CoreIndexRouteObj;
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/routes-list.js
----------------------------------------------------------------------
diff --git a/app/addons/indexes/routes-list.js b/app/addons/indexes/routes-list.js
new file mode 100644
index 0000000..437cef6
--- /dev/null
+++ b/app/addons/indexes/routes-list.js
@@ -0,0 +1,108 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+define([
+  "app",
+  "api",
+  "addons/databases/base",
+  "addons/indexes/views",
+  "addons/documents/views",
+  "addons/indexes/resources",
+  "addons/indexes/routes-core",
+  "addons/fauxton/components"
+],
+
+function (app, FauxtonAPI, Databases, Views, Documents, Resources, RouteCore, Components) {
+
+  var ListIndexes = RouteCore.extend({
+    routes: {
+      "database/:database/_design/:ddoc/_lists/:fn": {
+        route: "tempFn",
+        roles: ['_admin']
+      },
+      "database/:database/new_list": "newListsEditor",
+      "database/:database/new_list/:designDoc": "newListsEditor"
+    },
+
+    apiUrl: function() {
+      //TODO: Hook up proper API urls
+      return '';
+    },
+
+    newListsEditor: function (database, designDoc) {
+      var params = app.getParams();
+
+      /* --------------------------------------------------
+        Insert View Editor for new list
+      ----------------------------------------------------*/
+      this.setView("#left-content", new Views.ListEditor({
+        model: this.data.database,
+        currentddoc: designDoc ? "_design/"+designDoc : "",
+        ddocs: this.data.designDocs,
+        params: params,
+        database: this.data.database,
+        newView: true
+      }));
+      /* --------------------------------------------------
+        Insert Preview Screen View
+      ----------------------------------------------------*/
+      this.setView("#right-content", new Views.PreviewScreen({}));
+
+      /* --------------------------------------------------
+        Set up & Insert breadcrumb header
+      ----------------------------------------------------*/
+      var crumbs = [
+        {"name": "", "className": "fonticon-left-open", "link": Databases.databaseUrl(this.data.database)},
+        {"name": "Create a List Index", "link": Databases.databaseUrl(this.data.database)}
+      ];
+      this.leftheader = this.setView("#breadcrumbs", new Components.LeftHeader({
+        crumbs: crumbs
+      }));
+    },
+    tempFn:  function(databaseName, ddoc, view){
+      /* --------------------------------------------------
+        Set up breadcrumb header
+      ----------------------------------------------------*/
+      var crumbs = [
+        {"name": "", "className": "fonticon-left-open", "link": Databases.databaseUrl(this.data.database)},
+        {"name": view, "link": Databases.databaseUrl(this.data.database)}
+      ];
+
+      var dropdown = [{
+        links: [{
+          title: 'Duplicate Index',
+          icon: 'fonticon-documents'
+        },{
+          title: 'Delete',
+          icon: 'fonticon-trash'
+        }]
+      }];
+
+      this.leftheader = this.setView("#breadcrumbs", new Components.LeftHeader({
+        crumbs: crumbs,
+        dropdownMenu: dropdown
+      }));
+
+
+      /* --------------------------------------------------
+        Insert List Editor
+      ----------------------------------------------------*/
+      this.setView("#left-content", new Views.ListEditor({}));
+
+      /* --------------------------------------------------
+        Insert Preview Screen View
+      ----------------------------------------------------*/
+      this.setView("#right-content", new Views.PreviewScreen({}));
+    }
+  });
+
+  return ListIndexes;
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/routes-show.js
----------------------------------------------------------------------
diff --git a/app/addons/indexes/routes-show.js b/app/addons/indexes/routes-show.js
new file mode 100644
index 0000000..73f52d1
--- /dev/null
+++ b/app/addons/indexes/routes-show.js
@@ -0,0 +1,103 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+define([
+  "app",
+  "api",
+  "addons/databases/base",
+  "addons/indexes/views",
+  "addons/documents/views",
+  "addons/indexes/resources",
+  "addons/indexes/routes-core",
+   "addons/fauxton/components"
+],
+
+function (app, FauxtonAPI, Databases, Views, Documents, Resources, RouteCore, Components) {
+
+  var ShowIndexes = RouteCore.extend({
+    routes: {
+      "database/:database/_design/:ddoc/_show/:fn": {
+        route: "tempFn",
+        roles: ['_admin']
+      },
+      "database/:database/new_show": "newShowEditor",
+      "database/:database/new_show/:designDoc": "newShowEditor"
+    },
+
+    newShowEditor: function (database, designDoc) {
+      var params = app.getParams();
+      /* --------------------------------------------------
+        Insert View Editor for new view
+      ----------------------------------------------------*/
+      this.setView("#left-content", new Views.ShowEditor({
+        model: this.data.database,
+        currentddoc: designDoc ? "_design/"+designDoc : "",
+        ddocs: this.data.designDocs,
+        params: params,
+        database: this.data.database,
+        newView: true
+      }));
+
+      /* --------------------------------------------------
+        Insert Preview Screen View
+      ----------------------------------------------------*/
+      this.setView("#right-content", new Views.PreviewScreen({}));
+
+      /* --------------------------------------------------
+        Set up & Insert breadcrumb header
+      ----------------------------------------------------*/
+      var crumbs = [
+        {"name": "", "className": "fonticon-left-open", "link": Databases.databaseUrl(this.data.database)},
+        {"name": "Create a Show Index", "link": Databases.databaseUrl(this.data.database)}
+      ];
+      this.leftheader = this.setView("#breadcrumbs", new Components.LeftHeader({
+        crumbs: crumbs
+      }));
+    },
+    tempFn:  function(databaseName, ddoc, view){
+      /* --------------------------------------------------
+        Set up breadcrumb header
+      ----------------------------------------------------*/
+      var crumbs = [
+        {"name": "", "className": "fonticon-left-open", "link": Databases.databaseUrl(this.data.database)},
+        {"name": view, "link": Databases.databaseUrl(this.data.database)}
+      ];
+
+      var dropdown = [{
+        links: [{
+          title: 'Duplicate Index',
+          icon: 'fonticon-documents'
+        },{
+          title: 'Delete',
+          icon: 'fonticon-trash'
+        }]
+      }];
+
+      this.leftheader = this.setView("#breadcrumbs", new Components.LeftHeader({
+        crumbs: crumbs,
+        dropdownMenu: dropdown
+      }));
+
+
+      /* --------------------------------------------------
+        Insert Show Editor
+      ----------------------------------------------------*/
+      this.setView("#left-content", new Views.ListEditor({}));
+
+      /* --------------------------------------------------
+        Insert Preview Screen View
+      ----------------------------------------------------*/
+      this.setView("#right-content", new Views.PreviewScreen({}));
+    }
+  });
+
+  return ShowIndexes;
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/routes-viewindexes.js
----------------------------------------------------------------------
diff --git a/app/addons/indexes/routes-viewindexes.js b/app/addons/indexes/routes-viewindexes.js
new file mode 100644
index 0000000..663e0e8
--- /dev/null
+++ b/app/addons/indexes/routes-viewindexes.js
@@ -0,0 +1,144 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+define([
+  "app",
+  "api",
+  "addons/databases/base",
+  "addons/indexes/views",
+  "addons/documents/views",
+  "addons/indexes/resources",
+  "addons/indexes/routes-core",
+  "addons/fauxton/components"
+],
+
+function (app, FauxtonAPI, Databases, Views, Documents, Resources, RouteCore, Components) {
+
+  var ViewIndexes = RouteCore.extend({
+    routes: {
+      "database/:database/_design/:ddoc/_view/:view": {
+        route: "viewFn",
+        roles: ['_admin']
+      },
+      "database/:database/new_view": "newViewEditor",
+      "database/:database/new_view/:designDoc": "newViewEditor"
+    },
+
+    newViewEditor: function (database, designDoc) {
+      var params = app.getParams();
+      /* --------------------------------------------------
+        remove right header
+      ----------------------------------------------------*/
+      this.rightHeader && this.rightHeader.remove();
+
+      /* --------------------------------------------------
+        Insert Preview Screen View
+      ----------------------------------------------------*/
+      this.setView("#right-content", new Views.PreviewScreen({}));
+
+      /* --------------------------------------------------
+        Insert View Editor for new view
+      ----------------------------------------------------*/
+      this.viewEditor = this.setView("#left-content", new Views.ViewEditor({
+        model: this.data.database,
+        currentddoc: designDoc ? "_design/"+designDoc : "",
+        ddocs: this.data.designDocs,
+        params: params,
+        database: this.data.database,
+        newView: true
+      }));
+
+      /* --------------------------------------------------
+        Set up & Insert breadcrumb header
+      ----------------------------------------------------*/
+      var crumbs = [
+        {"name": "", "className": "fonticon-left-open", "link": Databases.databaseUrl(this.data.database)},
+        {"name": "Create a View Index", "link": Databases.databaseUrl(this.data.database)}
+      ];
+      this.leftheader = this.setView("#breadcrumbs", new Components.LeftHeader({
+        crumbs: crumbs
+      }));
+
+    },
+
+    viewFn: function (databaseName, ddoc, view) {
+      var params = this.createParams(),
+          urlParams = params.urlParams,
+          docParams = params.docParams,
+          decodeDdoc = decodeURIComponent(ddoc);
+          view = view.replace(/\?.*$/,'');
+
+      /* --------------------------------------------------
+        Set up breadcrumb header
+      ----------------------------------------------------*/
+      var crumbs = [
+        {"name": "", "className": "fonticon-left-open", "link": Databases.databaseUrl(this.data.database)},
+        {"name": view, "link": Databases.databaseUrl(this.data.database)}
+      ];
+
+      var dropdown = [{
+        links: [
+        {
+          title: 'Delete',
+          icon: 'fonticon-trash',
+          trigger: 'index:delete'
+        }]
+      }];
+
+      this.leftheader = this.setView("#breadcrumbs", new Components.LeftHeader({
+        crumbs: crumbs,
+        dropdownMenu: dropdown
+      }));
+
+      /* --------------------------------------------------
+        Set up Index Collection
+      ----------------------------------------------------*/
+      this.data.indexedDocs = new Resources.IndexCollection(null, {
+        database: this.data.database,
+        design: decodeDdoc,
+        view: view,
+        params: docParams,
+        paging: {
+          pageSize: this.getDocPerPageLimit(urlParams, parseInt(docParams.limit, 10))
+        }
+      });
+
+
+      /* --------------------------------------------------
+        Insert View Editor
+      ----------------------------------------------------*/
+      this.viewEditor = this.setView("#left-content", new Views.ViewEditor({
+        model: this.data.database,
+        ddocs: this.data.designDocs,
+        viewName: view,
+        params: urlParams,
+        newView: false,
+        database: this.data.database,
+        ddocInfo: this.ddocInfo(decodeDdoc, this.data.designDocs, view)
+      }));
+
+      /* --------------------------------------------------
+        Insert Docs returned from view
+      ----------------------------------------------------*/
+      this.documentsView = this.createViewDocumentsView({
+        designDoc: decodeDdoc,
+        docParams: docParams,
+        urlParams: urlParams,
+        database: this.data.database,
+        indexedDocs: this.data.indexedDocs,
+        designDocs: this.data.designDocs,
+        view: view
+      });
+    }
+  });
+
+  return ViewIndexes;
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/routes.js
----------------------------------------------------------------------
diff --git a/app/addons/indexes/routes.js b/app/addons/indexes/routes.js
new file mode 100644
index 0000000..90c9aac
--- /dev/null
+++ b/app/addons/indexes/routes.js
@@ -0,0 +1,24 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+define([
+  "app",
+  "api",
+  "addons/indexes/views",
+  "addons/indexes/routes-viewindexes",
+  "addons/indexes/routes-show",
+  "addons/indexes/routes-list",
+],
+
+function (app, FauxtonAPI, Views, ViewIndex, Show, List) {
+  Views.RouteObjects = [ViewIndex, Show, List];
+  return Views;
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/templates/design_doc_selector.html
----------------------------------------------------------------------
diff --git a/app/addons/indexes/templates/design_doc_selector.html b/app/addons/indexes/templates/design_doc_selector.html
new file mode 100644
index 0000000..2cad6af
--- /dev/null
+++ b/app/addons/indexes/templates/design_doc_selector.html
@@ -0,0 +1,34 @@
+<!--
+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.
+-->
+<label for="ddoc">Save to Design Document <a class="help-link" data-bypass="true" href="<%-getDocUrl('design_doc')%>" target="_blank"><i class="icon-question-sign"></i></a></label>
+<div class="row-fluid new-ddoc">
+  <div class="select-wrapper span5">
+    <select id="ddoc">
+      <optgroup label="Select a document">
+        <option value="new-doc" <% if(newView){ %>selected="selected" <%}%> >New document</option>
+
+        <% ddocs.each(function(ddoc) { %>
+          <% if (ddoc.id === ddocName && !newView) { %>
+          <option selected="selected" value="<%- ddoc.id %>"><%- ddoc.id %></option>
+          <% } else { %>
+          <option value="<%- ddoc.id %>"><%- ddoc.id %></option>
+          <% } %>
+        <% }); %>
+      </optgroup>
+    </select>
+    <i></i>
+  </div>
+
+  <input type="text" id="new-ddoc" class="span7" <% if(!newView){ %> style="display:none"<% } %> placeholder="Enter a design doc name" />
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/templates/header_right.html
----------------------------------------------------------------------
diff --git a/app/addons/indexes/templates/header_right.html b/app/addons/indexes/templates/header_right.html
new file mode 100644
index 0000000..2fba038
--- /dev/null
+++ b/app/addons/indexes/templates/header_right.html
@@ -0,0 +1,23 @@
+<!--
+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.
+-->
+  <!-- Select toggle-->
+  <div id="header-api-bar" class="button"></div>
+
+  <!-- Query Options-->
+  <div class="button header-query-options">
+    <!-- Insert the query options here :) -->
+    <div id="query-options"></div>
+  </div>
+
+

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/templates/index_row_docular.html
----------------------------------------------------------------------
diff --git a/app/addons/indexes/templates/index_row_docular.html b/app/addons/indexes/templates/index_row_docular.html
new file mode 100644
index 0000000..ba88d06
--- /dev/null
+++ b/app/addons/indexes/templates/index_row_docular.html
@@ -0,0 +1,35 @@
+<!--
+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.
+-->
+<% if (doc.isEditable()) { %>
+  <div class="select"><input type="checkbox"></div>
+<% } %>
+
+<div class="doc-item">
+  <header>
+    _id <span class="header-id-number">"<%=docID%>"</span>
+
+    <div class="doc-menu">
+      <a href="#<%= url %>" class="doc-menu-item icon fonticon-pencil"></a>
+      <% if (doc.isEditable()) { %>
+        <button href="#" class="btn btn-small btn-danger delete" title="Delete this document."><i class="icon icon-trash"></i></button>
+      <% } %>
+
+      <div id="view-doc-menu" class="doc-menu-item"></div>
+    </div>
+  </header>
+  <div class="doc-data">
+    <pre class="prettyprint"><%- doc.prettyJSON() %></pre>
+  </div>
+</div>
+<div class="clearfix"></div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/templates/list_editor.html
----------------------------------------------------------------------
diff --git a/app/addons/indexes/templates/list_editor.html b/app/addons/indexes/templates/list_editor.html
new file mode 100644
index 0000000..8644909
--- /dev/null
+++ b/app/addons/indexes/templates/list_editor.html
@@ -0,0 +1,18 @@
+<!--
+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.
+-->
+
+<form>
+  <label> A FORM</label>
+
+</form>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/templates/preview_screen.html
----------------------------------------------------------------------
diff --git a/app/addons/indexes/templates/preview_screen.html b/app/addons/indexes/templates/preview_screen.html
new file mode 100644
index 0000000..a7de45b
--- /dev/null
+++ b/app/addons/indexes/templates/preview_screen.html
@@ -0,0 +1,18 @@
+<!--
+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.
+-->
+
+<h3>No Index Created Yet!</h3>
+<!--
+<p> Run <a href="#/preview" class="btn btn-success save"> Preview</a> To query result</p>
+-->

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/templates/show_editor.html
----------------------------------------------------------------------
diff --git a/app/addons/indexes/templates/show_editor.html b/app/addons/indexes/templates/show_editor.html
new file mode 100644
index 0000000..ef36110
--- /dev/null
+++ b/app/addons/indexes/templates/show_editor.html
@@ -0,0 +1,41 @@
+<!--
+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.
+-->
+
+<div class="all-docs-list errors-container"></div>
+<div id="define-view" class="ddoc-alert index-form">
+  <div class="errors-container"></div>
+  <form class="form-horizontal view-query-save">
+    <div class="control-group">
+      <p>Views are the primary tool used for querying and reporting.</p>
+    </div>
+
+    <div class="control-group design-doc-group">
+    </div>
+
+
+    <div class="control-group">
+      <label for="index-name">Index name <a class="help-link" href="<%-getDocUrl('view_functions')%>" target="_blank"><i class="icon-question-sign"></i></a></label>
+      <input type="text" id="index-name" value="<%- viewName %>" placeholder="Index name" />
+    </div>
+
+
+    <div class="control-group">
+      <button class="btn btn-success save"><i class="icon fonticon-ok-circled"></i> Save &amp; Build Index</button>
+      <% if (!newView) { %>
+      <button class="btn btn-danger delete"><i class="icon fonticon-cancel-circled"></i> Delete</button>
+      <% } %>
+    </div>
+    <div class="clearfix"></div>
+  </form>
+</div>=

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/templates/view_editor.html
----------------------------------------------------------------------
diff --git a/app/addons/indexes/templates/view_editor.html b/app/addons/indexes/templates/view_editor.html
new file mode 100644
index 0000000..2b8c540
--- /dev/null
+++ b/app/addons/indexes/templates/view_editor.html
@@ -0,0 +1,80 @@
+<!--
+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.
+-->
+<div class="all-docs-list errors-container"></div>
+<div id="define-view" class="ddoc-alert index-form">
+  <div class="errors-container"></div>
+  <form class="form-horizontal view-query-save">
+    <div class="control-group">
+      <p>Views are the primary tool used for querying and reporting.</p>
+    </div>
+
+
+    <div class="control-group">
+      <label for="index-name">Database name</label>
+      <p><%- database %></p>
+    </div>
+
+
+    <div class="control-group design-doc-group">
+    </div>
+
+
+    <div class="control-group">
+      <label for="index-name">Index name <a class="help-link" href="<%-getDocUrl('view_functions')%>" target="_blank"><i class="icon-question-sign"></i></a></label>
+      <input type="text" id="index-name" value="<%- viewName %>" placeholder="Index name" />
+    </div>
+
+    <div class="control-group">
+      <label for="map-function">Map function <a class="help-link" href="<%-getDocUrl('map_functions')%>" target="_blank"><i class="icon-question-sign"></i></a></label>
+      <% if (newView) { %>
+      <div class="js-editor" id="map-function"><%= langTemplates.map %></div>
+      <% } else { %>
+      <div class="js-editor" id="map-function"><%- ddoc.get('views')[viewName].map %></div>
+      <button class="beautify beautify_map btn btn-primary btn-large hide beautify-tooltip" type="button" data-toggle="tooltip" title="Reformat your minified code to make edits to it.">beautify this code</button>
+      <% } %>
+    </div>
+
+
+    <div class="control-group">
+      <label for="reduce-function-selector">Reduce (optional) <a class="help-link" href="<%-getDocUrl('reduce_functions')%>" target="_blank"><i class="icon-question-sign"></i></a></label>
+
+      <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 function</option>
+      </select>
+    </div>
+
+    <div class="control-group reduce-function">
+      <label for="reduce-function">Custom Reduce function</label>
+      <% if (newView) { %>
+      <div class="js-editor" id="reduce-function"><%- langTemplates.reduce %></div>
+      <% } else { %>
+      <div class="js-editor" id="reduce-function"><%- ddoc.get('views')[viewName].reduce %></div>
+      <button class="beautify beautify_reduce btn btn-primary btn-large hide beautify-tooltip" type="button" data-toggle="tooltip" title="Reformat your minified code to make edits to it.">beautify this code</button>
+      <% } %>
+    </div>
+
+    <div class="control-group">
+      <button class="btn btn-success save"><i class="icon fonticon-ok-circled"></i> Save &amp; Build Index</button>
+      <% if (!newView) { %>
+      <button class="btn btn-danger delete"><i class="icon fonticon-cancel-circled"></i> Delete</button>
+      <% } %>
+    </div>
+    <div class="clearfix"></div>
+  </form>
+</div>
+

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/fc1eb5c8/app/addons/indexes/views.js
----------------------------------------------------------------------
diff --git a/app/addons/indexes/views.js b/app/addons/indexes/views.js
new file mode 100644
index 0000000..a0d5d0d
--- /dev/null
+++ b/app/addons/indexes/views.js
@@ -0,0 +1,653 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+  "app",
+
+  "api",
+  "addons/fauxton/components",
+
+  "addons/documents/resources",
+  "addons/databases/resources",
+  "addons/pouchdb/base",
+  //views
+  "addons/documents/views-advancedopts",
+  // Libs
+  "addons/fauxton/resizeColumns",
+
+  // Plugins
+  "plugins/beautify",
+  "plugins/prettify"
+],
+
+function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
+         QueryOptions, resizeColumns, beautify, prettify) {
+
+  var Views = {};
+
+
+//right header
+  Views.RightHeader = FauxtonAPI.View.extend({
+    className: "header-right",
+    template: "addons/indexes/templates/header_right",
+    initialize:function(options){
+      _.bindAll(this);
+      this.database = options.database;
+      this.title = options.title;
+      this.api = options.api;
+      this.endpoint = options.endpoint;
+      this.documentation = options.documentation;
+      FauxtonAPI.Events.on('advancedOptions:updateView', this.updateView);
+    },
+    updateApiUrl: function(api){
+      //this will update the api bar when the route changes
+      //you can find the method that updates it in components.js Components.ApiBar()
+      this.apiBar && this.apiBar.update(api);
+    },
+    beforeRender: function(){
+
+      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar({
+        endpoint: this.endpoint,
+        documentation: this.documentation
+      }));
+
+      this.advancedOptions = this.insertView('#query-options', new QueryOptions.AdvancedOptions({
+        database: this.database,
+        viewName: this.viewName,
+        ddocName: this.model.id,
+        hasReduce: this.hasReduce(),
+        showStale: true
+      }));
+    },
+
+    cleanup: function(){
+      FauxtonAPI.Events.unbind('advancedOptions:updateView');
+    },
+
+    hasReduce: function(){
+
+    },
+    updateView: function(event, paramInfo) {
+       event.preventDefault();
+
+       var errorParams = paramInfo.errorParams,
+           params = paramInfo.params;
+
+       if (_.any(errorParams)) {
+         _.map(errorParams, function(param) {
+           return FauxtonAPI.addNotification({
+             msg: "JSON Parse Error on field: "+param.name,
+             type: "error",
+             clear: true
+           });
+         });
+         FauxtonAPI.addNotification({
+           msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
+           type: "warning",
+           clear: true
+         });
+
+         return false;
+      }
+
+       var fragment = window.location.hash.replace(/\?.*$/, '');
+       if (!_.isEmpty(params)) {
+        fragment = fragment + '?' + $.param(params);
+       }
+
+       FauxtonAPI.navigate(fragment, {trigger: false});
+       FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: this.ddocID, view: this.viewName});
+    }
+  });
+
+
+
+
+  Views.PreviewScreen = FauxtonAPI.View.extend({
+    template: "addons/indexes/templates/preview_screen",
+    className: "watermark-logo"
+  });
+
+
+  Views.Row = FauxtonAPI.View.extend({
+    template: "addons/indexes/templates/index_row_docular",
+    className: "doc-row",
+    events: {
+      "click button.delete": "destroy"
+    },
+
+    destroy: function (event) {
+      event.preventDefault();
+
+      window.alert('Cannot delete a document generated from a view.');
+    },
+
+    beforeRender: function(){
+      var newLinks = [{
+        links: [{
+          title: 'JSON',
+          icon: 'fonticon-json'
+        }]
+      }];
+
+      this.insertView("#view-doc-menu", new Components.MenuDropDown({
+        icon: 'fonticon-drop-down-dots',
+        links: newLinks,
+      }));
+    },
+
+    serialize: function() {
+      return {
+        docID: this.model.get('id'),
+        doc: this.model,
+        url: this.model.url('app')
+      };
+    }
+  });
+
+
+/*
+
+  INDEX EDITORS____________________________________
+
+*/
+
+  //Index view CORE  extend this
+  Views.IndexCore = FauxtonAPI.View.extend({
+    initialize: function(options) {
+        this.newView = options.newView || false;
+        this.ddocs = options.ddocs;
+        this.params = options.params;
+        this.database = options.database;
+        this.currentDdoc = options.currentddoc;
+      if (this.newView) {
+        this.viewName = 'newView';
+      } else {
+        this.ddocID = options.ddocInfo ? options.ddocInfo.id : '';
+        this.viewName = options.viewName;
+        this.ddocInfo = new Documents.DdocInfo({_id: this.ddocID},{database: this.database});
+      }
+
+      this.showIndex = false;
+      _.bindAll(this);
+    },
+    establish: function () {
+      if (this.ddocInfo) {
+        return this.ddocInfo.fetch();
+      }
+    },
+    serialize: function() {
+      return {
+        database: this.database.get('id'),
+        ddocs: this.ddocs,
+        ddoc: this.model,
+        ddocName: this.model.id,
+        viewName: this.viewName,
+        newView: this.newView,
+        langTemplates: this.langTemplates.javascript
+      };
+    }
+  });
+
+  Views.ShowEditor = Views.IndexCore.extend({
+    template: "addons/indexes/templates/show_editor",
+    langTemplates: {
+      "javascript": {
+        map: "function(doc) {\n  emit(doc._id, 1);\n}",
+      }
+    },
+    defaultLang: "javascript"
+  });
+
+  Views.ListEditor = Views.IndexCore.extend({
+    template: "addons/indexes/templates/list_editor",
+    langTemplates: {
+      "javascript": {
+        map: "function(doc) {\n  emit(doc._id, 1);\n}",
+      }
+    },
+    defaultLang: "javascript"
+  });
+
+
+  Views.ViewEditor = FauxtonAPI.View.extend({
+    template: "addons/indexes/templates/view_editor",
+    builtinReduces: ['_sum', '_count', '_stats'],
+
+    events: {
+      "click button.save": "saveView",
+      "click button.delete": "deleteView",
+      "change select#reduce-function-selector": "updateReduce",
+      "click #db-views-tabs-nav": 'toggleIndexNav',
+      "click .beautify_map":  "beautifyCode",
+      "click .beautify_reduce":  "beautifyCode"
+    },
+
+    langTemplates: {
+      "javascript": {
+        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}"
+      }
+    },
+
+    defaultLang: "javascript",
+
+    initialize: function(options) {
+      this.newView = options.newView || false;
+      this.ddocs = options.ddocs;
+      this.params = options.params;
+      this.database = options.database;
+      this.currentDdoc = options.currentddoc;
+      if (this.newView) {
+        this.viewName = 'newView';
+      } else {
+        this.ddocID = options.ddocInfo.id;
+        this.viewName = options.viewName;
+        this.ddocInfo = new Documents.DdocInfo({_id: this.ddocID},{database: this.database});
+      }
+
+      this.showIndex = false;
+      _.bindAll(this);
+
+      FauxtonAPI.Events.on('index:delete', this.deleteEvent);
+    },
+
+    establish: function () {
+      if (this.ddocInfo) {
+        return this.ddocInfo.fetch();
+      }
+    },
+
+    updateValues: function() {
+      var notification;
+      if (this.model.changedAttributes()) {
+        notification = FauxtonAPI.addNotification({
+          msg: "Document saved successfully.",
+          type: "success",
+          clear: true
+        });
+        this.editor.setValue(this.model.prettyJSON());
+      }
+    },
+
+    updateReduce: function(event) {
+      var $ele = $("#reduce-function-selector");
+      var $reduceContainer = $(".control-group.reduce-function");
+      if ($ele.val() == "CUSTOM") {
+        this.createReduceEditor();
+        this.reduceEditor.setValue(this.langTemplates.javascript.reduce);
+        $reduceContainer.show();
+      } else {
+        $reduceContainer.hide();
+      }
+    },
+
+    deleteEvent: function(){
+      this.deleteView();
+    },
+
+    deleteView: function (event) {
+      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.safeID() + '/_all_docs?limit=' + Databases.DocLimit);
+        FauxtonAPI.triggerRouteEvent('reloadDesignDocs');
+      });
+    },
+
+    saveView: function(event) {
+      var json, notification,
+      that = this;
+
+      if (event) { event.preventDefault();}
+
+      $('#dashboard-content').scrollTop(0); //scroll up
+      //check if the code is valid & the inputs are filled out
+      if (this.hasValidCode() && this.$('#new-ddoc:visible').val() !=="") {
+        var mapVal = this.mapEditor.getValue(),
+        reduceVal = this.reduceVal(),
+        viewName = this.$('#index-name').val(),
+        ddoc = this.getCurrentDesignDoc(),
+        ddocName = ddoc.id;
+        this.viewNameChange = false;
+
+
+        if (this.viewName !== viewName) {
+          ddoc.removeDdocView(this.viewName);
+          this.viewName = viewName;
+          this.viewNameChange = true;
+        }
+
+        notification = FauxtonAPI.addNotification({
+          msg: "Saving document.",
+          clear: true
+        });
+
+        ddoc.setDdocView(viewName, mapVal, reduceVal);
+
+        ddoc.save().then(function () {
+          //on success
+          that.afterSave(ddoc, viewName, ddocName);
+        },
+        function(xhr) {
+          //on failure
+          var responseText = JSON.parse(xhr.responseText).reason;
+          notification = FauxtonAPI.addNotification({
+            msg: "Save failed: " + responseText,
+            type: "error",
+            clear: true
+          });
+        });
+      } else {
+        //if nothing is filled out give an error message
+        var errormessage = (this.$('#new-ddoc:visible').val() ==="")?"Enter a design doc name":"Please fix the Javascript errors and try again.";
+        notification = FauxtonAPI.addNotification({
+          msg: errormessage,
+          type: "error",
+          clear: true
+        });
+      }
+    },
+
+    afterSave: function(ddoc, viewName, ddocName){
+      var reduceVal = this.reduceVal();
+      //add ddoc to the collection
+      this.ddocs.add(ddoc);
+
+      //trigger the EditSaved function on the map editor & reduce editor
+      this.mapEditor.editSaved();
+      this.reduceEditor && this.reduceEditor.editSaved();
+
+
+      //show a notification
+      FauxtonAPI.addNotification({
+        msg: "View has been saved.",
+        type: "success",
+        clear: true
+      });
+
+
+      //if it's new or the name changed (aka created a new doc)
+      if (this.newView || this.viewNameChange) {
+        var fragment = '/database/' + this.database.safeID() +'/' + ddoc.safeID() + '/_view/' + app.utils.safeURLName(viewName);
+
+        FauxtonAPI.navigate(fragment, {trigger: false});
+        this.newView = false;
+        this.ddocID = ddoc.safeID();
+        this.viewName = viewName;
+        this.ddocInfo = ddoc;
+        this.showIndex = true;
+        this.render();
+      }
+
+      // TODO:// this should change to a trigger because we shouldn't define advanced options in this view
+      if (this.reduceFunStr !== reduceVal) {
+        this.reduceFunStr = reduceVal;
+       // this.advancedOptions.renderOnUpdatehasReduce(this.hasReduce());
+      }
+
+      // Route Event will reload the right content
+      FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: ddocName, view: viewName});
+    },
+
+    getCurrentDesignDoc: function () {
+      return this.designDocSelector.getCurrentDesignDoc();
+    },
+
+    isCustomReduceEnabled: function() {
+      return $("#reduce-function-selector").val() == "CUSTOM";
+    },
+
+    mapVal: function () {
+      if (this.mapEditor) {
+        return this.mapEditor.getValue();
+      }
+
+      return this.$('#map-function').text();
+    },
+
+    reduceVal: function() {
+      var reduceOption = this.$('#reduce-function-selector :selected').val(),
+      reduceVal = "";
+
+      if (reduceOption === 'CUSTOM') {
+        if (!this.reduceEditor) { this.createReduceEditor(); }
+        reduceVal = this.reduceEditor.getValue();
+      } else if ( reduceOption !== 'NONE') {
+        reduceVal = reduceOption;
+      }
+
+      return reduceVal;
+    },
+
+
+    hasValidCode: function() {
+      return _.every(["mapEditor", "reduceEditor"], function(editorName) {
+        var editor = this[editorName];
+        if (editorName === "reduceEditor" && ! this.isCustomReduceEnabled()) {
+          return true;
+        }
+        return editor.hadValidCode();
+      }, this);
+    },
+
+
+    serialize: function() {
+      return {
+        database: this.database.get('id'),
+        ddocs: this.ddocs,
+        ddoc: this.model,
+        ddocName: this.model.id,
+        viewName: this.viewName,
+        reduceFunStr: this.reduceFunStr,
+        isCustomReduce: this.hasCustomReduce(),
+        newView: this.newView,
+        langTemplates: this.langTemplates.javascript
+      };
+    },
+
+    hasCustomReduce: function() {
+      return this.reduceFunStr && ! _.contains(this.builtinReduces, this.reduceFunStr);
+    },
+
+    hasReduce: function () {
+      return this.reduceFunStr || false;
+    },
+
+    createReduceEditor: function () {
+      if (this.reduceEditor) {
+        this.reduceEditor.remove();
+      }
+
+      this.reduceEditor = new Components.Editor({
+        editorId: "reduce-function",
+        mode: "javascript",
+        couchJSHINT: true
+      });
+      this.reduceEditor.render();
+
+      if (this.reduceEditor.getLines() === 1){
+        this.$('.beautify_reduce').removeClass("hide");
+        $('.beautify-tooltip').tooltip();
+      }
+    },
+    beforeRender: function () {
+
+      if (this.newView) {
+        this.reduceFunStr = '';
+        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 {
+        var ddocDecode = decodeURIComponent(this.ddocID);
+        this.model = this.ddocs.get(this.ddocID).dDocModel();
+        this.reduceFunStr = this.model.viewHasReduce(this.viewName);
+      }
+
+      var viewFilters = FauxtonAPI.getExtensions('sidebar:viewFilters'),
+          filteredModels = this.ddocs.models,
+          designDocs = this.ddocs.clone();
+
+      if (!_.isEmpty(viewFilters)) {
+        _.each(viewFilters, function (filter) {
+          filteredModels = _.filter(filteredModels, filter);
+        });
+        designDocs.reset(filteredModels, {silent: true});
+      }
+
+      this.designDocSelector = this.setView('.design-doc-group', new Views.DesignDocSelector({
+        collection: designDocs,
+        newView: this.newView,
+        ddocName: this.currentDdoc || this.model.id,
+        database: this.database
+      }));
+
+    },
+
+    afterRender: function() {
+      //TODO: have this happen on a trigger once we move advanced options to the header
+      // if (this.params && !this.newView) {
+      //   this.advancedOptions.updateFromParams(this.params);
+      // }
+
+      this.designDocSelector.updateDesignDoc();
+      this.showEditors();
+      this.showIndex = false;
+
+    },
+
+    showEditors: function () {
+      this.mapEditor = new Components.Editor({
+        editorId: "map-function",
+        mode: "javascript",
+        couchJSHINT: true
+      });
+      this.mapEditor.render();
+
+      if (this.hasCustomReduce()) {
+        this.createReduceEditor();
+      } else {
+        $(".control-group.reduce-function").hide();
+      }
+
+      if (this.newView) {
+        this.mapEditor.setValue(this.langTemplates[this.defaultLang].map);
+        //Use a built in view by default
+        //this.reduceEditor.setValue(this.langTemplates[this.defaultLang].reduce);
+      }
+
+      this.mapEditor.editSaved();
+      this.reduceEditor && this.reduceEditor.editSaved();
+
+      if (this.mapEditor.getLines() === 1){
+        this.$('.beautify_map').removeClass("hide");
+        $('.beautify-tooltip').tooltip();
+      }
+    },
+    beautifyCode: function(e){
+      e.preventDefault();
+      var targetEditor = $(e.currentTarget).hasClass('beautify_reduce')?this.reduceEditor:this.mapEditor;
+      var beautifiedCode = beautify(targetEditor.getValue());
+      targetEditor.setValue(beautifiedCode);
+    },
+    cleanup: function () {
+      FauxtonAPI.Events.unbind('index:delete');
+      this.mapEditor && this.mapEditor.remove();
+      this.reduceEditor && this.reduceEditor.remove();
+    }
+  });
+
+  Views.DesignDocSelector = FauxtonAPI.View.extend({
+    template: "addons/indexes/templates/design_doc_selector",
+
+    events: {
+      "change select#ddoc": "updateDesignDoc"
+    },
+
+    initialize: function (options) {
+      this.newView = options.newView;
+      this.ddocName = options.ddocName;
+      this.database = options.database;
+      this.listenTo(this.collection, 'add', this.ddocAdded);
+      this.DocModel = options.DocModel || Documents.Doc;
+    },
+
+    ddocAdded: function (ddoc) {
+      this.ddocName = ddoc.id;
+      this.render();
+    },
+
+    serialize: function () {
+      return {
+        newView: this.newView,
+        ddocName: this.ddocName,
+        ddocs: this.collection
+      };
+    },
+
+    updateDesignDoc: function () {
+      if (this.newDesignDoc()) {
+        this.$('#new-ddoc').show();
+      } else {
+        this.$('#new-ddoc').hide();
+      }
+    },
+
+    newDesignDoc: function () {
+
+      return this.$('#ddoc').val() === 'new-doc';
+    },
+
+    newDocValidation: function(){
+      return this.newDesignDoc() && this.$('#new-ddoc').val()==="";
+    },
+    getCurrentDesignDoc: function () {
+      if (this.newDesignDoc()) {
+        var doc = {
+          _id: '_design/' + this.$('#new-ddoc').val(),
+          views: {},
+          language: "javascript"
+        };
+        var ddoc = new this.DocModel(doc, {database: this.database});
+        //this.collection.add(ddoc);
+        return ddoc;
+      } else if ( !this.newDesignDoc() ) {
+        var ddocName = this.$('#ddoc').val();
+        return this.collection.find(function (ddoc) {
+          return ddoc.id === ddocName;
+        }).dDocModel();
+      }
+    }
+  });
+
+  return Views;
+});


Mime
View raw message