1 |
|
2 | /**
|
3 | * socket.io
|
4 | * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
5 | * MIT Licensed
|
6 | */
|
7 |
|
8 | /**
|
9 | * Based on JSON2 (http://www.JSON.org/js.html).
|
10 | */
|
11 |
|
12 | (function (exports, nativeJSON) {
|
13 | ;
|
14 |
|
15 | // use native JSON if it's available
|
16 | if (nativeJSON && nativeJSON.parse){
|
17 | return exports.JSON = {
|
18 | parse: nativeJSON.parse
|
19 | , stringify: nativeJSON.stringify
|
20 | }
|
21 | }
|
22 |
|
23 | var JSON = exports.JSON = {};
|
24 |
|
25 | function f(n) {
|
26 | // Format integers to have at least two digits.
|
27 | return n < 10 ? '0' + n : n;
|
28 | }
|
29 |
|
30 | function date(d, key) {
|
31 | return isFinite(d.valueOf()) ?
|
32 | d.getUTCFullYear() + '-' +
|
33 | f(d.getUTCMonth() + 1) + '-' +
|
34 | f(d.getUTCDate()) + 'T' +
|
35 | f(d.getUTCHours()) + ':' +
|
36 | f(d.getUTCMinutes()) + ':' +
|
37 | f(d.getUTCSeconds()) + 'Z' : null;
|
38 | };
|
39 |
|
40 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
41 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
42 | gap,
|
43 | indent,
|
44 | meta = { // table of character substitutions
|
45 | '\b': '\\b',
|
46 | '\t': '\\t',
|
47 | '\n': '\\n',
|
48 | '\f': '\\f',
|
49 | '\r': '\\r',
|
50 | '"' : '\\"',
|
51 | '\\': '\\\\'
|
52 | },
|
53 | rep;
|
54 |
|
55 |
|
56 | function quote(string) {
|
57 |
|
58 | // If the string contains no control characters, no quote characters, and no
|
59 | // backslash characters, then we can safely slap some quotes around it.
|
60 | // Otherwise we must also replace the offending characters with safe escape
|
61 | // sequences.
|
62 |
|
63 | escapable.lastIndex = 0;
|
64 | return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
|
65 | var c = meta[a];
|
66 | return typeof c === 'string' ? c :
|
67 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
68 | }) + '"' : '"' + string + '"';
|
69 | }
|
70 |
|
71 |
|
72 | function str(key, holder) {
|
73 |
|
74 | // Produce a string from holder[key].
|
75 |
|
76 | var i, // The loop counter.
|
77 | k, // The member key.
|
78 | v, // The member value.
|
79 | length,
|
80 | mind = gap,
|
81 | partial,
|
82 | value = holder[key];
|
83 |
|
84 | // If the value has a toJSON method, call it to obtain a replacement value.
|
85 |
|
86 | if (value instanceof Date) {
|
87 | value = date(key);
|
88 | }
|
89 |
|
90 | // If we were called with a replacer function, then call the replacer to
|
91 | // obtain a replacement value.
|
92 |
|
93 | if (typeof rep === 'function') {
|
94 | value = rep.call(holder, key, value);
|
95 | }
|
96 |
|
97 | // What happens next depends on the value's type.
|
98 |
|
99 | switch (typeof value) {
|
100 | case 'string':
|
101 | return quote(value);
|
102 |
|
103 | case 'number':
|
104 |
|
105 | // JSON numbers must be finite. Encode non-finite numbers as null.
|
106 |
|
107 | return isFinite(value) ? String(value) : 'null';
|
108 |
|
109 | case 'boolean':
|
110 | case 'null':
|
111 |
|
112 | // If the value is a boolean or null, convert it to a string. Note:
|
113 | // typeof null does not produce 'null'. The case is included here in
|
114 | // the remote chance that this gets fixed someday.
|
115 |
|
116 | return String(value);
|
117 |
|
118 | // If the type is 'object', we might be dealing with an object or an array or
|
119 | // null.
|
120 |
|
121 | case 'object':
|
122 |
|
123 | // Due to a specification blunder in ECMAScript, typeof null is 'object',
|
124 | // so watch out for that case.
|
125 |
|
126 | if (!value) {
|
127 | return 'null';
|
128 | }
|
129 |
|
130 | // Make an array to hold the partial results of stringifying this object value.
|
131 |
|
132 | gap += indent;
|
133 | partial = [];
|
134 |
|
135 | // Is the value an array?
|
136 |
|
137 | if (Object.prototype.toString.apply(value) === '[object Array]') {
|
138 |
|
139 | // The value is an array. Stringify every element. Use null as a placeholder
|
140 | // for non-JSON values.
|
141 |
|
142 | length = value.length;
|
143 | for (i = 0; i < length; i += 1) {
|
144 | partial[i] = str(i, value) || 'null';
|
145 | }
|
146 |
|
147 | // Join all of the elements together, separated with commas, and wrap them in
|
148 | // brackets.
|
149 |
|
150 | v = partial.length === 0 ? '[]' : gap ?
|
151 | '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
|
152 | '[' + partial.join(',') + ']';
|
153 | gap = mind;
|
154 | return v;
|
155 | }
|
156 |
|
157 | // If the replacer is an array, use it to select the members to be stringified.
|
158 |
|
159 | if (rep && typeof rep === 'object') {
|
160 | length = rep.length;
|
161 | for (i = 0; i < length; i += 1) {
|
162 | if (typeof rep[i] === 'string') {
|
163 | k = rep[i];
|
164 | v = str(k, value);
|
165 | if (v) {
|
166 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
167 | }
|
168 | }
|
169 | }
|
170 | } else {
|
171 |
|
172 | // Otherwise, iterate through all of the keys in the object.
|
173 |
|
174 | for (k in value) {
|
175 | if (Object.prototype.hasOwnProperty.call(value, k)) {
|
176 | v = str(k, value);
|
177 | if (v) {
|
178 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
179 | }
|
180 | }
|
181 | }
|
182 | }
|
183 |
|
184 | // Join all of the member texts together, separated with commas,
|
185 | // and wrap them in braces.
|
186 |
|
187 | v = partial.length === 0 ? '{}' : gap ?
|
188 | '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
|
189 | '{' + partial.join(',') + '}';
|
190 | gap = mind;
|
191 | return v;
|
192 | }
|
193 | }
|
194 |
|
195 | // If the JSON object does not yet have a stringify method, give it one.
|
196 |
|
197 | JSON.stringify = function (value, replacer, space) {
|
198 |
|
199 | // The stringify method takes a value and an optional replacer, and an optional
|
200 | // space parameter, and returns a JSON text. The replacer can be a function
|
201 | // that can replace values, or an array of strings that will select the keys.
|
202 | // A default replacer method can be provided. Use of the space parameter can
|
203 | // produce text that is more easily readable.
|
204 |
|
205 | var i;
|
206 | gap = '';
|
207 | indent = '';
|
208 |
|
209 | // If the space parameter is a number, make an indent string containing that
|
210 | // many spaces.
|
211 |
|
212 | if (typeof space === 'number') {
|
213 | for (i = 0; i < space; i += 1) {
|
214 | indent += ' ';
|
215 | }
|
216 |
|
217 | // If the space parameter is a string, it will be used as the indent string.
|
218 |
|
219 | } else if (typeof space === 'string') {
|
220 | indent = space;
|
221 | }
|
222 |
|
223 | // If there is a replacer, it must be a function or an array.
|
224 | // Otherwise, throw an error.
|
225 |
|
226 | rep = replacer;
|
227 | if (replacer && typeof replacer !== 'function' &&
|
228 | (typeof replacer !== 'object' ||
|
229 | typeof replacer.length !== 'number')) {
|
230 | throw new Error('JSON.stringify');
|
231 | }
|
232 |
|
233 | // Make a fake root object containing our value under the key of ''.
|
234 | // Return the result of stringifying the value.
|
235 |
|
236 | return str('', {'': value});
|
237 | };
|
238 |
|
239 | // If the JSON object does not yet have a parse method, give it one.
|
240 |
|
241 | JSON.parse = function (text, reviver) {
|
242 | // The parse method takes a text and an optional reviver function, and returns
|
243 | // a JavaScript value if the text is a valid JSON text.
|
244 |
|
245 | var j;
|
246 |
|
247 | function walk(holder, key) {
|
248 |
|
249 | // The walk method is used to recursively walk the resulting structure so
|
250 | // that modifications can be made.
|
251 |
|
252 | var k, v, value = holder[key];
|
253 | if (value && typeof value === 'object') {
|
254 | for (k in value) {
|
255 | if (Object.prototype.hasOwnProperty.call(value, k)) {
|
256 | v = walk(value, k);
|
257 | if (v !== undefined) {
|
258 | value[k] = v;
|
259 | } else {
|
260 | delete value[k];
|
261 | }
|
262 | }
|
263 | }
|
264 | }
|
265 | return reviver.call(holder, key, value);
|
266 | }
|
267 |
|
268 |
|
269 | // Parsing happens in four stages. In the first stage, we replace certain
|
270 | // Unicode characters with escape sequences. JavaScript handles many characters
|
271 | // incorrectly, either silently deleting them, or treating them as line endings.
|
272 |
|
273 | text = String(text);
|
274 | cx.lastIndex = 0;
|
275 | if (cx.test(text)) {
|
276 | text = text.replace(cx, function (a) {
|
277 | return '\\u' +
|
278 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
279 | });
|
280 | }
|
281 |
|
282 | // In the second stage, we run the text against regular expressions that look
|
283 | // for non-JSON patterns. We are especially concerned with '()' and 'new'
|
284 | // because they can cause invocation, and '=' because it can cause mutation.
|
285 | // But just to be safe, we want to reject all unexpected forms.
|
286 |
|
287 | // We split the second stage into 4 regexp operations in order to work around
|
288 | // crippling inefficiencies in IE's and Safari's regexp engines. First we
|
289 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
|
290 | // replace all simple value tokens with ']' characters. Third, we delete all
|
291 | // open brackets that follow a colon or comma or that begin the text. Finally,
|
292 | // we look to see that the remaining characters are only whitespace or ']' or
|
293 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
294 |
|
295 | if (/^[\],:{}\s]*$/
|
296 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
|
297 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
|
298 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
299 |
|
300 | // In the third stage we use the eval function to compile the text into a
|
301 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
302 | // in JavaScript: it can begin a block or an object literal. We wrap the text
|
303 | // in parens to eliminate the ambiguity.
|
304 |
|
305 | j = eval('(' + text + ')');
|
306 |
|
307 | // In the optional fourth stage, we recursively walk the new structure, passing
|
308 | // each name/value pair to a reviver function for possible transformation.
|
309 |
|
310 | return typeof reviver === 'function' ?
|
311 | walk({'': j}, '') : j;
|
312 | }
|
313 |
|
314 | // If the text is not JSON parseable, then a SyntaxError is thrown.
|
315 |
|
316 | throw new SyntaxError('JSON.parse');
|
317 | };
|
318 |
|
319 | })(
|
320 | 'undefined' != typeof io ? io : module.exports
|
321 | , typeof JSON !== 'undefined' ? JSON : undefined
|
322 | );
|