UNPKG

useful-handlebars-helpers

Version:

More than 150 Handlebars helpers in 17 categories. Works in browsers and in Node.js.

282 lines (244 loc) 6.81 kB
'use strict'; const hasOwn = Object.hasOwnProperty; const getObject = require('get-object'); const getValue = require('get-value'); const util = require('handlebars-utils'); const isNumber = require('lodash/isNumber'); const typeOf = require('kind-of'); const createFrame = require('../create-frame'); const helpers = module.exports; /** * Extend the context with the properties of other objects. * A shallow merge is performed to avoid mutating the context. * * @param {Object} `objects` One or more objects to extend. * @return {Object} * @api public */ helpers.extend = function(/*objects*/) { const args = [].slice.call(arguments); let opts = {}; if (util.isOptions(args[args.length - 1])) { // remove handlebars options object opts = args.pop().hash; // re-add handlebars options.hash object args.push(opts); } const context = {}; for (let i = 0; i < args.length; i++) { const obj = args[i]; if (util.isObject(obj)) { const keys = Object.keys(obj); for (let j = 0; j < keys.length; j++) { const key = keys[j]; context[key] = obj[key]; } } } return context; }; /** * Block helper that iterates over the properties of * an object, exposing each key and value on the context. * * @param {Object} `context` * @param {Object} `options` * @return {String} * @block * @api public */ helpers.forIn = function(obj, options) { if (!util.isOptions(options)) { return obj.inverse(this); } const data = createFrame(options, options.hash); let result = ''; for (const key in obj) { data.key = key; result += options.fn(obj[key], {data: data}); } return result; }; /** * Block helper that iterates over the **own** properties of * an object, exposing each key and value on the context. * * @param {Object} `obj` The object to iterate over. * @param {Object} `options` * @return {String} * @block * @api public */ helpers.forOwn = function(obj, options) { if (!util.isOptions(options)) { return obj.inverse(this); } const data = createFrame(options, options.hash); let result = ''; for (const key in obj) { if (obj.hasOwnProperty(key)) { data.key = key; result += options.fn(obj[key], {data: data}); } } return result; }; /** * Take arguments and, if they are string or number, convert them to a dot-delineated object property path. * * @param {String|Number} `prop` The property segments to assemble (can be multiple). * @return {String} * @api public */ helpers.toPath = function(/*prop*/) { const prop = []; for (let i = 0; i < arguments.length; i++) { if (typeof arguments[i] === 'string' || typeof arguments[i] === 'number') { prop.push(arguments[i]); } } return prop.join('.'); }; /** * Use property paths (`a.b.c`) to get a value or nested value from * the context. Works as a regular helper or block helper. * * @param {String} `prop` The property to get, optionally using dot notation for nested properties. * @param {Object} `context` The object from which to get the property (defaults to current context). * @param {Object} `options` The handlebars options object, if used as a block helper. * @return {String} * @block * @api public */ helpers.get = function(prop, context, options) { const val = getValue(context, prop); if (options && options.fn) { return val ? options.fn(val) : options.inverse(context); } return val; }; /** * Use property paths (`a.b.c`) to get an object from * the context. Differs from the `get` helper in that this * helper will return the actual object, including the * given property key. Also, this helper does not work as a * block helper. * * @param {String} `prop` The property to get, optionally using dot notation for nested properties. * @param {Object} `context` The context object * @return {String} * @api public */ helpers.getObject = function(prop, context) { return getObject(context, prop); }; /** * Return true if `key` is an own, enumerable property * of the given `context` object. * * ```handlebars * {{hasOwn context key}} * ``` * * @param {String} `key` * @param {Object} `context` The context object. * @return {Boolean} * @api public */ helpers.hasOwn = function(context, key) { return hasOwn.call(context, key); }; /** * Return true if `value` is an object. * * ```handlebars * {{isObject "foo"}} * //=> false * ``` * @param {String} `value` * @return {Boolean} * @api public */ helpers.isObject = function(value) { return typeOf(value) === 'object'; }; /** * Parses the given string using `JSON.parse`. * * ```handlebars * <!-- string: '{"foo": "bar"}' --> * {{JSONparse string}} * <!-- results in: { foo: 'bar' } --> * ``` * @param {String} `string` The string to parse * @contributor github.com/keeganstreet * @block * @api public */ helpers.JSONparse = function(str, options) { return JSON.parse(str); }; /** * Stringify an object using `JSON.stringify`. * * ```handlebars * <!-- object: { foo: 'bar' } --> * {{JSONstringify object}} * <!-- results in: '{"foo": "bar"}' --> * ``` * @param {Object} `obj` Object to stringify * @return {String} * @api public */ helpers.JSONstringify = function(obj, indent) { if (!isNumber(indent)) { indent = 0; } return JSON.stringify(obj, null, indent); }; /** * Deeply merge the properties of the given `objects` with the * context object. * * @param {Object} `object` The target object. Pass an empty object to shallow clone. * @param {Object} `objects` * @return {Object} * @api public */ helpers.merge = function(context/*, objects, options*/) { const args = [].slice.call(arguments); let opts = {}; if (util.isOptions(args[args.length - 1])) { // remove handlebars options object opts = args.pop().hash; // re-add options.hash args.push(opts); } return Object.assign.apply(null, args); }; /** * Pick properties from the context object. * * @param {Array|String} `properties` One or more properties to pick. * @param {Object} `context` * @param {Object} `options` Handlebars options object. * @return {Object} Returns an object with the picked values. If used as a block helper, the values are passed as context to the inner block. If no values are found, the context is passed to the inverse block. * @block * @api public */ helpers.pick = function(props, context, options) { const keys = Array.isArray(props) ? props : [props]; const len = keys.length; let i = -1; let result = {}; while (++i < len) { result = helpers.extend({}, result, getObject(context, keys[i])); } if (options.fn) { if (Object.keys(result).length) { return options.fn(result); } return options.inverse(context); } return result; };