8000 support filters in attribute bindings · hwclass/vue@7f71239 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7f71239

Browse files
committed
support filters in attribute bindings
1 parent c4550f7 commit 7f71239

File tree

9 files changed

+302
-190
lines changed

9 files changed

+302
-190
lines changed

src/compiler.js

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -465,11 +465,11 @@ CompilerProto.checkPriorityDir = function (dirname, node, root) {
465465
root !== true &&
466466
(Ctor = this.resolveComponent(node, undefined, true))
467467
) {
468-
directive = Directive.parse(dirname, '', this, node)
468+
directive = Directive.build(dirname, '', this, node)
469469
directive.Ctor = Ctor
470470
} else {
471471
expression = utils.attr(node, dirname)
472-
directive = expression && Directive.parse(dirname, expression, this, node)
472+
directive = expression && Directive.build(dirname, expression, this, node)
473473
}
474474
if (directive) {
475475
if (root === true) {
@@ -541,7 +541,7 @@ CompilerProto.compileElement = function (node, root) {
541541
while (l--) {
542542
exp = exps[l]
543543
dirname = attr.name.slice(prefix.length)
544-
directive = Directive.parse(dirname, exp, this, node)
544+
directive = Directive.build(dirname, exp, this, node)
545545

546546
if (dirname === 'with') {
547547
this.bindDirective(directive, this.parent)
@@ -554,7 +554,7 @@ CompilerProto.compileElement = function (node, root) {
554554
// non directive attribute, check interpolation tags
555555
exp = TextParser.parseAttr(attr.value)
556556
if (exp) {
557-
directive = Directive.parse('attr', attr.name + ':' + exp, this, node)
557+
directive = Directive.build('attr', attr.name + ':' + exp, this, node)
558558
if (params && params.indexOf(attr.name) > -1) {
559559
// a param attribute... we should use the parent binding
560560
// to avoid circular updates like size={{size}}
@@ -595,14 +595,14 @@ CompilerProto.compileTextNode = function (node) {
595595
if (token.key) { // a binding
596596
if (token.key.charAt(0) === '>') { // a partial
597597
el = document.createComment('ref')
598-
directive = Directive.parse('partial', token.key.slice(1), this, el)
598+
directive = Directive.build('partial', token.key.slice(1), this, el)
599599
} else {
600600
if (!token.html) { // text binding
601601
el = document.createTextNode('')
602-
directive = Directive.parse('text', token.key, this, el)
602+
directive = Directive.build('text', token.key, this, el)
603603
} else { // html binding
604604
el = document.createComment(config.prefix + '-html')
605-
directive = Directive.parse('html', token.key, this, el)
605+
directive = Directive.build('html', token.key, this, el)
606606
}
607607
}
608608
} else { // a plain string
@@ -827,15 +827,19 @@ CompilerProto.markComputed = function (binding, value) {
827827
/**
828828
* Retrive an option from the compiler
829829
*/
830-
CompilerProto.getOption = function (type, id) {
830+
CompilerProto.getOption = function (type, id, silent) {
831831
var opts = this.options,
832832
parent = this.parent,
833-
globalAssets = config.globalAssets
834-
return (opts[type] && opts[type][id]) || (
835-
parent
836-
? parent.getOption(type, id)
837-
: globalAssets[type] && globalAssets[type][id]
838-
)
833+
globalAssets = config.globalAssets,
834+
res = (opts[type] && opts[type][id]) || (
835+
parent
836+
? parent.getOption(type, id, silent)
837+
: globalAssets[type] && globalAssets[type][id]
838+
)
839+
if (!res && !silent) {
840+
utils.warn('Unknown ' + type.slice(0, -1) + ': ' + id)
841+
}
842+
return res
839843
}
840844

841845
/**
@@ -882,7 +886,7 @@ CompilerProto.resolveComponent = function (node, data, test) {
882886
tagName = node.tagName,
883887
id = this.eval(exp, data),
884888
tagId = (tagName.indexOf('-') > 0 && tagName.toLowerCase()),
885-
Ctor = this.getOption('components', id || tagId)
889+
Ctor = this.getOption('components', id || tagId, true)
886890

887891
if (id && !Ctor) {
888892
utils.warn('Unknown component: ' + id)

src/directive.js

Lines changed: 110 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ var utils = require('./utils'),
1515
FILTERS_RE = /\|[^\|]+/g,
1616
FILTER_TOKEN_RE = /[^\s']+|'[^']+'|[^\s"]+|"[^"]+"/g,
1717
NESTING_RE = /^\$(parent|root)\./,
18-
SINGLE_VAR_RE = /^[\w\.$]+$/
18+
SINGLE_VAR_RE = /^[\w\.$]+$/,
19+
QUOTE_RE = /"/g
1920

2021
/**
2122
* Directive class
@@ -57,28 +58,35 @@ function Directive (dirname, definition, expression, rawKey, compiler, node) {
5758
: expression
5859
).trim()
5960

60-
parseKey(this, rawKey)
61+
var parsed = Directive.parseArg(rawKey)
62+
this.key = parsed.key
63+
this.arg = parsed.arg
6164

62-
var filterExps = this.expression.slice(rawKey.length).match(FILTERS_RE)
63-
if (filterExps) {
65+
var filters = Directive.parseFilters(this.expression.slice(rawKey.length)),
66+
filter, fn, i, l
67+
if (filters) {
6468
this.filters = []
65-
for (var i = 0, l = filterExps.length, filter; i < l; i++) {
66-
filter = parseFilter(filterExps[i], this.compiler)
67-
if (filter) {
69+
for (i = 0, l = filters.length; i < l; i++) {
70+
filter = filters[i]
71+
fn = this.compiler.getOption('filters', filter.name)
72+
if (fn) {
73+
filter.apply = fn
6874
this.filters.push(filter)
69-
if (filter.apply.computed) {
70-
// some special filters, e.g. filterBy & orderBy,
71-
// can involve VM properties and they often need to
72-
// be computed.
75+
if (fn.computed) {
7376
this.computeFilters = true
7477
}
7578
}
7679
}
77-
if (!this.filters.length) this.filters = null
78-
} else {
80+
}
81+
82+
if (!this.filters || !this.filters.length) {
7983
this.filters = null
8084
}
8185

86+
if (this.computeFilters) {
87+
this.key = Directive.inlineFilters(this.key, this.filters)
88+
}
89+
8290
this.isExp =
8391
this.computeFilters ||
8492
!SINGLE_VAR_RE.test(this.key) ||
@@ -88,47 +96,6 @@ function Directive (dirname, definition, expression, rawKey, compiler, node) {
8896

8997
var DirProto = Directive.prototype
9098

91-
/**
92-
* parse a key, extract argument and nesting/root info
93-
*/
94-
function parseKey (dir, rawKey) {
95-
var key = rawKey
96-
if (rawKey.indexOf(':') > -1) {
97-
var argMatch = rawKey.match(ARG_RE)
98-
key = argMatch
99-
? argMatch[2].trim()
100-
: key
101-
dir.arg = argMatch
102-
? argMatch[1].trim()
103-
: null
104-
}
105-
dir.key = key
106-
}
107-
108-
/**
109-
* parse a filter expression
110-
*/
111-
function parseFilter (filter, compiler) {
112-
113-
var tokens = filter.slice(1).match(FILTER_TOKEN_RE)
114-
if (!tokens) return
115-
116-
var name = tokens[0],
117-
apply = compiler.getOption('filters', name)
118-
if (!apply) {
119-
utils.warn('Unknown filter: ' + name)
120-
return
121-
}
122-
123-
return {
124-
name : name,
125-
apply : apply,
126-
args : tokens.length > 1
127-
? tokens.slice(1)
128-
: null
129-
}
130-
}
131-
13299
/**
133100
* called when a new value is set
134101
* for computed properties, this will only be called once
@@ -170,7 +137,7 @@ DirProto.unbind = function () {
170137
this.vm = this.el = this.binding = this.compiler = null
171138
}
172139

173-
// exposed methods ------------------------------------------------------------
140+
// Exposed static methods -----------------------------------------------------
174141

175142
/**
176143
* split a unquoted-comma separated expression into
@@ -183,27 +150,104 @@ Directive.split = function (exp) {
183150
}
184151

185152
/**
186-
* make sure the directive and expression is valid
187-
* before we create an instance
153+
* parse a key, extract argument
188154
*/
189-
Directive.parse = function (dirname, expression, compiler, node) {
155+
Directive.parseArg = function (rawKey) {
156+
var key = rawKey,
157+
arg = null
158+
if (rawKey.indexOf(':') > -1) {
159+
var argMatch = rawKey.match(ARG_RE)
160+
key = argMatch
161+
? argMatch[2].trim()
162+
: key
163+
arg = argMatch
164+
? argMatch[1].trim()
165+
: arg
166+
}
167+
return {
168+
key: key,
169+
arg: arg
170+
}
171+
}
190172

191-
var dir = compiler.getOption('directives', dirname) || directives[dirname]
192-
if (!dir) {
193-
utils.warn('Unknown directive: ' + dirname)
194-
return
173+
/**
174+
* parse a the filters
175+
*/
176+
Directive.parseFilters = function (exp) {
177+
if (!exp.indexOf('|') < 0) return
178+
var filters = exp.match(FILTERS_RE),
179+
res, i, l, tokens
180+
if (filters) {
181+
res = []
182+
for (i = 0, l = filters.length; i < l; i++) {
183+
tokens = filters[i].slice(1).match(FILTER_TOKEN_RE)
184+
if (tokens) {
185+
res.push({
186+
name: tokens[0],
187+
args: tokens.length > 1
188+
? tokens.slice(1)
189+
: null
190+
})
191+
}
192+
}
193+
}
194+
return res
195+
}
196+
197+
/**
198+
* Inline computed filters so they become part
199+
* of the expression
200+
*/
201+
Directive.inlineFilters = function (key, filters) {
202+
var args, filter
203+
for (var i = 0, l = filters.length; i < l; i++) {
204+
filter = filters[i]
205+
args = filter.args
206+
? ',"' + filter.args.map(escapeQuote).join('","') + '"'
207+
: ''
208+
key = 'this.$compiler.getOption("filters", "' +
209+
filter.name +
210+
'").call(this,' +
211+
key + args +
212+
')'
195213
}
214+
return key
215+
}
216+
217+
/**
218+
* Convert double quotes to single quotes
219+
* so they don't mess up the generated function body
220+
*/
221+
function escapeQuote (v) {
222+
return v.indexOf('"') > -1
223+
? v.replace(QUOTE_RE, '\'')
224+
: v
225+
}
196226

197-
var rawKey
227+
/**
228+
* Parse the key from a directive raw expression
229+
*/
230+
Directive.parseKey = function (expression) {
198231
if (expression.indexOf('|') > -1) {
199232
var keyMatch = expression.match(KEY_RE)
200233
if (keyMatch) {
201-
rawKey = keyMatch[0].trim()
234+
return keyMatch[0].trim()
202235
}
203236
} else {
204-
rawKey = expression.trim()
237+
return expression.trim()
205238
}
206-
239+
}
240+
241+
/**
242+
* make sure the directive and expression is valid
243+
* before we create an instance
244+
*/
245+
Directive.build = function (dirname, expression, compiler, node) {
246+
247+
var dir = compiler.getOption('directives', dirname)
248+
if (!dir) return
249+
250+
var rawKey = Directive.parseKey(expression)
207251
// have a valid raw key, or be an empty directive
208252
if (rawKey || expression === '') {
209253
return new Directive(dirname, dir, expression, rawKey, compiler, node)

src/directives/partial.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ module.exports = {
1919
if (!partial) {
2020
if (id === 'yield') {
2121
utils.warn('{{>yield}} syntax has been deprecated. Use <content> tag instead.')
22-
} else {
23-
utils.warn('Unknown partial: ' + id)
2422
}
2523
return
2624
}

src/exp-parser.js

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ var utils = require('./utils'),
33 C2EE
STR_RESTORE_RE = /"(\d+)"/g,
44
NEWLINE_RE = /\n/g,
55
CTOR_RE = new RegExp('constructor'.split('').join('[\'"+, ]*')),
6-
UNICODE_RE = /\\u\d\d\d\d/,
7-
QUOTE_RE = /"/g
6+
UNICODE_RE = /\\u\d\d\d\d/
87

98
// Variable extraction scooped from https://github.com/RubyLouvre/avalon
109

@@ -110,22 +109,12 @@ function escapeDollar (v) {
110109
: v
111110
}
112111

113-
/**
114-
* Convert double quotes to single quotes
115-
* so they don't mess up the generated function body
116-
*/
117-
function escapeQuote (v) {
118-
return v.indexOf('"') > -1
119-
? v.replace(QUOTE_RE, '\'')
120-
: v
121-
}
122-
123112
/**
124113
* Parse and return an anonymous computed property getter function
125114
* from an arbitrary expression, together with a list of paths to be
126115
* created as bindings.
127116
*/
128-
exports.parse = function (exp, compiler, data, filters) {
117+
exports.parse = function (exp, compiler, data) {
129118
// unicode and 'constructor' are not allowed for XSS security.
130119
if (UNICODE_RE.test(exp) || CTOR_RE.test(exp)) {
131120
utils.warn('Unsafe expression: ' + exp)
@@ -154,23 +143,6 @@ exports.parse = function (exp, compiler, data, filters) {
154143
.replace(pathRE, replacePath)
155144
.replace(STR_RESTORE_RE, restoreStrings)
156145

157-
// wrap expression with computed filters
158-
if (filters) {
159-
var args, filter
160-
for (var i = 0, l = filters.length; i < l; i++) {
161-
filter = filters[i]
162-
args = filter.args
163-
? ',"' + filter.args.map(escapeQuote).join('","') + '"'
164-
: ''
165-
body =
166-
'this.$compiler.getOption("filters", "' +
167-
filter.name +
168-
'").call(this,' +
169-
body + args +
170-
')'
171-
}
172-
}
173-
174146
body = accessors + 'return ' + body
175147

176148
function saveStrings (str) {

0 commit comments

Comments
 (0)
0