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
+ }]
+ }]
+
+ *
+ 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