Source: documentlibrary/_AlfHashMixin.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/>.
 */

/**
 * Provides HTML5 style hash based history and location marking. This adopts the pattern originally used by
 * the Document Library in Alfresco Share but can be mixed into any widget that requires hashing. Ideally this
 * should only be mixed into a single widget on a page (e.g. the [DocumentList]{@link module:alfresco/documentlibrary/AlfDocumentList})
 * or multiple publications will occur on a hash change, but the activity of these publications can be tweaked 
 * with the careful application of the hashVarsForUpdate, hashVarsForUpdateRequired and 
 * hashVarsForUpdateMustEqual. 
 * 
 * @module alfresco/documentlibrary/_AlfHashMixin
 * @extends module:alfresco/core/Core
 * @mixes module:alfresco/documentlibrary/_AlfDocumentListTopicMixin
 * @author Dave Draper
 * @author Richard Smith
 * @example <caption>Sample configuration as applied with an [AlfHashList]{@link module:alfresco/lists/AlfHashList})</caption>
 * {
 *    name: "alfresco/lists/AlfHashList",
 *    config: {
 *       loadDataPublishTopic: "MY_TOPIC",
 *       useHash: true,
 *       // Trigger a data update when any of these hash variables is present in the hash string
 *       hashVarsForUpdate: [
 *          "one",
 *          "two",
 *          "three"
 *       ],
 *       // Only perform an update when all of these hash variables are still present in the hash string
 *       hashVarsForUpdateRequired: [
 *          "one",
 *          "two"
 *       ],
 *       // Only proceed with the data update when the hash variable 'view' is equal to 'my_view'
 *       hashVarsForUpdateMustEqual: [
 *          {
 *             name: "view",
 *             value: "my_view"
 *          }
 *       ],
 *       widgets: [
 *          {
 *             name: "alfresco/lists/views/AlfListView",
 *             config: {
 *                ...
 *             }
 *          }
 *       ]
 *    }
 * }
 */

