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 }