Mercurial > wikked
comparison static/js/jsonjs/cycle.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/cycle.js@c946f4facfa2 |
children |
comparison
equal
deleted
inserted
replaced
59:59ecc742ab8e | 60:8250c977bc50 |
---|---|
1 /* | |
2 cycle.js | |
3 2012-08-19 | |
4 | |
5 Public Domain. | |
6 | |
7 NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. | |
8 | |
9 This code should be minified before deployment. | |
10 See http://javascript.crockford.com/jsmin.html | |
11 | |
12 USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO | |
13 NOT CONTROL. | |
14 */ | |
15 | |
16 /*jslint evil: true, regexp: true */ | |
17 | |
18 /*members $ref, apply, call, decycle, hasOwnProperty, length, prototype, push, | |
19 retrocycle, stringify, test, toString | |
20 */ | |
21 | |
22 if (typeof JSON.decycle !== 'function') { | |
23 JSON.decycle = function decycle(object) { | |
24 'use strict'; | |
25 | |
26 // Make a deep copy of an object or array, assuring that there is at most | |
27 // one instance of each object or array in the resulting structure. The | |
28 // duplicate references (which might be forming cycles) are replaced with | |
29 // an object of the form | |
30 // {$ref: PATH} | |
31 // where the PATH is a JSONPath string that locates the first occurance. | |
32 // So, | |
33 // var a = []; | |
34 // a[0] = a; | |
35 // return JSON.stringify(JSON.decycle(a)); | |
36 // produces the string '[{"$ref":"$"}]'. | |
37 | |
38 // JSONPath is used to locate the unique object. $ indicates the top level of | |
39 // the object or array. [NUMBER] or [STRING] indicates a child member or | |
40 // property. | |
41 | |
42 var objects = [], // Keep a reference to each unique object or array | |
43 paths = []; // Keep the path to each unique object or array | |
44 | |
45 return (function derez(value, path) { | |
46 | |
47 // The derez recurses through the object, producing the deep copy. | |
48 | |
49 var i, // The loop counter | |
50 name, // Property name | |
51 nu; // The new object or array | |
52 | |
53 switch (typeof value) { | |
54 case 'object': | |
55 | |
56 // typeof null === 'object', so get out if this value is not really an object. | |
57 // Also get out if it is a weird builtin object. | |
58 | |
59 if (value === null || | |
60 value instanceof Boolean || | |
61 value instanceof Date || | |
62 value instanceof Number || | |
63 value instanceof RegExp || | |
64 value instanceof String) { | |
65 return value; | |
66 } | |
67 | |
68 // If the value is an object or array, look to see if we have already | |
69 // encountered it. If so, return a $ref/path object. This is a hard way, | |
70 // linear search that will get slower as the number of unique objects grows. | |
71 | |
72 for (i = 0; i < objects.length; i += 1) { | |
73 if (objects[i] === value) { | |
74 return {$ref: paths[i]}; | |
75 } | |
76 } | |
77 | |
78 // Otherwise, accumulate the unique value and its path. | |
79 | |
80 objects.push(value); | |
81 paths.push(path); | |
82 | |
83 // If it is an array, replicate the array. | |
84 | |
85 if (Object.prototype.toString.apply(value) === '[object Array]') { | |
86 nu = []; | |
87 for (i = 0; i < value.length; i += 1) { | |
88 nu[i] = derez(value[i], path + '[' + i + ']'); | |
89 } | |
90 } else { | |
91 | |
92 // If it is an object, replicate the object. | |
93 | |
94 nu = {}; | |
95 for (name in value) { | |
96 if (Object.prototype.hasOwnProperty.call(value, name)) { | |
97 nu[name] = derez(value[name], | |
98 path + '[' + JSON.stringify(name) + ']'); | |
99 } | |
100 } | |
101 } | |
102 return nu; | |
103 case 'number': | |
104 case 'string': | |
105 case 'boolean': | |
106 return value; | |
107 } | |
108 }(object, '$')); | |
109 }; | |
110 } | |
111 | |
112 | |
113 if (typeof JSON.retrocycle !== 'function') { | |
114 JSON.retrocycle = function retrocycle($) { | |
115 'use strict'; | |
116 | |
117 // Restore an object that was reduced by decycle. Members whose values are | |
118 // objects of the form | |
119 // {$ref: PATH} | |
120 // are replaced with references to the value found by the PATH. This will | |
121 // restore cycles. The object will be mutated. | |
122 | |
123 // The eval function is used to locate the values described by a PATH. The | |
124 // root object is kept in a $ variable. A regular expression is used to | |
125 // assure that the PATH is extremely well formed. The regexp contains nested | |
126 // * quantifiers. That has been known to have extremely bad performance | |
127 // problems on some browsers for very long strings. A PATH is expected to be | |
128 // reasonably short. A PATH is allowed to belong to a very restricted subset of | |
129 // Goessner's JSONPath. | |
130 | |
131 // So, | |
132 // var s = '[{"$ref":"$"}]'; | |
133 // return JSON.retrocycle(JSON.parse(s)); | |
134 // produces an array containing a single element which is the array itself. | |
135 | |
136 var px = | |
137 /^\$(?:\[(?:\d+|\"(?:[^\\\"\u0000-\u001f]|\\([\\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*\")\])*$/; | |
138 | |
139 (function rez(value) { | |
140 | |
141 // The rez function walks recursively through the object looking for $ref | |
142 // properties. When it finds one that has a value that is a path, then it | |
143 // replaces the $ref object with a reference to the value that is found by | |
144 // the path. | |
145 | |
146 var i, item, name, path; | |
147 | |
148 if (value && typeof value === 'object') { | |
149 if (Object.prototype.toString.apply(value) === '[object Array]') { | |
150 for (i = 0; i < value.length; i += 1) { | |
151 item = value[i]; | |
152 if (item && typeof item === 'object') { | |
153 path = item.$ref; | |
154 if (typeof path === 'string' && px.test(path)) { | |
155 value[i] = eval(path); | |
156 } else { | |
157 rez(item); | |
158 } | |
159 } | |
160 } | |
161 } else { | |
162 for (name in value) { | |
163 if (typeof value[name] === 'object') { | |
164 item = value[name]; | |
165 if (item) { | |
166 path = item.$ref; | |
167 if (typeof path === 'string' && px.test(path)) { | |
168 value[name] = eval(path); | |
169 } else { | |
170 rez(item); | |
171 } | |
172 } | |
173 } | |
174 } | |
175 } | |
176 } | |
177 }($)); | |
178 return $; | |
179 }; | |
180 } |