UNPKG

twig

Version:

JS port of the Twig templating language.

337 lines (292 loc) 11.8 kB
// ## twig.functions.js // // This file handles parsing filters. module.exports = function (Twig) { /** * @constant * @type {string} */ const TEMPLATE_NOT_FOUND_MESSAGE = 'Template "{name}" is not defined.'; Twig.functions = { // Attribute, block, constant, date, dump, parent, random,. // Range function from http://phpjs.org/functions/range:499 // Used under an MIT License range(low, high, step) { // http://kevin.vanzonneveld.net // + original by: Waldo Malqui Silva // * example 1: range ( 0, 12 ); // * returns 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] // * example 2: range( 0, 100, 10 ); // * returns 2: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100] // * example 3: range( 'a', 'i' ); // * returns 3: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'] // * example 4: range( 'c', 'a' ); // * returns 4: ['c', 'b', 'a'] const matrix = []; let inival; let endval; const walker = step || 1; let chars = false; if (!isNaN(low) && !isNaN(high)) { inival = parseInt(low, 10); endval = parseInt(high, 10); } else if (isNaN(low) && isNaN(high)) { chars = true; inival = low.charCodeAt(0); endval = high.charCodeAt(0); } else { inival = (isNaN(low) ? 0 : low); endval = (isNaN(high) ? 0 : high); } const plus = (!((inival > endval))); if (plus) { while (inival <= endval) { matrix.push(((chars) ? String.fromCharCode(inival) : inival)); inival += walker; } } else { while (inival >= endval) { matrix.push(((chars) ? String.fromCharCode(inival) : inival)); inival -= walker; } } return matrix; }, cycle(arr, i) { const pos = i % arr.length; return arr[pos]; }, dump(...args) { // Don't pass arguments to `Array.slice`, that is a performance killer const argsCopy = [...args]; const state = this; const EOL = '\n'; const indentChar = ' '; let indentTimes = 0; let out = ''; const indent = function (times) { let ind = ''; while (times > 0) { times--; ind += indentChar; } return ind; }; const displayVar = function (variable) { out += indent(indentTimes); if (typeof (variable) === 'object') { dumpVar(variable); } else if (typeof (variable) === 'function') { out += 'function()' + EOL; } else if (typeof (variable) === 'string') { out += 'string(' + variable.length + ') "' + variable + '"' + EOL; } else if (typeof (variable) === 'number') { out += 'number(' + variable + ')' + EOL; } else if (typeof (variable) === 'boolean') { out += 'bool(' + variable + ')' + EOL; } }; const dumpVar = function (variable) { let i; if (variable === null) { out += 'NULL' + EOL; } else if (variable === undefined) { out += 'undefined' + EOL; } else if (typeof variable === 'object') { out += indent(indentTimes) + typeof (variable); indentTimes++; out += '(' + (function (obj) { let size = 0; let key; for (key in obj) { if (Object.hasOwnProperty.call(obj, key)) { size++; } } return size; })(variable) + ') {' + EOL; for (i in variable) { if (Object.hasOwnProperty.call(variable, i)) { out += indent(indentTimes) + '[' + i + ']=> ' + EOL; displayVar(variable[i]); } } indentTimes--; out += indent(indentTimes) + '}' + EOL; } else { displayVar(variable); } }; // Handle no argument case by dumping the entire render context if (argsCopy.length === 0) { argsCopy.push(state.context); } argsCopy.forEach(variable => { dumpVar(variable); }); return out; }, date(date) { let dateObj; if (date === undefined || date === null || date === '') { dateObj = new Date(); } else if (Twig.lib.is('Date', date)) { dateObj = date; } else if (Twig.lib.is('String', date)) { if (date.match(/^\d+$/)) { dateObj = new Date(date * 1000); } else { dateObj = new Date(Twig.lib.strtotime(date) * 1000); } } else if (Twig.lib.is('Number', date)) { // Timestamp dateObj = new Date(date * 1000); } else { throw new Twig.Error('Unable to parse date ' + date); } return dateObj; }, block(blockName) { const state = this; const block = state.getBlock(blockName); if (block !== undefined) { return block.render(state, state.context); } }, parent() { const state = this; return state.getBlock(state.getNestingStackToken(Twig.logic.type.block).blockName, true).render(state, state.context); }, attribute(object, method, params) { if (Twig.lib.is('Object', object)) { if (Object.hasOwnProperty.call(object, method)) { if (typeof object[method] === 'function') { return object[method].apply(undefined, params); } return object[method]; } } // Array will return element 0-index return object ? (object[method] || undefined) : undefined; }, max(values, ...args) { if (Twig.lib.is('Object', values)) { delete values._keys; return Twig.lib.max(values); } return Reflect.apply(Twig.lib.max, null, [values, ...args]); }, min(values, ...args) { if (Twig.lib.is('Object', values)) { delete values._keys; return Twig.lib.min(values); } return Reflect.apply(Twig.lib.min, null, [values, ...args]); }, template_from_string(template) { const state = this; if (template === undefined) { template = ''; } return Twig.Templates.parsers.twig({ options: state.template.options, data: template }); }, random(value) { const LIMIT_INT31 = 0x80000000; function getRandomNumber(n) { const random = Math.floor(Math.random() * LIMIT_INT31); const min = Math.min.call(null, 0, n); const max = Math.max.call(null, 0, n); return min + Math.floor((max - min + 1) * random / LIMIT_INT31); } if (Twig.lib.is('Number', value)) { return getRandomNumber(value); } if (Twig.lib.is('String', value)) { return value.charAt(getRandomNumber(value.length - 1)); } if (Twig.lib.is('Array', value)) { return value[getRandomNumber(value.length - 1)]; } if (Twig.lib.is('Object', value)) { const keys = Object.keys(value); return value[keys[getRandomNumber(keys.length - 1)]]; } return getRandomNumber(LIMIT_INT31 - 1); }, /** * Returns the content of a template without rendering it * @param {string} name * @param {boolean} [ignoreMissing=false] * @returns {string} */ source(name, ignoreMissing) { const state = this; const {namespaces} = state.template.options; let templateSource; let templateFound = false; const isNodeEnvironment = typeof module !== 'undefined' && typeof module.exports !== 'undefined' && typeof window === 'undefined'; let loader; let path = name; if (namespaces && typeof namespaces === 'object') { path = Twig.path.expandNamespace(namespaces, path); } // If we are running in a node.js environment, set the loader to 'fs'. if (isNodeEnvironment) { loader = 'fs'; } else { loader = 'ajax'; } // Build the params object const params = { id: name, path, method: loader, parser: 'source', async: false, fetchTemplateSource: true }; // Default ignoreMissing to false if (typeof ignoreMissing === 'undefined') { ignoreMissing = false; } // Try to load the remote template // // on exception, log it try { templateSource = Twig.Templates.loadRemote(name, params); // If the template is undefined or null, set the template to an empty string and do NOT flip the // boolean indicating we found the template // // else, all is good! flip the boolean indicating we found the template if (typeof templateSource === 'undefined' || templateSource === null) { templateSource = ''; } else { templateFound = true; } } catch (error) { Twig.log.debug('Twig.functions.source: ', 'Problem loading template ', error); } // If the template was NOT found AND we are not ignoring missing templates, return the same message // that is returned by the PHP implementation of the twig source() function // // else, return the template source if (!templateFound && !ignoreMissing) { return TEMPLATE_NOT_FOUND_MESSAGE.replace('{name}', name); } return templateSource; } }; Twig._function = function (_function, value, params) { if (!Twig.functions[_function]) { throw new Twig.Error('Unable to find function ' + _function); } return Twig.functions[_function](value, params); }; Twig._function.extend = function (_function, definition) { Twig.functions[_function] = definition; }; return Twig; };