define(["dojo/_base/declare",
        "alfresco/core/Core",
        "alfresco/documentlibrary/_AlfDocumentListTopicMixin",
        "alfresco/util/hashUtils",
        "dojo/_base/lang",
        "dojo/io-query"], 
        function(declare, AlfCore, _AlfDocumentListTopicMixin, hashUtils, lang, ioQuery) {
   
   return declare([AlfCore, _AlfDocumentListTopicMixin], {

      /**
       * Extends the constructor chain to subscribe to the "/dojo/hashchange" topic which is hitched
       * to [onHashChange]{@link module:alfresco/documentlibrary/_AlfHashMixin#onHashChange}.
       * @instance
       */
      constructor: function alfresco_documentlibrary__AlfHashMixin__constructor() {
         this.alfSubscribe("/dojo/hashchange", lang.hitch(this, "onHashChange"));
      },
      
      /**
       * Checks the initial state of the hash location. This is to ensure that bookmarks and copied
       * links work on page loading. It is possible to provide an optional hash string which if provided
       * will be used to set the current hash which in turn should trigger an hash change events.
       * 
       * @instance
       * @param {string} hashString An optional string to use as the hash. If not provided the current hash will be
       */
      initialiseFilter: function alfresco_documentlibrary__AlfHashMixin__intialiseFilter(hashString) {
         hashString && hashUtils.setHash(hashString);
         this.onHashChange(hashUtils.getHashString());
      },
      
      /**
       * Responds to changes in the page hash.
       * 
       * @instance
       * @param {object} payload The publication topic. This object needs to contain the attribute 'filter' for
       * anything to happen.
       */
      onHashChange: function alfresco_documentlibrary__AlfHashMixin__onHashChange(payload) {
         var filterObj = this.processHashFilter(payload);
         this.alfLog("log", "Publishing decoded filter", filterObj);
         this.alfPublish(this.hashChangeTopic, filterObj);
      },

      /**
       * Tests if a hashVar update should be performed by combining the results of payloadContainsUpdateableVar (does the hash 
       * containing ANYTHING we care about), payloadContainsRequiredUpdateableVars (does the hash contain EVERYTHING we care about) 
       * and payloadContainsEqualUpdateableVars (are there values in the hash that are EQUAL to what we care about). 
       *
       * @instance
       * @param {object} payload The payload object
       * @return {boolean}
       * @private
       */
      doHashVarUpdate: function alfresco_documentlibrary__AlfHashMixin__doHashVarUpdate(payload, updateInstanceValues, updateObject) {
         var payloadContainsUpdateableVar = this.payloadContainsUpdateableVar(payload, updateInstanceValues, updateObject),
            payloadContainsRequiredUpdateableVars = this.payloadContainsRequiredUpdateableVars(payload),
            payloadContainsEqualUpdateableVars = this.payloadContainsEqualUpdateableVars(payload);
         return payloadContainsUpdateableVar && payloadContainsRequiredUpdateableVars && payloadContainsEqualUpdateableVars;
      },

      /**
       * Compares the payload object with the hashVarsForUpdate array of key names
       * Returns true if hashVarsForUpdate is empty
       * Returns true if the payload contains a key that is specified in hashVarsForUpdate
       * Returns false otherwise
       *
       * Using configuration, this function allows hash updates to be performed when a particular hash value changes.
       * 
       * @instance
       * @param {object} payload The payload object
       * @param {boolean} updateInstanceValues Indicates whether or not the list instance should be updated with the payload values
       * @return {boolean}
       */
      payloadContainsUpdateableVar: function alfresco_documentlibrary__AlfHashMixin__payloadContainsUpdateableVar(payload, updateInstanceValues, updateObject) {
         // jshint maxcomplexity:false
         var containsUpdateableVar = false;

         // No hashVarsForUpdate - return true
         if(!this.hashVarsForUpdate || this.hashVarsForUpdate.length === 0)
         {
            containsUpdateableVar = true;
            if (updateInstanceValues === true)
            {
               lang.mixin(updateObject || this, payload);
            }
         }
         else
         {
            // Iterate over the keys defined in hashVarsForUpdate - return true if the payload contains one of them
            for(var i=0; i < this.hashVarsForUpdate.length; i++)
            {
               if(this.hashVarsForUpdate[i] in payload)
               {
                  if (updateInstanceValues === true)
                  {
                     var objectToUpdate = updateObject || this;
                     objectToUpdate[this.hashVarsForUpdate[i]] = payload[this.hashVarsForUpdate[i]];
                  }
                  containsUpdateableVar = true;
               }
            }

            // This code block has been added to address hash related pagination issues reported in 
            // AKU-293 and AKU-302. We want to be able to support updates for hash parameters that
            // do NOT require updating instance values. At some point we should be able to remove
            // this code block with refactoring to the AlfSearchList and AlfDocumentList
            if (this._coreHashVars)
            {
               for(i=0; i < this._coreHashVars.length; i++)
               {
                  if(this._coreHashVars[i] in payload)
                  {
                     containsUpdateableVar = true;
                  }
               }
            }
         }
         return containsUpdateableVar;
      },

      /**
       * Compares the payload object with the hashVarsForUpdateRequired array of key names
       * Returns true if hashVarsForUpdateRequired is empty
       * Returns true if the payload contains all keys that are specified in hashVarsForUpdateRequired
       * Returns false otherwise
       *
       * Using configuration, this function allows hash updates to be performed only when a particular set of hash values is present. All 
       * of the defined keys must be present in the hash string.
       *
       * @instance
       * @param {object} payload The payload object
       * @return {boolean}
       * @private
       */
      payloadContainsRequiredUpdateableVars: function alfresco_documentlibrary__AlfHashMixin__payloadContainsRequiredUpdateableVars(payload) {
         // No hashVarsForUpdateRequired - return true
         if(!this.hashVarsForUpdateRequired || this.hashVarsForUpdateRequired.length === 0)
         {
            return true;
         }
         
         // Iterate over the keys defined in hashVarsForUpdateRequired - return false if the payload does not contains one of them
         for(var i=0; i < this.hashVarsForUpdateRequired.length; i++)
         {
            if(!(this.hashVarsForUpdateRequired[i] in payload))
            {
               return false;
            }
         }
         return true;
      },

      /**
       * Compares the payload object with the hashVarsForUpdateMustEqual array of key value pairs
       * Returns true if hashVarsForUpdateMustEqual is empty
       * Returns true if the payload contains the keys that are specified in hashVarsForUpdateMustEqual at the correct value
       * Returns false otherwise
       *
       * Using configuration, this function allows hash updates to be performed only when a particular hash value is present and equal 
       * to a set value.
       *
       * @instance
       * @param {object} payload The payload object
       * @return {boolean}
       * @private
       */
      payloadContainsEqualUpdateableVars: function alfresco_documentlibrary__AlfHashMixin__payloadContainsEqualUpdateableVars(payload) {
         // No hashVarsForUpdateMustEqual - return true
         if(!this.hashVarsForUpdateMustEqual || this.hashVarsForUpdateMustEqual.length === 0)
         {
            return true;
         }
         
         // Iterate over the keys defined in hashVarsForUpdateMustEqual - return false if the key is missing or of the wrong value
         for(var i=0; i < this.hashVarsForUpdateMustEqual.length; i++)
         {
            if(!(this.hashVarsForUpdateMustEqual[i].name in payload) || 
               payload[this.hashVarsForUpdateMustEqual[i].name] !== this.hashVarsForUpdateMustEqual[i].value)
            {
               return false;
            }
         }
         return true;
      },

      /**
       * Converts a filter string (of the form filter=<id>|<data>|<display>)) into an
       * object.
       * 
       * @instance
       * @param {object} data The data to convert to a filter
       * @since 1.0.54
       */
      processHashFilter: function alfresco_documentlibrary__AlfHashMixin__processHashFilter(data) {
         var filterObj = ioQuery.queryToObject(data);
         if (!filterObj)
         {
            // The default filter is root location in a document lib...
            filterObj = {
               path: "/"
            };
         }
         return filterObj;
      }
   });
});