Mercurial > wikked
diff static/js/jsonjs/json_parse_state.js @ 60:8250c977bc50
Moved static files to the root directory.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Tue, 05 Feb 2013 14:49:34 -0800 |
parents | wikked/static/js/jsonjs/json_parse_state.js@c946f4facfa2 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/static/js/jsonjs/json_parse_state.js Tue Feb 05 14:49:34 2013 -0800 @@ -0,0 +1,397 @@ +/* + json_parse_state.js + 2012-06-01 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + This file creates a json_parse function. + + json_parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = json_parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + This is a reference implementation. You are free to copy, modify, or + redistribute. + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. +*/ + +/*jslint regexp: false*/ + +/*members "", "\"", ",", "\/", ":", "[", "\\", "]", acomma, avalue, b, + call, colon, container, exec, f, false, firstavalue, firstokey, + fromCharCode, go, hasOwnProperty, key, length, n, null, ocomma, okey, + ovalue, pop, prototype, push, r, replace, slice, state, t, test, true, + value, "{", "}" +*/ + +var json_parse = (function () { + "use strict"; + +// This function creates a JSON parse function that uses a state machine rather +// than the dangerous eval function to parse a JSON text. + + var state, // The state of the parser, one of + // 'go' The starting state + // 'ok' The final, accepting state + // 'firstokey' Ready for the first key of the object or + // the closing of an empty object + // 'okey' Ready for the next key of the object + // 'colon' Ready for the colon + // 'ovalue' Ready for the value half of a key/value pair + // 'ocomma' Ready for a comma or closing } + // 'firstavalue' Ready for the first value of an array or + // an empty array + // 'avalue' Ready for the next value of an array + // 'acomma' Ready for a comma or closing ] + stack, // The stack, for controlling nesting. + container, // The current container object or array + key, // The current key + value, // The current value + escapes = { // Escapement translation table + '\\': '\\', + '"': '"', + '/': '/', + 't': '\t', + 'n': '\n', + 'r': '\r', + 'f': '\f', + 'b': '\b' + }, + string = { // The actions for string tokens + go: function () { + state = 'ok'; + }, + firstokey: function () { + key = value; + state = 'colon'; + }, + okey: function () { + key = value; + state = 'colon'; + }, + ovalue: function () { + state = 'ocomma'; + }, + firstavalue: function () { + state = 'acomma'; + }, + avalue: function () { + state = 'acomma'; + } + }, + number = { // The actions for number tokens + go: function () { + state = 'ok'; + }, + ovalue: function () { + state = 'ocomma'; + }, + firstavalue: function () { + state = 'acomma'; + }, + avalue: function () { + state = 'acomma'; + } + }, + action = { + +// The action table describes the behavior of the machine. It contains an +// object for each token. Each object contains a method that is called when +// a token is matched in a state. An object will lack a method for illegal +// states. + + '{': { + go: function () { + stack.push({state: 'ok'}); + container = {}; + state = 'firstokey'; + }, + ovalue: function () { + stack.push({container: container, state: 'ocomma', key: key}); + container = {}; + state = 'firstokey'; + }, + firstavalue: function () { + stack.push({container: container, state: 'acomma'}); + container = {}; + state = 'firstokey'; + }, + avalue: function () { + stack.push({container: container, state: 'acomma'}); + container = {}; + state = 'firstokey'; + } + }, + '}': { + firstokey: function () { + var pop = stack.pop(); + value = container; + container = pop.container; + key = pop.key; + state = pop.state; + }, + ocomma: function () { + var pop = stack.pop(); + container[key] = value; + value = container; + container = pop.container; + key = pop.key; + state = pop.state; + } + }, + '[': { + go: function () { + stack.push({state: 'ok'}); + container = []; + state = 'firstavalue'; + }, + ovalue: function () { + stack.push({container: container, state: 'ocomma', key: key}); + container = []; + state = 'firstavalue'; + }, + firstavalue: function () { + stack.push({container: container, state: 'acomma'}); + container = []; + state = 'firstavalue'; + }, + avalue: function () { + stack.push({container: container, state: 'acomma'}); + container = []; + state = 'firstavalue'; + } + }, + ']': { + firstavalue: function () { + var pop = stack.pop(); + value = container; + container = pop.container; + key = pop.key; + state = pop.state; + }, + acomma: function () { + var pop = stack.pop(); + container.push(value); + value = container; + container = pop.container; + key = pop.key; + state = pop.state; + } + }, + ':': { + colon: function () { + if (Object.hasOwnProperty.call(container, key)) { + throw new SyntaxError('Duplicate key "' + key + '"'); + } + state = 'ovalue'; + } + }, + ',': { + ocomma: function () { + container[key] = value; + state = 'okey'; + }, + acomma: function () { + container.push(value); + state = 'avalue'; + } + }, + 'true': { + go: function () { + value = true; + state = 'ok'; + }, + ovalue: function () { + value = true; + state = 'ocomma'; + }, + firstavalue: function () { + value = true; + state = 'acomma'; + }, + avalue: function () { + value = true; + state = 'acomma'; + } + }, + 'false': { + go: function () { + value = false; + state = 'ok'; + }, + ovalue: function () { + value = false; + state = 'ocomma'; + }, + firstavalue: function () { + value = false; + state = 'acomma'; + }, + avalue: function () { + value = false; + state = 'acomma'; + } + }, + 'null': { + go: function () { + value = null; + state = 'ok'; + }, + ovalue: function () { + value = null; + state = 'ocomma'; + }, + firstavalue: function () { + value = null; + state = 'acomma'; + }, + avalue: function () { + value = null; + state = 'acomma'; + } + } + }; + + function debackslashify(text) { + +// Remove and replace any backslash escapement. + + return text.replace(/\\(?:u(.{4})|([^u]))/g, function (a, b, c) { + return b ? String.fromCharCode(parseInt(b, 16)) : escapes[c]; + }); + } + + return function (source, reviver) { + +// A regular expression is used to extract tokens from the JSON text. +// The extraction process is cautious. + + var r, // The result of the exec method. + tx = /^[\x20\t\n\r]*(?:([,:\[\]{}]|true|false|null)|(-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)|"((?:[^\r\n\t\\\"]|\\(?:["\\\/trnfb]|u[0-9a-fA-F]{4}))*)")/; + +// Set the starting state. + + state = 'go'; + +// The stack records the container, key, and state for each object or array +// that contains another object or array while processing nested structures. + + stack = []; + +// If any error occurs, we will catch it and ultimately throw a syntax error. + + try { + +// For each token... + + for (;;) { + r = tx.exec(source); + if (!r) { + break; + } + +// r is the result array from matching the tokenizing regular expression. +// r[0] contains everything that matched, including any initial whitespace. +// r[1] contains any punctuation that was matched, or true, false, or null. +// r[2] contains a matched number, still in string form. +// r[3] contains a matched string, without quotes but with escapement. + + if (r[1]) { + +// Token: Execute the action for this state and token. + + action[r[1]][state](); + + } else if (r[2]) { + +// Number token: Convert the number string into a number value and execute +// the action for this state and number. + + value = +r[2]; + number[state](); + } else { + +// String token: Replace the escapement sequences and execute the action for +// this state and string. + + value = debackslashify(r[3]); + string[state](); + } + +// Remove the token from the string. The loop will continue as long as there +// are tokens. This is a slow process, but it allows the use of ^ matching, +// which assures that no illegal tokens slip through. + + source = source.slice(r[0].length); + } + +// If we find a state/token combination that is illegal, then the action will +// cause an error. We handle the error by simply changing the state. + + } catch (e) { + state = e; + } + +// The parsing is finished. If we are not in the final 'ok' state, or if the +// remaining source contains anything except whitespace, then we did not have +//a well-formed JSON text. + + if (state !== 'ok' || /[^\x20\t\n\r]/.test(source)) { + throw state instanceof SyntaxError ? state : new SyntaxError('JSON'); + } + +// If there is a reviver function, we recursively walk the new structure, +// passing each name/value pair to the reviver function for possible +// transformation, starting with a temporary root object that holds the current +// value in an empty key. If there is not a reviver function, we simply return +// that value. + + return typeof reviver === 'function' ? (function walk(holder, key) { + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + }({'': value}, '')) : value; + }; +}());