UNPKG

x-http-client

Version:

An http client to make it easier to send requests (including JSONP requests) to the server.

236 lines (208 loc) 6.07 kB
var T_STR = 1; var T_EXP = 2; /** * A simple template function * * @example * // Rreturns '/post/1' * template('/post/{ post.id }', { post: { id: 1 } }) * * @param {string} template The template text. * @param {Object.<string, *>} data The data object. * @param {TemplateOptions} options The template options. * @returns {string} Returns the compiled text. */ function template(template, data, options) { var templ = template + ''; var model = data || {}; var opts = options || {}; var start = opts.start || '{'; var end = opts.end || '}'; var encode = opts.encode || encodeURIComponent; var ast = compile(templ, start, end, function (expr) { var first = expr.charAt(0); var second = expr.charAt(1); var raw = false; if (first === '-' && second === ' ') { raw = true; expr = expr.substr(2); } expr = expr.replace(/^\s+|\s+$/g, ''); return { type: T_EXP, text: expr, raw: raw }; }); var render = buildRenderFunction(ast, encode); try { return render(model); } catch (e) { throw new Error('Compile Error:\n\n' + template + '\n\n' + e.message); } } /** * Build render function. * * @param {Object.<string, *>[]} ast The abstract syntax tree. * @param {(str: string) => string} encode The function to encode the string. * @returns {(model: Object.<string, *>) => string} Returns a function that compile data to string. */ function buildRenderFunction(ast, encode) { var fn; var line; var lines = []; var i = 0; var l = ast.length; lines.push('var __o=[]'); lines.push('with(__s){'); for ( ; i < l; ++i) { line = ast[i]; if (line.type === T_STR) { lines.push('__o.push(' + JSON.stringify(line.text) + ')'); } else if (line.type === T_EXP && line.text) { if (line.raw) { lines.push('__o.push(' + line.text + ')'); } else { lines.push('__o.push(__e(' + line.text + '))'); } } } lines.push('}'); lines.push('return __o.join("")'); fn = new Function('__s', '__e', lines.join('\n')); return function (model) { return fn(model, function (val) { return (val === null || val === undefined) ? '' : encode(val + ''); }); }; } /** * Compile the template. * * @param {string} template The template to compile. * @param {string} startTag The start tag. * @param {string} endTag The end tag. * @param {(expr: string) => string} parseExpr The function to parse the expression. * @returns {string} Return the compiled string. */ function compile(template, startTag, endTag, parseExpr) { var i = 0; var l = template.length; var sl = startTag.length; var el = endTag.length; var ast = []; var strbuffer = []; var exprbuffer = []; var type = T_STR; /** * Get the char in `template` at the given position. * * @param {numner} index The index to read. * @returns {string} Returns the char. */ var charAt = function (index) { return template.charAt(index); }; /** * Escape the tag. * * @param {string} tag The tag to escape. * @param {string[]} buffer The buffer to put the char. */ var esc = function (tag, buffer) { var c; var m = tag.length; var s = '\\'; /*eslint no-constant-condition: ["error", { "checkLoops": false }]*/ while (1) { c = charAt(i); if (c === s) { c = charAt(++i); if (c === s) { buffer.push(s); ++i; } else if (isWord(tag)) { buffer.push(tag); i += m; } else { buffer.push(s); break; } } else { break; } } }; /** * Check whether the next input is the word. * * @param {string} word The word to check. * @returns {number} Returns `1` on yes, otherwise `0` is returned. */ var isWord = function (word) { var k = 0; var j = i; var m = word.length; while (k < m && j < l) { if (word.charAt(k) !== charAt(j)) return 0; ++k; ++j; } return 1; }; /** * Flush the str to the ast and reset the str buffer. */ var flushStr = function () { if (strbuffer.length) { ast.push({ type: T_STR, text: strbuffer.join('') }); strbuffer = []; } }; /** * Flush the expr to the ast and reset the expr buffer. */ var flushExpr = function () { flushStr(); ast.push(parseExpr(exprbuffer.join(''))); exprbuffer = []; }; while (i < l) { if (type === T_STR) { esc(startTag, strbuffer); if (isWord(startTag)) { type = T_EXP; i += sl; } else { strbuffer.push(charAt(i)); ++i; } } else if (type === T_EXP) { esc(endTag, exprbuffer); if (isWord(endTag)) { type = T_STR; i += el; flushExpr(); } else { exprbuffer.push(charAt(i)); ++i; } } } if (type === T_EXP) { throw new Error('Unexpected end'); } flushStr(); return ast; } /** * @typedef {Object.<string, *>} TemplateOptions * @property {string} [start] The start tag of the template, default is `{`. * @property {string} [end] The end tag of the template, default is `}`. * @property {(value: string) => string} [encode] The function to encode the string, default is `encodeURIComponent`. */ module.exports = template;