Skip to content

Commit 9e87102

Browse files
committed
Making Interpolation Symbols Configurable
1 parent 8e61789 commit 9e87102

File tree

4 files changed

+111
-11
lines changed

4 files changed

+111
-11
lines changed

src/compile.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,15 @@ function $CompileProvider($provide) {
101101
this.$get = ['$injector', '$parse', '$controller', '$rootScope', '$http', '$interpolate',
102102
function($injector, $parse, $controller, $rootScope, $http, $interpolate) {
103103

104+
var startSymbol = $interpolate.startSymbol();
105+
var endSymbol = $interpolate.endSymbol();
106+
var denormalizeTemplate = (startSymbol === '{{' && endSymbol === '}}') ?
107+
_.identity :
108+
function(template) {
109+
return template.replace(/\{\{/g, startSymbol)
110+
.replace(/\}\}/g, endSymbol);
111+
};
112+
104113
function Attributes(element) {
105114
this.$$element = element;
106115
this.$attr = {};
@@ -253,9 +262,14 @@ function $CompileProvider($provide) {
253262
if (!transcludedScope) {
254263
transcludedScope = scope.$new(false, containingScope);
255264
}
256-
return linkFn.nodeLinkFn.transclude(transcludedScope, cloneAttachFn, {
257-
transcludeControllers: transcludeControllers
265+
var didTransclude = linkFn.nodeLinkFn.transclude(transcludedScope, cloneAttachFn, {
266+
transcludeControllers: transcludeControllers,
267+
parentBoundTranscludeFn: parentBoundTranscludeFn
258268
});
269+
if (didTransclude.length === 0 && parentBoundTranscludeFn) {
270+
didTransclude = parentBoundTranscludeFn(transcludedScope, cloneAttachFn);
271+
}
272+
return didTransclude;
259273
};
260274
} else if (parentBoundTranscludeFn) {
261275
boundTranscludeFn = parentBoundTranscludeFn;
@@ -503,6 +517,7 @@ function $CompileProvider($provide) {
503517
var linkQueue = [];
504518
$compileNode.empty();
505519
$http.get(templateUrl).success(function(template) {
520+
template = denormalizeTemplate(template);
506521
directives.unshift(derivedSyncDirective);
507522
$compileNode.html(template);
508523
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, attrs, previousCompileContext);
@@ -641,9 +656,11 @@ function $CompileProvider($provide) {
641656
throw 'Multiple directives asking for template';
642657
}
643658
templateDirective = directive;
644-
$compileNode.html(_.isFunction(directive.template) ?
659+
var template = _.isFunction(directive.template) ?
645660
directive.template($compileNode, attrs) :
646-
directive.template);
661+
directive.template;
662+
template = denormalizeTemplate(template);
663+
$compileNode.html(template);
647664
}
648665
if (directive.templateUrl) {
649666
if (templateDirective) {

src/interpolate.js

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,26 @@
22
'use strict';
33

44
function $InterpolateProvider() {
5+
var startSymbol = '{{';
6+
var endSymbol = '}}';
7+
8+
this.startSymbol = function(value) {
9+
if (value) {
10+
startSymbol = value;
11+
return this;
12+
} else {
13+
return startSymbol;
14+
}
15+
};
16+
17+
this.endSymbol = function(value) {
18+
if (value) {
19+
endSymbol = value;
20+
return this;
21+
} else {
22+
return endSymbol;
23+
}
24+
};
525

626
function stringify(value) {
727
if (_.isNull(value) || _.isUndefined(value)) {
@@ -13,12 +33,18 @@ function $InterpolateProvider() {
1333
}
1434
}
1535

16-
function unescapeText(text) {
17-
return text.replace(/\\{\\{/g, '{{')
18-
.replace(/\\}\\}/g, '}}');
36+
function escapeChar(char) {
37+
return '\\\\\\' + char;
1938
}
2039

2140
this.$get = ['$parse', function($parse) {
41+
var escapedStartMatcher = new RegExp(startSymbol.replace(/./g, escapeChar), 'g');
42+
var escapedEndMatcher = new RegExp(endSymbol.replace(/./g, escapeChar), 'g');
43+
44+
function unescapeText(text) {
45+
return text.replace(escapedStartMatcher, startSymbol)
46+
.replace(escapedEndMatcher, endSymbol);
47+
}
2248

2349
function $interpolate(text, mustHaveExpressions) {
2450
var index = 0;
@@ -28,21 +54,21 @@ function $InterpolateProvider() {
2854
var expressionPositions = [];
2955
var startIndex, endIndex, exp, expFn;
3056
while (index < text.length) {
31-
startIndex = text.indexOf('{{', index);
57+
startIndex = text.indexOf(startSymbol, index);
3258
if (startIndex !== -1) {
33-
endIndex = text.indexOf('}}', startIndex + 2);
59+
endIndex = text.indexOf(endSymbol, startIndex + startSymbol.length);
3460
}
3561
if (startIndex !== -1 && endIndex !== -1) {
3662
if (startIndex !== index) {
3763
parts.push(unescapeText(text.substring(index, startIndex)));
3864
}
39-
exp = text.substring(startIndex + 2, endIndex);
65+
exp = text.substring(startIndex + startSymbol.length, endIndex);
4066
expFn = $parse(exp);
4167
expressions.push(exp);
4268
expressionFns.push(expFn);
4369
expressionPositions.push(parts.length);
4470
parts.push(expFn);
45-
index = endIndex + 2;
71+
index = endIndex + endSymbol.length;
4672
} else {
4773
parts.push(unescapeText(text.substring(index)));
4874
break;
@@ -82,6 +108,9 @@ function $InterpolateProvider() {
82108

83109
}
84110

111+
$interpolate.startSymbol = _.constant(startSymbol);
112+
$interpolate.endSymbol = _.constant(endSymbol);
113+
85114
return $interpolate;
86115
}];
87116

test/compile_spec.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3790,6 +3790,24 @@ describe('$compile', function() {
37903790
});
37913791
});
37923792

3793+
it('denormalizes directive templates', function() {
3794+
var injector = createInjector(['ng', function($interpolateProvider, $compileProvider) {
3795+
$interpolateProvider.startSymbol('[[').endSymbol(']]');
3796+
$compileProvider.directive('myDirective', function() {
3797+
return {
3798+
template: 'Value is {{myExpr}}'
3799+
};
3800+
});
3801+
}]);
3802+
injector.invoke(function($compile, $rootScope) {
3803+
var el = $('<div my-directive></div>');
3804+
$rootScope.myExpr = 42;
3805+
$compile(el)($rootScope);
3806+
$rootScope.$apply();
3807+
3808+
expect(el.html()).toEqual('Value is 42');
3809+
});
3810+
});
37933811

37943812
});
37953813

test/interpolate_spec.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,40 @@ describe('$interpolate', function() {
143143
expect(listenerSpy.calls.mostRecent().args[1]).toEqual('42');
144144
});
145145

146+
it('allows configuring start and end symbols', function() {
147+
var injector = createInjector(['ng', function($interpolateProvider) {
148+
$interpolateProvider.startSymbol('FOO').endSymbol('OOF');
149+
}]);
150+
var $interpolate = injector.get('$interpolate');
151+
expect($interpolate.startSymbol()).toEqual('FOO');
152+
expect($interpolate.endSymbol()).toEqual('OOF');
153+
});
154+
155+
it('works with start and end symbols that differ from default', function() {
156+
var injector = createInjector(['ng', function($interpolateProvider) {
157+
$interpolateProvider.startSymbol('FOO').endSymbol('OOF');
158+
}]);
159+
var $interpolate = injector.get('$interpolate');
160+
var interpFn = $interpolate('FOOmyExprOOF');
161+
expect(interpFn({myExpr: 42})).toEqual('42');
162+
});
163+
164+
it('does not work with default start and end symbols when reconfigured', function() {
165+
var injector = createInjector(['ng', function($interpolateProvider) {
166+
$interpolateProvider.startSymbol('FOO').endSymbol('OOF');
167+
}]);
168+
var $interpolate = injector.get('$interpolate');
169+
var interpFn = $interpolate('{{myExpr}}');
170+
expect(interpFn({myExpr: 42})).toEqual('{{myExpr}}');
171+
});
172+
173+
it('supports unescaping for reconfigured symbols', function() {
174+
var injector = createInjector(['ng', function($interpolateProvider) {
175+
$interpolateProvider.startSymbol('FOO').endSymbol('OOF');
176+
}]);
177+
var $interpolate = injector.get('$interpolate');
178+
var interpFn = $interpolate('\\F\\O\\OmyExpr\\O\\O\\F');
179+
expect(interpFn({})).toEqual('FOOmyExprOOF');
180+
});
181+
146182
});

0 commit comments

Comments
 (0)