aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/JSON.js149
-rw-r--r--src/ng/directive/input.js4
-rw-r--r--src/ng/filter/filters.js2
-rw-r--r--src/ng/filter/limitTo.js10
-rw-r--r--test/JsonSpec.js133
5 files changed, 51 insertions, 247 deletions
diff --git a/src/JSON.js b/src/JSON.js
index bcb5fabe..e6d48c86 100644
--- a/src/JSON.js
+++ b/src/JSON.js
@@ -1,5 +1,20 @@
'use strict';
+var jsonReplacer = function(key, value) {
+ var val = value;
+ if (/^\$+/.test(key)) {
+ val = undefined;
+ } else if (isWindow(value)) {
+ val = '$WINDOW';
+ } else if (value && document === value) {
+ val = '$DOCUMENT';
+ } else if (isScope(value)) {
+ val = '$SCOPE';
+ }
+
+ return val;
+};
+
/**
* @ngdoc function
* @name angular.toJson
@@ -13,9 +28,7 @@
* @returns {string} Jsonified string representing `obj`.
*/
function toJson(obj, pretty) {
- var buf = [];
- toJsonArray(buf, obj, pretty ? "\n " : null, []);
- return buf.join('');
+ return JSON.stringify(obj, jsonReplacer, pretty ? ' ' : null);
}
/**
@@ -34,133 +47,3 @@ function fromJson(json) {
? JSON.parse(json)
: json;
}
-
-
-function jsonDateToString(date){
- if (!date) return date;
- var isoString = date.toISOString ? date.toISOString() : '';
- return (isoString.length==24)
- ? isoString
- : padNumber(date.getUTCFullYear(), 4) + '-' +
- padNumber(date.getUTCMonth() + 1, 2) + '-' +
- padNumber(date.getUTCDate(), 2) + 'T' +
- padNumber(date.getUTCHours(), 2) + ':' +
- padNumber(date.getUTCMinutes(), 2) + ':' +
- padNumber(date.getUTCSeconds(), 2) + '.' +
- padNumber(date.getUTCMilliseconds(), 3) + 'Z';
-}
-
-function quoteUnicode(string) {
- var chars = ['"'];
- for ( var i = 0; i < string.length; i++) {
- var code = string.charCodeAt(i);
- var ch = string.charAt(i);
- switch(ch) {
- case '"': chars.push('\\"'); break;
- case '\\': chars.push('\\\\'); break;
- case '\n': chars.push('\\n'); break;
- case '\f': chars.push('\\f'); break;
- case '\r': chars.push(ch = '\\r'); break;
- case '\t': chars.push(ch = '\\t'); break;
- default:
- if (32 <= code && code <= 126) {
- chars.push(ch);
- } else {
- var encode = "000" + code.toString(16);
- chars.push("\\u" + encode.substring(encode.length - 4));
- }
- }
- }
- chars.push('"');
- return chars.join('');
- }
-
-
-function toJsonArray(buf, obj, pretty, stack) {
- if (isObject(obj)) {
- if (obj === window) {
- buf.push('WINDOW');
- return;
- }
-
- if (obj === document) {
- buf.push('DOCUMENT');
- return;
- }
-
- if (includes(stack, obj)) {
- buf.push('RECURSION');
- return;
- }
- stack.push(obj);
- }
- if (obj === null) {
- buf.push('null');
- } else if (obj instanceof RegExp) {
- buf.push(quoteUnicode(obj.toString()));
- } else if (isFunction(obj)) {
- return;
- } else if (isBoolean(obj)) {
- buf.push('' + obj);
- } else if (isNumber(obj)) {
- if (isNaN(obj)) {
- buf.push('null');
- } else {
- buf.push('' + obj);
- }
- } else if (isString(obj)) {
- return buf.push(quoteUnicode(obj));
- } else if (isObject(obj)) {
- if (isArray(obj)) {
- buf.push("[");
- var len = obj.length;
- var sep = false;
- for(var i=0; i<len; i++) {
- var item = obj[i];
- if (sep) buf.push(",");
- if (!(item instanceof RegExp) && (isFunction(item) || isUndefined(item))) {
- buf.push('null');
- } else {
- toJsonArray(buf, item, pretty, stack);
- }
- sep = true;
- }
- buf.push("]");
- } else if (isElement(obj)) {
- // TODO(misko): maybe in dev mode have a better error reporting?
- buf.push('DOM_ELEMENT');
- } else if (isDate(obj)) {
- buf.push(quoteUnicode(jsonDateToString(obj)));
- } else {
- buf.push("{");
- if (pretty) buf.push(pretty);
- var comma = false;
- var childPretty = pretty ? pretty + " " : false;
- var keys = [];
- for(var k in obj) {
- if (k!='this' && k!='$parent' && k.substring(0,2) != '$$' && obj.hasOwnProperty(k) && obj[k] !== undefined) {
- keys.push(k);
- }
- }
- keys.sort();
- for ( var keyIndex = 0; keyIndex < keys.length; keyIndex++) {
- var key = keys[keyIndex];
- var value = obj[key];
- if (!isFunction(value)) {
- if (comma) {
- buf.push(",");
- if (pretty) buf.push(pretty);
- }
- buf.push(quoteUnicode(key));
- buf.push(":");
- toJsonArray(buf, value, childPretty, stack);
- comma = true;
- }
- }
- buf.push("}");
- }
- }
- if (isObject(obj)) {
- stack.pop();
- }
-}
diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js
index 348c9f25..7343c358 100644
--- a/src/ng/directive/input.js
+++ b/src/ng/directive/input.js
@@ -671,7 +671,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
</doc:source>
<doc:scenario>
it('should initialize to model', function() {
- expect(binding('user')).toEqual('{"last":"visitor","name":"guest"}');
+ expect(binding('user')).toEqual('{"name":"guest","last":"visitor"}');
expect(binding('myForm.userName.$valid')).toEqual('true');
expect(binding('myForm.$valid')).toEqual('true');
});
@@ -685,7 +685,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
it('should be valid if empty when min length is set', function() {
input('user.last').enter('');
- expect(binding('user')).toEqual('{"last":"","name":"guest"}');
+ expect(binding('user')).toEqual('{"name":"guest","last":""}');
expect(binding('myForm.lastName.$valid')).toEqual('true');
expect(binding('myForm.$valid')).toEqual('true');
});
diff --git a/src/ng/filter/filters.js b/src/ng/filter/filters.js
index c792cace..19f79ad1 100644
--- a/src/ng/filter/filters.js
+++ b/src/ng/filter/filters.js
@@ -407,7 +407,7 @@ function dateFilter($locale) {
</doc:source>
<doc:scenario>
it('should jsonify filtered objects', function() {
- expect(binding("{'name':'value'}")).toBe('{\n "name":"value"}');
+ expect(binding("{'name':'value'}")).toMatch(/\{\n "name": ?"value"\n}/);
});
</doc:scenario>
</doc:example>
diff --git a/src/ng/filter/limitTo.js b/src/ng/filter/limitTo.js
index 4928fb9a..af94b2f4 100644
--- a/src/ng/filter/limitTo.js
+++ b/src/ng/filter/limitTo.js
@@ -31,24 +31,24 @@
}
</script>
<div ng-controller="Ctrl">
- Limit {{numbers}} to: <input type="integer" ng-model="limit"/>
- <p>Output: {{ numbers | limitTo:limit | json }}</p>
+ Limit {{numbers}} to: <input type="integer" ng-model="limit" ng-model-instant>
+ <p>Output: {{ numbers | limitTo:limit }}</p>
</div>
</doc:source>
<doc:scenario>
it('should limit the numer array to first three items', function() {
expect(element('.doc-example-live input[ng-model=limit]').val()).toBe('3');
- expect(binding('numbers | limitTo:limit | json')).toEqual('[1,2,3]');
+ expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3]');
});
it('should update the output when -3 is entered', function() {
input('limit').enter(-3);
- expect(binding('numbers | limitTo:limit | json')).toEqual('[7,8,9]');
+ expect(binding('numbers | limitTo:limit')).toEqual('[7,8,9]');
});
it('should not exceed the maximum size of input array', function() {
input('limit').enter(100);
- expect(binding('numbers | limitTo:limit | json')).toEqual('[1,2,3,4,5,6,7,8,9]');
+ expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3,4,5,6,7,8,9]');
});
</doc:scenario>
</doc:example>
diff --git a/test/JsonSpec.js b/test/JsonSpec.js
index e4c27455..5ca3e42e 100644
--- a/test/JsonSpec.js
+++ b/test/JsonSpec.js
@@ -4,7 +4,7 @@ describe('json', function() {
describe('fromJson', function() {
- it('should delegate to native parser', function() {
+ it('should delegate to JSON.parse', function() {
var spy = spyOn(JSON, 'parse').andCallThrough();
expect(fromJson('{}')).toEqual({});
@@ -13,125 +13,46 @@ describe('json', function() {
});
- it('should serialize primitives', function() {
- expect(toJson(0/0)).toEqual('null');
- expect(toJson(null)).toEqual('null');
- expect(toJson(true)).toEqual('true');
- expect(toJson(false)).toEqual('false');
- expect(toJson(123.45)).toEqual('123.45');
- expect(toJson('abc')).toEqual('"abc"');
- expect(toJson('a \t \n \r b \\')).toEqual('"a \\t \\n \\r b \\\\"');
- });
-
- it('should not serialize $$properties', function() {
- expect(toJson({$$some:'value', 'this':1, '$parent':1}, false)).toEqual('{}');
- });
-
- it('should not serialize this or $parent', function() {
- expect(toJson({'this':'value', $parent:'abc'}, false)).toEqual('{}');
- });
+ describe('toJson', function() {
- it('should serialize strings with escaped characters', function() {
- expect(toJson("7\\\"7")).toEqual("\"7\\\\\\\"7\"");
- });
-
- it('should serialize objects', function() {
- expect(toJson({a: 1, b: 2})).toEqual('{"a":1,"b":2}');
- expect(toJson({a: {b: 2}})).toEqual('{"a":{"b":2}}');
- expect(toJson({a: {b: {c: 0}}})).toEqual('{"a":{"b":{"c":0}}}');
- expect(toJson({a: {b: 0/0}})).toEqual('{"a":{"b":null}}');
- });
-
- it('should format objects pretty', function() {
- expect(toJson({a: 1, b: 2}, true)).toEqual('{\n "a":1,\n "b":2}');
- expect(toJson({a: {b: 2}}, true)).toEqual('{\n "a":{\n "b":2}}');
- });
+ it('should delegate to JSON.stringify', function() {
+ var spy = spyOn(JSON, 'stringify').andCallThrough();
- it('should serialize array', function() {
- expect(toJson([])).toEqual('[]');
- expect(toJson([1, 'b'])).toEqual('[1,"b"]');
- });
-
- it('should serialize RegExp', function() {
- expect(toJson(/foo/)).toEqual('"/foo/"');
- expect(toJson([1, new RegExp('foo')])).toEqual('[1,"/foo/"]');
- });
-
- it('should ignore functions', function() {
- expect(toJson([function() {},1])).toEqual('[null,1]');
- expect(toJson({a:function() {}})).toEqual('{}');
- });
-
- it('should serialize array with empty items', function() {
- var a = [];
- a[1] = 'X';
- expect(toJson(a)).toEqual('[null,"X"]');
- });
-
- it('should escape unicode', function() {
- expect('\u00a0'.length).toEqual(1);
- expect(toJson('\u00a0').length).toEqual(8);
- expect(fromJson(toJson('\u00a0')).length).toEqual(1);
- });
-
- it('should serialize UTC dates', function() {
- var date = new angular.mock.TzDate(-1, '2009-10-09T01:02:03.027Z');
- expect(toJson(date)).toEqual('"2009-10-09T01:02:03.027Z"');
- });
-
- it('should prevent recursion', function() {
- var obj = {a: 'b'};
- obj.recursion = obj;
- expect(angular.toJson(obj)).toEqual('{"a":"b","recursion":RECURSION}');
- });
+ expect(toJson({})).toEqual('{}');
+ expect(spy).toHaveBeenCalled();
+ });
- it('should serialize $ properties', function() {
- var obj = {$a: 'a'};
- expect(angular.toJson(obj)).toEqual('{"$a":"a"}');
- });
- it('should NOT serialize inherited properties', function() {
- // This is what native Browser does
- var obj = inherit({p:'p'});
- obj.a = 'a';
- expect(angular.toJson(obj)).toEqual('{"a":"a"}');
- });
+ it('should format objects pretty', function() {
+ expect(toJson({a: 1, b: 2}, true)).
+ toBeOneOf('{\n "a": 1,\n "b": 2\n}', '{\n "a":1,\n "b":2\n}');
+ expect(toJson({a: {b: 2}}, true)).
+ toBeOneOf('{\n "a": {\n "b": 2\n }\n}', '{\n "a":{\n "b":2\n }\n}');
+ });
- it('should serialize same objects multiple times', function() {
- var obj = {a:'b'};
- expect(angular.toJson({A:obj, B:obj})).toEqual('{"A":{"a":"b"},"B":{"a":"b"}}');
- });
- it('should not serialize undefined values', function() {
- expect(angular.toJson({A:undefined})).toEqual('{}');
- });
+ it('should not serialize properties starting with $', function() {
+ expect(toJson({$few: 'v', $$some:'value'}, false)).toEqual('{}');
+ });
- it('should not serialize $window object', function() {
- expect(toJson(window)).toEqual('WINDOW');
- });
- it('should not serialize $document object', function() {
- expect(toJson(document)).toEqual('DOCUMENT');
- });
+ it('should not serialize undefined values', function() {
+ expect(angular.toJson({A:undefined})).toEqual('{}');
+ });
- describe('string', function() {
- it('should quote', function() {
- expect(quoteUnicode('a')).toBe('"a"');
- expect(quoteUnicode('\\')).toBe('"\\\\"');
- expect(quoteUnicode("'a'")).toBe('"\'a\'"');
- expect(quoteUnicode('"a"')).toBe('"\\"a\\""');
- expect(quoteUnicode('\n\f\r\t')).toBe('"\\n\\f\\r\\t"');
+ it('should not serialize $window object', function() {
+ expect(toJson(window)).toEqual('"$WINDOW"');
});
- it('should quote slashes', function() {
- expect(quoteUnicode("7\\\"7")).toBe('"7\\\\\\\"7"');
- });
- it('should quote unicode', function() {
- expect(quoteUnicode('abc\u00A0def')).toBe('"abc\\u00a0def"');
+ it('should not serialize $document object', function() {
+ expect(toJson(document)).toEqual('"$DOCUMENT"');
});
- });
+ it('should not serialize scope instances', inject(function($rootScope) {
+ expect(toJson({key: $rootScope})).toEqual('{"key":"$SCOPE"}');
+ }));
+ });
});