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