diff --git a/angularFiles.js b/angularFiles.js index 54bbd4fa..f692ceb0 100755 --- a/angularFiles.js +++ b/angularFiles.js @@ -5,6 +5,7 @@ var angularFiles = { 'src/minErr.js', 'src/Angular.js', 'src/loader.js', + 'src/stringify.js', 'src/AngularPublic.js', 'src/jqLite.js', 'src/apis.js', @@ -73,6 +74,7 @@ var angularFiles = { ], 'angularLoader': [ + 'stringify.js', 'src/minErr.js', 'src/loader.js' ], diff --git a/src/.jshintrc b/src/.jshintrc index 5e364938..9500d330 100644 --- a/src/.jshintrc +++ b/src/.jshintrc @@ -19,6 +19,7 @@ "angularModule": false, "nodeName_": false, "uid": false, + "toDebugString": false, "REGEX_STRING_REGEXP" : false, "lowercase": false, diff --git a/src/minErr.js b/src/minErr.js index 96171915..5bf4ec84 100644 --- a/src/minErr.js +++ b/src/minErr.js @@ -37,31 +37,14 @@ function minErr(module, ErrorConstructor) { prefix = '[' + (module ? module + ':' : '') + code + '] ', template = arguments[1], templateArgs = arguments, - stringify = function(obj) { - if (typeof obj === 'function') { - return obj.toString().replace(/ \{[\s\S]*$/, ''); - } else if (typeof obj === 'undefined') { - return 'undefined'; - } else if (typeof obj !== 'string') { - return JSON.stringify(obj); - } - return obj; - }, + message, i; message = prefix + template.replace(/\{\d+\}/g, function(match) { var index = +match.slice(1, -1), arg; if (index + 2 < templateArgs.length) { - arg = templateArgs[index + 2]; - if (typeof arg === 'function') { - return arg.toString().replace(/ ?\{[\s\S]*$/, ''); - } else if (typeof arg === 'undefined') { - return 'undefined'; - } else if (typeof arg !== 'string') { - return toJson(arg); - } - return arg; + return toDebugString(templateArgs[index + 2]); } return match; }); @@ -70,7 +53,7 @@ function minErr(module, ErrorConstructor) { (module ? module + '/' : '') + code; for (i = 2; i < arguments.length; i++) { message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' + - encodeURIComponent(stringify(arguments[i])); + encodeURIComponent(toDebugString(arguments[i])); } return new ErrorConstructor(message); }; diff --git a/src/stringify.js b/src/stringify.js new file mode 100644 index 00000000..2c1a343c --- /dev/null +++ b/src/stringify.js @@ -0,0 +1,29 @@ +'use strict'; + +/* global: toDebugString: true */ + +function serializeObject(obj) { + var seen = []; + + return JSON.stringify(obj, function(key, val) { + val = toJsonReplacer(key, val); + if (isObject(val)) { + + if (seen.indexOf(val) >= 0) return '<>'; + + seen.push(val); + } + return val; + }); +} + +function toDebugString(obj) { + if (typeof obj === 'function') { + return obj.toString().replace(/ \{[\s\S]*$/, ''); + } else if (typeof obj === 'undefined') { + return 'undefined'; + } else if (typeof obj !== 'string') { + return serializeObject(obj); + } + return obj; +} diff --git a/test/.jshintrc b/test/.jshintrc index f767bd22..6bca8382 100644 --- a/test/.jshintrc +++ b/test/.jshintrc @@ -18,6 +18,7 @@ "angularModule": false, "nodeName_": false, "uid": false, + "toDebugString": false, "lowercase": false, "uppercase": false, diff --git a/test/minErrSpec.js b/test/minErrSpec.js index f0e67359..848188c9 100644 --- a/test/minErrSpec.js +++ b/test/minErrSpec.js @@ -60,6 +60,13 @@ describe('minErr', function() { toMatch(/^\[test:26\] false: false; zero: 0; null: null; undefined: undefined; emptyStr: /); }); + it('should handle arguments that are objects with cyclic references', function() { + var a = { b: { } }; + a.b.a = a; + + var myError = testError('26', 'a is {0}', a); + expect(myError.message).toMatch(/a is {"b":{"a":"<>"}}/); + }); it('should preserve interpolation markers when fewer arguments than needed are provided', function() { // this way we can easily see if we are passing fewer args than needed diff --git a/test/stringifySpec.js b/test/stringifySpec.js new file mode 100644 index 00000000..e849b3e8 --- /dev/null +++ b/test/stringifySpec.js @@ -0,0 +1,15 @@ +'use strict'; + +describe('toDebugString', function() { + it('should convert its argument to a string', function() { + expect(toDebugString('string')).toEqual('string'); + expect(toDebugString(123)).toEqual('123'); + expect(toDebugString({a:{b:'c'}})).toEqual('{"a":{"b":"c"}}'); + expect(toDebugString(function fn() { var a = 10; })).toEqual('function fn()'); + expect(toDebugString()).toEqual('undefined'); + var a = { }; + a.a = a; + expect(toDebugString(a)).toEqual('{"a":"<>"}'); + expect(toDebugString([a,a])).toEqual('[{"a":"<>"},"<>"]'); + }); +});