couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From robertkowal...@apache.org
Subject fauxton commit: updated refs/heads/master to 54b1185
Date Mon, 11 May 2015 16:11:53 GMT
Repository: couchdb-fauxton
Updated Branches:
  refs/heads/master 8f7ed8cb4 -> 54b118562


views: fix several issues with editing views

e.g.
 - saving views multiple times freezes the ui
 - reduce and map function is reset to default after changing the
   view name

closes COUCHDB-2662

PR: #402
PR-URL: https://github.com/apache/couchdb-fauxton/pull/402
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/54b11856
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/54b11856
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/54b11856

Branch: refs/heads/master
Commit: 54b118562813113d3ee93244ab8c41da01123e47
Parents: 8f7ed8c
Author: Robert Kowalski <robertkowalski@apache.org>
Authored: Tue Apr 14 17:42:04 2015 +0200
Committer: Robert Kowalski <robertkowalski@apache.org>
Committed: Mon May 11 18:14:36 2015 +0200

----------------------------------------------------------------------
 .../components/react-components.react.jsx       | 191 +++++++++++++++----
 app/addons/components/tests/codeEditorSpec.js   | 115 +++++++++++
 app/addons/components/tests/codeEditorSpec.jsx  | 115 +++++++++++
 app/addons/documents/helpers.js                 |   2 +-
 app/addons/documents/index-editor/actions.js    |  28 ++-
 .../documents/index-editor/actiontypes.js       |   6 +-
 .../documents/index-editor/components.react.jsx |  36 +++-
 app/addons/documents/index-editor/stores.js     |  57 ++++--
 .../mango/tests/mango.componentsSpec.react.jsx  |   4 +-
 app/addons/documents/routes-index-editor.js     |   4 +-
 app/addons/documents/shared-resources.js        |  14 ++
 app/addons/documents/tests/actionsSpec.js       |  56 +++++-
 .../documents/tests/nightwatch/changesFilter.js |   4 +-
 .../documents/tests/nightwatch/viewCreate.js    |  26 ++-
 .../tests/nightwatch/viewCreateBadView.js       |   9 +-
 .../documents/tests/nightwatch/viewEdit.js      |   2 +-
 .../tests/nightwatch/viewSaveManyTimes.js       |  50 +++++
 .../tests/viewIndex.componentsSpec.react.jsx    |  18 +-
 app/addons/documents/views-doceditor.js         |   2 +-
 app/core/routeObject.js                         |   3 +-
 20 files changed, 631 insertions(+), 111 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/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 fd4e2f5..3830073 100644
