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