• Jump To … +
    modules/Behavior.js modules/Cell.js modules/Collection.js modules/Events.js modules/FormModel.js modules/FormView.js modules/ListView.js modules/Model.js modules/NestedCell.js modules/NestedModel.js modules/Router.js modules/ServiceCell.js modules/View.js modules/behaviors/DataBehavior.js modules/configure.js modules/handlebarsUtils.js modules/history.js modules/mixins/cacheMixin.js modules/mixins/cellMixin.js modules/mixins/loadingMixin.js modules/mixins/modelMixin.js modules/mixins/pollingMixin.js modules/registry.js modules/stickitUtils.js modules/templateRenderer.js modules/torso.js modules/validation.js
  • handlebarsUtils.js

  • ¶
    /**
     * The handlebars reference
     * @external Handlebars
     * @see {@link https://handlebarsjs.com/|Handlebars}
     */
     /**
     * The handlebars Template reference
     * @external Handlebars-Template
     * @see {@link https://handlebarsjs.com/|Handlebars}
     */
    (function(root, factory) {
      if (typeof define === 'function' && define.amd) {
        define(['underscore'], factory);
      } else if (typeof exports === 'object') {
        module.exports = factory(require('underscore'));
      } else {
        root.Torso = root.Torso || {};
        root.Torso.Utils = root.Torso.Utils || {};
        root.Torso.Utils.handlebarsUtils = factory(root._);
      }
    }(this, function(_) {
      'use strict';
    
      /**
       * Extensions to handlebars helpers.
       *
       * Adds additonal helpers to {@link external:Handlebars}
       *
       * @function handlebarsUtils
       * @param {external:Handlebars} Handlebars Add the helpers to this Handlebars object.
       *
       * @author ariel.wexler@vecna.com, kent.willis@vecna.com
       *
       * @see {@link HandlebarsHelper} for the added helpers.
       * @see <a href="../annotated/modules/handlebarsUtils.html">handlebarsUtils Annotated Source</a>
       */
      /** @namespace HandlebarsHelper */
      return function(Handlebars) {
        var FEEDBACK_KEY = 'feedback';
        var MODEL_KEY = 'model';
    
        /**
         * Usage: {{labelFor 'fieldName' value="suffix"}}
         *
         * Generates: for="field-name-suffix"
         *
         * Usage: {{labelFor 'fieldName[x].sub' value="demo" x=123}}
         *
         * Generates: for="field-name-123_sub-demo"
         *
         * Usage: {{labelFor 'fieldName[bar].sub' value="demo" bar="abc"}}
         *
         * Generates: for="field-name_abc_sub-demo"
         *
         * @method HandlebarsHelper.labelFor
         * @param {string} field The field name to convert to a compliant "for" attribute
         * @param {Object} options The handlebars context.  Always passed in as the final argument.
         * @param {string} [option.hash.value] The value tacked on to the end of the field string (useful for radio and checkbox)
         * @returns {string} Compliant HTML generating the "for" attribute
         */
        Handlebars.registerHelper('labelFor', function(field, options) {
          options = _.extend(options, {noValueAttr: true});
          return Handlebars.helpers.formAttr(field, 'for', options);
        });
    
        /**
         * Usage: {{bindModel 'fieldName' value='suffix'}}
         *
         * Generates: id="field-name-suffix" name="field-name" data-model="fieldName" data-feedback="fieldName" value="demo"
         *
         * Usage: {{bindModel 'fieldName[x].sub' value='demo' x=123}}
         *
         * Generates: data-model="fieldName[123].sub" data-feedback="fieldName[123].sub" name="field-name-123_sub"
         *            id="field-name-123_sub-demo" value="demo"
         *
         * Usage: {{bindModel 'fieldName[bar].sub' value='demo' bar='abc'}}
         *
         * Generates: data-model="fieldName.abc.sub" data-feedback="fieldName[abc].sub" name="field-name_abc_sub"
                      id="field-name_abc_sub-demo" value="demo"
         *
         * @method HandlebarsHelper.bindModel
         * @param {string} field The field name to convert to compliant id, name, data-model, and data-feedback attributes
         * @param {Object} options The handlebars context.  Always passed in as the final argument.
         * @param {string} [options.hash.value] The value tacked on to the end of the field string (useful for radio and checkbox)
         * @returns {string} Compliant HTML generating the id, name, data-model, and data-feedback attributes
         */
        Handlebars.registerHelper('bindModel', function(field, options) {
          return Handlebars.helpers.formAttr(field, MODEL_KEY + ', ' + FEEDBACK_KEY + ', name, id', options);
        });
    
        /**
         * Usage: {{feedback 'fieldName'}}
         *
         * Generates: data-feedback="fieldName"
         *
         * Usage: {{feedback 'fieldName[x].sub' value='demo' x=123}}
         *
         * Generates: data-feedback="fieldName[123].sub"
         *
         * Usage: {{feedback 'fieldName[bar].sub value='demo' bar='abc'}}
         *
         * Generates: data-feedback="fieldName[abc].sub"
         *
         * @method HandlebarsHelper.feedback
         * @param {string} field The field name to convert to a compliant data-feedback attribute
         * @param {Object} options The handlebars context.  Always passed in as the final argument.
         * @returns {string} Compliant HTML generating the data-feedback attribute
         */
        Handlebars.registerHelper('feedback', function(field, options) {
          options = _.extend(options, {noValueAttr: true});
          return Handlebars.helpers.formAttr(field, FEEDBACK_KEY, options);
        });
    
        /**
         * Usage: {{formAttr 'fieldName[x].sub' 'id, for' value='demo' x=123}}
         *
         * Generates: id="field-name-123_sub-demo" for="field-name-123_sub-demo" value="demo"
         *
         * Usage: {{feedback 'fieldName[bar].sub value='demo' bar='abc'}}
         *
         * Generates: id="field-name_abc_sub-demo" for="field-name_abc_sub-demo" value="demo"
         *
         * @method HandlebarsHelper.formAttr
         * @param {string} field The field name to convert to a compliant data-feedback attribute
         * @param {Object} options The handlebars context.  Always passed in as the final argument.
         * @param {string} [options.hash.value] The value tacked on to the end of the field string (useful for radio and checkbox)
         * @param {boolean} [options.noValueAttr] when options.noValueAttr is set to true,
                                                  then it will not generate the "value" attribute in the DOM.
         * @returns {string} Compliant HTML generating the data-feedback attribute
         */
        Handlebars.registerHelper('formAttr', function(field, attrs, options) {
          var i, attrName,
            value = (options.hash ? options.hash.value : undefined),
            res = Handlebars.helpers.injectFieldIndices(field, options.hash),
            resWithArrayNotation = Handlebars.helpers.injectFieldIndices(field, options.hash, { forceArrayNotation: true }),
            attributes = '';
          attrs = attrs.split(',');
          for (i = 0; i < attrs.length; i++) {
            attrName = attrs[i].trim();
            var attrEnd = (i === attrs.length - 1) ? '"' : '" ';
            if (attrName === FEEDBACK_KEY) {
  • ¶

    Feedback needs to always use array notation because of the way it finds the element (by stripping [] contents).

              attributes += 'data-feedback="' + resWithArrayNotation + attrEnd;
            } else if (attrName === MODEL_KEY) {
              attributes += 'data-model="' + res + attrEnd;
            } else if (attrName === 'name') {
              attributes += 'name="' + Handlebars.helpers.dasherize(res) + attrEnd;
            } else if (attrName === 'id') {
              attributes += 'id="' + Handlebars.helpers.dasherize(res);
              if (value !== undefined) {
                attributes += '-' + value;
              }
              attributes += attrEnd;
            } else if (attrName === 'for') {
              attributes += 'for="' + Handlebars.helpers.dasherize(res);
              if (value !== undefined) {
                attributes += '-' + value;
              }
              attributes += attrEnd;
            }
          }
          if (value !== undefined && !options.noValueAttr) {
            attributes += ' value="' + value + '"';
          }
          return new Handlebars.SafeString(attributes);
        });
    
        /**
         * Usage: {{feedback 'fieldName[x].sub'}}
         *
         * Generates: field-name[x]_sub
         *
         * @method HandlebarsHelper.dasherize
         * @param {string} str The input string to make HTML compliant (convert to dashes)
         * @returns {string} HTML complicant / dasherized string
         */
        Handlebars.registerHelper('dasherize', function(str) {
          var camelCaseRemoved, dotsRemoved, bracesRemoved;
          camelCaseRemoved = str.replace(/([A-Z])/g, function(rep) {
            return '-' + rep.toLowerCase();
          });
          dotsRemoved = camelCaseRemoved.replace(/\./g, function() {
            return '_';
          });
          bracesRemoved = dotsRemoved.replace(/\[[0-9]+\]/g, function(rep) {
            return '-' + rep.substring(1, rep.length - 1);
          });
          return bracesRemoved;
        });
    
        /**
         * Usage: injectFieldIndices('test[x]-thisIsRegular-y', {x: 123, y: 456} and 'foo[x].abc', x='bar');
         *
         * Generates: 'test[123]-thisIsRegular-y' and 'foo.bar.abc'
         *
         * if options.forceArrayNotation is set then:
         *
         * Generates: 'test[123]-thisIsRegular-y' and 'foo[bar].abc'
         *
         * @method HandlebarsHelper.injectFieldIndices
         * @param {string} field The field name
         * @param {Object} indexMap A map of variables
         * @param {Object} options named parameters
         *   @param {boolean} [options.forceArrayNotation=false] Force the usage of [] insetad of . for string values.
         * @returns {string} the field string with array variables substituted
         */
        Handlebars.registerHelper('injectFieldIndices', function(field, indexMap, options) {
          if (indexMap) {
            return field.replace(/\[.+?\]/g, function(m) {
              var newIndex = indexMap[m.substring(1, m.length - 1)];
              var indexToken = '[' + (newIndex === undefined ? '' : newIndex) + ']';
              var forceArrayNotation = options && options.forceArrayNotation;
              if (_.isString(newIndex) && isNaN(newIndex) && !forceArrayNotation) {
                indexToken = '.' + newIndex;
              }
              return indexToken;
            });
          } else {
            return field;
          }
        });
      };
    }));