Source: core/ObjectProcessingMixin.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/>.
 */

/**
 * This mixin module is primarily provided for allowing publication payloads to be processed
 * using a set of utility functions. It was written to be used by the
 * [_PublishPayloadMixin]{@link module:alfresco/renderers/_PublishPayloadMixin} but can be mixed into
 * any module that needs to take advantage of the object processing capabilities that it provides.
 *
 * @module alfresco/core/ObjectProcessingMixin
 * @author Dave Draper
 */
define(["dojo/_base/declare",
        "dojo/_base/lang",
        "dojo/_base/array",
        "alfresco/core/ObjectTypeUtils",
        "alfresco/util/hashUtils"],
        function(declare, lang, array, ObjectTypeUtils, hashUtils) {

   return declare(null, {

      /**
       * This utility function will convert the value "___AlfCurrentItem" into the actual
       * currentItem object. It will return the supplied value if it is anything else.
       *
       * @instance
       * @param {string} v The value to process.
       * @returns The processed value
       */
      setCurrentItem: function alfresco_core_ObjectProcessingMixin__setCurrentItem(v) {
         if (v === "___AlfCurrentItem")
         {
            return this.currentItem;
         }
         else
         {
            return v;
         }
      },

      /**
       * This utility function will perform token substitution on the supplied string value using the
       * values from the calling object. If the token cannot be found in the calling object then it will be left
       * as is (including the curly braces).
       *
       * @param value {String} - the token to replace
       * @param object {Object} - the object containing the token
       * @return {*}
       */
      processTokens: function alfresco_core_ObjectProcessingMixin__processTokens(value, object) {
         // Default to returning the input value if it doesn't match.
         var processedValue = value;

         // Regular expression to match token in curly braces
         var re = /^{[a-zA-Z_$][0-9a-zA-Z_$]*(\.[a-zA-Z_$][0-9a-zA-Z_$]*)*}$/g;

         // If the whole string is the token, replace it if it matches
         if (re.test(value)) {
            // Strip off curly braces
            var tokenWithoutBraces = value.slice(1, -1);

            // If taken exists in object, replace it.
            if (lang.exists(tokenWithoutBraces, object)) {
               processedValue = lang.getObject(tokenWithoutBraces, false, object);
            }
         }
         else {
            // Deal with multiple tokens in the string.
            processedValue = lang.replace(value, lang.hitch(this, this.safeReplace, object));
         }
         return processedValue;
      },

      /**
       * Wrapper for processTokens, searching within this
       *
       * @instance
       * @param {string} valueToProcess The value to process.
       * @returns {*} The processed value
       */
      processInstanceTokens: function alfresco_core_ObjectProcessingMixin__processInstanceTokens(valueToProcess) {
         // Search for tokens in the current scope
         return this.processTokens(valueToProcess, this);
      },

      /**
       * Wrapper for processTokens, searching within currentItem
       *
       * @instance
       * @param {string} valueToProcess The value to process.
       * @returns {*} The processed value
       */
      processCurrentItemTokens: function alfresco_core_ObjectProcessingMixin__processCurrentItemTokens(valueToProcess) {
         // Search for tokens in the current item
         return this.processTokens(valueToProcess, this.currentItem);
      },

      /**
       * Wrapper for processTokens, searching within currentMetadata
       *
       * @instance
       * @param {string} valueToProcess The value to process.
       * @returns {*} The processed value
       * @since 1.0.43
       */
      processCurrentMetadataTokens: function alfresco_core_ObjectProcessingMixin__processCurrentMetadataTokens(valueToProcess) {
         // Search for tokens in the current metadata
         return this.processTokens(valueToProcess, this.currentMetadata);
      },
      
      /**
       * Wrapper for processTokens, searching within the currently set hash values
       *
       * @instance
       * @param {string} valueToProcess The value to process.
       * @returns {*} The processed value
       * @since 1.0.68
       */
      processHashTokens: function alfresco_core_ObjectProcessingMixin__processHashTokens(valueToProcess) {
          // this allows simple mixin of hash values e.g. into payloads
          return this.processTokens(valueToProcess, hashUtils.getHash());
      },
      
      /**
       * This utility function will resolve any token against configured i18n properties
       *
       * @instance
       * @param {string} valueToProcess The value to process.
       * @returns {*} The processed value
       * @since 1.0.68
       */
      processMessageTokens: function alfresco_core_ObjectProcessingMixin__processMessageTokens(valueToProcess) {
          var processedValue;
          
          if (typeof this.message === "function") {
              processedValue = lang.replace(valueToProcess, lang.hitch(this, function(tokenIncludingBraces, tokenWithoutBraces) {
                  var value, message;
                  
                  value = tokenIncludingBraces;
                  message = this.message(tokenWithoutBraces);
                  if (ObjectTypeUtils.isString(message) && message !== tokenWithoutBraces) {
                      value = message;
                  }
                  
                  return value;
              }));
          }
          else {
              processedValue = valueToProcess;
          }
          
          return processedValue;
      },

      /**
       * This utility function will convert any NodeRef token into a URL friendly version (e.g. the "://" will
       * be converted to "/")
       *
       * @instance
       * @param {string} nodeRef The value to process
       * @returns The processed value
       */
      convertNodeRefToUrl: function alfresco_core_ObjectProcessingMixin__convertNodeRefToUrl(nodeRef) {
         return nodeRef.replace(":/", "");
      },

      /**
       * This replace function updates the default Dojo replace function to only replace tokens if the token is a key in the supplied object.
       *
       * @param {object} sourceObject The object to retrieve values from.
       * @param {string} tokenIncudingBraces The token including braces (e.g. "{token}")
       * @param {string} tokenWithoutBraces The token without braces (e.g. "token")
       * @return {string} The replacement value
       */
      safeReplace: function alfresco_code_ObjectProcessingMixin__safeReplace(sourceObject, tokenIncudingBraces, tokenWithoutBraces) {
         var existingValue = lang.getObject(tokenWithoutBraces, false, sourceObject);
         if (existingValue === null || typeof existingValue === "undefined")
         {
            return tokenIncudingBraces;
         }
         else
         {
            return existingValue;
         }
      },

      /**
       * This utility function will replaced all colons in a string with underscores. This has been provided
       * for assisting with processing qnames into values that can be included in a URL.
       *
       * @instance
       * @param {string} v The value to process.
       * @returns {string} The processed value.
       */
      replaceColons: function alfresco_core_ObjectProcessingMixin__replaceColons(v) {
         var u = v.replace(/:/g, "_");
         return u;
      },

      /**
       * This utility function can be used to work through the supplied object and process string
       * values with all the supplied functions.
       *
       * @intance
       * @param {function[]|string[]} functions An array of functions or function names (within "this" scope) to apply to values
       * @param {object} obj The object to process
       * @param {object[]} [ancestors=[]] An ancestors stack, used to check for recursion
       * @return {object} The processed object
       */
      processObject: function alfresco_core_ObjectProcessingMixin__processObject(functions, obj, ancestors) {
         /*jshint loopfunc:true*/
         ancestors = ancestors || [];
         for (var key in obj)
         {
            if (obj.hasOwnProperty(key))
            {
               var value = obj[key];
               if (ObjectTypeUtils.isString(value))
               {
                  array.forEach(functions, lang.hitch(this, this.applyFunction, obj, key));
               }
               else if (ObjectTypeUtils.isArray(value))
               {
                  array.forEach(value, function(entry, index) {
                     // Needs special handling for arrays of strings...
                     if (ObjectTypeUtils.isString(entry))
                     {
                        array.forEach(functions, lang.hitch(this, this.applyFunction, value, index));
                     }
                     else if (array.indexOf(ancestors, value) === -1)
                     {
                        this.processObject(functions, entry, ancestors.concat(obj));
                     }
                  }, this);
               }
               else if (ObjectTypeUtils.isObject(value) && array.indexOf(ancestors, value) === -1)
               {
                  obj[key] = this.processObject(functions, value, ancestors.concat(obj));
               }
            }
         }
         return obj;
      },

      /**
       * Apply the supplied function to the value of the supplied key in the supplied object
       *
       * @instance
       * @param {object} obj The object to get the value from
       * @param {string} key The key of the object to use
       * @param {function|string} func A function or function name (within "this" scope) to apply to values
       */
      applyFunction: function alfresco_core_ObjectProcessingMixin__applyFunction(obj, key, func) {
         var value = obj[key];
         if (typeof func === "function")
         {
            value = func.apply(this, [value]);
         }
         else if (typeof this[func] === "function")
         {
           value = this[func].apply(this, [value]);
         }
         else
         {
            this.alfLog("warn", "The supplied function was not valid", func, this);
         }
         obj[key] = value;
      }
   });
});