diff --git a/src/JSON.js b/src/JSON.js index bcb5fabeca00..e6d48c861533 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 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 c792cacefbcf..19f79ad1a1a6 100644 --- a/src/ng/filter/filters.js +++ b/src/ng/filter/filters.js @@ -407,7 +407,7 @@ function dateFilter($locale) { it('should jsonify filtered objects', function() { - expect(binding("{'name':'value'}")).toBe('{\n "name":"value"}'); + expect(binding("{'name':'value'}")).toMatch(/\{\n "name": ?"value"\n}/); }); diff --git a/src/ng/filter/limitTo.js b/src/ng/filter/limitTo.js index 4928fb9a7d6e..af94b2f4a758 100644 --- a/src/ng/filter/limitTo.js +++ b/src/ng/filter/limitTo.js @@ -31,24 +31,24 @@ }
- Limit {{numbers}} to: -

Output: {{ numbers | limitTo:limit | json }}

+ Limit {{numbers}} to: +

Output: {{ numbers | limitTo:limit }}

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]'); }); diff --git a/test/JsonSpec.js b/test/JsonSpec.js index e4c2745508d1..5ca3e42eb863 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"}'); + })); + }); });