Skip to content

Commit 8ce2b3d

Browse files
committed
functional tests pass
1 parent 3bde232 commit 8ce2b3d

File tree

4 files changed

+81
-183
lines changed

4 files changed

+81
-183
lines changed

src/compiler.js

+31-14
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.build(dirname, '', this, node)
468+
directive = this.parseDirective(dirname, '', node)
469469
directive.Ctor = Ctor
470470
} else {
471471
expression = utils.attr(node, dirname)
472-
directive = expression && Directive.build(dirname, expression, this, node)
472+
directive = expression && this.parseDirective(dirname, expression, node)
473473
}
474474
if (directive) {
475475
if (root === true) {
@@ -525,7 +525,7 @@ CompilerProto.compileElement = function (node, root) {
525525
var prefix = config.prefix + '-',
526526
attrs = slice.call(node.attributes),
527527
params = this.options.paramAttributes,
528-
attr, isDirective, exps, exp, directive, dirname
528+
attr, isDirective, exp, directives, directive, dirname
529529

530530
for (i = 0, l = attrs.length; i < l; i++) {
531531

@@ -535,26 +535,24 @@ CompilerProto.compileElement = function (node, root) {
535535
if (attr.name.indexOf(prefix) === 0) {
536536
// a directive - split, parse and bind it.
537537
isDirective = true
538-
exps = Directive.split(attr.value)
538+
dirname = attr.name.slice(prefix.length)
539+
// build with multiple: true
540+
directives = this.parseDirective(dirname, attr.value, node, true)
539541
// loop through clauses (separated by ",")
540542
// inside each attribute
541-
for (j = 0, k = exps.length; j < k; j++) {
542-
exp = exps[j]
543-
dirname = attr.name.slice(prefix.length)
544-
directive = Directive.build(dirname, exp, this, node)
545-
543+
for (j = 0, k = directives.length; j < k; j++) {
544+
directive = directives[j]
546545
if (dirname === 'with') {
547546
this.bindDirective(directive, this.parent)
548547
} else {
549548
this.bindDirective(directive)
550549
}
551-
552550
}
553551
} else if (config.interpolate) {
554552
// non directive attribute, check interpolation tags
555553
exp = TextParser.parseAttr(attr.value)
556554
if (exp) {
557-
directive = Directive.build('attr', attr.name + ':' + exp, this, node)
555+
directive = this.parseDirective('attr', attr.name + ':' + exp, node)
558556
if (params && params.indexOf(attr.name) > -1) {
559557
// a param attribute... we should use the parent binding
560558
// to avoid circular updates like size={{size}}
@@ -595,14 +593,14 @@ CompilerProto.compileTextNode = function (node) {
595593
if (token.key) { // a binding
596594
if (token.key.charAt(0) === '>') { // a partial
597595
el = document.createComment('ref')
598-
directive = Directive.build('partial', token.key.slice(1), this, el)
596+
directive = this.parseDirective('partial', token.key.slice(1), el)
599597
} else {
600598
if (!token.html) { // text binding
601599
el = document.createTextNode('')
602-
directive = Directive.build('text', token.key, this, el)
600+
directive = this.parseDirective('text', token.key, el)
603601
} else { // html binding
604602
el = document.createComment(config.prefix + '-html')
605-
directive = Directive.build('html', token.key, this, el)
603+
directive = this.parseDirective('html', token.key, el)
606604
}
607605
}
608606
} else { // a plain string
@@ -618,6 +616,25 @@ CompilerProto.compileTextNode = function (node) {
618616
node.parentNode.removeChild(node)
619617
}
620618

619+
/**
620+
* Parse a directive name/value pair into one or more
621+
* directive instances
622+
*/
623+
CompilerProto.parseDirective = function (name, value, el, multiple) {
624+
var compiler = this,
625+
definition = compiler.getOption('directives', name)
626+
if (definition) {
627+
// parse into AST-like objects
628+
var asts = Directive.parse(value)
629+
return multiple
630+
? asts.map(build)
631+
: build(asts[0])
632+
}
633+
function build (ast) {
634+
return new Directive(name, ast, definition, compiler, el)
635+
}
636+
}
637+
621638
/**
622639
* Add a directive instance to the correct binding & viewmodel
623640
*/

src/directive.js

+41-162
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
var utils = require('./utils'),
2-
dirId = 1,
3-
4-
// match up to the first single pipe, ignore those within quotes.
5-
KEY_RE = /^(?:['"](?:\\.|[^'"])*['"]|\\.|[^\|]|\|\|)+/,
6-
ARG_RE = /^([\w-$ ]+):(.+)$/,
7-
FILTERS_RE = /\|[^\|]+/g,
8-
FILTER_TOKEN_RE = /[^\s']+|'[^']+'|[^\s"]+|"[^"]+"/g,
1+
var dirId = 1,
2+
ARG_RE = /^[\w-]+$/,
3+
FILTER_TOKEN_RE = /[^\s'"]+|'[^']+'|"[^"]+"/g,
94
NESTING_RE = /^\$(parent|root)\./,
105
SINGLE_VAR_RE = /^[\w\.$]+$/,
116
QUOTE_RE = /"/g
@@ -14,16 +9,19 @@ var utils = require('./utils'),
149
* Directive class
1510
* represents a single directive instance in the DOM
1611
*/
17-
function Directive (dirname, definition, expression, rawKey, compiler, node) {
12+
function Directive (name, ast, definition, compiler, el) {
1813

1914
this.id = dirId++
20-
this.name = dirname
15+
this.name = name
2116
this.compiler = compiler
2217
this.vm = compiler.vm
23-
this.el = node
18+
this.el = el
2419
this.computeFilters = false
20+
this.key = ast.key
21+
this.arg = ast.arg
22+
this.expression = ast.expression
2523

26-
var isEmpty = expression === ''
24+
var isEmpty = this.expression === ''
2725

2826
// mix in properties from the directive definition
2927
if (typeof definition === 'function') {
@@ -46,15 +44,11 @@ function Directive (dirname, definition, expression, rawKey, compiler, node) {
4644

4745
this.expression = (
4846
this.isLiteral
49-
? compiler.eval(expression)
50-
: expression
47+
? compiler.eval(this.expression)
48+
: this.expression
5149
).trim()
52-
53-
var parsed = Directive.parseArg(rawKey)
54-
this.key = parsed.key
55-
this.arg = parsed.arg
56-
57-
var filters = Directive.parseFilters(this.expression.slice(rawKey.length)),
50+
51+
var filters = ast.filters,
5852
filter, fn, i, l, computed
5953
if (filters) {
6054
this.filters = []
@@ -133,8 +127,8 @@ DirProto.unbind = function () {
133127
// Exposed static methods -----------------------------------------------------
134128

135129
/**
136-
* split a unquoted-comma separated expression into
137-
* multiple clauses
130+
* Parse a directive string into an Array of
131+
* AST-like objects representing directives
138132
*/
139133
Directive.parse = function (str) {
140134

@@ -147,7 +141,8 @@ Directive.parse = function (str) {
147141
argIndex = 0,
148142
dirs = [],
149143
dir = {},
150-
lastFilterIndex = 0
144+
lastFilterIndex = 0,
145+
arg
151146

152147
for (var c, i = 0, l = str.length; i < l; i++) {
153148
c = str.charAt(i)
@@ -165,12 +160,15 @@ Directive.parse = function (str) {
165160
begin = argIndex = lastFilterIndex = i + 1
166161
} else if (c === ':' && !dir.key && !dir.arg) {
167162
// argument
168-
argIndex = i + 1
169-
dir.arg = str.slice(begin, i).trim()
163+
arg = str.slice(begin, i).trim()
164+
if (ARG_RE.test(arg)) {
165+
argIndex = i + 1
166+
dir.arg = str.slice(begin, i).trim()
167+
}
170168
} else if (c === '|' && str.charAt(i + 1) !== '|') {
171-
if (!dir.key) {
169+
if (dir.key === undefined) {
172170
// first filter, end of key
173-
lastFilterIndex = i
171+
lastFilterIndex = i + 1
174172
dir.key = str.slice(argIndex, i).trim()
175173
} else {
176174
// already has filter
@@ -194,127 +192,40 @@ Directive.parse = function (str) {
194192
curly--
195193
}
196194
}
197-
if (begin !== i) {
195+
if (i === 0 || begin !== i) {
198196
pushDir()
199197
}
200198

201199
function pushDir () {
202200
dir.expression = str.slice(begin, i).trim()
203-
if (!dir.key) {
201+
if (dir.key === undefined) {
204202
dir.key = str.slice(argIndex, i).trim()
205203
} else if (lastFilterIndex !== begin) {
206204
pushFilter()
207205
}
208-
dirs.push(dir)
206+
if (i === 0 || dir.key) {
207+
dirs.push(dir)
208+
}
209209
}
210210

211211
function pushFilter () {
212-
(dir.filters = dir.filters || [])
213-
.push(str.slice(lastFilterIndex + 1, i).trim())
212+
var exp = str.slice(lastFilterIndex, i).trim(),
213+
filter
214+
if (exp) {
215+
filter = {}
216+
var tokens = exp.match(FILTER_TOKEN_RE)
217+
filter.name = tokens[0]
218+
filter.args = tokens.length > 1 ? tokens.slice(1) : null
219+
}
220+
if (filter) {
221+
(dir.filters = dir.filters || []).push(filter)
222+
}
214223
lastFilterIndex = i + 1
215224
}
216225

217226
return dirs
218227
}
219228

220-
// function split (str) {
221-
// var inSingle = false,
222-
// inDouble = false,
223-
// curly = 0,
224-
// square = 0,
225-
// paren = 0,
226-
// begin = 0,
227-
// end = 0,
228-
// res = []
229-
// for (var c, i = 0, l = str.length; i < l; i++) {
230-
// c = str.charAt(i)
231-
// if (inSingle) {
232-
// if (c === "'") {
233-
// inSingle = !inSingle
234-
// }
235-
// end++
236-
// } else if (inDouble) {
237-
// if (c === '"') {
238-
// inDouble = !inDouble
239-
// }
240-
// end++
241-
// } else if (c === ',' && !paren && !curly && !square) {
242-
// res.push(str.slice(begin, end))
243-
// begin = end = i + 1
244-
// } else {
245-
// if (c === '"') {
246-
// inDouble = true
247-
// } else if (c === "'") {
248-
// inSingle = true
249-
// } else if (c === '(') {
250-
// paren++
251-
// } else if (c === ')') {
252-
// paren--
253-
// } else if (c === '[') {
254-
// square++
255-
// } else if (c === ']') {
256-
// square--
257-
// } else if (c === '{') {
258-
// curly++
259-
// } else if (c === '}') {
260-
// curly--
261-
// }
262-
// end++
263-
// }
264-
// }
265-
// if (begin !== end) {
266-
// res.push(str.slice(begin, end))
267-
// }
268-
// return res
269-
// }
270-
271-
// /**
272-
// * parse a key, extract argument
273-
// */
274-
// Directive.parseArg = function (rawKey) {
275-
// var key = rawKey,
276-
// arg = null
277-
// if (rawKey.indexOf(':') > -1) {
278-
// var argMatch = rawKey.match(ARG_RE)
279-
// key = argMatch
280-
// ? argMatch[2].trim()
281-
// : key
282-
// arg = argMatch
283-
// ? argMatch[1].trim()
284-
// : arg
285-
// }
286-
// return {
287-
// key: key,
288-
// arg: arg
289-
// }
290-
// }
291-
292-
// /**
293-
// * parse a the filters
294-
// */
295-
// Directive.parseFilters = function (exp) {
296-
// if (exp.indexOf('|') < 0) {
297-
// return
298-
// }
299-
// var filters = exp.match(FILTERS_RE),
300-
// res, i, l, tokens
301-
// if (filters) {
302-
// res = []
303-
// for (i = 0, l = filters.length; i < l; i++) {
304-
// tokens = filters[i].slice(1).match(FILTER_TOKEN_RE)
305-
// if (tokens) {
306-
// res.push({
307-
// name: tokens[0],
308-
// args: tokens.length > 1
309-
// ? tokens.slice(1)
310-
// : null
311-
// })
312-
// }
313-
// }
314-
// }
315-
// return res
316-
// }
317-
318229
/**
319230
* Inline computed filters so they become part
320231
* of the expression
@@ -345,36 +256,4 @@ function escapeQuote (v) {
345256
: v
346257
}
347258

348-
/**
349-
* Parse the key from a directive raw expression
350-
*/
351-
Directive.parseKey = function (expression) {
352-
if (expression.indexOf('|') > -1) {
353-
var keyMatch = expression.match(KEY_RE)
354-
if (keyMatch) {
355-
return keyMatch[0].trim()
356-
}
357-
} else {
358-
return expression.trim()
359-
}
360-
}
361-
362-
/**
363-
* make sure the directive and expression is valid
364-
* before we create an instance
365-
*/
366-
Directive.build = function (dirname, expression, compiler, node) {
367-
368-
var dir = compiler.getOption('directives', dirname)
369-
if (!dir) return
370-
371-
var rawKey = Directive.parseKey(expression)
372-
// have a valid raw key, or be an empty directive
373-
if (rawKey || expression === '') {
374-
return new Directive(dirname, dir, expression, rawKey, compiler, node)
375-
} else {
376-
utils.warn('Invalid directive expression: ' + expression)
377-
}
378-
}
379-
380259
module.exports = Directive

0 commit comments

Comments
 (0)