938de152a585dde8aa7a9ff94ff7f81da7e0d514
[roojs1] / Roo / tree / TreeLoader.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11 /**
12  * @class Roo.tree.TreeLoader
13  * @extends Roo.util.Observable
14  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
15  * nodes from a specified URL. The response must be a javascript Array definition
16  * who's elements are node definition objects. eg:
17  * <pre><code>
18 {  success : true,
19    data :      [
20    
21     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
22     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
23     ]
24 }
25
26
27 </code></pre>
28  * <br><br>
29  * The old style respose with just an array is still supported, but not recommended.
30  * <br><br>
31  *
32  * A server request is sent, and child nodes are loaded only when a node is expanded.
33  * The loading node's id is passed to the server under the parameter name "node" to
34  * enable the server to produce the correct child nodes.
35  * <br><br>
36  * To pass extra parameters, an event handler may be attached to the "beforeload"
37  * event, and the parameters specified in the TreeLoader's baseParams property:
38  * <pre><code>
39     myTreeLoader.on("beforeload", function(treeLoader, node) {
40         this.baseParams.category = node.attributes.category;
41     }, this);
42 </code></pre><
43  * This would pass an HTTP parameter called "category" to the server containing
44  * the value of the Node's "category" attribute.
45  * @constructor
46  * Creates a new Treeloader.
47  * @param {Object} config A config object containing config properties.
48  */
49 Roo.tree.TreeLoader = function(config){
50     this.baseParams = {};
51     this.requestMethod = "POST";
52     Roo.apply(this, config);
53
54     this.addEvents({
55     
56         /**
57          * @event beforeload
58          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
59          * @param {Object} This TreeLoader object.
60          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
61          * @param {Object} callback The callback function specified in the {@link #load} call.
62          */
63         beforeload : true,
64         /**
65          * @event load
66          * Fires when the node has been successfuly loaded.
67          * @param {Object} This TreeLoader object.
68          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
69          * @param {Object} response The response object containing the data from the server.
70          */
71         load : true,
72         /**
73          * @event loadexception
74          * Fires if the network request failed.
75          * @param {Object} This TreeLoader object.
76          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
77          * @param {Object} response The response object containing the data from the server.
78          */
79         loadexception : true,
80         /**
81          * @event create
82          * Fires before a node is created, enabling you to return custom Node types 
83          * @param {Object} This TreeLoader object.
84          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
85          */
86         create : true
87     });
88
89     Roo.tree.TreeLoader.superclass.constructor.call(this);
90 };
91
92 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
93     /**
94     * @cfg {String} dataUrl The URL from which to request a Json string which
95     * specifies an array of node definition object representing the child nodes
96     * to be loaded.
97     */
98     /**
99     * @cfg {String} requestMethod either GET or POST
100     * defaults to POST (due to BC)
101     * to be loaded.
102     */
103     /**
104     * @cfg {Object} baseParams (optional) An object containing properties which
105     * specify HTTP parameters to be passed to each request for child nodes.
106     */
107     /**
108     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
109     * created by this loader. If the attributes sent by the server have an attribute in this object,
110     * they take priority.
111     */
112     /**
113     * @cfg {Object} uiProviders (optional) An object containing properties which
114     * 
115     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
116     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
117     * <i>uiProvider</i> attribute of a returned child node is a string rather
118     * than a reference to a TreeNodeUI implementation, this that string value
119     * is used as a property name in the uiProviders object. You can define the provider named
120     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
121     */
122     uiProviders : {},
123
124     /**
125     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
126     * child nodes before loading.
127     */
128     clearOnLoad : true,
129
130     /**
131     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
132     * property on loading, rather than expecting an array. (eg. more compatible to a standard
133     * Grid query { data : [ .....] }
134     */
135     
136     root : false,
137      /**
138     * @cfg {String} queryParam (optional) 
139     * Name of the query as it will be passed on the querystring (defaults to 'node')
140     * eg. the request will be ?node=[id]
141     */
142     
143     
144     queryParam: false,
145     
146     /**
147      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
148      * This is called automatically when a node is expanded, but may be used to reload
149      * a node (or append new children if the {@link #clearOnLoad} option is false.)
150      * @param {Roo.tree.TreeNode} node
151      * @param {Function} callback
152      */
153     load : function(node, callback){
154         if(this.clearOnLoad){
155             while(node.firstChild){
156                 node.removeChild(node.firstChild);
157             }
158         }
159         if(node.attributes.children){ // preloaded json children
160             var cs = node.attributes.children;
161             for(var i = 0, len = cs.length; i < len; i++){
162                 node.appendChild(this.createNode(cs[i]));
163             }
164             if(typeof callback == "function"){
165                 callback();
166             }
167         }else if(this.dataUrl){
168             this.requestData(node, callback);
169         }
170     },
171
172     getParams: function(node){
173         var buf = [], bp = this.baseParams;
174         for(var key in bp){
175             if(typeof bp[key] != "function"){
176                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
177             }
178         }
179         var n = this.queryParam === false ? 'node' : this.queryParam;
180         buf.push(n + "=", encodeURIComponent(node.id));
181         return buf.join("");
182     },
183
184     requestData : function(node, callback){
185         if(this.fireEvent("beforeload", this, node, callback) !== false){
186             this.transId = Roo.Ajax.request({
187                 method:this.requestMethod,
188                 url: this.dataUrl||this.url,
189                 success: this.handleResponse,
190                 failure: this.handleFailure,
191                 scope: this,
192                 argument: {callback: callback, node: node},
193                 params: this.getParams(node)
194             });
195         }else{
196             // if the load is cancelled, make sure we notify
197             // the node that we are done
198             if(typeof callback == "function"){
199                 callback();
200             }
201         }
202     },
203
204     isLoading : function(){
205         return this.transId ? true : false;
206     },
207
208     abort : function(){
209         if(this.isLoading()){
210             Roo.Ajax.abort(this.transId);
211         }
212     },
213
214     // private
215     createNode : function(attr)
216     {
217         // apply baseAttrs, nice idea Corey!
218         if(this.baseAttrs){
219             Roo.applyIf(attr, this.baseAttrs);
220         }
221         if(this.applyLoader !== false){
222             attr.loader = this;
223         }
224         // uiProvider = depreciated..
225         
226         if(typeof(attr.uiProvider) == 'string'){
227            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
228                 /**  eval:var:attr */ eval(attr.uiProvider);
229         }
230         if(typeof(this.uiProviders['default']) != 'undefined') {
231             attr.uiProvider = this.uiProviders['default'];
232         }
233         
234         this.fireEvent('create', this, attr);
235         
236         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
237         return(attr.leaf ?
238                         new Roo.tree.TreeNode(attr) :
239                         new Roo.tree.AsyncTreeNode(attr));
240     },
241
242     processResponse : function(response, node, callback)
243     {
244         var json = response.responseText;
245         try {
246             
247             var o = Roo.decode(json);
248             
249             if (this.root === false && typeof(o.success) != undefined) {
250                 this.root = 'data'; // the default behaviour for list like data..
251                 }
252                 
253             if (this.root !== false &&  !o.success) {
254                 // it's a failure condition.
255                 var a = response.argument;
256                 this.fireEvent("loadexception", this, a.node, response);
257                 Roo.log("Load failed - should have a handler really");
258                 return;
259             }
260             
261             
262             
263             if (this.root !== false) {
264                  o = o[this.root];
265             }
266             
267             for(var i = 0, len = o.length; i < len; i++){
268                 var n = this.createNode(o[i]);
269                 if(n){
270                     node.appendChild(n);
271                 }
272             }
273             if(typeof callback == "function"){
274                 callback(this, node);
275             }
276         }catch(e){
277             this.handleFailure(response);
278         }
279     },
280
281     handleResponse : function(response){
282         this.transId = false;
283         var a = response.argument;
284         this.processResponse(response, a.node, a.callback);
285         this.fireEvent("load", this, a.node, response);
286     },
287
288     handleFailure : function(response)
289     {
290         // should handle failure better..
291         this.transId = false;
292         var a = response.argument;
293         this.fireEvent("loadexception", this, a.node, response);
294         if(typeof a.callback == "function"){
295             a.callback(this, a.node);
296         }
297     }
298 });