UNPKG

velocity-template-engine

Version:
638 lines (617 loc) 24.4 kB
var executorFactory = function () { var boolOfValue = function (v) { return !!v || v === 0 || v === ''; }; var setValueByRef = function (scope, keys, v) { var isSet = arguments.length > 2; var main = keys[0]; var len = keys.length; var error = false; var data = scope, ref = main, val; if (!(ref in data)) { data = scope; ref = '$' + main; } val = data[ref]; if (len > 1) { var key, valType; for (var i = 1; i < len; i++) { key = keys[i]; valType = (val === undefined) ? '[object Undefined]' : Object.prototype.toString.call(val); if (key.type === 3 && (ref === 'get' || ref === 'keySet' || ref === 'substring' || ref === 'length' || ref === 'size')) { var dataType = (data === undefined) ? '[object Undefined]' : Object.prototype.toString.call(data); var exec = true; var wrongType = false; switch (ref) { case 'get': if (dataType !== '[object Object]' || 1 !== key.value.length) { wrongType = true; } else { ref = key.value[0]; val = data[ref]; exec = false; } break; case 'keySet': if (dataType !== '[object Object]' || 0 !== key.value.length) { wrongType = true; } else { var props = []; for (var prop in data) { props.push(prop); } return { code: 1, value: props }; } break; case 'substring': if (dataType !== '[object String]') { wrongType = true; } break; case 'length': if (dataType !== '[object String]') { wrongType = true; } else { ref = 'length'; val = data[ref]; exec = false; } break; case 'size': if (dataType !== '[object Array]') { wrongType = true; } else { ref = 'length'; val = data[ref]; exec = false; } break; default: return { code: 0, msg: 'Undefined method \'' + ref + '\'.', value: '' }; } if (wrongType) { return { code: 0, msg: 'Something wrong with method \'' + ref + '\'.', value: '' }; } if (exec) { var _this = data; data = val; ref = null; val = val.apply(_this, key.value); } continue; } if (key.type === 1 && valType === '[object String]') { data = val; ref = key.value; val = data[ref]; continue; } if ((key.type === 1 || key.type === 2) && (valType === '[object Object]' || valType === '[object Array]')) { data = val; ref = key.value; val = data[ref]; continue; } else { error = true; break; } } if (error) { return { code: 0, msg: 'error', value: '' }; } } if (isSet) { if (ref === null) { return { code: 0, msg: 'cannot set a result of a function!', value: '' }; } data[ref] = v; val = v; } return { code: 1, value: val }; }; var macros = {}; var executor = { console: false, _nodeType: '_', scope: null, run: function (e) { if (typeof e[executor._nodeType] !== 'string') return e; if (typeof this[e[executor._nodeType]] !== 'function') return ''; return this[e[executor._nodeType]].apply(e); }, concat: function (s) { var re = ''; for (var i = 0, len = s.length; i < len; i++) { re += executor.run(s[i]).toString(); } return re; }, '0': function () { // Statements return executor.concat(this.s); }, '1': function () { // IfStatement // console.log(this); if (boolOfValue(executor.run(this.e))) { return executor.concat(this.s); } if (this.elifs) { for (var i = 0, len = this.elifs.length; i < len; i++) { if (boolOfValue(executor.run(this.elifs[i].e))) { return executor.concat(this.elifs[i].s); } } } return this.el ? executor.concat(this.el) : ''; }, '2': function () { // ItemInArray var list = executor.run(this.data); if (Object.prototype.toString.call(list) === '[object Object]') { var keys = []; var toBreak = false; for (var k in list) { if (!list.hasOwnProperty(k)) continue; var i = parseInt(k); if (isNaN(i) || String(i) !== k) { toBreak = true; break; } keys.push(i); } if (!toBreak) { keys.sort(function (a, b) { return a - b; }); if (keys.length - 1 === keys[keys.length - 1]) { var listData = []; for (var i = 0, len = keys.length; i < len; i++) { listData.push(list[i]); } list = listData; } } } return { error: Object.prototype.toString.call(list) !== '[object Array]', key: this.id, dataset: list }; }, '3': function () { // ForeachStatement var iia = executor.run(this.it); if (iia.error) { if (executor.console) console.error('cannot iterate ', iia.dataset); return ''; } var scope = function () {}; var originalScope = executor.scope; scope.prototype = originalScope; var obj = new scope(); executor.scope = obj; executor.scope['$foreach'] = { count: -1 }; executor.scope['$velocityCount'] = -1; var re = '', data = iia.dataset; for (var i = 0, len = data.length; i < len; i++) { executor.scope['$foreach'].count = i + 1; executor.scope['$velocityCount'] = i + 1; executor.scope['$' + iia.key] = data[i]; re += executor.concat(this.s); } executor.scope = originalScope; scope = null; obj = null; return re; }, '4': function (keys) { // ReferenceInner var re = [keys[0]]; if (keys.length) { var t, v; for (var i = 1, len = keys.length; i < len; i++) { t = keys[i]; if (t.type === 2) { v = executor.run(t.value); } else if (t.type === 3) { v = []; for (var j = 0, llen = t.value.length; j < llen; j++) v.push(executor.run(t.value[j])); } else { v = t.value; } re.push({ type: t.type, value: v }); } } return re; }, '5': function () { // SetStatement setValueByRef(executor.scope, executor['4'](this.body), executor.run(this.v)); return ''; }, '23': function () { // DefineStatement var s = this.s; setValueByRef(executor.scope, executor['4'](this.body), function () { return executor.concat(s); }); return ''; }, '24': function () { // MacroStatement var s = this.s; var params = this.params; macros[this.name] = function () { var args = [].slice.call(arguments, 0); var scope = function () {}; var originalScope = executor.scope; scope.prototype = originalScope; var obj = new scope(); for (var i = 0, len = args.length; i < len; i++) { obj[params[i]] = args[i]; } executor.scope = obj; var re = executor.concat(s); executor.scope = originalScope; scope = null; obj = null; return re; }; return ''; }, '25': function () { // MacroExecStatement if (!macros[this.name]) return this.text; var args = []; for (var i = 0, len = this.args.length; i < len; i++) { args.push(executor.run(this.args[i])); } return macros[this.name].apply(null, args); }, '6': function () { // Reference var r = setValueByRef(executor.scope, executor['4'](this.body)); r.bang = this.bang; return r; }, '7': function () { // ReferenceRender var r = executor['6'].apply(this); if (r.code === 0) { if (executor.console) console.error(r.msg); return r.bang ? '' : this.text; } var stringify = function (v) { var vtype = (v === undefined) ? '[object Undefined]' : Object.prototype.toString.call(v); if (vtype === '[object Array]') { var vs = []; for (var i = 0, len = v.length; i < len; i++) { vs.push(stringify(v[i])); } return '[' + vs.join(', ') + ']'; } else if (vtype === '[object Object]') { var vs = []; for (var i in v) { vs.push(i + '=' + stringify(v[i])); } return '{' + vs.join(', ') + '}'; } else if (vtype === '[object Function]') { return stringify(v()); } else { return v.toString(); } }; return (r.bang && r.value == null) ? '' : (r.value == null ? this.text : stringify(r.value)); }, '8': function () { // ReferenceValue var r = executor['6'].apply(this); if (r.code === 0) { if (executor.console) console.error(r.msg); return null; } return r.value; }, '10': function () { // ObjLiteral var re = {}; if (this.tail) { for (var i = 0, len = this.tail.length; i < len; i++) { re[executor.run(this.tail[i].key)] = executor.run(this.tail[i].val); } } return re; }, '11': function () { // ArrRange var start = executor.run(this.startExp), end = executor.run(this.endExp); if (typeof start !== 'number' || typeof end !== 'number') { if (executor.console) console.error('wrong start or end of the array index'); return null; } var left = parseInt(start, 10); var right = parseInt(end, 10); if (isNaN(left) || isNaN(right) || left != start || right != end) { if (executor.console) console.error('wrong start or end of the array index'); return null; } var re = []; if (left <= right) { for (var i = left; i <= right; i++) { re.push(i); } } else { for (var i = left; i >= right; i--) { re.push(i); } } return re; }, '12': function () { // ArrLiteral var re = []; if (this.tail) { for (var i = 0, len = this.tail.length; i < len; i++) { re.push(executor.run(this.tail[i])); } } return re; }, '13': function () { // -ReferenceValue var r = executor['8'].apply(this); if (typeof r !== 'number') { if (executor.console) console.error('[velocity.js] type error: operator \'-\' is for Number.'); return null; } return -r; }, '14': function () { // !PrimaryExpression return !(boolOfValue(executor.run(this.p))); }, '15': function () { // [!-]? (Expression) var e = executor.run(this.exp); if (this.s === '-' && typeof e !== 'number') { if (executor.console) console.error('[velocity.js] type error: -' + e); } return this.s ? (this.s === '!' ? !(boolOfValue(e)) : -e) : e; }, '16': function () { // ConditionalOrExpression var re = executor.run(this.head); for (var i = 0, len = this.tail.length; i < len; i++) { if (boolOfValue(re)) return true; re = executor.run(this.tail[i][3]); } return this.tail.length ? boolOfValue(re) : re; }, '17': function () { // ConditionalAndExpression var re = executor.run(this.head); for (var i = 0, len = this.tail.length; i < len; i++) { if (!boolOfValue(re)) return false; re = executor.run(this.tail[i][3]); } return this.tail.length ? boolOfValue(re) : re; }, '18': function () { // EqualityExpression var re = executor.run(this.head); for (var i = 0, len = this.tail.length; i < len; i++) { var s = this.tail[i]; if (s[1] === '==') re = (re == executor.run(s[3])); else if (s[1] === '!=') re = (re != executor.run(s[3])); } return re; }, '19': function () { // RelationalExpression var v1 = executor.run(this.head); if (!this.tail) return v1; var op = this.tail[1], e = this.tail[3]; var v2 = executor.run(e); if (op === '<') return v1 < v2; if (op === '>') return v1 > v2; if (op === '<=') return v1 <= v2; if (op === '>=') return v1 >= v2; return false; }, '20': function () { // AdditiveExpression var re = executor.run(this.head); var t; for (var i = 0, len = this.tail.length; i < len; i++) { t = this.tail[i]; if (t[1] === '+') re += executor.run(t[3]); else if(t[1] === '-') re -= executor.run(t[3]); } return re; }, '21': function () { // MultiplicativeExpression var re = executor.run(this.head); var t; for (var i = 0, len = this.tail.length; i < len; i++) { t = this.tail[i]; if (t[1] === '*') re *= executor.run(t[3]); else if(t[1] === '/') re /= executor.run(t[3]); else if(t[1] === '%') re %= executor.run(t[3]); } return re; }, '22': function () { // StringLiteral return executor.concat(this.parts); } }; return executor; }; var _vlct = this['velocity']; var detectNodejs = (function () { return Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]'; })(); var CacheMan = function () { var data = {}; this.set = function (key, val) { data[key] = val; return this; }; this.get = function (key) { return data[key]; }; this.reset = function () { data = {}; return this; }; }; var expo = { context: function (data) { var context = executorFactory(); var patch = function (s, data) { for (var i in data) { if (!data.hasOwnProperty(i)) continue; if (Object.prototype.toString.call(s[i]) !== '[object Object]' || Object.prototype.toString.call(data[i]) !== '[object Object]') { s[i] = data[i]; continue; } patch(s[i], data[i]); } }; var history = ''; this.execute = function (tmpl, data, dataLevel) { // dataLevel: 0: set and not patch; 1: set and patch; 2: reset if (!dataLevel) this.set(data); else if (dataLevel === 1) this.set(data, true); else this.reset(data); var output = context.run(_vlct.parse(tmpl)); history += output; return { data: context.scope, output: output, history: history }; }; this.render = function (tmpl, data) { return this.execute(tmpl, data).output; }; this.set = function (data, isPatching) { data = data || {}; var s = context.scope; for (var i in data) { if (!data.hasOwnProperty(i)) continue; if (!isPatching || (Object.prototype.toString.call(s[i]) !== '[object Object]' || Object.prototype.toString.call(data[i]) !== '[object Object]')) { s[i] = data[i]; continue; } patch(s[i], data[i]); } }; this.reset = function (data) { context.scope = data || {}; }; this.empty = function () { history = ''; }; this.reset(data); }, render: (function () { var tmplCaches = new CacheMan(); var resultCaches = new CacheMan(); var tmplDataKey = function (tmplId, dataId) { return JSON.stringify({ tmpl: tmplId, data: dataId }); }; return function (tmpl, data, options) { options = options || {}; var noCache = !!options.noCache; if (noCache) { return this.compile(tmpl, options)(data); } var dataId = typeof options.dataId; if (dataId === 'string' || dataId === 'function') { if (dataId === 'string') { dataId = options.dataId; } else { dataId = options.dataId.call(data, data); } } else { dataId = null; } var tmplId = typeof options.tmplId; if (tmplId === 'string' || tmplId === 'function') { if (tmplId === 'string') { tmplId = options.tmplId; } else { tmplId = options.tmplId(tmpl); } if (dataId != null) { var tdk = tmplDataKey(tmplId, dataId); if (resultCaches.get(tdk)) return resultCaches.get(tdk); } if (!tmpl) { if (tmplCaches.get(tmplId)) { tmpl = tmplCaches.get(tmplId); } else { if (detectNodejs) { var fs = require('fs'); tmpl = fs.readFileSync(tmplId, 'utf8'); } else { var el = window.document.getElementById(tmplId); tmpl = el.value || el.innerHTML; } tmpl = this.compile(tmpl, options); tmplCaches.set(tmplId, tmpl); } } else { tmpl = this.compile(tmpl, options); tmplCaches.set(tmplId, tmpl); } } else { tmplId = '[TMPL_STR]' + tmpl; if (dataId != null) { var tdk = tmplDataKey(tmplId, dataId); if (resultCaches.get(tdk)) return resultCaches.get(tdk); } if (tmplCaches.get(tmplId)) { tmpl = tmplCaches.get(tmplId); } else { tmpl = this.compile(tmpl, options); tmplCaches.set(tmplId, tmpl); } } var re = tmpl(data); (tmplId && dataId) && resultCaches.set(tmplDataKey(tmplId, dataId), re); return re; }; })(), compile: function (tmpl, options) { var root = _vlct.parse(tmpl); if (options && (options === true || options.raw)) { // var findNodeType = function (nodeStr, type) { // return (nodeStr.indexOf('"$":"' + type + '"') > 0); // }; return 'var root = ' + JSON.stringify(root) + '; ' + 'var render = (' + executorFactory.toString() + ')(); ' + 'render.scope = arguments[0] || {}; ' + 'return render.run(root);'; } else { var render = executorFactory(); return function (data) { render.scope = data || {}; return render.run(root); }; } } }; return expo;