Source: search/FacetFilter.js

/**
 * Copyright (C) 2005-2016 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * 
 * @module alfresco/search/FacetFilter
 * @extends external:dijit/_WidgetBase
 * @mixes external:dojo/_TemplatedMixin
 * @mixes module:alfresco/core/Core
 * @mixes module:alfresco/documentlibrary/_AlfDocumentListTopicMixin
 * @author Dave Draper
 */
define(["dojo/_base/declare",
        "dijit/_WidgetBase", 
        "dijit/_TemplatedMixin",
        "dijit/_OnDijitClickMixin",
        "dojo/text!./templates/FacetFilter.html",
        "alfresco/core/Core",
        "alfresco/core/topics",
        "dojo/_base/lang",
        "dojo/_base/array",
        "dojo/dom-construct",
        "dojo/dom-class",
        "dojo/on",
        "alfresco/util/hashUtils",
        "dojo/io-query",
        "alfresco/core/ArrayUtils"], 
        function(declare, _WidgetBase, _TemplatedMixin, _OnDijitClickMixin, template,  AlfCore, topics, 
                 lang, array, domConstruct, domClass, on, hashUtils, ioQuery, arrayUtils) {

   return declare([_WidgetBase, _TemplatedMixin, AlfCore], {
      
      /**
       * An array of the i18n files to use with this widget.
       * 
       * @instance
       * @type {object[]}
       * @default [{i18nFile: "./i18n/FacetFilter.properties"}]
       */
      i18nRequirements: [{i18nFile: "./i18n/FacetFilter.properties"}],

      /**
       * An array of the CSS files to use with this widget.
       * 
       * @instance cssRequirements {Array}
       * @type {object[]}
       * @default [{cssFile:"./css/FacetFilter.css"}]
       */
      cssRequirements: [{cssFile:"./css/FacetFilter.css"}],
      
      /**
       * The HTML template to use for the widget.
       * @instance
       * @type {string}
       */
      templateString: template,
      
      /**
       * Indicate whether or not the filter is currently applied
       *
       * @instance
       * @type {boolean}
       * @default
       */
      applied: false,
      
      /**
       * The alt-text to use for the image that indicates that a filter has been applied
       *
       * @instance
       * @type {string} 
       * @default
       */
      appliedFilterAltText: "facet.filter.applied.alt-text",

      /**
       * The path to use as the source for the image that indicates that a filter has been applied
       *
       * @instance
       * @type {string}
       * @default
       */
      appliedFilterImageSrc: "12x12-selected-icon.png",

      /**
       * The facet qname
       *
       * @instance
       * @type {string} 
       * @default
       */
      facet: null,

      /**
       * The filter (or more accurately the filterId) for this filter
       * 
       * @instance
       * @type {string} 
       * @default
       */
      filter: null,
      
      /**
       * Additional data for the filter (appended after the filter with a bar, e.g. tag|sometag)
       * 
       * @instance
       * @type {string}
       * @default
       */
      filterData: "",

      /**
       * Indicates that the filter cannot be applied because the a search request is in progress.
       * This is updated by the [blockFilterRequests]{@link module:alfresco/search/FacetFilter#blockFilterRequests}
       * and [unblockFilterRequests]{@link module:alfresco/search/FacetFilter#unblockFilterRequests} functions.
       * It should not be configured.
       * 
       * @instance
       * @type {boolean}
       * @default
       * @since 1.0.51
       */
      _filteringBlocked: false,

      /**
       * Indicates whether or not the full width of the filter (including the count and the white space)
       * can be clicked to toggle the filter.
       * 
       * @instance
       * @type {boolean}
       * @default
       * @since 1.0.47
       */
      fullWidthClick: false,

      /**
       * Indicates that the filter should be hidden. This will be set to "true" if any required data is missing
       * 
       * @instance
       * @type {boolean}
       * @default
       */
      hide: false,
      
      /**
       * When this is set to true the current URL hash fragment will be used to initialise the facet selection
       * and when the facet is selected the hash fragment will be updated with the facet selection.
       *
       * @instance
       * @type {boolean}
       * @default
       */
      useHash: false,

      /**
       * Sets up the attributes required for the HTML template.
       * @instance
       */
      postMixInProperties: function alfresco_search_FacetFilter__postMixInProperties() {
         if (this.label && this.facet && this.filter && this.hits)
         {
            this.label = this.message(this.label);

            // Localize the alt-text for the applied filter message...
            this.appliedFilterAltText = this.message(this.appliedFilterAltText, {0: this.label});

            // Set the source for the image to use to indicate that a filter is applied...
            this.appliedFilterImageSrc = require.toUrl("alfresco/search/css/images/" + this.appliedFilterImageSrc);
         }
         else
         {
            // Hide the filter if there is no label or no link...
            this.alfLog("warn", "Not enough information provided for filter. It will not be displayed", this);
            this.hide = true;
         }
      },
      
      /**
       * @instance
       */
      postCreate: function alfresco_search_FacetFilter__postCreate() {
         if (this.hide === true)
         {
            domClass.add(this.domNode, "hidden");
         }

         if (this.applied)
         {
            domClass.remove(this.removeNode, "hidden");
            domClass.add(this.labelNode, "applied");
         }

         if (this.fullWidthClick)
         {
            domClass.add(this.domNode, "alfresco-search-FacetFilter--full-width-click");
         }

         // See AKU-782 - Ensure that filters cannot be applied when search requests are in progress...
         this.alfSubscribe(topics.SEARCH_REQUEST, lang.hitch(this, this.onSearchRequestStart));
         this.alfSubscribe(topics.GET_DOCUMENT_LIST_SUCCESS, lang.hitch(this, this.onSearchRequestEnd));
      },
      
      /**
       * This function is called from [onSearchRequestStart]{@link module:alfresco/search/FacetFilter#onSearchRequestStart}
       * and sets the [_filteringBlocked]{@link module:alfresco/search/FacetFilter#_filteringBlocked} to be true
       * to prevent further filtering from being performed whilst search requests are in progress.
       * 
       * @instance
       * @since 1.0.51
       */
      blockFilterRequests: function alfresco_search_FacetFilter__blockFilterRequests() {
         this._filteringBlocked = true;
         domClass.add(this.domNode, "alfresco-search-FacetFilter--block-requests");
      },

      /**
       * This function is called from [onSearchRequestEnd]{@link module:alfresco/search/FacetFilter#onSearchRequestEnd}
       * and sets the [_filteringBlocked]{@link module:alfresco/search/FacetFilter#_filteringBlocked} to be false
       * to indicate that the filter can be applied as there is no search request currently in progress.
       * 
       * @instance
       * @since 1.0.51
       */
      unblockFilterRequests: function alfresco_search_FacetFilter__unblockFilterRequests() {
         this._filteringBlocked = false;
         domClass.remove(this.domNode, "alfresco-search-FacetFilter--block-requests");
      },

      /**
       * This function is called when a search request is made. It calls
       * [blockFilterRequests]{@link module:alfresco/search/FacetFilter#blockFilterRequests} to ensure that
       * no further filtering can be performed whilst a search request is in progress.
       * 
       * @instance
       * @param  {object} payload The request start payload
       * @since 1.0.51
       */
      onSearchRequestStart: function alfresco_search_FacetFilter__onSearchRequestStart(/*jshint unused:false*/ payload) {
         this.blockFilterRequests();
      },

      /**
       * This function is called when a search request is made. It calls
       * [unblockFilterRequests]{@link module:alfresco/search/FacetFilter#unblockFilterRequests} to allow filtering
       * to occur as no search request is in progress.
       * 
       * @instance
       * @param  {object} payload The request start payload
       * @since 1.0.51
       */
      onSearchRequestEnd: function alfresco_search_FacetFilter__onSearchRequestEnd(/*jshint unused:false*/ payload) {
         this.unblockFilterRequests();
      },

      /**
       * If the filter has previously been applied then it is removed, if the filter is not applied
       * then it is applied. Note that it is not possible for filters to be applied when
       * [_filteringBlocked]{@link module:alfresco/search/FacetFilter#_filteringBlocked} has been set
       * to true.
       *
       * @instance
       */
      onToggleFilter: function alfresco_search_FacetFilter__onToggleFilter(evt) {
         if (this._filteringBlocked)
         {
            this.alfLog("log", "Facet filtering blocked whilst search request in progress", this);
         }
         else if (evt.target === this.labelNode || this.fullWidthClick)
         {
            if (this.applied)
            {
               this.onClearFilter();
            }
            else
            {
               this.onApplyFilter();
            }
         }
         else
         {
            this.alfLog("debug", "Clicking on details node not supported for current configuration", this);
         }
      },

      /**
       * Applies the current filter by publishing the details of the filter along with the facet to 
       * which it belongs and then displays the "applied" image.
       *
       * @instance
       */
      onApplyFilter: function alfresco_search_FacetFilter__onApplyFilter() {
         var fullFilter = encodeURIComponent(this.facet + "|" + this.filter);
         if(this.useHash)
         {
            this._updateHash(fullFilter, "add");
         }
         else
         {
            this.alfPublish("ALF_APPLY_FACET_FILTER", {
               filter: fullFilter
            });
         }

         domClass.remove(this.removeNode, "hidden");
         domClass.add(this.labelNode, "applied");
         this.applied = true;
      },

      /**
       * Removes the current filter by publishing the details of the filter along with the facet
       * to which it belongs and then hides the "applied" image
       * 
       * @instance
       */
      onClearFilter: function alfresco_search_FacetFilter__onClearFilter() {
         var fullFilter = encodeURIComponent(this.facet + "|" + this.filter);
         if(this.useHash)
         {
            this._updateHash(fullFilter, "remove");
         }
         else
         {
            this.alfPublish("ALF_REMOVE_FACET_FILTER", {
               filter: fullFilter
            });
         }

         domClass.add(this.removeNode, "hidden");
         domClass.remove(this.labelNode, "applied");
         this.applied = false;
      },

      /**
       * Performs updates to the url hash as facets are selected and de-selected
       * 
       * @instance
       */
      _updateHash: function alfresco_search_FacetFilter___updateHash(fullFilter, mode) {
         // Get the existing hash and extract the individual facetFilters into an array
         var aHash = hashUtils.getHash(),
             facetFilters = ((aHash.facetFilters) ? aHash.facetFilters : ""),
             facetFiltersArr = (facetFilters === "") ? [] : facetFilters.split(",");

         // Add or remove the filter from the hash object
         if(mode === "add" && !arrayUtils.arrayContains(facetFiltersArr, fullFilter))
         {
            facetFiltersArr.push(fullFilter);
         }
         else if (mode === "remove" && arrayUtils.arrayContains(facetFiltersArr, fullFilter))
         {
            facetFiltersArr.splice(facetFiltersArr.indexOf(fullFilter), 1);
         }

         // Put the manipulated filters back into the hash object or remove the property if empty
         if(facetFiltersArr.length < 1)
         {
            delete aHash.facetFilters;
         }
         else
         {
            aHash.facetFilters = facetFiltersArr.join();
         }

         // Send the hash value back to navigation
         this.alfPublish("ALF_NAVIGATE_TO_PAGE", {
            url: ioQuery.objectToQuery(aHash),
            type: "HASH"
         }, true);
      }
   });
});