You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@couchdb.apache.org by benkeen <gi...@git.apache.org> on 2014/10/03 19:41:54 UTC

[GitHub] couchdb-fauxton pull request: New Query Options tray added

GitHub user benkeen opened a pull request:

    https://github.com/apache/couchdb-fauxton/pull/78

    New Query Options tray added

    For scope, see: https://issues.apache.org/jira/browse/COUCHDB-2347
    
    Two important things to mention:
    1. The "Compact View" button has been removed. I want to discuss with @seanbarclay about whether it should really appear in the tray.
    2. There's an outstanding issue (outside this ticket) where the header bar doesn't always show the correct content when navigating from page to page.

You can merge this pull request into a Git repository by running:

    $ git pull https://github.com/benkeen/couchdb-fauxton 2347-new-query-options-tray

Alternatively you can review and apply these changes as the patch at:

    https://github.com/apache/couchdb-fauxton/pull/78.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

    This closes #78
    
----
commit ce90ef894db92333864b8ea3cfcfd7341d96b9c4
Author: Benjamin Keen <be...@gmail.com>
Date:   2014-10-03T17:38:33Z

    New Query Options tray added

----


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18429574
  
    --- Diff: app/addons/documents/views.js ---
    @@ -45,118 +45,88 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions,
           'click .toggle-select-menu': 'selectAllMenu'
         },
     
    -    initialize: function(options){
    -      //adding the database to the object
    +    initialize: function(options) {
           this.database = options.database;
    +      this.params = options.params;
    +
           _.bindAll(this);
           this.selectVisible = false;
    -      FauxtonAPI.Events.on('advancedOptions:updateView', this.updateAllDocs);
           FauxtonAPI.Events.on('success:bulkDelete', this.selectAllMenu);
    -    },
    -
    -    cleanup:function(){
    -      FauxtonAPI.Events.unbind('advancedOptions:updateView');
    -      FauxtonAPI.Events.unbind('success:bulkDelete');
    -    },
    -
    -    selectAllMenu: function(e){
    -      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    -      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
    -    },
     
    -    addAllDocsMenu: function(){
    -      //search docs
    +      // insert the Search Docs field
           this.headerSearch = this.insertView("#header-search", new Views.JumpToDoc({
             database: this.database,
             collection: this.database.allDocs
           }));
    -      //insert queryoptions
    -      //that file is included in require() above and the argument is QueryOptions
    -      // and it wants all these params:
    -      /* Sooooo I searched this file for where Advanced options was originally inserted to see what the hell
    -         is happening.  and it's in AllDocsLayout.  So I'm going to move some of those functions over here
    -
    -        These are required:
    -        this.database = options.database;
    -        this.updateViewFn = options.updateViewFn;
    -        this.previewFn = options.previewFn;
    -
    -        these are booleans:
    -        this.showStale = _.isUndefined(options.showStale) ? false : options.showStale;
    -        this.hasReduce = _.isUndefined(options.hasReduce) ? true : options.hasReduce;
    -
    -        these you only need for view indexes, not all docs because they are about
    -        specific views and design docs (ddocs, also views live inside a ddoc):
    -        this.viewName = options.viewName;
    -        this.ddocName = options.ddocName;
    -      */
    -
    -      /*this.queryOptions = this.insertView("#query-options", new QueryOptions.AdvancedOptions({
    -        database: this.database,
    -        hasReduce: false,
    -        showPreview: false,
    -      }));*/
     
    -      //Moved the apibar view into the components file so you can include it in your views
    -      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar({
    -        endpoint: this.apiEndpoints[0],
    -        documentation: this.apiEndpoints[1]
    +      // add the Query Options modal + header link
    +      this.queryOptions = this.insertView("#query-options", new QueryOptions.QueryOptionsTray({
    +        hasReduce: false,
    +        showStale: false
           }));
    +
    +      // insert the API Url header link
    +      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar());
         },
     
    -    updateApiUrl: function(api){
    -      //this will update the api bar when the route changes
    -      //you can find the method that updates it in components.js Components.ApiBar()
    -      this.apiEndpoints = api;
    -      this.apiBar && this.apiBar.update(api);
    +    afterRender: function() {
    +      this.toggleQueryOptionsHeader(this.isHidden);
         },
     
    -    serialize: function() {
    -      //basically if you want something in a template, You can define it here
    -      return {
    -        database: this.database.get('id')
    -      };
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind('success:bulkDelete');
         },
     
    -    beforeRender:function(){
    -      this.addAllDocsMenu();
    +    selectAllMenu: function(e) {
    +      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    +      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
         },
     
    -    //moved from alldocs layout
    -    updateAllDocs: function (event, paramInfo) {
    -      event.preventDefault();
    +    // updates the API bar when the route changes
    +    updateApiUrl: function(api) {
    +      this.apiBar && this.apiBar.update(api);
    +    },
     
    -      var errorParams = paramInfo.errorParams,
    -          params = paramInfo.params;
    +    // these are similar, but different! resetQueryOptions() completely resets the settings then overlays the new ones;
    +    // updateQueryOptions() just updates the existing settings with whatever is specified. Between them, the
    +    resetQueryOptions: function(options) {
    +      this.queryOptions.resetQueryOptions(options);
    +    },
     
    -      if (_.any(errorParams)) {
    -        _.map(errorParams, function(param) {
    -          return FauxtonAPI.addNotification({
    -            msg: "JSON Parse Error on field: "+param.name,
    -            type: "error",
    -            clear:  true
    -          });
    -        });
    -        FauxtonAPI.addNotification({
    -          msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
    -          type: "warning",
    -          clear:  true
    -        });
    +    updateQueryOptions: function(options) {
    +      this.queryOptions.updateQueryOptions(options);
    +    },
     
    -        return false;
    +    hideQueryOptions: function() {
    +      this.isHidden = true; // TODO should this be here...? move to subview?
    --- End diff --
    
    You can use `$("my-element").is(":visible")` so you don't have to maintain that state additionally


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by kxepal <gi...@git.apache.org>.
Github user kxepal commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18413835
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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" // TODO the doc says I can pass group_level=exact, but it doesn't work
    +    }
    +  };
    +
    +
    +  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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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");
    +
    +      // nah! This seems really overkill now it visually
    +//      if (isChecked && $("#qoIncludeDocs").is(":checked")) {
    +//        FauxtonAPI.addNotification({
    +//          msg: "include_docs has been disabled as you cannot include docs on a reduced view",
    --- End diff --
    
    looks as helpful error


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18430151
  
    --- Diff: app/addons/documents/views.js ---
    @@ -45,118 +45,88 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions,
           'click .toggle-select-menu': 'selectAllMenu'
         },
     
    -    initialize: function(options){
    -      //adding the database to the object
    +    initialize: function(options) {
           this.database = options.database;
    +      this.params = options.params;
    +
           _.bindAll(this);
           this.selectVisible = false;
    -      FauxtonAPI.Events.on('advancedOptions:updateView', this.updateAllDocs);
           FauxtonAPI.Events.on('success:bulkDelete', this.selectAllMenu);
    -    },
    -
    -    cleanup:function(){
    -      FauxtonAPI.Events.unbind('advancedOptions:updateView');
    -      FauxtonAPI.Events.unbind('success:bulkDelete');
    -    },
    -
    -    selectAllMenu: function(e){
    -      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    -      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
    -    },
     
    -    addAllDocsMenu: function(){
    -      //search docs
    +      // insert the Search Docs field
           this.headerSearch = this.insertView("#header-search", new Views.JumpToDoc({
             database: this.database,
             collection: this.database.allDocs
           }));
    -      //insert queryoptions
    -      //that file is included in require() above and the argument is QueryOptions
    -      // and it wants all these params:
    -      /* Sooooo I searched this file for where Advanced options was originally inserted to see what the hell
    -         is happening.  and it's in AllDocsLayout.  So I'm going to move some of those functions over here
    -
    -        These are required:
    -        this.database = options.database;
    -        this.updateViewFn = options.updateViewFn;
    -        this.previewFn = options.previewFn;
    -
    -        these are booleans:
    -        this.showStale = _.isUndefined(options.showStale) ? false : options.showStale;
    -        this.hasReduce = _.isUndefined(options.hasReduce) ? true : options.hasReduce;
    -
    -        these you only need for view indexes, not all docs because they are about
    -        specific views and design docs (ddocs, also views live inside a ddoc):
    -        this.viewName = options.viewName;
    -        this.ddocName = options.ddocName;
    -      */
    -
    -      /*this.queryOptions = this.insertView("#query-options", new QueryOptions.AdvancedOptions({
    -        database: this.database,
    -        hasReduce: false,
    -        showPreview: false,
    -      }));*/
     
    -      //Moved the apibar view into the components file so you can include it in your views
    -      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar({
    -        endpoint: this.apiEndpoints[0],
    -        documentation: this.apiEndpoints[1]
    +      // add the Query Options modal + header link
    +      this.queryOptions = this.insertView("#query-options", new QueryOptions.QueryOptionsTray({
    +        hasReduce: false,
    +        showStale: false
           }));
    +
    +      // insert the API Url header link
    +      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar());
         },
     
    -    updateApiUrl: function(api){
    -      //this will update the api bar when the route changes
    -      //you can find the method that updates it in components.js Components.ApiBar()
    -      this.apiEndpoints = api;
    -      this.apiBar && this.apiBar.update(api);
    +    afterRender: function() {
    +      this.toggleQueryOptionsHeader(this.isHidden);
         },
     
    -    serialize: function() {
    -      //basically if you want something in a template, You can define it here
    -      return {
    -        database: this.database.get('id')
    -      };
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind('success:bulkDelete');
         },
     
    -    beforeRender:function(){
    -      this.addAllDocsMenu();
    +    selectAllMenu: function(e) {
    +      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    +      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
    --- End diff --
    
    That was some older code. Actually - looks like it may be legacy... if you don't mind I'll leave it alone for now. I have a half-dozen follow up tickets in mind to this one, so I can look at it then. :)


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448418
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      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);
    --- End diff --
    
    Just for convention sake can you switch to `_.extend`.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by kxepal <gi...@git.apache.org>.
Github user kxepal commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18413702
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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" // TODO the doc says I can pass group_level=exact, but it doesn't work
    --- End diff --
    
    I believe docs you're referenced is http://docs.couchdb.org/en/latest/couchapp/views/intro.html?highlight=group_level#the-view-to-get-comments-for-posts (and a bit below)?
    
    Not sure, but group_level=exact is a legacy thing that isn't actual any more (btw. nice found!). It origins to https://github.com/apache/couchdb-couch-mrview/blob/master/src/couch_mrview_http.erl#L397 and as you see `group=true` is actually sets special value `exact` for `group_level` field but in the same time parser requires `group_level` query parameter to have integer value.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448874
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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.options.hasReduce) {
    +        $("#qoGroupLevel").val(this.options.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.options.hasReduce,
    +        showStale: this.options.showStale
    +      };
    +    }
    +  });
    +
    +
    +  var KeySearchFieldsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_key_search",
    +    events: {
    +      "click .toggle-btns > label": "toggleKeysSection"
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    // prefill the form fields
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      if (qp.keys) {
    +        this.$(".toggle-btns > label[data-action=showByKeys]").addClass("active");
    +        this.$(".js-query-keys-wrapper").removeClass("hide");
    +        this.showByKeysSection();
    +        $("#keys-input").val(qp.keys);
    +      } else {
    +
    +        // if the startKey, endKey or inclusive_end differs from the defaults, show the section. Meh, this sucks...
    +        if (defaultOptions.queryParams.startkey !== qp.startkey ||
    +            defaultOptions.queryParams.endkey !== qp.endkey ||
    +            defaultOptions.queryParams.inclusive_end !== qp.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(qp.startkey);
    +          $("#endkey").prop("disabled", false).val(qp.endkey);
    +          $("#qoIncludeEndKeyInResults").prop("checked", qp.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") {
    +        params.keys = $("#keys-input").val();
    +      } 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)) {
    +          errorMsg = "Keys must be valid json.";
    +        } else if (!_.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;
    +    },
    +
    +    // move to helpers?
    +    parseJSON: function(value) {
    +      try {
    +        return JSON.parse(value);
    +      } catch(e) {
    +        return undefined;
    +      }
    +    },
    +
    +    getSelectedKeysSection: function() {
    +      return $(".toggle-btns > label.active").data("action");
    --- End diff --
    
    Could you make this `this.$(...` to keep it in the view scope.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448934
  
    --- Diff: assets/fonts/styleguide/fauxtonicon-preview.html ---
    @@ -197,6 +197,7 @@
     .fonticon-exchange:before,
     .fonticon-expand:before,
     .fonticon-eye:before,
    +.fonticon-gears:before,
    --- End diff --
    
    Not sure why this font diff appeared here or are there new icons for this?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen closed the pull request at:

    https://github.com/apache/couchdb-fauxton/pull/78


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18459823
  
    --- Diff: app/addons/documents/views-index.js ---
    @@ -435,26 +436,21 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
           }));
     
           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,
    +        this.rightHeader.resetQueryOptions({
    +          queryParams: this.params,
               hasReduce: this.hasReduce(),
    -          eventer: this.eventer,
               showStale: true
    -        }));
    +        });
           }
    -
         },
     
         afterRender: function() {
    -
           if (this.params && !this.newView) {
    -        this.advancedOptions.updateFromParams(this.params);
    +        this.rightHeader.resetQueryOptions({
    --- End diff --
    
    Well spotted, that looks very fishy - I think the `beforeRender` chunk can be dropped. This was the result of some refactoring - I didn't notice it boiled down to that.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by kxepal <gi...@git.apache.org>.
Github user kxepal commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18413378
  
    --- Diff: app/addons/documents/views-index.js ---
    @@ -435,26 +436,35 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
           }));
     
           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,
    +
    +//        this.eventer = _.extend({}, Backbone.Events);
    --- End diff --
    
    Why commented? Hidden feature?(:


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-57907622
  
    In chrome: click multiple times on the checkboxes and labels for the query options/additional parameters. They often do not update.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448405
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      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() {
    --- End diff --
    
    Nice work here.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18429735
  
    --- Diff: app/addons/documents/views.js ---
    @@ -45,118 +45,88 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions,
           'click .toggle-select-menu': 'selectAllMenu'
         },
     
    -    initialize: function(options){
    -      //adding the database to the object
    +    initialize: function(options) {
           this.database = options.database;
    +      this.params = options.params;
    +
           _.bindAll(this);
           this.selectVisible = false;
    -      FauxtonAPI.Events.on('advancedOptions:updateView', this.updateAllDocs);
           FauxtonAPI.Events.on('success:bulkDelete', this.selectAllMenu);
    -    },
    -
    -    cleanup:function(){
    -      FauxtonAPI.Events.unbind('advancedOptions:updateView');
    -      FauxtonAPI.Events.unbind('success:bulkDelete');
    -    },
    -
    -    selectAllMenu: function(e){
    -      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    -      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
    -    },
     
    -    addAllDocsMenu: function(){
    -      //search docs
    +      // insert the Search Docs field
           this.headerSearch = this.insertView("#header-search", new Views.JumpToDoc({
             database: this.database,
             collection: this.database.allDocs
           }));
    -      //insert queryoptions
    -      //that file is included in require() above and the argument is QueryOptions
    -      // and it wants all these params:
    -      /* Sooooo I searched this file for where Advanced options was originally inserted to see what the hell
    -         is happening.  and it's in AllDocsLayout.  So I'm going to move some of those functions over here
    -
    -        These are required:
    -        this.database = options.database;
    -        this.updateViewFn = options.updateViewFn;
    -        this.previewFn = options.previewFn;
    -
    -        these are booleans:
    -        this.showStale = _.isUndefined(options.showStale) ? false : options.showStale;
    -        this.hasReduce = _.isUndefined(options.hasReduce) ? true : options.hasReduce;
    -
    -        these you only need for view indexes, not all docs because they are about
    -        specific views and design docs (ddocs, also views live inside a ddoc):
    -        this.viewName = options.viewName;
    -        this.ddocName = options.ddocName;
    -      */
    -
    -      /*this.queryOptions = this.insertView("#query-options", new QueryOptions.AdvancedOptions({
    -        database: this.database,
    -        hasReduce: false,
    -        showPreview: false,
    -      }));*/
     
    -      //Moved the apibar view into the components file so you can include it in your views
    -      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar({
    -        endpoint: this.apiEndpoints[0],
    -        documentation: this.apiEndpoints[1]
    +      // add the Query Options modal + header link
    +      this.queryOptions = this.insertView("#query-options", new QueryOptions.QueryOptionsTray({
    +        hasReduce: false,
    +        showStale: false
           }));
    +
    +      // insert the API Url header link
    +      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar());
         },
     
    -    updateApiUrl: function(api){
    -      //this will update the api bar when the route changes
    -      //you can find the method that updates it in components.js Components.ApiBar()
    -      this.apiEndpoints = api;
    -      this.apiBar && this.apiBar.update(api);
    +    afterRender: function() {
    +      this.toggleQueryOptionsHeader(this.isHidden);
         },
     
    -    serialize: function() {
    -      //basically if you want something in a template, You can define it here
    -      return {
    -        database: this.database.get('id')
    -      };
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind('success:bulkDelete');
         },
     
    -    beforeRender:function(){
    -      this.addAllDocsMenu();
    +    selectAllMenu: function(e) {
    +      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    +      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
         },
     
    -    //moved from alldocs layout
    -    updateAllDocs: function (event, paramInfo) {
    -      event.preventDefault();
    +    // updates the API bar when the route changes
    +    updateApiUrl: function(api) {
    +      this.apiBar && this.apiBar.update(api);
    +    },
     
    -      var errorParams = paramInfo.errorParams,
    -          params = paramInfo.params;
    +    // these are similar, but different! resetQueryOptions() completely resets the settings then overlays the new ones;
    +    // updateQueryOptions() just updates the existing settings with whatever is specified. Between them, the
    +    resetQueryOptions: function(options) {
    +      this.queryOptions.resetQueryOptions(options);
    +    },
     
    -      if (_.any(errorParams)) {
    -        _.map(errorParams, function(param) {
    -          return FauxtonAPI.addNotification({
    -            msg: "JSON Parse Error on field: "+param.name,
    -            type: "error",
    -            clear:  true
    -          });
    -        });
    -        FauxtonAPI.addNotification({
    -          msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
    -          type: "warning",
    -          clear:  true
    -        });
    +    updateQueryOptions: function(options) {
    +      this.queryOptions.updateQueryOptions(options);
    +    },
     
    -        return false;
    +    hideQueryOptions: function() {
    +      this.isHidden = true; // TODO should this be here...? move to subview?
    +      if (this.hasRendered) {
    +        this.toggleQueryOptionsHeader(this.isHidden);
           }
    +    },
     
    -      var fragment = window.location.hash.replace(/\?.*$/, '');
    +    showQueryOptions: function() {
    +      this.isHidden = false;
    +      if (this.hasRendered) {
    +        this.toggleQueryOptionsHeader(this.isHidden);
    +      }
    +    },
     
    -      if (!_.isEmpty(params)) {
    -        fragment = fragment + '?' + $.param(params);
    +    toggleQueryOptionsHeader: function(hide) {
    +      if (hide) {
    +        $("#header-query-options").addClass("hide");
    +      } else {
    +        $("#header-query-options").removeClass("hide");
           }
    --- End diff --
    
    You can use `this.$("#header-query-options")` here


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448293
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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);
    --- End diff --
    
    Could you change to `_.extend`.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448254
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    --- End diff --
    
    Yes later we should look at a mixin we can add to Tray views. Nice idea.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18447985
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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);
    --- End diff --
    
    By convention we normally use `_.extend` or `_.defaults` http://underscorejs.org/#defaults. But if they don't fit here then I'm happy to use `$.extend`


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18464938
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    --- End diff --
    
    Not at all! More explicit that way. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by kxepal <gi...@git.apache.org>.
Github user kxepal commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18415078
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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" // TODO the doc says I can pass group_level=exact, but it doesn't work
    +    }
    +  };
    +
    +
    +  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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +//    beforeRender: function () {
    --- End diff --
    
    Ok, just a revising commented bits to make sure that they are left with some propose, not by accident as it commonly happens (:


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18432738
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,509 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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.options.hasReduce) {
    +        $("#qoGroupLevel").val(this.options.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.options.hasReduce,
    +        showStale: this.options.showStale
    +      };
    +    }
    +  });
    +
    +
    +  var KeySearchFieldsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_key_search",
    +    events: {
    +      "click .toggle-btns > label": "toggleKeysSection"
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    // prefill the form fields
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      if (qp.keys) {
    +        this.$(".toggle-btns > label[data-action=showByKeys]").addClass("active");
    +        this.$(".js-query-keys-wrapper").removeClass("hide");
    +        this.showByKeysSection();
    +        $("#keys-input").val(qp.keys);
    +      } else {
    +
    +        // if the startKey, endKey or inclusive_end differs from the defaults, show the section. Meh, this sucks...
    +        if (defaultOptions.queryParams.startkey !== qp.startkey ||
    +            defaultOptions.queryParams.endkey !== qp.endkey ||
    +            defaultOptions.queryParams.inclusive_end !== qp.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(qp.startkey);
    +          $("#endkey").prop("disabled", false).val(qp.endkey);
    +          $("#qoIncludeEndKeyInResults").prop("checked", qp.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") {
    +        params.keys = $("#keys-input").val();
    +      } 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)) {
    +          errorMsg = "Keys must be valid json.";
    +        } else if (!_.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;
    +    },
    +
    +    // move to helpers?
    +    parseJSON: function(value) {
    +      try {
    +        return JSON.parse(value);
    +      } catch(e) {
    +        return undefined;
    +      }
    +    },
    +
    +    getSelectedKeysSection: function() {
    +      return $(".toggle-btns > label.active").data("action");
    +    }
    +  });
    +
    +
    +  var AdditionalParamsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_additional_params",
    +    events: {
    +      "change form.js-view-query-update select": "updateFilters" // TODO MOVE
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      $("#qoUpdateSeq").prop("checked", qp.update_seq === "true");
    +      $("#qoDescending").prop("checked", qp.descending === "true");
    +      $("#qoLimit").val(qp.limit);
    +      $("#qoSkip").val(qp.skip);
    +      $("#qoStale").prop("checked", qp.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.options.showStale
    +      };
    +    }
    +  });
    +
    +  return Views;
    +});
    --- End diff --
    
    Huh, good to know. Thanks!


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448859
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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.options.hasReduce) {
    +        $("#qoGroupLevel").val(this.options.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.options.hasReduce,
    +        showStale: this.options.showStale
    +      };
    +    }
    +  });
    +
    +
    +  var KeySearchFieldsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_key_search",
    +    events: {
    +      "click .toggle-btns > label": "toggleKeysSection"
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    // prefill the form fields
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      if (qp.keys) {
    +        this.$(".toggle-btns > label[data-action=showByKeys]").addClass("active");
    +        this.$(".js-query-keys-wrapper").removeClass("hide");
    +        this.showByKeysSection();
    +        $("#keys-input").val(qp.keys);
    +      } else {
    +
    +        // if the startKey, endKey or inclusive_end differs from the defaults, show the section. Meh, this sucks...
    +        if (defaultOptions.queryParams.startkey !== qp.startkey ||
    +            defaultOptions.queryParams.endkey !== qp.endkey ||
    +            defaultOptions.queryParams.inclusive_end !== qp.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(qp.startkey);
    +          $("#endkey").prop("disabled", false).val(qp.endkey);
    +          $("#qoIncludeEndKeyInResults").prop("checked", qp.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") {
    +        params.keys = $("#keys-input").val();
    +      } 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)) {
    +          errorMsg = "Keys must be valid json.";
    +        } else if (!_.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;
    +    },
    +
    +    // move to helpers?
    --- End diff --
    
    I think its fine for now as I think we only use it here. If we use something like this elsewhere then a move to helpers is a good idea.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18414851
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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" // TODO the doc says I can pass group_level=exact, but it doesn't work
    +    }
    +  };
    +
    +
    +  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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +//    beforeRender: function () {
    --- End diff --
    
    I don't quite follow..? This is commented out because there's more discussion needed about where the "Compact View" button should actually appear. It doesn't make terrible sense to put it in the Query Options tray (I'd argue!) so it'll hopefully end up elsewhere. Once that's in place I'll remove this.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by kxepal <gi...@git.apache.org>.
