diff js-lib/json2.js @ 90:08f93d043ed2 laserkard

saving progress
author Robert McIntyre <rlm@mit.edu>
date Mon, 26 Jul 2010 04:13:05 -0400
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js-lib/json2.js	Mon Jul 26 04:13:05 2010 -0400
     1.3 @@ -0,0 +1,483 @@
     1.4 +
     1.5 +/*
     1.6 +    http://www.JSON.org/json2.js
     1.7 +    2010-03-20
     1.8 +
     1.9 +    Public Domain.
    1.10 +
    1.11 +    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
    1.12 +
    1.13 +    See http://www.JSON.org/js.html
    1.14 +
    1.15 +
    1.16 +    This code should be minified before deployment.
    1.17 +    See http://javascript.crockford.com/jsmin.html
    1.18 +
    1.19 +    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
    1.20 +    NOT CONTROL.
    1.21 +
    1.22 +
    1.23 +    This file creates a global JSON object containing two methods: stringify
    1.24 +    and parse.
    1.25 +
    1.26 +        JSON.stringify(value, replacer, space)
    1.27 +            value       any JavaScript value, usually an object or array.
    1.28 +
    1.29 +            replacer    an optional parameter that determines how object
    1.30 +                        values are stringified for objects. It can be a
    1.31 +                        function or an array of strings.
    1.32 +
    1.33 +            space       an optional parameter that specifies the indentation
    1.34 +                        of nested structures. If it is omitted, the text will
    1.35 +                        be packed without extra whitespace. If it is a number,
    1.36 +                        it will specify the number of spaces to indent at each
    1.37 +                        level. If it is a string (such as '\t' or '&nbsp;'),
    1.38 +                        it contains the characters used to indent at each level.
    1.39 +
    1.40 +            This method produces a JSON text from a JavaScript value.
    1.41 +
    1.42 +            When an object value is found, if the object contains a toJSON
    1.43 +            method, its toJSON method will be called and the result will be
    1.44 +            stringified. A toJSON method does not serialize: it returns the
    1.45 +            value represented by the name/value pair that should be serialized,
    1.46 +            or undefined if nothing should be serialized. The toJSON method
    1.47 +            will be passed the key associated with the value, and this will be
    1.48 +            bound to the value
    1.49 +
    1.50 +            For example, this would serialize Dates as ISO strings.
    1.51 +
    1.52 +                Date.prototype.toJSON = function (key) {
    1.53 +                    function f(n) {
    1.54 +                        // Format integers to have at least two digits.
    1.55 +                        return n < 10 ? '0' + n : n;
    1.56 +                    }
    1.57 +
    1.58 +                    return this.getUTCFullYear()   + '-' +
    1.59 +                         f(this.getUTCMonth() + 1) + '-' +
    1.60 +                         f(this.getUTCDate())      + 'T' +
    1.61 +                         f(this.getUTCHours())     + ':' +
    1.62 +                         f(this.getUTCMinutes())   + ':' +
    1.63 +                         f(this.getUTCSeconds())   + 'Z';
    1.64 +                };
    1.65 +
    1.66 +            You can provide an optional replacer method. It will be passed the
    1.67 +            key and value of each member, with this bound to the containing
    1.68 +            object. The value that is returned from your method will be
    1.69 +            serialized. If your method returns undefined, then the member will
    1.70 +            be excluded from the serialization.
    1.71 +
    1.72 +            If the replacer parameter is an array of strings, then it will be
    1.73 +            used to select the members to be serialized. It filters the results
    1.74 +            such that only members with keys listed in the replacer array are
    1.75 +            stringified.
    1.76 +
    1.77 +            Values that do not have JSON representations, such as undefined or
    1.78 +            functions, will not be serialized. Such values in objects will be
    1.79 +            dropped; in arrays they will be replaced with null. You can use
    1.80 +            a replacer function to replace those with JSON values.
    1.81 +            JSON.stringify(undefined) returns undefined.
    1.82 +
    1.83 +            The optional space parameter produces a stringification of the
    1.84 +            value that is filled with line breaks and indentation to make it
    1.85 +            easier to read.
    1.86 +
    1.87 +            If the space parameter is a non-empty string, then that string will
    1.88 +            be used for indentation. If the space parameter is a number, then
    1.89 +            the indentation will be that many spaces.
    1.90 +
    1.91 +            Example:
    1.92 +
    1.93 +            text = JSON.stringify(['e', {pluribus: 'unum'}]);
    1.94 +            // text is '["e",{"pluribus":"unum"}]'
    1.95 +
    1.96 +
    1.97 +            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
    1.98 +            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
    1.99 +
   1.100 +            text = JSON.stringify([new Date()], function (key, value) {
   1.101 +                return this[key] instanceof Date ?
   1.102 +                    'Date(' + this[key] + ')' : value;
   1.103 +            });
   1.104 +            // text is '["Date(---current time---)"]'
   1.105 +
   1.106 +
   1.107 +        JSON.parse(text, reviver)
   1.108 +            This method parses a JSON text to produce an object or array.
   1.109 +            It can throw a SyntaxError exception.
   1.110 +
   1.111 +            The optional reviver parameter is a function that can filter and
   1.112 +            transform the results. It receives each of the keys and values,
   1.113 +            and its return value is used instead of the original value.
   1.114 +            If it returns what it received, then the structure is not modified.
   1.115 +            If it returns undefined then the member is deleted.
   1.116 +
   1.117 +            Example:
   1.118 +
   1.119 +            // Parse the text. Values that look like ISO date strings will
   1.120 +            // be converted to Date objects.
   1.121 +
   1.122 +            myData = JSON.parse(text, function (key, value) {
   1.123 +                var a;
   1.124 +                if (typeof value === 'string') {
   1.125 +                    a =
   1.126 +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
   1.127 +                    if (a) {
   1.128 +                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
   1.129 +                            +a[5], +a[6]));
   1.130 +                    }
   1.131 +                }
   1.132 +                return value;
   1.133 +            });
   1.134 +
   1.135 +            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
   1.136 +                var d;
   1.137 +                if (typeof value === 'string' &&
   1.138 +                        value.slice(0, 5) === 'Date(' &&
   1.139 +                        value.slice(-1) === ')') {
   1.140 +                    d = new Date(value.slice(5, -1));
   1.141 +                    if (d) {
   1.142 +                        return d;
   1.143 +                    }
   1.144 +                }
   1.145 +                return value;
   1.146 +            });
   1.147 +
   1.148 +
   1.149 +    This is a reference implementation. You are free to copy, modify, or
   1.150 +    redistribute.
   1.151 +*/
   1.152 +
   1.153 +/*jslint evil: true, strict: false */
   1.154 +
   1.155 +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
   1.156 +    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
   1.157 +    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
   1.158 +    lastIndex, length, parse, prototype, push, replace, slice, stringify,
   1.159 +    test, toJSON, toString, valueOf
   1.160 +*/
   1.161 +
   1.162 +
   1.163 +// Create a JSON object only if one does not already exist. We create the
   1.164 +// methods in a closure to avoid creating global variables.
   1.165 +
   1.166 +if (!this.JSON) {
   1.167 +    this.JSON = {};
   1.168 +}
   1.169 +
   1.170 +(function () {
   1.171 +
   1.172 +    function f(n) {
   1.173 +        // Format integers to have at least two digits.
   1.174 +        return n < 10 ? '0' + n : n;
   1.175 +    }
   1.176 +
   1.177 +    if (typeof Date.prototype.toJSON !== 'function') {
   1.178 +
   1.179 +        Date.prototype.toJSON = function (key) {
   1.180 +
   1.181 +            return isFinite(this.valueOf()) ?
   1.182 +                   this.getUTCFullYear()   + '-' +
   1.183 +                 f(this.getUTCMonth() + 1) + '-' +
   1.184 +                 f(this.getUTCDate())      + 'T' +
   1.185 +                 f(this.getUTCHours())     + ':' +
   1.186 +                 f(this.getUTCMinutes())   + ':' +
   1.187 +                 f(this.getUTCSeconds())   + 'Z' : null;
   1.188 +        };
   1.189 +
   1.190 +        String.prototype.toJSON =
   1.191 +        Number.prototype.toJSON =
   1.192 +        Boolean.prototype.toJSON = function (key) {
   1.193 +            return this.valueOf();
   1.194 +        };
   1.195 +    }
   1.196 +
   1.197 +    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
   1.198 +        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
   1.199 +        gap,
   1.200 +        indent,
   1.201 +        meta = {    // table of character substitutions
   1.202 +            '\b': '\\b',
   1.203 +            '\t': '\\t',
   1.204 +            '\n': '\\n',
   1.205 +            '\f': '\\f',
   1.206 +            '\r': '\\r',
   1.207 +            '"' : '\\"',
   1.208 +            '\\': '\\\\'
   1.209 +        },
   1.210 +        rep;
   1.211 +
   1.212 +
   1.213 +    function quote(string) {
   1.214 +
   1.215 +// If the string contains no control characters, no quote characters, and no
   1.216 +// backslash characters, then we can safely slap some quotes around it.
   1.217 +// Otherwise we must also replace the offending characters with safe escape
   1.218 +// sequences.
   1.219 +
   1.220 +        escapable.lastIndex = 0;
   1.221 +        return escapable.test(string) ?
   1.222 +            '"' + string.replace(escapable, function (a) {
   1.223 +                var c = meta[a];
   1.224 +                return typeof c === 'string' ? c :
   1.225 +                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
   1.226 +            }) + '"' :
   1.227 +            '"' + string + '"';
   1.228 +    }
   1.229 +
   1.230 +
   1.231 +    function str(key, holder) {
   1.232 +
   1.233 +// Produce a string from holder[key].
   1.234 +
   1.235 +        var i,          // The loop counter.
   1.236 +            k,          // The member key.
   1.237 +            v,          // The member value.
   1.238 +            length,
   1.239 +            mind = gap,
   1.240 +            partial,
   1.241 +            value = holder[key];
   1.242 +
   1.243 +// If the value has a toJSON method, call it to obtain a replacement value.
   1.244 +
   1.245 +        if (value && typeof value === 'object' &&
   1.246 +                typeof value.toJSON === 'function') {
   1.247 +            value = value.toJSON(key);
   1.248 +        }
   1.249 +
   1.250 +// If we were called with a replacer function, then call the replacer to
   1.251 +// obtain a replacement value.
   1.252 +
   1.253 +        if (typeof rep === 'function') {
   1.254 +            value = rep.call(holder, key, value);
   1.255 +        }
   1.256 +
   1.257 +// What happens next depends on the value's type.
   1.258 +
   1.259 +        switch (typeof value) {
   1.260 +        case 'string':
   1.261 +            return quote(value);
   1.262 +
   1.263 +        case 'number':
   1.264 +
   1.265 +// JSON numbers must be finite. Encode non-finite numbers as null.
   1.266 +
   1.267 +            return isFinite(value) ? String(value) : 'null';
   1.268 +
   1.269 +        case 'boolean':
   1.270 +        case 'null':
   1.271 +
   1.272 +// If the value is a boolean or null, convert it to a string. Note:
   1.273 +// typeof null does not produce 'null'. The case is included here in
   1.274 +// the remote chance that this gets fixed someday.
   1.275 +
   1.276 +            return String(value);
   1.277 +
   1.278 +// If the type is 'object', we might be dealing with an object or an array or
   1.279 +// null.
   1.280 +
   1.281 +        case 'object':
   1.282 +
   1.283 +// Due to a specification blunder in ECMAScript, typeof null is 'object',
   1.284 +// so watch out for that case.
   1.285 +
   1.286 +            if (!value) {
   1.287 +                return 'null';
   1.288 +            }
   1.289 +
   1.290 +// Make an array to hold the partial results of stringifying this object value.
   1.291 +
   1.292 +            gap += indent;
   1.293 +            partial = [];
   1.294 +
   1.295 +// Is the value an array?
   1.296 +
   1.297 +            if (Object.prototype.toString.apply(value) === '[object Array]') {
   1.298 +
   1.299 +// The value is an array. Stringify every element. Use null as a placeholder
   1.300 +// for non-JSON values.
   1.301 +
   1.302 +                length = value.length;
   1.303 +                for (i = 0; i < length; i += 1) {
   1.304 +                    partial[i] = str(i, value) || 'null';
   1.305 +                }
   1.306 +
   1.307 +// Join all of the elements together, separated with commas, and wrap them in
   1.308 +// brackets.
   1.309 +
   1.310 +                v = partial.length === 0 ? '[]' :
   1.311 +                    gap ? '[\n' + gap +
   1.312 +                            partial.join(',\n' + gap) + '\n' +
   1.313 +                                mind + ']' :
   1.314 +                          '[' + partial.join(',') + ']';
   1.315 +                gap = mind;
   1.316 +                return v;
   1.317 +            }
   1.318 +
   1.319 +// If the replacer is an array, use it to select the members to be stringified.
   1.320 +
   1.321 +            if (rep && typeof rep === 'object') {
   1.322 +                length = rep.length;
   1.323 +                for (i = 0; i < length; i += 1) {
   1.324 +                    k = rep[i];
   1.325 +                    if (typeof k === 'string') {
   1.326 +                        v = str(k, value);
   1.327 +                        if (v) {
   1.328 +                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
   1.329 +                        }
   1.330 +                    }
   1.331 +                }
   1.332 +            } else {
   1.333 +
   1.334 +// Otherwise, iterate through all of the keys in the object.
   1.335 +
   1.336 +                for (k in value) {
   1.337 +                    if (Object.hasOwnProperty.call(value, k)) {
   1.338 +                        v = str(k, value);
   1.339 +                        if (v) {
   1.340 +                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
   1.341 +                        }
   1.342 +                    }
   1.343 +                }
   1.344 +            }
   1.345 +
   1.346 +// Join all of the member texts together, separated with commas,
   1.347 +// and wrap them in braces.
   1.348 +
   1.349 +            v = partial.length === 0 ? '{}' :
   1.350 +                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
   1.351 +                        mind + '}' : '{' + partial.join(',') + '}';
   1.352 +            gap = mind;
   1.353 +            return v;
   1.354 +        }
   1.355 +    }
   1.356 +
   1.357 +// If the JSON object does not yet have a stringify method, give it one.
   1.358 +
   1.359 +    if (typeof JSON.stringify !== 'function') {
   1.360 +        JSON.stringify = function (value, replacer, space) {
   1.361 +
   1.362 +// The stringify method takes a value and an optional replacer, and an optional
   1.363 +// space parameter, and returns a JSON text. The replacer can be a function
   1.364 +// that can replace values, or an array of strings that will select the keys.
   1.365 +// A default replacer method can be provided. Use of the space parameter can
   1.366 +// produce text that is more easily readable.
   1.367 +
   1.368 +            var i;
   1.369 +            gap = '';
   1.370 +            indent = '';
   1.371 +
   1.372 +// If the space parameter is a number, make an indent string containing that
   1.373 +// many spaces.
   1.374 +
   1.375 +            if (typeof space === 'number') {
   1.376 +                for (i = 0; i < space; i += 1) {
   1.377 +                    indent += ' ';
   1.378 +                }
   1.379 +
   1.380 +// If the space parameter is a string, it will be used as the indent string.
   1.381 +
   1.382 +            } else if (typeof space === 'string') {
   1.383 +                indent = space;
   1.384 +            }
   1.385 +
   1.386 +// If there is a replacer, it must be a function or an array.
   1.387 +// Otherwise, throw an error.
   1.388 +
   1.389 +            rep = replacer;
   1.390 +            if (replacer && typeof replacer !== 'function' &&
   1.391 +                    (typeof replacer !== 'object' ||
   1.392 +                     typeof replacer.length !== 'number')) {
   1.393 +                throw new Error('JSON.stringify');
   1.394 +            }
   1.395 +
   1.396 +// Make a fake root object containing our value under the key of ''.
   1.397 +// Return the result of stringifying the value.
   1.398 +
   1.399 +            return str('', {'': value});
   1.400 +        };
   1.401 +    }
   1.402 +
   1.403 +
   1.404 +// If the JSON object does not yet have a parse method, give it one.
   1.405 +
   1.406 +    if (typeof JSON.parse !== 'function') {
   1.407 +        JSON.parse = function (text, reviver) {
   1.408 +
   1.409 +// The parse method takes a text and an optional reviver function, and returns
   1.410 +// a JavaScript value if the text is a valid JSON text.
   1.411 +
   1.412 +            var j;
   1.413 +
   1.414 +            function walk(holder, key) {
   1.415 +
   1.416 +// The walk method is used to recursively walk the resulting structure so
   1.417 +// that modifications can be made.
   1.418 +
   1.419 +                var k, v, value = holder[key];
   1.420 +                if (value && typeof value === 'object') {
   1.421 +                    for (k in value) {
   1.422 +                        if (Object.hasOwnProperty.call(value, k)) {
   1.423 +                            v = walk(value, k);
   1.424 +                            if (v !== undefined) {
   1.425 +                                value[k] = v;
   1.426 +                            } else {
   1.427 +                                delete value[k];
   1.428 +                            }
   1.429 +                        }
   1.430 +                    }
   1.431 +                }
   1.432 +                return reviver.call(holder, key, value);
   1.433 +            }
   1.434 +
   1.435 +
   1.436 +// Parsing happens in four stages. In the first stage, we replace certain
   1.437 +// Unicode characters with escape sequences. JavaScript handles many characters
   1.438 +// incorrectly, either silently deleting them, or treating them as line endings.
   1.439 +
   1.440 +            text = String(text);
   1.441 +            cx.lastIndex = 0;
   1.442 +            if (cx.test(text)) {
   1.443 +                text = text.replace(cx, function (a) {
   1.444 +                    return '\\u' +
   1.445 +                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
   1.446 +                });
   1.447 +            }
   1.448 +
   1.449 +// In the second stage, we run the text against regular expressions that look
   1.450 +// for non-JSON patterns. We are especially concerned with '()' and 'new'
   1.451 +// because they can cause invocation, and '=' because it can cause mutation.
   1.452 +// But just to be safe, we want to reject all unexpected forms.
   1.453 +
   1.454 +// We split the second stage into 4 regexp operations in order to work around
   1.455 +// crippling inefficiencies in IE's and Safari's regexp engines. First we
   1.456 +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
   1.457 +// replace all simple value tokens with ']' characters. Third, we delete all
   1.458 +// open brackets that follow a colon or comma or that begin the text. Finally,
   1.459 +// we look to see that the remaining characters are only whitespace or ']' or
   1.460 +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
   1.461 +
   1.462 +            if (/^[\],:{}\s]*$/.
   1.463 +test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
   1.464 +replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
   1.465 +replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
   1.466 +
   1.467 +// In the third stage we use the eval function to compile the text into a
   1.468 +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
   1.469 +// in JavaScript: it can begin a block or an object literal. We wrap the text
   1.470 +// in parens to eliminate the ambiguity.
   1.471 +
   1.472 +                j = eval('(' + text + ')');
   1.473 +
   1.474 +// In the optional fourth stage, we recursively walk the new structure, passing
   1.475 +// each name/value pair to a reviver function for possible transformation.
   1.476 +
   1.477 +                return typeof reviver === 'function' ?
   1.478 +                    walk({'': j}, '') : j;
   1.479 +            }
   1.480 +
   1.481 +// If the text is not JSON parseable, then a SyntaxError is thrown.
   1.482 +
   1.483 +            throw new SyntaxError('JSON.parse');
   1.484 +        };
   1.485 +    }
   1.486 +}());