couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From robertkowal...@apache.org
Subject [4/4] fauxton commit: updated refs/heads/master to 332c7b5
Date Thu, 16 Oct 2014 10:03:11 GMT
New Query Options tray added

This is a cherry-picked branch the clears up the old PR:
https://github.com/apache/couchdb-fauxton/pull/78

For comments regarding this PR, see the original PR. For scope
and discussion of the fix, see the Jira ticket:
https://issues.apache.org/jira/browse/COUCHDB-2347

Closes COUCHDB-2347


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

Branch: refs/heads/master
Commit: 332c7b5350a928e35006e821f1f8bf9b6b748c12
Parents: 5976c65
Author: Benjamin Keen <ben.keen@gmail.com>
Authored: Fri Oct 3 10:38:33 2014 -0700
Committer: Robert Kowalski <robertkowalski@apache.org>
Committed: Thu Oct 16 11:59:30 2014 +0200

----------------------------------------------------------------------
 app/addons/compaction/base.js                   |   3 +-
 .../documents/assets/less/advancedOptions.less  |   1 -
 app/addons/documents/assets/less/changes.less   |   3 -
 app/addons/documents/assets/less/documents.less | 122 ++---
 .../documents/assets/less/queryOptions.less     | 161 ++++++
 app/addons/documents/resources.js               |   1 -
 app/addons/documents/routes-documents.js        | 106 ++--
 .../documents/templates/advanced_options.html   | 157 ------
 .../documents/templates/all_docs_layout.html    |  11 +-
 .../documents/templates/all_docs_list.html      |   4 +-
 app/addons/documents/templates/ddoc_info.html   |   2 +-
 .../documents/templates/header_alldocs.html     |   5 +-
 .../documents/templates/query_options.html      |  38 ++
 .../query_options_additional_params.html        |  61 +++
 .../templates/query_options_key_search.html     |  50 ++
 .../templates/query_options_main_fields.html    |  55 ++
 app/addons/documents/templates/view_editor.html |   7 +-
 .../documents/tests/views-advancedoptsSpec.js   |   6 +-
 app/addons/documents/views-advancedopts.js      | 270 ----------
 app/addons/documents/views-index.js             |  90 ++--
 app/addons/documents/views-queryoptions.js      | 519 +++++++++++++++++++
 app/addons/documents/views.js                   | 239 +++------
 app/addons/fauxton/base.js                      |   3 +-
 app/addons/fauxton/components.js                |   6 +-
 app/addons/fauxton/templates/api_bar.html       |   2 +-
 app/core/routeObject.js                         |   7 +-
 app/core/router.js                              |   3 +-
 app/core/utils.js                               |  10 +
 assets/fonts/fauxtonicon.eot                    | Bin 17640 -> 18290 bytes
 assets/fonts/fauxtonicon.svg                    | 259 +++++----
 assets/fonts/fauxtonicon.ttf                    | Bin 17452 -> 18100 bytes
 assets/fonts/fauxtonicon.woff                   | Bin 10000 -> 10368 bytes
 .../fonts/styleguide/fauxtonicon-preview.html   | 441 ++++++++--------
 assets/icons/gears.svg                          |   8 +
 assets/less/bootstrap/forms.less                |   2 +-
 assets/less/fauxton.less                        |  27 +-
 assets/less/formstyles.less                     |  38 +-
 assets/less/icons.less                          | 214 ++++----
 38 files changed, 1688 insertions(+), 1243 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/compaction/base.js
----------------------------------------------------------------------
diff --git a/app/addons/compaction/base.js b/app/addons/compaction/base.js
index de0f124..f80c42e 100644
--- a/app/addons/compaction/base.js
+++ b/app/addons/compaction/base.js
@@ -17,6 +17,7 @@ define([
 ],
 
 function(app, FauxtonAPI, Compaction) {
+
   Compaction.initialize = function() {
     FauxtonAPI.registerExtension('docLinks', {
       title: "Compact & Clean", 
@@ -24,7 +25,7 @@ function(app, FauxtonAPI, Compaction) {
       icon: "icon-cogs"
     });
 
-    FauxtonAPI.registerExtension('advancedOptions:ViewButton', new Compaction.CompactView({}));
+    FauxtonAPI.registerExtension('ViewEditor:ButtonRow', new Compaction.CompactView({}));
   };
 
   return Compaction;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/assets/less/advancedOptions.less
----------------------------------------------------------------------
diff --git a/app/addons/documents/assets/less/advancedOptions.less b/app/addons/documents/assets/less/advancedOptions.less
deleted file mode 100644
index 2e29d38..0000000
--- a/app/addons/documents/assets/less/advancedOptions.less
+++ /dev/null
@@ -1 +0,0 @@
-/*for advanced options css*/

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/assets/less/changes.less
----------------------------------------------------------------------
diff --git a/app/addons/documents/assets/less/changes.less b/app/addons/documents/assets/less/changes.less
index 96e6d19..ebba21d 100644
--- a/app/addons/documents/assets/less/changes.less
+++ b/app/addons/documents/assets/less/changes.less
@@ -7,9 +7,6 @@
   right: 15px;
 }
 
-.changes-view {
-  padding-top: 70px;
-}
 
 .change-wrapper {
   margin-top: 20px;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/assets/less/documents.less
----------------------------------------------------------------------
diff --git a/app/addons/documents/assets/less/documents.less b/app/addons/documents/assets/less/documents.less
index 2b29744..8e730b5 100644
--- a/app/addons/documents/assets/less/documents.less
+++ b/app/addons/documents/assets/less/documents.less
@@ -10,31 +10,31 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 /*ALL DOCS TABLE*/
-
 @import "../../../../../assets/less/variables.less";
 @import "../../../../../assets/less/bootstrap/variables.less";
 @import "../../../../../assets/less/bootstrap/mixins.less";
-@import "advancedOptions.less";
+@import "queryOptions.less";
 @import "changes.less";
 @import "sidenav.less";
 
 tr.all-docs-item {
   border: none;
   background: transparent;
-    .btn-group {
-        position: absolute;
-        right: 0;
-        top: 6px;
-    }
+  .btn-group {
+    position: absolute;
+    right: 0;
+    top: 6px;
+  }
 }
+
 button.beautify {
   margin-top: 20px;
 }
 
 .toggle-btns {
-    label{
-        margin-right: 0;
-    }
+  label {
+    margin-right: 0;
+  }
 }
 
 #per-page {
@@ -43,65 +43,66 @@ button.beautify {
   #select-per-page {
     margin-top: 10px;
   }
-
 }
 
-
-#query div.controls-group.well{
-    height: 180px;
-    margin-right: 17px;
+#query div.controls-group.well {
+  height: 180px;
+  margin-right: 17px;
 }
 
 /** used in all_docs_list.html **/
 .view {
-    table td div {
-        position: relative;
-    }
+  table td div {
+    position: relative;
+  }
 
-    table td div div {
-        display: none;
-        line-height: 1;
-        position: absolute;
-        right: 4px;
-        top: 4px;
-    }
+  table td div div {
+    display: none;
+    line-height: 1;
+    position: absolute;
+    right: 4px;
+    top: 4px;
+  }
 
-    table td div:hover div a.edits {
-        padding-left: 16px;
-        padding-right: 16px;
-    }
+  table td div:hover div a.edits {
+    padding-left: 16px;
+    padding-right: 16px;
+  }
 
-    table td div:hover div {
-        display: block;
-    }
+  table td div:hover div {
+    display: block;
+  }
 }
 
 /** used in view_editor.html **/
-.design-doc-group{
-    .span3 { margin: 0;}
-    #new-ddoc-section {
-        margin-top: 10px;
-        label{ width: 100px}
-        .controls{
-            margin-left: 100px;
-        }
+.design-doc-group {
+  .span3 {
+    margin: 0;
+  }
+  #new-ddoc-section {
+    margin-top: 10px;
+    label {
+      width: 100px
     }
+    .controls {
+      margin-left: 100px;
+    }
+  }
 }
 
-
-#map-function, #reduce-function{
-    width: 100%;
-    font-size: 16px;
+#map-function, #reduce-function {
+  width: 100%;
+  font-size: 16px;
 }
 
 #doc-actions {
-	height: 42px;
+  height: 42px;
 }
 
 #editor-container {
