diff options
Diffstat (limited to 'js/dojo/dojox/secure/fromJson.js')
| -rw-r--r-- | js/dojo/dojox/secure/fromJson.js | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/js/dojo/dojox/secure/fromJson.js b/js/dojo/dojox/secure/fromJson.js new file mode 100644 index 0000000..263be3a --- /dev/null +++ b/js/dojo/dojox/secure/fromJson.js @@ -0,0 +1,245 @@ +//>>built +// wrapped by build app +define("dojox/secure/fromJson", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.secure.fromJson"); + +// Used with permission from Mike Samuel of Google (has CCLA), from the json-sans-eval project: +// http://code.google.com/p/json-sans-eval/ +// Mike Samuel <mikesamuel@gmail.com> + + + +dojox.secure.fromJson = typeof JSON != "undefined" ? JSON.parse : +// summary: +// Parses a string of well-formed JSON text. +// description: +// Parses a string of well-formed JSON text. If the input is not well-formed, +// then behavior is undefined, but it is +// deterministic and is guaranteed not to modify any object other than its +// return value. +// +// This does not use `eval` so is less likely to have obscure security bugs than +// json2.js. +// It is optimized for speed, so is much faster than json_parse.js. +// +// This library should be used whenever security is a concern (when JSON may +// come from an untrusted source), speed is a concern, and erroring on malformed +// JSON is *not* a concern. +// +// json2.js is very fast, but potentially insecure since it calls `eval` to +// parse JSON data, so an attacker might be able to supply strange JS that +// looks like JSON, but that executes arbitrary javascript. +// +// To configure dojox.secure.fromJson as the JSON parser for all Dojo +// JSON parsing, simply do: +// | dojo.require("dojox.secure.fromJson"); +// | dojo.fromJson = dojox.secure.fromJson; +// or alternately you could configure dojox.secure.fromJson to only handle +// XHR responses: +// | dojo._contentHandlers.json = function(xhr){ +// | return dojox.secure.fromJson.fromJson(xhr.responseText); +// | }; +// +// json: String +// per RFC 4627 +// optReviver: Function (this:Object, string, *) +// optional function +// that reworks JSON objects post-parse per Chapter 15.12 of EcmaScript3.1. +// If supplied, the function is called with a string key, and a value. +// The value is the property of 'this'. The reviver should return +// the value to use in its place. So if dates were serialized as +// {@code { "type": "Date", "time": 1234 }}, then a reviver might look like +// {@code +// function (key, value) { +// if (value && typeof value === 'object' && 'Date' === value.type) { +// return new Date(value.time); +// } else { +// return value; +// } +// }}. +// If the reviver returns {@code undefined} then the property named by key +// will be deleted from its container. +// {@code this} is bound to the object containing the specified property. +// returns: {Object|Array} +(function () { + var number + = '(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)'; + var oneChar = '(?:[^\\0-\\x08\\x0a-\\x1f\"\\\\]' + + '|\\\\(?:[\"/\\\\bfnrt]|u[0-9A-Fa-f]{4}))'; + var string = '(?:\"' + oneChar + '*\")'; + + // Will match a value in a well-formed JSON file. + // If the input is not well-formed, may match strangely, but not in an unsafe + // way. + // Since this only matches value tokens, it does not match whitespace, colons, + // or commas. + var jsonToken = new RegExp( + '(?:false|true|null|[\\{\\}\\[\\]]' + + '|' + number + + '|' + string + + ')', 'g'); + + // Matches escape sequences in a string literal + var escapeSequence = new RegExp('\\\\(?:([^u])|u(.{4}))', 'g'); + + // Decodes escape sequences in object literals + var escapes = { + '"': '"', + '/': '/', + '\\': '\\', + 'b': '\b', + 'f': '\f', + 'n': '\n', + 'r': '\r', + 't': '\t' + }; + function unescapeOne(_, ch, hex) { + return ch ? escapes[ch] : String.fromCharCode(parseInt(hex, 16)); + } + + // A non-falsy value that coerces to the empty string when used as a key. + var EMPTY_STRING = new String(''); + var SLASH = '\\'; + + // Constructor to use based on an open token. + var firstTokenCtors = { '{': Object, '[': Array }; + + var hop = Object.hasOwnProperty; + + return function (json, opt_reviver) { + // Split into tokens + var toks = json.match(jsonToken); + // Construct the object to return + var result; + var tok = toks[0]; + var topLevelPrimitive = false; + if ('{' === tok) { + result = {}; + } else if ('[' === tok) { + result = []; + } else { + // The RFC only allows arrays or objects at the top level, but the JSON.parse + // defined by the EcmaScript 5 draft does allow strings, booleans, numbers, and null + // at the top level. + result = []; + topLevelPrimitive = true; + } + + // If undefined, the key in an object key/value record to use for the next + // value parsed. + var key; + // Loop over remaining tokens maintaining a stack of uncompleted objects and + // arrays. + var stack = [result]; + for (var i = 1 - topLevelPrimitive, n = toks.length; i < n; ++i) { + tok = toks[i]; + + var cont; + switch (tok.charCodeAt(0)) { + default: // sign or digit + cont = stack[0]; + cont[key || cont.length] = +(tok); + key = void 0; + break; + case 0x22: // '"' + tok = tok.substring(1, tok.length - 1); + if (tok.indexOf(SLASH) !== -1) { + tok = tok.replace(escapeSequence, unescapeOne); + } + cont = stack[0]; + if (!key) { + if (cont instanceof Array) { + key = cont.length; + } else { + key = tok || EMPTY_STRING; // Use as key for next value seen. + break; + } + } + cont[key] = tok; + key = void 0; + break; + case 0x5b: // '[' + cont = stack[0]; + stack.unshift(cont[key || cont.length] = []); + key = void 0; + break; + case 0x5d: // ']' + stack.shift(); + break; + case 0x66: // 'f' + cont = stack[0]; + cont[key || cont.length] = false; + key = void 0; + break; + case 0x6e: // 'n' + cont = stack[0]; + cont[key || cont.length] = null; + key = void 0; + break; + case 0x74: // 't' + cont = stack[0]; + cont[key || cont.length] = true; + key = void 0; + break; + case 0x7b: // '{' + cont = stack[0]; + stack.unshift(cont[key || cont.length] = {}); + key = void 0; + break; + case 0x7d: // '}' + stack.shift(); + break; + } + } + // Fail if we've got an uncompleted object. + if (topLevelPrimitive) { + if (stack.length !== 1) { throw new Error(); } + result = result[0]; + } else { + if (stack.length) { throw new Error(); } + } + + if (opt_reviver) { + // Based on walk as implemented in http://www.json.org/json2.js + var walk = function (holder, key) { + var value = holder[key]; + if (value && typeof value === 'object') { + var toDelete = null; + for (var k in value) { + if (hop.call(value, k) && value !== holder) { + // Recurse to properties first. This has the effect of causing + // the reviver to be called on the object graph depth-first. + + // Since 'this' is bound to the holder of the property, the + // reviver can access sibling properties of k including ones + // that have not yet been revived. + + // The value returned by the reviver is used in place of the + // current value of property k. + // If it returns undefined then the property is deleted. + var v = walk(value, k); + if (v !== void 0) { + value[k] = v; + } else { + // Deleting properties inside the loop has vaguely defined + // semantics in ES3 and ES3.1. + if (!toDelete) { toDelete = []; } + toDelete.push(k); + } + } + } + if (toDelete) { + for (var i = toDelete.length; --i >= 0;) { + delete value[toDelete[i]]; + } + } + } + return opt_reviver.call(holder, key, value); + }; + result = walk({ '': result }, ''); + } + + return result; + }; +})(); +}); |
