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