-    width: 1316px;
-    height: 688px;
-    font-size: 16px;
+  width: 1316px;
+  height: 688px;
+  font-size: 16px;
 }
 
 button.string-edit {
@@ -137,21 +138,20 @@ button.string-edit[disabled] {
   display: inline-block;
 }
 
-
 .change-sequence {
   word-wrap: break-word;
 }
 
-
-#dashboard-upper-content{
-  .js-query-keys-wrapper{
+#dashboard-upper-content {
+  .js-query-keys-wrapper {
     padding-top: 0;
   }
-  /** used in advanced-options.html**/
+
+  /** used in query-options.html**/
   .custom-inputs {
-    .row-fluid{
+    .row-fluid {
       padding-top: 20px;
-      .radio, .checkbox{
+      .radio, .checkbox {
         padding-left: 0;
       }
       .checkbox.inline,
@@ -163,12 +163,14 @@ button.string-edit[disabled] {
   }
 }
 
-.nav-list{
+.nav-list {
   border-top: none;
 }
 
+.end-of-results {
+  margin-bottom: 30px;
 
-
-
-
-
+  .muted {
+    margin-bottom: 0px;
+  }
+}

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/assets/less/queryOptions.less
----------------------------------------------------------------------
diff --git a/app/addons/documents/assets/less/queryOptions.less b/app/addons/documents/assets/less/queryOptions.less
new file mode 100644
index 0000000..f326c5e
--- /dev/null
+++ b/app/addons/documents/assets/less/queryOptions.less
@@ -0,0 +1,161 @@
+// 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.
+
+#query-options-tray:before {
+  right: 140px;
+}
+
+#query-options-tray {
+  width: 490px;
+
+  .query-group:first-child {
+    margin-top: 0px;
+    padding-top: 0px;
+  }
+
+  .icon-question-sign {
+    margin-left: 3px;
+    font-size: 17.5px;
+  }
+
+  .query-group {
+    padding: 20px;
+    border-bottom: 1px solid #555;
+  }
+
+  .query-group:last-child {
+    border-bottom: none;
+  }
+  .controls-group {
+    margin: 0;
+  }
+  .controls-group:last-child {
+    margin: 0;
+  }
+  form {
+    overflow: scroll;
+    margin-bottom: 0;
+    .dropdown.inline {
+      display: inline-block;
+    }
+
+    input[type="text"],
+    textarea {
+      .border-radius(5px);
+      background-color: #666;
+      padding: 8px;
+      border: none;
+      color: #eee;
+      font-size: 13px;
+    }
+    .input-small {
+      width: 70px;
+      margin-left: 5px;
+    }
+    .checkbox {
+      padding: 0;
+      margin: 0;
+    }
+    .btn-success {
+      .border-radius(5px);
+    }
+    .btn-cancel, .btn-cancel:active {
+      background: none;
+      border: none;
+      box-shadow: none;
+      color: @red;
+      line-height: 1em;
+    }
+    select {
+      margin-top: -4px;
+      margin-bottom: 0px;
+    }
+    .include-end-key-row {
+      line-height: 20px;
+      margin-left: 8px;
+    }
+  }
+
+  .toggle-btns {
+    .btn {
+      padding: 5px 12px 5px;
+      background: #727A82;
+      color: #fff;
+      font-size: 12px;
+      border: none;
+    }
+    .btn.active {
+      background: #fff;
+      color: #E33F3B;
+      box-shadow: 2px 2px 0px rgba(0, 0, 0, 0.25) inset, 2px 2px 2px rgba(0, 0, 0, 0.15);
+    }
+    label:first-child {
+      .border-radius(5px 0 0 5px);
+    }
+    label:last-child {
+      .border-radius(0 5px 5px 0);
+      margin-left: 1px;
+    }
+  }
+
+  label {
+    margin-right: 0;
+    font-size: 13px;
+
+    /*
+    due to: https://code.google.com/p/chromium/issues/detail?id=411065 - that bug causes the checkbox state not to
+    update when the user clicks on labels quickly. It can be removed once the fix is in Chrome stable
+    */
+    -webkit-user-select: none;
+  }
+  label.disabled {
+    color: #777777;
+  }
+  div.controls-group.well{
+    height: 156px;
+    background: none;
+    border: none;
+    padding: 10px 0;
+  }
+  .row-fluid.fieldsets {
+    .inline {
+      width: 32%;
+    }
+    .inline:nth-child(3) {
+      text-align: right;
+      width: 34%;
+    }
+  }
+  .row-fluid.fieldsets {
+    margin-bottom: 5px;
+    :last-child {
+      margin-bottom: 2px;
+    }
+  }
+
+  #skipRows {
+    margin-left: 5px;
+  }
+  .add-on {
+    height: 28px;
+  }
+  .hide {
+    display: none;
+  }
+
+  .icon-question-sign:hover {
+    color: @red;
+  }
+  .additionalParams {
+    margin-bottom: 2px;
+  }
+}

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/resources.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/resources.js b/app/addons/documents/resources.js
index ef119ac..651ca37 100644
--- a/app/addons/documents/resources.js
+++ b/app/addons/documents/resources.js
@@ -249,7 +249,6 @@ function(app, FauxtonAPI, PagingCollection) {
       var ddoc = this.id.replace(/^_design\//,"");
       return "_design/"+app.utils.safeURLName(ddoc);
     }
-
   });
 
   Documents.ViewRow = FauxtonAPI.Model.extend({

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/routes-documents.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-documents.js b/app/addons/documents/routes-documents.js
index d3f693f..37ec482 100644
--- a/app/addons/documents/routes-documents.js
+++ b/app/addons/documents/routes-documents.js
@@ -12,7 +12,6 @@
 
 define([
   "app",
-
   "api",
 
   // Modules
@@ -85,16 +84,16 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
       "route:paginate": "paginate",
       "route:perPageChange": "perPageChange",
       "route:changesFilterAdd": "addFilter",
-      "route:changesFilterRemove": "removeFilter"
+      "route:changesFilterRemove": "removeFilter",
+      "route:updateQueryOptions": "updateQueryOptions",
+      "route:resetQueryOptions": "resetQueryOptions"
     },
 
     overrideBreadcrumbs: true,
 
     initialize: function (route, masterLayout, options) {
       this.databaseName = options[0];
-
       this.database = new Databases.Model({id:this.databaseName});
-
       this.designDocs = new Documents.AllDocs(null, {
         database: this.database,
         paging: {
@@ -123,12 +122,12 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
       }));
     },
 
-    setUpDropdown: function(){
+    setUpDropdown: function() {
       var defaultMenuLinks = [{
         links: [{
           title: 'Replicate Database',
           icon: 'fonticon-replicate',
-          url: '#/replication/'+this.databaseName
+          url: '#/replication/' + this.databaseName
         },{
           title: 'Delete',
           icon: 'fonticon-trash',
@@ -159,37 +158,33 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
       }];
 
       return _.reduce(FauxtonAPI.getExtensions('sidebar:links'), function (menuLinks, link) {
-
         menuLinks.push({
           title: link.title,
           url: newurlPrefix + "/" + link.url,
           icon: 'fonticon-plus-circled'
         });
-
         return menuLinks;
-     }, menuLinks);
-
+      }, menuLinks);
     },
 
     designDocMetadata:  function(database, ddoc){
       this.toolsView && this.toolsView.remove();
       this.viewEditor && this.viewEditor.remove();
 
-      var designDocInfo = new Resources.DdocInfo({_id: "_design/"+ddoc},{database: this.database });
-
+      var designDocInfo = new Resources.DdocInfo({ _id: "_design/" + ddoc }, { database: this.database });
       this.setView("#dashboard-lower-content", new Documents.Views.DdocInfo({
         ddocName: ddoc,
         model: designDocInfo
       }));
 
       this.sidebar.setSelectedTab(app.utils.removeSpecialCharacters(ddoc)+"_metadata");
+      this.leftheader.updateCrumbs(crumbs.allDocs(this.database));
+      this.rightHeader.hideQueryOptions();
 
-
-      this.leftheader.updateCrumbs(crumbs.allDocs(this.database)); 
-
+      // problem line again
       this.apiUrl = [designDocInfo.url('apiurl'), designDocInfo.documentation() ];
-
     },
+
     tempFn:  function(databaseName, ddoc, fn){
       this.setView("#dashboard-upper-content", new Documents.Views.temp({}));
       this.crumbs = function () {
@@ -197,7 +192,6 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
           {"name": this.database.id, "link": Databases.databaseUrl(this.database)},
         ];
       };
-
     },
 
     establish: function () {
@@ -215,10 +209,10 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
     },
 
     /*
-    * docParams are the options collection uses to fetch from the server
-    * urlParams are what are shown in the url and to the user
-    * They are not the same when paginating
-    */
+     * docParams are the options collection uses to fetch from the server
+     * urlParams are what are shown in the url and to the user
+     * They are not the same when paginating
+     */
     allDocs: function(databaseName, options) {
       var params = this.createParams(options),
           urlParams = params.urlParams,
@@ -232,23 +226,15 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
       this.leftheader.updateCrumbs(crumbs.allDocs(this.database)); 
       this.database.buildAllDocs(docParams);
 
-      if (docParams.startkey && docParams.startkey.indexOf('_design') > -1) {
-        this.sidebar.setSelectedTab('design-docs');
+      if (docParams.startkey && docParams.startkey.indexOf("_design") > -1) {
+        this.sidebar.setSelectedTab("design-docs");
       } else {
-        this.sidebar.setSelectedTab('all-docs');
+        this.sidebar.setSelectedTab("all-docs");
       }
 
       this.viewEditor && this.viewEditor.remove();
-
       this.database.allDocs.paging.pageSize = this.getDocPerPageLimit(urlParams, parseInt(docParams.limit, 10));
 
-      this.viewEditor = this.setView("#dashboard-upper-content", new Documents.Views.AllDocsLayout({
-        database: this.database,
-        collection: this.database.allDocs,
-        params: urlParams,
-        docParams: docParams
-      }));
-
       this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({
         database: this.database,
         collection: this.database.allDocs,
@@ -261,20 +247,23 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
        return [this.database.allDocs.urlRef("apiurl", urlParams), this.database.allDocs.documentation()];
       };
 
+      // update the rightHeader with the latest & greatest info
+      this.rightHeader.resetQueryOptions({ queryParams: urlParams });
+      this.rightHeader.showQueryOptions();
     },
 
-    viewFn: function (databaseName, ddoc, view) {
+    viewFn: function (databaseName, ddoc, viewName) {
       var params = this.createParams(),
           urlParams = params.urlParams,
           docParams = params.docParams,
           decodeDdoc = decodeURIComponent(ddoc);
 
-      view = view.replace(/\?.*$/,'');
+      viewName = viewName.replace(/\?.*$/,'');
 
       this.indexedDocs = new Documents.IndexCollection(null, {
         database: this.database,
         design: decodeDdoc,
-        view: view,
+        view: viewName,
         params: docParams,
         paging: {
           pageSize: this.getDocPerPageLimit(urlParams, parseInt(docParams.limit, 10))
@@ -284,11 +273,11 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
       this.viewEditor = this.setView("#dashboard-upper-content", new Index.ViewEditor({
         model: this.database,
         ddocs: this.designDocs,
-        viewName: view,
+        viewName: viewName,
         params: urlParams,
         newView: false,
         database: this.database,
-        ddocInfo: this.ddocInfo(decodeDdoc, this.designDocs, view)
+        ddocInfo: this.ddocInfo(decodeDdoc, this.designDocs, viewName)
       }));
 
       this.toolsView && this.toolsView.remove();
@@ -300,14 +289,23 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
         database: this.database,
         indexedDocs: this.indexedDocs,
         designDocs: this.designDocs,
-        view: view
+        view: viewName
       });
 
-      this.sidebar.setSelectedTab(app.utils.removeSpecialCharacters(ddoc) + '_' + app.utils.removeSpecialCharacters(view));
+      this.sidebar.setSelectedTab(app.utils.removeSpecialCharacters(ddoc) + '_' + app.utils.removeSpecialCharacters(viewName));
 
       this.apiUrl = function() {
        return [this.indexedDocs.urlRef("apiurl", urlParams), "docs"];
       };
+
+      this.rightHeader.showQueryOptions();
+      this.rightHeader.resetQueryOptions({
+        queryParams: urlParams,
+        showStale: true,
+        hasReduce: true,
+        viewName: viewName,
+        ddocName: ddoc
+      });
     },
 
     ddocInfo: function (designDoc, designDocs, view) {
@@ -319,7 +317,6 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
     },
 
     createViewDocumentsView: function (options) {
-
       return this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({
         database: options.database,
         collection: options.indexedDocs,
@@ -338,19 +335,22 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
       this.documentsView && this.documentsView.remove();
 
       this.viewEditor = this.setView("#dashboard-upper-content", new Index.ViewEditor({
-        currentddoc: "_design/"+designDoc || "",
+        currentddoc: "_design/" + designDoc || "",
         ddocs: this.designDocs,
         params: params,
         database: this.database,
         newView: true
       }));
 
-      this.sidebar.setSelectedTab('new-view');
+      this.sidebar.setSelectedTab("new-view");
+      this.rightHeader.hideQueryOptions();
 
       // clear out anything that was in the lower section
       this.removeView("#dashboard-lower-content");
 
-      //this.rightHeader.updateApiUrl([this.indexedDocs.urlRef("apiurl", urlParams), "docs"]);
+      // TODO
+//      var apiUrl = this.database.url("app") + "/new_view/" + designDoc;
+//      this.rightHeader.updateApiUrl([apiUrl, "docs"]);
     },
 
     updateAllDocsFromView: function (event) {
@@ -370,7 +370,6 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
         this.database.buildAllDocs(docParams);
         collection = this.database.allDocs;
         collection.paging.pageSize = pageSize;
-
       } else {
         collection = this.indexedDocs = new Documents.IndexCollection(null, {
           database: this.database,
@@ -399,7 +398,11 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
       this.documentsView.setParams(docParams, urlParams);
       this.documentsView.forceRender();
 
-      this.apiUrl = [collection.urlRef("apiurl", urlParams), "docs"];
+      // this has been commented out because it causes the header bar to disappear after a search (i.e the "Query
+      // Options" link disappears). This issue is being addressed in a separate ticket (not sure about the Jira ID)
+//      this.apiUrl = function() {
+//        return [this.indexedDocs.urlRef("apiurl", urlParams), "docs"];
+//      };
     },
 
     perPageChange: function (perPage) {
@@ -473,13 +476,12 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
       this.viewEditor && this.viewEditor.remove();
 
       this.sidebar.setSelectedTab('changes');
-
       this.leftheader.updateCrumbs(crumbs.changes(this.database));
+      this.rightHeader.showQueryOptions();
 
       this.apiUrl = function () {
         return [this.database.url("changes-apiurl"), this.database.documentation()];
       };
-
     },
 
     addFilter: function (filter) {
@@ -490,10 +492,16 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
     removeFilter: function (filter) {
       this.changesView.filters.splice(this.changesView.filters.indexOf(filter), 1);
       this.changesView.render();
-    }
+    },
 
+    resetQueryOptions: function(options) {
+      this.rightHeader.resetQueryOptions(options);
+    },
+
+    updateQueryOptions: function(options) {
+      this.rightHeader.updateQueryOptions(options);
+    }
   });
 
   return DocumentsRouteObject;
 });
-

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/templates/advanced_options.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/advanced_options.html b/app/addons/documents/templates/advanced_options.html
deleted file mode 100644
index c986d4e..0000000
--- a/app/addons/documents/templates/advanced_options.html
+++ /dev/null
@@ -1,157 +0,0 @@
-<!--
-Licensed under the Apache License, Version 2.0 (the "License"); you may not
-use this file except in compliance with the License. You may obtain a copy of
-the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-License for the specific language governing permissions and limitations under
-the License.
--->
-<div class="errors-container"></div>
-<form class="js-view-query-update custom-inputs">
-
-<!-- tabs for choosing Keys or Start & end -->
-
-  <div class="btn-group toggle-btns row-fluid">
-    <label for="showKeys" class="drop-down btn span6">
-     By Key(s)
-    </label>
-    <label for="showStartEnd" class="drop-down btn span6">
-      Between Keys
-    </label>
-  </div>
-
-  <div class="controls-group well hide js-query-keys-wrapper">
-    <div class="row-fluid" id="js-showKeys">
-      <div class="controls controls-row">
-        <label for="keys-input" class="drop-down">A key, or an array of keys.</label>
-        <textarea id="keys-input" name="keys" class="input-xxlarge" rows="5" type="text" placeholder='Enter valid JSON; e.g., ["1234"] or ["1234","2345"]'></textarea>
-        <div id="keys-error" class="inline-block js-keys-error"></div>
-      </div>
-    </div>
-    <div class="row-fluid hide" id="js-showStartEnd">
-      <div class="controls controls-row">
-        <div class="span6">
-          <label for="startkey" class="drop-down">Start key</label>
-          <input name="startkey" id="startkey" type="text" placeholder='e.g., "1234"' disabled>
-        </div>
-        <div class="span6">
-          <label for="endkey" class="drop-down">End key</label>
-          <input id="endkey" name="endkey" type="text" placeholder='e.g., "1234"'>
-          <div class="controls controls-row checkbox inline">
-            <input id="check5" name="inclusive_end" type="checkbox" value="true" checked disabled>
-            <label for="check5">Include End Key in results</label>
-          </div>
-        </div>
-
-      </div>
-
-    </div>
-  </div>
-
-<!-- Limit and Skip are conditional -->
-
-  <div class="controls-group">
-
-    <div class="row-fluid">
-      <div class="span6">
-        <label class="drop-down inline">
-          Limit:
-          <select name="limit" class="input-medium">
-            <option selected="selected">None</option>
-            <option>5</option>
-            <option>10</option>
-            <option>20</option>
-            <option>30</option>
-            <option>50</option>
-            <option>100</option>
-  		      <option>500</option>
-          </select>
-        </label>
-      </div>
-      <div class="span6">
-        <label for="skipRows" class="inline drop-down">
-          # of rows to skip
-          <input name="skip" class="input-small" type="text" id="skipRows" placeholder="0">
-        </label>
-      </div>
-    </div>
-    <div class="row-fluid">
-      <div class="span6">
-        <label id="select2" class="drop-down inline">
-          Order:
-          <select id="select2" name="descending" class="input-medium">
-            <option value="false">Ascending</option>
-            <option value="true">Descending</option>
-          </select>
-        </label>
-      </div>
-      <div class="span6">
-        <label id="select2" class="drop-down inline">
-          Docs:
-          <select id="select2" name="include_docs" class="input-medium">
-            <option value="false">Exclude</option>
-            <option value="true">Include</option>
-          </select>
-        </label>
-      </div>
-    </div>
-    <div class="row-fluid">
-      <% if (showStale) { %>
-        <div class="span6">
-          <div class="checkbox inline">
-            <input id="check7" name="stale" type="checkbox" value="ok">
-            <label for="check7">Stale</label>
-          </div>
-        </div>
-      <% } %>
-      <div class="span6 update-seq">
-        <div class="checkbox inline">
-          <input id="check6" name="update_seq" type="checkbox" value="true">
-          <label for="check6">Update Sequence</label>
-        </div>
-      </div>
-
-    </div>
-  <% if (hasReduce) { %>
-    <div class="row-fluid">
-      <div class="span6">
-        <div class="checkbox inline">
-          <input id="check2" name="reduce" type="checkbox" value="true">
-          <label for="check2">Reduce</label>
-        </div>
-      </div>
-      <div class="span6">
-        <label id="select1" class="drop-down inline">
-          Group Level:
-          <select id="select1" disabled name="group_level" class="input-small">
-            <option value="0">None</option>
-            <option value="1">1</option>
-            <option value="2">2</option>
-            <option value="3">3</option>
-            <option value="4">4</option>
-            <option value="5">5</option>
-            <option value="6">6</option>
-            <option value="7">7</option>
-            <option value="8">8</option>
-            <option value="9">9</option>
-            <option value="999" selected="selected">Exact</option>
-          </select>
-        </label>
-      </div>
-    </div>
-  <% } %>
-  </div>
-
-  <div class="controls-group">
-    <div id="button-options" class="controls controls-row">
-      <button type="submit" class="btn btn-success">Query</button>
-    </div>
-  </div>
-</form>
-</div>
-

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/templates/all_docs_layout.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/all_docs_layout.html b/app/addons/documents/templates/all_docs_layout.html
index e2c2479..8db91ed 100644
--- a/app/addons/documents/templates/all_docs_layout.html
+++ b/app/addons/documents/templates/all_docs_layout.html
@@ -11,15 +11,6 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 License for the specific language governing permissions and limitations under
 the License.
 -->
-<div class="dashboard-upper-menu">
-	<ul class="nav nav-tabs window-resizeable" id="db-views-tabs-nav">
-	  <li><a id="toggle-query" href="#query" data-bypass="true" data-toggle="tab">
-	    <i class="fonticon fonticon-plus"></i> Query Options</a></li>
-	</ul>
-	<div class="js-search searchbox-container"></div>
-</div>
-
 <div class="tab-content">
-  <div class="tab-pane" id="query">
-  </div>
+  <div class="tab-pane" id="query"></div>
 </div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/templates/all_docs_list.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/all_docs_list.html b/app/addons/documents/templates/all_docs_list.html
index 8db7bbe..c77fc05 100644
--- a/app/addons/documents/templates/all_docs_list.html
+++ b/app/addons/documents/templates/all_docs_list.html
@@ -33,9 +33,9 @@ the License.
   </table>
 
   <% if (endOfResults) { %>
-  <div class="text-center well">
+  <div class="end-of-results text-center well">
     <p class="muted">
-      End of results - <a id="js-end-results" href="#query" data-bypass="true" data-toggle="tab">edit query</a>
+      End of results - <a id="js-end-results" href="#query" data-bypass="true">edit query</a>
     </p>
     </div>
   <% } %>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/templates/ddoc_info.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/ddoc_info.html b/app/addons/documents/templates/ddoc_info.html
index e9b1b6e..5c180ec 100644
--- a/app/addons/documents/templates/ddoc_info.html
+++ b/app/addons/documents/templates/ddoc_info.html
@@ -58,4 +58,4 @@ the License.
       </dl>
     </div>
   </div>
-</div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/templates/header_alldocs.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/header_alldocs.html b/app/addons/documents/templates/header_alldocs.html
index b353d89..a4da0fb 100644
--- a/app/addons/documents/templates/header_alldocs.html
+++ b/app/addons/documents/templates/header_alldocs.html
@@ -13,13 +13,12 @@ the License.
 -->
 
   <!-- floats right -->
-
   <div id="header-api-bar" class="button"></div>
 
   <!-- Query Options-->
-  <!--<div class="button header-query-options">
+  <div id="header-query-options" class="button">
     <div id="query-options"></div>
-  </div>-->
+  </div>
 
   <!-- search (jump to doc)-->
   <div id="header-search" class="js-search searchbox-container"></div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/templates/query_options.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/query_options.html b/app/addons/documents/templates/query_options.html
new file mode 100644
index 0000000..8126457
--- /dev/null
+++ b/app/addons/documents/templates/query_options.html
@@ -0,0 +1,38 @@
+<% /*
+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.
+*/ %>
+<a id="toggle-query" href="#query-options-tray" data-bypass="true" data-toggle="tab"
+   class="btn btn-primary pull-right query-options-btn">
+  <i class="icon header-icon fonticon-gears"></i>
+  Query Options
+</a>
+
+<div id="query-options-tray" class="query-options tray">
+  <form class="js-view-query-update custom-inputs">
+
+    <div class="query-group" id="query-options-main-fields"></div>
+    <div class="query-group" id="query-options-key-search"></div>
+    <div class="controls-group query-group" id="query-options-additional-params"></div>
+
+    <div class="controls-group query-group">
+      <div id="button-options" class="controls controls-row">
+        <button type="submit" class="btn btn-success">
+          <i class="fonticon-play icon"></i>
+          Query
+        </button>
+        <a class="btn btn-cancel">Cancel</a>
+      </div>
+    </div>
+
+  </form>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/templates/query_options_additional_params.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/query_options_additional_params.html b/app/addons/documents/templates/query_options_additional_params.html
new file mode 100644
index 0000000..3784315
--- /dev/null
+++ b/app/addons/documents/templates/query_options_additional_params.html
@@ -0,0 +1,61 @@
+<% /*
+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.
+*/ %>
+
+<!-- Limit and Skip are conditional -->
+<div class="add-on additionalParams">Additional Parameters</div>
+
+<div class="row-fluid fieldsets">
+
+  <% if (showStale) { %>
+  <div class="checkbox inline">
+    <input id="qoStale" name="stale" type="checkbox" value="ok" />
+    <label for="qoStale">Stale</label>
+  </div>
+  <% } %>
+
+  <div class="checkbox inline">
+    <input id="qoUpdateSeq" name="update_seq" type="checkbox" value="true" />
+    <label for="qoUpdateSeq">Update Sequence</label>
+  </div>
+
+  <div class="dropdown inline">
+    <label class="drop-down">
+      Limit
+      <select id="qoLimit" name="limit" class="input-small">
+        <option value="" selected="selected">None</option>
+        <option value="5">5</option>
+        <option value="10">10</option>
+        <option value="20">20</option>
+        <option value="30">30</option>
+        <option value="50">50</option>
+        <option value="100">100</option>
+        <option value="500">500</option>
+      </select>
+    </label>
+  </div>
+</div>
+
+<div class="row-fluid fieldsets">
+  <div class="checkbox inline">
+    <input id="qoDescending" name="descending" type="checkbox" value="true" />
+    <label for="qoDescending">Descending</label>
+  </div>
+
+  <div class="dropdown inline">
+    <label for="qoSkip" class="drop-down">
+      Skip
+      <input name="skip" class="input-small" type="text" id="qoSkip" placeholder="# of rows" />
+    </label>
+  </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/templates/query_options_key_search.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/query_options_key_search.html b/app/addons/documents/templates/query_options_key_search.html
new file mode 100644
index 0000000..b288b4d
--- /dev/null
+++ b/app/addons/documents/templates/query_options_key_search.html
@@ -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.
+*/ %>
+
+<div class="add-on">Keys</div>
+
+<!-- tabs for choosing Keys or start & end -->
+<div class="btn-group toggle-btns row-fluid">
+  <label data-action="showByKeys" class="drop-down btn">By Key(s)</label>
+  <label data-action="showBetweenKeys" class="drop-down btn">Between Keys</label>
+</div>
+
+<div class="controls-group well hide js-query-keys-wrapper">
+  <div class="row-fluid js-keys-section" id="js-showKeys">
+    <div class="controls controls-row">
+      <label for="keys-input" class="drop-down">A key, or an array of keys.</label>
+      <textarea id="keys-input" name="keys" class="input-xxlarge" rows="5" type="text"
+        placeholder='Enter valid JSON; e.g., ["1234"] or ["1234","2345"]'></textarea>
+      <div id="keys-error" class="inline-block js-keys-error"></div>
+    </div>
+  </div>
+
+  <div class="row-fluid js-keys-section hide" id="js-showStartEnd">
+    <div class="controls controls-row">
+      <div>
+        <label for="startkey" class="drop-down">Start key</label>
+        <input name="startkey" id="startkey" type="text" placeholder='e.g., "1234"' disabled />
+      </div>
+      <div>
+        <label for="endkey" class="drop-down">End key</label>
+        <input id="endkey" name="endkey" type="text" placeholder='e.g., "1234"'>
+        <div class="controls include-end-key-row checkbox controls-row inline">
+          <input id="qoIncludeEndKeyInResults" name="inclusive_end" type="checkbox" value="true" checked disabled />
+          <label for="qoIncludeEndKeyInResults">Include End Key in results</label>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+  

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/templates/query_options_main_fields.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/query_options_main_fields.html b/app/addons/documents/templates/query_options_main_fields.html
new file mode 100644
index 0000000..48b5d3c
--- /dev/null
+++ b/app/addons/documents/templates/query_options_main_fields.html
@@ -0,0 +1,55 @@
+<% /*
+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.
+*/ %>
+
+<span class="add-on">
+  Query Options
+  <a class="help-link" href="<%-getDocUrl(documentation)%>" target="_blank" data-bypass="true">
+    <i class="icon-question-sign"></i>
+  </a>
+</span>
+
+<div class="errors-container"></div>
+
+<div class="controls-group">
+  <div class="row-fluid fieldsets">
+    <div class="checkbox inline">
+      <input id="qoIncludeDocs" name="include_docs" type="checkbox" value="true" />
+      <label for="qoIncludeDocs" id="qoIncludeDocsLabel">Include Docs</label>
+    </div>
+
+    <% if (hasReduce) { %>
+    <div class="checkbox inline">
+      <input id="qoReduce" name="reduce" type="checkbox" value="true" />
+      <label for="qoReduce">Reduce</label>
+    </div>
+
+    <label class="drop-down inline" id="qoGroupLevelGroup">
+      Group Level
+      <select id="qoGroupLevel" name="group_level" class="input-small">
+        <option value="0">None</option>
+        <option value="1">1</option>
+        <option value="2">2</option>
+        <option value="3">3</option>
+        <option value="4">4</option>
+        <option value="5">5</option>
+        <option value="6">6</option>
+        <option value="7">7</option>
+        <option value="8">8</option>
+        <option value="9">9</option>
+        <option value="999" selected="selected">Exact</option>
+      </select>
+    </label>
+    <% } %>
+  </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/templates/view_editor.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/view_editor.html b/app/addons/documents/templates/view_editor.html
index 769deed..d22c734 100644
--- a/app/addons/documents/templates/view_editor.html
+++ b/app/addons/documents/templates/view_editor.html
@@ -15,12 +15,8 @@ the License.
   <ul class="nav nav-tabs" id="db-views-tabs-nav">
     <li class="active"> <a data-bypass="true" id="index-nav" data-toggle="tab" href="#index">
       <i class="fonticon-wrench fonticon"></i>
-      <% if (newView) { %>Create Index <% } else { %>Edit Index <% } %></a></li>
-    <% if (!newView) { %>
-    <li><a data-bypass="true" id="query-nav" href="#query" data-toggle="tab">
-      <i class="fonticon-plus fonticon"></i> Query Options</a>
+      <% if (newView) { %>Create Index <% } else { %>Edit Index <% } %></a>
     </li>
-    <% } %>
   </ul>
 </div>
   <div class="all-docs-list errors-container"></div>
@@ -78,6 +74,7 @@ the License.
             <% if (!newView) { %>
             <button class="btn btn-danger delete"><i class="icon fonticon-cancel-circled"></i> Delete</button>
             <% } %>
+            <span id="viewBtnExtensions"></span>
           </div>
           <div class="clearfix"></div>
         </form>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/tests/views-advancedoptsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/views-advancedoptsSpec.js b/app/addons/documents/tests/views-advancedoptsSpec.js
index 3f9373b..fe9bbe5 100644
--- a/app/addons/documents/tests/views-advancedoptsSpec.js
+++ b/app/addons/documents/tests/views-advancedoptsSpec.js
@@ -10,14 +10,14 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 define([
-        'addons/documents/views-advancedopts',
-        'testUtils'
+  'addons/documents/views-queryoptions',
+  'testUtils'
 ], function (Views, testUtils) {
   var assert = testUtils.assert,
       ViewSandbox = testUtils.ViewSandbox,
       viewSandbox;
 
-  describe('Documents AdvancedOptions', function () {
+  describe('Documents QueryOptions', function () {
     var view;
       beforeEach(function () {
         viewSandbox = new ViewSandbox();

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/views-advancedopts.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/views-advancedopts.js b/app/addons/documents/views-advancedopts.js
deleted file mode 100644
index b2e1af5..0000000
--- a/app/addons/documents/views-advancedopts.js
+++ /dev/null
@@ -1,270 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-define([
-       "app",
-       "api",
-       // Libs
-       "addons/fauxton/resizeColumns",
-],
-
-function(app, FauxtonAPI, resizeColumns ) {
-
-  var Views = {};
-
-  Views.AdvancedOptions = FauxtonAPI.View.extend({
-    template: "addons/documents/templates/advanced_options",
-    className: "advanced-options well",
-
-    initialize: function (options) {
-      this.database = options.database;
-      this.ddocName = options.ddocName;
-      this.viewName = options.viewName;
-      this.updateViewFn = options.updateViewFn;
-      this.previewFn = options.previewFn;
-      this.showStale = _.isUndefined(options.showStale) ? false : options.showStale;
-      this.hasReduce = _.isUndefined(options.hasReduce) ? true : options.hasReduce;
-    },
-
-    events: {
-      "change form.js-view-query-update input": "updateFilters",
-      "change form.js-view-query-update select": "updateFilters",
-      "submit form.js-view-query-update": "updateView",
-      "click .toggle-btns > label":  "toggleQuery"
-    },
-
-    toggleQuery: function(e){
-      e.preventDefault();
-
-      if (this.$(e.currentTarget).hasClass("active")){
-        this.$('.js-query-keys-wrapper').addClass("hide");
-        this.$(".toggle-btns > label").removeClass('active');
-        this.$('.js-query-keys-wrapper').find("input,textarea").attr("disabled","true");
-      } else {
-        this.$('.js-query-keys-wrapper').removeClass("hide");
-        var showFunctionName =this.$(e.currentTarget).attr("for");
-        //highlight current
-        this.$(".toggle-btns > label").removeClass('active');
-        this.$(e.currentTarget).addClass("active");
-        this.$("[id^='js-show']").hide();
-        //show section & disable what needs to be disabled
-        this[showFunctionName]();
-      }
-    },
-
-    showKeys: function(){
-      this.$("#js-showKeys, .js-disabled-message").show();
-      this.$('[name="startkey"],[name="endkey"],[name="inclusive_end"]').attr("disabled","true");
-      this.$('[name="keys"]').removeAttr("disabled");
-    },
-
-    showStartEnd: function(){
-      this.$("#js-showStartEnd").show();
-      this.$('[name="startkey"],[name="endkey"],[name="inclusive_end"]').removeAttr("disabled");
-      this.$('.js-disabled-message').hide();
-      this.$('[name="keys"]').attr("disabled","true");
-    },
-
-    beforeRender: function () {
-      if (this.viewName && this.ddocName) {
-        var buttonViews = FauxtonAPI.getExtensions('advancedOptions:ViewButton');
-        _.each(buttonViews, function (view) {
-          this.insertView('#button-options', view);
-          view.update(this.database, this.ddocName, this.viewName);
-        }, this);
-      }
-    },
-
-    renderOnUpdatehasReduce: function (hasReduce) {
-      this.hasReduce = hasReduce;
-      this.render();
-    },
-
-    parseJSON: function (value) {
-      try {
-        return JSON.parse(value);
-      } catch(e) {
-        return undefined;
-      }
-    },
-
-    validateKeys:  function(param){
-      var errorMsg = false,
-          parsedValue = this.parseJSON(param.value);
-
-      if (_.isUndefined(parsedValue)) {
-        errorMsg = "Keys must be valid json.";
-      } else if (!_.isArray(parsedValue)) {
-        errorMsg =  "Keys values must be in an array. E.g [1,2,3]";
-      }
-
-      if (errorMsg) {
-        this.$('.js-keys-error').empty();
-        FauxtonAPI.addNotification({
-          type: "error",
-          msg: errorMsg,
-          clear:  false,
-          selector: '.advanced-options .errors-container'
-        });
-        return false;
-      }
-
-      return true;
-    },
-    validateFields: function(params){
-      var errors = false;
-      //so ghetto. Spaghetti code.
-      for (var i= 0; i <params.length; i++){
-        if (params[i].name === "skip"){
-          if (!(/^\d+$/).test(params[i].value)){
-            FauxtonAPI.addNotification({
-              msg: "Numbers only for skip",
-              type: "warn",
-              selector: ".advanced-options .errors-container",
-              clear:  true
-            });
-            errors = true;
-          }
-        }
-      }
-      return errors;
-    },
-    queryParams: function () {
-      var $form = this.$(".js-view-query-update"),
-          keysParam = false;
-
-      var params = _.reduce($form.serializeArray(), function(params, param) {
-        if (!param.value) { return params; }
-        if (param.name === "limit" && param.value === 'None') { return params; }
-        if (param.name === "keys") { keysParam = param; }
-        params.push(param);
-        return params;
-      }, []);
-
-
-      if (keysParam && !this.validateKeys(keysParam)) { return false; }
-
-      if (params && this.validateFields(params)){ return false; }
-
-      // Validate *key* params to ensure they're valid JSON
-      var keyParams = ["keys","startkey","endkey"];
-      var errorParams = _.filter(params, function(param) {
-        if (_.contains(keyParams, param.name) && _.isUndefined(this.parseJSON(param.value))) {
-            return true;
-          }
-
-          return false;
-      }, this);
-
-      return {params: params, errorParams: errorParams};
-    },
-
-    updateView: function (event) {
-      event.preventDefault();
-      var params = this.queryParams();
-      if (!params) { return;}
-      this.updateViewFn(event, params);
-    },
-
-    updateFilters: function(event) {
-      event.preventDefault();
-      var $ele = $(event.currentTarget);
-      var name = $ele.attr('name');
-      this.updateFiltersFor(name, $ele);
-    },
-
-    updateFiltersFor: function(name, $ele) {
-      var $form = $ele.parents("form.js-view-query-update:first");
-      switch (name) {
-        // Reduce constraints
-        //   - Can't include_docs for reduce=true
-        //   - can't include group_level for reduce=false
-        case "reduce":
-          if ($ele.prop('checked') === true) {
-          if ($form.find("input[name=include_docs]").prop("checked") === true) {
-            $form.find("input[name=include_docs]").prop("checked", false);
-            var notification = FauxtonAPI.addNotification({
-              msg: "include_docs has been disabled as you cannot include docs on a reduced view",
-              type: "warn",
-              selector: ".advanced-options .errors-container",
-              clear:  true
-            });
-          }
-          $form.find("input[name=include_docs]").prop("disabled", true);
-          $form.find("select[name=group_level]").prop("disabled", false);
-        } else {
-          $form.find("select[name=group_level]").val("999").prop("disabled", true);
-          $form.find("input[name=include_docs]").prop("disabled", false);
-        }
-        break;
-        case "skip":
-          if (!(/^\d+$/).test($ele.val())){
-            FauxtonAPI.addNotification({
-              msg: "Numbers only for skip",
-              type: "warn",
-              selector: ".advanced-options .errors-container",
-              clear:  true
-            });
-          }
-        break;
-        case "include_docs":
-        break;
-      }
-    },
-
-    updateFromParams: function (params) {
-      var $form = this.$el.find("form.js-view-query-update");
-      _.each(params, function(val, key) {
-        var $ele;
-        switch (key) {
-          case "limit":
-          case "descending":
-          case "group_level":
-            if (!val) { return; }
-            $form.find("select[name='"+key+"']").val(val);
-          break;
-          case "include_docs":
-            case "stale":
-            case "inclusive_end":
-            $form.find("input[name='"+key+"']").prop('checked', true);
-          break;
-          case "reduce":
-            $ele = $form.find("input[name='"+key+"']");
-          if (val == "true") {
-            $ele.prop('checked', true);
-          }
-          this.updateFiltersFor(key, $ele);
-          break;
-          case "key":
-          case "keys":
-            $form.find("textarea[name='"+key+"']").val(val);
-          break;
-          default:
-            $form.find("input[name='"+key+"']").val(val);
-          break;
-        }
-      }, this);
-    },
-
-    serialize: function () {
-      return {
-        hasReduce: this.hasReduce,
-        showPreview: false,
-        showStale: this.showStale
-      };
-    }
-  });
-
-
-  return Views;
-
-});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/views-index.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/views-index.js b/app/addons/documents/views-index.js
index db4a83b..cbc76ad 100644
--- a/app/addons/documents/views-index.js
+++ b/app/addons/documents/views-index.js
@@ -12,15 +12,15 @@
 
 define([
   "app",
-
   "api",
   "addons/fauxton/components",
-
   "addons/documents/resources",
   "addons/databases/resources",
   "addons/pouchdb/base",
+
   //views
-  "addons/documents/views-advancedopts",
+  "addons/documents/views-queryoptions",
+
   // Libs
   "addons/fauxton/resizeColumns",
 
@@ -29,11 +29,20 @@ define([
   "plugins/prettify"
 ],
 
-function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
-         QueryOptions, resizeColumns, beautify, prettify) {
+function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, QueryOptions, resizeColumns, beautify) {
 
   var Views = {};
 
+
+  // this is a temporary workaround until I hear of a better on. The problem is that on initial page load (i.e. a refresh
+  // of the View page) the afterRender() functions calls a FauxtonAPI.triggerRouteEvent(). That causes this View to be
+  // rendered twice (at least, the afterRender() function then gets called twice) - and that causes the header content to
+  // disappear. This var tracks whether the View has been rendered and if not, doesn't call the triggerRouteEvent. btw,
+  // the reason the triggerRouteEvent('resetQueryOptions') code is there is that it ensures the Query Options tray shows
+  // the appropriate content for the current View (i.e. hasReduce or not)
+  var hasRenderedOnce = false;
+
+
   Views.ViewEditor = FauxtonAPI.View.extend({
     template: "addons/documents/templates/view_editor",
     builtinReduces: ['_sum', '_count', '_stats'],
@@ -57,13 +66,16 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
     },
 
     defaultLang: "javascript",
+    rendered: false,
 
     initialize: function(options) {
+      this.rightHeader = options.rightHeader;
       this.newView = options.newView || false;
       this.ddocs = options.ddocs;
       this.params = options.params;
       this.database = options.database;
       this.currentDdoc = options.currentddoc;
+
       if (this.newView) {
         this.viewName = 'newView';
       } else {
@@ -133,14 +145,14 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
     },
 
     saveView: function(event) {
-      var json, notification,
-      that = this;
+      var notification,
+          that = this;
 
-      if (event) { event.preventDefault();}
+      if (event) { event.preventDefault(); }
 
       $('#dashboard-content').scrollTop(0); //scroll up
 
-      if (this.hasValidCode() && this.$('#new-ddoc:visible').val() !=="") {
+      if (this.hasValidCode() && this.$('#new-ddoc:visible').val() !== "") {
         var mapVal = this.mapEditor.getValue(),
         reduceVal = this.reduceVal(),
         viewName = this.$('#index-name').val(),
@@ -177,7 +189,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
           if (that.newView || viewNameChange) {
             var fragment = '/database/' + that.database.safeID() +'/' + ddoc.safeID() + '/_views/' + app.utils.safeURLName(viewName);
 
-            FauxtonAPI.navigate(fragment, {trigger: false});
+            FauxtonAPI.navigate(fragment, { trigger: false });
             that.newView = false;
             that.ddocID = ddoc.safeID();
             that.viewName = viewName;
@@ -192,7 +204,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
 
           if (that.reduceFunStr !== reduceVal) {
             that.reduceFunStr = reduceVal;
-            that.advancedOptions.renderOnUpdatehasReduce(that.hasReduce());
+            FauxtonAPI.triggerRouteEvent("updateQueryOptions", { hasReduce: that.hasReduce() });
           }
 
           FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: ddocName, view: viewName});
@@ -231,29 +243,25 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
            // bootstrap wants the error on a control-group div, but we're not using that
            //$('form.view-query-update input[name='+param+'], form.view-query-update select[name='+param+']').addClass('error');
            return FauxtonAPI.addNotification({
-             msg: "JSON Parse Error on field: "+param.name,
+             msg: "JSON Parse Error on field: " + param.name,
              type: "error",
-             selector: ".advanced-options .errors-container",
+             selector: ".query-options .errors-container",
              clear: true
            });
          });
          FauxtonAPI.addNotification({
            msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
            type: "warning",
-           selector: ".advanced-options .errors-container",
+           selector: ".query-options .errors-container",
            clear: true
          });
 
          return false;
       }
 
-       var fragment = window.location.hash.replace(/\?.*$/, '');
-       if (!_.isEmpty(params)) {
-        fragment = fragment + '?' + $.param(params);
-       }
-
-       FauxtonAPI.navigate(fragment, {trigger: false});
-       FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: this.ddocID, view: this.viewName});
+      var url = app.utils.replaceQueryParams(params);
+      FauxtonAPI.navigate(url, {trigger: false});
+      FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: this.ddocID, view: this.viewName});
     },
 
 
@@ -276,7 +284,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
       FauxtonAPI.addNotification({
         msg: "<strong>Warning!</strong> Preview executes the Map/Reduce functions in your browser, and may behave differently from CouchDB.",
         type: "warning",
-        selector: ".advanced-options .errors-container",
+        selector: ".query-options .errors-container",
         fade: true,
         escape: false // beware of possible XSS when the message changes
       });
@@ -311,7 +319,6 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
       if (this.mapEditor) {
         return this.mapEditor.getValue();
       }
-
       return this.$('#map-function').text();
     },
 
@@ -401,8 +408,8 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
         $('.beautify-tooltip').tooltip();
       }
     },
-    beforeRender: function () {
 
+    beforeRender: function () {
       if (this.newView) {
         this.reduceFunStr = '';
         if (this.ddocs.length === 0) {
@@ -427,7 +434,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
         });
         designDocs.reset(filteredModels, {silent: true});
       }
-      
+
       if (!this.designDocSelector) { 
         this.designDocSelector = this.setView('.design-doc-group', new Views.DesignDocSelector({
           collection: designDocs,
@@ -436,27 +443,25 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
         }));
       }
 
+      // if this isn't a new View, add in whatever extensions have been associated with this location
       if (!this.newView) {
-        this.eventer = _.extend({}, Backbone.Events);
-
-        this.advancedOptions = this.insertView('#query', new QueryOptions.AdvancedOptions({
-          updateViewFn: this.updateView,
-          previewFn: this.previewView,
-          database: this.database,
-          viewName: this.viewName,
-          ddocName: this.model.id,
-          hasReduce: this.hasReduce(),
-          eventer: this.eventer,
-          showStale: true
-        }));
+        var buttonViews = FauxtonAPI.getExtensions('ViewEditor:ButtonRow');
+        _.each(buttonViews, function (view) {
+          this.insertView("#viewBtnExtensions", view);
+          view.update(this.database, this.ddocInfo.safeID(), this.viewName);
+        }, this);
       }
-
     },
 
     afterRender: function() {
-
-      if (this.params && !this.newView) {
-        this.advancedOptions.updateFromParams(this.params);
+      if (this.params && !this.newView && hasRenderedOnce) {
+        FauxtonAPI.triggerRouteEvent('resetQueryOptions', {
+          queryParams: this.params,
+          hasReduce: this.hasReduce(),
+          showStale: true,
+          viewName: this.viewName,
+          ddocName: this.ddocInfo.get('name')
+        });
       }
 
       this.designDocSelector.updateDesignDoc();
@@ -468,6 +473,8 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
         this.$('#index-nav').parent().removeClass('active');
       }
 
+      // note that this View has been rendered
+      hasRenderedOnce = true;
     },
 
     showEditors: function () {
@@ -550,6 +557,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
     newDocValidation: function(){
       return this.newDesignDoc() && this.$('#new-ddoc').val()==="";
     },
+
     getCurrentDesignDoc: function () {
       if (this.newDesignDoc()) {
         var doc = {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/332c7b53/app/addons/documents/views-queryoptions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/views-queryoptions.js b/app/addons/documents/views-queryoptions.js
new file mode 100644
index 0000000..5bea495
--- /dev/null
+++ b/app/addons/documents/views-queryoptions.js
@@ -0,0 +1,519 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+  "app",
+  "api",
+
+  // libs
+  "addons/fauxton/resizeColumns"
+],
+
+  function (app, FauxtonAPI) {
+
+    // our default settings for the Query Options tray
+    var defaultOptions = {
+      showStale: false,
+      hasReduce: false,
+      viewName: null,
+      ddocName: null,
+
+      // all the possible query search params. Ultimately these should probably be moved higher-up (route object?),
+      // because they also apply to the actual search results. Seems better to place them there, then use them in
+      // both places
+      queryParams: {
+        include_docs: "false",
+        keys: "",
+        limit: "",
+        descending: "false",
+        skip: "",
+        update_seq: "false",
+        startkey: "",
+        endkey: "",
+        inclusive_end: "true",
+        reduce: "false",
+        stale: "",
+        group_level: "999"
+      }
+    };
+
+
+    var Views = {};
+
+    // our main View. This is the only View exposed externally
+    Views.QueryOptionsTray = FauxtonAPI.View.extend({
+      template: "addons/documents/templates/query_options",
+      className: "query-options",
+
+      initialize: function (options) {
+
+        // overlays whatever custom options were passed with defaultOptions above, so this.options
+        // always contains all options. [This does a deep copy: we never overwrite defaultOptions!]
+        this.options = $.extend(true, {}, defaultOptions, options);
+
+        // add any general events relating to the Query Options tray
+        this.addEvents();
+
+        // add the sub-views
+        this.mainFieldsView       = this.setView("#query-options-main-fields", new MainFieldsView(this.options));
+        this.keySearchFieldsView  = this.setView("#query-options-key-search", new KeySearchFieldsView(this.options));
+        this.additionalParamsView = this.setView("#query-options-additional-params", new AdditionalParamsView(this.options));
+      },
+
+      addEvents: function () {
+        FauxtonAPI.Events.on('QueryOptions:closeTray', this.closeTray, this);
+        FauxtonAPI.Events.on('QueryOptions:openTray', this.toggleQueryOptionsTray, this);
+
+        // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
+        var trayIsVisible = this.trayIsVisible;
+        var closeTray = this.closeTray;
+
+        $("body").on("click.queryOptions", function (e) {
+          if (!trayIsVisible()) { return; }
+          if ($(e.target).closest("#query-options-tray").length === 0) {
+            closeTray();
+          }
+        });
+
+        $(window).on("resize", this.onResize);
+      },
+
+      afterRender: function () {
+        this.onResize();
+      },
+
+      cleanup: function () {
+        FauxtonAPI.Events.unbind("QueryOptions:closeTray");
+        FauxtonAPI.Events.unbind("QueryOptions:openTray");
+        $(window).off("resize", this.onResize);
+      },
+
+      events: {
+        "click #toggle-query": "toggleQueryOptionsTray", // hide/show the Query Options tray
+        "submit form.js-view-query-update": "onSubmit",  // submits the form
+        "click .btn-cancel": "onCancel"                  // closes the tray (doesn't reset the fields)
+      },
+
+      toggleQueryOptionsTray: function () {
+        if (!this.trayIsVisible()) {
+          $("#query-options-tray").velocity("transition.slideDownIn", 250); // TODO constant
+          FauxtonAPI.Events.trigger("APIbar:closeTray");
+        }
+      },
+
+      // returns all applicable query parameters for the Query Options tray
+      getQueryParams: function () {
+        var mainFieldParams = this.mainFieldsView.getParams();
+        var keySearchParams = this.keySearchFieldsView.getParams();
+        var additionalParams = this.additionalParamsView.getParams();
+
+        // assumption: there aren't conflicting keys
+        return _.extend({}, mainFieldParams, keySearchParams, additionalParams);
+      },
+
+      onSubmit: function (e) {
+        e.preventDefault();
+
+        // validate the user-inputted fields. If anything is invalid, the sub-view will display the appropriate
+        // errors to the user
+        if (!this.keySearchFieldsView.hasValidInputs() || !this.additionalParamsView.hasValidInputs()) {
+          return;
+        }
+
+        this.closeTray();
+
+        // this may be empty. That's ok! Perhaps the user just did a search with params, then removed them & wants the default
+        var params = this.getQueryParams();
+
+        // all looks good! Close the tray and publish the message
+        var url = app.utils.replaceQueryParams(params);
+        FauxtonAPI.navigate(url, { trigger: false });
+
+        if (this.options.viewName !== null && this.options.ddocName !== null) {
+          FauxtonAPI.triggerRouteEvent('updateAllDocs', { ddoc: this.options.ddocName, view: this.options.viewName });
+        } else {
+          FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
+        }
+      },
+
+      onCancel: function () {
+        this.closeTray();
+      },
+
+      // if the screen is so small there isn't space for the full tray height we manually shrink the height to allow scrolling.
+      // Technically this should handle width as well, but we won't bother because there are way bigger issues with a screen
+      // with such a small width!
+      onResize: function () {
+        var $tray = $("#query-options-tray");
+        var heightFromTop = parseInt($tray.css("top"), 10);
+        var windowHeight = $(window).height();
+
+        // we apply the max-height to the form rather than the entire tray to allow the little up arrow to appear normally
+        $tray.find("form").css("max-height", windowHeight - heightFromTop);
+      },
+
+      /*
+       * Updates specific query options, leaving any that have been set already intact.
+       */
+      updateQueryOptions: function (options) {
+        this.options = $.extend(this.options, options);
+        this.updateSubViews();
+      },
+
+      /*
+       * Reset the query options back to the defaults, then apply whatever new options are needed.
+       */
+      resetQueryOptions: function (options) {
+        this.options = $.extend(true, {}, defaultOptions, options);
+        this.updateSubViews();
+      },
+
+      // helper
+      updateSubViews: function () {
+        this.mainFieldsView.update(this.options);
+        this.keySearchFieldsView.update(this.options);
+        this.additionalParamsView.update(this.options);
+      },
+
+      trayIsVisible: function () {
+        return $("#query-options-tray").is(":visible");
+      },
+
+      closeTray: function () {
+        $("#query-options-tray").velocity("reverse", 250, function () { // TODO constant
+          $("#query-options-tray").hide();
+        });
+      }
+    });
+
+
+    // ------ "private" Views ------
+
+    var MainFieldsView = FauxtonAPI.View.extend({
+      template: "addons/documents/templates/query_options_main_fields",
+      events: {
+        "change #qoReduce": "onToggleReduceCheckbox"
+      },
+
+      initialize: function (options) {
+        this.queryParams = options.queryParams;
+        this.showStale = options.showStale;
+        this.hasReduce = options.hasReduce;
+      },
+
+      update: function (options) {
+        this.queryParams = options.queryParams;
+        this.showStale = options.showStale;
+        this.hasReduce = options.hasReduce;
+
+        // if the View hasn't already rendered we can rely on afterRender() to pre-fill the fields
+        if (this.hasRendered) {
+          this.render();
+        }
+      },
+
+      afterRender: function () {
+        $("#qoIncludeDocs").prop("checked", this.queryParams.include_docs === "true");
+        this.updateReduceSettings(this.queryParams.reduce === "true");
+      },
+
+      /*
+       * The "Reduce" option comes with baggage:
+       *   - we can't include_docs for reduce = true
+       *   - can't include group_level for reduce = false
+       */
+      onToggleReduceCheckbox: function (e) {
+        e.preventDefault();
+        var isChecked = $(e.currentTarget).prop("checked");
+        this.updateReduceSettings(isChecked);
+      },
+
+
+      // helper function to hide/show, disable/enable fields based on whether "Reduce" is an option and whether
+      // it's checked
+      updateReduceSettings: function (isChecked) {
+        $("#qoReduce").prop("checked", isChecked);
+
+        var $qoIncludeDocs = $("#qoIncludeDocs"),
+          $qoIncludeDocsLabel = $("#qoIncludeDocsLabel"),
+          $qoGroupLevelGroup = $("#qoGroupLevelGroup");
+
+        if (this.hasReduce) {
+          $("#qoGroupLevel").val(this.queryParams.group_level);
+
+          if (isChecked) {
+            $qoIncludeDocs.prop({ "checked": false, "disabled": true });
+            $qoIncludeDocsLabel.addClass("disabled");
+            $qoGroupLevelGroup.removeClass("hide");
+          } else {
+            $qoIncludeDocs.prop("disabled", false);
+            $qoIncludeDocsLabel.removeClass("disabled");
+            $qoGroupLevelGroup.addClass("hide");
+          }
+        } else {
+          $qoIncludeDocs.prop("disabled", false);
+          $qoIncludeDocsLabel.removeClass("disabled");
+          $qoGroupLevelGroup.addClass("hide");
+        }
+      },
+
+      getParams: function () {
+        var params = {};
+        this.$("input:checked,select").each(function () {
+
+          // this ensures that only settings that differ from the defaults are passed along. If we didn't do this,
+          // the query string would be loaded up with all possible vals for each search (which would work, but would be ugly)
+          if (this.value !== defaultOptions.queryParams[this.name]) {
+            params[this.name] = this.value;
+          }
+        });
+        return params;
+      },
+
+      serialize: function () {
+        return {
+          hasReduce: this.hasReduce,
+          showStale: this.showStale
+        };
+      }
+    });
+
+
+    var KeySearchFieldsView = FauxtonAPI.View.extend({
+      template: "addons/documents/templates/query_options_key_search",
+      events: {
+        "click .toggle-btns > label": "toggleKeysSection"
+      },
+
+      initialize: function (options) {
+        this.queryParams = options.queryParams;
+        this.hasReduce = options.hasReduce;
+      },
+
+      update: function (options) {
+        this.queryParams = options.queryParams;
+        this.hasReduce = options.hasReduce;
+
+        if (this.hasRendered) {
+          this.render();
+        }
+      },
+
+      // prefill the form fields
+      afterRender: function () {
+        if (this.queryParams.keys) {
+          this.$(".toggle-btns > label[data-action=showByKeys]").addClass("active");
+          this.$(".js-query-keys-wrapper").removeClass("hide");
+          this.showByKeysSection();
+          $("#keys-input").val(this.queryParams.keys);
+        } else {
+
+          // if the startKey, endKey or inclusive_end differs from the defaults, show the section. Meh, this sucks...
+          if (defaultOptions.queryParams.startkey !== this.queryParams.startkey ||
+            defaultOptions.queryParams.endkey !== this.queryParams.endkey ||
+            defaultOptions.queryParams.inclusive_end !== this.queryParams.inclusive_end) {
+            this.$(".toggle-btns > label[data-action=showBetweenKeys]").addClass("active");
+            this.$(".js-keys-section").addClass("hide");
+            this.$(".js-query-keys-wrapper").removeClass("hide");
+            this.showBetweenKeysSection();
+
+            $("#startkey").prop("disabled", false).val(this.queryParams.startkey);
+            $("#endkey").prop("disabled", false).val(this.queryParams.endkey);
+            $("#qoIncludeEndKeyInResults").prop("checked", this.queryParams.inclusive_end === "true");
+          }
+        }
+      },
+
+      toggleKeysSection: function (e) {
+        e.preventDefault();
+
+        var $clickedEl = $(e.currentTarget);
+        var $keyFieldsWrapper = this.$(".js-query-keys-wrapper");
+
+        if ($clickedEl.hasClass("active")){
+          $clickedEl.removeClass("active");
+          $keyFieldsWrapper.addClass("hide");
+        } else {
+          this.$(".toggle-btns > label").removeClass("active");
+          this.$(".js-keys-section").hide();
+
+          $clickedEl.addClass("active");
+          $keyFieldsWrapper.removeClass("hide");
+
+          // show section and disable what needs to be disabled
+          var action = $clickedEl.data("action");
+          if (action === "showByKeys") {
+            this.showByKeysSection();
+          } else {
+            this.showBetweenKeysSection();
+          }
+        }
+      },
+
+      showByKeysSection: function () {
+        this.$("#js-showKeys, .js-disabled-message").show();
+        this.$('[name="startkey"],[name="endkey"],[name="inclusive_end"]').prop("disabled", true);
+        this.$('[name="keys"]').removeAttr("disabled");
+      },
+
+      showBetweenKeysSection: function (){
+        this.$("#js-showStartEnd").show();
+        this.$('[name="startkey"],[name="endkey"],[name="inclusive_end"]').removeAttr("disabled");
+        this.$('.js-disabled-message').hide();
+        this.$('[name="keys"]').prop("disabled", true);
+      },
+
+      // this assumes that hasValidInputs has been called. Otherwise the returned param data may be invalid
+      getParams: function () {
+        var params = {};
+        var selectedKeysSection = this.getSelectedKeysSection();
+
+        // basically the gist of this is that it only actually returns *relevant* key-value pairs. Defaults
+        // aren't included because they'd clutter up the URL
+        if (selectedKeysSection === "showByKeys") {
+          var keys = $.trim($("#keys-input").val());
+          if (keys !== "") {
+            params.keys = keys;
+          }
+        } else if (selectedKeysSection === "showBetweenKeys") {
+          var startKey = $.trim($("#startkey").val());
+          if (startKey !== defaultOptions.queryParams.startkey) {
+            params.startkey = startKey;
+          }
+          var endKey = $.trim($("#endkey").val());
+          if (endKey !== defaultOptions.queryParams.endkey) {
+            params.endkey = endKey;
+          }
+          var includeEndKeyVal = $("#qoIncludeEndKeyInResults").is(":checked");
+          params.inclusive_end = (includeEndKeyVal) ? "true" : "false";
+        }
+        return params;
+      },
+
+      /*
+       * Checks to see that the user-inputted values are valid. If not, it displays a message to the user.
+       * @returns {boolean} true if all valid; false otherwise
+       */
+      hasValidInputs: function () {
+        var selectedKeysSection = this.getSelectedKeysSection(),
+          errorMsg = null;
+
+        if (selectedKeysSection === "showByKeys") {
+          var keys = this.parseJSON($("#keys-input").val());
+          if (_.isUndefined(keys) || !_.isArray(keys)) {
+            errorMsg = "Keys values must be in an array, e.g [1,2,3]";
+          }
+        } else {
+          var startKey = $.trim($("#startkey").val()),
+            endKey = $.trim($("#endkey").val());
+
+          if (startKey !== "" && _.isUndefined(this.parseJSON(startKey))) {
+            errorMsg = "JSON Parse Error on the Start Key field";
+          } else if (endKey !== "" && _.isUndefined(this.parseJSON(endKey))) {
+            errorMsg = "JSON Parse Error on the End Key field";
+          }
+        }
+
+        if (errorMsg !== null) {
+          this.$(".js-keys-error").empty();
+
+          FauxtonAPI.addNotification({
+            type: "error",
+            msg: errorMsg,
+            clear:  false,
+            selector: ".query-options .errors-container"
+          });
+          return false;
+        }
+
+        return true;
+      },
+
+      parseJSON: function (value) {
+        try {
+          return JSON.parse(value);
+        } catch(e) {
+          return undefined;
+        }
+      },
+
+      getSelectedKeysSection: function () {
+        return this.$(".toggle-btns > label.active").data("action");
+      }
+    });
+
+
+    var AdditionalParamsView = FauxtonAPI.View.extend({
+      template: "addons/documents/templates/query_options_additional_params",
+
+      initialize: function (options) {
+        this.queryParams = options.queryParams;
+        this.showStale = options.showStale;
+      },
+
+      update: function (options) {
+        this.queryParams = options.queryParams;
+        this.showStale = options.showStale;
+        if (this.hasRendered) {
+          this.render();
+        }
+      },
+
+      afterRender: function () {
+        $("#qoUpdateSeq").prop("checked", this.queryParams.update_seq === "true");
+        $("#qoDescending").prop("checked", this.queryParams.descending === "true");
+        $("#qoLimit").val(this.queryParams.limit);
+        $("#qoSkip").val(this.queryParams.skip);
+        $("#qoStale").prop("checked", this.queryParams.stale === "ok");
+      },
+
+      getParams: function () {
+        var params = {};
+        this.$("input,select").each(function () {
+          if ($(this).is(":checkbox")) {
+            if (this.checked) {
+              params[this.name] = this.value;
+            }
+          } else {
+            var val = $.trim(this.value);
+            if (val !== "") {
+              params[this.name] = this.value;
+            }
+          }
+        });
+        return params;
+      },
+
+      hasValidInputs: function () {
+        var allValid = true;
+        var skipVal = $("#qoSkip").val();
+        if (skipVal !== "" && /\D/.test(skipVal)) {
+          FauxtonAPI.addNotification({
+            msg: "Please only enter numbers only for the Skip field.",
+            type: "error",
+            selector: ".query-options .errors-container",
+            clear:  true
+          });
+          allValid = false;
+        }
+        return allValid;
+      },
+
+      serialize: function () {
+        return {
+          showStale: this.showStale
+        };
+      }
+    });
+
+    return Views;
+  });


Mime
View raw message