UNPKG

33.4 kBJavaScriptView Raw
1
2/**
3 * should.js by TJ Holowaychuk (MIT), adapted to run in browser and node.
4 */
5
6(function (should) {
7
8 if ('undefined' != typeof exports) {
9 module.exports = exports = should = require('assert');
10 }
11
12 /**
13 * Expose constructor.
14 */
15
16 should.Assertion = Assertion;
17
18 /**
19 * Possible assertion flags.
20 */
21
22 var flags = {
23 not: ['be', 'have', 'include']
24 , an: ['instance']
25 , and: ['be', 'have', 'include', 'an']
26 , be: ['an']
27 , have: ['an', 'own']
28 , include: ['an']
29 , not: ['include', 'have', 'be']
30 , own: []
31 , instance: []
32 };
33
34 /**
35 * Extend Object.prototype.
36 */
37
38 if ('object' == typeof process) {
39 Object.defineProperty(
40 Object.prototype
41 , 'should'
42 , {
43 get: function () {
44 var self = this.valueOf()
45 , fn = function () {
46 return new Assertion(self);
47 };
48
49 if ('undefined' != typeof exports) {
50 fn.__proto__ = exports;
51 fn.exports = exports;
52 }
53
54 return fn;
55 }
56 , enumerable: false
57 }
58 );
59 } else {
60 Object.prototype.should = function () {
61 return new Assertion(this.valueOf());
62 };
63 }
64
65 /**
66 * Constructor
67 *
68 * @api private
69 */
70
71 function Assertion (obj) {
72 if (obj !== undefined) {
73 this.obj = obj;
74 this.flags = {};
75
76 var $flags = keys(flags);
77
78 for (var i = 0, l = $flags.length; i < l; i++) {
79 this[$flags[i]] = new FlaggedAssertion(this, $flags[i]);
80 }
81 }
82 };
83
84 /**
85 * Performs an assertion
86 *
87 * @api private
88 */
89
90 Assertion.prototype.assert = function (truth, msg, error) {
91 var msg = this.flags.not ? error : msg
92 , ok = this.flags.not ? !truth : truth;
93
94 if (!ok) {
95 throw new Error(msg);
96 }
97
98 this.flags = {};
99 };
100
101 /**
102 * Checks if the value is true
103 *
104 * @api public
105 */
106
107 Assertion.prototype.be_true = function () {
108 this.assert(
109 this.obj === true
110 , 'expected ' + i(this.obj) + ' to be true'
111 , 'expected ' + i(this.obj) + ' to not be true');
112 return this;
113 };
114
115 /**
116 * Checks if the value is true
117 *
118 * @api public
119 */
120
121 Assertion.prototype.be_false = function () {
122 this.assert(
123 this.obj === false
124 , 'expected ' + i(this.obj) + ' to be false'
125 , 'expected ' + i(this.obj) + ' to not be false'
126 );
127 return this;
128 };
129
130 /**
131 * Check if the value is truthy
132 *
133 * @api public
134 */
135
136 Assertion.prototype.ok = function () {
137 this.assert(
138 this.obj == true
139 , 'expected ' + i(this.obj) + ' to be true'
140 , 'expected ' + i(this.obj) + ' to not be true');
141 };
142
143 /**
144 * Checks if the array is empty.
145 *
146 * @api public
147 */
148
149 Assertion.prototype.empty = function () {
150 this.obj.should().have.property('length');
151 this.assert(
152 0 === this.obj.length
153 , 'expected ' + i(this.obj) + ' to be empty'
154 , 'expected ' + i(this.obj) + ' to not be empty');
155 return this;
156 };
157
158 /**
159 * Checks if the obj is arguments.
160 *
161 * @api public
162 */
163
164 Assertion.prototype.arguments = function () {
165 this.assert(
166 '[object Arguments]' == Object.prototype.toString.call(this.obj)
167 , 'expected ' + i(this.obj) + ' to be arguments'
168 , 'expected ' + i(this.obj) + ' to not be arguments');
169 return this;
170 };
171
172 /**
173 * Checks if the obj exactly equals another.
174 *
175 * @api public
176 */
177
178 Assertion.prototype.equal = function (obj) {
179 this.assert(
180 obj === this.obj
181 , 'expected ' + i(this.obj) + ' to equal ' + i(obj)
182 , 'expected ' + i(this.obj) + ' to not equal ' + i(obj));
183 return this;
184 };
185
186 /**
187 * Checks if the obj sortof equals another.
188 *
189 * @api public
190 */
191
192 Assertion.prototype.eql = function (obj) {
193 this.assert(
194 should.eql(obj, this.obj)
195 , 'expected ' + i(this.obj) + ' to sort of equal ' + i(obj)
196 , 'expected ' + i(this.obj) + ' to sort of not equal ' + i(obj));
197 return this;
198 };
199
200 /**
201 * Assert within start to finish (inclusive).
202 *
203 * @param {Number} start
204 * @param {Number} finish
205 * @api public
206 */
207
208 Assertion.prototype.within = function (start, finish) {
209 var range = start + '..' + finish;
210 this.assert(
211 this.obj >= start && this.obj <= finish
212 , 'expected ' + i(this.obj) + ' to be within ' + range
213 , 'expected ' + i(this.obj) + ' to not be within ' + range);
214 return this;
215 };
216
217 /**
218 * Assert typeof.
219 *
220 * @api public
221 */
222
223 Assertion.prototype.a = function (type) {
224 this.assert(
225 type == typeof this.obj
226 , 'expected ' + i(this.obj) + ' to be a ' + type
227 , 'expected ' + i(this.obj) + ' not to be a ' + type);
228 return this;
229 };
230
231 /**
232 * Assert instanceof.
233 *
234 * @api public
235 */
236
237 Assertion.prototype.of = function (constructor) {
238 var name = constructor.name;
239 this.assert(
240 this.obj instanceof constructor
241 , 'expected ' + i(this.obj) + ' to be an instance of ' + name
242 , 'expected ' + i(this.obj) + ' not to be an instance of ' + name);
243 return this;
244 };
245
246 /**
247 * Assert numeric value above _n_.
248 *
249 * @param {Number} n
250 * @api public
251 */
252
253 Assertion.prototype.greaterThan =
254 Assertion.prototype.above = function (n) {
255 this.assert(
256 this.obj > n
257 , 'expected ' + i(this.obj) + ' to be above ' + n
258 , 'expected ' + i(this.obj) + ' to be below ' + n);
259 return this;
260 };
261
262 /**
263 * Assert numeric value below _n_.
264 *
265 * @param {Number} n
266 * @api public
267 */
268
269 Assertion.prototype.lessThan =
270 Assertion.prototype.below = function (n) {
271 this.assert(
272 this.obj < n
273 , 'expected ' + i(this.obj) + ' to be below ' + n
274 , 'expected ' + i(this.obj) + ' to be above ' + n);
275 return this;
276 };
277
278 /**
279 * Assert string value matches _regexp_.
280 *
281 * @param {RegExp} regexp
282 * @api public
283 */
284
285 Assertion.prototype.match = function (regexp) {
286 this.assert(
287 regexp.exec(this.obj)
288 , 'expected ' + i(this.obj) + ' to match ' + regexp
289 , 'expected ' + i(this.obj) + ' not to match ' + regexp);
290 return this;
291 };
292
293 /**
294 * Assert property "length" exists and has value of _n_.
295 *
296 * @param {Number} n
297 * @api public
298 */
299
300 Assertion.prototype.length = function (n) {
301 this.obj.should().have.property('length');
302 var len = this.obj.length;
303 this.assert(
304 n == len
305 , 'expected ' + i(this.obj) + ' to have a length of ' + n + ' but got ' + len
306 , 'expected ' + i(this.obj) + ' to not have a length of ' + len);
307 return this;
308 };
309
310 /**
311 * Assert substring.
312 *
313 * @param {String} str
314 * @api public
315 */
316
317 Assertion.prototype.string = function(str){
318 this.obj.should().be.a('string');
319 this.assert(
320 ~this.obj.indexOf(str)
321 , 'expected ' + i(this.obj) + ' to include ' + i(str)
322 , 'expected ' + i(this.obj) + ' to not include ' + i(str));
323 return this;
324 };
325
326 /**
327 * Assert inclusion of object.
328 *
329 * @param {Object} obj
330 * @api public
331 */
332
333 Assertion.prototype.object = function(obj){
334 this.obj.should().be.a('object');
335 var included = true;
336 for (var key in obj) {
337 if (obj.hasOwnProperty(key) && !should.eql(obj[key], this.obj[key])) {
338 included = false;
339 break;
340 }
341 }
342 this.assert(
343 included
344 , 'expected ' + i(this.obj) + ' to include ' + i(obj)
345 , 'expected ' + i(this.obj) + ' to not include ' + i(obj));
346 return this;
347 };
348
349 /**
350 * Assert property _name_ exists, with optional _val_.
351 *
352 * @param {String} name
353 * @param {Mixed} val
354 * @api public
355 */
356
357 Assertion.prototype.property = function (name, val) {
358 if (this.flags.own) {
359 this.assert(
360 this.obj.hasOwnProperty(name)
361 , 'expected ' + i(this.obj) + ' to have own property ' + i(name)
362 , 'expected ' + i(this.obj) + ' to not have own property ' + i(name));
363 return this;
364 }
365
366 if (this.flags.not && undefined !== val) {
367 if (undefined === this.obj[name]) {
368 throw new Error(i(this.obj) + ' has no property ' + i(name));
369 }
370 } else {
371 this.assert(
372 undefined !== this.obj[name]
373 , 'expected ' + i(this.obj) + ' to have a property ' + i(name)
374 , 'expected ' + i(this.obj) + ' to not have a property ' + i(name));
375 }
376
377 if (undefined !== val) {
378 this.assert(
379 val === this.obj[name]
380 , 'expected ' + i(this.obj) + ' to have a property ' + i(name)
381 + ' of ' + i(val) + ', but got ' + i(this.obj[name])
382 , 'expected ' + i(this.obj) + ' to not have a property ' + i(name)
383 + ' of ' + i(val));
384 }
385
386 this.obj = this.obj[name];
387 return this;
388 };
389
390 /**
391 * Assert that the array contains _obj_.
392 *
393 * @param {Mixed} obj
394 * @api public
395 */
396
397 Assertion.prototype.contain = function (obj) {
398 this.obj.should().be.an.instance.of(Array);
399 this.assert(
400 ~indexOf(this.obj, obj)
401 , 'expected ' + i(this.obj) + ' to contain ' + i(obj)
402 , 'expected ' + i(this.obj) + ' to not contain ' + i(obj));
403 return this;
404 };
405
406 /**
407 * Assert exact keys or inclusion of keys by using
408 * the `.include` modifier.
409 *
410 * @param {Array|String ...} keys
411 * @api public
412 */
413
414 Assertion.prototype.key =
415 Assertion.prototype.keys = function (keys) {
416 var str
417 , ok = true;
418
419 keys = keys instanceof Array
420 ? keys
421 : Array.prototype.slice.call(arguments);
422
423 if (!keys.length) throw new Error('keys required');
424
425 var actual = keys(this.obj)
426 , len = keys.length;
427
428 // Inclusion
429 ok = every(keys, function(key){
430 return ~indexOf(actual, key);
431 });
432
433 // Strict
434 if (!this.flags.not && !this.flags.include) {
435 ok = ok && keys.length == actual.length;
436 }
437
438 // Key string
439 if (len > 1) {
440 keys = map(keys, function(key){
441 return i(key);
442 });
443 var last = keys.pop();
444 str = keys.join(', ') + ', and ' + last;
445 } else {
446 str = i(keys[0]);
447 }
448
449 // Form
450 str = (len > 1 ? 'keys ' : 'key ') + str;
451
452 // Have / include
453 str = (this.flag.include ? 'include ' : 'have ') + str;
454
455 // Assertion
456 this.assert(
457 ok
458 , 'expected ' + i(this.obj) + ' to ' + str
459 , 'expected ' + i(this.obj) + ' to not ' + str);
460
461 return this;
462 };
463
464 /**
465 * Assertion with a flag.
466 *
467 * @api private
468 */
469
470 function FlaggedAssertion (parent, flag) {
471 this.parent = parent;
472 this.obj = parent.obj;
473
474 this.flag = flag;
475 this.flags = {};
476 this.flags[flag] = true;
477
478 for (var i in parent.flags) {
479 if (parent.flags.hasOwnProperty(i)) {
480 this.flags[i] = true;
481 }
482 }
483
484 var $flags = flags[flag];
485
486 for (var i = 0, l = $flags.length; i < l; i++) {
487 this[$flags[i]] = new FlaggedAssertion(this, $flags[i]);
488 }
489 };
490
491 /**
492 * Inherits from assertion
493 */
494
495 FlaggedAssertion.prototype = new Assertion;
496
497 /**
498 * Array every compatibility
499 *
500 * @see bit.ly/5Fq1N2
501 * @api public
502 */
503
504 function every (arr, fn, thisObj) {
505 var scope = thisObj || window;
506 for (var i = 0, j = arr.length; i < j; ++i) {
507 if (!fn.call(scope, arr[i], i, arr)) {
508 return false;
509 }
510 }
511 return true;
512 };
513
514 /**
515 * Array indexOf compatibility.
516 *
517 * @see bit.ly/a5Dxa2
518 * @api public
519 */
520
521 function indexOf (arr, o, i) {
522 if (Array.prototype.indexOf) {
523 return Array.prototype.indexOf.call(arr, o, i);
524 }
525
526 for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0
527 ; i < j && arr[i] !== o; i++);
528
529 return j <= i ? -1 : i;
530 };
531
532 /**
533 * Inspects an object.
534 *
535 * @see taken from node.js `util` module (copyright Joyent, MIT license)
536 * @api private
537 */
538
539 function i (obj, showHidden, depth) {
540 var seen = [];
541
542 function stylize (str) {
543 return str;
544 };
545
546 function format (value, recurseTimes) {
547 // Provide a hook for user-specified inspect functions.
548 // Check that value is an object with an inspect function on it
549 if (value && typeof value.inspect === 'function' &&
550 // Filter out the util module, it's inspect function is special
551 value !== exports &&
552 // Also filter out any prototype objects using the circular check.
553 !(value.constructor && value.constructor.prototype === value)) {
554 return value.inspect(recurseTimes);
555 }
556
557 // Primitive types cannot have properties
558 switch (typeof value) {
559 case 'undefined':
560 return stylize('undefined', 'undefined');
561
562 case 'string':
563 var simple = '\'' + json.stringify(value).replace(/^"|"$/g, '')
564 .replace(/'/g, "\\'")
565 .replace(/\\"/g, '"') + '\'';
566 return stylize(simple, 'string');
567
568 case 'number':
569 return stylize('' + value, 'number');
570
571 case 'boolean':
572 return stylize('' + value, 'boolean');
573 }
574 // For some reason typeof null is "object", so special case here.
575 if (value === null) {
576 return stylize('null', 'null');
577 }
578
579 // Look up the keys of the object.
580 var visible_keys = keys(value);
581 var $keys = showHidden ? Object.getOwnPropertyNames(value) : visible_keys;
582
583 // Functions without properties can be shortcutted.
584 if (typeof value === 'function' && $keys.length === 0) {
585 if (isRegExp(value)) {
586 return stylize('' + value, 'regexp');
587 } else {
588 var name = value.name ? ': ' + value.name : '';
589 return stylize('[Function' + name + ']', 'special');
590 }
591 }
592
593 // Dates without properties can be shortcutted
594 if (isDate(value) && $keys.length === 0) {
595 return stylize(value.toUTCString(), 'date');
596 }
597
598 var base, type, braces;
599 // Determine the object type
600 if (isArray(value)) {
601 type = 'Array';
602 braces = ['[', ']'];
603 } else {
604 type = 'Object';
605 braces = ['{', '}'];
606 }
607
608 // Make functions say that they are functions
609 if (typeof value === 'function') {
610 var n = value.name ? ': ' + value.name : '';
611 base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']';
612 } else {
613 base = '';
614 }
615
616 // Make dates with properties first say the date
617 if (isDate(value)) {
618 base = ' ' + value.toUTCString();
619 }
620
621 if ($keys.length === 0) {
622 return braces[0] + base + braces[1];
623 }
624
625 if (recurseTimes < 0) {
626 if (isRegExp(value)) {
627 return stylize('' + value, 'regexp');
628 } else {
629 return stylize('[Object]', 'special');
630 }
631 }
632
633 seen.push(value);
634
635 var output = map($keys, function(key) {
636 var name, str;
637 if (value.__lookupGetter__) {
638 if (value.__lookupGetter__(key)) {
639 if (value.__lookupSetter__(key)) {
640 str = stylize('[Getter/Setter]', 'special');
641 } else {
642 str = stylize('[Getter]', 'special');
643 }
644 } else {
645 if (value.__lookupSetter__(key)) {
646 str = stylize('[Setter]', 'special');
647 }
648 }
649 }
650 if (indexOf(visible_keys, key) < 0) {
651 name = '[' + key + ']';
652 }
653 if (!str) {
654 if (indexOf(seen, value[key]) < 0) {
655 if (recurseTimes === null) {
656 str = format(value[key]);
657 } else {
658 str = format(value[key], recurseTimes - 1);
659 }
660 if (str.indexOf('\n') > -1) {
661 if (isArray(value)) {
662 str = map(str.split('\n'), function(line) {
663 return ' ' + line;
664 }).join('\n').substr(2);
665 } else {
666 str = '\n' + map(str.split('\n'), function(line) {
667 return ' ' + line;
668 }).join('\n');
669 }
670 }
671 } else {
672 str = stylize('[Circular]', 'special');
673 }
674 }
675 if (typeof name === 'undefined') {
676 if (type === 'Array' && key.match(/^\d+$/)) {
677 return str;
678 }
679 name = json.stringify('' + key);
680 if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
681 name = name.substr(1, name.length - 2);
682 name = stylize(name, 'name');
683 } else {
684 name = name.replace(/'/g, "\\'")
685 .replace(/\\"/g, '"')
686 .replace(/(^"|"$)/g, "'");
687 name = stylize(name, 'string');
688 }
689 }
690
691 return name + ': ' + str;
692 });
693
694 seen.pop();
695
696 var numLinesEst = 0;
697 var length = reduce(output, function(prev, cur) {
698 numLinesEst++;
699 if (indexOf(cur, '\n') >= 0) numLinesEst++;
700 return prev + cur.length + 1;
701 }, 0);
702
703 if (length > 50) {
704 output = braces[0] +
705 (base === '' ? '' : base + '\n ') +
706 ' ' +
707 output.join(',\n ') +
708 ' ' +
709 braces[1];
710
711 } else {
712 output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
713 }
714
715 return output;
716 }
717 return format(obj, (typeof depth === 'undefined' ? 2 : depth));
718 };
719
720 function isArray (ar) {
721 return ar instanceof Array ||
722 Object.prototype.toString.call(ar) == '[object Array]';
723 };
724
725 function isRegExp(re) {
726 var s = '' + re;
727 return re instanceof RegExp || // easy case
728 // duck-type for context-switching evalcx case
729 typeof(re) === 'function' &&
730 re.constructor.name === 'RegExp' &&
731 re.compile &&
732 re.test &&
733 re.exec &&
734 s.match(/^\/.*\/[gim]{0,3}$/);
735 };
736
737 function isDate(d) {
738 if (d instanceof Date) return true;
739 return false;
740 };
741
742 function keys (obj) {
743 if (Object.keys) {
744 return Object.keys(obj);
745 }
746
747 var keys = [];
748
749 for (var i in obj) {
750 if (obj.hasOwnProperty(i)) {
751 keys.push(i);
752 }
753 }
754
755 return keys;
756 }
757
758 function map (arr, mapper, that) {
759 if (Array.prototype.map) {
760 return Array.prototype.map.call(arr, mapper, that);
761 }
762
763 var other= new Array(arr.length);
764
765 for (var i= 0, n = arr.length; i<n; i++)
766 if (i in arr)
767 other[i] = mapper.call(that, arr[i], i, arr);
768
769 return other;
770 };
771
772 function reduce (arr, fun) {
773 if (Array.prototype.reduce) {
774 return Array.prototype.reduce.apply(
775 arr
776 , Array.prototype.slice.call(arguments, 1)
777 );
778 }
779
780 var len = +this.length;
781
782 if (typeof fun !== "function")
783 throw new TypeError();
784
785 // no value to return if no initial value and an empty array
786 if (len === 0 && arguments.length === 1)
787 throw new TypeError();
788
789 var i = 0;
790 if (arguments.length >= 2) {
791 var rv = arguments[1];
792 } else {
793 do {
794 if (i in this) {
795 rv = this[i++];
796 break;
797 }
798
799 // if array contains no values, no initial value to return
800 if (++i >= len)
801 throw new TypeError();
802 } while (true);
803 }
804
805 for (; i < len; i++) {
806 if (i in this)
807 rv = fun.call(null, rv, this[i], i, this);
808 }
809
810 return rv;
811 };
812
813 /**
814 * Strict equality
815 *
816 * @api public
817 */
818
819 should.equal = function (a, b) {
820 if (a !== b) {
821 should.fail('expected ' + i(a) + ' to equal ' + i(b));
822 }
823 };
824
825 /**
826 * Fails with msg
827 *
828 * @param {String} msg
829 * @api public
830 */
831
832 should.fail = function (msg) {
833 throw new Error(msg);
834 };
835
836 /**
837 * Asserts deep equality
838 *
839 * @see taken from node.js `assert` module (copyright Joyent, MIT license)
840 * @api private
841 */
842
843 should.eql = function eql (actual, expected) {
844 // 7.1. All identical values are equivalent, as determined by ===.
845 if (actual === expected) {
846 return true;
847 } else if ('undefined' != typeof Buffer
848 && Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
849 if (actual.length != expected.length) return false;
850
851 for (var i = 0; i < actual.length; i++) {
852 if (actual[i] !== expected[i]) return false;
853 }
854
855 return true;
856
857 // 7.2. If the expected value is a Date object, the actual value is
858 // equivalent if it is also a Date object that refers to the same time.
859 } else if (actual instanceof Date && expected instanceof Date) {
860 return actual.getTime() === expected.getTime();
861
862 // 7.3. Other pairs that do not both pass typeof value == "object",
863 // equivalence is determined by ==.
864 } else if (typeof actual != 'object' && typeof expected != 'object') {
865 return actual == expected;
866
867 // 7.4. For all other Object pairs, including Array objects, equivalence is
868 // determined by having the same number of owned properties (as verified
869 // with Object.prototype.hasOwnProperty.call), the same set of keys
870 // (although not necessarily the same order), equivalent values for every
871 // corresponding key, and an identical "prototype" property. Note: this
872 // accounts for both named and indexed properties on Arrays.
873 } else {
874 return objEquiv(actual, expected);
875 }
876 }
877
878 function isUndefinedOrNull (value) {
879 return value === null || value === undefined;
880 }
881
882 function isArguments (object) {
883 return Object.prototype.toString.call(object) == '[object Arguments]';
884 }
885
886 function objEquiv (a, b) {
887 if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
888 return false;
889 // an identical "prototype" property.
890 if (a.prototype !== b.prototype) return false;
891 //~~~I've managed to break Object.keys through screwy arguments passing.
892 // Converting to array solves the problem.
893 if (isArguments(a)) {
894 if (!isArguments(b)) {
895 return false;
896 }
897 a = pSlice.call(a);
898 b = pSlice.call(b);
899 return should.eql(a, b);
900 }
901 try{
902 var ka = keys(a),
903 kb = keys(b),
904 key, i;
905 } catch (e) {//happens when one is a string literal and the other isn't
906 return false;
907 }
908 // having the same number of owned properties (keys incorporates hasOwnProperty)
909 if (ka.length != kb.length)
910 return false;
911 //the same set of keys (although not necessarily the same order),
912 ka.sort();
913 kb.sort();
914 //~~~cheap key test
915 for (i = ka.length - 1; i >= 0; i--) {
916 if (ka[i] != kb[i])
917 return false;
918 }
919 //equivalent values for every corresponding key, and
920 //~~~possibly expensive deep test
921 for (i = ka.length - 1; i >= 0; i--) {
922 key = ka[i];
923 if (!should.eql(a[key], b[key]))
924 return false;
925 }
926 return true;
927 }
928
929 var json = (function () {
930 "use strict";
931
932 if ('object' == typeof JSON && JSON.parse && JSON.stringify) {
933 return {
934 parse: nativeJSON.parse
935 , stringify: nativeJSON.stringify
936 }
937 }
938
939 var JSON = {};
940
941 function f(n) {
942 // Format integers to have at least two digits.
943 return n < 10 ? '0' + n : n;
944 }
945
946 function date(d, key) {
947 return isFinite(d.valueOf()) ?
948 d.getUTCFullYear() + '-' +
949 f(d.getUTCMonth() + 1) + '-' +
950 f(d.getUTCDate()) + 'T' +
951 f(d.getUTCHours()) + ':' +
952 f(d.getUTCMinutes()) + ':' +
953 f(d.getUTCSeconds()) + 'Z' : null;
954 };
955
956 var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
957 escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
958 gap,
959 indent,
960 meta = { // table of character substitutions
961 '\b': '\\b',
962 '\t': '\\t',
963 '\n': '\\n',
964 '\f': '\\f',
965 '\r': '\\r',
966 '"' : '\\"',
967 '\\': '\\\\'
968 },
969 rep;
970
971
972 function quote(string) {
973
974 // If the string contains no control characters, no quote characters, and no
975 // backslash characters, then we can safely slap some quotes around it.
976 // Otherwise we must also replace the offending characters with safe escape
977 // sequences.
978
979 escapable.lastIndex = 0;
980 return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
981 var c = meta[a];
982 return typeof c === 'string' ? c :
983 '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
984 }) + '"' : '"' + string + '"';
985 }
986
987
988 function str(key, holder) {
989
990 // Produce a string from holder[key].
991
992 var i, // The loop counter.
993 k, // The member key.
994 v, // The member value.
995 length,
996 mind = gap,
997 partial,
998 value = holder[key];
999
1000 // If the value has a toJSON method, call it to obtain a replacement value.
1001
1002 if (value instanceof Date) {
1003 value = date(key);
1004 }
1005
1006 // If we were called with a replacer function, then call the replacer to
1007 // obtain a replacement value.
1008
1009 if (typeof rep === 'function') {
1010 value = rep.call(holder, key, value);
1011 }
1012
1013 // What happens next depends on the value's type.
1014
1015 switch (typeof value) {
1016 case 'string':
1017 return quote(value);
1018
1019 case 'number':
1020
1021 // JSON numbers must be finite. Encode non-finite numbers as null.
1022
1023 return isFinite(value) ? String(value) : 'null';
1024
1025 case 'boolean':
1026 case 'null':
1027
1028 // If the value is a boolean or null, convert it to a string. Note:
1029 // typeof null does not produce 'null'. The case is included here in
1030 // the remote chance that this gets fixed someday.
1031
1032 return String(value);
1033
1034 // If the type is 'object', we might be dealing with an object or an array or
1035 // null.
1036
1037 case 'object':
1038
1039 // Due to a specification blunder in ECMAScript, typeof null is 'object',
1040 // so watch out for that case.
1041
1042 if (!value) {
1043 return 'null';
1044 }
1045
1046 // Make an array to hold the partial results of stringifying this object value.
1047
1048 gap += indent;
1049 partial = [];
1050
1051 // Is the value an array?
1052
1053 if (Object.prototype.toString.apply(value) === '[object Array]') {
1054
1055 // The value is an array. Stringify every element. Use null as a placeholder
1056 // for non-JSON values.
1057
1058 length = value.length;
1059 for (i = 0; i < length; i += 1) {
1060 partial[i] = str(i, value) || 'null';
1061 }
1062
1063 // Join all of the elements together, separated with commas, and wrap them in
1064 // brackets.
1065
1066 v = partial.length === 0 ? '[]' : gap ?
1067 '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
1068 '[' + partial.join(',') + ']';
1069 gap = mind;
1070 return v;
1071 }
1072
1073 // If the replacer is an array, use it to select the members to be stringified.
1074
1075 if (rep && typeof rep === 'object') {
1076 length = rep.length;
1077 for (i = 0; i < length; i += 1) {
1078 if (typeof rep[i] === 'string') {
1079 k = rep[i];
1080 v = str(k, value);
1081 if (v) {
1082 partial.push(quote(k) + (gap ? ': ' : ':') + v);
1083 }
1084 }
1085 }
1086 } else {
1087
1088 // Otherwise, iterate through all of the keys in the object.
1089
1090 for (k in value) {
1091 if (Object.prototype.hasOwnProperty.call(value, k)) {
1092 v = str(k, value);
1093 if (v) {
1094 partial.push(quote(k) + (gap ? ': ' : ':') + v);
1095 }
1096 }
1097 }
1098 }
1099
1100 // Join all of the member texts together, separated with commas,
1101 // and wrap them in braces.
1102
1103 v = partial.length === 0 ? '{}' : gap ?
1104 '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
1105 '{' + partial.join(',') + '}';
1106 gap = mind;
1107 return v;
1108 }
1109 }
1110
1111 // If the JSON object does not yet have a stringify method, give it one.
1112
1113 JSON.stringify = function (value, replacer, space) {
1114
1115 // The stringify method takes a value and an optional replacer, and an optional
1116 // space parameter, and returns a JSON text. The replacer can be a function
1117 // that can replace values, or an array of strings that will select the keys.
1118 // A default replacer method can be provided. Use of the space parameter can
1119 // produce text that is more easily readable.
1120
1121 var i;
1122 gap = '';
1123 indent = '';
1124
1125 // If the space parameter is a number, make an indent string containing that
1126 // many spaces.
1127
1128 if (typeof space === 'number') {
1129 for (i = 0; i < space; i += 1) {
1130 indent += ' ';
1131 }
1132
1133 // If the space parameter is a string, it will be used as the indent string.
1134
1135 } else if (typeof space === 'string') {
1136 indent = space;
1137 }
1138
1139 // If there is a replacer, it must be a function or an array.
1140 // Otherwise, throw an error.
1141
1142 rep = replacer;
1143 if (replacer && typeof replacer !== 'function' &&
1144 (typeof replacer !== 'object' ||
1145 typeof replacer.length !== 'number')) {
1146 throw new Error('JSON.stringify');
1147 }
1148
1149 // Make a fake root object containing our value under the key of ''.
1150 // Return the result of stringifying the value.
1151
1152 return str('', {'': value});
1153 };
1154
1155 // If the JSON object does not yet have a parse method, give it one.
1156
1157 JSON.parse = function (text, reviver) {
1158 // The parse method takes a text and an optional reviver function, and returns
1159 // a JavaScript value if the text is a valid JSON text.
1160
1161 var j;
1162
1163 function walk(holder, key) {
1164
1165 // The walk method is used to recursively walk the resulting structure so
1166 // that modifications can be made.
1167
1168 var k, v, value = holder[key];
1169 if (value && typeof value === 'object') {
1170 for (k in value) {
1171 if (Object.prototype.hasOwnProperty.call(value, k)) {
1172 v = walk(value, k);
1173 if (v !== undefined) {
1174 value[k] = v;
1175 } else {
1176 delete value[k];
1177 }
1178 }
1179 }
1180 }
1181 return reviver.call(holder, key, value);
1182 }
1183
1184
1185 // Parsing happens in four stages. In the first stage, we replace certain
1186 // Unicode characters with escape sequences. JavaScript handles many characters
1187 // incorrectly, either silently deleting them, or treating them as line endings.
1188
1189 text = String(text);
1190 cx.lastIndex = 0;
1191 if (cx.test(text)) {
1192 text = text.replace(cx, function (a) {
1193 return '\\u' +
1194 ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
1195 });
1196 }
1197
1198 // In the second stage, we run the text against regular expressions that look
1199 // for non-JSON patterns. We are especially concerned with '()' and 'new'
1200 // because they can cause invocation, and '=' because it can cause mutation.
1201 // But just to be safe, we want to reject all unexpected forms.
1202
1203 // We split the second stage into 4 regexp operations in order to work around
1204 // crippling inefficiencies in IE's and Safari's regexp engines. First we
1205 // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
1206 // replace all simple value tokens with ']' characters. Third, we delete all
1207 // open brackets that follow a colon or comma or that begin the text. Finally,
1208 // we look to see that the remaining characters are only whitespace or ']' or
1209 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
1210
1211 if (/^[\],:{}\s]*$/
1212 .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
1213 .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
1214 .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
1215
1216 // In the third stage we use the eval function to compile the text into a
1217 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
1218 // in JavaScript: it can begin a block or an object literal. We wrap the text
1219 // in parens to eliminate the ambiguity.
1220
1221 j = eval('(' + text + ')');
1222
1223 // In the optional fourth stage, we recursively walk the new structure, passing
1224 // each name/value pair to a reviver function for possible transformation.
1225
1226 return typeof reviver === 'function' ?
1227 walk({'': j}, '') : j;
1228 }
1229
1230 // If the text is not JSON parseable, then a SyntaxError is thrown.
1231
1232 throw new SyntaxError('JSON.parse');
1233 };
1234
1235 return JSON;
1236 })();
1237
1238})('undefined' != typeof exports ? exports : (should = {}));