X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/ee06f37b0f6f6d94cd05a6ffae556660f7c4a2bc..c930e9176a5a85509c5b0230e2bff5c22a591432:/src/widgets/tree/TreeLoader.js?ds=sidebyside diff --git a/src/widgets/tree/TreeLoader.js b/src/widgets/tree/TreeLoader.js new file mode 100644 index 00000000..0fe32c84 --- /dev/null +++ b/src/widgets/tree/TreeLoader.js @@ -0,0 +1,342 @@ +/*! + * Ext JS Library 3.0.0 + * Copyright(c) 2006-2009 Ext JS, LLC + * licensing@extjs.com + * http://www.extjs.com/license + */ +/** + * @class Ext.tree.TreeLoader + * @extends Ext.util.Observable + * A TreeLoader provides for lazy loading of an {@link Ext.tree.TreeNode}'s child + * nodes from a specified URL. The response must be a JavaScript Array definition + * whose elements are node definition objects. e.g.: + *

+    [{
+        id: 1,
+        text: 'A leaf Node',
+        leaf: true
+    },{
+        id: 2,
+        text: 'A folder Node',
+        children: [{
+            id: 3,
+            text: 'A child Node',
+            leaf: true
+        }]
+   }]
+
+ *

+ * A server request is sent, and child nodes are loaded only when a node is expanded. + * The loading node's id is passed to the server under the parameter name "node" to + * enable the server to produce the correct child nodes. + *

+ * To pass extra parameters, an event handler may be attached to the "beforeload" + * event, and the parameters specified in the TreeLoader's baseParams property: + *

+    myTreeLoader.on("beforeload", function(treeLoader, node) {
+        this.baseParams.category = node.attributes.category;
+    }, this);
+
+ * This would pass an HTTP parameter called "category" to the server containing + * the value of the Node's "category" attribute. + * @constructor + * Creates a new Treeloader. + * @param {Object} config A config object containing config properties. + */ +Ext.tree.TreeLoader = function(config){ + this.baseParams = {}; + Ext.apply(this, config); + + this.addEvents( + /** + * @event beforeload + * Fires before a network request is made to retrieve the Json text which specifies a node's children. + * @param {Object} This TreeLoader object. + * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded. + * @param {Object} callback The callback function specified in the {@link #load} call. + */ + "beforeload", + /** + * @event load + * Fires when the node has been successfuly loaded. + * @param {Object} This TreeLoader object. + * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded. + * @param {Object} response The response object containing the data from the server. + */ + "load", + /** + * @event loadexception + * Fires if the network request failed. + * @param {Object} This TreeLoader object. + * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded. + * @param {Object} response The response object containing the data from the server. + */ + "loadexception" + ); + Ext.tree.TreeLoader.superclass.constructor.call(this); + if(typeof this.paramOrder == 'string'){ + this.paramOrder = this.paramOrder.split(/[\s,|]/); + } +}; + +Ext.extend(Ext.tree.TreeLoader, Ext.util.Observable, { + /** + * @cfg {String} dataUrl The URL from which to request a Json string which + * specifies an array of node definition objects representing the child nodes + * to be loaded. + */ + /** + * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}). + */ + /** + * @cfg {String} url Equivalent to {@link #dataUrl}. + */ + /** + * @cfg {Boolean} preloadChildren If set to true, the loader recursively loads "children" attributes when doing the first load on nodes. + */ + /** + * @cfg {Object} baseParams (optional) An object containing properties which + * specify HTTP parameters to be passed to each request for child nodes. + */ + /** + * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes + * created by this loader. If the attributes sent by the server have an attribute in this object, + * they take priority. + */ + /** + * @cfg {Object} uiProviders (optional) An object containing properties which + * specify custom {@link Ext.tree.TreeNodeUI} implementations. If the optional + * uiProvider attribute of a returned child node is a string rather + * than a reference to a TreeNodeUI implementation, then that string value + * is used as a property name in the uiProviders object. + */ + uiProviders : {}, + + /** + * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing + * child nodes before loading. + */ + clearOnLoad : true, + + /** + * @cfg {Array/String} paramOrder Defaults to undefined. Only used when using directFn. + * A list of params to be executed + * server side. Specify the params in the order in which they must be executed on the server-side + * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace, + * comma, or pipe. For example, + * any of the following would be acceptable:

+paramOrder: ['param1','param2','param3']
+paramOrder: 'param1 param2 param3'
+paramOrder: 'param1,param2,param3'
+paramOrder: 'param1|param2|param'
+     
+ */ + paramOrder: undefined, + + /** + * @cfg {Boolean} paramsAsHash Only used when using directFn. + * Send parameters as a collection of named arguments (defaults to false). Providing a + * {@link #paramOrder} nullifies this configuration. + */ + paramsAsHash: false, + + /** + * @cfg {Function} directFn + * Function to call when executing a request. + */ + directFn : undefined, + + /** + * Load an {@link Ext.tree.TreeNode} from the URL specified in the constructor. + * This is called automatically when a node is expanded, but may be used to reload + * a node (or append new children if the {@link #clearOnLoad} option is false.) + * @param {Ext.tree.TreeNode} node + * @param {Function} callback + * @param (Object) scope + */ + load : function(node, callback, scope){ + if(this.clearOnLoad){ + while(node.firstChild){ + node.removeChild(node.firstChild); + } + } + if(this.doPreload(node)){ // preloaded json children + this.runCallback(callback, scope || node, []); + }else if(this.directFn || this.dataUrl || this.url){ + this.requestData(node, callback, scope || node); + } + }, + + doPreload : function(node){ + if(node.attributes.children){ + if(node.childNodes.length < 1){ // preloaded? + var cs = node.attributes.children; + node.beginUpdate(); + for(var i = 0, len = cs.length; i < len; i++){ + var cn = node.appendChild(this.createNode(cs[i])); + if(this.preloadChildren){ + this.doPreload(cn); + } + } + node.endUpdate(); + } + return true; + } + return false; + }, + + getParams: function(node){ + var buf = [], bp = this.baseParams; + if(this.directFn){ + buf.push(node.id); + if(bp){ + if(this.paramOrder){ + for(var i = 0, len = this.paramOrder.length; i < len; i++){ + buf.push(bp[this.paramOrder[i]]); + } + }else if(this.paramsAsHash){ + buf.push(bp); + } + } + return buf; + }else{ + for(var key in bp){ + if(!Ext.isFunction(bp[key])){ + buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&"); + } + } + buf.push("node=", encodeURIComponent(node.id)); + return buf.join(""); + } + }, + + requestData : function(node, callback, scope){ + if(this.fireEvent("beforeload", this, node, callback) !== false){ + if(this.directFn){ + var args = this.getParams(node); + args.push(this.processDirectResponse.createDelegate(this, [{callback: callback, node: node, scope: scope}], true)); + this.directFn.apply(window, args); + }else{ + this.transId = Ext.Ajax.request({ + method:this.requestMethod, + url: this.dataUrl||this.url, + success: this.handleResponse, + failure: this.handleFailure, + scope: this, + argument: {callback: callback, node: node, scope: scope}, + params: this.getParams(node) + }); + } + }else{ + // if the load is cancelled, make sure we notify + // the node that we are done + this.runCallback(callback, scope || node, []); + } + }, + + processDirectResponse: function(result, response, args){ + if(response.status){ + this.handleResponse({ + responseData: Ext.isArray(result) ? result : null, + responseText: result, + argument: args + }); + }else{ + this.handleFailure({ + argument: args + }); + } + }, + + // private + runCallback: function(cb, scope, args){ + if(Ext.isFunction(cb)){ + cb.apply(scope, args); + } + }, + + isLoading : function(){ + return !!this.transId; + }, + + abort : function(){ + if(this.isLoading()){ + Ext.Ajax.abort(this.transId); + } + }, + + /** + *

Override this function for custom TreeNode node implementation, or to + * modify the attributes at creation time.

+ * Example:

+new Ext.tree.TreePanel({
+    ...
+    new Ext.tree.TreeLoader({
+        url: 'dataUrl',
+        createNode: function(attr) {
+//          Allow consolidation consignments to have
+//          consignments dropped into them.
+            if (attr.isConsolidation) {
+                attr.iconCls = 'x-consol',
+                attr.allowDrop = true;
+            }
+            return Ext.tree.TreeLoader.prototype.call(this, attr);
+        }
+    }),
+    ...
+});
+
+ * @param attr {Object} The attributes from which to create the new node. + */ + createNode : function(attr){ + // apply baseAttrs, nice idea Corey! + if(this.baseAttrs){ + Ext.applyIf(attr, this.baseAttrs); + } + if(this.applyLoader !== false){ + attr.loader = this; + } + if(typeof attr.uiProvider == 'string'){ + attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider); + } + if(attr.nodeType){ + return new Ext.tree.TreePanel.nodeTypes[attr.nodeType](attr); + }else{ + return attr.leaf ? + new Ext.tree.TreeNode(attr) : + new Ext.tree.AsyncTreeNode(attr); + } + }, + + processResponse : function(response, node, callback, scope){ + var json = response.responseText; + try { + var o = response.responseData || Ext.decode(json); + node.beginUpdate(); + for(var i = 0, len = o.length; i < len; i++){ + var n = this.createNode(o[i]); + if(n){ + node.appendChild(n); + } + } + node.endUpdate(); + this.runCallback(callback, scope || node, [node]); + }catch(e){ + this.handleFailure(response); + } + }, + + handleResponse : function(response){ + this.transId = false; + var a = response.argument; + this.processResponse(response, a.node, a.callback, a.scope); + this.fireEvent("load", this, a.node, response); + }, + + handleFailure : function(response){ + this.transId = false; + var a = response.argument; + this.fireEvent("loadexception", this, a.node, response); + this.runCallback(a.callback, a.scope || a.node, [a.node]); + } +}); \ No newline at end of file