commit extjs-2.2.1
[extjs.git] / source / widgets / tree / TreeLoader.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10  * @class Ext.tree.TreeLoader\r
11  * @extends Ext.util.Observable\r
12  * A TreeLoader provides for lazy loading of an {@link Ext.tree.TreeNode}'s child\r
13  * nodes from a specified URL. The response must be a JavaScript Array definition\r
14  * whose elements are node definition objects. eg:\r
15  * <pre><code>\r
16     [{\r
17         id: 1,\r
18         text: 'A leaf Node',\r
19         leaf: true\r
20     },{\r
21         id: 2,\r
22         text: 'A folder Node',\r
23         children: [{\r
24             id: 3,\r
25             text: 'A child Node',\r
26             leaf: true\r
27         }]\r
28    }]\r
29 </code></pre>\r
30  * <br><br>\r
31  * A server request is sent, and child nodes are loaded only when a node is expanded.\r
32  * The loading node's id is passed to the server under the parameter name "node" to\r
33  * enable the server to produce the correct child nodes.\r
34  * <br><br>\r
35  * To pass extra parameters, an event handler may be attached to the "beforeload"\r
36  * event, and the parameters specified in the TreeLoader's baseParams property:\r
37  * <pre><code>\r
38     myTreeLoader.on("beforeload", function(treeLoader, node) {\r
39         this.baseParams.category = node.attributes.category;\r
40     }, this);\r
41 </code></pre>\r
42  * This would pass an HTTP parameter called "category" to the server containing\r
43  * the value of the Node's "category" attribute.\r
44  * @constructor\r
45  * Creates a new Treeloader.\r
46  * @param {Object} config A config object containing config properties.\r
47  */\r
48 Ext.tree.TreeLoader = function(config){\r
49     this.baseParams = {};\r
50     Ext.apply(this, config);\r
51 \r
52     this.addEvents(\r
53         /**\r
54          * @event beforeload\r
55          * Fires before a network request is made to retrieve the Json text which specifies a node's children.\r
56          * @param {Object} This TreeLoader object.\r
57          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
58          * @param {Object} callback The callback function specified in the {@link #load} call.\r
59          */\r
60         "beforeload",\r
61         /**\r
62          * @event load\r
63          * Fires when the node has been successfuly loaded.\r
64          * @param {Object} This TreeLoader object.\r
65          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
66          * @param {Object} response The response object containing the data from the server.\r
67          */\r
68         "load",\r
69         /**\r
70          * @event loadexception\r
71          * Fires if the network request failed.\r
72          * @param {Object} This TreeLoader object.\r
73          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
74          * @param {Object} response The response object containing the data from the server.\r
75          */\r
76         "loadexception"\r
77     );\r
78 \r
79     Ext.tree.TreeLoader.superclass.constructor.call(this);\r
80 };\r
81 \r
82 Ext.extend(Ext.tree.TreeLoader, Ext.util.Observable, {\r
83     /**\r
84     * @cfg {String} dataUrl The URL from which to request a Json string which\r
85     * specifies an array of node definition objects representing the child nodes\r
86     * to be loaded.\r
87     */\r
88     /**\r
89      * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).\r
90      */\r
91     /**\r
92      * @cfg {String} url Equivalent to {@link #dataUrl}.\r
93      */\r
94     /**\r
95      * @cfg {Boolean} preloadChildren If set to true, the loader recursively loads "children" attributes when doing the first load on nodes.\r
96      */\r
97     /**\r
98     * @cfg {Object} baseParams (optional) An object containing properties which\r
99     * specify HTTP parameters to be passed to each request for child nodes.\r
100     */\r
101     /**\r
102     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes\r
103     * created by this loader. If the attributes sent by the server have an attribute in this object,\r
104     * they take priority.\r
105     */\r
106     /**\r
107     * @cfg {Object} uiProviders (optional) An object containing properties which\r
108     * specify custom {@link Ext.tree.TreeNodeUI} implementations. If the optional\r
109     * <i>uiProvider</i> attribute of a returned child node is a string rather\r
110     * than a reference to a TreeNodeUI implementation, then that string value\r
111     * is used as a property name in the uiProviders object.\r
112     */\r
113     uiProviders : {},\r
114 \r
115     /**\r
116     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing\r
117     * child nodes before loading.\r
118     */\r
119     clearOnLoad : true,\r
120 \r
121     /**\r
122      * Load an {@link Ext.tree.TreeNode} from the URL specified in the constructor.\r
123      * This is called automatically when a node is expanded, but may be used to reload\r
124      * a node (or append new children if the {@link #clearOnLoad} option is false.)\r
125      * @param {Ext.tree.TreeNode} node\r
126      * @param {Function} callback\r
127      */\r
128     load : function(node, callback){\r
129         if(this.clearOnLoad){\r
130             while(node.firstChild){\r
131                 node.removeChild(node.firstChild);\r
132             }\r
133         }\r
134         if(this.doPreload(node)){ // preloaded json children\r
135             if(typeof callback == "function"){\r
136                 callback();\r
137             }\r
138         }else if(this.dataUrl||this.url){\r
139             this.requestData(node, callback);\r
140         }\r
141     },\r
142 \r
143     doPreload : function(node){\r
144         if(node.attributes.children){\r
145             if(node.childNodes.length < 1){ // preloaded?\r
146                 var cs = node.attributes.children;\r
147                 node.beginUpdate();\r
148                 for(var i = 0, len = cs.length; i < len; i++){\r
149                     var cn = node.appendChild(this.createNode(cs[i]));\r
150                     if(this.preloadChildren){\r
151                         this.doPreload(cn);\r
152                     }\r
153                 }\r
154                 node.endUpdate();\r
155             }\r
156             return true;\r
157         }else {\r
158             return false;\r
159         }\r
160     },\r
161 \r
162     getParams: function(node){\r
163         var buf = [], bp = this.baseParams;\r
164         for(var key in bp){\r
165             if(typeof bp[key] != "function"){\r
166                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");\r
167             }\r
168         }\r
169         buf.push("node=", encodeURIComponent(node.id));\r
170         return buf.join("");\r
171     },\r
172 \r
173     requestData : function(node, callback){\r
174         if(this.fireEvent("beforeload", this, node, callback) !== false){\r
175             this.transId = Ext.Ajax.request({\r
176                 method:this.requestMethod,\r
177                 url: this.dataUrl||this.url,\r
178                 success: this.handleResponse,\r
179                 failure: this.handleFailure,\r
180                 scope: this,\r
181                 argument: {callback: callback, node: node},\r
182                 params: this.getParams(node)\r
183             });\r
184         }else{\r
185             // if the load is cancelled, make sure we notify\r
186             // the node that we are done\r
187             if(typeof callback == "function"){\r
188                 callback();\r
189             }\r
190         }\r
191     },\r
192 \r
193     isLoading : function(){\r
194         return !!this.transId;\r
195     },\r
196 \r
197     abort : function(){\r
198         if(this.isLoading()){\r
199             Ext.Ajax.abort(this.transId);\r
200         }\r
201     },\r
202 \r
203     /**\r
204     * <p>Override this function for custom TreeNode node implementation, or to\r
205     * modify the attributes at creation time.</p>\r
206     * Example:<code><pre>\r
207 new Ext.tree.TreePanel({\r
208     ...\r
209     new Ext.tree.TreeLoader({\r
210         url: 'dataUrl',\r
211         createNode: function(attr) {\r
212 //          Allow consolidation consignments to have\r
213 //          consignments dropped into them.\r
214             if (attr.isConsolidation) {\r
215                 attr.iconCls = 'x-consol',\r
216                 attr.allowDrop = true;\r
217             }\r
218             return Ext.tree.TreeLoader.prototype.call(this, attr);\r
219         }\r
220     }),\r
221     ...\r
222 }); \r
223 </pre></code>\r
224     * @param attr {Object} The attributes from which to create the new node.\r
225     */\r
226     createNode : function(attr){\r
227         // apply baseAttrs, nice idea Corey!\r
228         if(this.baseAttrs){\r
229             Ext.applyIf(attr, this.baseAttrs);\r
230         }\r
231         if(this.applyLoader !== false){\r
232             attr.loader = this;\r
233         }\r
234         if(typeof attr.uiProvider == 'string'){\r
235            attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider);\r
236         }\r
237         if(attr.nodeType){\r
238             return new Ext.tree.TreePanel.nodeTypes[attr.nodeType](attr);\r
239         }else{\r
240             return attr.leaf ?\r
241                         new Ext.tree.TreeNode(attr) :\r
242                         new Ext.tree.AsyncTreeNode(attr);\r
243         }\r
244     },\r
245 \r
246     processResponse : function(response, node, callback){\r
247         var json = response.responseText;\r
248         try {\r
249             var o = eval("("+json+")");\r
250             node.beginUpdate();\r
251             for(var i = 0, len = o.length; i < len; i++){\r
252                 var n = this.createNode(o[i]);\r
253                 if(n){\r
254                     node.appendChild(n);\r
255                 }\r
256             }\r
257             node.endUpdate();\r
258             if(typeof callback == "function"){\r
259                 callback(this, node);\r
260             }\r
261         }catch(e){\r
262             this.handleFailure(response);\r
263         }\r
264     },\r
265 \r
266     handleResponse : function(response){\r
267         this.transId = false;\r
268         var a = response.argument;\r
269         this.processResponse(response, a.node, a.callback);\r
270         this.fireEvent("load", this, a.node, response);\r
271     },\r
272 \r
273     handleFailure : function(response){\r
274         this.transId = false;\r
275         var a = response.argument;\r
276         this.fireEvent("loadexception", this, a.node, response);\r
277         if(typeof a.callback == "function"){\r
278             a.callback(this, a.node);\r
279         }\r
280     }\r
281 });