Github user kxepal commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18415010
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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",
    --- End diff --
    
    Oh, that makes a sense.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18466079
  
    --- Diff: assets/fonts/styleguide/fauxtonicon-preview.html ---
    @@ -197,6 +197,7 @@
     .fonticon-exchange:before,
     .fonticon-expand:before,
     .fonticon-eye:before,
    +.fonticon-gears:before,
    --- End diff --
    
    Yeah, @seanbarclay sent me a new icon. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-58730390
  
    this PR includes commits that are already on master. Maybe a rebase issue? I would suggest to pick over the changes you want to have into a temporary branch, then deleting the local branch of this and after that force-pushing the new cleaned one tempory-branch onto this remote branch if everything looks sane to you.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18429611
  
    --- Diff: app/addons/documents/views.js ---
    @@ -45,118 +45,88 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions,
           'click .toggle-select-menu': 'selectAllMenu'
         },
     
    -    initialize: function(options){
    -      //adding the database to the object
    +    initialize: function(options) {
           this.database = options.database;
    +      this.params = options.params;
    +
           _.bindAll(this);
           this.selectVisible = false;
    -      FauxtonAPI.Events.on('advancedOptions:updateView', this.updateAllDocs);
           FauxtonAPI.Events.on('success:bulkDelete', this.selectAllMenu);
    -    },
    -
    -    cleanup:function(){
    -      FauxtonAPI.Events.unbind('advancedOptions:updateView');
    -      FauxtonAPI.Events.unbind('success:bulkDelete');
    -    },
    -
    -    selectAllMenu: function(e){
    -      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    -      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
    -    },
     
    -    addAllDocsMenu: function(){
    -      //search docs
    +      // insert the Search Docs field
           this.headerSearch = this.insertView("#header-search", new Views.JumpToDoc({
             database: this.database,
             collection: this.database.allDocs
           }));
    -      //insert queryoptions
    -      //that file is included in require() above and the argument is QueryOptions
    -      // and it wants all these params:
    -      /* Sooooo I searched this file for where Advanced options was originally inserted to see what the hell
    -         is happening.  and it's in AllDocsLayout.  So I'm going to move some of those functions over here
    -
    -        These are required:
    -        this.database = options.database;
    -        this.updateViewFn = options.updateViewFn;
    -        this.previewFn = options.previewFn;
    -
    -        these are booleans:
    -        this.showStale = _.isUndefined(options.showStale) ? false : options.showStale;
    -        this.hasReduce = _.isUndefined(options.hasReduce) ? true : options.hasReduce;
    -
    -        these you only need for view indexes, not all docs because they are about
    -        specific views and design docs (ddocs, also views live inside a ddoc):
    -        this.viewName = options.viewName;
    -        this.ddocName = options.ddocName;
    -      */
    -
    -      /*this.queryOptions = this.insertView("#query-options", new QueryOptions.AdvancedOptions({
    -        database: this.database,
    -        hasReduce: false,
    -        showPreview: false,
    -      }));*/
     
    -      //Moved the apibar view into the components file so you can include it in your views
    -      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar({
    -        endpoint: this.apiEndpoints[0],
    -        documentation: this.apiEndpoints[1]
    +      // add the Query Options modal + header link
    +      this.queryOptions = this.insertView("#query-options", new QueryOptions.QueryOptionsTray({
    +        hasReduce: false,
    +        showStale: false
           }));
    +
    +      // insert the API Url header link
    +      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar());
         },
     
    -    updateApiUrl: function(api){
    -      //this will update the api bar when the route changes
    -      //you can find the method that updates it in components.js Components.ApiBar()
    -      this.apiEndpoints = api;
    -      this.apiBar && this.apiBar.update(api);
    +    afterRender: function() {
    +      this.toggleQueryOptionsHeader(this.isHidden);
         },
     
    -    serialize: function() {
    -      //basically if you want something in a template, You can define it here
    -      return {
    -        database: this.database.get('id')
    -      };
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind('success:bulkDelete');
         },
     
    -    beforeRender:function(){
    -      this.addAllDocsMenu();
    +    selectAllMenu: function(e) {
    +      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    +      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
    --- End diff --
    
    you can also use `is(":visible")` here


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-57907572
  
    it is not possible to reach the button on a small screen with scrolling:
    
    ![bildschirmfoto 2014-10-04 um 16 42 00](https://cloud.githubusercontent.com/assets/298166/4515826/d5e22936-4bd4-11e4-8697-eca8956ad6be.png)



---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18430231
  
    --- Diff: app/addons/documents/routes-documents.js ---
    @@ -333,16 +324,20 @@ 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 || "",
    +        rightHeaderView: this.rightHeader,
    +        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();
     
    -      //this.rightHeader.updateApiUrl([this.indexedDocs.urlRef("apiurl", urlParams), "docs"]);
    +      // TODO
    +//      var apiUrl = this.database.url("app") + "/new_view/" + designDoc;
    --- End diff --
    
    As well you might! l'll remove the changed lines. To explain: this was originally commented out - I didn't add/remove any code. I'd noticed that the content of the API Url didn't seem to be correct for that route. 
    
    But I figured it was out of scope for this ticket, so I just put the "TODO" so I wouldn't forget. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448805
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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.options.hasReduce) {
    +        $("#qoGroupLevel").val(this.options.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.options.hasReduce,
    +        showStale: this.options.showStale
    +      };
    +    }
    +  });
    +
    +
    +  var KeySearchFieldsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_key_search",
    +    events: {
    +      "click .toggle-btns > label": "toggleKeysSection"
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    // prefill the form fields
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      if (qp.keys) {
    +        this.$(".toggle-btns > label[data-action=showByKeys]").addClass("active");
    +        this.$(".js-query-keys-wrapper").removeClass("hide");
    +        this.showByKeysSection();
    +        $("#keys-input").val(qp.keys);
    +      } else {
    +
    +        // if the startKey, endKey or inclusive_end differs from the defaults, show the section. Meh, this sucks...
    +        if (defaultOptions.queryParams.startkey !== qp.startkey ||
    --- End diff --
    
    This is good enough. Its never going to be pretty. If you want a different way you could do:
    
        if (!_.all(["startkey", "endkey", "inclusive_end"], function (key) { return defaultOptsion.querParams[key] === qp[key]})) {
         /* do something */
        }


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by kxepal <gi...@git.apache.org>.
Github user kxepal commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18414987
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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" // TODO the doc says I can pass group_level=exact, but it doesn't work
    --- End diff --
    
    No, not at all. `group_level=999` is a workaround to emulate `group=true` (until you key arrays are not contains more than 998 items what isn't common situation). 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18430167
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,509 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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.options.hasReduce) {
    +        $("#qoGroupLevel").val(this.options.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.options.hasReduce,
    +        showStale: this.options.showStale
    +      };
    +    }
    +  });
    +
    +
    +  var KeySearchFieldsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_key_search",
    +    events: {
    +      "click .toggle-btns > label": "toggleKeysSection"
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    // prefill the form fields
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      if (qp.keys) {
    +        this.$(".toggle-btns > label[data-action=showByKeys]").addClass("active");
    +        this.$(".js-query-keys-wrapper").removeClass("hide");
    +        this.showByKeysSection();
    +        $("#keys-input").val(qp.keys);
    +      } else {
    +
    +        // if the startKey, endKey or inclusive_end differs from the defaults, show the section. Meh, this sucks...
    +        if (defaultOptions.queryParams.startkey !== qp.startkey ||
    +            defaultOptions.queryParams.endkey !== qp.endkey ||
    +            defaultOptions.queryParams.inclusive_end !== qp.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(qp.startkey);
    +          $("#endkey").prop("disabled", false).val(qp.endkey);
    +          $("#qoIncludeEndKeyInResults").prop("checked", qp.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") {
    +        params.keys = $("#keys-input").val();
    +      } 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)) {
    +          errorMsg = "Keys must be valid json.";
    +        } else if (!_.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;
    +    },
    +
    +    // move to helpers?
    +    parseJSON: function(value) {
    +      try {
    +        return JSON.parse(value);
    +      } catch(e) {
    +        return undefined;
    +      }
    +    },
    +
    +    getSelectedKeysSection: function() {
    +      return $(".toggle-btns > label.active").data("action");
    +    }
    +  });
    +
    +
    +  var AdditionalParamsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_additional_params",
    +    events: {
    +      "change form.js-view-query-update select": "updateFilters" // TODO MOVE
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      $("#qoUpdateSeq").prop("checked", qp.update_seq === "true");
    +      $("#qoDescending").prop("checked", qp.descending === "true");
    +      $("#qoLimit").val(qp.limit);
    +      $("#qoSkip").val(qp.skip);
    +      $("#qoStale").prop("checked", qp.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.options.showStale
    +      };
    +    }
    +  });
    +
    +  return Views;
    +});
    --- End diff --
    
    At the end of the file? Huh! Is that necessary?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18432625
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,509 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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.options.hasReduce) {
    +        $("#qoGroupLevel").val(this.options.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.options.hasReduce,
    +        showStale: this.options.showStale
    +      };
    +    }
    +  });
    +
    +
    +  var KeySearchFieldsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_key_search",
    +    events: {
    +      "click .toggle-btns > label": "toggleKeysSection"
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    // prefill the form fields
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      if (qp.keys) {
    +        this.$(".toggle-btns > label[data-action=showByKeys]").addClass("active");
    +        this.$(".js-query-keys-wrapper").removeClass("hide");
    +        this.showByKeysSection();
    +        $("#keys-input").val(qp.keys);
    +      } else {
    +
    +        // if the startKey, endKey or inclusive_end differs from the defaults, show the section. Meh, this sucks...
    +        if (defaultOptions.queryParams.startkey !== qp.startkey ||
    +            defaultOptions.queryParams.endkey !== qp.endkey ||
    +            defaultOptions.queryParams.inclusive_end !== qp.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(qp.startkey);
    +          $("#endkey").prop("disabled", false).val(qp.endkey);
    +          $("#qoIncludeEndKeyInResults").prop("checked", qp.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") {
    +        params.keys = $("#keys-input").val();
    +      } 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)) {
    +          errorMsg = "Keys must be valid json.";
    +        } else if (!_.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;
    +    },
    +
    +    // move to helpers?
    +    parseJSON: function(value) {
    +      try {
    +        return JSON.parse(value);
    +      } catch(e) {
    +        return undefined;
    +      }
    +    },
    +
    +    getSelectedKeysSection: function() {
    +      return $(".toggle-btns > label.active").data("action");
    +    }
    +  });
    +
    +
    +  var AdditionalParamsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_additional_params",
    +    events: {
    +      "change form.js-view-query-update select": "updateFilters" // TODO MOVE
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      $("#qoUpdateSeq").prop("checked", qp.update_seq === "true");
    +      $("#qoDescending").prop("checked", qp.descending === "true");
    +      $("#qoLimit").val(qp.limit);
    +      $("#qoSkip").val(qp.skip);
    +      $("#qoStale").prop("checked", qp.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.options.showStale
    +      };
    +    }
    +  });
    +
    +  return Views;
    +});
    --- End diff --
    
    Please try to add newlines to the end of textfiles. Reason is that Unix tools handle files that are not terminated by a newline different than the ones terminated by a newline. Git even warns you in a diff (`No newline at end of file`)
    
    A good read is http://stackoverflow.com/questions/729692/why-should-files-end-with-a-newline regarding that topic


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18415052
  
    --- Diff: app/addons/documents/views-index.js ---
    @@ -435,26 +436,35 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
           }));
     
           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,
    +
    +//        this.eventer = _.extend({}, Backbone.Events);
    --- End diff --
    
    Oops, I'll remove it! Old code. [the preview / updateView stuff wasn't done in the original Bocoup code, btw - just stubbed out]. It's be added at a later date but not with this commit. 
    
    Well spotted! :)


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18460102
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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);
    --- End diff --
    
    Great, np. Those functions look comparable. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18414742
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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" // TODO the doc says I can pass group_level=exact, but it doesn't work
    --- End diff --
    
    Ah, interesting! So is the "999" just a workaround - a high number to ensure "exact"? 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18414707
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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",
    --- End diff --
    
    Yeah, bit odd hey? It's because these settings map directly to the query string, which are all pulled out as strings. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18430171
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,509 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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.options.hasReduce) {
    +        $("#qoGroupLevel").val(this.options.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.options.hasReduce,
    +        showStale: this.options.showStale
    +      };
    +    }
    +  });
    +
    +
    +  var KeySearchFieldsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_key_search",
    +    events: {
    +      "click .toggle-btns > label": "toggleKeysSection"
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    // prefill the form fields
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      if (qp.keys) {
    +        this.$(".toggle-btns > label[data-action=showByKeys]").addClass("active");
    +        this.$(".js-query-keys-wrapper").removeClass("hide");
    +        this.showByKeysSection();
    +        $("#keys-input").val(qp.keys);
    +      } else {
    +
    +        // if the startKey, endKey or inclusive_end differs from the defaults, show the section. Meh, this sucks...
    +        if (defaultOptions.queryParams.startkey !== qp.startkey ||
    +            defaultOptions.queryParams.endkey !== qp.endkey ||
    +            defaultOptions.queryParams.inclusive_end !== qp.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(qp.startkey);
    +          $("#endkey").prop("disabled", false).val(qp.endkey);
    +          $("#qoIncludeEndKeyInResults").prop("checked", qp.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") {
    +        params.keys = $("#keys-input").val();
    +      } 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)) {
    +          errorMsg = "Keys must be valid json.";
    +        } else if (!_.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;
    +    },
    +
    +    // move to helpers?
    +    parseJSON: function(value) {
    +      try {
    +        return JSON.parse(value);
    +      } catch(e) {
    +        return undefined;
    +      }
    +    },
    +
    +    getSelectedKeysSection: function() {
    +      return $(".toggle-btns > label.active").data("action");
    +    }
    +  });
    +
    +
    +  var AdditionalParamsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_additional_params",
    +    events: {
    +      "change form.js-view-query-update select": "updateFilters" // TODO MOVE
    --- End diff --
    
    Yikes, well spotted.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-57907886
  
    Hi Ben,
    
    cool stuff, is a pleasure to use!
    
    I found a few issues which is nothing to worry about regarding the size of that PR. :) 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448663
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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.options.hasReduce) {
    +        $("#qoGroupLevel").val(this.options.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.options.hasReduce,
    +        showStale: this.options.showStale
    +      };
    +    }
    +  });
    +
    +
    +  var KeySearchFieldsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_key_search",
    +    events: {
    +      "click .toggle-btns > label": "toggleKeysSection"
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    // prefill the form fields
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      if (qp.keys) {
    --- End diff --
    
    I don't really like `qp` as a variable name. Could you change this to `queryParam`.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18461839
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    --- End diff --
    
    Ahh, interesting...! I thought doing a _.bind() would set the context but that didn't seem to work in this instance.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18415642
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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" // TODO the doc says I can pass group_level=exact, but it doesn't work
    +    }
    +  };
    +
    +
    +  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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +//    beforeRender: function () {
    --- End diff --
    
    haha agreed, will do :)


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-58052261
  
    Again, thanks for the feedback @garrensmith! All fixed locally. Just one thing I'm not clear on. I replaced the `this.rightHeader.resetQueryOptions()` call in ViewEditor.afterRender with:
    
    ```javascript
            FauxtonAPI.triggerRouteEvent('resetQueryOptions', {
              queryParams: this.params,
              hasReduce: this.hasReduce(),
              showStale: true
            });
    ```
    Then added an event on the Route Object that mapped to this: 
    
    ```
        resetQueryOptions: function(options) {
          this.rightHeader.resetQueryOptions(options);
        },
    ```
    However, now it appears that the `afterRender()` method of the ViewEditor fires twice, and the header components disappear. I don't understand what's happening there - anything obvious I'm missing? 



