style($compile): clean up the code and normalize fn names

This commit is contained in:
Igor Minar 2012-05-03 00:15:07 -07:00
parent 9cba23a588
commit fff31d8d61
2 changed files with 190 additions and 161 deletions

View file

@ -1,5 +1,23 @@
'use strict';
/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
*
* DOM-related variables:
*
* - "node" - DOM Node
* - "element" - DOM Element or Node
* - "$node" or "$element" - jqLite-wrapped node or element
*
*
* Compiler related stuff:
*
* - "linkFn" - linking fn of a single directive
* - "nodeLinkFn" - function that aggregates all linking fns for a particular node
* - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
* - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
*/
/**
* @ngdoc function
* @name angular.module.ng.$compile
@ -290,30 +308,31 @@ function $CompileProvider($provide) {
//================================
function compile(templateElement, transcludeFn, maxPriority) {
if (!(templateElement instanceof jqLite)) {
function compile($compileNode, transcludeFn, maxPriority) {
if (!($compileNode instanceof jqLite)) {
// jquery always rewraps, where as we need to preserve the original selector so that we can modify it.
templateElement = jqLite(templateElement);
$compileNode = jqLite($compileNode);
}
// We can not compile top level text elements since text nodes can be merged and we will
// not be able to attach scope data to them, so we will wrap them in <span>
forEach(templateElement, function(node, index){
forEach($compileNode, function(node, index){
if (node.nodeType == 3 /* text node */) {
templateElement[index] = jqLite(node).wrap('<span>').parent()[0];
$compileNode[index] = jqLite(node).wrap('<span>').parent()[0];
}
});
var linkingFn = compileNodes(templateElement, transcludeFn, templateElement, maxPriority);
var compositeLinkFn = compileNodes($compileNode, transcludeFn, $compileNode, maxPriority);
return function(scope, cloneConnectFn){
assertArg(scope, 'scope');
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
// and sometimes changes the structure of the DOM.
var element = cloneConnectFn
? JQLitePrototype.clone.call(templateElement) // IMPORTANT!!!
: templateElement;
safeAddClass(element.data('$scope', scope), 'ng-scope');
if (cloneConnectFn) cloneConnectFn(element, scope);
if (linkingFn) linkingFn(scope, element, element);
return element;
var $linkNode = cloneConnectFn
? JQLitePrototype.clone.call($compileNode) // IMPORTANT!!!
: $compileNode;
$linkNode.data('$scope', scope);
safeAddClass($linkNode, 'ng-scope');
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
return $linkNode;
};
}
@ -321,9 +340,9 @@ function $CompileProvider($provide) {
throw Error("Unsupported '" + mode + "' for '" + localName + "'.");
}
function safeAddClass(element, className) {
function safeAddClass($element, className) {
try {
element.addClass(className);
$element.addClass(className);
} catch(e) {
// ignore, since it means that we are trying to set class on
// SVG element, where class name is read-only.
@ -339,15 +358,15 @@ function $CompileProvider($provide) {
* @param {NodeList} nodeList an array of nodes to compile
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
* scope argument is auto-generated to the new child of the transcluded parent scope.
* @param {DOMElement=} rootElement If the nodeList is the root of the compilation tree then the
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
* rootElement must be set the jqLite collection of the compile root. This is
* needed so that the jqLite collection items can be replaced with widgets.
* @param {number=} max directive priority
* @returns {?function} A composite linking function of all of the matched directives or null.
*/
function compileNodes(nodeList, transcludeFn, rootElement, maxPriority) {
var linkingFns = [],
directiveLinkingFn, childLinkingFn, directives, attrs, linkingFnFound;
function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) {
var linkFns = [],
nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
for(var i = 0; i < nodeList.length; i++) {
attrs = new Attributes();
@ -355,41 +374,41 @@ function $CompileProvider($provide) {
// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
directives = collectDirectives(nodeList[i], [], attrs, maxPriority);
directiveLinkingFn = (directives.length)
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, rootElement)
nodeLinkFn = (directives.length)
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
: null;
childLinkingFn = (directiveLinkingFn && directiveLinkingFn.terminal)
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal)
? null
: compileNodes(nodeList[i].childNodes,
directiveLinkingFn ? directiveLinkingFn.transclude : transcludeFn);
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
linkingFns.push(directiveLinkingFn);
linkingFns.push(childLinkingFn);
linkingFnFound = (linkingFnFound || directiveLinkingFn || childLinkingFn);
linkFns.push(nodeLinkFn);
linkFns.push(childLinkFn);
linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
}
// return a linking function if we have found anything, null otherwise
return linkingFnFound ? linkingFn : null;
return linkFnFound ? compositeLinkFn : null;
/* nodesetLinkingFn */ function linkingFn(scope, nodeList, rootElement, boundTranscludeFn) {
var childLinkingFn, directiveLinkingFn, node, childScope, childTransclusionFn;
function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn;
for(var i=0, n=0, ii=linkingFns.length; i<ii; n++) {
for(var i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
node = nodeList[n];
directiveLinkingFn = /* directiveLinkingFn */ linkingFns[i++];
childLinkingFn = /* nodesetLinkingFn */ linkingFns[i++];
nodeLinkFn = linkFns[i++];
childLinkFn = linkFns[i++];
if (directiveLinkingFn) {
if (directiveLinkingFn.scope) {
childScope = scope.$new(isObject(directiveLinkingFn.scope));
if (nodeLinkFn) {
if (nodeLinkFn.scope) {
childScope = scope.$new(isObject(nodeLinkFn.scope));
jqLite(node).data('$scope', childScope);
} else {
childScope = scope;
}
childTransclusionFn = directiveLinkingFn.transclude;
if (childTransclusionFn || (!boundTranscludeFn && transcludeFn)) {
directiveLinkingFn(childLinkingFn, childScope, node, rootElement,
childTranscludeFn = nodeLinkFn.transclude;
if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
nodeLinkFn(childLinkFn, childScope, node, $rootElement,
(function(transcludeFn) {
return function(cloneFn) {
var transcludeScope = scope.$new();
@ -397,13 +416,13 @@ function $CompileProvider($provide) {
return transcludeFn(transcludeScope, cloneFn).
bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
};
})(childTransclusionFn || transcludeFn)
})(childTranscludeFn || transcludeFn)
);
} else {
directiveLinkingFn(childLinkingFn, childScope, node, undefined, boundTranscludeFn);
nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
}
} else if (childLinkingFn) {
childLinkingFn(scope, node.childNodes, undefined, boundTranscludeFn);
} else if (childLinkFn) {
childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
}
}
}
@ -493,48 +512,47 @@ function $CompileProvider($provide) {
*
* @param {Array} directives Array of collected directives to execute their compile function.
* this needs to be pre-sorted by priority order.
* @param {Node} templateNode The raw DOM node to apply the compile functions to
* @param {Node} compileNode The raw DOM node to apply the compile functions to
* @param {Object} templateAttrs The shared attribute function
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
* scope argument is auto-generated to the new child of the transcluded parent scope.
* @param {DOMElement} rootElement If we are working on the root of the compile tree then this
* @param {DOMElement} $rootElement If we are working on the root of the compile tree then this
* argument has the root jqLite array so that we can replace widgets on it.
* @returns linkingFn
* @returns linkFn
*/
function applyDirectivesToNode(directives, templateNode, templateAttrs, transcludeFn, rootElement) {
function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) {
var terminalPriority = -Number.MAX_VALUE,
preLinkingFns = [],
postLinkingFns = [],
preLinkFns = [],
postLinkFns = [],
newScopeDirective = null,
newIsolatedScopeDirective = null,
templateDirective = null,
delayedLinkingFn = null,
element = templateAttrs.$$element = jqLite(templateNode),
$compileNode = templateAttrs.$$element = jqLite(compileNode),
directive,
directiveName,
template,
$template,
transcludeDirective,
childTranscludeFn = transcludeFn,
controllerDirectives,
linkingFn,
linkFn,
directiveValue;
// executes all directives on the current element
for(var i = 0, ii = directives.length; i < ii; i++) {
directive = directives[i];
template = undefined;
$template = undefined;
if (terminalPriority > directive.priority) {
break; // prevent further processing of directives
}
if (directiveValue = directive.scope) {
assertNoDuplicate('isolated scope', newIsolatedScopeDirective, directive, element);
assertNoDuplicate('isolated scope', newIsolatedScopeDirective, directive, $compileNode);
if (isObject(directiveValue)) {
safeAddClass(element, 'ng-isolate-scope');
safeAddClass($compileNode, 'ng-isolate-scope');
newIsolatedScopeDirective = directive;
}
safeAddClass(element, 'ng-scope');
safeAddClass($compileNode, 'ng-scope');
newScopeDirective = newScopeDirective || directive;
}
@ -543,34 +561,35 @@ function $CompileProvider($provide) {
if (directiveValue = directive.controller) {
controllerDirectives = controllerDirectives || {};
assertNoDuplicate("'" + directiveName + "' controller",
controllerDirectives[directiveName], directive, element);
controllerDirectives[directiveName], directive, $compileNode);
controllerDirectives[directiveName] = directive;
}
if (directiveValue = directive.transclude) {
assertNoDuplicate('transclusion', transcludeDirective, directive, element);
assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode);
transcludeDirective = directive;
terminalPriority = directive.priority;
if (directiveValue == 'element') {
template = jqLite(templateNode);
templateNode = (element = templateAttrs.$$element = jqLite(
'<!-- ' + directiveName + ': ' + templateAttrs[directiveName] + ' -->'))[0];
replaceWith(rootElement, jqLite(template[0]), templateNode);
childTranscludeFn = compile(template, transcludeFn, terminalPriority);
$template = jqLite(compileNode);
$compileNode = templateAttrs.$$element =
jqLite('<!-- ' + directiveName + ': ' + templateAttrs[directiveName] + ' -->');
compileNode = $compileNode[0];
replaceWith($rootElement, jqLite($template[0]), compileNode);
childTranscludeFn = compile($template, transcludeFn, terminalPriority);
} else {
template = jqLite(JQLiteClone(templateNode));
element.html(''); // clear contents
childTranscludeFn = compile(template.contents(), transcludeFn);
$template = jqLite(JQLiteClone(compileNode)).contents();
$compileNode.html(''); // clear contents
childTranscludeFn = compile($template, transcludeFn);
}
}
if (directiveValue = directive.template) {
assertNoDuplicate('template', templateDirective, directive, element);
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;
templateNode = jqLite(directiveValue)[0];
compileNode = jqLite(directiveValue)[0];
if (directive.replace) {
replaceWith(rootElement, element, templateNode);
replaceWith($rootElement, $compileNode, compileNode);
var newTemplateAttrs = {$attr: {}};
@ -581,7 +600,7 @@ function $CompileProvider($provide) {
// - append the second group with new directives to the first group
directives = directives.concat(
collectDirectives(
templateNode,
compileNode,
directives.splice(i + 1, directives.length - (i + 1)),
newTemplateAttrs
)
@ -590,59 +609,58 @@ function $CompileProvider($provide) {
ii = directives.length;
} else {
element.html(directiveValue);
$compileNode.html(directiveValue);
}
}
if (directive.templateUrl) {
assertNoDuplicate('template', templateDirective, directive, element);
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;
delayedLinkingFn = compileTemplateUrl(directives.splice(i, directives.length - i),
/* directiveLinkingFn */ compositeLinkFn, element, templateAttrs, rootElement,
directive.replace, childTranscludeFn);
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
nodeLinkFn, $compileNode, templateAttrs, $rootElement, directive.replace,
childTranscludeFn);
ii = directives.length;
} else if (directive.compile) {
try {
linkingFn = directive.compile(element, templateAttrs, childTranscludeFn);
if (isFunction(linkingFn)) {
addLinkingFns(null, linkingFn);
} else if (linkingFn) {
addLinkingFns(linkingFn.pre, linkingFn.post);
linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
if (isFunction(linkFn)) {
addLinkFns(null, linkFn);
} else if (linkFn) {
addLinkFns(linkFn.pre, linkFn.post);
}
} catch (e) {
$exceptionHandler(e, startingTag(element));
$exceptionHandler(e, startingTag($compileNode));
}
}
if (directive.terminal) {
compositeLinkFn.terminal = true;
nodeLinkFn.terminal = true;
terminalPriority = Math.max(terminalPriority, directive.priority);
}
}
linkingFn = delayedLinkingFn || compositeLinkFn;
linkingFn.scope = newScopeDirective && newScopeDirective.scope;
linkingFn.transclude = transcludeDirective && childTranscludeFn;
nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope;
nodeLinkFn.transclude = transcludeDirective && childTranscludeFn;
// if we have templateUrl, then we have to delay linking
return linkingFn;
// might be normal or delayed nodeLinkFn depending on if templateUrl is present
return nodeLinkFn;
////////////////////
function addLinkingFns(pre, post) {
function addLinkFns(pre, post) {
if (pre) {
pre.require = directive.require;
preLinkingFns.push(pre);
preLinkFns.push(pre);
}
if (post) {
post.require = directive.require;
postLinkingFns.push(post);
postLinkFns.push(post);
}
}
function getControllers(require, element) {
function getControllers(require, $element) {
var value, retrievalMethod = 'data', optional = false;
if (isString(require)) {
while((value = require.charAt(0)) == '^' || value == '?') {
@ -652,7 +670,7 @@ function $CompileProvider($provide) {
}
optional = optional || value == '?';
}
value = element[retrievalMethod]('$' + require + 'Controller');
value = $element[retrievalMethod]('$' + require + 'Controller');
if (!value && !optional) {
throw Error("No controller: " + require);
}
@ -660,24 +678,22 @@ function $CompileProvider($provide) {
} else if (isArray(require)) {
value = [];
forEach(require, function(require) {
value.push(getControllers(require, element));
value.push(getControllers(require, $element));
});
}
return value;
}
/* directiveLinkingFn */
function compositeLinkFn(/* nodesetLinkingFn */ childLinkingFn,
scope, linkNode, rootElement, boundTranscludeFn) {
var attrs, element, i, ii, linkingFn, controller;
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
var attrs, $element, i, ii, linkFn, controller;
if (templateNode === linkNode) {
if (compileNode === linkNode) {
attrs = templateAttrs;
} else {
attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr));
}
element = attrs.$$element;
$element = attrs.$$element;
if (newScopeDirective && isObject(newScopeDirective.scope)) {
forEach(newScopeDirective.scope, function(mode, name) {
@ -690,7 +706,7 @@ function $CompileProvider($provide) {
forEach(controllerDirectives, function(directive) {
var locals = {
$scope: scope,
$element: element,
$element: $element,
$attrs: attrs,
$transclude: boundTranscludeFn
};
@ -706,34 +722,34 @@ function $CompileProvider($provide) {
controller = attrs[directive.name];
}
element.data(
$element.data(
'$' + directive.name + 'Controller',
$controller(controller, locals));
});
}
// PRELINKING
for(i = 0, ii = preLinkingFns.length; i < ii; i++) {
for(i = 0, ii = preLinkFns.length; i < ii; i++) {
try {
linkingFn = preLinkingFns[i];
linkingFn(scope, element, attrs,
linkingFn.require && getControllers(linkingFn.require, element));
linkFn = preLinkFns[i];
linkFn(scope, $element, attrs,
linkFn.require && getControllers(linkFn.require, $element));
} catch (e) {
$exceptionHandler(e, startingTag(element));
$exceptionHandler(e, startingTag($element));
}
}
// RECURSION
childLinkingFn && childLinkingFn(scope, linkNode.childNodes, undefined, boundTranscludeFn);
childLinkFn && childLinkFn(scope, linkNode.childNodes, undefined, boundTranscludeFn);
// POSTLINKING
for(i = 0, ii = postLinkingFns.length; i < ii; i++) {
for(i = 0, ii = postLinkFns.length; i < ii; i++) {
try {
linkingFn = postLinkingFns[i];
linkingFn(scope, element, attrs,
linkingFn.require && getControllers(linkingFn.require, element));
linkFn = postLinkFns[i];
linkFn(scope, $element, attrs,
linkFn.require && getControllers(linkFn.require, $element));
} catch (e) {
$exceptionHandler(e, startingTag(element));
$exceptionHandler(e, startingTag($element));
}
}
}
@ -758,7 +774,7 @@ function $CompileProvider($provide) {
var match = false;
if (hasDirectives.hasOwnProperty(name)) {
for(var directive, directives = $injector.get(name + Suffix),
i=0, ii = directives.length; i<ii; i++) {
i = 0, ii = directives.length; i<ii; i++) {
try {
directive = directives[i];
if ( (maxPriority === undefined || maxPriority > directive.priority) &&
@ -784,7 +800,7 @@ function $CompileProvider($provide) {
function mergeTemplateAttributes(dst, src) {
var srcAttr = src.$attr,
dstAttr = dst.$attr,
element = dst.$$element;
$element = dst.$$element;
// reapply the old attributes to the new element
forEach(dst, function(value, key) {
if (key.charAt(0) != '$') {
@ -797,9 +813,9 @@ function $CompileProvider($provide) {
// copy the new attributes on the old attrs object
forEach(src, function(value, key) {
if (key == 'class') {
safeAddClass(element, value);
safeAddClass($element, value);
} else if (key == 'style') {
element.attr('style', element.attr('style') + ';' + value);
$element.attr('style', $element.attr('style') + ';' + value);
} else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
dst[key] = value;
dstAttr[key] = srcAttr[key];
@ -808,71 +824,70 @@ function $CompileProvider($provide) {
}
function compileTemplateUrl(directives, /* directiveLinkingFn */ beforeWidgetLinkFn,
tElement, tAttrs, rootElement, replace, transcludeFn) {
function compileTemplateUrl(directives, beforeTemplateNodeLinkFn, $compileNode, tAttrs,
$rootElement, replace, childTranscludeFn) {
var linkQueue = [],
afterWidgetLinkFn,
afterWidgetChildrenLinkFn,
originalWidgetNode = tElement[0],
asyncWidgetDirective = directives.shift(),
afterTemplateNodeLinkFn,
afterTemplateChildLinkFn,
origCompileNode = $compileNode[0],
origAsyncDirective = directives.shift(),
// The fact that we have to copy and patch the directive seems wrong!
syncWidgetDirective = extend({}, asyncWidgetDirective, {
derivedSyncDirective = extend({}, origAsyncDirective, {
controller: null, templateUrl: null, transclude: null
}),
html = tElement.html();
});
tElement.html('');
$compileNode.html('');
$http.get(asyncWidgetDirective.templateUrl, {cache: $templateCache}).
$http.get(origAsyncDirective.templateUrl, {cache: $templateCache}).
success(function(content) {
if (replace && !content.match(HAS_ROOT_ELEMENT)) {
throw Error('Template must have exactly one root element: ' + content);
}
var templateNode, tempTemplateAttrs;
var compileNode, tempTemplateAttrs;
if (replace) {
tempTemplateAttrs = {$attr: {}};
templateNode = jqLite(content)[0];
replaceWith(rootElement, tElement, templateNode);
collectDirectives(tElement[0], directives, tempTemplateAttrs);
compileNode = jqLite(content)[0];
replaceWith($rootElement, $compileNode, compileNode);
collectDirectives(compileNode, directives, tempTemplateAttrs);
mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
} else {
templateNode = tElement[0];
tElement.html(content);
compileNode = origCompileNode;
$compileNode.html(content);
}
directives.unshift(syncWidgetDirective);
afterWidgetLinkFn = /* directiveLinkingFn */ applyDirectivesToNode(directives, tElement, tAttrs, transcludeFn);
afterWidgetChildrenLinkFn = /* nodesetLinkingFn */ compileNodes(tElement.contents(), transcludeFn);
directives.unshift(derivedSyncDirective);
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, tAttrs, childTranscludeFn);
afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn);
while(linkQueue.length) {
var controller = linkQueue.pop(),
linkRootElement = linkQueue.pop(),
cLinkNode = linkQueue.pop(),
origLinkNode = linkQueue.pop(),
scope = linkQueue.pop(),
node = templateNode,
cLinkNodeJq = jqLite(cLinkNode);
linkNode = compileNode,
$origLinkNode = jqLite(origLinkNode);
if (cLinkNode !== originalWidgetNode) {
if (origLinkNode !== origCompileNode) {
// it was cloned therefore we have to clone as well.
node = JQLiteClone(templateNode);
replaceWith(linkRootElement, jqLite(cLinkNode), node);
linkNode = JQLiteClone(compileNode);
replaceWith(linkRootElement, $origLinkNode, linkNode);
}
if (replace) {
if (cLinkNodeJq.data('$scope')) {
if ($origLinkNode.data('$scope')) {
// if the original element before replacement had a new scope, the replacement should
// get it as well
jqLite(node).data('$scope', scope);
jqLite(linkNode).data('$scope', scope);
}
dealoc(cLinkNodeJq);
dealoc($origLinkNode);
}
afterWidgetLinkFn(function() {
beforeWidgetLinkFn(afterWidgetChildrenLinkFn, scope, node, rootElement, controller);
}, scope, node, rootElement, controller);
afterTemplateNodeLinkFn(function() {
beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, controller);
}, scope, linkNode, $rootElement, controller);
}
linkQueue = null;
}).
@ -880,16 +895,15 @@ function $CompileProvider($provide) {
throw Error('Failed to load template: ' + config.url);
});
return /* directiveLinkingFn */ function(ignoreChildLinkingFn, scope, node, rootElement,
controller) {
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, controller) {
if (linkQueue) {
linkQueue.push(scope);
linkQueue.push(node);
linkQueue.push(rootElement);
linkQueue.push(controller);
} else {
afterWidgetLinkFn(function() {
beforeWidgetLinkFn(afterWidgetChildrenLinkFn, scope, node, rootElement, controller);
afterTemplateNodeLinkFn(function() {
beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, controller);
}, scope, node, rootElement, controller);
}
};
@ -963,28 +977,28 @@ function $CompileProvider($provide) {
* This is a special jqLite.replaceWith, which can replace items which
* have no parents, provided that the containing jqLite collection is provided.
*
* @param {JqLite=} rootElement The root of the compile tree. Used so that we can replace nodes
* @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
* in the root of the tree.
* @param {JqLite} element The jqLite element which we are going to replace. We keep the shell,
* @param {JqLite} $element The jqLite element which we are going to replace. We keep the shell,
* but replace its DOM node reference.
* @param {Node} newNode The new DOM node.
*/
function replaceWith(rootElement, element, newNode) {
var oldNode = element[0],
function replaceWith($rootElement, $element, newNode) {
var oldNode = $element[0],
parent = oldNode.parentNode,
i, ii;
if (rootElement) {
for(i = 0, ii = rootElement.length; i<ii; i++) {
if (rootElement[i] == oldNode) {
rootElement[i] = newNode;
if ($rootElement) {
for(i = 0, ii = $rootElement.length; i < ii; i++) {
if ($rootElement[i] == oldNode) {
$rootElement[i] = newNode;
}
}
}
if (parent) {
parent.replaceChild(newNode, oldNode);
}
element[0] = newNode;
$element[0] = newNode;
}
}];
}

View file

@ -1088,6 +1088,21 @@ describe('$compile', function() {
}));
it('should allow creation of new scopes for replace directives with templates in a repeater',
inject(function($rootScope, $compile, log, $httpBackend) {
$httpBackend.expect('GET', 'trscope.html').
respond('<p><a log>{{name}}; scopeId: {{$id}} |</a></p>');
element = $compile('<div><span ng-repeat="i in [1,2,3]" trscope></span></div>')($rootScope);
$httpBackend.flush();
expect(log).toEqual('LOG; log-003-002; 003; LOG; log-005-004; 005; LOG; log-007-006; 007');
$rootScope.name = 'Jozo';
$rootScope.$apply();
expect(element.text()).toBe('Jozo; scopeId: 003 |Jozo; scopeId: 005 |Jozo; scopeId: 007 |');
expect(element.find('p').scope().$id).toBe('003');
expect(element.find('a').scope().$id).toBe('003');
}));
it('should allow creation of new isolated scopes for directives with templates', inject(
function($rootScope, $compile, log, $httpBackend) {
$httpBackend.expect('GET', 'tiscope.html').respond('<a log></a>');