Upgrade to ExtJS 3.0.0 - Released 07/06/2009
[extjs.git] / src / util / core / JSON.js
1 /*!
2  * Ext JS Library 3.0.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
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(typeof o == "undefined" || o === null){
35                 return "null";
36             }else if(Ext.isArray(o)){
37                 return encodeArray(o);
38             }else if(Object.prototype.toString.apply(o) === '[object Date]'){
39                 return Ext.util.JSON.encodeDate(o);
40             }else if(typeof o == "string"){
41                 return encodeString(o);
42             }else if(typeof o == "number"){
43                 return isFinite(o) ? String(o) : "null";
44             }else if(typeof o == "boolean"){
45                 return String(o);
46             }else {
47                 var a = ["{"], b, i, v;
48                 for (i in o) {
49                     if(!useHasOwn || o.hasOwnProperty(i)) {
50                         v = o[i];
51                         switch (typeof v) {
52                         case "undefined":
53                         case "function":
54                         case "unknown":
55                             break;
56                         default:
57                             if(b){
58                                 a.push(',');
59                             }
60                             a.push(doEncode(i), ":",
61                                     v === null ? "null" : doEncode(v));
62                             b = true;
63                         }
64                     }
65                 }
66                 a.push("}");
67                 return a.join("");
68             }    
69         },
70         m = {
71             "\b": '\\b',
72             "\t": '\\t',
73             "\n": '\\n',
74             "\f": '\\f',
75             "\r": '\\r',
76             '"' : '\\"',
77             "\\": '\\\\'
78         },
79         encodeString = function(s){
80             if (/["\\\x00-\x1f]/.test(s)) {
81                 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
82                     var c = m[b];
83                     if(c){
84                         return c;
85                     }
86                     c = b.charCodeAt();
87                     return "\\u00" +
88                         Math.floor(c / 16).toString(16) +
89                         (c % 16).toString(16);
90                 }) + '"';
91             }
92             return '"' + s + '"';
93         },
94         encodeArray = function(o){
95             var a = ["["], b, i, l = o.length, v;
96                 for (i = 0; i < l; i += 1) {
97                     v = o[i];
98                     switch (typeof v) {
99                         case "undefined":
100                         case "function":
101                         case "unknown":
102                             break;
103                         default:
104                             if (b) {
105                                 a.push(',');
106                             }
107                             a.push(v === null ? "null" : Ext.util.JSON.encode(v));
108                             b = true;
109                     }
110                 }
111                 a.push("]");
112                 return a.join("");
113         };
114
115     this.encodeDate = function(o){
116         return '"' + o.getFullYear() + "-" +
117                 pad(o.getMonth() + 1) + "-" +
118                 pad(o.getDate()) + "T" +
119                 pad(o.getHours()) + ":" +
120                 pad(o.getMinutes()) + ":" +
121                 pad(o.getSeconds()) + '"';
122     };
123
124     /**
125      * Encodes an Object, Array or other value
126      * @param {Mixed} o The variable to encode
127      * @return {String} The JSON string
128      */
129     this.encode = function() {
130         var ec;
131         return function(o) {
132             if (!ec) {
133                 // setup encoding function on first access
134                 ec = isNative() ? JSON.stringify : doEncode;
135             }
136             return ec(o);
137         };
138     }();
139
140
141     /**
142      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
143      * @param {String} json The JSON string
144      * @return {Object} The resulting object
145      */
146     this.decode = function() {
147         var dc;
148         return function(json) {
149             if (!dc) {
150                 // setup decoding function on first access
151                 dc = isNative() ? JSON.parse : doDecode;
152             }
153             return dc(json);
154         };
155     }();
156
157 })();
158 /**
159  * Shorthand for {@link Ext.util.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.util.JSON.encode;
166 /**
167  * Shorthand for {@link Ext.util.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.util.JSON.decode;