---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18463316
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    --- End diff --
    
    Agreed. I'll add a `utils.replaceQueryParams()` function & update the spots.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18429672
  
    --- Diff: 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>
    --- End diff --
    
    newline missing


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-57909920
  
    Marvellous, thanks so much for the feedback! I'll go through them now. :)


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-57997586
  
    @benkeen this is fantastic work. You have made a section of our code that was rather ugly into something great. I really like this.
    
    One thing though is that I'm not sure you should pass the `rightHeader` into the `ViewEditor` view (I have a feeling I might have asked you to do this in the beginning but I now realise its not great). I think we should rather fire a routeObject event, like we do for `route:updateAllDocs`, when we want the query options to change because of a `ViewEditor` change. We should only pass views into other viewsif they are genuine sub-views.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-57916761
  
    The scrolling issue + multiple click issues are also now fixed. [The latter was kind of neat, it's a Chrome bug: https://code.google.com/p/chromium/issues/detail?id=411065]


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by kxepal <gi...@git.apache.org>.
Github user kxepal commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18413804
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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" // TODO the doc says I can pass group_level=exact, but it doesn't work
    +    }
    +  };
    +
    +
    +  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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +//    beforeRender: function () {
    --- End diff --
    
    Not actual?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by kxepal <gi...@git.apache.org>.
