couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From robertkowal...@apache.org
Subject [2/2] fauxton commit: updated refs/heads/master to a9e829c
Date Tue, 19 May 2015 17:11:39 GMT
Part 2/2 for Mango:

Adds search functionality, makes the feature visible and enables
deletion of mango-created-indexes.

closes COUCHDB-2627

PR: #362
PR-URL: https://github.com/apache/couchdb-fauxton/pull/362
Reviewed-By: Michelle Phung <michellephung@gmail.com>
Reviewed-By: garren smith <garren.smith@gmail.com>
Reviewed-By: Benjamin Keen <ben.keen@gmail.com>


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

Branch: refs/heads/master
Commit: a9e829c441a58aa8eafef75f8d3819ea7bb19a0a
Parents: 30f6577
Author: Robert Kowalski <robertkowalski@apache.org>
Authored: Wed Apr 1 13:11:15 2015 +0200
Committer: Robert Kowalski <robertkowalski@apache.org>
Committed: Tue May 19 19:14:39 2015 +0200

----------------------------------------------------------------------
 .gitignore                                      |   1 +
 .../components/react-components.react.jsx       |  21 +-
 app/addons/components/tests/docSpec.react.jsx   |  16 ++
 .../documents/assets/less/view-editor.less      |   6 +
 app/addons/documents/base.js                    |  24 ++
 .../documents/index-editor/components.react.jsx |   6 +-
 .../tests/viewIndex.componentsSpec.react.jsx    |   3 +-
 app/addons/documents/index-results/actions.js   |  45 ++++
 .../index-results.components.react.jsx          |  12 +-
 app/addons/documents/index-results/stores.js    | 132 +++++++++--
 .../tests/index-results.actionsSpec.js          |   1 +
 .../index-results.componentsSpec.react.jsx      |  11 +-
 .../tests/index-results.storesSpec.js           | 200 +++++++++-------
 app/addons/documents/mango/mango.actions.js     |  79 ++++++-
 app/addons/documents/mango/mango.actiontypes.js |   5 +
 .../documents/mango/mango.components.react.jsx  | 236 +++++++++++++++----
 app/addons/documents/mango/mango.helper.js      |  44 ++++
 app/addons/documents/mango/mango.stores.js      | 123 +++++++++-
 .../mango/tests/mango.componentsSpec.react.jsx  | 173 +++++++++++---
 .../documents/mango/tests/mango.storesSpec.js   | 101 ++++++++
 app/addons/documents/resources.js               | 209 +++++++++++++++-
 app/addons/documents/routes-documents.js        |   7 +-
 app/addons/documents/routes-index-editor.js     |   6 +-
 app/addons/documents/routes-mango.js            | 141 ++++++-----
 app/addons/documents/routes.js                  |   3 +-
 app/addons/documents/shared-resources.js        |  28 +--
 app/addons/documents/shared-views.js            |  49 ++--
 app/addons/documents/templates/sidebar.html     |  39 ++-
 .../documents/tests/nightwatch/mangoIndex.js    |  49 +++-
 .../tests/nightwatch/mangoIndexList.js          |  31 ---
 .../documents/tests/nightwatch/mangoQuery.js    |  44 ++++
 app/addons/documents/tests/resourcesSpec.js     |  58 ++++-
 app/addons/documents/views-mango.js             |  66 ------
 app/addons/fauxton/components.js                |   2 +
 app/constants.js                                |   1 +
 i18n.json.default                               |   7 +-
 .../custom-commands/populateDatabase.js         |   8 +-
 37 files changed, 1531 insertions(+), 456 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index b56fe9d..b3517fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@ app/addons/*
 !app/addons/styletests
 !app/addons/cors
 settings.json*
+i18n.json
 !settings.json.default
 !assets/js/plugins/zeroclipboard/ZeroClipboard.swf
 test/test.config.js

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/components/react-components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/react-components.react.jsx b/app/addons/components/react-components.react.jsx
index 6a4f680..bf8c6af 100644
--- a/app/addons/components/react-components.react.jsx
+++ b/app/addons/components/react-components.react.jsx
@@ -77,6 +77,7 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
         autoScrollEditorIntoView: true,
         setHeightWithJS: true,
         isFullPageEditor: false,
+        disableUnload: false,
         change: function () {}
       };
     },
@@ -108,14 +109,18 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
       this.editor.getSession().setMode("ace/mode/" + props.mode);
       this.editor.setTheme("ace/theme/" + props.theme);
       this.editor.setFontSize(props.fontSize);
-
+      this.editor.getSession().setUseSoftTabs(true);
     },
 
     setupEvents: function () {
+      this.editor.on('blur', _.bind(this.saveCodeChange, this));
+
+      if (this.props.disableUnload) {
+        return;
+      }
+
       $(window).on('beforeunload.editor_' + this.props.id, _.bind(this.quitWarningMsg));
       FauxtonAPI.beforeUnload('editor_' + this.props.id, _.bind(this.quitWarningMsg, this));
-
-      this.editor.on('blur', _.bind(this.saveCodeChange, this));
     },
 
     saveCodeChange: function () {
@@ -129,6 +134,10 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
     },
 
     removeEvents: function () {
+      if (this.props.disableUnload) {
+        return;
+      }
+
       $(window).off('beforeunload.editor_' + this.props.id);
       FauxtonAPI.removeBeforeUnload('editor_' + this.props.id);
     },
@@ -201,12 +210,12 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
 
     getTitleFragment: function () {
       if (!this.props.docs) {
-        return <strong>{this.props.title}</strong>;
+        return (<strong>{this.props.title}</strong>);
       }
 
       return (
         <label>
-          <strong>{this.props.title}</strong>
+          <strong>{this.props.title + ' '}</strong>
           <a
             className="help-link"
             data-bypass="true"
@@ -391,7 +400,7 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
                 {this.props.keylabel}
               </span>
               <span className="header-doc-id">
-                "{this.props.docIdentifier}"
+                {this.props.header ? '"' + this.props.header + '"' : null}
               </span>
               {this.getUrlFragment()}
               <div className="doc-item-extension-icons pull-right">{this.getExtensionIcons()}</div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/components/tests/docSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/docSpec.react.jsx b/app/addons/components/tests/docSpec.react.jsx
index 3cf82a2..c7d9d9d 100644
--- a/app/addons/components/tests/docSpec.react.jsx
+++ b/app/addons/components/tests/docSpec.react.jsx
@@ -114,6 +114,22 @@ define([
       );
       assert.equal(0, $(el.getDOMNode()).find('.doc-data').length);
     });
+
+    it('allows empty headers', function () {
+      el = TestUtils.renderIntoDocument(
+        <ReactComponents.Document header={null} isDeletable={true} checked={true} docIdentifier="foo" docContent='' />,
+        container
+      );
+      assert.equal('', $(el.getDOMNode()).find('.header-doc-id').text());
+    });
+
+    it('allows supports headers with "', function () {
+      el = TestUtils.renderIntoDocument(
+        <ReactComponents.Document header="foo" isDeletable={true} checked={true} docIdentifier="foo" docContent='' />,
+        container
+      );
+      assert.equal('"foo"', $(el.getDOMNode()).find('.header-doc-id').text());
+    });
   });
 
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/assets/less/view-editor.less
----------------------------------------------------------------------
diff --git a/app/addons/documents/assets/less/view-editor.less b/app/addons/documents/assets/less/view-editor.less
index fa2d358..05f40ba 100644
--- a/app/addons/documents/assets/less/view-editor.less
+++ b/app/addons/documents/assets/less/view-editor.less
@@ -45,6 +45,12 @@
     margin-top: 25px;
     height: 46px;
   }
+  pre.prettyprint-left {
+    padding: 5px;
+  }
+  form {
+    padding-bottom: 50px;
+  }
 }
 
 body .view-query-save .control-group {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/base.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/base.js b/app/addons/documents/base.js
index d6ac60d..ba4a892 100644
--- a/app/addons/documents/base.js
+++ b/app/addons/documents/base.js
@@ -135,6 +135,30 @@ function (app, FauxtonAPI, Documents) {
       }
 
       return 'database/' + db + '/_index' + query;
+    },
+
+    'query-server': function (db, query) {
+      if (!query) {
+        query = '';
+      }
+
+      return app.host + '/' + db + '/_find' + query;
+    },
+
+    'query-apiurl': function (db, query) {
+      if (!query) {
+        query = '';
+      }
+
+      return window.location.origin + '/' + db + '/_find' + query;
+    },
+
+    'query-app': function (db, query) {
+      if (!query) {
+        query = '';
+      }
+
+      return 'database/' + db + '/_find' + query;
     }
   });
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/index-editor/components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/components.react.jsx b/app/addons/documents/index-editor/components.react.jsx
index f943c0c..ed99757 100644
--- a/app/addons/documents/index-editor/components.react.jsx
+++ b/app/addons/documents/index-editor/components.react.jsx
@@ -386,6 +386,8 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
         );
       }
 
+      var url = FauxtonAPI.urls('allDocs', this.state.database.id, '');
+
       return (
         <div className="define-view">
           <PaddedBorderedBox>
@@ -393,7 +395,9 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
           </PaddedBorderedBox>
           <PaddedBorderedBox>
             <strong>Database</strong>
-            <div className="db-title">{this.state.database.id}</div>
+            <div className="db-title">
+              <a href={url}>this.state.database.id</a>
+            </div>
           </PaddedBorderedBox>
           <form className="form-horizontal view-query-save" onSubmit={this.saveView}>
             <DesignDocSelector />

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx b/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
index 9cac62d..5fea728 100644
--- a/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
+++ b/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
@@ -110,8 +110,7 @@ define([
 
     beforeEach(function () {
       container = document.createElement('div');
-      $('body').append('<div id="map-function"></div>');
-      $('body').append('<div id="editor"></div>');
+
       var designDoc = {
         "id": "_design/test-doc",
         "key": "_design/test-doc",

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/index-results/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/actions.js b/app/addons/documents/index-results/actions.js
index c3c10d6..d8bb584 100644
--- a/app/addons/documents/index-results/actions.js
+++ b/app/addons/documents/index-results/actions.js
@@ -71,7 +71,52 @@ function (app, FauxtonAPI, ActionTypes, Stores, HeaderStores, HeaderActions, Doc
       });
     },
 
+    newMangoResultsList: function (options) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.INDEX_RESULTS_NEW_RESULTS,
+        options: options
+      });
+    },
+
+    runMangoFindQuery: function (options) {
+      var query = JSON.parse(options.queryCode),
+          collection = indexResultsStore.getCollection();
+
+      this.clearResults();
+
+      return collection
+        .setQuery(query)
+        .fetch()
+        .then(function () {
+          this.resultsListReset();
+          this.newMangoResultsList({
+            collection: collection,
+            isListDeletable: indexResultsStore.isListDeletable(),
+            query: options.queryCode,
+            textEmptyIndex: 'No Results Found!',
+            bulkCollection: Documents.BulkDeleteDocCollection
+          });
+        }.bind(this), function (res) {
+          FauxtonAPI.addNotification({
+            msg: res.reason,
+            clear:  true,
+            type: 'error'
+          });
+
+          this.resultsListReset();
+        }.bind(this));
+    },
+
     reloadResultsList: function () {
+      if (indexResultsStore.getTypeOfIndex() === 'mango') {
+        return this.newResultsList({
+          collection: indexResultsStore.getCollection(),
+          isListDeletable: true,
+          bulkCollection: Documents.MangoBulkDeleteDocCollection,
+          typeOfIndex: 'mango'
+        });
+      }
+
       return this.newResultsList({
         collection: indexResultsStore.getCollection(),
         isListDeletable: indexResultsStore.isListDeletable()

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/index-results/index-results.components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/index-results.components.react.jsx b/app/addons/documents/index-results/index-results.components.react.jsx
index f930f61..2b5fe39 100644
--- a/app/addons/documents/index-results/index-results.components.react.jsx
+++ b/app/addons/documents/index-results/index-results.components.react.jsx
@@ -19,7 +19,7 @@ define([
   'addons/components/react-components.react',
   'addons/documents/resources',
 
-  "plugins/prettify"
+  'plugins/prettify'
 ],
 
 function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) {
@@ -54,20 +54,22 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) {
     },
 
     getDocumentList: function () {
-      return _.map(this.props.results, function (doc) {
+      var noop = function () {};
 
+      return _.map(this.props.results, function (doc, i) {
         return (
          <Components.Document
-           key={doc.id}
+           key={doc.id + i}
            doc={doc}
-           onDoubleClick={this.onDoubleClick}
+           onDoubleClick={this.props.isEditable ? this.onDoubleClick : noop}
            keylabel={doc.keylabel}
            docContent={doc.content}
            checked={this.props.isSelected(doc.id)}
+           header={doc.header}
            docChecked={this.props.docChecked}
            isDeletable={doc.isDeletable}
            docIdentifier={doc.id} >
-           {this.getUrlFragment('#' + doc.url)}
+           {doc.url ? this.getUrlFragment('#' + doc.url) : doc.url}
          </Components.Document>
        );
       }, this);

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/index-results/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/stores.js b/app/addons/documents/index-results/stores.js
index 3d6cb1f..37989f1 100644
--- a/app/addons/documents/index-results/stores.js
+++ b/app/addons/documents/index-results/stores.js
@@ -14,10 +14,11 @@ define([
   'api',
   'addons/documents/index-results/actiontypes',
   'addons/documents/header/header.actiontypes',
-  "addons/documents/resources"
+  'addons/documents/resources',
+  'addons/documents/mango/mango.helper'
 ],
 
-function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
+function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents, MangoHelper) {
   var Stores = {};
 
   /*TODO:
@@ -33,6 +34,9 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
       this.clearCollapsedDocs();
       this._isLoading = false;
       this._textEmptyIndex = 'No Index Created Yet!';
+      this._typeOfIndex = 'view';
+      this._lastQuery = null;
+      this._bulkDeleteDocCollection = null;
     },
 
     clearSelectedItems: function () {
@@ -49,9 +53,27 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
       this.clearSelectedItems();
       this.clearCollapsedDocs();
 
+      this._bulkDeleteDocCollection = options.bulkCollection;
+
       if (options.textEmptyIndex) {
         this._textEmptyIndex = options.textEmptyIndex;
       }
+
+      if (options.typeOfIndex) {
+        this._typeOfIndex = options.typeOfIndex;
+      }
+
+      if (options.query) {
+        this._lastQuery = options.query;
+      }
+    },
+
+    getTypeOfIndex: function () {
+      return this._typeOfIndex;
+    },
+
+    getLastQuery: function () {
+      return this._lastQuery;
     },
 
     isEditable: function (doc) {
@@ -59,6 +81,14 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
         return false;
       }
 
+      if (doc && this.isGhostDoc(doc)) {
+        return false;
+      }
+
+      if (doc && !doc.get('_id')) {
+        return false;
+      }
+
       if (!this._collection.isEditable) {
         return false;
       }
@@ -66,7 +96,17 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
       return this._collection.isEditable();
     },
 
+    isGhostDoc: function (doc) {
+      // ghost docs are empty results where all properties were
+      // filtered away by mango
+      return !doc || !doc.attributes || !Object.keys(doc.attributes).length;
+    },
+
     isDeletable: function (doc) {
+      if (this.isGhostDoc(doc)) {
+        return false;
+      }
+
       return doc.isDeletable();
     },
 
@@ -80,7 +120,8 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
 
     getDocContent: function (originalDoc) {
       var doc = originalDoc.toJSON();
-      return (this.isCollapsed(doc._id)) ? '' : JSON.stringify(doc, null, ' ');
+
+      return this.isCollapsed(doc._id) ? '' : JSON.stringify(doc, null, ' ');
     },
 
     getDocId: function (doc) {
@@ -96,17 +137,68 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
       return '';
     },
 
-    getResults: function () {
-      return this._collection.map(function (doc) {
+    getMangoDocContent: function (originalDoc) {
+      var doc = originalDoc.toJSON();
+
+      delete doc.ddoc;
+      delete doc.name;
+
+      return this.isCollapsed(originalDoc.id) ? '' : JSON.stringify(doc, null, ' ');
+    },
+
+    getMangoDoc: function (doc, index) {
+      var selector,
+          header;
+
+      if (doc.get('def') && doc.get('def').fields) {
+
+        header = MangoHelper.getIndexName(doc);
+
         return {
-          content: this.getDocContent(doc),
-          id: this.getDocId(doc),
-          keylabel: doc.isFromView() ? 'key' : 'id',
+          content: this.getMangoDocContent(doc),
+          header: header,
+          id: doc.getId(),
+          keylabel: '',
           url: doc.isFromView() ? doc.url('app') : doc.url('web-index'),
           isDeletable: this.isDeletable(doc),
           isEditable: this.isEditable(doc)
         };
-      }, this);
+      }
+
+      // we filtered away our content with the fields param
+      return {
+        content: ' ',
+        header: header,
+        id: this.getDocId(doc) + index,
+        keylabel: '',
+        url: this.isEditable(doc) ? doc.url('app') : null,
+        isDeletable: this.isDeletable(doc),
+        isEditable: this.isEditable(doc)
+      };
+
+    },
+
+    getResults: function () {
+      function filterOutGeneratedMangoDocs (doc) {
+        return doc.get('language') !== 'query';
+      }
+
+      return this._collection
+        .filter(filterOutGeneratedMangoDocs)
+        .map(function (doc, i) {
+          if (doc.get('def') || this.isGhostDoc(doc)) {
+            return this.getMangoDoc(doc, i);
+          }
+          return {
+            content: this.getDocContent(doc),
+            id: this.getDocId(doc),
+            header: this.getDocId(doc),
+            keylabel: doc.isFromView() ? 'key' : 'id',
+            url: this.getDocId(doc) ? doc.url('app') : null,
+            isDeletable: this.isDeletable(doc),
+            isEditable: this.isEditable(doc)
+          };
+        }, this);
     },
 
     hasResults: function () {
@@ -118,11 +210,11 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
       return this._isLoading;
     },
 
-    isDeleteable: function () {
-      return this._deleteable;
-    },
-
     selectDoc: function (id) {
+      if (!id || id === '_all_docs') {
+        return;
+      }
+
       if (!this._selectedItems[id]) {
         this._selectedItems[id] = true;
       } else {
@@ -168,6 +260,10 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
       return this._textEmptyIndex;
     },
 
+    setbulkDeleteDocCollection: function (bulkDeleteDocCollection) {
+      this._bulkDeleteDocCollection = bulkDeleteDocCollection;
+    },
+
     createBulkDeleteFromSelected: function () {
       var items = _.map(_.keys(this._selectedItems), function (id) {
         var doc = this._collection.get(id);
@@ -179,7 +275,7 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
         };
       }, this);
 
-      var bulkDelete = new Documents.BulkDeleteDocCollection(items, {
+      var bulkDelete = new this._bulkDeleteDocCollection(items, {
         databaseId: this.getDatabase().safeID()
       });
 
@@ -187,7 +283,13 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
     },
 
     canSelectAll: function () {
-      return this._collection.length > this.getSelectedItemsLength();
+      var length = this._collection.length;
+
+      if (this._collection.get && this._collection.get('_all_docs')) {
+        length = length - 1;
+      }
+
+      return length > this.getSelectedItemsLength();
     },
 
     canDeselectAll: function () {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/index-results/tests/index-results.actionsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/tests/index-results.actionsSpec.js b/app/addons/documents/index-results/tests/index-results.actionsSpec.js
index 1bbba77..fb67414 100644
--- a/app/addons/documents/index-results/tests/index-results.actionsSpec.js
+++ b/app/addons/documents/index-results/tests/index-results.actionsSpec.js
@@ -99,6 +99,7 @@ define([
           safeID: function () { return '1';}
         }
       });
+      store._bulkDeleteDocCollection = Documents.BulkDeleteDocCollection;
 
       store._selectedItems = {
         'testId1': true,

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx b/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx
index 22d9366..85dfecd 100644
--- a/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx
+++ b/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx
@@ -13,9 +13,11 @@ define([
   'api',
   'addons/documents/index-results/index-results.components.react',
   'addons/documents/index-results/actions',
+  'addons/documents/resources',
+  'addons/databases/resources',
   'testUtils',
   "react"
-], function (FauxtonAPI, Views, IndexResultsActions, utils, React) {
+], function (FauxtonAPI, Views, IndexResultsActions, Resources, Databases, utils, React) {
   FauxtonAPI.router = new FauxtonAPI.Router([]);
 
   var assert = utils.assert;
@@ -26,7 +28,6 @@ define([
 
     beforeEach(function () {
       container = document.createElement('div');
-
     });
 
     afterEach(function () {
@@ -59,7 +60,13 @@ define([
     });
 
     describe('loading', function () {
+      beforeEach(function () {
+        container = document.createElement('div');
+      });
 
+      afterEach(function () {
+        React.unmountComponentAtNode(container);
+      });
       it('should show loading component', function () {
         var resultsEl = TestUtils.renderIntoDocument(<Views.ResultsScreen
           isLoading={true}

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/index-results/tests/index-results.storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/tests/index-results.storesSpec.js b/app/addons/documents/index-results/tests/index-results.storesSpec.js
index 7b5c0b1..e72d25c 100644
--- a/app/addons/documents/index-results/tests/index-results.storesSpec.js
+++ b/app/addons/documents/index-results/tests/index-results.storesSpec.js
@@ -20,12 +20,22 @@ define([
   var assert = testUtils.assert;
   var dispatchToken;
   var store;
+  var opts;
 
   describe('Index Results Store', function () {
-
     beforeEach(function () {
       store = new Stores.IndexResultsStore();
       dispatchToken = FauxtonAPI.dispatcher.register(store.dispatch);
+      opts = {
+        params: {},
+        database: {
+          safeID: function () { return '1';}
+        }
+      };
+    });
+
+    afterEach(function () {
+      FauxtonAPI.dispatcher.unregister(dispatchToken);
     });
 
     describe('#hasResults', function () {
@@ -47,12 +57,7 @@ define([
     describe('#getResults', function () {
 
       it('has correct doc format', function () {
-        store._collection = new Documents.AllDocs([{_id: 'testId'}], {
-          params: {},
-          database: {
-            safeID: function () { return '1';}
-          }
-        });
+        store._collection = new Documents.AllDocs([{_id: 'testId'}], opts);
 
         var doc = store.getResults()[0];
         assert.equal(doc.id, 'testId');
@@ -60,33 +65,19 @@ define([
       });
 
     });
-
-    afterEach(function () {
-      FauxtonAPI.dispatcher.unregister(dispatchToken);
-    });
   });
 
   describe('canSelectAll', function () {
 
     it('returns true for selected docs less than collection', function () {
-      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
-        params: {},
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], opts);
 
       store._selectedItems = {'testId1': true};
       assert.ok(store.canSelectAll());
     });
 
     it('returns false for selected docs same as collection', function () {
-      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
-        params: {},
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], opts);
 
       store._selectedItems = {
         'testId1': true,
@@ -96,6 +87,20 @@ define([
       assert.notOk(store.canSelectAll());
     });
 
+    it('returns true even with _all_docs (mango)', function () {
+      store._collection = new Documents.AllDocs([
+        {_id: 'testId1'},
+        {_id: 'testId2'},
+        {_id: '_all_docs'}
+      ], opts);
+
+      store._selectedItems = {
+        'testId1': true,
+        'testId2': true
+      };
+
+      assert.notOk(store.canSelectAll());
+    });
   });
 
   describe('canDeselectAll', function () {
@@ -121,12 +126,7 @@ define([
     });
 
     it('returns false for all collapsed docs', function () {
-      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
-        params: {},
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], opts);
 
       store._collapsedDocs = {
         'testId1': true,
@@ -156,12 +156,7 @@ define([
   describe('getDocContent', function () {
 
     it('returns full doc if not collapsed', function () {
-      store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}] , {
-        params: {},
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}], opts);
 
       var doc = store._collection.first();
       var result = store.getDocContent(doc);
@@ -170,12 +165,7 @@ define([
     });
 
     it('returns no doc content if collapsed', function () {
-      store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}] , {
-        params: {},
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}], opts);
 
       var doc = store._collection.first();
       store._collapsedDocs = {'testId1': true};
@@ -204,12 +194,7 @@ define([
   describe('#selectAllDocuments', function () {
 
     it('selects all documents', function () {
-      store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}] , {
-        params: {},
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}], opts);
 
       store.selectAllDocuments();
       assert.ok(store.getSelectedItems().testId1);
@@ -220,12 +205,7 @@ define([
   describe('#deSelectAllDocuments', function () {
 
     it('deselects all documents', function () {
-      store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}] , {
-        params: {},
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}], opts);
 
       store.selectAllDocuments();
       assert.ok(store.getSelectedItems().testId1);
@@ -237,12 +217,7 @@ define([
   describe('#collapseSelectedDocs', function () {
 
     it('collapses all selected docs', function () {
-      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
-        params: {},
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], opts);
 
       store.clearCollapsedDocs();
 
@@ -260,12 +235,7 @@ define([
   describe('#unCollapseSelectedDocs', function () {
 
     it('uncollapses all selected docs', function () {
-      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
-        params: {},
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], opts);
 
       store.clearCollapsedDocs();
 
@@ -284,12 +254,9 @@ define([
   describe('#createBulkDeleteFromSelected', function () {
 
     it('correctly creates BulkDeleteDocCollection', function () {
-      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
-        params: {},
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], opts);
+
+      store._bulkDeleteDocCollection = Documents.BulkDeleteDocCollection;
 
       store._selectedItems = {
         'testId1': true,
@@ -304,16 +271,62 @@ define([
 
   });
 
+  describe('#getMangoDoc', function () {
+    var store = new Stores.IndexResultsStore();
+    var fakeMango = {
+      ddoc: '_design/e4d338e5d6f047749f5399ab998b4fa04ba0c816',
+      def: {
+        fields: [
+          {'_id': 'asc'},
+          {'foo': 'bar'},
+          {'ente': 'gans'}
+        ]
+      },
+      name: 'e4d338e5d6f047749f5399ab998b4fa04ba0c816',
+      type: 'json'
+    };
+
+    it('creates a special id from the header fields', function () {
+      var doc = new Documents.MangoIndex(fakeMango, opts);
+      assert.equal(store.getMangoDoc(doc).header, 'json: _id, foo, ente');
+    });
+
+    it('supports custom header fields', function () {
+      FauxtonAPI.registerExtension('mango:additionalIndexes', {
+        createHeader: function (doc) {
+          return ['foobar'];
+        }
+      });
+
+      var doc = new Documents.MangoIndex({
+        ddoc: '_design/e4d338e5d6f047749f5399ab998b4fa04ba0c816',
+        def: {
+          fields: []
+        },
+        name: 'e4d338e5d6f047749f5399ab998b4fa04ba0c816',
+        type: 'json'
+      }, opts);
+      assert.equal(store.getMangoDoc(doc).header, 'foobar');
+    });
+
+    it('removes the name and ddoc field', function () {
+      var doc = new Documents.MangoIndex(fakeMango, opts);
+      assert.ok(doc.get('name'));
+      assert.ok(doc.get('ddoc'));
+
+      var newDoc = store.getMangoDoc(doc);
+      assert.notOk(JSON.parse(newDoc.content).name);
+      assert.notOk(JSON.parse(newDoc.content).ddoc);
+      assert.ok(JSON.parse(newDoc.content).type);
+    });
+  });
+
   describe('#getDocId', function () {
 
     it('returns id if it exists', function () {
       var doc = new Documents.Doc({
         _id: 'doc-id'
-      }, {
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      }, opts);
 
       assert.equal(store.getDocId(doc), 'doc-id');
 
@@ -322,11 +335,7 @@ define([
     it('returns key if it exists', function () {
       var doc = new Documents.Doc({
         key: 'doc-key'
-      }, {
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      }, opts);
 
       assert.equal(store.getDocId(doc), 'doc-key');
 
@@ -336,11 +345,7 @@ define([
       var doc = new Documents.Doc({
         key: null,
         value: 'the-value'
-      }, {
-        database: {
-          safeID: function () { return '1';}
-        }
-      });
+      }, opts);
 
       assert.equal(store.getDocId(doc), '');
 
@@ -348,6 +353,7 @@ define([
   });
 
   describe('isEditable', function () {
+    store = new Stores.IndexResultsStore();
 
     it('returns false for no collection', function () {
       store._collection = null;
@@ -360,9 +366,27 @@ define([
     });
 
     it('delegates to collection', function () {
+      store._collection = {
+        attributes: {
+          fields: ["foo"]
+        }
+      };
+      store._collection.isEditable = function () { return {'stub': true}; };
+      assert.deepEqual(store.isEditable(), {'stub': true});
       store._collection = {};
-      store._collection.isEditable = function () { return 'stub'; };
-      assert.equal(store.isEditable(), 'stub');
+    });
+
+    it('retuns false for ghost-docs that are filtered away', function () {
+      store._collection = {};
+      assert.equal(store.isEditable({}), false);
+    });
+  });
+
+  describe('isDeletable', function () {
+    store = new Stores.IndexResultsStore();
+
+    it('retuns false for ghost-docs that are filtered away', function () {
+      assert.equal(store.isDeletable({}), false);
     });
   });
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/mango/mango.actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/mango.actions.js b/app/addons/documents/mango/mango.actions.js
index d3df146..0c7c3ed 100644
--- a/app/addons/documents/mango/mango.actions.js
+++ b/app/addons/documents/mango/mango.actions.js
@@ -16,10 +16,10 @@ define([
   'addons/documents/resources',
   'addons/documents/mango/mango.actiontypes',
   'addons/documents/mango/mango.stores',
-  'addons/documents/index-results/actions'
-
+  'addons/documents/pagination/stores',
+  'addons/documents/index-results/actions',
 ],
-function (app, FauxtonAPI, Documents, ActionTypes, Stores, IndexResultsActions) {
+function (app, FauxtonAPI, Documents, ActionTypes, Stores, PaginationStores, IndexResultActions) {
   var store = Stores.mangoStore;
 
   return {
@@ -31,8 +31,23 @@ function (app, FauxtonAPI, Documents, ActionTypes, Stores, IndexResultsActions)
       });
     },
 
+    newQueryFindCode: function (options) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.MANGO_NEW_QUERY_FIND_CODE,
+        options: options
+      });
+    },
+
+    newQueryCreateIndexCode: function (options) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.MANGO_NEW_QUERY_CREATE_INDEX_CODE,
+        options: options
+      });
+    },
+
     saveQuery: function (options) {
-      var mangoIndex = new Documents.MangoIndex(JSON.parse(options.queryCode), {database: options.database});
+      var queryCode = JSON.parse(options.queryCode),
+          mangoIndex = new Documents.MangoIndex(queryCode, {database: options.database});
 
       FauxtonAPI.addNotification({
         msg:  'Saving Index for Query...',
@@ -41,18 +56,60 @@ function (app, FauxtonAPI, Documents, ActionTypes, Stores, IndexResultsActions)
       });
 
       mangoIndex.save().then(function (res) {
-        var msg = res.result === 'created' ? 'Index created' : 'Index already exits',
-            url = FauxtonAPI.urls('mango', 'index-app', options.database.safeID());
+        var url = FauxtonAPI.urls('mango', 'query-app', options.database.safeID());
 
-        FauxtonAPI.addNotification({
-          msg:  msg,
-          type: 'success',
-          clear: true
+        FauxtonAPI.dispatch({
+          type: ActionTypes.MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS,
+          options: {
+            fields: queryCode.index.fields
+          }
         });
 
-        IndexResultsActions.reloadResultsList();
+        var mangoIndexCollection = new Documents.MangoIndexCollection(null, {
+          database: options.database,
+          params: null,
+          paging: {
+            pageSize: PaginationStores.indexPaginationStore.getPerPage()
+          }
+        });
+
+        this.getIndexList({indexList: mangoIndexCollection}).then(function () {
+
+          IndexResultActions.reloadResultsList();
+
+          FauxtonAPI.addNotification({
+            msg:  'Index is ready for querying. <a href="' + url + '">Run a Query.</a>',
+            type: 'success',
+            clear: true,
+            escape: false
+          });
+        }.bind(this));
+
       }.bind(this));
+    },
+
+    mangoResetIndexList: function (options) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.MANGO_RESET,
+        options: options
+      });
+    },
 
+    getIndexList: function (options) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.MANGO_NEW_AVAILABLE_INDEXES,
+        options: options
+      });
+
+      return options.indexList.fetch({reset: true}).then(function () {
+        this.mangoResetIndexList({isLoading: false});
+      }.bind(this), function () {
+        FauxtonAPI.addNotification({
+          msg: 'Bad request!',
+          type: "error",
+          clear:  true
+       });
+      });
     }
   };
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/mango/mango.actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/mango.actiontypes.js b/app/addons/documents/mango/mango.actiontypes.js
index 112eacd..b6bdbcc 100644
--- a/app/addons/documents/mango/mango.actiontypes.js
+++ b/app/addons/documents/mango/mango.actiontypes.js
@@ -13,5 +13,10 @@
 define([], function () {
   return {
     MANGO_SET_DB: 'MANGO_SET_DB',
+    MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS: 'MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS',
+    MANGO_NEW_QUERY_FIND_CODE: 'MANGO_NEW_QUERY_FIND_CODE',
+    MANGO_NEW_QUERY_CREATE_INDEX_CODE: 'MANGO_NEW_QUERY_CREATE_INDEX_CODE',
+    MANGO_NEW_AVAILABLE_INDEXES: 'MANGO_NEW_AVAILABLE_INDEXES',
+    MANGO_RESET: 'MANGO_RESET'
   };
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/mango/mango.components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/mango.components.react.jsx b/app/addons/documents/mango/mango.components.react.jsx
index 8764779..468c189 100644
--- a/app/addons/documents/mango/mango.components.react.jsx
+++ b/app/addons/documents/mango/mango.components.react.jsx
@@ -17,40 +17,35 @@ define([
   'addons/documents/mango/mango.stores',
   'addons/documents/mango/mango.actions',
   'addons/components/react-components.react',
+  'addons/documents/index-results/actions',
+  'addons/documents/mango/mango.helper',
 
   'plugins/prettify'
 ],
 
-function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
+function (app, FauxtonAPI, React, Stores, Actions,
+          ReactComponents, IndexResultActions, MangoHelper) {
+
   var mangoStore = Stores.mangoStore;
+  var getDocUrl = app.helpers.getDocUrl;
 
   var PaddedBorderedBox = ReactComponents.PaddedBorderedBox;
   var CodeEditor = ReactComponents.CodeEditor;
   var ConfirmButton = ReactComponents.ConfirmButton;
 
-  var HelpScreen = React.createClass({
-    render: function () {
-      return (
-        <div className="watermark-logo">
-          <h3>{this.props.title}</h3>
-          <div>
-            Create an Index to query it afterwards.<br/><br/>
-            The example on the left shows how to create an index for the field '_id'
-          </div>
-        </div>
-      );
-    }
-  });
-
-  var MangoIndexEditorController = React.createClass({
+  var MangoQueryEditorController = React.createClass({
     getInitialState: function () {
       return this.getStoreState();
     },
 
     getStoreState: function () {
       return {
-        queryCode: mangoStore.getQueryCode(),
+        queryCode: mangoStore.getQueryFindCode(),
         database: mangoStore.getDatabase(),
+        changedQuery: mangoStore.getQueryFindCodeChanged(),
+        availableIndexes: mangoStore.getAvailableQueryIndexes(),
+        additionalIndexes: mangoStore.getAvailableAdditionalIndexes(),
+        isLoading: mangoStore.getLoadingIndexes(),
       };
     },
 
@@ -58,7 +53,12 @@ function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
       this.setState(this.getStoreState());
     },
 
+    componentDidUpdate: function () {
+      prettyPrint();
+    },
+
     componentDidMount: function () {
+      prettyPrint();
       mangoStore.on('change', this.onChange, this);
     },
 
@@ -66,28 +66,93 @@ function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
       mangoStore.off('change', this.onChange);
     },
 
+    getMangoEditor: function () {
+      return this.refs.mangoEditor;
+    },
+
+    render: function () {
+      var loadLines;
+      if (this.state.isLoading) {
+        return (
+          <div className="editor-wrapper span5 scrollable">
+            <ReactComponents.LoadLines />
+          </div>
+        );
+      }
+
+      return (
+        <MangoEditor
+          ref="mangoEditor"
+          description={this.props.description}
+          dbName={this.state.database.id}
+          onSubmit={this.runQuery}
+          title={this.props.editorTitle}
+          additionalIndexesText={this.props.additionalIndexesText}
+          docs={getDocUrl('MANGO')}
+          exampleCode={this.state.queryCode}
+          changedQuery={this.state.changedQuery}
+          availableIndexes={this.state.availableIndexes}
+          additionalIndexes={this.state.additionalIndexes}
+          confirmbuttonText="Run Query" />
+      );
+    },
+
+    runQuery: function (event) {
+      event.preventDefault();
+
+      if (!this.getMangoEditor().hasValidCode()) {
+        FauxtonAPI.addNotification({
+          msg:  'Please fix the Javascript errors and try again.',
+          type: 'error',
+          clear: true
+        });
+        return;
+      }
+
+      IndexResultActions.runMangoFindQuery({
+        database: this.state.database,
+        queryCode: this.getMangoEditor().getEditorValue()
+      });
+    }
+  });
+
+  var MangoEditor = React.createClass({
+    getDefaultProps: function () {
+      return {
+        changedQuery: null,
+        availableIndexes: null,
+        additionalIndexes: null
+      };
+    },
+
     render: function () {
       return (
         <div className="editor-wrapper span5 scrollable">
           <PaddedBorderedBox>
-            <div className="editor-description">{this.props.description}</div>
+            <div
+              dangerouslySetInnerHTML={{__html: this.props.description}}
+              className="editor-description">
+            </div>
           </PaddedBorderedBox>
           <PaddedBorderedBox>
             <strong>Database</strong>
-            <div className="db-title">{this.state.database.id}</div>
+            <div className="db-title">{this.props.dbName}</div>
           </PaddedBorderedBox>
-          <form className="form-horizontal" onSubmit={this.saveQuery}>
+          <form className="form-horizontal" onSubmit={this.props.onSubmit}>
             <PaddedBorderedBox>
               <CodeEditor
                 id="query-field"
-                ref="indexQueryEditor"
-                title={'Index'}
-                docs={false}
-                code={this.state.queryCode} />
+                ref="field"
+                title={this.props.title}
+                docs={this.props.docs}
+                code={this.props.exampleCode}
+                disableUnload={true} />
+              {this.getChangedQueryText()}
             </PaddedBorderedBox>
+            {this.getIndexBox()}
             <div className="padded-box">
               <div className="control-group">
-                <ConfirmButton text="Create Index" />
+                <ConfirmButton text={this.props.confirmbuttonText} />
               </div>
             </div>
           </form>
@@ -95,24 +160,110 @@ function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
       );
     },
 
+    getChangedQueryText: function () {
+      if (!this.props.changedQuery) {
+        return null;
+      }
+
+      return (
+        <div className="info-changed-query">
+          <strong>Info:</strong>
+          <div>We changed the default query based on the last Index you created.</div>
+        </div>
+      );
+    },
+
+    getIndexBox: function () {
+      if (!this.props.availableIndexes) {
+        return null;
+      }
+
+      return (
+        <PaddedBorderedBox>
+          <strong>Your available Indexes:</strong>
+          <pre
+            className="mango-available-indexes">
+            {this.getIndexes('index', this.props.availableIndexes)}
+            {this.getIndexes('additonal', this.props.additionalIndexes)}
+          </pre>
+        </PaddedBorderedBox>
+      );
+    },
+
+    getIndexes: function (prefix, indexes) {
+      if (!indexes) {
+        return;
+      }
+
+      return indexes.map(function (index, i) {
+        var name = MangoHelper.getIndexName(index);
+
+        return (
+          <div key={prefix + i}>{name}</div>
+        );
+      });
+    },
+
+    getEditorValue: function () {
+      return this.refs.field.getValue();
+    },
+
     getEditor: function () {
-      return this.refs.indexQueryEditor.getEditor();
+      return this.refs.field.getEditor();
     },
 
     hasValidCode: function () {
       var editor = this.getEditor();
       return editor.hadValidCode();
+    }
+  });
+
+  var MangoIndexEditorController = React.createClass({
+    getInitialState: function () {
+      return this.getStoreState();
     },
 
-    clearNotifications: function () {
-      var editor = this.getEditor();
-      editor.editSaved();
+    getStoreState: function () {
+      return {
+        queryIndexCode: mangoStore.getQueryIndexCode(),
+        database: mangoStore.getDatabase(),
+      };
+    },
+
+    onChange: function () {
+      this.setState(this.getStoreState());
+    },
+
+    componentDidMount: function () {
+      mangoStore.on('change', this.onChange, this);
+    },
+
+    componentWillUnmount: function () {
+      mangoStore.off('change', this.onChange);
+    },
+
+    getMangoEditor: function () {
+      return this.refs.mangoEditor;
+    },
+
+    render: function () {
+      return (
+        <MangoEditor
+          ref="mangoEditor"
+          description={this.props.description}
+          dbName={this.state.database.id}
+          onSubmit={this.saveQuery}
+          title="Index"
+          docs={getDocUrl('MANGO')}
+          exampleCode={this.state.queryIndexCode}
+          confirmbuttonText="Create Index" />
+      );
     },
 
     saveQuery: function (event) {
       event.preventDefault();
 
-      if (!this.hasValidCode()) {
+      if (!this.getMangoEditor().hasValidCode()) {
         FauxtonAPI.addNotification({
           msg:  'Please fix the Javascript errors and try again.',
           type: 'error',
@@ -121,35 +272,16 @@ function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
         return;
       }
 
-      this.clearNotifications();
-
       Actions.saveQuery({
         database: this.state.database,
-        queryCode: this.refs.indexQueryEditor.getValue()
+        queryCode: this.getMangoEditor().getEditorValue()
       });
     }
   });
 
   var Views = {
-    renderHelpScreen: function (el) {
-      React.render(
-        <HelpScreen title={app.i18n.en_US['mango-help-title']} />,
-        el
-      );
-    },
-    removeHelpScreen: function (el) {
-      React.unmountComponentAtNode(el);
-    },
-    renderMangoIndexEditor: function (el) {
-      React.render(
-        <MangoIndexEditorController description={app.i18n.en_US['mango-descripton']} />,
-        el
-      );
-    },
-    removeMangoIndexEditor: function (el) {
-      React.unmountComponentAtNode(el);
-    },
-    MangoIndexEditorController: MangoIndexEditorController
+    MangoIndexEditorController: MangoIndexEditorController,
+    MangoQueryEditorController: MangoQueryEditorController
   };
 
   return Views;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/mango/mango.helper.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/mango.helper.js b/app/addons/documents/mango/mango.helper.js
new file mode 100644
index 0000000..d311973
--- /dev/null
+++ b/app/addons/documents/mango/mango.helper.js
@@ -0,0 +1,44 @@
+// 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([
+  'api'
+], function (FauxtonAPI) {
+
+  function getIndexName (doc) {
+    var nameArray = [],
+        indexes;
+
+    nameArray = doc.get('def').fields.reduce(function (acc, el, i) {
+      if (i === 0) {
+        acc.push('json: ' + Object.keys(el)[0]);
+      } else {
+        acc.push(Object.keys(el)[0]);
+      }
+
+      return acc;
+    }, []);
+
+    if (!nameArray.length) {
+      indexes = FauxtonAPI.getExtensions('mango:additionalIndexes')[0];
+      nameArray = indexes.createHeader(doc);
+    }
+
+    return nameArray.join(', ');
+  }
+
+  return {
+    getIndexName: getIndexName
+  };
+
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/mango/mango.stores.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/mango.stores.js b/app/addons/documents/mango/mango.stores.js
index f72a046..f0f2638 100644
--- a/app/addons/documents/mango/mango.stores.js
+++ b/app/addons/documents/mango/mango.stores.js
@@ -18,19 +18,80 @@ define([
 function (FauxtonAPI, ActionTypes) {
 
 
-  var defaultQuery = '{\n' +
-      '  "index": {\n' +
-      '    "fields": ["_id"]\n' +
-      '  },\n' +
-      '  "type" : "json"\n' +
-      '}';
+  var defaultQueryIndexCode = {
+    "index": {
+      "fields": ["_id"]
+    },
+    "type" : "json"
+  };
+
+  var defaultQueryFindCode = {
+    "selector": {
+      "_id": {"$gt": null}
+    }
+  };
 
   var Stores = {};
 
   Stores.MangoStore = FauxtonAPI.Store.extend({
 
-    getQueryCode: function () {
-      return defaultQuery;
+    initialize: function () {
+      this._queryFindCode = defaultQueryFindCode;
+      this._queryIndexCode = defaultQueryIndexCode;
+      this._queryFindCodeChanged = false;
+      this._availableIndexes = [];
+      this._getLoadingIndexes = true;
+    },
+
+    getQueryIndexCode: function () {
+      return this.formatCode(this._queryIndexCode);
+    },
+
+    setQueryIndexCode: function (options) {
+      this._queryIndexCode = options.code;
+    },
+
+    getQueryFindCode: function () {
+      return this.formatCode(this._queryFindCode);
+    },
+
+    setQueryFindCode: function (options) {
+      this._queryFindCode = options.code;
+    },
+
+    getLoadingIndexes: function () {
+      return this._getLoadingIndexes;
+    },
+
+    setLoadingIndexes: function (options) {
+      this._getLoadingIndexes = options.isLoading;
+    },
+
+    formatCode: function (code) {
+      return JSON.stringify(code, null, '  ');
+    },
+
+    newQueryFindCodeFromFields: function (options) {
+      var fields = options.fields,
+          queryCode = JSON.parse(JSON.stringify(this._queryFindCode)),
+          selectorContent;
+
+      if (!fields) {
+        return;
+      }
+
+      selectorContent = fields.reduce(function (acc, field) {
+        acc[field] = {"$gt": null};
+        return acc;
+      }, {});
+
+      queryCode.selector = selectorContent;
+      this._queryFindCode = queryCode;
+      this._queryFindCodeChanged = true;
+    },
+
+    getQueryFindCodeChanged: function () {
+      return this._queryFindCodeChanged;
     },
 
     setDatabase: function (options) {
@@ -41,6 +102,28 @@ function (FauxtonAPI, ActionTypes) {
       return this._database;
     },
 
+    setAvailableIndexes: function (options) {
+      this._availableIndexes = options.indexList;
+    },
+
+    getAvailableQueryIndexes: function () {
+      return this._availableIndexes.filter(function (el) {
+        return ['json', 'special'].indexOf(el.get('type')) !== -1;
+      });
+    },
+
+    getAvailableAdditionalIndexes: function () {
+      var indexes = FauxtonAPI.getExtensions('mango:additionalIndexes')[0];
+
+      if (!indexes) {
+        return;
+      }
+
+      return this._availableIndexes.filter(function (el) {
+        return el.get('type').indexOf(indexes.type) !== -1;
+      });
+    },
+
     dispatch: function (action) {
       switch (action.type) {
 
@@ -49,6 +132,30 @@ function (FauxtonAPI, ActionTypes) {
           this.triggerChange();
         break;
 
+        case ActionTypes.MANGO_NEW_QUERY_CREATE_INDEX_CODE:
+          this.setQueryIndexCode(action.options);
+          this.triggerChange();
+        break;
+
+        case ActionTypes.MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS:
+          this.newQueryFindCodeFromFields(action.options);
+          this.triggerChange();
+        break;
+
+        case ActionTypes.MANGO_NEW_QUERY_FIND_CODE:
+          this.setQueryFindCode(action.options);
+          this.triggerChange();
+        break;
+
+        case ActionTypes.MANGO_NEW_AVAILABLE_INDEXES:
+          this.setAvailableIndexes(action.options);
+          this.triggerChange();
+        break;
+
+        case ActionTypes.MANGO_RESET:
+          this.setLoadingIndexes(action.options);
+          this.triggerChange();
+        break;
       }
     }
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/mango/tests/mango.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/tests/mango.componentsSpec.react.jsx b/app/addons/documents/mango/tests/mango.componentsSpec.react.jsx
index 0f71fa3..2c126ad 100644
--- a/app/addons/documents/mango/tests/mango.componentsSpec.react.jsx
+++ b/app/addons/documents/mango/tests/mango.componentsSpec.react.jsx
@@ -15,29 +15,91 @@ define([
   'addons/documents/mango/mango.components.react',
   'addons/documents/mango/mango.stores',
   'addons/documents/mango/mango.actions',
+  'addons/documents/mango/mango.actiontypes',
 
   'addons/documents/resources',
   'addons/databases/resources',
 
   'testUtils',
   'react'
-], function (FauxtonAPI, Views, Stores, MangoActions, Resources, Databases, utils, React) {
+], function (FauxtonAPI, Views, Stores, MangoActions, ActionTypes, Resources, Databases, utils, React) {
 
   var assert = utils.assert;
   var TestUtils = React.addons.TestUtils;
 
-  var fakeData = [
-      {
+  describe('Mango IndexEditor', function () {
+    var database = new Databases.Model({id: 'testdb'}),
+        container,
+        editor;
+
+    beforeEach(function () {
+      container = document.createElement('div');
+      MangoActions.setDatabase({
+        database: database
+      });
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+    it('renders a default index definition', function () {
+      editor = TestUtils.renderIntoDocument(
+        <Views.MangoIndexEditorController description="foo" />,
+        container
+      );
+
+      var payload = JSON.parse(editor.getMangoEditor().getEditorValue());
+      assert.equal(payload.index.fields[0], '_id');
+    });
+
+    it('renders the current database', function () {
+      editor = TestUtils.renderIntoDocument(
+        <Views.MangoIndexEditorController description="foo" />,
+        container
+      );
+      var $el = $(editor.getDOMNode());
+
+      assert.equal($el.find('.db-title').text(), 'testdb');
+    });
+
+    it('renders a description', function () {
+      editor = TestUtils.renderIntoDocument(
+        <Views.MangoIndexEditorController description="CouchDB Query is great!" />,
+        container
+      );
+      var $el = $(editor.getDOMNode());
+
+      assert.equal($el.find('.editor-description').text(), 'CouchDB Query is great!');
+    });
+  });
+
+  describe('Mango QueryEditor', function () {
+    var database = new Databases.Model({id: 'testdb'}),
+        container,
+        editor,
+        mangoCollection;
+
+    beforeEach(function () {
+      container = document.createElement('div');
+      MangoActions.setDatabase({
+        database: database
+      });
+
+      MangoActions.mangoResetIndexList({isLoading: false});
+
+      mangoCollection = new Resources.MangoIndexCollection([{
         ddoc: '_design/e4d338e5d6f047749f5399ab998b4fa04ba0c816',
         def: {
-          fields: [{
-            '_id': 'asc'
-          }]
+          fields: [
+            {'_id': 'asc'},
+            {'foo': 'bar'},
+            {'ente': 'gans'}
+          ]
         },
         name: 'e4d338e5d6f047749f5399ab998b4fa04ba0c816',
         type: 'json'
-      },
-      {
+      }, {
         ddoc: null,
         def: {
           fields: [{
@@ -46,46 +108,103 @@ define([
         },
         name: '_all_docs',
         type: 'special'
-      }
-    ];
-
-
-  describe('Mango IndexEditor', function () {
-    var database = new Databases.Model({id: 'testdb'}),
-        container,
-        editor;
+      }], {
+        params: {},
+        database: {
+          safeID: function () { return '1'; }
+        }
+      });
 
-    beforeEach(function () {
-      container = document.createElement('div');
-      MangoActions.setDatabase({
-        database: database
+      FauxtonAPI.dispatch({
+        type: ActionTypes.MANGO_NEW_AVAILABLE_INDEXES,
+        options: {indexList: mangoCollection}
       });
+
     });
 
     afterEach(function () {
       React.unmountComponentAtNode(container);
     });
 
-    it('renders a default index definition', function () {
-      editor = TestUtils.renderIntoDocument(<Views.MangoIndexEditorController description="foo" />, container);
+    it('lists our available indexes', function () {
+      editor = TestUtils.renderIntoDocument(
+        <Views.MangoQueryEditorController description="foo" />,
+        container
+      );
       var $el = $(editor.getDOMNode());
-      var payload = JSON.parse(editor.refs.indexQueryEditor.getValue());
-      assert.equal(payload.index.fields[0], '_id');
+      assert.equal($el.find('.mango-available-indexes').length, 1);
+
+      assert.include(
+        $el.find('.mango-available-indexes').text(),
+        'json: _id, foo, ente'
+      );
+      assert.include(
+        $el.find('.mango-available-indexes').text(),
+        'json: _id'
+      );
+    });
+
+    it('has a default query', function () {
+      editor = React.render(
+        <Views.MangoQueryEditorController description="foo" />,
+        container
+      );
+      var json = JSON.parse(editor.getMangoEditor().getEditorValue());
+      assert.equal(Object.keys(json.selector)[0], '_id');
+    });
+
+    it('can render a query based on the last defined index', function () {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS,
+        options: {
+          fields: ['zetti', 'mussmaennchen']
+        }
+      });
+
+      editor = TestUtils.renderIntoDocument(
+        <Views.MangoQueryEditorController description="foo" />,
+        container
+      );
+
+      var json = JSON.parse(editor.getMangoEditor().getEditorValue());
+      assert.equal(Object.keys(json.selector)[0], 'zetti');
+      assert.equal(Object.keys(json.selector)[1], 'mussmaennchen');
+    });
+
+    it('informs the user that it uses a query based on the last defined index', function () {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS,
+        options: {
+          fields: ['zetti', 'mussmaennchen']
+        }
+      });
+
+      editor = TestUtils.renderIntoDocument(
+        <Views.MangoQueryEditorController description="foo" />,
+        container
+      );
+      var $el = $(editor.getDOMNode());
+      assert.equal($el.find('.info-changed-query').length, 1);
     });
 
     it('renders the current database', function () {
-      editor = TestUtils.renderIntoDocument(<Views.MangoIndexEditorController description="foo" />, container);
+      editor = TestUtils.renderIntoDocument(
+        <Views.MangoQueryEditorController description="foo" />,
+        container
+      );
       var $el = $(editor.getDOMNode());
 
       assert.equal($el.find('.db-title').text(), 'testdb');
     });
 
     it('renders a description', function () {
-      editor = TestUtils.renderIntoDocument(<Views.MangoIndexEditorController description="CouchDB Query is great!" />, container);
+      editor = TestUtils.renderIntoDocument(
+        <Views.MangoQueryEditorController description="CouchDB Query is great!" />,
+        container
+      );
       var $el = $(editor.getDOMNode());
 
       assert.equal($el.find('.editor-description').text(), 'CouchDB Query is great!');
     });
   });
-
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/mango/tests/mango.storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/tests/mango.storesSpec.js b/app/addons/documents/mango/tests/mango.storesSpec.js
new file mode 100644
index 0000000..b41d7f6
--- /dev/null
+++ b/app/addons/documents/mango/tests/mango.storesSpec.js
@@ -0,0 +1,101 @@
+// 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([
+  'api',
+  'addons/documents/mango/mango.stores',
+  'addons/documents/mango/mango.actiontypes',
+  'addons/documents/resources',
+
+  'testUtils'
+], function (FauxtonAPI, Stores, ActionTypes, Resources, testUtils) {
+  var assert = testUtils.assert;
+  var dispatchToken;
+  var store;
+
+  describe('Mango Store', function () {
+
+    describe('getQueryCode', function () {
+
+      beforeEach(function () {
+        store = new Stores.MangoStore();
+        dispatchToken = FauxtonAPI.dispatcher.register(store.dispatch);
+      });
+
+      afterEach(function () {
+        FauxtonAPI.dispatcher.unregister(dispatchToken);
+      });
+
+      it('returns a default query', function () {
+        assert.ok(store.getQueryFindCode());
+      });
+
+      it('can set new selectors', function () {
+        store.newQueryFindCodeFromFields({fields: ['foo', 'bar']});
+        var res = store.getQueryFindCode();
+        assert.equal(res, JSON.stringify({
+          "selector": {
+            "foo": {"$gt": null},
+            "bar": {"$gt": null}
+          }
+        }, null, '  '));
+      });
+
+      it('indicates that we set another query for the user', function () {
+        assert.notOk(store.getQueryFindCodeChanged());
+        store.newQueryFindCodeFromFields({fields: ['mussman', 'zetti']});
+        assert.ok(store.getQueryFindCodeChanged());
+      });
+
+      it('alters the default query', function () {
+        assert.notOk(store.getQueryFindCodeChanged());
+        store.newQueryFindCodeFromFields({fields: ['mussman', 'zetti']});
+        assert.deepEqual(store.getQueryFindCode(), JSON.stringify({
+          "selector": {
+            "mussman": {"$gt": null},
+            "zetti": {"$gt": null}
+          }
+        }, null, '  '));
+      });
+
+      it('filters querytypes that are not needed', function () {
+
+        var collection = new Resources.MangoIndexCollection([
+          new Resources.MangoIndex({
+            ddoc: null,
+            name: 'emma',
+            type: 'special',
+            def: {fields: [{_id: 'asc'}]}
+          }, {}),
+          new Resources.MangoIndex({
+            ddoc: null,
+            name: 'biene',
+            type: 'json',
+            def: {fields: [{_id: 'desc'}]}
+          }, {}),
+          new Resources.MangoIndex({
+            ddoc: null,
+            name: 'alf',
+            type: 'nickname',
+            def: {fields: [{_id: 'asc'}]}
+          }, {})
+        ], {
+          database: {id: 'databaseId', safeID: function () { return this.id; }},
+          params: {limit: 20}
+        });
+        store._availableIndexes = collection;
+        assert.equal(store.getAvailableQueryIndexes().length, 2);
+      });
+
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/resources.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/resources.js b/app/addons/documents/resources.js
index 7385a68..7857231 100644
--- a/app/addons/documents/resources.js
+++ b/app/addons/documents/resources.js
@@ -73,7 +73,17 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
   });
 
   Documents.MangoIndex = Documents.Doc.extend({
-    idAttribute: 'name',
+    idAttribute: 'ddoc',
+
+    getId: function () {
+
+      if (this.id) {
+        return this.id;
+      }
+
+
+      return '_all_docs';
+    },
 
     isNew: function () {
       // never use put
@@ -120,22 +130,168 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
       return res.indexes;
     },
 
-    urlRef: function (params) {
+    urlRef: function (context, params) {
       var database = this.database.safeID(),
           query = '';
 
-      if (params) {
-        if (!_.isEmpty(params)) {
-          query = '?' + $.param(params);
-        } else {
+      if (!context) {
+        context = 'index-server';
+      }
+
+      return FauxtonAPI.urls('mango', context, database, query);
+    }
+  });
+
+  // MANGO INDEX EDITOR
+  Documents.MangoDoc = Documents.Doc.extend({
+    isMangoDoc: function () {
+      return true;
+    }
+  });
+
+  Documents.MangoDocumentCollection = PagingCollection.extend({
+    model: Documents.MangoDoc,
+    initialize: function (_attr, options) {
+      var defaultLimit = FauxtonAPI.constants.MISC.DEFAULT_PAGE_SIZE;
+
+      this.database = options.database;
+      this.params = _.extend({limit: defaultLimit}, options.params);
+
+      this.paging = _.defaults((options.paging || {}), {
+        defaultParams: _.defaults({}, options.params),
+        hasNext: false,
+        hasPrevious: false,
+        params: {},
+        pageSize: FauxtonAPI.constants.MISC.DEFAULT_PAGE_SIZE,
+        direction: undefined
+      });
+
+      this.paging.params = _.clone(this.paging.defaultParams);
+    },
+
+    url: function () {
+      return this.urlRef.apply(this, arguments);
+    },
+
+    updateSeq: function () {
+      return false;
+    },
+
+    isEditable: function () {
+      return true;
+    },
+
+    setQuery: function (query) {
+      this.query = query;
+      return this;
+    },
+
+    pageSizeReset: function (pageSize, opts) {
+      var options = _.defaults((opts || {}), {fetch: true});
+      this.paging.direction = undefined;
+      this.paging.pageSize = pageSize;
+      this.paging.params = this.paging.defaultParams;
+      this.paging.params.limit = pageSize;
+
+      if (options.fetch) {
+        return this.fetch();
+      }
+    },
+
+    _iterate: function (offset, opts) {
+      var options = _.defaults((opts || {}), {fetch: true});
+
+      this.paging.params = this.calculateParams(this.paging.params, offset, this.paging.pageSize);
+
+      return this.fetch();
+    },
+
+    getPaginatedQuery: function () {
+      var paginatedQuery = JSON.parse(JSON.stringify(this.query));
+
+      if (!this.paging.direction && this.paging.params.limit > 0) {
+        this.paging.direction = 'fetch';
+        this.paging.params.limit = this.paging.params.limit + 1;
+      }
+
+      // just update if NOT provided by editor
+      if (!paginatedQuery.limit) {
+        paginatedQuery.limit = this.paging.params.limit;
+      }
+
+      if (!paginatedQuery.skip) {
+        paginatedQuery.skip = this.paging.params.skip;
+      }
+
+      return paginatedQuery;
+    },
+
+    fetch: function () {
+      var url = this.urlRef(),
+                promise = FauxtonAPI.Deferred(),
+                query = this.getPaginatedQuery();
+
+      $.ajax({
+        type: 'POST',
+        url: url,
+        contentType: 'application/json',
+        dataType: 'json',
+        data: JSON.stringify(query),
+      })
+      .then(function (res) {
+        this.handleResponse(res, promise);
+      }.bind(this))
+      .fail(function (res) {
+        promise.reject(res.responseJSON);
+      }.bind(this));
+
+      return promise;
+    },
+
+    parse: function (resp) {
+      var rows = resp.docs;
+
+      this.paging.hasNext = this.paging.hasPrevious = false;
+
+      this.viewMeta = {
+        total_rows: resp.total_rows,
+        offset: resp.offset,
+        update_seq: resp.update_seq
+      };
+
+      var skipLimit = this.paging.defaultParams.skip || 0;
+      if (this.paging.params.skip > skipLimit) {
+        this.paging.hasPrevious = true;
+      }
+
+      if (rows.length === this.paging.pageSize + 1) {
+        this.paging.hasNext = true;
+
+        // remove the next page marker result
+        rows.pop();
+        this.viewMeta.total_rows = this.viewMeta.total_rows - 1;
+      }
+
+      return rows;
+    },
+
+    handleResponse: function (res, promise) {
+      var models = this.parse(res);
+
+      this.reset(models);
+
+      promise.resolve();
+    },
+
+    urlRef: function (context) {
+      var database = this.database.safeID(),
           query = '';
-        }
-      } else if (this.params) {
-        var parsedParam = Documents.QueryParams.stringify(this.params);
-        query = '?' + $.param(parsedParam);
+
+      if (!context) {
+        context = 'query-server';
       }
 
-      return FauxtonAPI.urls('mango', 'index-apiurl', database, query);
+      return FauxtonAPI.urls('mango', context, database, query);
     }
   });
 
@@ -171,6 +327,10 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
       this.databaseId = options.databaseId;
     },
 
+    url: function () {
+      return app.host + '/' + this.databaseId + '/_bulk_docs';
+    },
+
     bulkDelete: function () {
       var payload = this.createPayload(this.toJSON()),
           promise = FauxtonAPI.Deferred(),
@@ -178,7 +338,7 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
 
       $.ajax({
         type: 'POST',
-        url: app.host + '/' + this.databaseId + '/_bulk_docs',
+        url: this.url(),
         contentType: 'application/json',
         dataType: 'json',
         data: JSON.stringify(payload),
@@ -254,6 +414,27 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
     }
   });
 
+  Documents.MangoBulkDeleteDocCollection = Documents.BulkDeleteDocCollection.extend({
+    url: function () {
+      return app.host + '/' + this.databaseId + '/_index/_bulk_delete';
+    },
+
+    createPayload: function (documents) {
+      var documentList = documents
+        .filter(function (doc) {
+          return doc._id !== '_all_docs';
+        })
+        .map(function (doc) {
+          return doc._id;
+        });
+
+      return {
+        docids: documentList
+      };
+    }
+
+  });
+
   Documents.IndexCollection = PagingCollection.extend({
     model: Documents.Doc,
     documentation: function () {
@@ -422,6 +603,10 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
       title: 'New View',
       url: newUrlPrefix + '/new_view',
       icon: 'fonticon-plus-circled'
+    }, {
+      title: app.i18n.en_US['new-mango-index'],
+      url: newUrlPrefix + '/_index',
+      icon: 'fonticon-plus-circled'
     }];
 
     return _.reduce(FauxtonAPI.getExtensions('sidebar:links'), function (menuLinks, link) {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/routes-documents.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-documents.js b/app/addons/documents/routes-documents.js
index 7f8535c..82fcc35 100644
--- a/app/addons/documents/routes-documents.js
+++ b/app/addons/documents/routes-documents.js
@@ -20,7 +20,6 @@ define([
   'addons/documents/changes/components.react',
   'addons/documents/changes/actions',
   'addons/documents/views-doceditor',
-  'addons/documents/views-mango',
 
   'addons/databases/base',
   'addons/documents/resources',
@@ -33,11 +32,10 @@ define([
   'addons/documents/header/header.actions'
 ],
 
-function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, DocEditor, Mango,
+function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, DocEditor,
   Databases, Resources, Components, PaginationStores, IndexResultsActions,
   IndexResultsComponents, ReactPagination, ReactHeader, ReactActions) {
 
-
     var DocumentsRouteObject = BaseRoute.extend({
       layout: "with_tabs_sidebar",
       routes: {
@@ -138,7 +136,8 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, DocEdi
         IndexResultsActions.newResultsList({
           collection: collection,
           isListDeletable: true,
-          textEmptyIndex: 'No Document Created Yet!'
+          textEmptyIndex: 'No Document Created Yet!',
+          bulkCollection: Documents.BulkDeleteDocCollection
         });
 
         this.database.allDocs.paging.pageSize = PaginationStores.indexPaginationStore.getPerPage();

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/routes-index-editor.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-index-editor.js b/app/addons/documents/routes-index-editor.js
index 217cc7e..981d6f7 100644
--- a/app/addons/documents/routes-index-editor.js
+++ b/app/addons/documents/routes-index-editor.js
@@ -94,7 +94,8 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Documents, IndexEditorComponents,
 
       IndexResultsActions.newResultsList({
         collection: this.indexedDocs,
-        isListDeletable: false
+        isListDeletable: false,
+        bulkCollection: Documents.BulkDeleteDocCollection
       });
 
       ActionsIndexEditor.fetchDesignDocsBeforeEdit({
@@ -147,7 +148,8 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Documents, IndexEditorComponents,
 
       IndexResultsActions.newResultsList({
         collection: [],
-        isListDeletable: false
+        isListDeletable: false,
+        bulkCollection: Documents.BulkDeleteDocCollection
       });
     }
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/routes-mango.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-mango.js b/app/addons/documents/routes-mango.js
index d8c3d64..2bad50a 100644
--- a/app/addons/documents/routes-mango.js
+++ b/app/addons/documents/routes-mango.js
@@ -13,42 +13,45 @@
 define([
   'app',
   'api',
-
   // Modules
   'addons/documents/helpers',
   'addons/documents/shared-routes',
-  'addons/documents/views-mango',
   'addons/databases/resources',
+
   'addons/fauxton/components',
   'addons/documents/resources',
   'addons/documents/views',
-
   'addons/documents/index-results/actions',
   'addons/documents/pagination/stores',
+
   'addons/documents/header/header.react',
-  'addons/documents/header/header.actions'
+  'addons/documents/header/header.actions',
+  'addons/documents/pagination/pagination.react',
+
+  'addons/documents/mango/mango.components.react',
+  'addons/documents/mango/mango.actions',
+  'addons/documents/mango/mango.stores',
+  'addons/documents/index-results/index-results.components.react'
 
 ],
 
-function (app, FauxtonAPI, Helpers, BaseRoute, Mango, Databases,
-  Components, Resources, Documents, IndexResultsActions,
-  PaginationStores, ReactHeader, ReactActions) {
 
-  var MangoIndexList = BaseRoute.extend({
-    layout: 'with_tabs_sidebar',
+function (app, FauxtonAPI, Helpers, BaseRoute, Databases,
+  Components, Resources, Documents, IndexResultsActions, PaginationStores,
+  ReactHeader, ReactActions, ReactPagination,
+  MangoComponents, MangoActions, MangoStores, IndexResultsComponents) {
+
+  var MangoIndexEditorAndQueryEditor = BaseRoute.extend({
+    layout: 'two_pane',
     routes: {
-      'database/:database/_indexlist(:extra)': {
-        route: 'mangoIndexList',
+      'database/:database/_index': {
+        route: 'createIndex',
+        roles: ['fx_loggedIn']
+      },
+      'database/:database/_find': {
+        route: 'findUsingIndex',
         roles: ['fx_loggedIn']
       },
-
-    },
-
-    establish: function () {
-      return [
-        this.designDocs.fetch({reset: true}),
-        this.allDatabases.fetchOnce()
-      ];
     },
 
     initialize: function (route, masterLayout, options) {
@@ -62,15 +65,21 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Mango, Databases,
       this.addLeftHeader();
       this.addSidebar();
 
-      this.rightHeader = this.setView('#right-header', new Documents.Views.RightAllDocsHeader({
+      MangoActions.setDatabase({
         database: this.database
-      }));
+      });
     },
 
-    mangoIndexList: function () {
+    findUsingIndex: function () {
       var params = this.createParams(),
           urlParams = params.urlParams,
-          mangoIndexCollection = new Resources.MangoIndexCollection(null, {
+          mangoResultCollection = new Resources.MangoDocumentCollection(null, {
+            database: this.database,
+            paging: {
+              pageSize: PaginationStores.indexPaginationStore.getPerPage()
+            }
+          }),
+          mangoIndexList = new Resources.MangoIndexCollection(null, {
             database: this.database,
             params: null,
             paging: {
@@ -78,81 +87,85 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Mango, Databases,
             }
           });
 
-      ReactActions.resetHeaderController();
-
-      this.viewEditor && this.viewEditor.remove();
-      this.headerView && this.headerView.remove();
-
-      this.sidebar.setSelectedTab('mango-indexes');
+      // magic method
+      this.sidebar.setSelectedTab('mango-query');
+      this.setComponent('#react-headerbar', ReactHeader.HeaderBarController);
+      this.setComponent('#footer', ReactPagination.Footer);
 
-      IndexResultsActions.newResultsList({
-        collection: mangoIndexCollection,
-        isListDeletable: false
+      IndexResultsActions.newMangoResultsList({
+        collection: mangoResultCollection,
+        isListDeletable: true,
+        textEmptyIndex: 'No Results',
+        bulkCollection: Documents.BulkDeleteDocCollection
       });
 
-      this.setComponent('#react-headerbar', ReactHeader.HeaderBarController);
+      MangoActions.getIndexList({
+        indexList: mangoIndexList
+      });
 
-      this.leftheader.updateCrumbs(this.getCrumbs(this.database));
-      this.rightHeader.hideQueryOptions();
+      this.breadcrumbs = this.setView('#breadcrumbs', new Components.Breadcrumbs({
+        toggleDisabled: true,
+        crumbs: [
+          {'type': 'back', 'link': Databases.databaseUrl(this.database)},
+          {'name': app.i18n.en_US['mango-title-editor'], 'link': Databases.databaseUrl(this.database)}
+        ]
+      }));
 
-      this.resultList = this.setView('#dashboard-lower-content', new Mango.MangoIndexListReact());
+      this.setComponent('#left-content', MangoComponents.MangoQueryEditorController, {
+        description: app.i18n.en_US['mango-descripton'],
+        editorTitle: app.i18n.en_US['mango-title-editor'],
+        additionalIndexesText: app.i18n.en_US['mango-additional-indexes-heading']
+      });
+      this.setComponent('#dashboard-lower-content', IndexResultsComponents.List);
 
       this.apiUrl = function () {
-        return [mangoIndexCollection.urlRef(urlParams), FauxtonAPI.constants.DOC_URLS.GENERAL];
+        return [mangoResultCollection.urlRef('query-apiurl', urlParams), FauxtonAPI.constants.DOC_URLS.MANGO];
       };
-    }
-  });
-
-  var MangoIndexEditorAndResults = BaseRoute.extend({
-    layout: 'two_pane',
-    routes: {
-      'database/:database/_index': {
-        route: 'createIndex',
-        roles: ['fx_loggedIn']
-      }
-    },
-
-    initialize: function (route, masterLayout, options) {
-      var databaseName = options[0];
-
-      this.databaseName = databaseName;
-      this.database = new Databases.Model({id: databaseName});
     },
 
     createIndex: function (database) {
       var params = this.createParams(),
           urlParams = params.urlParams,
           mangoIndexCollection = new Resources.MangoIndexCollection(null, {
-            database: this.database
+            database: this.database,
+            params: null,
+            paging: {
+              pageSize: PaginationStores.indexPaginationStore.getPerPage()
+            }
           });
 
+
       IndexResultsActions.newResultsList({
         collection: mangoIndexCollection,
-        isListDeletable: false
+        isListDeletable: true,
+        bulkCollection: Documents.MangoBulkDeleteDocCollection,
+        typeOfIndex: 'mango'
       });
 
       this.breadcrumbs = this.setView('#breadcrumbs', new Components.Breadcrumbs({
         toggleDisabled: true,
         crumbs: [
-          {'type': 'back', 'link': Helpers.getPreviousPage(this.database)},
+          {'type': 'back', 'link': Databases.databaseUrl(this.database)},
           {'name': 'Create new index', 'link': Databases.databaseUrl(this.database) }
         ]
       }));
 
-      this.resultList = this.setView('#dashboard-lower-content', new Mango.HelpScreen());
+      ReactActions.resetHeaderController();
+      this.setComponent('#react-headerbar', ReactHeader.HeaderBarController);
+      this.setComponent('#footer', ReactPagination.Footer);
 
-      this.mangoEditor = this.setView('#left-content', new Mango.MangoIndexEditorReact({
-        database: this.database
-      }));
+      this.setComponent('#dashboard-lower-content', IndexResultsComponents.List);
+      this.setComponent('#left-content', MangoComponents.MangoIndexEditorController, {
+        description: app.i18n.en_US['mango-descripton-index-editor']
+      });
 
       this.apiUrl = function () {
-        return [mangoIndexCollection.urlRef(urlParams), FauxtonAPI.constants.DOC_URLS.GENERAL];
+        return [mangoIndexCollection.urlRef('index-apiurl', urlParams), FauxtonAPI.constants.DOC_URLS.MANGO];
       };
     }
   });
 
   return {
-    MangoIndexEditorAndResults: MangoIndexEditorAndResults,
-    MangoIndexList: MangoIndexList
+    MangoIndexEditorAndQueryEditor: MangoIndexEditorAndQueryEditor
   };
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/routes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes.js b/app/addons/documents/routes.js
index ad92ded..4401edc 100644
--- a/app/addons/documents/routes.js
+++ b/app/addons/documents/routes.js
@@ -25,8 +25,7 @@ function (Documents, DocumentsRouteObject, docEditor, IndexEditorRouteObject, Ma
     docEditor.NewDocEditorRouteObject,
     DocumentsRouteObject,
     IndexEditorRouteObject,
-    Mango.MangoIndexList,
-    Mango.MangoIndexEditorAndResults
+    Mango.MangoIndexEditorAndQueryEditor
   ];
 
   return Documents;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/a9e829c4/app/addons/documents/shared-resources.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/shared-resources.js b/app/addons/documents/shared-resources.js
index 35c0e51..00e0a59 100644
--- a/app/addons/documents/shared-resources.js
+++ b/app/addons/documents/shared-resources.js
@@ -58,13 +58,26 @@ define([
     },
 
     isDeletable: function () {
-      return true;
+      return !!this.id;
     },
 
     isFromView: function () {
       return !this.id;
     },
 
+    isMangoDoc: function () {
+      if (!this.isDdoc()) return false;
+      if (this.get('language') === 'query') {
+        return true;
+      }
+
+      if (this.get('doc') && this.get('doc').language === 'query') {
+        return true;
+      }
+
+      return false;
+    },
+
     isReducedShown : function () {
       if (this.collection) {
         return this.collection.params.reduce;
@@ -130,19 +143,6 @@ define([
       this.set({views: views});
     },
 
-    isMangoDoc: function () {
-      if (!this.isDdoc()) return false;
-      if (this.get('language') === 'query') {
-        return true;
-      }
-
-      if (this.get('doc') && this.get('doc').language === 'query') {
-        return true;
-      }
-
-      return false;
-    },
-
     dDocModel: function () {
       if (!this.isDdoc()) return false;
       var doc = this.get('doc');


Mime
View raw message