/*
* Based on:
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
*
* Originally Released Under LGPL - original licence link has changed is not relivant.
*
* Fork - LGPL
* <script type="text/javascript">
*/
/**
* @class Roo.tree.TreeLoader
* @extends Roo.util.Observable
* A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
* nodes from a specified URL. The response must be a javascript Array definition
* who's elements are node definition objects. eg:
* <pre><code>
{ success : true,
data : [
{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
{ 'id': 2, 'text': 'A leaf Node', 'leaf': true }
]
}
</code></pre>
* <br><br>
* The old style respose with just an array is still supported, but not recommended.
* <br><br>
*
* 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.
* <br><br>
* To pass extra parameters, an event handler may be attached to the "beforeload"
* event, and the parameters specified in the TreeLoader's baseParams property:
* <pre><code>
myTreeLoader.on("beforeload", function(treeLoader, node) {
this.baseParams.category = node.attributes.category;
}, this);
</code></pre>
*
* 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.
*/
Roo.tree.TreeLoader = function(config){
this.baseParams = {};
this.requestMethod = "POST";
Roo.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 Roo.tree.TreeNode} object being loaded.
* @param {Object} callback The callback function specified in the {@link #load} call.
*/
beforeload : true,
/**
* @event load
* Fires when the node has been successfuly loaded.
* @param {Object} This TreeLoader object.
* @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
* @param {Object} response The response object containing the data from the server.
*/
load : true,
/**
* @event loadexception
* Fires if the network request failed.
* @param {Object} This TreeLoader object.
* @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
* @param {Object} response The response object containing the data from the server.
*/
loadexception : true,
/**
* @event create
* Fires before a node is created, enabling you to return custom Node types
* @param {Object} This TreeLoader object.
* @param {Object} attr - the data returned from the AJAX call (modify it to suit)
*/
create : true
});
Roo.tree.TreeLoader.superclass.constructor.call(this);
};
Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
/**
* @cfg {String} dataUrl The URL from which to request a Json string which
* specifies an array of node definition object representing the child nodes
* to be loaded.
*/
/**
* @cfg {String} requestMethod either GET or POST
* defaults to POST (due to BC)
* to be loaded.
*/
/**
* @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
*
* DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
* specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
* <i>uiProvider</i> attribute of a returned child node is a string rather
* than a reference to a TreeNodeUI implementation, this that string value
* is used as a property name in the uiProviders object. You can define the provider named
* 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
*/
uiProviders : {},
/**
* @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
* child nodes before loading.
*/
clearOnLoad : true,
/**
* @cfg {String} root (optional) Default to false. Use this to read data from an object
* property on loading, rather than expecting an array. (eg. more compatible to a standard
* Grid query { data : [ .....] }
*/
root : false,
/**
* @cfg {String} queryParam (optional)
* Name of the query as it will be passed on the querystring (defaults to 'node')
* eg. the request will be ?node=[id]
*/
queryParam: false,
/**
* Load an {@link Roo.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 {Roo.tree.TreeNode} node
* @param {Function} callback
*/
load : function(node, callback){
if(this.clearOnLoad){
while(node.firstChild){
node.removeChild(node.firstChild);
}
}
if(node.attributes.children){ // preloaded json children
var cs = node.attributes.children;
for(var i = 0, len = cs.length; i < len; i++){
node.appendChild(this.createNode(cs[i]));
}
if(typeof callback == "function"){
callback();
}
}else if(this.dataUrl){
this.requestData(node, callback);
}
},
getParams: function(node){
var buf = [], bp = this.baseParams;
for(var key in bp){
if(typeof bp[key] != "function"){
buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
}
}
var n = this.queryParam === false ? 'node' : this.queryParam;
buf.push(n + "=", encodeURIComponent(node.id));
return buf.join("");
},
requestData : function(node, callback){
if(this.fireEvent("beforeload", this, node, callback) !== false){
this.transId = Roo.Ajax.request({
method:this.requestMethod,
url: this.dataUrl||this.url,
success: this.handleResponse,
failure: this.handleFailure,
scope: this,
argument: {callback: callback, node: node},
params: this.getParams(node)
});
}else{
// if the load is cancelled, make sure we notify
// the node that we are done
if(typeof callback == "function"){
callback();
}
}
},
isLoading : function(){
return this.transId ? true : false;
},
abort : function(){
if(this.isLoading()){
Roo.Ajax.abort(this.transId);
}
},
// private
createNode : function(attr)
{
// apply baseAttrs, nice idea Corey!
if(this.baseAttrs){
Roo.applyIf(attr, this.baseAttrs);
}
if(this.applyLoader !== false){
attr.loader = this;
}
// uiProvider = depreciated..
if(typeof(attr.uiProvider) == 'string'){
attr.uiProvider = this.uiProviders[attr.uiProvider] ||
/** eval:var:attr */ eval(attr.uiProvider);
}
if(typeof(this.uiProviders['default']) != 'undefined') {
attr.uiProvider = this.uiProviders['default'];
}
this.fireEvent('create', this, attr);
attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
return(attr.leaf ?
new Roo.tree.TreeNode(attr) :
new Roo.tree.AsyncTreeNode(attr));
},
processResponse : function(response, node, callback)
{
var json = response.responseText;
try {
var o = Roo.decode(json);
if (this.root === false && typeof(o.success) != undefined) {
this.root = 'data'; // the default behaviour for list like data..
}
if (this.root !== false && !o.success) {
// it's a failure condition.
var a = response.argument;
this.fireEvent("loadexception", this, a.node, response);
Roo.log("Load failed - should have a handler really");
return;
}
if (this.root !== false) {
o = o[this.root];
}
for(var i = 0, len = o.length; i < len; i++){
var n = this.createNode(o[i]);
if(n){
node.appendChild(n);
}
}
if(typeof callback == "function"){
callback(this, node);
}
}catch(e){
this.handleFailure(response);
}
},
handleResponse : function(response){
this.transId = false;
var a = response.argument;
this.processResponse(response, a.node, a.callback);
this.fireEvent("load", this, a.node, response);
},
handleFailure : function(response)
{
// should handle failure better..
this.transId = false;
var a = response.argument;
this.fireEvent("loadexception", this, a.node, response);
if(typeof a.callback == "function"){
a.callback(this, a.node);
}
}
});