@@ -4,27 +4,22 @@ var textParser = require('../parsers/text')
44var dirParser = require ( '../parsers/directive' )
55var templateParser = require ( '../parsers/template' )
66
7+ module . exports = compile
8+
79/**
810 * Compile a template and return a reusable composite link
911 * function, which recursively contains more link functions
1012 * inside. This top level compile function should only be
1113 * called on instance root nodes.
1214 *
13- * When the `asParent` flag is true, this means we are doing
14- * a partial compile for a component's parent scope markup
15- * (See #502). This could **only** be triggered during
16- * compilation of `v-component`, and we need to skip v-with,
17- * v-ref & v-component in this situation.
18- *
1915 * @param {Element|DocumentFragment } el
2016 * @param {Object } options
2117 * @param {Boolean } partial
22- * @param {Boolean } asParent - compiling a component
23- * container as its parent.
18+ * @param {Boolean } transcluded
2419 * @return {Function }
2520 */
2621
27- module . exports = function compile ( el , options , partial , asParent ) {
22+ function compile ( el , options , partial , transcluded ) {
2823 var isBlock = el . nodeType === 11
2924 var params = ! partial && options . paramAttributes
3025 // if el is a fragment, this is a block instance
@@ -37,7 +32,7 @@ module.exports = function compile (el, options, partial, asParent) {
3732 : null
3833 var nodeLinkFn = isBlock
3934 ? null
40- : compileNode ( el , options , asParent )
35+ : compileNode ( el , options )
4136 var childLinkFn =
4237 ! ( nodeLinkFn && nodeLinkFn . terminal ) &&
4338 el . tagName !== 'SCRIPT' &&
@@ -57,12 +52,16 @@ module.exports = function compile (el, options, partial, asParent) {
5752
5853 return function link ( vm , el ) {
5954 var originalDirCount = vm . _directives . length
55+ var parentOriginalDirCount =
56+ vm . $parent && vm . $parent . _directives . length
6057 if ( paramsLinkFn ) {
6158 var paramsEl = isBlock ? el . childNodes [ 1 ] : el
6259 paramsLinkFn ( vm , paramsEl )
6360 }
6461 // cache childNodes before linking parent, fix #657
6562 var childNodes = _ . toArray ( el . childNodes )
63+ // if transcluded, link in parent scope
64+ if ( transcluded ) vm = vm . $parent
6665 if ( nodeLinkFn ) nodeLinkFn ( vm , el )
6766 if ( childLinkFn ) childLinkFn ( vm , childNodes )
6867
@@ -73,16 +72,26 @@ module.exports = function compile (el, options, partial, asParent) {
7372 * linking.
7473 */
7574
76- if ( partial ) {
77- var dirs = vm . _directives . slice ( originalDirCount )
78- return function unlink ( ) {
75+ if ( partial && ! transcluded ) {
76+ var selfDirs = vm . _directives . slice ( originalDirCount )
77+ var parentDirs = vm . $parent &&
78+ vm . $parent . _directives . slice ( parentOriginalDirCount )
79+
80+ var teardownDirs = function ( vm , dirs ) {
7981 var i = dirs . length
8082 while ( i -- ) {
8183 dirs [ i ] . _teardown ( )
8284 }
8385 i = vm . _directives . indexOf ( dirs [ 0 ] )
8486 vm . _directives . splice ( i , dirs . length )
8587 }
88+
89+ return function unlink ( ) {
90+ teardownDirs ( vm , selfDirs )
91+ if ( parentDirs ) {
92+ teardownDirs ( vm . $parent , parentDirs )
93+ }
94+ }
8695 }
8796 }
8897}
@@ -93,14 +102,13 @@ module.exports = function compile (el, options, partial, asParent) {
93102 *
94103 * @param {Node } node
95104 * @param {Object } options
96- * @param {Boolean } asParent
97105 * @return {Function|null }
98106 */
99107
100- function compileNode ( node , options , asParent ) {
108+ function compileNode ( node , options ) {
101109 var type = node . nodeType
102110 if ( type === 1 && node . tagName !== 'SCRIPT' ) {
103- return compileElement ( node , options , asParent )
111+ return compileElement ( node , options )
104112 } else if ( type === 3 && config . interpolate && node . data . trim ( ) ) {
105113 return compileTextNode ( node , options )
106114 } else {
@@ -113,14 +121,20 @@ function compileNode (node, options, asParent) {
113121 *
114122 * @param {Element } el
115123 * @param {Object } options
116- * @param {Boolean } asParent
117124 * @return {Function|null }
118125 */
119126
120- function compileElement ( el , options , asParent ) {
127+ function compileElement ( el , options ) {
128+ if ( checkTransclusion ( el ) ) {
129+ // unwrap textNode
130+ if ( el . hasAttribute ( '__vue__wrap' ) ) {
131+ el = el . firstChild
132+ }
133+ return compile ( el , options . _parent . $options , true , true )
134+ }
121135 var linkFn , tag , component
122136 // check custom element component, but only on non-root
123- if ( ! asParent && ! el . __vue__ ) {
137+ if ( ! el . __vue__ ) {
124138 tag = el . tagName . toLowerCase ( )
125139 component =
126140 tag . indexOf ( '-' ) > 0 &&
@@ -131,12 +145,10 @@ function compileElement (el, options, asParent) {
131145 }
132146 if ( component || el . hasAttributes ( ) ) {
133147 // check terminal direcitves
134- if ( ! asParent ) {
135- linkFn = checkTerminalDirectives ( el , options )
136- }
148+ linkFn = checkTerminalDirectives ( el , options )
137149 // if not terminal, build normal link function
138150 if ( ! linkFn ) {
139- var dirs = collectDirectives ( el , options , asParent )
151+ var dirs = collectDirectives ( el , options )
140152 linkFn = dirs . length
141153 ? makeDirectivesLinkFn ( dirs )
142154 : null
@@ -166,16 +178,21 @@ function makeDirectivesLinkFn (directives) {
166178 return function directivesLinkFn ( vm , el ) {
167179 // reverse apply because it's sorted low to high
168180 var i = directives . length
169- var dir , j , k
181+ var dir , j , k , target
170182 while ( i -- ) {
171183 dir = directives [ i ]
184+ // a directive can be transcluded if it's written
185+ // on a component's container in its parent tempalte.
186+ target = dir . transcluded
187+ ? vm . $parent
188+ : vm
172189 if ( dir . _link ) {
173190 // custom link fn
174- dir . _link ( vm , el )
191+ dir . _link ( target , el )
175192 } else {
176193 k = dir . descriptors . length
177194 for ( j = 0 ; j < k ; j ++ ) {
178- vm . _bindDir ( dir . name , el ,
195+ target . _bindDir ( dir . name , el ,
179196 dir . descriptors [ j ] , dir . def )
180197 }
181198 }
@@ -478,38 +495,37 @@ function makeTeriminalLinkFn (el, dirName, value, options) {
478495 *
479496 * @param {Element } el
480497 * @param {Object } options
481- * @param {Boolean } asParent
482498 * @return {Array }
483499 */
484500
485- function collectDirectives ( el , options , asParent ) {
501+ function collectDirectives ( el , options ) {
486502 var attrs = _ . toArray ( el . attributes )
487503 var i = attrs . length
488504 var dirs = [ ]
489- var attr , attrName , dir , dirName , dirDef
505+ var attr , attrName , dir , dirName , dirDef , transcluded
490506 while ( i -- ) {
491507 attr = attrs [ i ]
492508 attrName = attr . name
509+ transcluded =
510+ options . _transcludedAttrs &&
511+ options . _transcludedAttrs [ attrName ]
493512 if ( attrName . indexOf ( config . prefix ) === 0 ) {
494513 dirName = attrName . slice ( config . prefix . length )
495- if ( asParent &&
496- ( dirName === 'with' ||
497- dirName === 'component' ) ) {
498- continue
499- }
500514 dirDef = options . directives [ dirName ]
501515 _ . assertAsset ( dirDef , 'directive' , dirName )
502516 if ( dirDef ) {
503517 dirs . push ( {
504518 name : dirName ,
505519 descriptors : dirParser . parse ( attr . value ) ,
506- def : dirDef
520+ def : dirDef ,
521+ transcluded : transcluded
507522 } )
508523 }
509524 } else if ( config . interpolate ) {
510525 dir = collectAttrDirective ( el , attrName , attr . value ,
511526 options )
512527 if ( dir ) {
528+ dir . transcluded = transcluded
513529 dirs . push ( dir )
514530 }
515531 }
@@ -531,10 +547,6 @@ function collectDirectives (el, options, asParent) {
531547 */
532548
533549function collectAttrDirective ( el , name , value , options ) {
534- if ( options . _skipAttrs &&
535- options . _skipAttrs . indexOf ( name ) > - 1 ) {
536- return
537- }
538550 var tokens = textParser . parse ( value )
539551 if ( tokens ) {
540552 var def = options . directives . attr
@@ -572,4 +584,19 @@ function directiveComparator (a, b) {
572584 a = a . def . priority || 0
573585 b = b . def . priority || 0
574586 return a > b ? 1 : - 1
587+ }
588+
589+ /**
590+ * Check whether an element is transcluded
591+ *
592+ * @param {Element } el
593+ * @return {Boolean }
594+ */
595+
596+ var transcludedFlagAttr = '__vue__transcluded'
597+ function checkTransclusion ( el ) {
598+ if ( el . nodeType === 1 && el . hasAttribute ( transcludedFlagAttr ) ) {
599+ el . removeAttribute ( transcludedFlagAttr )
600+ return true
601+ }
575602}
0 commit comments