--- a/app/addons/components/react-components.react.jsx
+++ b/app/addons/components/react-components.react.jsx
@@ -15,10 +15,11 @@ define([
   'api',
   'react',
   'addons/fauxton/components',
+  'ace/ace',
   'plugins/beautify'
 ],
 
-function (app, FauxtonAPI, React, Components, beautifyHelper) {
+function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
 
   var ToggleHeaderButton = React.createClass({
     render: function () {
@@ -63,15 +64,138 @@ function (app, FauxtonAPI, React, Components, beautifyHelper) {
   });
 
   var CodeEditor = React.createClass({
-    render: function () {
-      var code = this.aceEditor ? this.aceEditor.getValue() : this.props.code;
-      return (
-        <div className="control-group">
-          {this.getTitleFragment()}
-          <div className="js-editor" id={this.props.id}>{this.props.code}</div>
-          <Beautify code={code} beautifiedCode={this.setEditorValue} />
-        </div>
-      );
+    getDefaultProps: function () {
+      return {
+        id: 'code-editor',
+        mode: 'javascript',
+        theme: 'idle_fingers',
+        fontSize: 13,
+        code: '',
+        showGutter: true,
+        highlightActiveLine: true,
+        showPrintMargin: false,
+        autoScrollEditorIntoView: true,
+        setHeightWithJS: true,
+        isFullPageEditor: false,
+        change: function () {}
+      };
+    },
+
+    hasChanged: function () {
+      return !_.isEqual(this.props.code, this.getValue());
+    },
+
+    setupAce: function (props, shouldUpdateCode) {
+      var el = this.getDOMNode(this.refs.ace);
+      //set the id so our nightwatch tests can find it
+      el.id = props.id;
+
+      this.editor = ace.edit(el);
+      // Automatically scrolling cursor into view after selection
+      // change this will be disabled in the next version
+      // set editor.$blockScrolling = Infinity to disable this message
+      this.editor.$blockScrolling = Infinity;
+
+      if (shouldUpdateCode) {
+        this.setEditorValue(props.code);
+      }
+
+      this.editor.setShowPrintMargin(props.showPrintMargin);
+      this.editor.autoScrollEditorIntoView = props.autoScrollEditorIntoView;
+      this.setHeightToLineCount();
+      this.removeIncorrectAnnotations();
+      this.editor.getSession().setMode("ace/mode/" + props.mode);
+      this.editor.setTheme("ace/theme/" + props.theme);
+      this.editor.setFontSize(props.fontSize);
+
+    },
+
+    setupEvents: function () {
+      $(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 () {
+      this.props.change(this.getValue());
+    },
+
+    quitWarningMsg: function () {
+      if (this.hasChanged()) {
+        return 'Your changes have not been saved. Click cancel to return to the document.';
+      }
+    },
+
+    removeEvents: function () {
+      $(window).off('beforeunload.editor_' + this.props.id);
+      FauxtonAPI.removeBeforeUnload('editor_' + this.props.id);
+    },
+
+    setHeightToLineCount: function () {
+      if (!this.props.setHeightWithJS) {
+        return;
+      }
+
+      var lines = this.editor.getSession().getDocument().getLength();
+
+      if (this.props.isFullPageEditor) {
+        var maxLines = this.getMaxAvailableLinesOnPage();
+        lines = lines < maxLines ? lines : maxLines;
+      }
+      this.editor.setOptions({
+        maxLines: lines
+      });
+    },
+
+    // List of JSHINT errors to ignore
+    // Gets around problem of anonymous functions not being a valid statement
+    excludedViewErrors: [
+      "Missing name in function declaration.",
+      "['{a}'] is better written in dot notation."
+    ],
+
+    isIgnorableError: function (msg) {
+      return _.contains(this.excludedViewErrors, msg);
+    },
+
+    removeIncorrectAnnotations: function () {
+      var editor = this.editor,
+          isIgnorableError = this.isIgnorableError;
+
+      this.editor.getSession().on("changeAnnotation", function () {
+        var annotations = editor.getSession().getAnnotations();
+
+        var newAnnotations = _.reduce(annotations, function (annotations, error) {
+          if (!isIgnorableError(error.raw)) {
+            annotations.push(error);
+          }
+          return annotations;
+        }, []);
+
+        if (annotations.length !== newAnnotations.length) {
+          editor.getSession().setAnnotations(newAnnotations);
+        }
+      });
+    },
+
+    componentDidMount: function () {
+      this.setupAce(this.props, true);
+      this.setupEvents();
+    },
+
+    componentWillUnmount: function () {
+      this.removeEvents();
+      this.editor.destroy();
+    },
+
+    componentWillReceiveProps: function (nextProps) {
+      var codeChanged = !_.isEqual(nextProps.code, this.getValue());
+      this.setupAce(nextProps, codeChanged);
+    },
+
+    editSaved: function () {
+      return this.hasChanged();
     },
 
     getTitleFragment: function () {
@@ -94,40 +218,39 @@ function (app, FauxtonAPI, React, Components, beautifyHelper) {
       );
     },
 
-    setEditorValue: function (code) {
-      this.aceEditor.setValue(code);
-      //this is not a good practice normally but because we working with a backbone view
as the mapeditor
-      //that keeps the map code state this is the best way to force a render so that the
beautify button will hide
-      this.forceUpdate();
+    getAnnotations: function () {
+      return this.editor.getSession().getAnnotations();
     },
 
-    getValue: function () {
-      return this.aceEditor.getValue();
+    hadValidCode: function () {
+      var errors = this.getAnnotations();
+      // By default CouchDB view functions don't pass lint
+      return _.every(errors, function (error) {
+        return this.isIgnorableError(error.raw);
+      }, this);
     },
 
-    getEditor: function () {
-      return this.aceEditor;
+    setEditorValue: function (code, lineNumber) {
+      lineNumber = lineNumber ? lineNumber : -1;
+      this.editor.setValue(code, lineNumber);
     },
 
-    componentDidMount: function () {
-      this.aceEditor = new Components.Editor({
-        editorId: this.props.id,
-        mode: 'javascript',
-        couchJSHINT: true
-      });
-      this.aceEditor.render();
+    getValue: function () {
+      return this.editor.getValue();
     },
 
-    shouldComponentUpdate: function () {
-      //we don't want to re-render the map editor as we are using backbone underneath
-      //which will cause the editor to break
-      this.aceEditor.editSaved();
-
-      return false;
+    getEditor: function () {
+      return this;
     },
 
-    componentWillUnmount: function () {
-      this.aceEditor.remove();
+    render: function () {
+      return (
+        <div className="control-group">
+          {this.getTitleFragment()}
+          <div ref="ace" className="js-editor" id={this.props.id}></div>
+          <Beautify code={this.props.code} beautifiedCode={this.setEditorValue} />
+        </div>
+      );
     }
 
   });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/components/tests/codeEditorSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/codeEditorSpec.js b/app/addons/components/tests/codeEditorSpec.js
new file mode 100644
index 0000000..62e7c3a
--- /dev/null
+++ b/app/addons/components/tests/codeEditorSpec.js
@@ -0,0 +1,115 @@
+// 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/components/react-components.react',
+
+  'testUtils',
+  'react'
+], function (FauxtonAPI, ReactComponents, utils, React) {
+
+  var assert = utils.assert;
+  var TestUtils = React.addons.TestUtils;
+  var code = 'function (doc) {\n  emit(doc._id, 1);\n}';
+  var code2 = 'function (doc) {\n if(doc._id) { \n emit(doc._id, 2); \n } \n}';
+
+  describe('Code Editor', function () {
+    var container, codeEditorEl, spy;
+
+    beforeEach(function () {
+      spy = sinon.spy();
+      container = document.createElement('div');
+      codeEditorEl = TestUtils.renderIntoDocument(
+        React.createElement(ReactComponents.CodeEditor, {code: code, change: spy}),
+        container
+      );
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+    describe('Tracking edits', function () {
+
+      it('no change on mount', function () {
+        assert.notOk(codeEditorEl.hasChanged());
+      });
+
+      it('detects change on user input', function () {
+        codeEditorEl.editor.setValue(code2, -1);
+
+        assert.ok(codeEditorEl.hasChanged());
+      });
+
+    });
+
+    describe('onBlur', function () {
+
+      it('calls changed function', function () {
+        codeEditorEl.editor._emit('blur');
+        assert.ok(spy.calledOnce);
+      });
+
+    });
+
+    describe('setHeightToLineCount', function () {
+
+      beforeEach(function () {
+        codeEditorEl = TestUtils.renderIntoDocument(
+          React.createElement(ReactComponents.CodeEditor, {code: code, isFullPageEditor:
false, setHeightWithJS: true}),
+          container
+        );
+
+      });
+
+      it('sets line height correctly for non full page', function () {
+        var spy = sinon.spy(codeEditorEl.editor, 'setOptions');
+
+        codeEditorEl.setHeightToLineCount();
+        assert.ok(spy.calledOnce);
+        assert.equal(spy.getCall(0).args[0].maxLines, 3);
+      });
+
+    });
+
+    describe('removeIncorrectAnnotations', function () {
+
+      beforeEach(function () {
+        codeEditorEl = TestUtils.renderIntoDocument(
+          React.createElement(ReactComponents.CodeEditor, {code: code}),
+          container
+        );
+
+      });
+
+      it('removes default errors that do not apply to CouchDB Views', function () {
+        assert.equal(codeEditorEl.getAnnotations(), 0);
+      });
+
+    });
+
+    describe('setEditorValue', function () {
+
+      it('sets new code', function () {
+        codeEditorEl = TestUtils.renderIntoDocument(
+          React.createElement(ReactComponents.CodeEditor, {code: code}),
+          container
+        );
+
+        codeEditorEl.setEditorValue(code2);
+        assert.deepEqual(codeEditorEl.getValue(), code2);
+      });
+
+    });
+
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/components/tests/codeEditorSpec.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/codeEditorSpec.jsx b/app/addons/components/tests/codeEditorSpec.jsx
new file mode 100644
index 0000000..849f36c
--- /dev/null
+++ b/app/addons/components/tests/codeEditorSpec.jsx
@@ -0,0 +1,115 @@
+// 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/components/react-components.react',
+
+  'testUtils',
+  'react'
+], function (FauxtonAPI, ReactComponents, utils, React) {
+
+  var assert = utils.assert;
+  var TestUtils = React.addons.TestUtils;
+  var code = 'function (doc) {\n  emit(doc._id, 1);\n}';
+  var code2 = 'function (doc) {\n if(doc._id) { \n emit(doc._id, 2); \n } \n}';
+
+  describe('Code Editor', function () {
+    var container, codeEditorEl, spy;
+
+    beforeEach(function () {
+      spy = sinon.spy();
+      container = document.createElement('div');
+      codeEditorEl = TestUtils.renderIntoDocument(
+        <ReactComponents.CodeEditor code={code} change={spy} />,
+        container
+      );
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+    describe('Tracking edits', function () {
+
+      it('no change on mount', function () {
+        assert.notOk(codeEditorEl.hasChanged());
+      });
+
+      it('detects change on user input', function () {
+        codeEditorEl.editor.setValue(code2, -1);
+
+        assert.ok(codeEditorEl.hasChanged());
+      });
+
+    });
+
+    describe('onBlur', function () {
+
+      it('calls changed function', function () {
+        codeEditorEl.editor._emit('blur');
+        assert.ok(spy.calledOnce);
+      });
+
+    });
+
+    describe('setHeightToLineCount', function () {
+
+      beforeEach(function () {
+        codeEditorEl = TestUtils.renderIntoDocument(
+          <ReactComponents.CodeEditor code={code} isFullPageEditor={false}  setHeightWithJS={true}/>,
+          container
+        );
+
+      });
+
+      it('sets line height correctly for non full page', function () {
+        var spy = sinon.spy(codeEditorEl.editor, 'setOptions');
+
+        codeEditorEl.setHeightToLineCount();
+        assert.ok(spy.calledOnce);
+        assert.equal(spy.getCall(0).args[0].maxLines, 3);
+      });
+
+    });
+
+    describe('removeIncorrectAnnotations', function () {
+
+      beforeEach(function () {
+        codeEditorEl = TestUtils.renderIntoDocument(
+          <ReactComponents.CodeEditor code={code}/>,
+          container
+        );
+
+      });
+
+      it('removes default errors that do not apply to CouchDB Views', function () {
+        assert.equal(codeEditorEl.getAnnotations(), 0);
+      });
+
+    });
+
+    describe('setEditorValue', function () {
+
+      it('sets new code', function () {
+        codeEditorEl = TestUtils.renderIntoDocument(
+          <ReactComponents.CodeEditor code={code}/>,
+          container
+        );
+
+        codeEditorEl.setEditorValue(code2);
+        assert.deepEqual(codeEditorEl.getValue(), code2);
+      });
+
+    });
+
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/helpers.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/helpers.js b/app/addons/documents/helpers.js
index e04e3b4..1d156b2 100644
--- a/app/addons/documents/helpers.js
+++ b/app/addons/documents/helpers.js
@@ -23,7 +23,7 @@ define([
     if (!wasCloned && lastPages.length >= 2) {
 
       // if we came from "/new", we don't want to link the user there
-      if (/new$/.test(lastPages[1])) {
+      if (/(new|new_view)$/.test(lastPages[1])) {
         previousPage = lastPages[0];
       } else {
         previousPage = lastPages[1];

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/index-editor/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/actions.js b/app/addons/documents/index-editor/actions.js
index a7b33c7..b5250a0 100644
--- a/app/addons/documents/index-editor/actions.js
+++ b/app/addons/documents/index-editor/actions.js
@@ -107,9 +107,6 @@ function (app, FauxtonAPI, Documents, ActionTypes, IndexResultsActions)
{
                             viewInfo.reduce);
 
       if (result) {
-        FauxtonAPI.dispatch({
-         type: ActionTypes.SAVE_VIEW
-        });
 
         FauxtonAPI.addNotification({
           msg:  "Saving View...",
@@ -124,19 +121,29 @@ function (app, FauxtonAPI, Documents, ActionTypes, IndexResultsActions)
{
             clear: true
           });
 
+
           if (_.any([viewInfo.designDocChanged, viewInfo.hasViewNameChanged, viewInfo.newDesignDoc,
viewInfo.newView])) {
             FauxtonAPI.dispatch({
               type: ActionTypes.VIEW_SAVED
             });
             var fragment = FauxtonAPI.urls('view', 'showNewlySavedView', viewInfo.database.safeID(),
designDoc.safeID(), app.utils.safeURLName(viewInfo.viewName));
             FauxtonAPI.navigate(fragment, {trigger: true});
+          } else {
+            this.updateDesignDoc(designDoc);
           }
 
           IndexResultsActions.reloadResultsList();
-        });
+        }.bind(this));
       }
     },
 
+    updateDesignDoc: function (designDoc) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.VIEW_UPDATE_DESIGN_DOC,
+        designDoc: designDoc.toJSON()
+      });
+    },
+
     deleteView: function (options) {
       var viewName = options.viewName;
       var database = options.database;
@@ -156,7 +163,20 @@ function (app, FauxtonAPI, Documents, ActionTypes, IndexResultsActions)
{
         FauxtonAPI.navigate(url);
         FauxtonAPI.triggerRouteEvent('reloadDesignDocs');
       });
+    },
 
+    updateMapCode: function (code) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.VIEW_UPDATE_MAP_CODE,
+        code: code
+      });
+    },
+
+    updateReduceCode: function (code) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.VIEW_UPDATE_REDUCE_CODE,
+        code: code
+      });
     }
   };
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/index-editor/actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/actiontypes.js b/app/addons/documents/index-editor/actiontypes.js
index fcce54e..e13c57a 100644
--- a/app/addons/documents/index-editor/actiontypes.js
+++ b/app/addons/documents/index-editor/actiontypes.js
@@ -17,9 +17,11 @@ define([], function () {
     SELECT_REDUCE_CHANGE: 'SELECT_REDUCE_CHANGE',
     VIEW_SAVED: 'VIEW_SAVED',
     VIEW_CREATED: 'VIEW_CREATED',
-    SAVE_VIEW: 'SAVE_VIEW',
     DESIGN_DOC_CHANGE: 'DESIGN_DOC_CHANGE',
     NEW_DESIGN_DOC: 'NEW_DESIGN_DOC',
-    VIEW_NAME_CHANGE: 'VIEW_NAME_CHANGE'
+    VIEW_NAME_CHANGE: 'VIEW_NAME_CHANGE',
+    VIEW_UPDATE_DESIGN_DOC: 'VIEW_UPDATE_DESIGN_DOC',
+    VIEW_UPDATE_MAP_CODE: 'VIEW_UPDATE_MAP_CODE',
+    VIEW_UPDATE_REDUCE_CODE: 'VIEW_UPDATE_REDUCE_CODE'
   };
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/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 c38be29..1ba8bc6 100644
--- a/app/addons/documents/index-editor/components.react.jsx
+++ b/app/addons/documents/index-editor/components.react.jsx
@@ -27,6 +27,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
   var CodeEditor = ReactComponents.CodeEditor;
   var PaddedBorderedBox = ReactComponents.PaddedBorderedBox;
   var ConfirmButton = ReactComponents.ConfirmButton;
+  var LoadLines = ReactComponents.LoadLines;
 
   var DesignDocSelector = React.createClass({
 
@@ -58,7 +59,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
 
     getDesignDocOptions: function () {
       return this.state.designDocs.map(function (doc, i) {
-        return <option key={i} value={doc.id}> {doc.id} </option>;
+        return <option key={i} value={doc.id}>{doc.id}</option>;
       });
     },
 
@@ -67,7 +68,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
 
       return (
         <optgroup label="Select a document">
-          <option value="new">New Design Document </option>
+          <option value="new">New Design Document</option>
           {designDocOptions}
         </optgroup>
       );
@@ -151,7 +152,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
 
     getOptionsList: function () {
       return _.map(this.state.reduceOptions, function (reduce, i) {
-        return <option key={i} value={reduce}> {reduce} </option>;
+        return <option key={i} value={reduce}>{reduce}</option>;
       }, this);
 
     },
@@ -174,10 +175,15 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
 
     render: function () {
       var reduceOptions = this.getOptionsList(),
-      customReduceSection;
+          customReduceSection;
 
       if (this.state.hasCustomReduce) {
-        customReduceSection = <CodeEditor ref='reduceEditor' id={'reduce-function'} code={this.state.reduce}
docs={false} title={'Custom Reduce function'} />;
+        customReduceSection = <CodeEditor
+          ref='reduceEditor'
+          id='reduce-function'
+          code={this.state.reduce}
+          change={this.updateReduceCode}
+          docs={false} title={'Custom Reduce function'} />;
       }
 
       return (
@@ -206,6 +212,10 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
       );
     },
 
+    updateReduceCode: function (code) {
+      Actions.updateReduceCode(code);
+    },
+
     selectChange: function (event) {
       Actions.selectReduceChanged(event.target.value);
     },
@@ -278,7 +288,8 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
         hasDesignDocChanged: indexEditorStore.hasDesignDocChanged(),
         newDesignDoc: indexEditorStore.isNewDesignDoc(),
         designDocId: indexEditorStore.getDesignDocId(),
-        map: indexEditorStore.getMap()
+        map: indexEditorStore.getMap(),
+        isLoading: indexEditorStore.isLoading()
       };
     },
 
@@ -350,7 +361,19 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
       Actions.changeViewName(event.target.value);
     },
 
+    updateMapCode: function (code) {
+      Actions.updateMapCode(code);
+    },
+
     render: function () {
+      if (this.state.isLoading) {
+        return (
+          <div className="define-view">
+            <LoadLines />
+          </div>
+        );
+      }
+
       return (
         <div className="define-view">
           <PaddedBorderedBox>
@@ -389,6 +412,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
                   ref="mapEditor"
                   title={"Map function"}
                   docs={getDocUrl('MAP_FUNCS')}
+                  change={this.updateMapCode}
                   code={this.state.map} />
               </PaddedBorderedBox>
             </div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/index-editor/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/stores.js b/app/addons/documents/index-editor/stores.js
index 9839001..3f7bb7d 100644
--- a/app/addons/documents/index-editor/stores.js
+++ b/app/addons/documents/index-editor/stores.js
@@ -25,13 +25,9 @@ function (FauxtonAPI, ActionTypes) {
 
     initialize: function () {
       this._designDocs = [];
-      this._view = {
-        reduce: this.defaultMap,
-        map: this.defaultReduce
-      };
-      this._database = {
-        id: '0'
-      };
+      this._isLoading = true;
+      this._view = { reduce: '', map: this.defaultMap };
+      this._database = { id: '0' };
     },
 
     editIndex: function (options) {
@@ -43,14 +39,19 @@ function (FauxtonAPI, ActionTypes) {
       this._designDocId = options.designDocId;
       this._designDocChanged = false;
       this._viewNameChanged = false;
+      this.setView();
+      this._isLoading = false;
+    },
 
-      if (!this._newView && !this._newDesignDoc) {
-        this._view = this.getDesignDoc().get('views')[this._viewName];
+    isLoading: function () {
+      return this._isLoading;
+    },
+
+    setView: function () {
+      if (this._newView || this._newDesignDoc) {
+        this._view = { reduce: '', map: this.defaultMap };
       } else {
-        this._view = {
-          reduce: '',
-          map: ''
-        };
+        this._view = this.getDesignDoc().get('views')[this._viewName];
       }
     },
 
@@ -59,13 +60,13 @@ function (FauxtonAPI, ActionTypes) {
     },
 
     getMap: function () {
-      if (this._newView) {
-        return this.defaultMap;
-      }
-
       return this._view.map;
     },
 
+    setMap: function (map) {
+      this._view.map = map;
+    },
+
     getReduce: function () {
       return this._view.reduce;
     },
@@ -83,7 +84,7 @@ function (FauxtonAPI, ActionTypes) {
 
     getDesignDocs: function () {
       return this._designDocs.filter(function (ddoc) {
-        return ddoc.get('doc').language !== 'query';
+        return !ddoc.isMangoDoc();
       });
     },
 
@@ -168,6 +169,10 @@ function (FauxtonAPI, ActionTypes) {
       this.setReduce(selectedReduce);
     },
 
+    updateDesignDoc: function (designDoc) {
+      this._designDocs.add(designDoc, {merge: true});
+    },
+
     dispatch: function (action) {
       switch (action.type) {
         case ActionTypes.EDIT_INDEX:
@@ -208,6 +213,22 @@ function (FauxtonAPI, ActionTypes) {
           this.triggerChange();
         break;
 
+        case ActionTypes.VIEW_UPDATE_DESIGN_DOC:
+          this.updateDesignDoc(action.designDoc);
+          this.setView();
+          this.triggerChange();
+        break;
+
+        case ActionTypes.VIEW_UPDATE_MAP_CODE:
+          this.setMap(action.code);
+          this.triggerChange();
+        break;
+
+        case ActionTypes.VIEW_UPDATE_REDUCE_CODE:
+          this.setReduce(action.code);
+          this.triggerChange();
+        break;
+
         default:
         return;
         // do nothing

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/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 c50897b..0f71fa3 100644
--- a/app/addons/documents/mango/tests/mango.componentsSpec.react.jsx
+++ b/app/addons/documents/mango/tests/mango.componentsSpec.react.jsx
@@ -60,18 +60,16 @@ define([
       MangoActions.setDatabase({
         database: database
       });
-      $('body').append('<div id="query-field"></div>');
     });
 
     afterEach(function () {
       React.unmountComponentAtNode(container);
-      $('#query-field').remove();
     });
 
     it('renders a default index definition', function () {
       editor = TestUtils.renderIntoDocument(<Views.MangoIndexEditorController description="foo"
/>, container);
       var $el = $(editor.getDOMNode());
-      var payload = JSON.parse($el.find('.js-editor').text());
+      var payload = JSON.parse(editor.refs.indexQueryEditor.getValue());
       assert.equal(payload.index.fields[0], '_id');
     });
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/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 e04b35b..b15177c 100644
--- a/app/addons/documents/routes-index-editor.js
+++ b/app/addons/documents/routes-index-editor.js
@@ -126,8 +126,8 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Documents, IndexEditorComponents,
       this.breadcrumbs = this.setView('#breadcrumbs', new Components.Breadcrumbs({
         toggleDisabled: true,
         crumbs: [
-          {'type': 'back', 'link': Helpers.getPreviousPage(this.database)},
-          {'name': 'Create new index', 'link': Databases.databaseUrl(this.database) }
+          { type: 'back', link: Helpers.getPreviousPage(this.database) },
+          { name: 'Create new index', link: Databases.databaseUrl(this.database) }
         ]
       }));
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/shared-resources.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/shared-resources.js b/app/addons/documents/shared-resources.js
index a78709a..35c0e51 100644
--- a/app/addons/documents/shared-resources.js
+++ b/app/addons/documents/shared-resources.js
@@ -130,11 +130,25 @@ 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');
 
       if (doc) {
+        doc._rev = this.get('_rev');
         return new Documents.Doc(doc, {database: this.database});
       }
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/tests/actionsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/actionsSpec.js b/app/addons/documents/tests/actionsSpec.js
index d0a44bd..dec71fb 100644
--- a/app/addons/documents/tests/actionsSpec.js
+++ b/app/addons/documents/tests/actionsSpec.js
@@ -15,10 +15,11 @@ define([
   'addons/documents/index-editor/actions',
   'addons/documents/resources',
   'addons/documents/index-editor/actiontypes',
+  'addons/documents/index-editor/stores',
   'testUtils',
   'addons/documents/index-results/actions',
   'addons/documents/base'
-], function (FauxtonAPI, Actions, Documents, ActionTypes, testUtils, IndexResultsActions)
{
+], function (FauxtonAPI, Actions, Documents, ActionTypes, Stores, testUtils, IndexResultsActions)
{
   var assert = testUtils.assert;
   var restore = testUtils.restore;
 
@@ -34,14 +35,15 @@ define([
       beforeEach(function () {
         designDoc = {
           _id: '_design/test-doc',
+          _rev: '1-231313',
           views: {
             'test-view': {
               map: 'function () {};',
             }
           }
         };
-
-        designDocs = new Documents.AllDocs([designDoc], {
+        var doc = new Documents.Doc(designDoc, {database: database});
+        designDocs = new Documents.AllDocs([doc], {
           params: { limit: 10 },
           database: database
         });
@@ -53,6 +55,7 @@ define([
         restore(FauxtonAPI.navigate);
         restore(FauxtonAPI.triggerRouteEvent);
         restore(IndexResultsActions.reloadResultsList);
+        restore(Actions.updateDesignDoc);
       });
 
       it('shows a notification if no design doc id given', function () {
@@ -127,6 +130,45 @@ define([
         assert.ok(spy.calledOnce);
       });
 
+      it('updates design doc', function () {
+        var viewInfo = {
+          viewName: 'test-view',
+          designDocId: '_design/test-doc',
+          map: 'map',
+          reduce: '_sum',
+          newDesignDoc: false,
+          newView: false,
+          designDocs: designDocs,
+          database: {
+            safeID: function () { return '1';}
+          }
+        };
+
+        designDocs.find = function () {};
+        designDocs.add = function () {};
+        designDocs.dDocModel = function () {};
+
+        Actions.editIndex({
+          database: {id: 'rockos-db'},
+          newView: true,
+          viewName: 'test-view',
+          designDocs: designDocs,
+          designDocId: designDocs[0]._id
+        });
+
+        var promise = FauxtonAPI.Deferred();
+        promise.resolve();
+
+        var updatedDesignDoc = _.first(designDocs).dDocModel();
+        var stub = sinon.stub(updatedDesignDoc, 'save');
+        stub.returns(promise);
+
+        var spy = sinon.spy(Actions, 'updateDesignDoc');
+        Actions.saveView(viewInfo);
+
+        assert.ok(spy.calledOnce);
+      });
+
       it('navigates to new url for new view', function () {
         var spy = sinon.spy(FauxtonAPI, 'navigate');
 
@@ -178,6 +220,9 @@ define([
           return promise;
         };
 
+        var stub = sinon.stub(Actions, 'updateDesignDoc');
+        stub.returns(true);
+
         Actions.saveView(viewInfo);
         assert.ok(spy.calledOnce);
       });
@@ -194,6 +239,7 @@ define([
         designDocId = '_design/test-doc';
         designDocs = new Documents.AllDocs([{
           _id: designDocId ,
+          _rev: '1-231',
           views: {
               'test-view': {
                 map: 'function () {};',
@@ -212,8 +258,8 @@ define([
       });
 
       afterEach(function () {
-        FauxtonAPI.navigate.restore && FauxtonAPI.navigate.restore();
-        FauxtonAPI.triggerRouteEvent.restore && FauxtonAPI.triggerRouteEvent.restore();
+        restore(FauxtonAPI.navigate);
+        restore(FauxtonAPI.triggerRouteEvent);
       });
 
       it('removes view from design doc', function () {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/tests/nightwatch/changesFilter.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/nightwatch/changesFilter.js b/app/addons/documents/tests/nightwatch/changesFilter.js
index e2e4e5b..73846d8 100644
--- a/app/addons/documents/tests/nightwatch/changesFilter.js
+++ b/app/addons/documents/tests/nightwatch/changesFilter.js
@@ -33,10 +33,10 @@ module.exports = {
       .waitForElementPresent('.change-box[data-id="doc_3"]', waitTime, false)
 
       // add a filter
-      .click("#db-views-tabs-nav a")
+      .clickWhenVisible("#db-views-tabs-nav a")
       .waitForElementVisible('.js-changes-filter-field', waitTime, false)
       .setValue('.js-changes-filter-field', "doc_1")
-      .click('.js-filter-form button[type="submit"]')
+      .clickWhenVisible('.js-filter-form button[type="submit"]')
 
       // confirm only the single result is now listed in the page
       .waitForElementVisible('span.label-info', waitTime, false)

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/tests/nightwatch/viewCreate.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/nightwatch/viewCreate.js b/app/addons/documents/tests/nightwatch/viewCreate.js
index 61d8dcc..b96eab6 100644
--- a/app/addons/documents/tests/nightwatch/viewCreate.js
+++ b/app/addons/documents/tests/nightwatch/viewCreate.js
@@ -10,16 +10,11 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-var waitTime,
-    baseUrl,
-    newDatabaseName,
-    newDocumentName,
-    modifier;
-
-var tests = {
+module.exports = {
 
   'Creates a Design Doc using the dropdown at "all documents"': function (client) {
     var waitTime = client.globals.maxWaitTime;
+    var baseUrl = client.globals.test_settings.launch_url;
 
     /*jshint multistr: true */
     openDifferentDropdownsAndClick(client, '#header-dropdown-menu')
@@ -41,6 +36,7 @@ var tests = {
 
   'Creates a Design Doc using the dropdown at "the upper dropdown in the header"': function
(client) {
     var waitTime = client.globals.maxWaitTime;
+    var baseUrl = client.globals.test_settings.launch_url;
 
     /*jshint multistr: true */
     openDifferentDropdownsAndClick(client, '#header-dropdown-menu')
@@ -48,6 +44,7 @@ var tests = {
       .setValue('#new-ddoc', 'test_design_doc-selenium-2')
       .clearValue('#index-name')
       .setValue('#index-name', 'gaenseindex')
+      .sendKeys("textarea.ace_text-input", client.Keys.Enter)
       .execute('\
         var editor = ace.edit("map-function");\
         editor.getSession().setValue("function (doc) { emit(\'gansgans\'); }");\
@@ -62,11 +59,14 @@ var tests = {
 
   'Adds a View to a DDoc using an existing DDoc': function (client) {
     var waitTime = client.globals.maxWaitTime;
+    var baseUrl = client.globals.test_settings.launch_url;
+    var newDatabaseName = client.globals.testDatabaseName;
     /*jshint multistr: true */
 
     openDifferentDropdownsAndClick(client, '#nav-header-testdesigndoc')
       .clearValue('#index-name')
       .setValue('#index-name', 'test-new-view')
+      .sendKeys("textarea.ace_text-input", client.Keys.Enter)
       .execute('\
         var editor = ace.edit("map-function");\
         editor.getSession().setValue("function (doc) { emit(\'enteente\', 1); }");\
@@ -89,11 +89,11 @@ var tests = {
 };
 
 function openDifferentDropdownsAndClick (client, dropDownElement) {
-  modifier = dropDownElement.slice(1);
-  waitTime = client.globals.maxWaitTime;
-  newDatabaseName = client.globals.testDatabaseName;
-  newDocumentName = 'create_view_doc' + modifier;
-  baseUrl = client.globals.test_settings.launch_url;
+  var modifier = dropDownElement.slice(1);
+  var waitTime = client.globals.maxWaitTime;
+  var newDatabaseName = client.globals.testDatabaseName;
+  var newDocumentName = 'create_view_doc' + modifier;
+  var baseUrl = client.globals.test_settings.launch_url;
 
   return client
     .loginToGUI()
@@ -104,5 +104,3 @@ function openDifferentDropdownsAndClick (client, dropDownElement) {
     .click(dropDownElement + ' a[href*="new_view"]')
     .waitForElementPresent('.editor-wrapper', waitTime, false);
 }
-
-module.exports = tests;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/tests/nightwatch/viewCreateBadView.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/nightwatch/viewCreateBadView.js b/app/addons/documents/tests/nightwatch/viewCreateBadView.js
index 19a79bd..f82f7af 100644
--- a/app/addons/documents/tests/nightwatch/viewCreateBadView.js
+++ b/app/addons/documents/tests/nightwatch/viewCreateBadView.js
@@ -22,17 +22,20 @@ module.exports = {
     client
       .loginToGUI()
       .populateDatabase(newDatabaseName)
-      .url(baseUrl + '/#/database/' + newDatabaseName + '/new_view')
+      .url(baseUrl + '/#/database/' + newDatabaseName + '/_all_docs')
+      .waitForElementPresent(dropDownElement, waitTime, false)
+      .click(dropDownElement + ' a')
+      .click(dropDownElement + ' a[href*="new_view"]')
       .waitForElementVisible('#new-ddoc', waitTime, false)
       .setValue('#new-ddoc', 'test_design_doc-selenium-bad-reduce')
       .clearValue('#index-name')
       .setValue('#index-name', 'hasenindex')
+      .click('#reduce-function-selector')
+      .keys(['\uE013', '\uE013', '\uE013', '\uE013', '\uE006'])
       .execute('\
         var editor = ace.edit("map-function");\
         editor.getSession().setValue("function (doc) { emit(\'boom\', doc._id); }");\
       ')
-      .click('#reduce-function-selector')
-      .keys(['\uE013', '\uE013', '\uE013', '\uE013', '\uE006'])
       .execute('$(".save")[0].scrollIntoView();')
       .click('button.btn-success.save')
       .waitForElementVisible('.alert-error', waitTime, false)

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/tests/nightwatch/viewEdit.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/nightwatch/viewEdit.js b/app/addons/documents/tests/nightwatch/viewEdit.js
index f23cda2..cb04c99 100644
--- a/app/addons/documents/tests/nightwatch/viewEdit.js
+++ b/app/addons/documents/tests/nightwatch/viewEdit.js
@@ -55,10 +55,10 @@ module.exports = {
       .execute('\
         var editor = ace.edit("map-function");\
         editor.getSession().setValue("function (doc) { emit(\'hasehase5000\', 1); }");\
+        editor._emit(\'blur\');\
       ')
       .execute('$(".save")[0].scrollIntoView();')
       .clickWhenVisible('button.btn-success.save')
-
       .waitForElementNotVisible('.global-notification', waitTime, false)
       .waitForElementNotPresent('.loading-lines', waitTime, false)
       .assert.containsText('.prettyprint', 'hasehase5000')

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/tests/nightwatch/viewSaveManyTimes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/nightwatch/viewSaveManyTimes.js b/app/addons/documents/tests/nightwatch/viewSaveManyTimes.js
new file mode 100644
index 0000000..6f6570d
--- /dev/null
+++ b/app/addons/documents/tests/nightwatch/viewSaveManyTimes.js
@@ -0,0 +1,50 @@
+// 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.
+
+module.exports = {
+
+  'View can be saved multiple times': function (client) {
+    /*jshint multistr: true */
+    var waitTime = client.globals.maxWaitTime,
+    newDatabaseName = client.globals.testDatabaseName,
+    dropDownElement = '#header-dropdown-menu',
+    baseUrl = client.globals.test_settings.launch_url;
+
+    client
+    .loginToGUI()
+    .populateDatabase(newDatabaseName)
+    .url(baseUrl + '/#/database/' + newDatabaseName + '/_all_docs')
+    .waitForElementPresent(dropDownElement, waitTime, false)
+    .click(dropDownElement + ' a')
+    .click(dropDownElement + ' a[href*="new_view"]')
+    .waitForElementPresent('.editor-wrapper', waitTime, false)
+    .setValue('#new-ddoc', 'test_design_doc-save-many-times')
+    .clearValue('#index-name')
+    .setValue('#index-name', 'multiple-saves')
+    .sendKeys("textarea.ace_text-input", client.Keys.Enter)
+    .execute('\
+      var editor = ace.edit("map-function");\
+      editor.getSession().setValue("function (doc) { emit(\'boom\', doc._id); }");\
+      editor._emit(\'blur\');\
+    ')
+    .click('button.btn-success.save')
+    .waitForElementVisible('.alert-success', waitTime, false)
+    .waitForElementNotVisible('.alert-success', waitTime, false)
+    .click('button.btn-success.save')
+    .waitForElementVisible('.alert-success', waitTime, false)
+    .waitForElementNotVisible('.alert-success', waitTime, false)
+    .click('button.btn-success.save')
+    .waitForElementVisible('.alert-success', waitTime, false)
+    .assert.containsText('.alert-success', 'View Saved.')
+    .end();
+  },
+};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/tests/viewIndex.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/viewIndex.componentsSpec.react.jsx b/app/addons/documents/tests/viewIndex.componentsSpec.react.jsx
index 68c4033..9cac62d 100644
--- a/app/addons/documents/tests/viewIndex.componentsSpec.react.jsx
+++ b/app/addons/documents/tests/viewIndex.componentsSpec.react.jsx
@@ -22,20 +22,13 @@ define([
 
   var assert = utils.assert;
   var TestUtils = React.addons.TestUtils;
+  var restore = utils.restore;
 
   var resetStore = function (designDocs) {
     designDocs = designDocs.map(function (doc) {
       return Documents.Doc.prototype.parse(doc);
     });
 
-    designDocs.map(function (ddoc) {
-      return new Documents.Doc(ddoc, {
-        database: {
-          safeID: function () { return 'id'; }
-        }
-      });
-    });
-
     var ddocs = new Documents.AllDocs(designDocs, {
       params: { limit: 10 },
       database: {
@@ -179,8 +172,8 @@ define([
 
 
     afterEach(function () {
-      Actions.newDesignDoc.restore && Actions.newDesignDoc.restore();
-      Actions.designDocChange.restore && Actions.designDocChange.restore();
+      restore(Actions.newDesignDoc);
+      restore(Actions.designDocChange);
       React.unmountComponentAtNode(container);
     });
 
@@ -243,13 +236,13 @@ define([
     });
 
     it('returns false on invalid map editor code', function () {
-      var stub = sinon.stub(editorEl.refs.mapEditor.aceEditor, 'hadValidCode');
+      var stub = sinon.stub(editorEl.refs.mapEditor, 'hadValidCode');
       stub.returns(false);
       assert.notOk(editorEl.hasValidCode());
     });
 
     it('returns true on valid map editor code', function () {
-      var stub = sinon.stub(editorEl.refs.mapEditor.aceEditor, 'hadValidCode');
+      var stub = sinon.stub(editorEl.refs.mapEditor, 'hadValidCode');
       stub.returns(true);
       assert.ok(editorEl.hasValidCode());
     });
@@ -271,6 +264,5 @@ define([
       });
       assert.ok(spy.calledWith(viewName));
     });
-
   });
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/addons/documents/views-doceditor.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/views-doceditor.js b/app/addons/documents/views-doceditor.js
index 0e2f4c7..7df5efe 100644
--- a/app/addons/documents/views-doceditor.js
+++ b/app/addons/documents/views-doceditor.js
@@ -227,7 +227,7 @@ function (app, FauxtonAPI, Components, Documents, Databases, prettify)
{
     },
 
     goBack: function () {
-      FauxtonAPI.navigate(FauxtonAPI.urls('allDocs', 'app', this.database.id, '?limit=20'));
+      FauxtonAPI.navigate(FauxtonAPI.urls('allDocs', 'app', this.database.id));
     },
 
     destroy: function () {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/54b11856/app/core/routeObject.js
----------------------------------------------------------------------
diff --git a/app/core/routeObject.js b/app/core/routeObject.js
index 818f4eb..c3705b8 100644
--- a/app/core/routeObject.js
+++ b/app/core/routeObject.js
@@ -283,7 +283,6 @@ function (FauxtonAPI, React, Backbone) {
     },
 
     removeViews: function () {
-      this.reactComponents = {};
       _.each(this.views, function (view, selector) {
         view.remove();
         delete this.views[selector];
@@ -304,8 +303,8 @@ function (FauxtonAPI, React, Backbone) {
     },
 
     cleanup: function () {
-      this.removeViews();
       this.removeComponents();
+      this.removeViews();
       this.rejectPromises();
     },
 


Mime
View raw message