forked from orfon/reinhardt
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfilterexpression.js
142 lines (135 loc) · 5.14 KB
/
filterexpression.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
var {Variable} = require('./variable');
var {isSafe, markSafe, isMarkedForEscaping, markForEscaping} = require('./utils');
var {TemplateSyntaxError} = require('./errors');
var constants = require("./constants");
/*
Parses a variable token and its optional filters (all as a single string),
and return a list of tuples of the filter name and arguments.
Sample::
>>> token = 'variable|default:"Default value"|date:"Y-m-d"'
>>> p = Parser('')
>>> fe = FilterExpression(token, p)
>>> len(fe.filters)
2
>>> fe.var
<Variable: 'variable'>
*/
var FilterExpression = exports.FilterExpression = function(token, parser) {
/**
regex groups:
2 "variable"
3 variable
9 'variable'
4 filterName
6 "value"
7 variable-value
8 'value' || value
*/
if (parser && parser.environment != undefined) {
this.stringIfInvalid = parser.environment.TEMPLATE_STRING_IF_INVALID;
} else {
this.stringIfInvalid = constants.TEMPLATE_STRING_IF_INVALID;
}
var filterRe = /\s*(?:^_\("([^\\"]*(?:\\.[^\\"])*)"\)|^"([^\\"]*(?:\\.[^\\"]*)*)"|^([a-zA-Z0-9_.]+)|\|\s*(\w+)(?::(?:_\("([^\\"]*(?:\\.[^\\"])*)"\)|"([^\\"]*(?:\\.[^\\"]*)*)"|([a-zA-Z0-9_.]+)|'([^\\']*(?:\\.[^\\']*)*)'))?|^'([^\\']*(?:\\.[^\\']*)*)')/g;
this.token = token;
var varObj = null;
var filters = [];
var upTo = 0;
var match;
filterRe.lastIndex = 0;
while ((match = filterRe.exec(token)) !== null) {
if (match.index != upTo) {
throw new TemplateSyntaxError('Could not parse some characters after "' +
token.substring(0, match.index) + '", namely: "' +
token.substring(upTo, match.index) + '" in "' + token + '"', upTo, match.index);
}
if (varObj === null) {
var constant = match[2] !== undefined ? match[2] : match[9];
var variable = match[3];
if (constant !== undefined) {
constant = '"' + constant + '"';
varObj = (new Variable(constant)).resolve({});
} else {
varObj = new Variable(variable);
}
} else {
var filterName = match[4];
var args = [];
var constantArg = match[6] !== undefined ? match[6] : match[8];
var variableArg = match[7];
if (constantArg !== undefined) {
constantArg = '"' + constantArg + '"';
args.push({
isVariable: false,
value: (new Variable(constantArg)).resolve({}),
});
} else if (variableArg) {
args.push({
isVariable: true,
value: new Variable(variableArg)
})
}
var filterFunc = parser.filters[filterName];
if (!filterFunc) {
throw new TemplateSyntaxError('Unknown filter "' + filterName + '"');
}
filterFunc._filterName = filterName;
// FIXME args_check whether enough arguments for filter
filters.push([filterFunc, args]);
}
upTo = upTo + match[0].length;
}
if (upTo != token.length) {
throw new TemplateSyntaxError('Could not parse the remainder: "' + token.substring(upTo) + '"');
}
this.filters = filters;
this.varObj = varObj;
return this;
}
FilterExpression.prototype.resolve = function(context, ignoreFailures) {
ignoreFailures = ignoreFailures === true ? true : false;
var obj = null;
if (this.varObj instanceof Variable) {
obj = this.varObj.resolve(context);
if (obj === undefined && ignoreFailures == false) {
obj = this.stringIfInvalid;
if (this.stringIfInvalid !== '') {
if (this.stringIfInvalid.indexOf('%s') > -1) {
obj = obj.replace('%s', this.varObj.toString());
}
// return immediatly without
// applying the filters
return obj;
}
}
} else {
obj = this.varObj;
}
for (var idx in this.filters) {
var filterDef = this.filters[idx];
var argVals = [];
filterDef[1].forEach(function(arg) {
if (arg.isVariable) {
argVals.push(arg.value.resolve(context));
} else {
argVals.push(markSafe(arg.value));
}
}, this);
if (filterDef[0].needsAutoescape === true) {
argVals.push(context.autoescape);
}
var newObj = filterDef[0].apply(null, [obj].concat(argVals));
//print ('[FilterExpression safeness] filter:', filterDef[0].isSafe, '| obj:', isSafe(obj), '| newObj:', isSafe(newObj));
// If the filter returns a new string but we know that the filter itself isSafe
// and the input string was safe, then repair the damage and mark the output
// as safe
if (filterDef[0].isSafe === true && isSafe(obj) === true) {
obj = markSafe(newObj);
} else if (isMarkedForEscaping(obj) === true) {
obj = markForEscaping(newObj);
} else {
obj = newObj;
}
};
return obj;
}