Subversion Repositories HomeAutomation

Rev

Details | Last modification | View Log | SVN | RSS feed

Rev Author Line No. Line
1619 runge 1
/*
2
    json.js
3
    2010-11-18
4
 
5
    Public Domain
6
 
7
    No warranty expressed or implied. Use at your own risk.
8
 
9
    This file has been superceded by http://www.JSON.org/json2.js
10
 
11
    See http://www.JSON.org/js.html
12
 
13
    This code should be minified before deployment.
14
    See http://javascript.crockford.com/jsmin.html
15
 
16
    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
17
    NOT CONTROL.
18
 
19
    This file adds these methods to JavaScript:
20
 
21
        object.toJSONString(whitelist)
22
            This method produce a JSON text from a JavaScript value.
23
            It must not contain any cyclical references. Illegal values
24
            will be excluded.
25
 
26
            The default conversion for dates is to an ISO string. You can
27
            add a toJSONString method to any date object to get a different
28
            representation.
29
 
30
            The object and array methods can take an optional whitelist
31
            argument. A whitelist is an array of strings. If it is provided,
32
            keys in objects not found in the whitelist are excluded.
33
 
34
        string.parseJSON(filter)
35
            This method parses a JSON text to produce an object or
36
            array. It can throw a SyntaxError exception.
37
 
38
            The optional filter parameter is a function which can filter and
39
            transform the results. It receives each of the keys and values, and
40
            its return value is used instead of the original value. If it
41
            returns what it received, then structure is not modified. If it
42
            returns undefined then the member is deleted.
43
 
44
            Example:
45
 
46
            // Parse the text. If a key contains the string 'date' then
47
            // convert the value to a date.
48
 
49
            myData = text.parseJSON(function (key, value) {
50
                return key.indexOf('date') >= 0 ? new Date(value) : value;
51
            });
52
 
53
    This file will break programs with improper for..in loops. See
54
    http://yuiblog.com/blog/2006/09/26/for-in-intrigue/
55
 
56
    This file creates a global JSON object containing two methods: stringify
57
    and parse.
58
 
59
        JSON.stringify(value, replacer, space)
60
            value       any JavaScript value, usually an object or array.
61
 
62
            replacer    an optional parameter that determines how object
63
                        values are stringified for objects. It can be a
64
                        function or an array of strings.
65
 
66
            space       an optional parameter that specifies the indentation
67
                        of nested structures. If it is omitted, the text will
68
                        be packed without extra whitespace. If it is a number,
69
                        it will specify the number of spaces to indent at each
70
                        level. If it is a string (such as '\t' or ' '),
71
                        it contains the characters used to indent at each level.
72
 
73
            This method produces a JSON text from a JavaScript value.
74
 
75
            When an object value is found, if the object contains a toJSON
76
            method, its toJSON method will be called and the result will be
77
            stringified. A toJSON method does not serialize: it returns the
78
            value represented by the name/value pair that should be serialized,
79
            or undefined if nothing should be serialized. The toJSON method
80
            will be passed the key associated with the value, and this will be
81
            bound to the object holding the key.
82
 
83
            For example, this would serialize Dates as ISO strings.
84
 
85
                Date.prototype.toJSON = function (key) {
86
                    function f(n) {
87
                        // Format integers to have at least two digits.
88
                        return n < 10 ? '0' + n : n;
89
                    }
90
 
91
                    return this.getUTCFullYear()   + '-' +
92
                         f(this.getUTCMonth() + 1) + '-' +
93
                         f(this.getUTCDate())      + 'T' +
94
                         f(this.getUTCHours())     + ':' +
95
                         f(this.getUTCMinutes())   + ':' +
96
                         f(this.getUTCSeconds())   + 'Z';
97
                };
98
 
99
            You can provide an optional replacer method. It will be passed the
100
            key and value of each member, with this bound to the containing
101
            object. The value that is returned from your method will be
102
            serialized. If your method returns undefined, then the member will
103
            be excluded from the serialization.
104
 
105
            If the replacer parameter is an array of strings, then it will be
106
            used to select the members to be serialized. It filters the results
107
            such that only members with keys listed in the replacer array are
108
            stringified.
109
 
110
            Values that do not have JSON representations, such as undefined or
111
            functions, will not be serialized. Such values in objects will be
112
            dropped; in arrays they will be replaced with null. You can use
113
            a replacer function to replace those with JSON values.
114
            JSON.stringify(undefined) returns undefined.
115
 
116
            The optional space parameter produces a stringification of the
117
            value that is filled with line breaks and indentation to make it
118
            easier to read.
119
 
120
            If the space parameter is a non-empty string, then that string will
121
            be used for indentation. If the space parameter is a number, then
122
            the indentation will be that many spaces.
123
 
124
            Example:
125
 
126
            text = JSON.stringify(['e', {pluribus: 'unum'}]);
127
            // text is '["e",{"pluribus":"unum"}]'
128
 
129
 
130
            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
131
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
132
 
133
            text = JSON.stringify([new Date()], function (key, value) {
134
                return this[key] instanceof Date ?
135
                    'Date(' + this[key] + ')' : value;
136
            });
137
            // text is '["Date(---current time---)"]'
138
 
139
 
140
        JSON.parse(text, reviver)
141
            This method parses a JSON text to produce an object or array.
142
            It can throw a SyntaxError exception.
143
 
144
            The optional reviver parameter is a function that can filter and
145
            transform the results. It receives each of the keys and values,
146
            and its return value is used instead of the original value.
147
            If it returns what it received, then the structure is not modified.
148
            If it returns undefined then the member is deleted.
149
 
150
            Example:
151
 
152
            // Parse the text. Values that look like ISO date strings will
153
            // be converted to Date objects.
154
 
155
            myData = JSON.parse(text, function (key, value) {
156
                var a;
157
                if (typeof value === 'string') {
158
                    a =
159
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
160
                    if (a) {
161
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
162
                            +a[5], +a[6]));
163
                    }
164
                }
165
                return value;
166
            });
167
 
168
            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
169
                var d;
170
                if (typeof value === 'string' &&
171
                        value.slice(0, 5) === 'Date(' &&
172
                        value.slice(-1) === ')') {
173
                    d = new Date(value.slice(5, -1));
174
                    if (d) {
175
                        return d;
176
                    }
177
                }
178
                return value;
179
            });
180
 
181
 
182
    This is a reference implementation. You are free to copy, modify, or
183
    redistribute.
184
*/
185
 
