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",
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 icon and label dom node (used to allow for item updates to change respective CSS)
var tree = this.tree, model = tree.model;
if(tree._v10Compat && item === model.root){
// For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
item = null;
}
this.iconNode.className = "dijitInline dijitTreeIcon " + tree.getIconClass(item, this.isExpanded);
this.labelNode.className = "dijitTreeLabel " + tree.getLabelClass(item, this.isExpanded);
},
_updateLayout: function(){
// summary: set appropriate CSS classes for this.domNode
var parent = this.getParent();
if(!parent || parent.rowNode.style.display == "none"){
/* 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.contentNode.className = "dijitTreeContent dijitTreeContentExpanded";
this._setExpando();
this._updateItemClasses(this.item);
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.contentNode.className = "dijitTreeContent";
this._setExpando();
this._updateItemClasses(this.item);
this._wipeOut.play();
},
setLabelNode: function(label){
this.labelNode.innerHTML="";
this.labelNode.appendChild(dojo.doc.createTextNode(label));
},
setChildItems: function(/* Object[] */ items){
// summary:
// Sets the child items of this node, removing/adding nodes
// from current children to match specified items[] array.
var tree = this.tree,
model = tree.model;
// Orphan all my existing children.
// If items contains some of the same items as before then we will reattach them.
// Don't call this.removeChild() because that will collapse the tree etc.
this.getChildren().forEach(function(child){
dijit._Container.prototype.removeChild.call(this, child);
}, this);
this.state = "LOADED";
if(items && items.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, unless one already
// exists and isn't being used (presumably it's from a DnD move and was recently
// released
dojo.forEach(items, function(item){
var id = model.getIdentity(item),
existingNode = tree._itemNodeMap[id],
node =
( existingNode && !existingNode.getParent() ) ?
existingNode :
new dijit._TreeNode({
item: item,
tree: tree,
isExpandable: model.mayHaveChildren(item),
label: tree.getLabel(item)
});
this.addChild(node);
// note: this won't work if there are two nodes for one item (multi-parented items); will be fixed later
tree._itemNodeMap[id] = node;
if(this.tree.persist){
if(tree._openedItemIds[id]){
tree._expandNode(node);
}
}
}, 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/from dot or + icon, as appropriate
this._setExpando(false);
}
// On initial tree show, put focus on either the root node of the tree,
// or the first child, if the root node is hidden
if(!this.parent){
var fc = this.tree.showRoot ? this : this.getChildren()[0],
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});
}
},
removeChild: function(/* treeNode */ node){
this.inherited(arguments);
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);
},
_onNodeFocus: function(evt){
var node = dijit.getEnclosingWidget(evt.target);
this.tree._onTreeFocus(node);
}
});
dojo.declare(
"dijit.Tree",
[dijit._Widget, dijit._Templated],
{
// 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.
// May remove for 2.0 in favor of "model".
store: null,
// model: dijit.Tree.model
// Alternate interface from store to access data (and changes to data) in the tree
model: null,
// query: anything
// Specifies datastore query to return the root item for the tree.
//
// Deprecated functionality: if the query returns multiple items, the tree is given
// a fake root node (not corresponding to any item in the data store),
// whose children are the items that match this query.
//
// The root node is shown or hidden based on whether a label is specified.
//
// Having a query return multiple items is deprecated.
// If your store doesn't have a root item, wrap the store with
// dijit.tree.ForestStoreModel, and specify model=myModel
//
// example:
// {type:'continent'}
query: null,
// label: String
// Deprecated. Use dijit.tree.ForestStoreModel directly instead.
// Used in conjunction with query parameter.
// If a query is specified (rather than a root node id), and a label is also specified,
// then a fake root node is created and displayed, with this label.
label: "",
// showRoot: Boolean
// Should the root node be displayed, or hidden?
showRoot: true,
// childrenAttr: String[]
// one ore more attributes that holds children of a tree node
childrenAttr: ["children"],
// openOnClick: Boolean
// If true, clicking a folder node's label will open it, rather than calling onClick()
openOnClick: false,
templateString:"\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._itemNodeMap={};
if(!this.cookieName){
this.cookieName = this.id + "SaveStateCookie";
}
},
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 = dojo.doc.createElement('div');
div.style.display = 'none';
div.className = "dijitTreeContainer";
dijit.setWaiRole(div, "presentation");
this.containerNodeTemplate = div;
// Create glue between store and Tree, if not specified directly by user
if(!this.model){
this._store2model();
}
// monitor changes to items
this.connect(this.model, "onChange", "_onItemChange");
this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
// TODO: monitor item deletes so we don't end up w/orphaned nodes?
this._load();
this.inherited("postCreate", arguments);
if(this.dndController){
if(dojo.isString(this.dndController)){
this.dndController= dojo.getObject(this.dndController);
}
var params={};
for (var i=0; i