1 if (!Monk.utils) Workbench.namespace('Monk.utils'); 2 3 /* 4 Script: JSON.js 5 6 JSON encoder / decoder: 7 This object uses good practices to encode/decode quikly and a bit safer(*) every kind of JSON compatible variable. 8 9 (*) Please read more about JSON and Ajax JavaScript Hijacking problems, <http://www.fortifysoftware.com/advisory.jsp> 10 11 To download last version of this script use this link: <http://www.devpro.it/code/149.html> 12 13 Version: 14 1.3b - modified toDate method, now compatible with milliseconds time too (time or milliseconds/1000) 15 16 Compatibility: 17 FireFox - Version 1, 1.5, 2 and 3 (FireFox uses secure code evaluation) 18 Internet Explorer - Version 5, 5.5, 6 and 7 19 Opera - 8 and 9 (probably 7 too) 20 Safari - Version 2 (probably 1 too) 21 Konqueror - Version 3 or greater 22 23 Dependencies: 24 <JSONError.js> 25 26 Credits: 27 - JSON site for safe RegExp and generic JSON informations, <http://www.json.org/> 28 - kenta for safe evaluation idea, <http://mykenta.blogspot.com/> 29 30 Author: 31 Andrea Giammarchi, <http://www.3site.eu> 32 33 License: 34 >Copyright (C) 2007 Andrea Giammarchi - www.3site.eu 35 > 36 >Permission is hereby granted, free of charge, 37 >to any person obtaining a copy of this software and associated 38 >documentation files (the "Software"), 39 >to deal in the Software without restriction, 40 >including without limitation the rights to use, copy, modify, merge, 41 >publish, distribute, sublicense, and/or sell copies of the Software, 42 >and to permit persons to whom the Software is furnished to do so, 43 >subject to the following conditions: 44 > 45 >The above copyright notice and this permission notice shall be included 46 >in all copies or substantial portions of the Software. 47 > 48 >THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 49 >INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 >FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 51 >IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 52 >DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 53 >ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 54 >OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 55 */ 56 57 /* 58 Object: JSON 59 Stand alone or prototyped encode, decode or toDate public methods. 60 61 Example: 62 >alert(JSON.encode([0,1,false,true,null,[2,3],{"some":"value"}])); 63 >// [0,1,false,true,null,[2,3],{"some":"value"}] 64 > 65 >alert(JSON.decode('[0,1,false,true,null,[2,3],{"some":"value"}]')) 66 >// 0,1,false,true,,2,3,[object Object] 67 */ 68 Monk.utils.json2string = new function(){ 69 70 /* Section: Methods - Public */ 71 72 /* 73 Method: decode 74 decodes a valid JSON encoded string. 75 76 Arguments: 77 [String / Function] - Optional JSON string to decode or a filter function if method is a String prototype. 78 [Function] - Optional filter function if first argument is a JSON string and this method is not a String prototype. 79 80 Returns: 81 Object - Generic JavaScript variable or undefined 82 83 Example [Basic]: 84 >var arr = JSON.decode('[1,2,3]'); 85 >alert(arr); // 1,2,3 86 > 87 >arr = JSON.decode('[1,2,3]', function(key, value){return key * value}); 88 >alert(arr); // 0,2,6 89 90 Example [Prototype]: 91 >String.prototype.parseJSON = JSON.decode; 92 > 93 >alert('[1,2,3]'.parseJSON()); // 1,2,3 94 > 95 >try { 96 > alert('[1,2,3]'.parseJSON(function(key, value){return key * value})); 97 > // 0,2,6 98 >} 99 >catch(e) { 100 > alert(e.message); 101 >} 102 103 Note: 104 Internet Explorer 5 and other old browsers should use a different regular expression to check if a JSON string is valid or not. 105 This old browsers dedicated RegExp is not safe as native version is but it required for compatibility. 106 */ 107 this.decode = function(){ 108 var filter, result, self, tmp; 109 if($$("toString")) { 110 switch(arguments.length){ 111 case 2: 112 self = arguments[0]; 113 filter = arguments[1]; 114 break; 115 case 1: 116 if($[typeof arguments[0]](arguments[0]) === Function) { 117 self = this; 118 filter = arguments[0]; 119 } 120 else 121 self = arguments[0]; 122 break; 123 default: 124 self = this; 125 break; 126 }; 127 if(rc.test(self)){ 128 try{ 129 result = e("(".concat(self, ")")); 130 if(filter && result !== null && (tmp = $[typeof result](result)) && (tmp === Array || tmp === Object)){ 131 for(self in result) 132 result[self] = v(self, result) ? filter(self, result[self]) : result[self]; 133 } 134 } 135 catch(z){} 136 } 137 else { 138 throw "bad data"; 139 } 140 }; 141 return result; 142 }; 143 144 /* 145 Method: encode 146 encode a generic JavaScript variable into a valid JSON string. 147 148 Arguments: 149 [Object] - Optional generic JavaScript variable to encode if method is not an Object prototype. 150 151 Returns: 152 String - Valid JSON string or undefined 153 154 Example [Basic]: 155 >var s = JSON.encode([1,2,3]); 156 >alert(s); // [1,2,3] 157 158 Example [Prototype]: 159 >Object.prototype.toJSONString = JSON.encode; 160 > 161 >alert([1,2,3].toJSONString()); // [1,2,3] 162 */ 163 this.encode = function(){ 164 var self = arguments.length ? arguments[0] : this, 165 result, tmp; 166 if(self === null) 167 result = "null"; 168 else if(self !== undefined && (tmp = $[typeof self](self))) { 169 switch(tmp){ 170 case Array: 171 result = []; 172 for(var i = 0, j = 0, k = self.length; j < k; j++) { 173 if(self[j] !== undefined && (tmp = Monk.utils.json2string.encode(self[j]))) 174 result[i++] = tmp; 175 }; 176 result = "[".concat(result.join(","), "]"); 177 break; 178 case Boolean: 179 result = String(self); 180 break; 181 case Date: 182 result = '"'.concat(self.getFullYear(), '-', d(self.getMonth() + 1), '-', d(self.getDate()), 'T', d(self.getHours()), ':', d(self.getMinutes()), ':', d(self.getSeconds()), '"'); 183 break; 184 case Function: 185 break; 186 case Number: 187 result = isFinite(self) ? String(self) : "null"; 188 break; 189 case String: 190 result = '"'.concat(self.replace(rs, s).replace(ru, u), '"'); 191 break; 192 default: 193 var i = 0, key; 194 result = []; 195 for(key in self) { 196 if(self[key] !== undefined && (tmp = Monk.utils.json2string.encode(self[key]))) 197 result[i++] = '"'.concat(key.replace(rs, s).replace(ru, u), '":', tmp); 198 }; 199 result = "{".concat(result.join(","), "}"); 200 break; 201 } 202 }; 203 return result; 204 }; 205 206 /* 207 Method: toDate 208 transforms a JSON encoded Date string into a native Date object. 209 210 Arguments: 211 [String/Number] - Optional JSON Date string or server time if this method is not a String prototype. Server time should be an integer, based on seconds since 1970/01/01 or milliseconds / 1000 since 1970/01/01. 212 213 Returns: 214 Date - Date object or undefined if string is not a valid Date 215 216 Example [Basic]: 217 >var serverDate = JSON.toDate("2007-04-05T08:36:46"); 218 >alert(serverDate.getMonth()); // 3 (months start from 0) 219 220 Example [Prototype]: 221 >String.prototype.parseDate = JSON.toDate; 222 > 223 >alert("2007-04-05T08:36:46".parseDate().getDate()); // 5 224 225 Example [Server Time]: 226 >var phpServerDate = JSON.toDate(<?php echo time(); ?>); 227 >var csServerDate = JSON.toDate(<%=(DateTime.Now.Ticks/10000-62135596800000)%>/1000); 228 229 Example [Server Time Prototype]: 230 >Number.prototype.parseDate = JSON.toDate; 231 >var phpServerDate = (<?php echo time(); ?>).parseDate(); 232 >var csServerDate = (<%=(DateTime.Now.Ticks/10000-62135596800000)%>/1000).parseDate(); 233 234 Note: 235 This method accepts an integer or numeric string too to mantain compatibility with generic server side time() function. 236 You can convert quickly mtime, ctime, time and other time based values. 237 With languages that supports milliseconds you can send total milliseconds / 1000 (time is set as time * 1000) 238 */ 239 this.toDate = function(){ 240 var self = arguments.length ? arguments[0] : this, 241 result; 242 if(rd.test(self)){ 243 result = new Date; 244 result.setHours(i(self, 11, 2)); 245 result.setMinutes(i(self, 14, 2)); 246 result.setSeconds(i(self, 17, 2)); 247 result.setMonth(i(self, 5, 2) - 1); 248 result.setDate(i(self, 8, 2)); 249 result.setFullYear(i(self, 0, 4)); 250 } 251 else if(rt.test(self)) 252 result = new Date(self * 1000); 253 return result; 254 }; 255 256 /* Section: Properties - Private */ 257 258 /* 259 Property: Private 260 261 List: 262 Object - 'c' - a dictionary with useful keys / values for fast encode convertion 263 Function - 'd' - returns decimal string rappresentation of a number ("14", "03", etc) 264 Function - 'e' - safe and native code evaulation 265 Function - 'i' - returns integer from string ("01" => 1, "15" => 15, etc) 266 Array - 'p' - a list with different "0" strings for fast special chars escape convertion 267 RegExp - 'rc' - regular expression to check JSON strings (different for IE5 or old browsers and new one) 268 RegExp - 'rd' - regular expression to check a JSON Date string 269 RegExp - 'rs' - regular expression to check string chars to modify using c (char) values 270 RegExp - 'rt' - regular expression to check integer numeric string (for toDate time version evaluation) 271 RegExp - 'ru' - regular expression to check string chars to escape using "\u" prefix 272 Function - 's' - returns escaped string adding "\\" char as prefix ("\\" => "\\\\", etc.) 273 Function - 'u' - returns escaped string, modifyng special chars using "\uNNNN" notation 274 Function - 'v' - returns boolean value to skip object methods or prototyped parameters (length, others), used for optional decode filter function 275 Function - '$' - returns object constructor if it was not cracked (someVar = {}; someVar.constructor = String <= ignore them) 276 Function - '$$' - returns boolean value to check native Array and Object constructors before convertion 277 */ 278 var c = {"\b":"b","\t":"t","\n":"n","\f":"f","\r":"r",'"':'"',"\\":"\\","/":"/"}, 279 d = function(n){return n<10?"0".concat(n):n}, 280 e = function(c,f,e){e=eval;delete eval;if(typeof eval==="undefined")eval=e;f=eval(""+c);eval=e;return f}, 281 i = function(e,p,l){return 1*e.substr(p,l)}, 282 p = ["","000","00","0",""], 283 rc = null, 284 rd = /^[0-9]{4}\-[0-9]{2}\-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/, 285 rs = /(\x5c|\x2F|\x22|[\x0c-\x0d]|[\x08-\x0a])/g, 286 rt = /^([0-9]+|[0-9]+[,\.][0-9]{1,3})$/, 287 ru = /([\x00-\x07]|\x0b|[\x0e-\x1f])/g, 288 s = function(i,d){return "\\".concat(c[d])}, 289 u = function(i,d){ 290 var n=d.charCodeAt(0).toString(16); 291 return "\\u".concat(p[n.length],n) 292 }, 293 v = function(k,v){return $[typeof result](result)!==Function&&(v.hasOwnProperty?v.hasOwnProperty(k):v.constructor.prototype[k]!==v[k])}, 294 $ = { 295 "boolean":function(){return Boolean}, 296 "function":function(){return Function}, 297 "number":function(){return Number}, 298 "object":function(o){return o instanceof o.constructor?o.constructor:null}, 299 "string":function(){return String}, 300 "undefined":function(){return null} 301 }, 302 $$ = function(m){ 303 function $(c,t){t=c[m];delete c[m];try{e(c)}catch(z){c[m]=t;return 1}}; 304 return $(Array)&&$(Object) 305 }; 306 try{rc=new RegExp('^("(\\\\.|[^"\\\\\\n\\r])*?"|[,:{}\\[\\]0-9.\\-+Eaeflnr-u \\n\\r\\t])+?$')} 307 catch(z){rc=/^(true|false|null|\[.*\]|\{.*\}|".*"|\d+|\d+\.\d+)$/} 308 };