This commit is contained in:
harttle
2017-04-24 21:31:30 +08:00
parent 82f84d96c1
commit 2632e3e97e
50 changed files with 3941 additions and 3903 deletions
+165 -169
View File
@@ -1,182 +1,178 @@
const _ = require('./util/underscore.js');
const lexical = require('./lexical.js');
const assert = require('./util/assert.js');
const toStr = Object.prototype.toString;
const _ = require('./util/underscore.js')
const lexical = require('./lexical.js')
const assert = require('./util/assert.js')
const toStr = Object.prototype.toString
var Scope = {
getAll: function() {
var ctx = {};
for (var i = this.scopes.length - 1; i >= 0; i--) {
_.assign(ctx, this.scopes[i]);
getAll: function () {
var ctx = {}
for (var i = this.scopes.length - 1; i >= 0; i--) {
_.assign(ctx, this.scopes[i])
}
return ctx
},
get: function (str) {
for (var i = this.scopes.length - 1; i >= 0; i--) {
try {
return this.getPropertyByPath(this.scopes[i], str)
} catch (e) {
if (/undefined variable/.test(e.message)) {
continue
}
return ctx;
},
get: function(str) {
for (var i = this.scopes.length - 1; i >= 0; i--) {
try {
return this.getPropertyByPath(this.scopes[i], str);
} catch (e) {
if (/undefined variable/.test(e.message)) {
continue;
}
if (/Cannot read property/.test(e.message)) {
if (this.opts.strict_variables) {
e.message += ': ' + str;
throw e;
} else {
continue;
}
} else {
e.message += ': ' + str;
throw e;
}
}
if (/Cannot read property/.test(e.message)) {
if (this.opts.strict_variables) {
e.message += ': ' + str
throw e
} else {
continue
}
} else {
e.message += ': ' + str
throw e
}
if (this.opts.strict_variables) {
throw new TypeError('undefined variable: ' + str);
}
}
if (this.opts.strict_variables) {
throw new TypeError('undefined variable: ' + str)
}
},
set: function (k, v) {
this.setPropertyByPath(this.scopes[this.scopes.length - 1], k, v)
return this
},
push: function (ctx) {
assert(ctx, `trying to push ${ctx} into scopes`)
return this.scopes.push(ctx)
},
pop: function () {
return this.scopes.pop()
},
unshift: function (ctx) {
assert(ctx, `trying to push ${ctx} into scopes`)
return this.scopes.unshift(ctx)
},
shift: function () {
return this.scopes.shift()
},
setPropertyByPath: function (obj, path, val) {
if (_.isString(path)) {
var paths = path.replace(/\[/g, '.').replace(/\]/g, '').split('.')
for (var i = 0; i < paths.length; i++) {
var key = paths[i]
if (i === paths.length - 1) {
return (obj[key] = val)
}
},
set: function(k, v) {
this.setPropertyByPath(this.scopes[this.scopes.length - 1], k, v);
return this;
},
push: function(ctx) {
assert(ctx, `trying to push ${ctx} into scopes`);
return this.scopes.push(ctx);
},
pop: function() {
return this.scopes.pop();
},
unshift: function(ctx) {
assert(ctx, `trying to push ${ctx} into scopes`);
return this.scopes.unshift(ctx);
},
shift: function() {
return this.scopes.shift();
},
setPropertyByPath: function(obj, path, val) {
if (_.isString(path)) {
var paths = path.replace(/\[/g, '.').replace(/\]/g, '').split('.');
for (var i = 0; i < paths.length; i++) {
var key = paths[i];
if (i === paths.length - 1) {
return obj[key] = val;
}
if (undefined === obj[key]) obj[key] = {};
if (undefined === obj[key]) obj[key] = {}
// case for readonly objects
obj = obj[key] || {};
}
}
},
getPropertyByPath: function(obj, path) {
var paths = this.propertyAccessSeq(path + '');
var varName = paths.shift();
if (!obj.hasOwnProperty(varName)) {
throw new TypeError('undefined variable');
}
var variable = obj[varName];
var lastName = paths.pop();
paths.forEach(p => variable = variable[p]);
if (undefined !== lastName) {
if (lastName === 'size' &&
(toStr.call(variable) === '[object Array]'
|| toStr.call(variable) === '[object String]')) {
return variable.length;
}
variable = variable[lastName]
}
return variable;
},
/*
* Parse property access sequence from access string
* @example
* accessSeq("foo.bar") // ['foo', 'bar']
* accessSeq("foo['bar']") // ['foo', 'bar']
* accessSeq("foo['b]r']") // ['foo', 'b]r']
* accessSeq("foo[bar.coo]") // ['foo', 'bar'], for bar.coo == 'bar'
*/
propertyAccessSeq: function(str) {
var seq = [],
name = '';
for (var i = 0; i < str.length; i++) {
if (str[i] === '[') {
seq.push(name);
name = '';
var delemiter = str[i + 1];
// foo[bar.coo]
if (delemiter !== "'" && delemiter !== '"') {
var j = matchRightBracket(str, i + 1);
assert(j !== -1, `unbalanced []: ${str}`);
name = str.slice(i + 1, j);
// foo[1]
if (lexical.isInteger(name)) {
seq.push(name);
}
// foo["bar"]
else {
seq.push(this.get(name));
}
name = '';
i = j;
}
// foo["bar"]
else {
j = str.indexOf(delemiter, i + 2);
assert(j !== -1, `unbalanced ${delemiter}: ${str}`);
name = str.slice(i + 2, j);
seq.push(name);
name = '';
i = j + 2;
}
}
// foo.bar
else if (str[i] === ".") {
seq.push(name);
name = '';
}
//foo.bar
else {
name += str[i];
}
}
if (name.length) seq.push(name);
return seq;
obj = obj[key] || {}
}
}
};
},
function matchRightBracket(str, begin) {
var stack = 1; // count of '[' - count of ']'
for (var i = begin; i < str.length; i++) {
if (str[i] === '[') {
stack++;
}
if (str[i] === ']') {
stack--;
if (stack === 0) {
return i;
}
}
getPropertyByPath: function (obj, path) {
var paths = this.propertyAccessSeq(path + '')
var varName = paths.shift()
if (!obj.hasOwnProperty(varName)) {
throw new TypeError('undefined variable')
}
return -1;
var variable = obj[varName]
var lastName = paths.pop()
paths.forEach(p => (variable = variable[p]))
if (undefined !== lastName) {
if (lastName === 'size' &&
(toStr.call(variable) === '[object Array]' ||
toStr.call(variable) === '[object String]')) {
return variable.length
}
variable = variable[lastName]
}
return variable
},
/*
* Parse property access sequence from access string
* @example
* accessSeq("foo.bar") // ['foo', 'bar']
* accessSeq("foo['bar']") // ['foo', 'bar']
* accessSeq("foo['b]r']") // ['foo', 'b]r']
* accessSeq("foo[bar.coo]") // ['foo', 'bar'], for bar.coo == 'bar'
*/
propertyAccessSeq: function (str) {
var seq = []
var name = ''
for (var i = 0; i < str.length; i++) {
if (str[i] === '[') {
seq.push(name)
name = ''
var delemiter = str[i + 1]
if (delemiter !== "'" && delemiter !== '"') {
// foo[bar.coo]
var j = matchRightBracket(str, i + 1)
assert(j !== -1, `unbalanced []: ${str}`)
name = str.slice(i + 1, j)
if (lexical.isInteger(name)) {
// foo[1]
seq.push(name)
} else {
// foo["bar"]
seq.push(this.get(name))
}
name = ''
i = j
} else {
// foo["bar"]
j = str.indexOf(delemiter, i + 2)
assert(j !== -1, `unbalanced ${delemiter}: ${str}`)
name = str.slice(i + 2, j)
seq.push(name)
name = ''
i = j + 2
}
} else if (str[i] === '.') {
// foo.bar
seq.push(name)
name = ''
} else {
// foo.bar
name += str[i]
}
}
if (name.length) seq.push(name)
return seq
}
}
exports.factory = function(ctx, opts) {
opts = _.assign({
strict_variables: false,
strict_filters: false,
blocks: {},
root: []
}, opts);
function matchRightBracket (str, begin) {
var stack = 1 // count of '[' - count of ']'
for (var i = begin; i < str.length; i++) {
if (str[i] === '[') {
stack++
}
if (str[i] === ']') {
stack--
if (stack === 0) {
return i
}
}
}
return -1
}
ctx = _.assign(ctx, {
liquid: opts
});
exports.factory = function (ctx, opts) {
opts = _.assign({
strict_variables: false,
strict_filters: false,
blocks: {},
root: []
}, opts)
var scope = Object.create(Scope);
scope.opts = opts;
scope.scopes = [ctx];
return scope;
};
ctx = _.assign(ctx, {
liquid: opts
})
var scope = Object.create(Scope)
scope.opts = opts
scope.scopes = [ctx]
return scope
}