UNPKG

caveman

Version:
402 lines (340 loc) 9.54 kB
/** * Caveman * https://github.com/andrewchilds/caveman */ (function () { var partials = {}; var macros = {}; var blockState = []; var prefixes = {}; var options = { escapeByDefault: false, openTag: '{{', closeTag: '}}', shrinkWrap: false // remove whitespace between variables }; var addMacro = function (name, macro) { macros[name] = macro; }; var each = function (obj, fn) { if (obj && obj.forEach) { return obj.forEach(fn); } else { for (var prop in obj) { if (obj.hasOwnProperty(prop)) { fn(obj[prop], prop); } } } }; var escapeText = function (str) { return str.replace(/'/g, "\\'").replace(/^\n+|\n+$/g, '').replace(/\n/g, "'+'"); }; var escapeHTML = function (str) { return exists(str) ? (str + '').replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/\'/g, '&#39;') .replace(/\"/g, '&quot;') : ''; }; var exists = function (obj) { return typeof obj !== 'undefined' && obj !== null; }; var shrinkWrapTemplate = function (str) { return str.replace(/^\s+/gm, ''); }; var resetBlockState = function () { blockState = []; }; var pushBlockState = function (state) { blockState.push(state); }; var popBlockState = function () { return blockState.pop(); }; var getBlockState = function () { return blockState; }; var resetPrefix = function () { prefixes = {}; }; var addPrefix = function (name, prefix) { prefixes[name] = prefix; }; var getPrefix = function () { var str = ''; each(prefixes, function (prefix) { str += prefix; }); return str; }; var expandShortcuts = function (str) { var match = false; each(macros, function (macro) { var shortcut = macro.shortcut; if (!match && shortcut && shortcut.find.test(str)) { match = true; if (typeof shortcut.replace === 'string') { str = shortcut.replace; } else if (typeof shortcut.replace === 'function') { str = shortcut.replace(str); } } }); return str; }; var isScript = function (str) { return str && str.charAt(0) === '-'; }; var isComment = function (str) { return str && str.slice(0, 2) === '//'; }; var translateScript = function (str) { str = str.substr(1).trim(); // remove dash character var match = false; each(macros, function (macro, macroName) { if (!match && macro.find.test(str)) { match = true; if (typeof macro.replace === 'string') { str = macro.replace; } else if (typeof macro.replace === 'function') { str = macro.replace(str); } if (macro.blockEnd) { if (typeof macro.blockEnd === 'string') { pushBlockState(macro.blockEnd); } else if (typeof macro.blockEnd === 'function') { pushBlockState(macro.blockEnd()); } } if (macro.prefix) { addPrefix(macroName, macro.prefix); } } }); if (!match) { // script isn't using any macros return str + ';'; } return str; }; var compile = function (template) { if (typeof template !== 'string') { return ''; } if (options.shrinkWrap) { template = shrinkWrapTemplate(template); } var output = "var _CfS = Caveman.forceStr; var str = '';"; var parts = template.split(options.openTag); resetBlockState(); resetPrefix(); for (var i = 0; i < parts.length; i++) { var part = parts[i]; if (part.indexOf(options.closeTag) !== -1) { part = part.split(options.closeTag); } else { part = ['', part]; } var code = part[0]; var text = part[1]; if (code) { code = expandShortcuts(code); if (isComment(code)) { output += ''; } else if (isScript(code)) { output += translateScript(code); } else { output += "str += _CfS(" + code + ");"; } } if (text) { text = escapeText(text); output += ("str += '" + text + "';"); } } return getPrefix() + output + 'return str;'; }; var forceStr = function (str) { str = exists(str) ? str : ''; if (options.escapeByDefault && str) { return escapeHTML(str); } return str; }; // Readymade macros addMacro('if', { find: /^if /, replace: function (str) { return str.replace(/^if (.*)/, 'if ($1) {'); }, blockEnd: '}', shortcut: { find: /^\?\s?/, replace: function (str) { return str.replace(/^\?\s?(.*)/, '- if $1'); } } }); addMacro('unless', { find: /^unless /, replace: function (str) { return str.replace(/^unless (.*)/, 'if (!$1) {'); }, blockEnd: '}', shortcut: { find: /^\^\s?/, replace: function (str) { return str.replace(/^\^\s?(.*)/, '- unless $1'); } } }); addMacro('elseif', { find: /^else if /, replace: function (str) { return str.replace(/^else if (.*)/, '} else if ($1) {'); } }); addMacro('else', { find: /^else$/, replace: '} else {' }); addMacro('end', { find: /^end$/, replace: function () { return popBlockState(); }, shortcut: { find: /^\/$/, replace: '- end ' } }); addMacro('for', { find: /^for /, prefix: 'var _ds = new Array(5), _i, _len;', replace: function (str) { var id = getBlockState().length; var js = '_ds.push(d); var _d' + id + ' = $1; for (var _i' + id + ' = 0, _len' + id + ' = ($1 || []).length; _i' + id + ' < _len' + id + '; _i' + id + '++) { _i = _i' + id + '; _len = _len' + id + ';'; return str.replace(/^for (.*) as (.*)/, js + 'var $2 = _d' + id + '[_i' + id + '];') .replace(/^for (.*)/, js + 'd = _d' + id + '[_i' + id + '];'); }, blockEnd: function () { var id = getBlockState().length - 1; var resetIndex = ''; var lastState = getBlockState()[id] + ''; var lastStateIsForBlock = lastState.indexOf('_ds.pop()') !== -1; if (id >= 0 && lastStateIsForBlock) { resetIndex = '_i = _i' + id + '; _len = _len' + id + ';'; } return '} d = _ds.pop();' + resetIndex; } }); addMacro('each', { find: /^each /, prefix: 'var _Ce = Caveman.each;', replace: function (str) { return str.replace(/^each (.*) as (.*)/, '_Ce($1, function ($2, _key) {') .replace(/^each (.*)/, '_Ce($1, function (d, _key) {'); }, blockEnd: '});' }); addMacro('with', { find: /^with /, prefix: 'var _Cw = Caveman.each;', replace: function (str) { return str.replace(/^with (.*) as (.*)/, '_Cw([$1], function ($2, _key) {') .replace(/^with (.*)/, '_Cw([$1], function (d, _key) {'); }, blockEnd: '});' }); addMacro('render', { find: /^render /, prefix: 'var _Cr = Caveman.render;', replace: function (str) { return str.replace(/^render ([^ ]*) (.*)/, 'str += _Cr(\'$1\', $2);') .replace(/^render (.*)/, 'str += _Cr(\'$1\', d);'); } }); addMacro('print', { find: /^print /, replace: function (str) { return str.replace(/^print (.*)/, 'str += ($1);'); } }); addMacro('log', { find: /^log /, replace: function (str) { return str.replace(/^log (.*)/, 'console.log($1);'); } }); addMacro('escape', { find: /^escape /, prefix: 'var _CeH = Caveman.escapeHTML;', replace: function (str) { return str.replace(/^escape (.*)/, 'str += _CeH($1);'); } }); addMacro('unescape', { find: /^unescape /, replace: function (str) { return str.replace(/^unescape (.*)/, 'str += ($1);'); } }); addMacro('first', { find: /^first$/, replace: 'if (_i === 0) {', blockEnd: '}' }); addMacro('last', { find: /^last$/, replace: 'if (_i === _len - 1) {', blockEnd: '}' }); // Init var register = function (partialName, template) { if (typeof template === 'string') { template = Caveman(template); } partials[partialName] = template; return partials[partialName]; }; var render = function (partialName, data) { if (partials[partialName]) { return partials[partialName](Caveman, data); } else { throw Error('Partial "' + partialName + '" not found.'); } }; var Caveman = function (template, data) { var compiled = compile(template, options); var fn = new Function('Caveman', 'd', compiled); var renderFn = function (Caveman, data) { return fn(Caveman, data); }; renderFn.compiled = compiled; return data ? renderFn(Caveman, data) : renderFn; }; Caveman.options = options; Caveman.each = each; Caveman.escapeHTML = escapeHTML; Caveman.compile = compile; Caveman.forceStr = forceStr; Caveman.partials = partials; Caveman.register = register; Caveman.render = render; Caveman.macros = macros; Caveman.addMacro = addMacro; Caveman.popBlockState = popBlockState; Caveman.pushBlockState = pushBlockState; // Expose if (typeof define === 'function' && define.amd) { define(function() { return Caveman; }); } else if (typeof module === 'object' && typeof exports === 'object') { module.exports = Caveman; } else if (typeof window !== 'undefined') { window.Caveman = Caveman; } }());