Skip to content

Commit

Permalink
Making Interpolation Symbols Configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
teropa committed Jun 15, 2015
1 parent ec20908 commit dda6618
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 12 deletions.
27 changes: 22 additions & 5 deletions src/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ function $CompileProvider($provide) {
this.$get = ['$injector', '$parse', '$controller', '$rootScope', '$http', '$interpolate',
function($injector, $parse, $controller, $rootScope, $http, $interpolate) {

var startSymbol = $interpolate.startSymbol();
var endSymbol = $interpolate.endSymbol();
var denormalizeTemplate = (startSymbol === '{{' && endSymbol === '}}') ?
_.identity :
function(template) {
return template.replace(/\{\{/g, startSymbol)
.replace(/\}\}/g, endSymbol);
};

function Attributes(element) {
this.$$element = element;
this.$attr = {};
Expand Down Expand Up @@ -239,9 +248,14 @@ function $CompileProvider($provide) {
if (!transcludedScope) {
transcludedScope = scope.$new(false, containingScope);
}
return linkFn.nodeLinkFn.transclude(transcludedScope, cloneAttachFn, {
transcludeControllers: transcludeControllers
var didTransclude = linkFn.nodeLinkFn.transclude(transcludedScope, cloneAttachFn, {
transcludeControllers: transcludeControllers,
parentBoundTranscludeFn: parentBoundTranscludeFn
});
if (didTransclude.length === 0 && parentBoundTranscludeFn) {
didTransclude = parentBoundTranscludeFn(transcludedScope, cloneAttachFn);
}
return didTransclude;
};
} else if (parentBoundTranscludeFn) {
boundTranscludeFn = parentBoundTranscludeFn;
Expand Down Expand Up @@ -403,7 +417,7 @@ function $CompileProvider($provide) {
if (/^(on[a-z]+|formaction)$/.test(name)) {
throw 'Interpolations for HTML DOM event attributes not allowed';
}

var newValue = attrs[name];
if (newValue !== value) {
interpolateFn = newValue && $interpolate(newValue, true);
Expand Down Expand Up @@ -437,6 +451,7 @@ function $CompileProvider($provide) {
var linkQueue = [];
$compileNode.empty();
$http.get(templateUrl).success(function(template) {
template = denormalizeTemplate(template);
directives.unshift(derivedSyncDirective);
$compileNode.html(template);
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, attrs, previousCompileContext);
Expand Down Expand Up @@ -575,9 +590,11 @@ function $CompileProvider($provide) {
throw 'Multiple directives asking for template';
}
templateDirective = directive;
$compileNode.html(_.isFunction(directive.template) ?
var template = _.isFunction(directive.template) ?
directive.template($compileNode, attrs) :
directive.template);
directive.template;
template = denormalizeTemplate(template);
$compileNode.html(template);
}
if (directive.templateUrl) {
if (templateDirective) {
Expand Down
43 changes: 36 additions & 7 deletions src/interpolate.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@
'use strict';

function $InterpolateProvider() {
var startSymbol = '{{';
var endSymbol = '}}';

this.startSymbol = function(value) {
if (value) {
startSymbol = value;
return this;
} else {
return startSymbol;
}
};

this.endSymbol = function(value) {
if (value) {
endSymbol = value;
return this;
} else {
return endSymbol;
}
};

function stringify(value) {
if (_.isNull(value) || _.isUndefined(value)) {
Expand All @@ -13,12 +33,18 @@ function $InterpolateProvider() {
}
}

function unescapeText(text) {
return text.replace(/\\{\\{/g, '{{')
.replace(/\\}\\}/g, '}}');
function escapeChar(char) {
return '\\\\\\' + char;
}

this.$get = ['$parse', function($parse) {
var escapedStartMatcher = new RegExp(startSymbol.replace(/./g, escapeChar), 'g');
var escapedEndMatcher = new RegExp(endSymbol.replace(/./g, escapeChar), 'g');

function unescapeText(text) {
return text.replace(escapedStartMatcher, startSymbol)
.replace(escapedEndMatcher, endSymbol);
}

function $interpolate(text, mustHaveExpressions) {
var index = 0;
Expand All @@ -28,21 +54,21 @@ function $InterpolateProvider() {
var expressionPositions = [];
var startIndex, endIndex, exp, expFn;
while (index < text.length) {
startIndex = text.indexOf('{{', index);
startIndex = text.indexOf(startSymbol, index);
if (startIndex !== -1) {
endIndex = text.indexOf('}}', startIndex + 2);
endIndex = text.indexOf(endSymbol, startIndex + startSymbol.length);
}
if (startIndex !== -1 && endIndex !== -1) {
if (startIndex !== index) {
parts.push(unescapeText(text.substring(index, startIndex)));
}
exp = text.substring(startIndex + 2, endIndex);
exp = text.substring(startIndex + startSymbol.length, endIndex);
expFn = $parse(exp);
expressions.push(exp);
expressionFns.push(expFn);
expressionPositions.push(parts.length);
parts.push(expFn);
index = endIndex + 2;
index = endIndex + endSymbol.length;
} else {
parts.push(unescapeText(text.substring(index)));
break;
Expand Down Expand Up @@ -82,6 +108,9 @@ function $InterpolateProvider() {

}

$interpolate.startSymbol = _.constant(startSymbol);
$interpolate.endSymbol = _.constant(endSymbol);

return $interpolate;
}];

Expand Down
101 changes: 101 additions & 0 deletions test/compile_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3135,6 +3135,89 @@ describe('$compile', function() {
});
});

it('works with thing 1', function() {
var injector = makeInjectorWithDirectives({
inner: function() {
return {
transclude: true,
template: '<u ng-transclude></u>'
};
}
});
injector.invoke(function($compile, $rootScope) {
var el = $('<inner>y</inner>');
$compile(el)($rootScope);
expect(el.html()).toEqual('<u ng-transclude="">y</u>');
});
});

it('works with nested transcludes', function() {
var injector = makeInjectorWithDirectives({
inner: function() {
return {
transclude: true,
template: '<u ng-transclude></u>'
};
},
workaround: function() {
return {
transclude: true,
template: '<a href="#"><inner><foo ng-transclude></foo></inner></a>'
};
}
});
injector.invoke(function($compile, $rootScope) {
var el = $('<workaround>y</workaround>');
$compile(el)($rootScope);
expect(el.html()).toEqual('<a href="#"><inner><u ng-transclude=""><foo ng-transclude="">y</foo></u></inner></a>');
});
});

it('works with nested transcludes without wrap', function() {
var injector = makeInjectorWithDirectives({
inner: function() {
return {
transclude: true,
template: '<u my-trans></u>'
};
},
workaround: function() {
return {
transclude: true,
template: '<a href="#"><inner><foo my-trans></foo></inner></a>'
};
},
outer: function() {
return {
transclude: true,
template: '<a href="#"><inner my-trans></inner></a>'
};
},
myTrans: function() {
return {
link: function(scope, element, attrs, ctrl, trans) {
var trcluded = trans();
//console.log('tr', trcluded.length);
element.empty().append(trcluded);
}
};
}
});
injector.invoke(function($compile, $rootScope) {
var el = $('<inner>y</inner>');
$compile(el)($rootScope);
expect(el.html()).toEqual('<u my-trans="">y</u>');

el = $('<workaround>y</workaround>');
$compile(el)($rootScope);
expect(el.html()).toEqual('<a href="#"><inner><u my-trans=""><foo my-trans="">y</foo></u></inner></a>');

el = $('<outer>y</outer>');
$compile(el)($rootScope);
expect(el.html()).toEqual('<a href="#"><inner my-trans=""><u my-trans="">y</u></inner></a>');
});
});

});

describe('clone attach function', function() {
Expand Down Expand Up @@ -3702,6 +3785,24 @@ describe('$compile', function() {
});
});

it('denormalizes directive templates', function() {
var injector = createInjector(['ng', function($interpolateProvider, $compileProvider) {
$interpolateProvider.startSymbol('[[').endSymbol(']]');
$compileProvider.directive('myDirective', function() {
return {
template: 'Value is {{myExpr}}'
};
});
}]);
injector.invoke(function($compile, $rootScope) {
var el = $('<div my-directive></div>');
$rootScope.myExpr = 42;
$compile(el)($rootScope);
$rootScope.$apply();

expect(el.html()).toEqual('Value is 42');
});
});

});

Expand Down
36 changes: 36 additions & 0 deletions test/interpolate_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,40 @@ describe('$interpolate', function() {
expect(listenerSpy.calls.mostRecent().args[1]).toEqual('42');
});

it('allows configuring start and end symbols', function() {
var injector = createInjector(['ng', function($interpolateProvider) {
$interpolateProvider.startSymbol('FOO').endSymbol('OOF');
}]);
var $interpolate = injector.get('$interpolate');
expect($interpolate.startSymbol()).toEqual('FOO');
expect($interpolate.endSymbol()).toEqual('OOF');
});

it('works with start and end symbols that differ from default', function() {
var injector = createInjector(['ng', function($interpolateProvider) {
$interpolateProvider.startSymbol('FOO').endSymbol('OOF');
}]);
var $interpolate = injector.get('$interpolate');
var interpFn = $interpolate('FOOmyExprOOF');
expect(interpFn({myExpr: 42})).toEqual('42');
});

it('does not work with default start and end symbols when reconfigured', function() {
var injector = createInjector(['ng', function($interpolateProvider) {
$interpolateProvider.startSymbol('FOO').endSymbol('OOF');
}]);
var $interpolate = injector.get('$interpolate');
var interpFn = $interpolate('{{myExpr}}');
expect(interpFn({myExpr: 42})).toEqual('{{myExpr}}');
});

it('supports unescaping for reconfigured symbols', function() {
var injector = createInjector(['ng', function($interpolateProvider) {
$interpolateProvider.startSymbol('FOO').endSymbol('OOF');
}]);
var $interpolate = injector.get('$interpolate');
var interpFn = $interpolate('\\F\\O\\OmyExpr\\O\\O\\F');
expect(interpFn({})).toEqual('FOOmyExprOOF');
});

});

0 comments on commit dda6618

Please sign in to comment.