186
/*jslint evil: true, regexp: false */
187
 
188
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
189
    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
190
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
191
    lastIndex, length, parse, parseJSON, prototype, push, replace, slice,
192
    stringify, test, toJSON, toJSONString, toString, valueOf
193
*/
194
 
195
 
196
(function () {
197
    "use strict";
198
 
199
    function f(n) {
200
        // Format integers to have at least two digits.
201
        return n < 10 ? '0' + n : n;
202
    }
203
 
204
    if (typeof Date.prototype.toJSON !== 'function') {
205
 
206
        Date.prototype.toJSON = function (key) {
207
 
208
            return isFinite(this.valueOf()) ?
209
                   this.getUTCFullYear()   + '-' +
210
                 f(this.getUTCMonth() + 1) + '-' +
211
                 f(this.getUTCDate())      + 'T' +
212
                 f(this.getUTCHours())     + ':' +
213
                 f(this.getUTCMinutes())   + ':' +
214
                 f(this.getUTCSeconds())   + 'Z' : null;
215
        };
216
 
217
        String.prototype.toJSON =
218
        Number.prototype.toJSON =
219
        Boolean.prototype.toJSON = function (key) {
220
            return this.valueOf();
221
        };
222
    }
223
 
224
    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
225
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
226
        gap,
227
        indent,
228
        meta = {    // table of character substitutions
229
            '\b': '\\b',
230
            '\t': '\\t',
231
            '\n': '\\n',
232
            '\f': '\\f',
233
            '\r': '\\r',
234
            '"' : '\\"',
235
            '\\': '\\\\'
236
        },
237
        rep;
238
 
239
 
240
    function quote(string) {
241
 
242
// If the string contains no control characters, no quote characters, and no
243
// backslash characters, then we can safely slap some quotes around it.
244
// Otherwise we must also replace the offending characters with safe escape
245
// sequences.
246
 
247
        escapable.lastIndex = 0;
248
        return escapable.test(string) ?
249
            '"' + string.replace(escapable, function (a) {
250
                var c = meta[a];
251
                return typeof c === 'string' ? c :
252
                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
253
            }) + '"' :
254
            '"' + string + '"';
255
    }
256
 
257
 
258
    function str(key, holder) {
259
 
260
// Produce a string from holder[key].
261
 
262
        var i,          // The loop counter.
263
            k,          // The member key.
264
            v,          // The member value.
265
            length,
266
            mind = gap,
267
            partial,
268
            value = holder[key];
269
 
270
// If the value has a toJSON method, call it to obtain a replacement value.
271
 
272
        if (value && typeof value === 'object' &&
273
                typeof value.toJSON === 'function') {
274
            value = value.toJSON(key);
275
        }
276
 
277
// If we were called with a replacer function, then call the replacer to
278
// obtain a replacement value.
279
 
280
        if (typeof rep === 'function') {
281
            value = rep.call(holder, key, value);
282
        }
283
 
284
// What happens next depends on the value's type.
285
 
286
        switch (typeof value) {
287
        case 'string':
288
            return quote(value);
289
 
290
        case 'number':
291
 
292
// JSON numbers must be finite. Encode non-finite numbers as null.
293
 
294
            return isFinite(value) ? String(value) : 'null';
295
 
296
        case 'boolean':
297
        case 'null':
298
 
299
// If the value is a boolean or null, convert it to a string. Note:
300
// typeof null does not produce 'null'. The case is included here in
301
// the remote chance that this gets fixed someday.
302
 
303
            return String(value);
304
 
305
// If the type is 'object', we might be dealing with an object or an array or
306
// null.
307
 
308
        case 'object':
309
 
310
// Due to a specification blunder in ECMAScript, typeof null is 'object',
311
// so watch out for that case.
312
 
313
            if (!value) {
314
                return 'null';
315
            }
316
 
317
// Make an array to hold the partial results of stringifying this object value.
318
 
319
            gap += indent;
320
            partial = [];
321
 
322
// Is the value an array?
323
 
324
            if (Object.prototype.toString.apply(value) === '[object Array]') {
325
 
326
// The value is an array. Stringify every element. Use null as a placeholder
327
// for non-JSON values.
328
 
329
                length = value.length;
330
                for (i = 0; i < length; i += 1) {
331
                    partial[i] = str(i, value) || 'null';
332
                }
333
 
334
// Join all of the elements together, separated with commas, and wrap them in
335
// brackets.
336
 
337
                v = partial.length === 0 ? '[]' :
338
                    gap ? '[\n' + gap +
339
                            partial.join(',\n' + gap) + '\n' +
340
                                mind + ']' :
341
                          '[' + partial.join(',') + ']';
342
                gap = mind;
343
                return v;
344
            }
345
 
346
// If the replacer is an array, use it to select the members to be stringified.
347
 
348
            if (rep && typeof rep === 'object') {
349
                length = rep.length;
350
                for (i = 0; i < length; i += 1) {
351
                    k = rep[i];
352
                    if (typeof k === 'string') {
353
                        v = str(k, value);
354
                        if (v) {
355
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
356
                        }
357
                    }
358
                }
359
            } else {
360
 
361
// Otherwise, iterate through all of the keys in the object.
362
 
363
                for (k in value) {
364
                    if (Object.hasOwnProperty.call(value, k)) {
365
                        v = str(k, value);
366
                        if (v) {
367
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
368
                        }
369
                    }
370
                }
371
            }
372
 
373
// Join all of the member texts together, separated with commas,
374
// and wrap them in braces.
375
 
376
            v = partial.length === 0 ? '{}' :
377
                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
378
                        mind + '}' : '{' + partial.join(',') + '}';
379
            gap = mind;
380
            return v;
381
        }
382
    }
383
 
384
// If the JSON object does not yet have a stringify method, give it one.
385
 
386
    if (typeof JSON.stringify !== 'function') {
387
        JSON.stringify = function (value, replacer, space) {
388
 
389
// The stringify method takes a value and an optional replacer, and an optional
390
// space parameter, and returns a JSON text. The replacer can be a function
391
// that can replace values, or an array of strings that will select the keys.
392
// A default replacer method can be provided. Use of the space parameter can
393
// produce text that is more easily readable.
394
 
395
            var i;
396
            gap = '';
397
            indent = '';
398
 
399
// If the space parameter is a number, make an indent string containing that
400
// many spaces.
401
 
402
            if (typeof space === 'number') {
403
                for (i = 0; i < space; i += 1) {
404
                    indent += ' ';
405
                }
406
 
407
// If the space parameter is a string, it will be used as the indent string.
408
 
409
            } else if (typeof space === 'string') {
410
                indent = space;
411
            }
412
 
413
// If there is a replacer, it must be a function or an array.
414
// Otherwise, throw an error.
415
 
416
            rep = replacer;
417
            if (replacer && typeof replacer !== 'function' &&
418
                    (typeof replacer !== 'object' ||
419
                     typeof replacer.length !== 'number')) {
420
                throw new Error('JSON.stringify');
421
            }
422
 
423
// Make a fake root object containing our value under the key of ''.
424
// Return the result of stringifying the value.
425
 
426
            return str('', {'': value});
427
        };
428
    }
429
 
430
 
431
// If the JSON object does not yet have a parse method, give it one.
432
 
433
    if (typeof JSON.parse !== 'function') {
434
        JSON.parse = function (text, reviver) {
435
 
436
// The parse method takes a text and an optional reviver function, and returns
437
// a JavaScript value if the text is a valid JSON text.
438
 
439
            var j;
440
 
441
            function walk(holder, key) {
442
 
443
// The walk method is used to recursively walk the resulting structure so
444
// that modifications can be made.
445
 
446
                var k, v, value = holder[key];
447
                if (value && typeof value === 'object') {
448
                    for (k in value) {
449
                        if (Object.hasOwnProperty.call(value, k)) {
450
                            v = walk(value, k);
451
                            if (v !== undefined) {
452
                                value[k] = v;
453
                            } else {
454
                                delete value[k];
455
                            }
456
                        }
457
                    }
458
                }
459
                return reviver.call(holder, key, value);
460
            }
461
 
462
 
463
// Parsing happens in four stages. In the first stage, we replace certain
464
// Unicode characters with escape sequences. JavaScript handles many characters
465
// incorrectly, either silently deleting them, or treating them as line endings.
466
 
467
            text = String(text);
468
            cx.lastIndex = 0;
469
            if (cx.test(text)) {
470
                text = text.replace(cx, function (a) {
471
                    return '\\u' +
472
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
473
                });
474
            }
475
 
476
// In the second stage, we run the text against regular expressions that look
477
// for non-JSON patterns. We are especially concerned with '()' and 'new'
478
// because they can cause invocation, and '=' because it can cause mutation.
479
// But just to be safe, we want to reject all unexpected forms.
480
 
481
// We split the second stage into 4 regexp operations in order to work around
482
// crippling inefficiencies in IE's and Safari's regexp engines. First we
483
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
484
// replace all simple value tokens with ']' characters. Third, we delete all
485
// open brackets that follow a colon or comma or that begin the text. Finally,
486
// we look to see that the remaining characters are only whitespace or ']' or
487
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
488
 
489
            if (/^[\],:{}\s]*$/
490
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
491
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
492
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
493
 
494
// In the third stage we use the eval function to compile the text into a
495
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
496
// in JavaScript: it can begin a block or an object literal. We wrap the text
497
// in parens to eliminate the ambiguity.
498
 
499
                j = eval('(' + text + ')');
500
 
501
// In the optional fourth stage, we recursively walk the new structure, passing
502
// each name/value pair to a reviver function for possible transformation.
503
 
504
                return typeof reviver === 'function' ?
505
                    walk({'': j}, '') : j;
506
            }
507
 
508
// If the text is not JSON parseable, then a SyntaxError is thrown.
509
 
510
            throw new SyntaxError('JSON.parse');
511
        };
512
    }
513
}());
514