@@ -20,18 +20,9 @@ module.exports = compile
20
20
*/
21
21
22
22
function compile ( el , options , partial , transcluded ) {
23
- var isBlock = el . nodeType === 11
24
- // link function for param attributes.
25
- var params = options . paramAttributes
26
- var paramsLinkFn = params && ! partial && ! transcluded && ! isBlock
27
- ? compileParamAttributes ( el , params , options )
28
- : null
29
23
// link function for the node itself.
30
- // if this is a block instance, we return a link function
31
- // for the attributes found on the container, if any.
32
- // options._containerAttrs are collected during transclusion.
33
- var nodeLinkFn = isBlock
34
- ? compileBlockContainer ( options . _containerAttrs , params , options )
24
+ var nodeLinkFn = options . _asComponent && ! partial
25
+ ? compileRoot ( el , options )
35
26
: compileNode ( el , options )
36
27
// link function for the childNodes
37
28
var childLinkFn =
@@ -52,12 +43,12 @@ function compile (el, options, partial, transcluded) {
52
43
*/
53
44
54
45
function compositeLinkFn ( vm , el ) {
46
+ // save original directive count before linking
47
+ // so we can capture the directives created during a
48
+ // partial compilation.
55
49
var originalDirCount = vm . _directives . length
56
50
var parentOriginalDirCount =
57
51
vm . $parent && vm . $parent . _directives . length
58
- if ( paramsLinkFn ) {
59
- paramsLinkFn ( vm , el )
60
- }
61
52
// cache childNodes before linking parent, fix #657
62
53
var childNodes = _ . toArray ( el . childNodes )
63
54
// if this is a transcluded compile, linkers need to be
@@ -109,36 +100,61 @@ function compile (el, options, partial, transcluded) {
109
100
}
110
101
111
102
/**
112
- * Compile the attributes found on a "block container" -
113
- * i.e. the container node in the parent tempate of a block
114
- * instance. We are only concerned with v-with and
115
- * paramAttributes here.
103
+ * Compile the root element of a component. There are
104
+ * 4 types of things to process here:
105
+ *
106
+ * 1. paramAttributes on parent container (child scope)
107
+ * 2. v-with on parent container (child scope)
108
+ * 3. other attrs on parent container (parent scope)
109
+ * 4. attrs on the component template root node, if
110
+ * replace:true (child scope)
116
111
*
117
- * @param {Object } attrs - a map of attr name/value pairs
118
- * @param {Array } params - param attributes list
112
+ * Also, if this is a block instance, we only need to
113
+ * compile 1 & 2 here.
114
+ *
115
+ * @param {Element } el
119
116
* @param {Object } options
120
117
* @return {Function }
121
118
*/
122
119
123
- function compileBlockContainer ( attrs , params , options ) {
124
- if ( ! attrs ) return null
125
- var paramsLinkFn = params
126
- ? compileParamAttributes ( attrs , params , options )
120
+ function compileRoot ( el , options ) {
121
+ var isBlock = el . nodeType === 11 // DocumentFragment
122
+ var containerAttrs = options . _containerAttrs
123
+ var replacerAttrs = options . _replacerAttrs
124
+ var params = options . paramAttributes
125
+ var paramsLinkFn , withLinkFn , parentLinkFn , replacerLinkFn
126
+ // 1. paramAttributes
127
+ paramsLinkFn = params
128
+ ? compileParamAttributes ( el , containerAttrs , params , options )
127
129
: null
128
- var withVal = attrs [ config . prefix + 'with' ]
129
- var withLinkFn = null
130
+ // 2.
F438
v-with
131
+ var withName = config . prefix + 'with'
132
+ var withVal = containerAttrs && containerAttrs [ withName ]
130
133
if ( withVal ) {
131
- var descriptor = dirParser . parse ( withVal ) [ 0 ]
132
- var def = options . directives [ 'with' ]
133
- withLinkFn = function ( vm , el ) {
134
- vm . _bindDir ( 'with' , el , descriptor , def )
134
+ containerAttrs [ withName ] = null
135
+ withLinkFn = makeNodeLinkFn ( [ {
136
+ name : 'with' ,
137
+ descriptors : dirParser . parse ( withVal ) ,
138
+ def : options . directives [ 'with' ]
139
+ } ] )
140
+ }
141
+ if ( ! isBlock ) {
142
+ // 3. container attributes
143
+ if ( containerAttrs ) {
144
+ parentLinkFn = compileDirectives ( containerAttrs , options )
145
+ }
146
+ if ( replacerAttrs ) {
147
+ // 4. replacer attributes
148
+ replacerLinkFn = compileDirectives ( replacerAttrs , options )
135
149
}
136
150
}
137
- return function blockContainerLinkFn ( vm ) {
138
- // explicitly passing null to the linkers
139
- // since v-with doesn 't need a real element
151
+ return function rootLinkFn ( vm , el , host ) {
152
+ // explicitly passing null to paramAttributes and v-with
153
+ // linkers because they don 't need a real element
140
154
if ( paramsLinkFn ) paramsLinkFn ( vm , null )
141
155
if ( withLinkFn ) withLinkFn ( vm , null )
156
+ if ( parentLinkFn ) parentLinkFn ( vm . $parent , el , host )
157
+ if ( replacerLinkFn ) replacerLinkFn ( vm , el , host )
142
158
}
143
159
}
144
160
@@ -194,10 +210,7 @@ function compileElement (el, options) {
194
210
linkFn = checkTerminalDirectives ( el , options )
195
211
// if not terminal, build normal link function
196
212
if ( ! linkFn ) {
197
- var dirs = collectDirectives ( el , options )
198
- linkFn = dirs . length
199
- ? makeNodeLinkFn ( dirs )
200
- : null
213
+ linkFn = compileDirectives ( el , options )
201
214
}
202
215
}
203
216
// if the element is a textarea, we need to interpolate
@@ -213,39 +226,6 @@ function compileElement (el, options) {
213
226
return linkFn
214
227
}
215
228
216
- /**
217
- * Build a link function for all directives on a single node.
218
- *
219
- * @param {Array } directives
220
- * @return {Function } directivesLinkFn
221
- */
222
-
223
- function makeNodeLinkFn ( directives ) {
224
- return function nodeLinkFn ( vm , el , host ) {
225
- // reverse apply because it's sorted low to high
226
- var i = directives . length
227
- var dir , j , k , target
228
- while ( i -- ) {
229
- dir = directives [ i ]
230
- // a directive can be transcluded if it's written
231
- // on a component's container in its parent tempalte.
232
- target = dir . transcluded
233
- ? vm . $parent
234
- : vm
235
- if ( dir . _link ) {
236
- // custom link fn
237
- dir . _link ( target , el )
238
- } else {
239
- k = dir . descriptors . length
240
- for ( j = 0 ; j < k ; j ++ ) {
241
- target . _bindDir ( dir . name , el ,
242
- dir . descriptors [ j ] , dir . def , host )
243
- }
244
- }
245
- }
246
- }
247
- }
248
-
249
229
/**
250
230
* Compile a textNode and return a nodeLinkFn.
251
231
*
@@ -397,19 +377,19 @@ function makeChildLinkFn (linkFns) {
397
377
* Compile param attributes on a root element and return
398
378
* a paramAttributes link function.
399
379
*
400
- * @param {Element|Object } el
401
- * @param {Array } attrs
380
+ * @param {Element|DocumentFragment } el
381
+ * @param {Object } attrs
382
+ * @param {Array } paramNames
402
383
* @param {Object } options
403
384
* @return {Function } paramsLinkFn
404
385
*/
405
386
406
- function compileParamAttributes ( el , attrs , options ) {
387
+ function compileParamAttributes ( el , attrs , paramNames , options ) {
407
388
var params = [ ]
408
- var isEl = el . nodeType
409
- var i = attrs . length
389
+ var i = paramNames . length
410
390
var name , value , param
411
391
while ( i -- ) {
412
- name = attrs [ i ]
392
+ name = paramNames [ i ]
413
393
if ( / [ A - Z ] / . test ( name ) ) {
414
394
_ . warn (
415
395
'You seem to be using camelCase for a paramAttribute, ' +
@@ -419,15 +399,18 @@ function compileParamAttributes (el, attrs, options) {
419
399
'http://vuejs.org/api/options.html#paramAttributes'
420
400
)
421
401
}
422
- value = isEl ? el . getAttribute ( name ) : el [ name ]
402
+ value = attrs [ name ]
423
403
if ( value !== null ) {
424
404
param = {
425
405
name : name ,
426
406
value : value
427
407
}
428
408
var tokens = textParser . parse ( value )
429
409
if ( tokens ) {
430
- if ( isEl ) el . removeAttribute ( name )
410
+ if ( el && el . nodeType === 1 ) {
411
+ el . removeAttribute ( name )
412
+ }
413
+ attrs [ name ] = null
431
414
if ( tokens . length > 1 ) {
432
415
_ . warn (
433
416
'Invalid param attribute binding: "' +
@@ -536,62 +519,109 @@ function makeTerminalNodeLinkFn (el, dirName, value, options) {
536
519
}
537
520
538
521
/**
539
- * Collect the directives on an element.
522
+ * Compile the directives on an element and return a linker .
540
523
*
541
- * @param {Element } el
524
+ * @param {Element|Object } elOrAttrs
525
+ * - could be an object of already-extracted
526
+ * container attributes.
542
527
* @param {Object } options
543
- * @return {Array }
528
+ * @return {Function }
544
529
*/
545
530
546
- function collectDirectives ( el , options ) {
547
- var attrs = _ . toArray ( el . attributes )
531
+ function compileDirectives ( elOrAttrs , options ) {
532
+ var attrs = _ . isPlainObject ( elOrAttrs )
533
+ ? mapToList ( elOrAttrs )
534
+ : elOrAttrs . attributes
548
535
var i = attrs . length
549
536
var dirs = [ ]
550
- var attr , attrName , dir , dirName , dirDef , transcluded
537
+ var attr , name , value , dir , dirName , dirDef
551
538
while ( i -- ) {
552
539
attr = attrs [ i ]
553
- attrName = attr . name
554
- transcluded =
555
- options . _transcludedAttrs &&
556
- options . _transcludedAttrs [ attrName ]
557
- if ( attrName . indexOf ( config . prefix ) === 0 ) {
558
- dirName = attrName . slice ( config . prefix . length )
540
+ name = attr . name
541
+ value = attr . value
542
+ if ( value === null ) continue
543
+ if ( name . indexOf ( config . prefix ) === 0 ) {
544
+ dirName = name . slice ( config . prefix . length )
559
545
dirDef = options . directives [ dirName ]
560
546
_ . assertAsset ( dirDef , 'directive' , dirName )
561
547
if ( dirDef ) {
562
548
dirs . push ( {
563
549
name : dirName ,
564
- descriptors : dirParser . parse ( attr . value ) ,
565
- def : dirDef ,
566
- transcluded : transcluded
550
+ descriptors : dirParser . parse ( value ) ,
551
+ def : dirDef
567
552
} )
568
553
}
569
554
} else if ( config . interpolate ) {
570
- dir = collectAttrDirective ( el , attrName , attr . value ,
571
- options )
555
+ dir = collectAttrDirective ( name , value , options )
572
556
if ( dir ) {
573
- dir . transcluded = transcluded
574
557
dirs . push ( dir )
575
558
}
576
559
}
577
560
}
578
561
// sort by priority, LOW to HIGH
579
- dirs . sort ( directiveComparator )
580
- return dirs
562
+ if ( dirs . length ) {
563
+ dirs . sort ( directiveComparator )
564
+ return makeNodeLinkFn ( dirs )
565
+ }
566
+ }
567
+
568
+ /**
569
+ * Convert a map (Object) of attributes to an Array.
570
+ *
571
+ * @param {Object } map
572
+ * @return {Array }
573
+ */
574
+
575
+ function mapToList ( map ) {
576
+ var list = [ ]
577
+ for ( var key in map ) {
578
+ list . push ( {
579
+ name : key ,
580
+ value : map [ key ]
581
+ } )
582
+ }
583
+ return list
584
+ }
585
+
586
+ /**
587
+ * Build a link function for all directives on a single node.
588
+ *
589
+ * @param {Array } directives
590
+ * @return {Function } directivesLinkFn
591
+ */
592
+
593
+ function makeNodeLinkFn ( directives ) {
594
+ return function nodeLinkFn ( vm , el , host ) {
595
+ // reverse apply because it's sorted low to high
596
+ var i = directives . length
597
+ var dir , j , k
598
+ while ( i -- ) {
599
+ dir = directives [ i ]
600
+ if ( dir . _link ) {
601
+ // custom link fn
602
+ dir . _link ( vm , el )
603
+ } else {
604
+ k = dir . descriptors . length
605
+ for ( j = 0 ; j < k ; j ++ ) {
606
+ vm . _bindDir ( dir . name , el ,
607
+ dir . descriptors [ j ] , dir . def , host )
608
+ }
609
+ }
610
+ }
611
+ }
581
612
}
582
613
583
614
/**
584
615
* Check an attribute for potential dynamic bindings,
585
616
* and return a directive object.
586
617
*
587
- * @param {Element } el
588
618
* @param {String } name
589
619
* @param {String } value
590
620
* @param {Object } options
591
621
* @return {Object }
592
622
*/
593
623
594
- function collectAttrDirective ( el , name , value , options ) {
624
+ function collectAttrDirective ( name , value , options ) {
595
625
var tokens = textParser . parse ( value )
596
626
if ( tokens ) {
597
627
var def = options . directives . attr
0 commit comments