require({cache:{
'url:dijit/templates/TreeNode.html':"
\n\t\t\t\n\t\t \n\t
\n
\n",
'url:dijit/templates/Tree.html':"\n"}});
define("dijit/Tree", [
"dojo/_base/array", // array.filter array.forEach array.map
"dojo/_base/connect", // connect.isCopyKey()
"dojo/cookie", // cookie
"dojo/_base/declare", // declare
"dojo/Deferred", // Deferred
"dojo/DeferredList", // DeferredList
"dojo/dom", // dom.isDescendant
"dojo/dom-class", // domClass.add domClass.remove domClass.replace domClass.toggle
"dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.position
"dojo/dom-style",// domStyle.set
"dojo/_base/event", // event.stop
"dojo/errors/create", // createError
"dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // arrows etc.
"dojo/_base/lang", // lang.getObject lang.mixin lang.hitch
"dojo/on", // on(), on.selector()
"dojo/topic",
"dojo/touch",
"dojo/when",
"./focus",
"./registry", // registry.byNode(), registry.getEnclosingWidget()
"./_base/manager", // manager.defaultDuration
"./_Widget",
"./_TemplatedMixin",
"./_Container",
"./_Contained",
"./_CssStateMixin",
"dojo/text!./templates/TreeNode.html",
"dojo/text!./templates/Tree.html",
"./tree/TreeStoreModel",
"./tree/ForestStoreModel",
"./tree/_dndSelector"
], function(array, connect, cookie, declare, Deferred, DeferredList,
dom, domClass, domGeometry, domStyle, event, createError, fxUtils, kernel, keys, lang, on, topic, touch, when,
focus, registry, manager, _Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin,
treeNodeTemplate, treeTemplate, TreeStoreModel, ForestStoreModel, _dndSelector){
// module:
// dijit/Tree
// Back-compat shim
Deferred = declare(Deferred, {
addCallback: function(callback){ this.then(callback); },
addErrback: function(errback){ this.then(null, errback); }
});
var TreeNode = declare(
"dijit._TreeNode",
[_Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin],
{
// summary:
// Single node within a tree. This class is used internally
// by Tree and should not be accessed directly.
// tags:
// private
// item: [const] Item
// the dojo.data entry this tree represents
item: null,
// isTreeNode: [protected] Boolean
// Indicates that this is a TreeNode. Used by `dijit.Tree` only,
// should not be accessed directly.
isTreeNode: true,
// label: String
// Text of this tree node
label: "",
_setLabelAttr: {node: "labelNode", type: "innerText"},
// isExpandable: [private] Boolean
// This node has children, so show the expando node (+ sign)
isExpandable: null,
// isExpanded: [readonly] Boolean
// This node is currently expanded (ie, opened)
isExpanded: false,
// state: [private] 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: treeNodeTemplate,
baseClass: "dijitTreeNode",
// For hover effect for tree node, and focus effect for label
cssStateNodes: {
rowNode: "dijitTreeRow"
},
// Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
_setTooltipAttr: {node: "rowNode", type: "attribute", attribute: "title"},
buildRendering: function(){
this.inherited(arguments);
// set expand icon for leaf
this._setExpando();
// set icon and label class based on item
this._updateItemClasses(this.item);
if(this.isExpandable){
this.labelNode.setAttribute("aria-expanded", this.isExpanded);
}
//aria-selected should be false on all selectable elements.
this.setSelected(false);
},
_setIndentAttr: function(indent){
// summary:
// Tell this node how many levels it should be indented
// description:
// 0 for top level nodes, 1 for their children, 2 for their
// grandchildren, etc.
// Math.max() is to prevent negative padding on hidden root node (when indent == -1)
var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
domStyle.set(this.domNode, "backgroundPosition", pixels + " 0px"); // TODOC: what is this for???
domStyle.set(this.indentNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
array.forEach(this.getChildren(), function(child){
child.set("indent", indent+1);
});
this._set("indent", indent);
},
markProcessing: function(){
// summary:
// Visually denote that tree is loading data, etc.
// tags:
// private
this.state = "LOADING";
this._setExpando(true);
},
unmarkProcessing: function(){
// summary:
// Clear markup from markProcessing() call
// tags:
// private
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)
// tags:
// private
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._applyClassAndStyle(item, "icon", "Icon");
this._applyClassAndStyle(item, "label", "Label");
this._applyClassAndStyle(item, "row", "Row");
this.tree._startPaint(true); // signifies paint started and finished (synchronously)
},
_applyClassAndStyle: function(item, lower, upper){
// summary:
// Set the appropriate CSS classes and styles for labels, icons and rows.
//
// item:
// The data item.
//
// lower:
// The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
//
// upper:
// The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
//
// tags:
// private
var clsName = "_" + lower + "Class";
var nodeName = lower + "Node";
var oldCls = this[clsName];
this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
domClass.replace(this[nodeName], this[clsName] || "", oldCls || "");
domStyle.set(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
},
_updateLayout: function(){
// summary:
// Set appropriate CSS classes for this.domNode
// tags:
// private
var parent = this.getParent();
if(!parent || !parent.rowNode || parent.rowNode.style.display == "none"){
/* if we are hiding the root node then make every first level child look like a root node */
domClass.add(this.domNode, "dijitTreeIsRoot");
}else{
domClass.toggle(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
}
},
_setExpando: function(/*Boolean*/ processing){
// summary:
// Set the right image for the expando node
// tags:
// private
var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
"dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
_a11yStates = ["*","-","+","*"],
idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
// apply the appropriate class to the expando node
domClass.replace(this.expandoNode, styles[idx], styles);
// provide a non-image based indicator for images-off mode
this.expandoNodeText.innerHTML = _a11yStates[idx];
},
expand: function(){
// summary:
// Show my children
// returns:
// Deferred that fires when expansion is complete
// If there's already an expand in progress or we are already expanded, just return
if(this._expandDeferred){
return this._expandDeferred; // dojo/_base/Deferred
}
// cancel in progress collapse operation
if(this._collapseDeferred){
this._collapseDeferred.cancel();
delete this._collapseDeferred;
}
// All the state information for when a node is expanded, maybe this should be
// set when the animation completes instead
this.isExpanded = true;
this.labelNode.setAttribute("aria-expanded", "true");
if(this.tree.showRoot || this !== this.tree.rootNode){
this.containerNode.setAttribute("role", "group");
}
domClass.add(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
if(this == this.tree.rootNode && this.tree.showRoot){
this.tree.domNode.setAttribute("aria-expanded", "true");
}
var def,
wipeIn = fxUtils.wipeIn({
node: this.containerNode,
duration: manager.defaultDuration,
onEnd: function(){
def.resolve(true);
}
});
// Deferred that fires when expand is complete
def = (this._expandDeferred = new Deferred(function(){
// Canceller
wipeIn.stop();
}));
wipeIn.play();
return def; // dojo/_base/Deferred
},
collapse: function(){
// summary:
// Collapse this node (if it's expanded)
if(this._collapseDeferred){
// Node is already collapsed, or there's a collapse in progress, just return that Deferred
return this._collapseDeferred;
}
// cancel in progress expand operation
if(this._expandDeferred){
this._expandDeferred.cancel();
delete this._expandDeferred;
}
this.isExpanded = false;
this.labelNode.setAttribute("aria-expanded", "false");
if(this == this.tree.rootNode && this.tree.showRoot){
this.tree.domNode.setAttribute("aria-expanded", "false");
}
domClass.remove(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
var def,
wipeOut = fxUtils.wipeOut({
node: this.containerNode,
duration: manager.defaultDuration,
onEnd: function(){
def.resolve(true);
}
});
// Deferred that fires when expand is complete
def = (this._collapseDeferred = new Deferred(function(){
// Canceller
wipeOut.stop();
}));
wipeOut.play();
return def; // dojo/_base/Deferred
},
// indent: Integer
// Levels from this node to the root node
indent: 0,
setChildItems: function(/* Object[] */ items){
// summary:
// Sets the child items of this node, removing/adding nodes
// from current children to match specified items[] array.
// Also, if this.persist == true, expands any children that were previously
// opened.
// returns:
// Deferred object that fires after all previously opened children
// have been expanded again (or fires instantly if there are no such children).
var tree = this.tree,
model = tree.model,
defs = []; // list of deferreds that need to fire before I am complete
// 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.
var oldChildren = this.getChildren();
array.forEach(oldChildren, function(child){
_Container.prototype.removeChild.call(this, child);
}, this);
// All the old children of this TreeNode are subject for destruction if
// 1) they aren't listed in the new children array (items)
// 2) they aren't immediately adopted by another node (DnD)
this.defer(function(){
array.forEach(oldChildren, function(node){
if(!node._destroyed && !node.getParent()){
// If node is in selection then remove it.
tree.dndController.removeTreeNode(node);
// Deregister mapping from item id --> this node
var id = model.getIdentity(node.item),
ary = tree._itemNodesMap[id];
if(ary.length == 1){
delete tree._itemNodesMap[id];
}else{
var index = array.indexOf(ary, node);
if(index != -1){
ary.splice(index, 1);
}
}
// And finally we can destroy the node
node.destroyRecursive();
}
});
});
this.state = "LOADED";
if(items && items.length > 0){
this.isExpandable = true;
// 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
array.forEach(items, function(item){ // MARKER: REUSE NODE
var id = model.getIdentity(item),
existingNodes = tree._itemNodesMap[id],
node;
if(existingNodes){
for(var i=0;i itself
tree.domNode.setAttribute("tabIndex", "0");
}
}
var def = new DeferredList(defs);
this.tree._startPaint(def); // to reset TreeNode widths after an item is added/removed from the Tree
return def; // dojo/_base/Deferred
},
getTreePath: function(){
var node = this;
var path = [];
while(node && node !== this.tree.rootNode){
path.unshift(node.item);
node = node.getParent();
}
path.unshift(this.tree.rootNode.item);
return path;
},
getIdentity: function(){
return this.tree.model.getIdentity(this.item);
},
removeChild: function(/* treeNode */ node){
this.inherited(arguments);
var children = this.getChildren();
if(children.length == 0){
this.isExpandable = false;
this.collapse();
}
array.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()
// TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
this.isExpandable = true;
this._setExpando(false);
},
setSelected: function(/*Boolean*/ selected){
// summary:
// A Tree has a (single) currently selected node.
// Mark that this node is/isn't that currently selected node.
// description:
// In particular, setting a node as selected involves setting tabIndex
// so that when user tabs to the tree, focus will go to that node (only).
this.labelNode.setAttribute("aria-selected", selected ? "true" : "false");
domClass.toggle(this.rowNode, "dijitTreeRowSelected", selected);
},
setFocusable: function(/*Boolean*/ selected){
// summary:
// A Tree has a (single) node that's focusable.
// Mark that this node is/isn't that currently focsuable node.
// description:
// In particular, setting a node as selected involves setting tabIndex
// so that when user tabs to the tree, focus will go to that node (only).
this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
},
_setTextDirAttr: function(textDir){
if(textDir &&((this.textDir != textDir) || !this._created)){
this._set("textDir", textDir);
this.applyTextDir(this.labelNode, this.labelNode.innerText || this.labelNode.textContent || "");
array.forEach(this.getChildren(), function(childNode){
childNode.set("textDir", textDir);
}, this);
}
}
});
var Tree = declare("dijit.Tree", [_Widget, _TemplatedMixin], {
// summary:
// This widget displays hierarchical data from a store.
// store: [deprecated] String|dojo/data/Store
// Deprecated. Use "model" parameter instead.
// The store to get data to display in the tree.
store: null,
// model: dijit/tree/model
// Interface to read tree data, get notifications of changes to tree data,
// and for handling drop operations (i.e drag and drop onto the tree)
model: null,
// query: [deprecated] anything
// Deprecated. User should specify query to the model directly instead.
// Specifies datastore query to return the root item or top items for the tree.
query: null,
// label: [deprecated] 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: [const] Boolean
// Should the root node be displayed, or hidden?
showRoot: true,
// childrenAttr: [deprecated] String[]
// Deprecated. This information should be specified in the model.
// One ore more attributes that holds children of a tree node
childrenAttr: ["children"],
// paths: String[][] or Item[][]
// Full paths from rootNode to selected nodes expressed as array of items or array of ids.
// Since setting the paths may be asynchronous (because of waiting on dojo.data), set("paths", ...)
// returns a Deferred to indicate when the set is complete.
paths: [],
// path: String[] or Item[]
// Backward compatible singular variant of paths.
path: [],
// selectedItems: [readonly] Item[]
// The currently selected items in this tree.
// This property can only be set (via set('selectedItems', ...)) when that item is already
// visible in the tree. (I.e. the tree has already been expanded to show that node.)
// Should generally use `paths` attribute to set the selected items instead.
selectedItems: null,
// selectedItem: [readonly] Item
// Backward compatible singular variant of selectedItems.
selectedItem: null,
// openOnClick: Boolean
// If true, clicking a folder node's label will open it, rather than calling onClick()
openOnClick: false,
// openOnDblClick: Boolean
// If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
openOnDblClick: false,
templateString: treeTemplate,
// persist: Boolean
// Enables/disables use of cookies for state saving.
persist: true,
// autoExpand: Boolean
// Fully expand the tree on load. Overrides `persist`.
autoExpand: false,
// dndController: [protected] Function|String
// Class to use as as the dnd controller. Specifying this class enables DnD.
// Generally you should specify this as dijit/tree/dndSource.
// Setting of dijit/tree/_dndSelector handles selection only (no actual DnD).
dndController: _dndSelector,
// parameters to pull off of the tree and pass on to the dndController as its params
dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
//declare the above items so they can be pulled from the tree's markup
// onDndDrop: [protected] Function
// Parameter to dndController, see `dijit/tree/dndSource.onDndDrop()`.
// Generally this doesn't need to be set.
onDndDrop: null,
itemCreator: null,
/*=====
itemCreator: function(nodes, target, source){
// summary:
// Returns objects passed to `Tree.model.newItem()` based on DnD nodes
// dropped onto the tree. Developer must override this method to enable
// dropping from external sources onto this Tree, unless the Tree.model's items
// happen to look like {id: 123, name: "Apple" } with no other attributes.
//
// For each node in nodes[], which came from source, create a hash of name/value
// pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
// nodes: DomNode[]
// The DOMNodes dragged from the source container
// target: DomNode
// The target TreeNode.rowNode
// source: dojo/dnd/Source
// The source container the nodes were dragged from, perhaps another Tree or a plain dojo/dnd/Source
// returns: Object[]
// Array of name/value hashes for each new item to be added to the Tree, like:
// | [
// | { id: 123, label: "apple", foo: "bar" },
// | { id: 456, label: "pear", zaz: "bam" }
// | ]
// tags:
// extension
return [{}];
},
=====*/
// onDndCancel: [protected] Function
// Parameter to dndController, see `dijit/tree/dndSource.onDndCancel()`.
// Generally this doesn't need to be set.
onDndCancel: null,
/*=====
checkAcceptance: function(source, nodes){
// summary:
// Checks if the Tree itself can accept nodes from this source
// source: dijit/tree/dndSource
// The source which provides items
// nodes: DOMNode[]
// Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
// source is a dijit/Tree.
// tags:
// extension
return true; // Boolean
},
=====*/
checkAcceptance: null,
/*=====
checkItemAcceptance: function(target, source, position){
// summary:
// Stub function to be overridden if one wants to check for the ability to drop at the node/item level
// description:
// In the base case, this is called to check if target can become a child of source.
// When betweenThreshold is set, position="before" or "after" means that we
// are asking if the source node can be dropped before/after the target node.
// target: DOMNode
// The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
// Use registry.getEnclosingWidget(target) to get the TreeNode.
// source: dijit/tree/dndSource
// The (set of) nodes we are dropping
// position: String
// "over", "before", or "after"
// tags:
// extension
return true; // Boolean
},
=====*/
checkItemAcceptance: null,
// dragThreshold: Integer
// Number of pixels mouse moves before it's considered the start of a drag operation
dragThreshold: 5,
// betweenThreshold: Integer
// Set to a positive value to allow drag and drop "between" nodes.
//
// If during DnD mouse is over a (target) node but less than betweenThreshold
// pixels from the bottom edge, dropping the the dragged node will make it
// the next sibling of the target node, rather than the child.
//
// Similarly, if mouse is over a target node but less that betweenThreshold
// pixels from the top edge, dropping the dragged node will make it
// the target node's previous sibling rather than the target node's child.
betweenThreshold: 0,
// _nodePixelIndent: Integer
// Number of pixels to indent tree nodes (relative to parent node).
// Default is 19 but can be overridden by setting CSS class dijitTreeIndent
// and calling resize() or startup() on tree after it's in the DOM.
_nodePixelIndent: 19,
_publish: function(/*String*/ topicName, /*Object*/ message){
// summary:
// Publish a message for this widget/topic
topic.publish(this.id, lang.mixin({tree: this, event: topicName}, message || {})); // publish
},
postMixInProperties: function(){
this.tree = this;
if(this.autoExpand){
// There's little point in saving opened/closed state of nodes for a Tree
// that initially opens all it's nodes.
this.persist = false;
}
this._itemNodesMap = {};
if(!this.cookieName && this.id){
this.cookieName = this.id + "SaveStateCookie";
}
// Deferred that fires when all the children have loaded.
this.expandChildrenDeferred = new Deferred();
// Deferred that fires when all pending operations complete.
this.pendingCommandsDeferred = this.expandChildrenDeferred;
this.inherited(arguments);
},
postCreate: function(){
this._initState();
// Catch events on TreeNodes
var self = this;
this.own(
on(this.domNode, on.selector(".dijitTreeNode", touch.enter), function(evt){
self._onNodeMouseEnter(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeNode", touch.leave), function(evt){
self._onNodeMouseLeave(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeNode", "click"), function(evt){
self._onClick(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeNode", "dblclick"), function(evt){
self._onDblClick(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeNode", "keypress"), function(evt){
self._onKeyPress(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeNode", "keydown"), function(evt){
self._onKeyDown(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeRow", "focusin"), function(evt){
self._onNodeFocus(registry.getEnclosingWidget(this), evt);
})
);
// 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");
this.connect(this.model, "onDelete", "_onItemDelete");
this.inherited(arguments);
if(this.dndController){
if(lang.isString(this.dndController)){
this.dndController = lang.getObject(this.dndController);
}
var params={};
for(var i=0; i