org.dojo.dijit.Tree.js Maven / Gradle / Ivy
The newest version!
if(!dojo._hasResource["dijit.Tree"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.Tree"] = true;
dojo.provide("dijit.Tree");
dojo.require("dojo.fx");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("dijit._Container");
dojo.require("dojo.cookie");
dojo.declare(
"dijit._TreeNode",
[dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained],
{
// summary
// Single node within a tree
// item: dojo.data.Item
// the dojo.data entry this tree represents
item: null,
isTreeNode: true,
// label: String
// Text of this tree node
label: "",
isExpandable: null, // show expando node
isExpanded: false,
// state: String
// dynamic loading-related stuff.
// When an empty folder node appears, it is "UNCHECKED" first,
// then after dojo.data query it becomes "LOADING" and, finally "LOADED"
state: "UNCHECKED",
templateString:"\n\t\n\t\t\n\t\t\n\t\n\n",
postCreate: function(){
// set label, escaping special characters
this.setLabelNode(this.label);
// set expand icon for leaf
this._setExpando();
// set icon and label class based on item
this._updateItemClasses(this.item);
if(this.isExpandable){
dijit.setWaiState(this.labelNode, "expanded", this.isExpanded);
}
},
markProcessing: function(){
// summary: visually denote that tree is loading data, etc.
this.state = "LOADING";
this._setExpando(true);
},
unmarkProcessing: function(){
// summary: clear markup from markProcessing() call
this._setExpando(false);
},
_updateItemClasses: function(item){
// summary: set appropriate CSS classes for item (used to allow for item updates to change respective CSS)
this.iconNode.className = "dijitInline dijitTreeIcon " + this.tree.getIconClass(item);
this.labelNode.className = "dijitTreeLabel " + this.tree.getLabelClass(item);
},
_updateLayout: function(){
// summary: set appropriate CSS classes for this.domNode
var parent = this.getParent();
if(parent && parent.isTree && parent._hideRoot){
/* if we are hiding the root node then make every first level child look like a root node */
dojo.addClass(this.domNode, "dijitTreeIsRoot");
}else{
dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
}
},
_setExpando: function(/*Boolean*/ processing){
// summary: set the right image for the expando node
// apply the appropriate class to the expando node
var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
"dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"];
var idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
dojo.forEach(styles,
function(s){
dojo.removeClass(this.expandoNode, s);
}, this
);
dojo.addClass(this.expandoNode, styles[idx]);
// provide a non-image based indicator for images-off mode
this.expandoNodeText.innerHTML =
processing ? "*" :
(this.isExpandable ?
(this.isExpanded ? "-" : "+") : "*");
},
expand: function(){
// summary: show my children
if(this.isExpanded){ return; }
// cancel in progress collapse operation
if(this._wipeOut.status() == "playing"){
this._wipeOut.stop();
}
this.isExpanded = true;
dijit.setWaiState(this.labelNode, "expanded", "true");
dijit.setWaiRole(this.containerNode, "group");
this._setExpando();
this._wipeIn.play();
},
collapse: function(){
if(!this.isExpanded){ return; }
// cancel in progress expand operation
if(this._wipeIn.status() == "playing"){
this._wipeIn.stop();
}
this.isExpanded = false;
dijit.setWaiState(this.labelNode, "expanded", "false");
this._setExpando();
this._wipeOut.play();
},
setLabelNode: function(label){
this.labelNode.innerHTML="";
this.labelNode.appendChild(document.createTextNode(label));
},
_setChildren: function(/* Object[] */ childrenArray){
// summary:
// Sets the children of this node.
// Sets this.isExpandable based on whether or not there are children
// Takes array of objects like: {label: ...} (_TreeNode options basically)
// See parameters of _TreeNode for details.
this.destroyDescendants();
this.state = "LOADED";
var nodeMap= {};
if(childrenArray && childrenArray.length > 0){
this.isExpandable = true;
if(!this.containerNode){ // maybe this node was unfolderized and still has container
this.containerNode = this.tree.containerNodeTemplate.cloneNode(true);
this.domNode.appendChild(this.containerNode);
}
// Create _TreeNode widget for each specified tree node
dojo.forEach(childrenArray, function(childParams){
var child = new dijit._TreeNode(dojo.mixin({
tree: this.tree,
label: this.tree.getLabel(childParams.item)
}, childParams));
this.addChild(child);
var identity = this.tree.store.getIdentity(childParams.item);
nodeMap[identity] = child;
if(this.tree.persist){
if(this.tree._openedItemIds[identity]){
this.tree._expandNode(child);
}
}
}, this);
// note that updateLayout() needs to be called on each child after
// _all_ the children exist
dojo.forEach(this.getChildren(), function(child, idx){
child._updateLayout();
});
}else{
this.isExpandable=false;
}
if(this._setExpando){
// change expando to/form dot or + icon, as appropriate
this._setExpando(false);
}
if(this.isTree && this._hideRoot){
// put first child in tab index if one exists.
var fc = this.getChildren()[0];
var tabnode = fc ? fc.labelNode : this.domNode;
tabnode.setAttribute("tabIndex", "0");
}
// create animations for showing/hiding the children (if children exist)
if(this.containerNode && !this._wipeIn){
this._wipeIn = dojo.fx.wipeIn({node: this.containerNode, duration: 150});
this._wipeOut = dojo.fx.wipeOut({node: this.containerNode, duration: 150});
}
return nodeMap;
},
_addChildren: function(/* object[] */ childrenArray){
// summary:
// adds the children to this node.
// Takes array of objects like: {label: ...} (_TreeNode options basically)
// See parameters of _TreeNode for details.
var nodeMap = {};
if(childrenArray && childrenArray.length > 0){
dojo.forEach(childrenArray, function(childParams){
var child = new dijit._TreeNode(
dojo.mixin({
tree: this.tree,
label: this.tree.getLabel(childParams.item)
}, childParams)
);
this.addChild(child);
nodeMap[this.tree.store.getIdentity(childParams.item)] = child;
}, this);
dojo.forEach(this.getChildren(), function(child, idx){
child._updateLayout();
});
}
return nodeMap;
},
deleteNode: function(/* treeNode */ node){
node.destroy();
var children = this.getChildren();
if(children.length == 0){
this.isExpandable = false;
this.collapse();
}
dojo.forEach(children, function(child){
child._updateLayout();
});
},
makeExpandable: function(){
//summary
// if this node wasn't already showing the expando node,
// turn it into one and call _setExpando()
this.isExpandable = true;
this._setExpando(false);
}
});
dojo.declare(
"dijit.Tree",
dijit._TreeNode,
{
// summary
// This widget displays hierarchical data from a store. A query is specified
// to get the "top level children" from a data store, and then those items are
// queried for their children and so on (but lazily, as the user clicks the expand node).
//
// Thus in the default mode of operation this widget is technically a forest, not a tree,
// in that there can be multiple "top level children". However, if you specify label,
// then a special top level node (not corresponding to any item in the datastore) is
// created, to father all the top level children.
// store: String||dojo.data.Store
// The store to get data to display in the tree
store: null,
// query: String
// query to get top level node(s) of tree (ex: {type:'continent'})
query: null,
// childrenAttr: String
// one ore more attributes that holds children of a tree node
childrenAttr: ["children"],
templateString:"\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\n",
isExpandable: true,
isTree: true,
// persist: Boolean
// enables/disables use of cookies for state saving.
persist: true,
// dndController: String
// class name to use as as the dnd controller
dndController: null,
//parameters to pull off of the tree and pass on to the dndController as its params
dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance"],
//declare the above items so they can be pulled from the tree's markup
onDndDrop:null,
itemCreator:null,
onDndCancel:null,
checkAcceptance:null,
checkItemAcceptance:null,
_publish: function(/*String*/ topicName, /*Object*/ message){
// summary:
// Publish a message for this widget/topic
dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message||{})]);
},
postMixInProperties: function(){
this.tree = this;
this.lastFocused = this.labelNode;
this._itemNodeMap={};
this._hideRoot = !this.label;
if(!this.store.getFeatures()['dojo.data.api.Identity']){
throw new Error("dijit.tree requires access to a store supporting the dojo.data Identity api");
}
if(!this.cookieName){
this.cookieName = this.id + "SaveStateCookie";
}
// if the store supports Notification, subscribe to the notification events
if(this.store.getFeatures()['dojo.data.api.Notification']){
this.connect(this.store, "onNew", "_onNewItem");
this.connect(this.store, "onDelete", "_onDeleteItem");
this.connect(this.store, "onSet", "_onSetItem");
}
},
postCreate: function(){
// load in which nodes should be opened automatically
if(this.persist){
var cookie = dojo.cookie(this.cookieName);
this._openedItemIds = {};
if(cookie){
dojo.forEach(cookie.split(','), function(item){
this._openedItemIds[item] = true;
}, this);
}
}
// make template for container node (we will clone this and insert it into
// any nodes that have children)
var div = document.createElement('div');
div.style.display = 'none';
div.className = "dijitTreeContainer";
dijit.setWaiRole(div, "presentation");
this.containerNodeTemplate = div;
if(this._hideRoot){
this.rowNode.style.display="none";
}
this.inherited("postCreate", arguments);
// load top level children
this._expandNode(this);
if(this.dndController){
if(dojo.isString(this.dndController)){
this.dndController= dojo.getObject(this.dndController);
}
var params={};
for (var i=0; i