Github user kxepal commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18413756
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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",
    --- End diff --
    
    Just curious but why boolean parameters are coded as strings?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448234
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    --- End diff --
    
    Here is a nice fix, if you change this to:
    
        FauxtonAPI.Events.on('QueryOptions:openTray', this.toggleQueryOptionsTray, this);
    
    Then you don't need the anonymous function to set the context. Same for the event on line 74. See the [backbone docs](http://backbonejs.org/#Events-on) for the `[context]` optional parameter.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448395
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    --- End diff --
    
    We do this trick in a couple places. I think we should create a `util` function that gets us the fragment.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18414930
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,522 @@
    +// 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,
    +
    +    // 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" // TODO the doc says I can pass group_level=exact, but it doesn't work
    +    }
    +  };
    +
    +
    +  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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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");
    +
    +      // nah! This seems really overkill now it visually
    +//      if (isChecked && $("#qoIncludeDocs").is(":checked")) {
    +//        FauxtonAPI.addNotification({
    +//          msg: "include_docs has been disabled as you cannot include docs on a reduced view",
    --- End diff --
    
    Originally it was helpful, I agree, but the new UI greys out the "Include as Docs" checkbox label so it's visually very clear what's going on when you check the Reduce checkbox. Seemed overkill to show the (rather intrusive) notification. I *think* I persuaded @seanbarclay that this was okay. :) 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-59005951
  
    @benkeen making some good progress, this is looking good. Querying views works nicely. Unfortunately querying all docs is completely broken. If I refresh the page then the query works.
    
    We need to fix this branches git history. Its very difficult to review. I think you will have to cherry pick all your changes onto a new branch.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18430692
  
    --- Diff: app/addons/documents/routes-documents.js ---
    @@ -391,7 +385,8 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
           this.documentsView.setParams(docParams, urlParams);
           this.documentsView.forceRender();
     
    -      this.apiUrl = [collection.urlRef("apiurl", urlParams), "docs"];
    +      // problem. This
    +      //this.apiUrl = [collection.urlRef("apiurl", urlParams), "docs"];
    --- End diff --
    
    Ooh good call, I'll add a comment. This was commented out due to a related issue that I mentioned in the original PR: 
    
    > 2. There's an outstanding issue (outside this ticket) where the header bar doesn't always show the correct content when navigating from page to page.
    
    @ocelotpotpie is looking into a fix for this. But for this ticket, to prevent the header bar disappearing each time you do a search I'll leave this commented out. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-57997872
  
    While testing:
    
    I think an empty keys is valid and we should be able to handle that:
    ![](https://www.dropbox.com/s/a99jsrst7op3kk9/Screenshot%202014-10-06%2012.12.33.png?dl=1)


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-59043048
  
    Sounds good. The querying all docs is "fixed". Fixed in that when merging in the changes from master, I uncommented a problem line w.r.t. to the apiurl. Once the latest master code is merged in with @ocelotpotpie's change I think it'll be fine.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18429547
  
    --- Diff: app/addons/documents/views.js ---
    @@ -180,11 +150,12 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions,
         deleteDatabase: function (event) {
           event.preventDefault();
     
    -      var enterredName = this.$('#db_name')[0].value;
    -      if (this.database.id != enterredName) {
    -        this.set_error_msg(enterredName + " does not match database id - are you sure you want to delete " + this.database.id + "?");
    +      var enteredName = this.$('#db_name')[0].value;
    --- End diff --
    
    you can use jQuery here: `this.$('#db_name').val()`


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18429684
  
    --- Diff: app/addons/documents/routes-documents.js ---
    @@ -391,7 +385,8 @@ function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resou
           this.documentsView.setParams(docParams, urlParams);
           this.documentsView.forceRender();
     
    -      this.apiUrl = [collection.urlRef("apiurl", urlParams), "docs"];
    +      // problem. This
    +      //this.apiUrl = [collection.urlRef("apiurl", urlParams), "docs"];
    --- End diff --
    
    feeling unsure about this, what is the intention?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18430097
  
    --- Diff: app/addons/documents/views.js ---
    @@ -180,11 +150,12 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions,
         deleteDatabase: function (event) {
           event.preventDefault();
     
    -      var enterredName = this.$('#db_name')[0].value;
    -      if (this.database.id != enterredName) {
    -        this.set_error_msg(enterredName + " does not match database id - are you sure you want to delete " + this.database.id + "?");
    +      var enteredName = this.$('#db_name')[0].value;
    --- End diff --
    
    Will do! One note: I see we do a lot of this.$("#id"). I've just been dumping the "this" since it's a bit more verbose and unnecessary, considering the ID is unique anyway. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18429627
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,509 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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.options.hasReduce) {
    +        $("#qoGroupLevel").val(this.options.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.options.hasReduce,
    +        showStale: this.options.showStale
    +      };
    +    }
    +  });
    +
    +
    +  var KeySearchFieldsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_key_search",
    +    events: {
    +      "click .toggle-btns > label": "toggleKeysSection"
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    // prefill the form fields
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      if (qp.keys) {
    +        this.$(".toggle-btns > label[data-action=showByKeys]").addClass("active");
    +        this.$(".js-query-keys-wrapper").removeClass("hide");
    +        this.showByKeysSection();
    +        $("#keys-input").val(qp.keys);
    +      } else {
    +
    +        // if the startKey, endKey or inclusive_end differs from the defaults, show the section. Meh, this sucks...
    +        if (defaultOptions.queryParams.startkey !== qp.startkey ||
    +            defaultOptions.queryParams.endkey !== qp.endkey ||
    +            defaultOptions.queryParams.inclusive_end !== qp.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(qp.startkey);
    +          $("#endkey").prop("disabled", false).val(qp.endkey);
    +          $("#qoIncludeEndKeyInResults").prop("checked", qp.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") {
    +        params.keys = $("#keys-input").val();
    +      } 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)) {
    +          errorMsg = "Keys must be valid json.";
    +        } else if (!_.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;
    +    },
    +
    +    // move to helpers?
    +    parseJSON: function(value) {
    +      try {
    +        return JSON.parse(value);
    +      } catch(e) {
    +        return undefined;
    +      }
    +    },
    +
    +    getSelectedKeysSection: function() {
    +      return $(".toggle-btns > label.active").data("action");
    +    }
    +  });
    +
    +
    +  var AdditionalParamsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_additional_params",
    +    events: {
    +      "change form.js-view-query-update select": "updateFilters" // TODO MOVE
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      $("#qoUpdateSeq").prop("checked", qp.update_seq === "true");
    +      $("#qoDescending").prop("checked", qp.descending === "true");
    +      $("#qoLimit").val(qp.limit);
    +      $("#qoSkip").val(qp.skip);
    +      $("#qoStale").prop("checked", qp.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.options.showStale
    +      };
    +    }
    +  });
    +
    +  return Views;
    +});
    --- End diff --
    
    newline missing
    
    :space_invader: 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18429537
  
    --- Diff: assets/fonts/fontcustom.yml ---
    @@ -28,10 +28,10 @@ input:
     # --------------------------------------------------------------------------- #
     
     output:
    -  fonts: PUT_YOUR_PATH_HERE/couchdb-fauxton/assets/fonts                              # required
    -  # css: PUT_YOUR_PATH_HERE/couchdb-fauxton/assets/less
    -  preview: PUT_YOUR_PATH_HERE/couchdb-fauxton/assets/fonts/styleguide
    -  icons.less: PUT_YOUR_PATH_HERE/couchdb-fauxton/assets/less 
    +  fonts: /Applications/MAMP/htdocs/couchdb-fauxton/assets/fonts                              # required
    --- End diff --
    
    should that be included?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18464794
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      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);
    --- End diff --
    
    Pity... 'fraid we're stuck with `$.extend` due to the options being a nested object + I need it to intelligently merge the object structures. e.g. options could contain: `{ showStale: true, queryParams: { limit: "5" }}` but I want the end result to contain the entire queryParams object content found in `this.options`.
    
    I totally agree with the need for consistency, but I think I'd make a case to start using the jQuery version just for the extra functionality it affords.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18430089
  
    --- Diff: assets/fonts/fontcustom.yml ---
    @@ -28,10 +28,10 @@ input:
     # --------------------------------------------------------------------------- #
     
     output:
    -  fonts: PUT_YOUR_PATH_HERE/couchdb-fauxton/assets/fonts                              # required
    -  # css: PUT_YOUR_PATH_HERE/couchdb-fauxton/assets/less
    -  preview: PUT_YOUR_PATH_HERE/couchdb-fauxton/assets/fonts/styleguide
    -  icons.less: PUT_YOUR_PATH_HERE/couchdb-fauxton/assets/less 
    +  fonts: /Applications/MAMP/htdocs/couchdb-fauxton/assets/fonts                              # required
    --- End diff --
    
    Ooh drat, thanks. Not sure how that snuck in. I'll revert this file.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-59082702
  
    Closing. This PR has been cleaned up and re-created here: https://github.com/apache/couchdb-fauxton/pull/97


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#issuecomment-58732055
  
    Yeah, git-wise things didn't quite go according to plan on this PR. I've been speaking to @garrensmith a lot about this ticket. Let's wait until he has time to go over it with me on Tues.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18429558
  
    --- Diff: app/addons/documents/views.js ---
    @@ -45,118 +45,88 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions,
           'click .toggle-select-menu': 'selectAllMenu'
         },
     
    -    initialize: function(options){
    -      //adding the database to the object
    +    initialize: function(options) {
           this.database = options.database;
    +      this.params = options.params;
    +
           _.bindAll(this);
           this.selectVisible = false;
    -      FauxtonAPI.Events.on('advancedOptions:updateView', this.updateAllDocs);
           FauxtonAPI.Events.on('success:bulkDelete', this.selectAllMenu);
    -    },
    -
    -    cleanup:function(){
    -      FauxtonAPI.Events.unbind('advancedOptions:updateView');
    -      FauxtonAPI.Events.unbind('success:bulkDelete');
    -    },
    -
    -    selectAllMenu: function(e){
    -      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    -      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
    -    },
     
    -    addAllDocsMenu: function(){
    -      //search docs
    +      // insert the Search Docs field
           this.headerSearch = this.insertView("#header-search", new Views.JumpToDoc({
             database: this.database,
             collection: this.database.allDocs
           }));
    -      //insert queryoptions
    -      //that file is included in require() above and the argument is QueryOptions
    -      // and it wants all these params:
    -      /* Sooooo I searched this file for where Advanced options was originally inserted to see what the hell
    -         is happening.  and it's in AllDocsLayout.  So I'm going to move some of those functions over here
    -
    -        These are required:
    -        this.database = options.database;
    -        this.updateViewFn = options.updateViewFn;
    -        this.previewFn = options.previewFn;
    -
    -        these are booleans:
    -        this.showStale = _.isUndefined(options.showStale) ? false : options.showStale;
    -        this.hasReduce = _.isUndefined(options.hasReduce) ? true : options.hasReduce;
    -
    -        these you only need for view indexes, not all docs because they are about
    -        specific views and design docs (ddocs, also views live inside a ddoc):
    -        this.viewName = options.viewName;
    -        this.ddocName = options.ddocName;
    -      */
    -
    -      /*this.queryOptions = this.insertView("#query-options", new QueryOptions.AdvancedOptions({
    -        database: this.database,
    -        hasReduce: false,
    -        showPreview: false,
    -      }));*/
     
    -      //Moved the apibar view into the components file so you can include it in your views
    -      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar({
    -        endpoint: this.apiEndpoints[0],
    -        documentation: this.apiEndpoints[1]
    +      // add the Query Options modal + header link
    +      this.queryOptions = this.insertView("#query-options", new QueryOptions.QueryOptionsTray({
    +        hasReduce: false,
    +        showStale: false
           }));
    +
    +      // insert the API Url header link
    +      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar());
         },
     
    -    updateApiUrl: function(api){
    -      //this will update the api bar when the route changes
    -      //you can find the method that updates it in components.js Components.ApiBar()
    -      this.apiEndpoints = api;
    -      this.apiBar && this.apiBar.update(api);
    +    afterRender: function() {
    +      this.toggleQueryOptionsHeader(this.isHidden);
         },
     
    -    serialize: function() {
    -      //basically if you want something in a template, You can define it here
    -      return {
    -        database: this.database.get('id')
    -      };
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind('success:bulkDelete');
         },
     
    -    beforeRender:function(){
    -      this.addAllDocsMenu();
    +    selectAllMenu: function(e) {
    +      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    +      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
         },
     
    -    //moved from alldocs layout
    -    updateAllDocs: function (event, paramInfo) {
    -      event.preventDefault();
    +    // updates the API bar when the route changes
    +    updateApiUrl: function(api) {
    +      this.apiBar && this.apiBar.update(api);
    +    },
     
    -      var errorParams = paramInfo.errorParams,
    -          params = paramInfo.params;
    +    // these are similar, but different! resetQueryOptions() completely resets the settings then overlays the new ones;
    +    // updateQueryOptions() just updates the existing settings with whatever is specified. Between them, the
    +    resetQueryOptions: function(options) {
    +      this.queryOptions.resetQueryOptions(options);
    +    },
     
    -      if (_.any(errorParams)) {
    -        _.map(errorParams, function(param) {
    -          return FauxtonAPI.addNotification({
    -            msg: "JSON Parse Error on field: "+param.name,
    -            type: "error",
    -            clear:  true
    -          });
    -        });
    -        FauxtonAPI.addNotification({
    -          msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
    -          type: "warning",
    -          clear:  true
    -        });
    +    updateQueryOptions: function(options) {
    +      this.queryOptions.updateQueryOptions(options);
    +    },
     
    -        return false;
    +    hideQueryOptions: function() {
    +      this.isHidden = true; // TODO should this be here...? move to subview?
    +      if (this.hasRendered) {
    +        this.toggleQueryOptionsHeader(this.isHidden);
           }
    +    },
     
    -      var fragment = window.location.hash.replace(/\?.*$/, '');
    +    showQueryOptions: function() {
    +      this.isHidden = false;
    +      if (this.hasRendered) {
    +        this.toggleQueryOptionsHeader(this.isHidden);
    +      }
    +    },
     
    -      if (!_.isEmpty(params)) {
    -        fragment = fragment + '?' + $.param(params);
    +    toggleQueryOptionsHeader: function(hide) {
    +      if (hide) {
    +        $("#header-query-options").addClass("hide");
    +      } else {
    +        $("#header-query-options").removeClass("hide");
           }
    --- End diff --
    
    You can use `toggleClass` here:
    
    ```javascript
    toggleQueryOptionsHeader: function(hide) {
      $("#header-query-options").toggleClass("hide", hide);
    }
    
    ```
    
    http://api.jquery.com/toggleclass/


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18429680
  
    --- Diff: app/addons/documents/routes-documents.js ---
    @@ -333,16 +324,20 @@ 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 || "",
    +        rightHeaderView: this.rightHeader,
    +        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();
     
    -      //this.rightHeader.updateApiUrl([this.indexedDocs.urlRef("apiurl", urlParams), "docs"]);
    +      // TODO
    +//      var apiUrl = this.database.url("app") + "/new_view/" + designDoc;
    --- End diff --
    
    feeling unsure about this


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by benkeen <gi...@git.apache.org>.
Github user benkeen commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18430125
  
    --- Diff: app/addons/documents/views.js ---
    @@ -45,118 +45,88 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions,
           'click .toggle-select-menu': 'selectAllMenu'
         },
     
    -    initialize: function(options){
    -      //adding the database to the object
    +    initialize: function(options) {
           this.database = options.database;
    +      this.params = options.params;
    +
           _.bindAll(this);
           this.selectVisible = false;
    -      FauxtonAPI.Events.on('advancedOptions:updateView', this.updateAllDocs);
           FauxtonAPI.Events.on('success:bulkDelete', this.selectAllMenu);
    -    },
    -
    -    cleanup:function(){
    -      FauxtonAPI.Events.unbind('advancedOptions:updateView');
    -      FauxtonAPI.Events.unbind('success:bulkDelete');
    -    },
    -
    -    selectAllMenu: function(e){
    -      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    -      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
    -    },
     
    -    addAllDocsMenu: function(){
    -      //search docs
    +      // insert the Search Docs field
           this.headerSearch = this.insertView("#header-search", new Views.JumpToDoc({
             database: this.database,
             collection: this.database.allDocs
           }));
    -      //insert queryoptions
    -      //that file is included in require() above and the argument is QueryOptions
    -      // and it wants all these params:
    -      /* Sooooo I searched this file for where Advanced options was originally inserted to see what the hell
    -         is happening.  and it's in AllDocsLayout.  So I'm going to move some of those functions over here
    -
    -        These are required:
    -        this.database = options.database;
    -        this.updateViewFn = options.updateViewFn;
    -        this.previewFn = options.previewFn;
    -
    -        these are booleans:
    -        this.showStale = _.isUndefined(options.showStale) ? false : options.showStale;
    -        this.hasReduce = _.isUndefined(options.hasReduce) ? true : options.hasReduce;
    -
    -        these you only need for view indexes, not all docs because they are about
    -        specific views and design docs (ddocs, also views live inside a ddoc):
    -        this.viewName = options.viewName;
    -        this.ddocName = options.ddocName;
    -      */
    -
    -      /*this.queryOptions = this.insertView("#query-options", new QueryOptions.AdvancedOptions({
    -        database: this.database,
    -        hasReduce: false,
    -        showPreview: false,
    -      }));*/
     
    -      //Moved the apibar view into the components file so you can include it in your views
    -      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar({
    -        endpoint: this.apiEndpoints[0],
    -        documentation: this.apiEndpoints[1]
    +      // add the Query Options modal + header link
    +      this.queryOptions = this.insertView("#query-options", new QueryOptions.QueryOptionsTray({
    +        hasReduce: false,
    +        showStale: false
           }));
    +
    +      // insert the API Url header link
    +      this.apiBar = this.insertView("#header-api-bar", new Components.ApiBar());
         },
     
    -    updateApiUrl: function(api){
    -      //this will update the api bar when the route changes
    -      //you can find the method that updates it in components.js Components.ApiBar()
    -      this.apiEndpoints = api;
    -      this.apiBar && this.apiBar.update(api);
    +    afterRender: function() {
    +      this.toggleQueryOptionsHeader(this.isHidden);
         },
     
    -    serialize: function() {
    -      //basically if you want something in a template, You can define it here
    -      return {
    -        database: this.database.get('id')
    -      };
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind('success:bulkDelete');
         },
     
    -    beforeRender:function(){
    -      this.addAllDocsMenu();
    +    selectAllMenu: function(e) {
    +      FauxtonAPI.triggerRouteEvent("toggleSelectHeader");
    +      FauxtonAPI.Events.trigger("documents:showSelectAll",this.selectVisible);
         },
     
    -    //moved from alldocs layout
    -    updateAllDocs: function (event, paramInfo) {
    -      event.preventDefault();
    +    // updates the API bar when the route changes
    +    updateApiUrl: function(api) {
    +      this.apiBar && this.apiBar.update(api);
    +    },
     
    -      var errorParams = paramInfo.errorParams,
    -          params = paramInfo.params;
    +    // these are similar, but different! resetQueryOptions() completely resets the settings then overlays the new ones;
    +    // updateQueryOptions() just updates the existing settings with whatever is specified. Between them, the
    +    resetQueryOptions: function(options) {
    +      this.queryOptions.resetQueryOptions(options);
    +    },
     
    -      if (_.any(errorParams)) {
    -        _.map(errorParams, function(param) {
    -          return FauxtonAPI.addNotification({
    -            msg: "JSON Parse Error on field: "+param.name,
    -            type: "error",
    -            clear:  true
    -          });
    -        });
    -        FauxtonAPI.addNotification({
    -          msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
    -          type: "warning",
    -          clear:  true
    -        });
    +    updateQueryOptions: function(options) {
    +      this.queryOptions.updateQueryOptions(options);
    +    },
     
    -        return false;
    +    hideQueryOptions: function() {
    +      this.isHidden = true; // TODO should this be here...? move to subview?
    --- End diff --
    
    True, however in this case I needed to store it. It was due to an interesting race condition: the hide/showQueryOptions function could be called prior to the `RightAllDocsHeader` View being actually rendered (since it loads its template async). Because of that, the `afterRender` function knows whether to hide it or not. Not terribly elegant, but an ok workaround I figured...


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18447476
  
    --- Diff: app/addons/compaction/base.js ---
    @@ -24,8 +25,8 @@ function(app, FauxtonAPI, Compaction) {
           icon: "icon-cogs"
         });
     
    -    FauxtonAPI.registerExtension('advancedOptions:ViewButton', new Compaction.CompactView({}));
    +    FauxtonAPI.registerExtension('QueryOptions:ViewButton', new Compaction.CompactView({}));
       };
     
       return Compaction;
    -});
    +});
    --- End diff --
    
    You will need a new line here. Thanks @robertkowalski I didn't know about that either.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18448538
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,525 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +
    +      $(window).on("resize", self.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    --- End diff --
    
    I don't really like the blind settings of options like this. I would prefer that the view has a `this.queryParams` instead of `this.options.queryParams`. Its a little pedantic so I apologise for that, but I think it will be neater.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by robertkowalski <gi...@git.apache.org>.
Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18429636
  
    --- Diff: app/addons/documents/views-queryoptions.js ---
    @@ -0,0 +1,509 @@
    +// 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,
    +
    +    // 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() {
    +      var self = this;
    +
    +      FauxtonAPI.Events.on('QueryOptions:closeTray', self.closeTray);
    +
    +      // very bizarre. Need the anonymous function + "self" for this to work (?!)
    +      FauxtonAPI.Events.on('QueryOptions:openTray', function() { self.toggleQueryOptionsTray(); });
    +
    +      // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays]
    +      $("body").on("click.queryOptions", function(e) {
    +        if (!self.trayIsVisible()) { return; }
    +        if ($(e.target).closest("#query-options-tray").length === 0) {
    +          self.closeTray();
    +        }
    +      });
    +    },
    +
    +    cleanup: function() {
    +      FauxtonAPI.Events.unbind("QueryOptions:closeTray");
    +      FauxtonAPI.Events.unbind("QueryOptions:openTray");
    +    },
    +
    +    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");
    +      }
    +    },
    +
    +    onCancel: function() {
    +      this.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. This used to pass off the work to the parent View
    +      // (Views.RightAllDocsHeader) but it feels cleaner to do it right here
    +      var fragment = window.location.hash.replace(/\?.*$/, "");
    +      if (!_.isEmpty(params)) {
    +        fragment = fragment + "?" + $.param(params);
    +      }
    +      FauxtonAPI.navigate(fragment, { trigger: false });
    +      FauxtonAPI.triggerRouteEvent("updateAllDocs", { allDocs: true });
    +    },
    +
    +    /*
    +     * 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");
    +    },
    +
    +    // Note: this has been commented out temporarily until a decision has been made regarding the location
    +    // of the "Compact View" button
    +
    +//    beforeRender: function () {
    +//      //if (this.viewName && this.ddocName) {
    +//      var buttonViews = FauxtonAPI.getExtensions('QueryOptions:ViewButton');
    +//        _.each(buttonViews, function (view) {
    +//          this.insertView('#button-options', view);
    +//          //view.update(this.database, this.ddocName, this.viewName);
    +//        }, this);
    +//      //}
    +//    },
    +
    +    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.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +
    +      // 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.options.queryParams.include_docs === "true");
    +      this.updateReduceSettings(this.options.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.options.hasReduce) {
    +        $("#qoGroupLevel").val(this.options.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.options.hasReduce,
    +        showStale: this.options.showStale
    +      };
    +    }
    +  });
    +
    +
    +  var KeySearchFieldsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_key_search",
    +    events: {
    +      "click .toggle-btns > label": "toggleKeysSection"
    +    },
    +
    +    initialize: function(options) {
    +      this.options = options;
    +    },
    +
    +    update: function(options) {
    +      this.options = options;
    +      if (this.hasRendered) {
    +        this.render();
    +      }
    +    },
    +
    +    // prefill the form fields
    +    afterRender: function() {
    +      var qp = this.options.queryParams;
    +
    +      if (qp.keys) {
    +        this.$(".toggle-btns > label[data-action=showByKeys]").addClass("active");
    +        this.$(".js-query-keys-wrapper").removeClass("hide");
    +        this.showByKeysSection();
    +        $("#keys-input").val(qp.keys);
    +      } else {
    +
    +        // if the startKey, endKey or inclusive_end differs from the defaults, show the section. Meh, this sucks...
    +        if (defaultOptions.queryParams.startkey !== qp.startkey ||
    +            defaultOptions.queryParams.endkey !== qp.endkey ||
    +            defaultOptions.queryParams.inclusive_end !== qp.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(qp.startkey);
    +          $("#endkey").prop("disabled", false).val(qp.endkey);
    +          $("#qoIncludeEndKeyInResults").prop("checked", qp.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") {
    +        params.keys = $("#keys-input").val();
    +      } 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)) {
    +          errorMsg = "Keys must be valid json.";
    +        } else if (!_.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;
    +    },
    +
    +    // move to helpers?
    +    parseJSON: function(value) {
    +      try {
    +        return JSON.parse(value);
    +      } catch(e) {
    +        return undefined;
    +      }
    +    },
    +
    +    getSelectedKeysSection: function() {
    +      return $(".toggle-btns > label.active").data("action");
    +    }
    +  });
    +
    +
    +  var AdditionalParamsView = FauxtonAPI.View.extend({
    +    template: "addons/documents/templates/query_options_additional_params",
    +    events: {
    +      "change form.js-view-query-update select": "updateFilters" // TODO MOVE
    --- End diff --
    
    there is no method `updateFilters`


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] couchdb-fauxton pull request: New Query Options tray added

Posted by garrensmith <gi...@git.apache.org>.
Github user garrensmith commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/78#discussion_r18447857
  
    --- Diff: app/addons/documents/views-index.js ---
    @@ -435,26 +436,21 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
           }));
     
           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,
    +        this.rightHeader.resetQueryOptions({
    +          queryParams: this.params,
               hasReduce: this.hasReduce(),
    -          eventer: this.eventer,
               showStale: true
    -        }));
    +        });
           }
    -
         },
     
         afterRender: function() {
    -
           if (this.params && !this.newView) {
    -        this.advancedOptions.updateFromParams(this.params);
    +        this.rightHeader.resetQueryOptions({
    --- End diff --
    
    I'm just trying to understand this section. Why do you call `resetQueryOptions` in `beforeRender` and `afterRender`?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---