Upgrade to ExtJS 3.2.0 - Released 03/30/2010
[extjs.git] / src / ext-core / src / util / JSON.js
1 /*!
2  * Ext JS Library 3.2.0
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.util.JSON
9  * Modified version of Douglas Crockford"s json.js that doesn"t
10  * mess with the Object prototype
11  * http://www.json.org/js.html
12  * @singleton
13  */
14 Ext.util.JSON = new (function(){
15     var useHasOwn = !!{}.hasOwnProperty,
16         isNative = function() {
17             var useNative = null;
18
19             return function() {
20                 if (useNative === null) {
21                     useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
22                 }
23         
24                 return useNative;
25             };
26         }(),
27         pad = function(n) {
28             return n < 10 ? "0" + n : n;
29         },
30         doDecode = function(json){
31             return eval("(" + json + ')');    
32         },
33         doEncode = function(o){
34             if(!Ext.isDefined(o) || o === null){
35                 return "null";
36             }else if(Ext.isArray(o)){
37                 return encodeArray(o);
38             }else if(Ext.isDate(o)){
39                 return Ext.util.JSON.encodeDate(o);
40             }else if(Ext.isString(o)){
41                 return encodeString(o);
42             }else if(typeof o == "number"){
43                 //don't use isNumber here, since finite checks happen inside isNumber
44                 return isFinite(o) ? String(o) : "null";
45             }else if(Ext.isBoolean(o)){
46                 return String(o);
47             }else {
48                 var a = ["{"], b, i, v;
49                 for (i in o) {
50                     // don't encode DOM objects
51                     if(!o.getElementsByTagName){
52                         if(!useHasOwn || o.hasOwnProperty(i)) {
53                             v = o[i];
54                             switch (typeof v) {
55                             case "undefined":
56                             case "function":
57                             case "unknown":
58                                 break;
59                             default:
60                                 if(b){
61                                     a.push(',');
62                                 }
63                                 a.push(doEncode(i), ":",
64                                         v === null ? "null" : doEncode(v));
65                                 b = true;
66                             }
67                         }
68                     }
69                 }
70                 a.push("}");
71                 return a.join("");
72             }    
73         },
74         m = {
75             "\b": '\\b',
76             "\t": '\\t',
77             "\n": '\\n',
78             "\f": '\\f',
79             "\r": '\\r',
80             '"' : '\\"',
81             "\\": '\\\\'
82         },
83         encodeString = function(s){
84             if (/["\\\x00-\x1f]/.test(s)) {
85                 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
86                     var c = m[b];
87                     if(c){
88                         return c;
89                     }
90                     c = b.charCodeAt();
91                     return "\\u00" +
92                         Math.floor(c / 16).toString(16) +
93                         (c % 16).toString(16);
94                 }) + '"';
95             }
96             return '"' + s + '"';
97         },
98         encodeArray = function(o){
99             var a = ["["], b, i, l = o.length, v;
100                 for (i = 0; i < l; i += 1) {
101                     v = o[i];
102                     switch (typeof v) {
103                         case "undefined":
104                         case "function":
105                         case "unknown":
106                             break;
107                         default:
108                             if (b) {
109                                 a.push(',');
110                             }
111                             a.push(v === null ? "null" : Ext.util.JSON.encode(v));
112                             b = true;
113                     }
114                 }
115                 a.push("]");
116                 return a.join("");
117         };
118
119     /**
120      * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
121      * <b>The returned value includes enclosing double quotation marks.</b></p>
122      * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
123      * <p>To override this:</p><pre><code>
124 Ext.util.JSON.encodeDate = function(d) {
125     return d.format('"Y-m-d"');
126 };
127 </code></pre>
128      * @param {Date} d The Date to encode
129      * @return {String} The string literal to use in a JSON string.
130      */
131     this.encodeDate = function(o){
132         return '"' + o.getFullYear() + "-" +
133                 pad(o.getMonth() + 1) + "-" +
134                 pad(o.getDate()) + "T" +
135                 pad(o.getHours()) + ":" +
136                 pad(o.getMinutes()) + ":" +
137                 pad(o.getSeconds()) + '"';
138     };
139
140     /**
141      * Encodes an Object, Array or other value
142      * @param {Mixed} o The variable to encode
143      * @return {String} The JSON string
144      */
145     this.encode = function() {
146         var ec;
147         return function(o) {
148             if (!ec) {
149                 // setup encoding function on first access
150                 ec = isNative() ? JSON.stringify : doEncode;
151             }
152             return ec(o);
153         };
154     }();
155
156
157     /**
158      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
159      * @param {String} json The JSON string
160      * @return {Object} The resulting object
161      */
162     this.decode = function() {
163         var dc;
164         return function(json) {
165             if (!dc) {
166                 // setup decoding function on first access
167                 dc = isNative() ? JSON.parse : doDecode;
168             }
169             return dc(json);
170         };
171     }();
172
173 })();
174 /**
175  * Shorthand for {@link Ext.util.JSON#encode}
176  * @param {Mixed} o The variable to encode
177  * @return {String} The JSON string
178  * @member Ext
179  * @method encode
180  */
181 Ext.encode = Ext.util.JSON.encode;
182 /**
183  * Shorthand for {@link Ext.util.JSON#decode}
184  * @param {String} json The JSON string
185  * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
186  * @return {Object} The resulting object
187  * @member Ext
188  * @method decode
189  */
190 Ext.decode = Ext.util.JSON.decode;