185
185
* * `$scope` - Current scope associated with the element
186
186
* * `$element` - Current element
187
187
* * `$attrs` - Current attributes object for the element
188
- * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope.
189
- * The scope can be overridden by an optional first argument.
190
- * `function([scope], cloneLinkingFn)`.
188
+ * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
189
+ * `function([scope], cloneLinkingFn, futureParentNode)`.
190
+ * * The scope can be overridden by an optional first argument.
191
+ * * The `futureParentNode` defines the parent to which the `cloneLinkingFn` will add the cloned elements.
192
+ * Only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
193
+ * and when the `cloneLinkinFn` is passed,
194
+ * as those elements need to created and cloned in a special way when they are defined outside their
195
+ * usual containers (e.g. like `<svg>`). See also the `directive.templateNamespace` property.
196
+ * Default value: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
191
197
*
192
198
*
193
199
* #### `require`
265
271
* one. See the {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive
266
272
* Directives Guide} for an example.
267
273
*
274
+ * Right now, this is only really required for SVG directives that transclude content.
275
+ *
268
276
* #### `transclude`
269
277
* compile the content of the element and make it available to the directive.
270
278
* Typically used with {@link ng.directive:ngTransclude
361
369
* * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
362
370
* The scope can be overridden by an optional first argument. This is the same as the `$transclude`
363
371
* parameter of directive controllers.
364
- * `function([scope], cloneLinkingFn)`.
372
+ * `function([scope], cloneLinkingFn, futureParentNode)`.
373
+ *
374
+ * * `html` - All transcluded root nodes are HTML. Root nodes may also be
375
+ * top-level elements such as `<svg>` or `<math>`.
376
+ * * `svg` - The transcluded root nodes are SVG elements (excluding `<math>`).
377
+ * * `math` - The transcluded root nodes are MathML elements (excluding `<svg>`).
378
+ *
379
+ * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
380
+ *
381
+
382
+ * (e.g. SVG elements)
383
+ to determine the correct as those elements need to be created and cloned
384
+ * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
385
+ *
386
+ * * `html` - All transcluded root nodes are HTML. Root nodes may also be
387
+ * top-level elements such as `<svg>` or `<math>`.
388
+ * * `svg` - The transcluded root nodes are SVG elements (excluding `<math>`).
389
+ * * `math` - The transcluded root nodes are MathML elements (excluding `<svg>`).
390
+ *
391
+ * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
392
+ *
393
+
394
+ * This is needed
395
+ * needed when the content of the transclude are allowed to be non html elements,
396
+ * e.g. SVG elements or MathML elements.
365
397
*
366
398
*
367
399
* #### Pre-linking function
@@ -879,8 +911,18 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
879
911
compileNodes ( $compileNodes , transcludeFn , $compileNodes ,
880
912
maxPriority , ignoreDirective , previousCompileContext ) ;
881
913
safeAddClass ( $compileNodes , 'ng-scope' ) ;
882
- return function publicLinkFn ( scope , cloneConnectFn , transcludeControllers , parentBoundTranscludeFn ) {
914
+ var namespace = null ;
915
+ return function publicLinkFn ( scope , cloneConnectFn , transcludeControllers , parentBoundTranscludeFn , futureParentNode ) {
883
916
assertArg ( scope , 'scope' ) ;
917
+ if ( ! namespace ) {
918
+ namespace = detectNamespaceForChildElements ( futureParentNode ) ;
919
+ if ( namespace !== 'html' ) {
920
+ $compileNodes = jqLite (
921
+ wrapTemplate ( namespace , jqLite ( '<div>' ) . append ( $compileNodes ) . html ( ) )
922
+ ) ;
923
+ }
924
+ }
925
+
884
926
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
885
927
// and sometimes changes the structure of the DOM.
886
928
var $linkNode = cloneConnectFn
@@ -901,6 +943,17 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
901
943
} ;
902
944
}
903
945
946
+ function detectNamespaceForChildElements ( parentElement ) {
947
+ // TODO: Make this detect MathML as well...
948
+ var node = parentElement && parentElement [ 0 ] ;
949
+ if ( ! node ) {
950
+ return 'html' ;
951
+ } else {
952
+ var toString = node . toString ( ) ;
953
+ return node . nodeName !== 'foreignObject' && toString . match ( / S V G / ) ? 'svg' : 'html' ;
954
+ }
955
+ }
956
+
904
957
function safeAddClass ( $element , className ) {
905
958
try {
906
959
$element . addClass ( className ) ;
@@ -1024,7 +1077,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1024
1077
1025
1078
function createBoundTranscludeFn ( scope , transcludeFn , previousBoundTranscludeFn , elementTransclusion ) {
1026
1079
1027
- var boundTranscludeFn = function ( transcludedScope , cloneFn , controllers ) {
1080
+ var boundTranscludeFn = function ( transcludedScope , cloneFn , controllers , futureParentNode ) {
1028
1081
var scopeCreated = false ;
1029
1082
1030
1083
if ( ! transcludedScope ) {
@@ -1033,7 +1086,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1033
1086
scopeCreated = true ;
1034
1087
}
1035
1088
1036
- var clone = transcludeFn ( transcludedScope , cloneFn , controllers , previousBoundTranscludeFn ) ;
1089
+ var clone = transcludeFn ( transcludedScope , cloneFn , controllers , previousBoundTranscludeFn , futureParentNode ) ;
1037
1090
if ( scopeCreated && ! elementTransclusion ) {
1038
1091
clone . on ( '$destroy' , function ( ) { transcludedScope . $destroy ( ) ; } ) ;
1039
1092
}
@@ -1645,20 +1698,30 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1645
1698
}
1646
1699
1647
1700
// This is the function that is injected as `$transclude`.
1648
- function controllersBoundTransclude ( scope , cloneAttachFn ) {
1701
+ // Note: all arguments are optional!
1702
+ function controllersBoundTransclude ( /*scope, cloneAttachFn, futureParentNode*/ ) {
1703
+ var scope , cloneAttachFn , futureParentNode ;
1649
1704
var transcludeControllers ;
1650
1705
1651
- // no scope passed
1652
- if ( ! cloneAttachFn ) {
1653
- cloneAttachFn = scope ;
1654
- scope = undefined ;
1706
+ var arg ;
1707
+ for ( var i = 0 ; i < arguments . length ; i ++ ) {
1708
+ arg = arguments [ i ] ;
1709
+ if ( isScope ( arg ) ) {
1710
+ scope = arg ;
1711
+ } else if ( isFunction ( arg ) ) {
1712
+ cloneAttachFn = arg ;
1713
+ } else {
1714
+ futureParentNode = arg ;
1715
+ }
1655
1716
}
1656
1717
1657
1718
if ( hasElementTranscludeDirective ) {
1658
1719
transcludeControllers = elementControllers ;
1659
1720
}
1660
-
1661
- return boundTranscludeFn ( scope , cloneAttachFn , transcludeControllers ) ;
1721
+ if ( ! futureParentNode ) {
1722
+ futureParentNode = hasElementTranscludeDirective ? $element . parent ( ) : $element ;
1723
+ }
1724
+ return boundTranscludeFn ( scope , cloneAttachFn , transcludeControllers , futureParentNode ) ;
1662
1725
}
1663
1726
}
1664
1727
}
0 commit comments