@@ -358,11 +358,12 @@ function $CompileProvider($provide) {
358
358
// jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
359
359
$compileNodes = jqLite ( $compileNodes ) ;
360
360
}
361
+ var tempParent = document . createDocumentFragment ( ) ;
361
362
// We can not compile top level text elements since text nodes can be merged and we will
362
363
// not be able to attach scope data to them, so we will wrap them in <span>
363
364
forEach ( $compileNodes , function ( node , index ) {
364
365
if ( node . nodeType == 3 /* text node */ && node . nodeValue . match ( / \S + / ) /* non-empty */ ) {
365
- $compileNodes [ index ] = jqLite ( node ) . wrap ( '<span></span>' ) . parent ( ) [ 0 ] ;
366
+ $compileNodes [ index ] = node = jqLite ( node ) . wrap ( '<span></span>' ) . parent ( ) [ 0 ] ;
366
367
}
367
368
} ) ;
368
369
var compositeLinkFn = compileNodes ( $compileNodes , transcludeFn , $compileNodes , maxPriority ) ;
@@ -420,7 +421,7 @@ function $CompileProvider($provide) {
420
421
attrs = new Attributes ( ) ;
421
422
422
423
// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
423
- directives = collectDirectives ( nodeList [ i ] , [ ] , attrs , maxPriority ) ;
424
+ directives = collectDirectives ( nodeList [ i ] , [ ] , attrs , i == 0 ? maxPriority : undefined ) ;
424
425
425
426
nodeLinkFn = ( directives . length )
426
427
? applyDirectivesToNode ( directives , nodeList [ i ] , attrs , transcludeFn , $rootElement )
@@ -509,6 +510,10 @@ function $CompileProvider($provide) {
509
510
// iterate over the attributes
510
511
for ( var attr , name , nName , ngAttrName , value , nAttrs = node . attributes ,
511
512
j = 0 , jj = nAttrs && nAttrs . length ; j < jj ; j ++ ) {
513
+ var attrStartName ;
514
+ var attrEndName ;
515
+ var index ;
516
+
512
517
attr = nAttrs [ j ] ;
513
518
if ( attr . specified ) {
514
519
name = attr . name ;
@@ -517,6 +522,11 @@ function $CompileProvider($provide) {
517
522
if ( NG_ATTR_BINDING . test ( ngAttrName ) ) {
518
523
name = ngAttrName . substr ( 6 ) . toLowerCase ( ) ;
519
524
}
525
+ if ( ( index = ngAttrName . lastIndexOf ( 'Start' ) ) != - 1 && index == ngAttrName . length - 5 ) {
526
+ attrStartName = name ;
527
+ attrEndName = name . substr ( 0 , name . length - 5 ) + 'end' ;
528
+ name = name . substr ( 0 , name . length - 6 ) ;
529
+ }
520
530
nName = directiveNormalize ( name . toLowerCase ( ) ) ;
521
531
attrsMap [ nName ] = name ;
522
532
attrs [ nName ] = value = trim ( ( msie && name == 'href' )
@@ -526,7 +536,7 @@ function $CompileProvider($provide) {
526
536
attrs [ nName ] = true ; // presence means true
527
537
}
528
538
addAttrInterpolateDirective ( node , directives , value , nName ) ;
529
- addDirective ( directives , nName , 'A' , maxPriority ) ;
539
+ addDirective ( directives , nName , 'A' , maxPriority , attrStartName , attrEndName ) ;
530
540
}
531
541
}
532
542
@@ -565,6 +575,47 @@ function $CompileProvider($provide) {
565
575
return directives ;
566
576
}
567
577
578
+ /**
579
+ * Given a node with an directive-start it collects all of the siblings until it find directive-end.
580
+ * @param node
581
+ * @param attrStart
582
+ * @param attrEnd
583
+ * @returns {* }
584
+ */
585
+ function groupScan ( node , attrStart , attrEnd ) {
586
+ var nodes = [ ] ;
587
+ var depth = 0 ;
588
+ if ( attrStart && node . hasAttribute && node . hasAttribute ( attrStart ) ) {
589
+ var startNode = node ;
590
+ do {
591
+ if ( ! node ) {
592
+ throw ngError ( 51 , "Unterminated attribute, found '{0}' but no matching '{1}' found." , attrStart , attrEnd ) ;
593
+ }
594
+ if ( node . hasAttribute ( attrStart ) ) depth ++ ;
595
+ if ( node . hasAttribute ( attrEnd ) ) depth -- ;
596
+ nodes . push ( node ) ;
597
+ node = node . nextSibling ;
598
+ } while ( depth > 0 ) ;
599
+ } else {
600
+ nodes . push ( node ) ;
601
+ }
602
+ return jqLite ( nodes ) ;
603
+ }
604
+
605
+ /**
606
+ * Wrapper for linking function which converts normal linking function into a grouped
607
+ * linking function.
608
+ * @param linkFn
609
+ * @param attrStart
610
+ * @param attrEnd
611
+ * @returns {Function }
612
+ */
613
+ function groupElementsLinkFnWrapper ( linkFn , attrStart , attrEnd ) {
614
+ return function ( scope , element , attrs , controllers ) {
615
+ element = groupScan ( element [ 0 ] , attrStart , attrEnd ) ;
616
+ return linkFn ( scope , element , attrs , controllers ) ;
617
+ }
618
+ }
568
619
569
620
/**
570
621
* Once the directives have been collected, their compile functions are executed. This method
@@ -601,6 +652,13 @@ function $CompileProvider($provide) {
601
652
// executes all directives on the current element
602
653
for ( var i = 0 , ii = directives . length ; i < ii ; i ++ ) {
603
654
directive = directives [ i ] ;
655
+ var attrStart = directive . $$start ;
656
+ var attrEnd = directive . $$end ;
657
+
658
+ // collect multiblock sections
659
+ if ( attrStart ) {
660
+ $compileNode = groupScan ( compileNode , attrStart , attrEnd )
661
+ }
604
662
$template = undefined ;
605
663
606
664
if ( terminalPriority > directive . priority ) {
@@ -631,11 +689,11 @@ function $CompileProvider($provide) {
631
689
transcludeDirective = directive ;
632
690
terminalPriority = directive . priority ;
633
691
if ( directiveValue == 'element' ) {
634
- $template = jqLite ( compileNode ) ;
692
+ $template = groupScan ( compileNode , attrStart , attrEnd )
635
693
$compileNode = templateAttrs . $$element =
636
694
jqLite ( document . createComment ( ' ' + directiveName + ': ' + templateAttrs [ directiveName ] + ' ' ) ) ;
637
695
compileNode = $compileNode [ 0 ] ;
638
- replaceWith ( jqCollection , jqLite ( $template [ 0 ] ) , compileNode ) ;
696
+ replaceWith ( jqCollection , jqLite ( sliceArgs ( $template ) ) , compileNode ) ;
639
697
childTranscludeFn = compile ( $template , transcludeFn , terminalPriority ) ;
640
698
} else {
641
699
$template = jqLite ( JQLiteClone ( compileNode ) ) . contents ( ) ;
@@ -699,9 +757,9 @@ function $CompileProvider($provide) {
699
757
try {
700
758
linkFn = directive . compile ( $compileNode , templateAttrs , childTranscludeFn ) ;
701
759
if ( isFunction ( linkFn ) ) {
702
- addLinkFns ( null , linkFn ) ;
760
+ addLinkFns ( null , linkFn , attrStart , attrEnd ) ;
703
761
} else if ( linkFn ) {
704
- addLinkFns ( linkFn . pre , linkFn . post ) ;
762
+ addLinkFns ( linkFn . pre , linkFn . post , attrStart , attrEnd ) ;
705
763
}
706
764
} catch ( e ) {
707
765
$exceptionHandler ( e , startingTag ( $compileNode ) ) ;
@@ -723,12 +781,14 @@ function $CompileProvider($provide) {
723
781
724
782
////////////////////
725
783
726
- function addLinkFns ( pre , post ) {
784
+ function addLinkFns ( pre , post , attrStart , attrEnd ) {
727
785
if ( pre ) {
786
+ if ( attrStart ) pre = groupElementsLinkFnWrapper ( pre , attrStart , attrEnd ) ;
728
787
pre . require = directive . require ;
729
788
preLinkFns . push ( pre ) ;
730
789
}
731
790
if ( post ) {
791
+ if ( attrStart ) post = groupElementsLinkFnWrapper ( post , attrStart , attrEnd ) ;
732
792
post . require = directive . require ;
733
793
postLinkFns . push ( post ) ;
734
794
}
@@ -907,17 +967,20 @@ function $CompileProvider($provide) {
907
967
* * `M`: comment
908
968
* @returns true if directive was added.
909
969
*/
910
- function addDirective ( tDirectives , name , location , maxPriority ) {
911
- var match = false ;
970
+ function addDirective ( tDirectives , name , location , maxPriority , startAttrName , endAttrName ) {
971
+ var match = null ;
912
972
if ( hasDirectives . hasOwnProperty ( name ) ) {
913
973
for ( var directive , directives = $injector . get ( name + Suffix ) ,
914
974
i = 0 , ii = directives . length ; i < ii ; i ++ ) {
915
975
try {
916
976
directive = directives [ i ] ;
917
977
if ( ( maxPriority === undefined || maxPriority > directive . priority ) &&
918
978
directive . restrict . indexOf ( location ) != - 1 ) {
979
+ if ( startAttrName ) {
980
+ directive = inherit ( directive , { $$start : startAttrName , $$end : endAttrName } ) ;
981
+ }
919
982
tDirectives . push ( directive ) ;
920
- match = true ;
983
+ match = directive ;
921
984
}
922
985
} catch ( e ) { $exceptionHandler ( e ) ; }
923
986
}
@@ -1120,30 +1183,50 @@ function $CompileProvider($provide) {
1120
1183
*
1121
1184
* @param {JqLite= } $rootElement The root of the compile tree. Used so that we can replace nodes
1122
1185
* in the root of the tree.
1123
- * @param {JqLite } $element The jqLite element which we are going to replace. We keep the shell,
1186
+ * @param {JqLite } elementsToRemove The jqLite element which we are going to replace. We keep the shell,
1124
1187
* but replace its DOM node reference.
1125
1188
* @param {Node } newNode The new DOM node.
1126
1189
*/
1127
- function replaceWith ( $rootElement , $element , newNode ) {
1128
- var oldNode = $element [ 0 ] ,
1129
- parent = oldNode . parentNode ,
1190
+ function replaceWith ( $rootElement , elementsToRemove , newNode ) {
1191
+ var firstElementToRemove = elementsToRemove [ 0 ] ,
1192
+ removeCount = elementsToRemove . length ,
1193
+ parent = firstElementToRemove . parentNode ,
1130
1194
i , ii ;
1131
1195
1132
1196
if ( $rootElement ) {
1133
1197
for ( i = 0 , ii = $rootElement . length ; i < ii ; i ++ ) {
1134
- if ( $rootElement [ i ] == oldNode ) {
1135
- $rootElement [ i ] = newNode ;
1198
+ if ( $rootElement [ i ] == firstElementToRemove ) {
1199
+ $rootElement [ i ++ ] = newNode ;
1200
+ for ( var j = i , j2 = j + removeCount - 1 ,
1201
+ jj = $rootElement . length ;
1202
+ j < jj ; j ++ , j2 ++ ) {
1203
+ if ( j2 < jj ) {
1204
+ $rootElement [ j ] = $rootElement [ j2 ] ;
1205
+ } else {
1206
+ delete $rootElement [ j ] ;
1207
+ }
1208
+ }
1209
+ $rootElement . length -= removeCount - 1 ;
1136
1210
break ;
1137
1211
}
1138
1212
}
1139
1213
}
1140
1214
1141
1215
if ( parent ) {
1142
- parent . replaceChild ( newNode , oldNode ) ;
1216
+ parent . replaceChild ( newNode , firstElementToRemove ) ;
1217
+ }
1218
+ var fragment = document . createDocumentFragment ( ) ;
1219
+ fragment . appendChild ( firstElementToRemove ) ;
1220
+ newNode [ jqLite . expando ] = firstElementToRemove [ jqLite . expando ] ;
1221
+ for ( var k = 1 , kk = elementsToRemove . length ; k < kk ; k ++ ) {
1222
+ var element = elementsToRemove [ k ] ;
1223
+ jqLite ( element ) . remove ( ) ; // must do this way to clean up expando
1224
+ fragment . appendChild ( element ) ;
1225
+ delete elementsToRemove [ k ] ;
1143
1226
}
1144
1227
1145
- newNode [ jqLite . expando ] = oldNode [ jqLite . expando ] ;
1146
- $element [ 0 ] = newNode ;
1228
+ elementsToRemove [ 0 ] = newNode ;
1229
+ elementsToRemove . length = 1
1147
1230
}
1148
1231
} ] ;
1149
1232
}
0 commit comments