Always disable Rico debugging
[misc/kostenrechnung] / lib / json_parse.js
1 /*
2     http://www.JSON.org/json_parse.js
3     2009-04-18
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 /*members "", "\"", "\/", "\\", at, b, call, charAt, f, fromCharCode,
50     hasOwnProperty, message, n, name, push, r, t, text
51 */
52
53 /*global json_parse */
54
55 json_parse = (function () {
56
57 // This is a function that can parse a JSON text, producing a JavaScript
58 // data structure. It is a simple, recursive descent parser. It does not use
59 // eval or regular expressions, so it can be used as a model for implementing
60 // a JSON parser in other languages.
61
62 // We are defining the function inside of another function to avoid creating
63 // global variables.
64
65     var at,     // The index of the current character
66         ch,     // The current character
67         escapee = {
68             '"':  '"',
69             '\\': '\\',
70             '/':  '/',
71             b:    '\b',
72             f:    '\f',
73             n:    '\n',
74             r:    '\r',
75             t:    '\t'
76         },
77         text,
78
79         error = function (m) {
80
81 // Call error when something is wrong.
82
83             throw {
84                 name:    'SyntaxError',
85                 message: m,
86                 at:      at,
87                 text:    text
88             };
89         },
90
91         next = function (c) {
92
93 // If a c parameter is provided, verify that it matches the current character.
94
95             if (c && c !== ch) {
96                 error("Expected '" + c + "' instead of '" + ch + "'");
97             }
98
99 // Get the next character. When there are no more characters,
100 // return the empty string.
101
102             ch = text.charAt(at);
103             at += 1;
104             return ch;
105         },
106
107         number = function () {
108
109 // Parse a number value.
110
111             var number,
112                 string = '';
113
114             if (ch === '-') {
115                 string = '-';
116                 next('-');
117             }
118             while (ch >= '0' && ch <= '9') {
119                 string += ch;
120                 next();
121             }
122             if (ch === '.') {
123                 string += '.';
124                 while (next() && ch >= '0' && ch <= '9') {
125                     string += ch;
126                 }
127             }
128             if (ch === 'e' || ch === 'E') {
129                 string += ch;
130                 next();
131                 if (ch === '-' || ch === '+') {
132                     string += ch;
133                     next();
134                 }
135                 while (ch >= '0' && ch <= '9') {
136                     string += ch;
137                     next();
138                 }
139             }
140             number = +string;
141             if (isNaN(number)) {
142                 error("Bad number");
143             } else {
144                 return number;
145             }
146         },
147
148         string = function () {
149
150 // Parse a string value.
151
152             var hex,
153                 i,
154                 string = '',
155                 uffff;
156
157 // When parsing for string values, we must look for " and \ characters.
158
159             if (ch === '"') {
160                 while (next()) {
161                     if (ch === '"') {
162                         next();
163                         return string;
164                     } else if (ch === '\\') {
165                         next();
166                         if (ch === 'u') {
167                             uffff = 0;
168                             for (i = 0; i < 4; i += 1) {
169                                 hex = parseInt(next(), 16);
170                                 if (!isFinite(hex)) {
171                                     break;
172                                 }
173                                 uffff = uffff * 16 + hex;
174                             }
175                             string += String.fromCharCode(uffff);
176                         } else if (typeof escapee[ch] === 'string') {
177                             string += escapee[ch];
178                         } else {
179                             break;
180                         }
181                     } else {
182                         string += ch;
183                     }
184                 }
185             }
186             error("Bad string");
187         },
188
189         white = function () {
190
191 // Skip whitespace.
192
193             while (ch && ch <= ' ') {
194                 next();
195             }
196         },
197
198         word = function () {
199
200 // true, false, or null.
201
202             switch (ch) {
203             case 't':
204                 next('t');
205                 next('r');
206                 next('u');
207                 next('e');
208                 return true;
209             case 'f':
210                 next('f');
211                 next('a');
212                 next('l');
213                 next('s');
214                 next('e');
215                 return false;
216             case 'n':
217                 next('n');
218                 next('u');
219                 next('l');
220                 next('l');
221                 return null;
222             }
223             error("Unexpected '" + ch + "'");
224         },
225
226         value,  // Place holder for the value function.
227
228         array = function () {
229
230 // Parse an array value.
231
232             var array = [];
233
234             if (ch === '[') {
235                 next('[');
236                 white();
237                 if (ch === ']') {
238                     next(']');
239                     return array;   // empty array
240                 }
241                 while (ch) {
242                     array.push(value());
243                     white();
244                     if (ch === ']') {
245                         next(']');
246                         return array;
247                     }
248                     next(',');
249                     white();
250                 }
251             }
252             error("Bad array");
253         },
254
255         object = function () {
256
257 // Parse an object value.
258
259             var key,
260                 object = {};
261
262             if (ch === '{') {
263                 next('{');
264                 white();
265                 if (ch === '}') {
266                     next('}');
267                     return object;   // empty object
268                 }
269                 while (ch) {
270                     key = string();
271                     white();
272                     next(':');
273                     if (Object.hasOwnProperty.call(object, key)) {
274                         error('Duplicate key "' + key + '"');
275                     }
276                     object[key] = value();
277                     white();
278                     if (ch === '}') {
279                         next('}');
280                         return object;
281                     }
282                     next(',');
283                     white();
284                 }
285             }
286             error("Bad object");
287         };
288
289     value = function () {
290
291 // Parse a JSON value. It could be an object, an array, a string, a number,
292 // or a word.
293
294         white();
295         switch (ch) {
296         case '{':
297             return object();
298         case '[':
299             return array();
300         case '"':
301             return string();
302         case '-':
303             return number();
304         default:
305             return ch >= '0' && ch <= '9' ? number() : word();
306         }
307     };
308
309 // Return the json_parse function. It will have access to all of the above
310 // functions and variables.
311
312     return function (source, reviver) {
313         var result;
314
315         text = source;
316         at = 0;
317         ch = ' ';
318         result = value();
319         white();
320         if (ch) {
321             error("Syntax error");
322         }
323
324 // If there is a reviver function, we recursively walk the new structure,
325 // passing each name/value pair to the reviver function for possible
326 // transformation, starting with a temporary root object that holds the result
327 // in an empty key. If there is not a reviver function, we simply return the
328 // result.
329
330         return typeof reviver === 'function' ? (function walk(holder, key) {
331             var k, v, value = holder[key];
332             if (value && typeof value === 'object') {
333                 for (k in value) {
334                     if (Object.hasOwnProperty.call(value, k)) {
335                         v = walk(value, k);
336                         if (v !== undefined) {
337                             value[k] = v;
338                         } else {
339                             delete value[k];
340                         }
341                     }
342                 }
343             }
344             return reviver.call(holder, key, value);
345         }({'': result}, '')) : result;
346     };
347 }());