diff --git a/src/service/interpolate.js b/src/service/interpolate.js
index 03692824..6d3ae868 100644
--- a/src/service/interpolate.js
+++ b/src/service/interpolate.js
@@ -1,82 +1,145 @@
'use strict';
-function $InterpolateProvider(){
- this.$get = ['$parse', function($parse){
- return function(text, templateOnly) {
- var bindings = parseBindings(text);
- if (hasBindings(bindings) || !templateOnly) {
- return compileBindTemplate(text);
+/**
+ * @ngdoc function
+ * @name angular.module.ng.$interpolateProvider
+ * @function
+ *
+ * @description
+ *
+ * Used for configuring the interpolation markup. Deafults to `{{` and `}}`.
+ */
+function $InterpolateProvider() {
+ var startSymbol = '{{';
+ var endSymbol = '}}';
+
+ /**
+ * @ngdoc method
+ * @name angular.module.ng.$interpolateProvider#startSymbol
+ * @methodOf angular.module.ng.$interpolateProvider
+ * @description
+ * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
+ *
+ * @prop {string=} value new value to set the starting symbol to.
+ */
+ this.startSymbol = function(value){
+ if (value) {
+ startSymbol = value;
+ return this;
+ } else {
+ return startSymbol;
+ }
+ };
+
+ /**
+ * @ngdoc method
+ * @name angular.module.ng.$interpolateProvider#endSymbol
+ * @methodOf angular.module.ng.$interpolateProvider
+ * @description
+ * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
+ *
+ * @prop {string=} value new value to set the ending symbol to.
+ */
+ this.endSymbol = function(value){
+ if (value) {
+ endSymbol = value;
+ return this;
+ } else {
+ return startSymbol;
+ }
+ };
+
+
+ this.$get = ['$parse', function($parse) {
+ var startSymbolLength = startSymbol.length,
+ endSymbolLength = endSymbol.length;
+
+ /**
+ * @ngdoc function
+ * @name angular.module.ng.$interpolate
+ * @function
+ *
+ * @requires $parse
+ *
+ * @description
+ *
+ * Compiles a string with markup into an interpolation function. This service is used by the
+ * HTML {@link angular.module.ng.$compile $compile} service for data binding. See
+ * {@link angular.module.ng.$interpolateProvider $interpolateProvider} for configuring the
+ * interpolation markup.
+ *
+ *
+
+ var $interpolate = ...; // injected
+ var exp = $interpolate('Hello {{name}}!');
+ expect(exp({name:'Angular'}).toEqual('Hello Angular!');
+
+ *
+ *
+ * @param {string} text The text with markup to interpolate.
+ * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
+ * embedded expression in order to return an interpolation function. Strings with no
+ * embedded expression will return null for the interpolation function.
+ * @returns {function(context)} an interpolation function which is used to compute the interpolated
+ * string. The function has these parameters:
+ *
+ * * `context`: an object against which any expressions embedded in the strings are evaluated
+ * against.
+ *
+ */
+ return function(text, mustHaveExpression) {
+ var startIndex,
+ endIndex,
+ index = 0,
+ parts = [],
+ length = text.length,
+ hasInterpolation = false,
+ fn,
+ exp,
+ concat = [];
+
+ while(index < length) {
+ if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) &&
+ ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) {
+ (index != startIndex) && parts.push(text.substring(index, startIndex));
+ parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex)));
+ fn.exp = exp;
+ index = endIndex + endSymbolLength;
+ hasInterpolation = true;
+ } else {
+ // we did not find anything, so we have to add the remainder to the parts array
+ (index != length) && parts.push(text.substring(index));
+ index = length;
+ }
+ }
+
+ if (!(length = parts.length)) {
+ // we added, nothing, must have been an empty string.
+ parts.push('');
+ length = 1;
+ }
+
+ if (!mustHaveExpression || hasInterpolation) {
+ concat.length = length;
+ fn = function(context) {
+ for(var i = 0, ii = length, part; i -1) {
- if (lastIndex < index)
- results.push(string.substr(lastIndex, index - lastIndex));
- lastIndex = index;
-
- index = string.indexOf('}}', index);
- index = index < 0 ? string.length : index + 2;
-
- results.push(string.substr(lastIndex, index - lastIndex));
- lastIndex = index;
- }
- if (lastIndex != string.length)
- results.push(string.substr(lastIndex, string.length - lastIndex));
- return results.length === 0 ? [ string ] : results;
-}
-
-function binding(string) {
- var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/);
- return binding ? binding[1] : null;
-}
-
-function hasBindings(bindings) {
- return bindings.length > 1 || binding(bindings[0]) !== null;
-}
diff --git a/test/markupSpec.js b/test/markupSpec.js
index e2e11f7f..0dcbbfe9 100644
--- a/test/markupSpec.js
+++ b/test/markupSpec.js
@@ -170,91 +170,5 @@ describe("markups", function() {
expect(sortedHtml(element)).toEqual('');
dealoc(element);
}));
-
- it('should Parse Text With No Bindings', inject(function($rootScope, $compile) {
- var parts = parseBindings("a");
- expect(parts.length).toBe(1);
- expect(parts[0]).toBe("a");
- expect(binding(parts[0])).toBeFalsy();
- }));
-
- it('should Parse Empty Text', inject(function($rootScope, $compile) {
- var parts = parseBindings("");
- expect(parts.length).toBe(1);
- expect(parts[0]).toBe("");
- expect(binding(parts[0])).toBeFalsy();
- }));
-
- it('should Parse Inner Binding', inject(function($rootScope, $compile) {
- var parts = parseBindings("a{{b}}C");
- expect(parts.length).toBe(3);
- expect(parts[0]).toBe("a");
- expect(binding(parts[0])).toBeFalsy();
- expect(parts[1]).toBe("{{b}}");
- expect(binding(parts[1])).toBe("b");
- expect(parts[2]).toBe("C");
- expect(binding(parts[2])).toBeFalsy();
- }));
-
- it('should Parse Ending Binding', inject(function($rootScope, $compile) {
- var parts = parseBindings("a{{b}}");
- expect(parts.length).toBe(2);
- expect(parts[0]).toBe("a");
- expect(binding(parts[0])).toBeFalsy();
- expect(parts[1]).toBe("{{b}}");
- expect(binding(parts[1])).toBe("b");
- }));
-
- it('should Parse Begging Binding', inject(function($rootScope, $compile) {
- var parts = parseBindings("{{b}}c");
- expect(parts.length).toBe(2);
- expect(parts[0]).toBe("{{b}}");
- expect(binding(parts[0])).toBe("b");
- expect(parts[1]).toBe("c");
- expect(binding(parts[1])).toBeFalsy();
- }));
-
- it('should Parse Loan Binding', inject(function($rootScope, $compile) {
- var parts = parseBindings("{{b}}");
- expect(parts.length).toBe(1);
- expect(parts[0]).toBe("{{b}}");
- expect(binding(parts[0])).toBe("b");
- }));
-
- it('should Parse Two Bindings', inject(function($rootScope, $compile) {
- var parts = parseBindings("{{b}}{{c}}");
- expect(parts.length).toBe(2);
- expect(parts[0]).toBe("{{b}}");
- expect(binding(parts[0])).toBe("b");
- expect(parts[1]).toBe("{{c}}");
- expect(binding(parts[1])).toBe("c");
- }));
-
- it('should Parse Two Bindings With Text In Middle', inject(function($rootScope, $compile) {
- var parts = parseBindings("{{b}}x{{c}}");
- expect(parts.length).toBe(3);
- expect(parts[0]).toBe("{{b}}");
- expect(binding(parts[0])).toBe("b");
- expect(parts[1]).toBe("x");
- expect(binding(parts[1])).toBeFalsy();
- expect(parts[2]).toBe("{{c}}");
- expect(binding(parts[2])).toBe("c");
- }));
-
- it('should Parse Multiline', inject(function($rootScope, $compile) {
- var parts = parseBindings('"X\nY{{A\nB}}C\nD"');
- expect(binding('{{A\nB}}')).toBeTruthy();
- expect(parts.length).toBe(3);
- expect(parts[0]).toBe('"X\nY');
- expect(parts[1]).toBe('{{A\nB}}');
- expect(parts[2]).toBe('C\nD"');
- }));
-
- it('should Has Binding', inject(function($rootScope, $compile) {
- expect(hasBindings(parseBindings("{{a}}"))).toBe(true);
- expect(hasBindings(parseBindings("a"))).toBeFalsy();
- expect(hasBindings(parseBindings("{{b}}x{{c}}"))).toBe(true);
- }));
-
});
diff --git a/test/service/interpolateSpec.js b/test/service/interpolateSpec.js
index 8644ee0a..d5f251ea 100644
--- a/test/service/interpolateSpec.js
+++ b/test/service/interpolateSpec.js
@@ -13,9 +13,101 @@ describe('$interpolate', function() {
expect($interpolate('some text', true)).toBeUndefined();
}));
+ it('should suppress falsy objects', inject(function($interpolate) {
+ expect($interpolate('{{undefined}}')()).toEqual('');
+ expect($interpolate('{{null}}')()).toEqual('');
+ expect($interpolate('{{a.b}}')()).toEqual('');
+ }));
+
+ it('should jsonify objects', inject(function($interpolate) {
+ expect($interpolate('{{ {} }}')()).toEqual('{}');
+ expect($interpolate('{{ true }}')()).toEqual('true');
+ expect($interpolate('{{ false }}')()).toEqual('false');
+ }));
+
it('should return interpolation function', inject(function($interpolate, $rootScope) {
$rootScope.name = 'Misko';
expect($interpolate('Hello {{name}}!')($rootScope)).toEqual('Hello Misko!');
}));
+
+ describe('provider', function() {
+ beforeEach(module(function($interpolateProvider) {
+ $interpolateProvider.startSymbol('--');
+ $interpolateProvider.endSymbol('--');
+ }));
+
+ it('should not get confused with same markers', inject(function($interpolate) {
+ expect($interpolate('---').parts).toEqual(['---']);
+ expect($interpolate('----')()).toEqual('');
+ expect($interpolate('--1--')()).toEqual('1');
+ }));
+ });
+
+ describe('parseBindings', function() {
+ it('should Parse Text With No Bindings', inject(function($interpolate) {
+ var parts = $interpolate("a").parts;
+ expect(parts.length).toEqual(1);
+ expect(parts[0]).toEqual("a");
+ }));
+
+ it('should Parse Empty Text', inject(function($interpolate) {
+ var parts = $interpolate("").parts;
+ expect(parts.length).toEqual(1);
+ expect(parts[0]).toEqual("");
+ }));
+
+ it('should Parse Inner Binding', inject(function($interpolate) {
+ var parts = $interpolate("a{{b}}C").parts;
+ expect(parts.length).toEqual(3);
+ expect(parts[0]).toEqual("a");
+ expect(parts[1].exp).toEqual("b");
+ expect(parts[1]({b:123})).toEqual(123);
+ expect(parts[2]).toEqual("C");
+ }));
+
+ it('should Parse Ending Binding', inject(function($interpolate) {
+ var parts = $interpolate("a{{b}}").parts;
+ expect(parts.length).toEqual(2);
+ expect(parts[0]).toEqual("a");
+ expect(parts[1].exp).toEqual("b");
+ expect(parts[1]({b:123})).toEqual(123);
+ }));
+
+ it('should Parse Begging Binding', inject(function($interpolate) {
+ var parts = $interpolate("{{b}}c").parts;
+ expect(parts.length).toEqual(2);
+ expect(parts[0].exp).toEqual("b");
+ expect(parts[1]).toEqual("c");
+ }));
+
+ it('should Parse Loan Binding', inject(function($interpolate) {
+ var parts = $interpolate("{{b}}").parts;
+ expect(parts.length).toEqual(1);
+ expect(parts[0].exp).toEqual("b");
+ }));
+
+ it('should Parse Two Bindings', inject(function($interpolate) {
+ var parts = $interpolate("{{b}}{{c}}").parts;
+ expect(parts.length).toEqual(2);
+ expect(parts[0].exp).toEqual("b");
+ expect(parts[1].exp).toEqual("c");
+ }));
+
+ it('should Parse Two Bindings With Text In Middle', inject(function($interpolate) {
+ var parts = $interpolate("{{b}}x{{c}}").parts;
+ expect(parts.length).toEqual(3);
+ expect(parts[0].exp).toEqual("b");
+ expect(parts[1]).toEqual("x");
+ expect(parts[2].exp).toEqual("c");
+ }));
+
+ it('should Parse Multiline', inject(function($interpolate) {
+ var parts = $interpolate('"X\nY{{A\n+B}}C\nD"').parts;
+ expect(parts.length).toEqual(3);
+ expect(parts[0]).toEqual('"X\nY');
+ expect(parts[1].exp).toEqual('A\n+B');
+ expect(parts[2]).toEqual('C\nD"');
+ }));
+ });
});