comparison 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
comparison
equal deleted inserted replaced
59:59ecc742ab8e 60:8250c977bc50
1 /*
2 json_parse_state.js
3 2012-06-01
4
5 Public Domain.
6
7 NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8
9 This file creates a json_parse function.
10
11 json_parse(text, reviver)
12 This method parses a JSON text to produce an object or array.
13 It can throw a SyntaxError exception.
14
15 The optional reviver parameter is a function that can filter and
16 transform the results. It receives each of the keys and values,
17 and its return value is used instead of the original value.
18 If it returns what it received, then the structure is not modified.
19 If it returns undefined then the member is deleted.
20
21 Example:
22
23 // Parse the text. Values that look like ISO date strings will
24 // be converted to Date objects.
25
26 myData = json_parse(text, function (key, value) {
27 var a;
28 if (typeof value === 'string') {
29 a =
30 /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
31 if (a) {
32 return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
33 +a[5], +a[6]));
34 }
35 }
36 return value;
37 });
38
39 This is a reference implementation. You are free to copy, modify, or
40 redistribute.
41
42 This code should be minified before deployment.
43 See http://javascript.crockford.com/jsmin.html
44
45 USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
46 NOT CONTROL.
47 */
48
49 /*jslint regexp: false*/
50
51 /*members "", "\"", ",", "\/", ":", "[", "\\", "]", acomma, avalue, b,
52 call, colon, container, exec, f, false, firstavalue, firstokey,
53 fromCharCode, go, hasOwnProperty, key, length, n, null, ocomma, okey,
54 ovalue, pop, prototype, push, r, replace, slice, state, t, test, true,
55 value, "{", "}"
56 */
57
58 var json_parse = (function () {
59 "use strict";
60
61 // This function creates a JSON parse function that uses a state machine rather
62 // than the dangerous eval function to parse a JSON text.
63
64 var state, // The state of the parser, one of
65 // 'go' The starting state
66 // 'ok' The final, accepting state
67 // 'firstokey' Ready for the first key of the object or
68 // the closing of an empty object
69 // 'okey' Ready for the next key of the object
70 // 'colon' Ready for the colon
71 // 'ovalue' Ready for the value half of a key/value pair
72 // 'ocomma' Ready for a comma or closing }
73 // 'firstavalue' Ready for the first value of an array or
74 // an empty array
75 // 'avalue' Ready for the next value of an array
76 // 'acomma' Ready for a comma or closing ]
77 stack, // The stack, for controlling nesting.
78 container, // The current container object or array
79 key, // The current key
80 value, // The current value
81 escapes = { // Escapement translation table
82 '\\': '\\',
83 '"': '"',
84 '/': '/',
85 't': '\t',
86 'n': '\n',
87 'r': '\r',
88 'f': '\f',
89 'b': '\b'
90 },
91 string = { // The actions for string tokens
92 go: function () {
93 state = 'ok';
94 },
95 firstokey: function () {
96 key = value;
97 state = 'colon';
98 },
99 okey: function () {
100 key = value;
101 state = 'colon';
102 },
103 ovalue: function () {
104 state = 'ocomma';
105 },
106 firstavalue: function () {
107 state = 'acomma';
108 },
109 avalue: function () {
110 state = 'acomma';
111 }
112 },
113 number = { // The actions for number tokens
114 go: function () {
115 state = 'ok';
116 },
117 ovalue: function () {
118 state = 'ocomma';
119 },
120 firstavalue: function () {
121 state = 'acomma';
122 },
123 avalue: function () {
124 state = 'acomma';
125 }
126 },
127 action = {
128
129 // The action table describes the behavior of the machine. It contains an
130 // object for each token. Each object contains a method that is called when
131 // a token is matched in a state. An object will lack a method for illegal
132 // states.
133
134 '{': {
135 go: function () {
136 stack.push({state: 'ok'});
137 container = {};
138 state = 'firstokey';
139 },
140 ovalue: function () {
141 stack.push({container: container, state: 'ocomma', key: key});
142 container = {};
143 state = 'firstokey';
144 },
145 firstavalue: function () {
146 stack.push({container: container, state: 'acomma'});
147 container = {};
148 state = 'firstokey';
149 },
150 avalue: function () {
151 stack.push({container: container, state: 'acomma'});
152 container = {};
153 state = 'firstokey';
154 }
155 },
156 '}': {
157 firstokey: function () {
158 var pop = stack.pop();
159 value = container;
160 container = pop.container;
161 key = pop.key;
162 state = pop.state;
163 },
164 ocomma: function () {
165 var pop = stack.pop();
166 container[key] = value;
167 value = container;
168 container = pop.container;
169 key = pop.key;
170 state = pop.state;
171 }
172 },
173 '[': {
174 go: function () {
175 stack.push({state: 'ok'});
176 container = [];
177 state = 'firstavalue';
178 },
179 ovalue: function () {
180 stack.push({container: container, state: 'ocomma', key: key});
181 container = [];
182 state = 'firstavalue';
183 },
184 firstavalue: function () {
185 stack.push({container: container, state: 'acomma'});
186 container = [];
187 state = 'firstavalue';
188 },
189 avalue: function () {
190 stack.push({container: container, state: 'acomma'});
191 container = [];
192 state = 'firstavalue';
193 }
194 },
195 ']': {
196 firstavalue: function () {
197 var pop = stack.pop();
198 value = container;
199 container = pop.container;
200 key = pop.key;
201 state = pop.state;
202 },
203 acomma: function () {
204 var pop = stack.pop();
205 container.push(value);
206 value = container;
207 container = pop.container;
208 key = pop.key;
209 state = pop.state;
210 }
211 },
212 ':': {
213 colon: function () {
214 if (Object.hasOwnProperty.call(container, key)) {
215 throw new SyntaxError('Duplicate key "' + key + '"');
216 }
217 state = 'ovalue';
218 }
219 },
220 ',': {
221 ocomma: function () {
222 container[key] = value;
223 state = 'okey';
224 },
225 acomma: function () {
226 container.push(value);
227 state = 'avalue';
228 }
229 },
230 'true': {
231 go: function () {
232 value = true;
233 state = 'ok';
234 },
235 ovalue: function () {
236 value = true;
237 state = 'ocomma';
238 },
239 firstavalue: function () {
240 value = true;
241 state = 'acomma';
242 },
243 avalue: function () {
244 value = true;
245 state = 'acomma';
246 }
247 },
248 'false': {
249 go: function () {
250 value = false;
251 state = 'ok';
252 },
253 ovalue: function () {
254 value = false;
255 state = 'ocomma';
256 },
257 firstavalue: function () {
258 value = false;
259 state = 'acomma';
260 },
261 avalue: function () {
262 value = false;
263 state = 'acomma';
264 }
265 },
266 'null': {
267 go: function () {
268 value = null;
269 state = 'ok';
270 },
271 ovalue: function () {
272 value = null;
273 state = 'ocomma';
274 },
275 firstavalue: function () {
276 value = null;
277 state = 'acomma';
278 },
279 avalue: function () {
280 value = null;
281 state = 'acomma';
282 }
283 }
284 };
285
286 function debackslashify(text) {
287
288 // Remove and replace any backslash escapement.
289
290 return text.replace(/\\(?:u(.{4})|([^u]))/g, function (a, b, c) {
291 return b ? String.fromCharCode(parseInt(b, 16)) : escapes[c];
292 });
293 }
294
295 return function (source, reviver) {
296
297 // A regular expression is used to extract tokens from the JSON text.
298 // The extraction process is cautious.
299
300 var r, // The result of the exec method.
301 tx = /^[\x20\t\n\r]*(?:([,:\[\]{}]|true|false|null)|(-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)|"((?:[^\r\n\t\\\"]|\\(?:["\\\/trnfb]|u[0-9a-fA-F]{4}))*)")/;
302
303 // Set the starting state.
304
305 state = 'go';
306
307 // The stack records the container, key, and state for each object or array
308 // that contains another object or array while processing nested structures.
309
310 stack = [];
311
312 // If any error occurs, we will catch it and ultimately throw a syntax error.
313
314 try {
315
316 // For each token...
317
318 for (;;) {
319 r = tx.exec(source);
320 if (!r) {
321 break;
322 }
323
324 // r is the result array from matching the tokenizing regular expression.
325 // r[0] contains everything that matched, including any initial whitespace.
326 // r[1] contains any punctuation that was matched, or true, false, or null.
327 // r[2] contains a matched number, still in string form.
328 // r[3] contains a matched string, without quotes but with escapement.
329
330 if (r[1]) {
331
332 // Token: Execute the action for this state and token.
333
334 action[r[1]][state]();
335
336 } else if (r[2]) {
337
338 // Number token: Convert the number string into a number value and execute
339 // the action for this state and number.
340
341 value = +r[2];
342 number[state]();
343 } else {
344
345 // String token: Replace the escapement sequences and execute the action for
346 // this state and string.
347
348 value = debackslashify(r[3]);
349 string[state]();
350 }
351
352 // Remove the token from the string. The loop will continue as long as there
353 // are tokens. This is a slow process, but it allows the use of ^ matching,
354 // which assures that no illegal tokens slip through.
355
356 source = source.slice(r[0].length);
357 }
358
359 // If we find a state/token combination that is illegal, then the action will
360 // cause an error. We handle the error by simply changing the state.
361
362 } catch (e) {
363 state = e;
364 }
365
366 // The parsing is finished. If we are not in the final 'ok' state, or if the
367 // remaining source contains anything except whitespace, then we did not have
368 //a well-formed JSON text.
369
370 if (state !== 'ok' || /[^\x20\t\n\r]/.test(source)) {
371 throw state instanceof SyntaxError ? state : new SyntaxError('JSON');
372 }
373
374 // If there is a reviver function, we recursively walk the new structure,
375 // passing each name/value pair to the reviver function for possible
376 // transformation, starting with a temporary root object that holds the current
377 // value in an empty key. If there is not a reviver function, we simply return
378 // that value.
379
380 return typeof reviver === 'function' ? (function walk(holder, key) {
381 var k, v, value = holder[key];
382 if (value && typeof value === 'object') {
383 for (k in value) {
384 if (Object.prototype.hasOwnProperty.call(value, k)) {
385 v = walk(value, k);
386 if (v !== undefined) {
387 value[k] = v;
388 } else {
389 delete value[k];
390 }
391 }
392 }
393 }
394 return reviver.call(holder, key, value);
395 }({'': value}, '')) : value;
396 };
397 }());