Source: src/js/views/EditCollectionView.js

define(['underscore',
        'jquery',
        'backbone',
        "models/Map",
        "models/CollectionModel",
        "models/Search",
        "views/DataCatalogViewWithFilters",
        "views/queryBuilder/QueryBuilderView",
        "text!templates/editCollection.html"],
function(_, $, Backbone, Map, CollectionModel, Search, DataCatalogViewWithFilters,
          QueryBuilder, Template){

  /**
  * @class EditCollectionView
  * @classdesc A view that allows the user to edit the search filters that define their dataset collection
  * @classcategory Views
  * @extends Backbone.View
  * @constructor
  */
  var EditCollectionView = Backbone.View.extend(
    /** @lends EditCollectionView.prototype */{

    /**
    * The type of View this is
    * @type {string}
    */
    type: "EditCollection",

    /**
     * A reference to the parent editor view, if there is one
     * @type {PortalEditorView}
     */
    editorView: undefined,

    /**
    * The HTML tag name to use for this view's element
    * @type {string}
    */
    tagName: "div",

    /**
    * The HTML classes to use for this view's element
    * @type {string}
    */
    className: "edit-collection",

    /**
    * The Collection model that is being edited
    * @type {CollectionModel}
    */
    model: undefined,

    /**
    * The template for this view. An HTML file is converted to an Underscore.js template
    */
    template: _.template(Template),

    /**
    * A jQuery selector for the element that the DataCatalogViewWithFilters should be inserted into
    * @type {string}
    */
    dataCatalogViewContainer: ".data-catalog-view-container",
    /**
    * A jQuery selector for the element that the Save and Cancel buttons should be inserted into
    * @type {string}
    */
    collectionControlsContainer: ".applied-filters-container",
    /**
    * A jQuery selector for the element that the QueryBuilder should be inserted into
    * @type {string}
    */
    queryBuilderViewContainer: ".query-builder-view-container",
    /**
    * A jQuery selector for the element that contains the filter help text
    * @type {string}
    */
    helpTextContainer: "#filter-help-text",

    /**
     * An array of hex color codes used to help distinguish between different rules
     * @type {string[]}
     */
     ruleColorPalette: ["#44AA99", "#137733", "#c9a538", "#CC6677", "#882355", "#AA4499","#332288"],

    /**
     * Query fields to exclude in the metadata field selector of each Query Rule. This
     * is a list of field names that exist in the query service index (i.e. Solr), but
     * which should be hidden in the Query Builder
     * @type {string[]}
     */
    queryBuilderExcludeFields: MetacatUI.appModel.get("collectionQueryExcludeFields"),

    /**
     * Query fields to exclude in the metadata field selector for any Query Rules that are
     * in nested Query Builders (i.e. in nested Filter Groups). This is a list of field
     * names that exist in the query service index (i.e. Solr), but which should be hidden
     * in nested Query Builders
     * @type {string[]}
     */
    queryBuilderNestedExcludeFields: _.union(
      MetacatUI.appModel.get("collectionQueryExcludeFields"),
      MetacatUI.appModel.get("queryIdentifierFields")
    ),

    /**
     * Query fields that do not exist in the query service index, but which we would
     * like to show as options in the Query Builder field input.
     * @type {SpecialField[]}
     * @since 2.15.0
     */
    queryBuilderSpecialFields: MetacatUI.appModel.get("collectionQuerySpecialFields"),

    /**
    * The events this view will listen to and the associated function to call.
    * @type {Object}
    */
    events: {
    },

    /**
    * Is exexcuted when a new EditCollectionView is created
    * @param {Object} options - A literal object with options to pass to the view
    * @property {CollectionModel} options.model - The collection whose search results will be displayed and edited in this view
    */
    initialize: function(options){

      if( typeof options == "object" ){
        this.model = options.model || undefined;
      }

    },

    /**
    * Renders this view
    */
    render: function(){

      var title = "Change the data in your collection"
      if(this.model.isNew()){
        title = "Add data to your collection"
      }

      var helpText = "",
          email = MetacatUI.appModel.get("emailContact");

      if (email) {
        helpText = 'Need help building your data collection? <a href="maito:' + email + '">Get in touch.</a>'
      }

      this.$el.html(this.template({
        title: title,
        description: "Your collection can include any of the datasets that are available on the network. " +
          "Build rules based on metadata to define which datasets should be included in your collection. " +
          "Data added to the network in the future that match these rules will also be added to your collection. " +
          "Click the save button when you're happy with the results.",
        helpText: helpText
      }));

      // Remove this when the Query Builder is no longer new:
      this.$el
        .find(".edit-collection-title")
        .append($('<span class="new-icon" style="margin-left:10px; font-size:1rem; line-height: 25px;"><i class="icon icon-star icon-on-right"></i> NEW </span>'));
        // .append($('<span class="badge badge-info d1_pill d1_pill--primary" style="margin-left:10px">NEW!</span>'));

      // Make sure that we have a series ID before we render the Data Catalog
      // View With Filters. For new portals, we generate and reserve a series ID
      // and use it to add an isPartOf filter to the portal model. This takes time,
      // and influences the search results shown in the data catalog.
      if( this.model.get("seriesId") || this.model.get("latestVersion") ){
        //Render the DataCatalog
        this.renderDataCatalog();
      } else {
        //When the seriesId or latest pid version is found, render the DataCatalog
        this.listenToOnce(this.model, "change:seriesId",    this.renderDataCatalog);
        this.listenToOnce(this.model, "latestVersionFound", this.renderDataCatalog);
      }

      //this.renderCollectionControls();

    },

    /**
     * renderQueryBuilder - Render the QueryBuilder and insert it into this view
     */
    renderQueryBuilder: function(){

      // If the isPartOf filter is hidden, then don't allow users to build
      // a Query Rule using the isPartOf field. If they do, that rule will
      // be hidden the next time they open the portal in the editor. Also,
      // the filter they create will overwrite the isPartOf filter created by
      // default.
      if(MetacatUI.appModel.get("hideIsPartOfFilter") === true ? true : false){
        this.queryBuilderExcludeFields.push("isPartOf")
      }

      var queryBuilder = new QueryBuilder({
        filterGroup: this.model.get("definition"),
        ruleColorPalette: this.ruleColorPalette,
        excludeFields: this.queryBuilderExcludeFields,
        nestedExcludeFields: this.queryBuilderNestedExcludeFields,
        specialFields: this.queryBuilderSpecialFields,
      });

      // Render the Query Builder and insert it into this view
      this.$(this.queryBuilderViewContainer).html(queryBuilder.el);
      queryBuilder.render();
    },

    /**
     * Render the DataCatalogViewWithFilters
     */
    renderDataCatalog: function(){

      this.renderQueryBuilder();

      var searchModel = this.model.get("searchModel");

      searchModel.set("useGeohash", false);

      // Create a DataCatalog view
      var dataCatalogView = new DataCatalogViewWithFilters({
        searchModel: searchModel,
        searchResults: this.model.get("searchResults"),
        mapModel: this.model.get("mapModel") || new Map(),
        isSubView: true,
        mode: "map",
        filters: false,
        solrError500Message: "There may be a problem with one of the rules you created." +
          " Try undoing the last change you made.",
        solrErrorTitle: "Something went wrong searching for datasets that match your query",
        // Override the function that creates filter groups on the left of the
        // data catalog view. With the Query Builder view, they are not needed.
        // Otherwise, the defaultFilterGroups will be added to the Query Builder
        createFilterGroups: function(){ return },
        addAnnotationFilter: function(){ return },
        editorView: this.editorView
      });

      //Render the view and insert it into the page
      this.$(this.dataCatalogViewContainer).html(dataCatalogView.el);
      dataCatalogView.render();

      this.listenTo(this.model.get("searchResults"), "reset", this.toggleHelpText);

    },

    /**
    * Renders the edit collection controls - e.g. a Save and Cancel buttton
    */
    renderCollectionControls: function(){

      //Create a Save button
      var saveButton   = $(document.createElement("a"))
                        .addClass("save btn btn-primary")
                        .text("Save"),
      //Create a Cancel button
          cancelButton = $(document.createElement("a"))
                        .addClass("cancel btn")
                        .text("Cancel"),
      //Create a container for the buttons
          buttons      = $(document.createElement("div"))
                        .addClass("collection-controls")
                        .append(saveButton, cancelButton);

      //Add the buttons to the view
      this.$(this.collectionControlsContainer).append(buttons);

    },

    /**
     * Either hides or shows the help message that lets the user know
     * they can add filters when the collection is empty.
     */
    toggleHelpText: function() {

      //Get the list of filters currently applied to the collection definition
      var currentFilters = this.model.get("definitionFilters"),
          msg = "";

      // If there are no filters set at all, the entire repository catalog will be listed as
      // search results, so display a helpful message
      if ( currentFilters.length == 0 && this.model.get("searchResults").length ) {
        msg = "<h5>Your dataset collection hasn't been created yet.</h5>" +
              "<p>The datasets listed here are totally unfiltered. To specify which datasets belong to your collection, " +
              "add rules in the Query Builder above.</p>";
      }
      //If there is only an isPartOf filter, but no datasets have been marked as part of this collection
      else if( currentFilters.length == 1 &&
               currentFilters.models[0].get("fields")[0] == "isPartOf" &&
               !this.model.get("searchResults").length){

         msg = "<h5>Your dataset collection is empty.</h5> " +
               "<p>To add datasets to your collection, " +
               "add rules in query builder above.</p>";

        //TODO: When the ability to add datasets to collection via the "isPartOf" relationship is added to MetacatUI
        // then update this message with details on how to add datasets to the collection
      }

      //If a message string was created, display it
      if( msg ){
        //Show the message
        MetacatUI.appView.showAlert(msg, "alert-warning", this.$(this.helpTextContainer));
      }
      else{
        //Remove the message
        this.$(this.helpTextContainer).empty();
        //Remove validation messaging, too
        this.$(".notification.error[data-category='definition']").removeClass("error").empty();
      }

    }

  });

  return EditCollectionView;

});