~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/canonical/launchpad/icing-contrib/json2.js

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-06-25 08:55:37 UTC
  • mfrom: (13287.1.8 bug-800652)
  • Revision ID: launchpad@pqm.canonical.com-20110625085537-moikyoo2pe98zs7r
[r=jcsackett, julian-edwards][bug=800634,
        800652] Enable and display overrides on sync package uploads.

Show diffs side-by-side

added added

removed removed

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