UNPKG

node-amy

Version:

A HTML template framework without client-side JavaScript dependencies

151 lines (127 loc) 4.6 kB
'use strict' const PLACEHOLDER_START_SYMBOL = '{{' const PLACEHOLDER_END_SYMBOL = '}}' /** */ class StringInterpolator { /** * Returns true if the string contains a placeholder that can be interpolated * @param {String} string the string to be interpolated * @returns {boolean} * @example * const string = 'I got a new phone! It's a {{ phone.manufacture.name }}!' * * const canInterpolate = interpolator.canInterpolate(string) * // canInterpolate => true */ canInterpolate(string) { return string.includes(PLACEHOLDER_START_SYMBOL) && string.includes(PLACEHOLDER_END_SYMBOL) } /** * Returns an object containing placeholders and static strings * @param {String} string the string to be interpolated * @returns {Object} * @example * const string = 'I got a new phone! It's a {{ phone.manufacture.name }}!' * * const canInterpolate = interpolator.interpolatables(string) * // canInterpolate => true */ interpolatables(string) { return this._tokenize(string) } /** * Interpolates the given string by searching for placeholders/handlebars and resolving the values using * the given context * @param {String} string the string to be interpolated * @param {Object} context the context the interpolated values are resolved against * @returns {String} the interpolated String * @example * const string = 'I got a new phone! It's a {{ phone.manufacture.name }}!' * * const context = { * phone: { * manufacturer: { * name: 'Pixus' * } * } * } * * const propertyPath = 'phone.manufacturer.name' * const value = interpolator.interpolate(propertyPath, context) * // value => 'I got a new phone! It's a Pixus!' */ interpolate(string, context) { const tokens = this._tokenize(string) return this.interpolateWithTokens(tokens, context) } interpolateWithTokens(tokens, context) { const staticTokens = tokens.statics const interpolatableTokens = tokens.interpolatables const result = [] const loopCount = Math.max(staticTokens.length, interpolatableTokens.length) for(let i = 0, n = loopCount; i < n; i++) { if (i < staticTokens.length) { result.push(staticTokens[i]) } if (i < interpolatableTokens.length) { const value = this.valueFor(interpolatableTokens[i], context) result.push(value) } } return result.join('') } /** * Extracts a property out of a given context. Properties can be looked up deeply by specifying the correct * names of the objects all along the path to the value we want to get. Names have to be separated by dots. * @param {String} propertyPath the path to lookup the value we want * @param {Object} context the context the value is resolved against * @returns {Any} * @example * const context = { * phone: { * manufacturer: { * name: 'Sammy' * } * } * } * * const propertyPath = 'phone.manufacturer.name' * const value = interpolator.valueFor(propertyPath, context) * // value => 'Sammy' */ valueFor(propertyPath, context) { if (!propertyPath) { return context } const splittedPath = propertyPath.split('.') return splittedPath.reduce((previous, current) => { return previous ? previous[current] : '' }, context) } /** @private */ _tokenize(string) { const statics = [] const interpolatables = [] let buffer = [] for (let i = 0, n = string.length; i<n; i++) { const char = string.charAt(i) const nextChar = string.charAt(i+1) if (char === '{' && nextChar === '{') { statics.push(buffer.join('')) buffer = [] i++ } else if (char === '}' && nextChar === '}') { interpolatables.push(buffer.join('').trim()) buffer = [] i++ } else { buffer.push(char) } } if (buffer.length) { statics.push(buffer.join('')) } return { statics: statics, interpolatables: interpolatables } } } module.exports = StringInterpolator