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 } |
