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