]> git.wh0rd.org - tt-rss.git/blobdiff - lib/dijit/Tree.js
update dojo to 1.7.3
[tt-rss.git] / lib / dijit / Tree.js
index d702a3b94e56bb8a0aee524c6a684cee6c2b6390..25c44568a6bb8de93505928883e595e723557f81 100644 (file)
-/*
-       Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
-       Available via Academic Free License >= 2.1 OR the modified BSD license.
-       see: http://dojotoolkit.org/license for details
-*/
-
-
-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("dojo.DeferredList");
-dojo.require("dijit._Widget");
-dojo.require("dijit._Templated");
-dojo.require("dijit._Container");
-dojo.require("dijit._Contained");
-dojo.require("dijit._CssStateMixin");
-dojo.require("dojo.cookie");
-dojo.require("dijit.tree.TreeStoreModel");
-dojo.require("dijit.tree.ForestStoreModel");
-dojo.require("dijit.tree._dndSelector");
-
-
-dojo.declare(
-       "dijit._TreeNode",
-       [dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained, dijit._CssStateMixin],
-{
-       // summary:
-       //              Single node within a tree.   This class is used internally
-       //              by Tree and should not be accessed directly.
-       // tags:
-       //              private
-
-       // item: [const] dojo.data.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: "",
-
-       // 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: dojo.cache("dijit", "templates/TreeNode.html", "<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div dojoAttachPoint=\"rowNode\" class=\"dijitTreeRow\" role=\"presentation\" dojoAttachEvent=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\n\t\t><img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span dojoAttachPoint=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span dojoAttachPoint=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span dojoAttachPoint=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\" dojoAttachEvent=\"onfocus:_onLabelFocus\"></span>\n\t\t</span\n\t></div>\n\t<div dojoAttachPoint=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n"),
-
-       baseClass: "dijitTreeNode",
-
-       // For hover effect for tree node, and focus effect for label
-       cssStateNodes: {
-               rowNode: "dijitTreeRow",
-               labelNode: "dijitTreeLabel"
-       },
-
-       attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
-               label: {node: "labelNode", type: "innerText"},
-               tooltip: {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){
-                       dijit.setWaiState(this.labelNode, "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";
-
-               dojo.style(this.domNode, "backgroundPosition",  pixels + " 0px");
-               dojo.style(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
-
-               dojo.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");
-       },
-
-       _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);
-               dojo.replaceClass(this[nodeName], this[clsName] || "", oldCls || "");
-               dojo.style(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.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
-               // 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
-               dojo.replaceClass(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.Deferred
-               }
-
-               // cancel in progress collapse operation
-               this._wipeOut && this._wipeOut.stop();
-
-               // All the state information for when a node is expanded, maybe this should be
-               // set when the animation completes instead
-               this.isExpanded = true;
-               dijit.setWaiState(this.labelNode, "expanded", "true");
-               if(this.tree.showRoot || this !== this.tree.rootNode){
-                       dijit.setWaiRole(this.containerNode, "group");
-               }
-               dojo.addClass(this.contentNode,'dijitTreeContentExpanded');
-               this._setExpando();
-               this._updateItemClasses(this.item);
-               if(this == this.tree.rootNode){
-                       dijit.setWaiState(this.tree.domNode, "expanded", "true");
-               }
-
-               var def,
-                       wipeIn = dojo.fx.wipeIn({
-                               node: this.containerNode, duration: dijit.defaultDuration,
-                               onEnd: function(){
-                                       def.callback(true);
-                               }
-                       });
-
-               // Deferred that fires when expand is complete
-               def = (this._expandDeferred = new dojo.Deferred(function(){
-                       // Canceller
-                       wipeIn.stop();
-               }));
-
-               wipeIn.play();
-
-               return def;             // dojo.Deferred
-       },
-
-       collapse: function(){
-               // summary:
-               //              Collapse this node (if it's expanded)
-
-               if(!this.isExpanded){ return; }
-
-               // cancel in progress expand operation
-               if(this._expandDeferred){
-                       this._expandDeferred.cancel();
-                       delete this._expandDeferred;
-               }
-
-               this.isExpanded = false;
-               dijit.setWaiState(this.labelNode, "expanded", "false");
-               if(this == this.tree.rootNode){
-                       dijit.setWaiState(this.tree.domNode, "expanded", "false");
-               }
-               dojo.removeClass(this.contentNode,'dijitTreeContentExpanded');
-               this._setExpando();
-               this._updateItemClasses(this.item);
-
-               if(!this._wipeOut){
-                       this._wipeOut = dojo.fx.wipeOut({
-                               node: this.containerNode, duration: dijit.defaultDuration
-                       });
-               }
-               this._wipeOut.play();
-       },
-
-       // 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.
-               dojo.forEach(this.getChildren(), function(child){
-                       dijit._Container.prototype.removeChild.call(this, child);
-               }, this);
-
-               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
-                       dojo.forEach(items, function(item){
-                               var id = model.getIdentity(item),
-                                       existingNodes = tree._itemNodesMap[id],
-                                       node;
-                               if(existingNodes){
-                                       for(var i=0;i<existingNodes.length;i++){
-                                               if(existingNodes[i] && !existingNodes[i].getParent()){
-                                                       node = existingNodes[i];
-                                                       node.set('indent', this.indent+1);
-                                                       break;
-                                               }
-                                       }
-                               }
-                               if(!node){
-                                       node = this.tree._createTreeNode({
-                                                       item: item,
-                                                       tree: tree,
-                                                       isExpandable: model.mayHaveChildren(item),
-                                                       label: tree.getLabel(item),
-                                                       tooltip: tree.getTooltip(item),
-                                                       dir: tree.dir,
-                                                       lang: tree.lang,
-                                                       indent: this.indent + 1
-                                               });
-                                       if(existingNodes){
-                                               existingNodes.push(node);
-                                       }else{
-                                               tree._itemNodesMap[id] = [node];
-                                       }
-                               }
-                               this.addChild(node);
-
-                               // If node was previously opened then open it again now (this may trigger
-                               // more data store accesses, recursively)
-                               if(this.tree.autoExpand || this.tree._state(item)){
-                                       defs.push(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);
-               }
-
-               // Set leaf icon or folder icon, as appropriate
-               this._updateItemClasses(this.item);
-
-               // On initial tree show, make the selected TreeNode as either the root node of the tree,
-               // or the first child, if the root node is hidden
-               if(this == tree.rootNode){
-                       var fc = this.tree.showRoot ? this : this.getChildren()[0];
-                       if(fc){
-                               fc.setFocusable(true);
-                               tree.lastFocused = fc;
-                       }else{
-                               // fallback: no nodes in tree so focus on Tree <div> itself
-                               tree.domNode.setAttribute("tabIndex", "0");
-                       }
-               }
-
-               return new dojo.DeferredList(defs);     // dojo.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();
-               }
-
-               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()
-
-               // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
-
-               this.isExpandable = true;
-               this._setExpando(false);
-       },
-
-       _onLabelFocus: function(evt){
-               // summary:
-               //              Called when this row is focused (possibly programatically)
-               //              Note that we aren't using _onFocus() builtin to dijit
-               //              because it's called when focus is moved to a descendant TreeNode.
-               // tags:
-               //              private
-               this.tree._onNodeFocus(this);
-       },
-
-       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).
-               dijit.setWaiState(this.labelNode, "selected", selected);
-               dojo.toggleClass(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");
-       },
-
-       _onClick: function(evt){
-               // summary:
-               //              Handler for onclick event on a node
-               // tags:
-               //              private
-               this.tree._onClick(this, evt);
-       },
-       _onDblClick: function(evt){
-               // summary:
-               //              Handler for ondblclick event on a node
-               // tags:
-               //              private
-               this.tree._onDblClick(this, evt);
-       },
-
-       _onMouseEnter: function(evt){
-               // summary:
-               //              Handler for onmouseenter event on a node
-               // tags:
-               //              private
-               this.tree._onNodeMouseEnter(this, evt);
-       },
-
-       _onMouseLeave: function(evt){
-               // summary:
-               //              Handler for onmouseenter event on a node
-               // tags:
-               //              private
-               this.tree._onNodeMouseLeave(this, evt);
-       }
-});
-
-dojo.declare(
-       "dijit.Tree",
-       [dijit._Widget, dijit._Templated],
-{
-       // 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 ofwaiting 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: dojo.cache("dijit", "templates/Tree.html", "<div class=\"dijitTree dijitTreeContainer\" role=\"tree\"\n\tdojoAttachEvent=\"onkeypress:_onKeyPress\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" dojoAttachPoint=\"indentDetector\"></div>\n</div>\n"),
-
-       // 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] String
-       //              Class name to use as as the dnd controller.  Specifying this class enables DnD.
-       //              Generally you should specify this as "dijit.tree.dndSource".
-       //      Default of "dijit.tree._dndSelector" handles selection only (no actual DnD).
-       dndController: "dijit.tree._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: 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.
-               // description:
-               //              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 [{}];
-       },
-       =====*/
-       itemCreator: null,
-
-       // 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 dijit.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
-               dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message || {})]);
-       },
-
-       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.cookieName = this.id + "SaveStateCookie";
-               }
-
-               this._loadDeferred = new dojo.Deferred();
-
-               this.inherited(arguments);
-       },
-
-       postCreate: function(){
-               this._initState();
-
-               // 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._load();
-
-               this.inherited(arguments);
-
-               if(this.dndController){
-                       if(dojo.isString(this.dndController)){
-                               this.dndController = dojo.getObject(this.dndController);
-                       }
-                       var params={};
-                       for(var i=0; i<this.dndParams.length;i++){
-                               if(this[this.dndParams[i]]){
-                                       params[this.dndParams[i]] = this[this.dndParams[i]];
-                               }
-                       }
-                       this.dndController = new this.dndController(this, params);
-               }
-       },
-
-       _store2model: function(){
-               // summary:
-               //              User specified a store&query rather than model, so create model from store/query
-               this._v10Compat = true;
-               dojo.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
-
-               var modelParams = {
-                       id: this.id + "_ForestStoreModel",
-                       store: this.store,
-                       query: this.query,
-                       childrenAttrs: this.childrenAttr
-               };
-
-               // Only override the model's mayHaveChildren() method if the user has specified an override
-               if(this.params.mayHaveChildren){
-                       modelParams.mayHaveChildren = dojo.hitch(this, "mayHaveChildren");
-               }
-
-               if(this.params.getItemChildren){
-                       modelParams.getChildren = dojo.hitch(this, function(item, onComplete, onError){
-                               this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
-                       });
-               }
-               this.model = new dijit.tree.ForestStoreModel(modelParams);
-
-               // For backwards compatibility, the visibility of the root node is controlled by
-               // whether or not the user has specified a label
-               this.showRoot = Boolean(this.label);
-       },
-
-       onLoad: function(){
-               // summary:
-               //              Called when tree finishes loading and expanding.
-               // description:
-               //              If persist == true the loading may encompass many levels of fetches
-               //              from the data store, each asynchronous.   Waits for all to finish.
-               // tags:
-               //              callback
-       },
-
-       _load: function(){
-               // summary:
-               //              Initial load of the tree.
-               //              Load root node (possibly hidden) and it's children.
-               this.model.getRoot(
-                       dojo.hitch(this, function(item){
-                               var rn = (this.rootNode = this.tree._createTreeNode({
-                                       item: item,
-                                       tree: this,
-                                       isExpandable: true,
-                                       label: this.label || this.getLabel(item),
-                                       indent: this.showRoot ? 0 : -1
-                               }));
-                               if(!this.showRoot){
-                                       rn.rowNode.style.display="none";
-                                       // if root is not visible, move tree role to the invisible
-                                       // root node's containerNode, see #12135
-                                       dijit.setWaiRole(this.domNode, 'presentation');
-                                       
-                                       dijit.setWaiRole(rn.labelNode, 'presentation');
-                                       dijit.setWaiRole(rn.containerNode, 'tree');
-                               }
-                               this.domNode.appendChild(rn.domNode);
-                               var identity = this.model.getIdentity(item);
-                               if(this._itemNodesMap[identity]){
-                                       this._itemNodesMap[identity].push(rn);
-                               }else{
-                                       this._itemNodesMap[identity] = [rn];
-                               }
-
-                               rn._updateLayout();             // sets "dijitTreeIsRoot" CSS classname
-
-                               // load top level children and then fire onLoad() event
-                               this._expandNode(rn).addCallback(dojo.hitch(this, function(){
-                                       this._loadDeferred.callback(true);
-                                       this.onLoad();
-                               }));
-                       }),
-                       function(err){
-                               console.error(this, ": error loading root: ", err);
-                       }
-               );
-       },
-
-       getNodesByItem: function(/*dojo.data.Item or id*/ item){
-               // summary:
-               //              Returns all tree nodes that refer to an item
-               // returns:
-               //              Array of tree nodes that refer to passed item
-
-               if(!item){ return []; }
-               var identity = dojo.isString(item) ? item : this.model.getIdentity(item);
-               // return a copy so widget don't get messed up by changes to returned array
-               return [].concat(this._itemNodesMap[identity]);
-       },
-
-       _setSelectedItemAttr: function(/*dojo.data.Item or id*/ item){
-               this.set('selectedItems', [item]);
-       },
-
-       _setSelectedItemsAttr: function(/*dojo.data.Items or ids*/ items){
-               // summary:
-               //              Select tree nodes related to passed items.
-               //              WARNING: if model use multi-parented items or desired tree node isn't already loaded
-               //              behavior is undefined. Use set('paths', ...) instead.
-               var tree = this;
-               this._loadDeferred.addCallback( dojo.hitch(this, function(){
-                       var identities = dojo.map(items, function(item){
-                               return (!item || dojo.isString(item)) ? item : tree.model.getIdentity(item);
-                       });
-                       var nodes = [];
-                       dojo.forEach(identities, function(id){
-                               nodes = nodes.concat(tree._itemNodesMap[id] || []);
-                       });
-                       this.set('selectedNodes', nodes);
-               }));
-       },
-
-       _setPathAttr: function(/*Item[] || String[]*/ path){
-               // summary:
-               //      Singular variant of _setPathsAttr
-               if(path.length) {
-                       return this.set("paths", [path]);
-               } else {
-                       //Empty list is interpreted as "select nothing"
-                       return this.set("paths", []);
-               }
-       },
-       
-       _setPathsAttr: function(/*Item[][] || String[][]*/ paths){
-               // summary:
-               //              Select the tree nodes identified by passed paths.
-               // paths:
-               //              Array of arrays of items or item id's
-               // returns:
-               //              Deferred to indicate when the set is complete
-               var tree = this;
-
-               // We may need to wait for some nodes to expand, so setting
-               // each path will involve a Deferred. We bring those deferreds
-               // together witha DeferredList.
-               return new dojo.DeferredList(dojo.map(paths, function(path){
-                       var d = new dojo.Deferred();
-                       
-                       // normalize path to use identity
-                       path = dojo.map(path, function(item){
-                               return dojo.isString(item) ? item : tree.model.getIdentity(item);
-                       });
-
-                       if(path.length){
-                               // Wait for the tree to load, if it hasn't already.
-                               tree._loadDeferred.addCallback(function(){ selectPath(path, [tree.rootNode], d); });
-                       }else{
-                               d.errback("Empty path");
-                       }
-                       return d;
-               })).addCallback(setNodes);
-
-               function selectPath(path, nodes, def){
-                       // Traverse path; the next path component should be among "nodes".
-                       var nextPath = path.shift();
-                       var nextNode = dojo.filter(nodes, function(node){
-                               return node.getIdentity() == nextPath;
-                       })[0];
-                       if(!!nextNode){
-                               if(path.length){
-                                       tree._expandNode(nextNode).addCallback(function(){ selectPath(path, nextNode.getChildren(), def); });
-                               }else{
-                                       //Successfully reached the end of this path
-                                       def.callback(nextNode);
-                               }
-                       } else {
-                               def.errback("Could not expand path at " + nextPath);
-                       }
-               }
-               
-               function setNodes(newNodes){
-                       //After all expansion is finished, set the selection to
-                       //the set of nodes successfully found.
-                       tree.set("selectedNodes", dojo.map(
-                               dojo.filter(newNodes,function(x){return x[0];}),
-                               function(x){return x[1];}));
-               }
-       },
-
-       _setSelectedNodeAttr: function(node){
-               this.set('selectedNodes', [node]);
-       },
-       _setSelectedNodesAttr: function(nodes){
-               this._loadDeferred.addCallback( dojo.hitch(this, function(){
-                       this.dndController.setSelection(nodes);
-               }));
-       },
-
-
-       ////////////// Data store related functions //////////////////////
-       // These just get passed to the model; they are here for back-compat
-
-       mayHaveChildren: function(/*dojo.data.Item*/ item){
-               // summary:
-               //              Deprecated.   This should be specified on the model itself.
-               //
-               //              Overridable function to tell if an item has or may have children.
-               //              Controls whether or not +/- expando icon is shown.
-               //              (For efficiency reasons we may not want to check if an element actually
-               //              has children until user clicks the expando node)
-               // tags:
-               //              deprecated
-       },
-
-       getItemChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete){
-               // summary:
-               //              Deprecated.   This should be specified on the model itself.
-               //
-               //              Overridable function that return array of child items of given parent item,
-               //              or if parentItem==null then return top items in tree
-               // tags:
-               //              deprecated
-       },
-
-       ///////////////////////////////////////////////////////
-       // Functions for converting an item to a TreeNode
-       getLabel: function(/*dojo.data.Item*/ item){
-               // summary:
-               //              Overridable function to get the label for a tree node (given the item)
-               // tags:
-               //              extension
-               return this.model.getLabel(item);       // String
-       },
-
-       getIconClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
-               // summary:
-               //              Overridable function to return CSS class name to display icon
-               // tags:
-               //              extension
-               return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
-       },
-
-       getLabelClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
-               // summary:
-               //              Overridable function to return CSS class name to display label
-               // tags:
-               //              extension
-       },
-
-       getRowClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
-               // summary:
-               //              Overridable function to return CSS class name to display row
-               // tags:
-               //              extension
-       },
-
-       getIconStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
-               // summary:
-               //              Overridable function to return CSS styles to display icon
-               // returns:
-               //              Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
-               // tags:
-               //              extension
-       },
-
-       getLabelStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
-               // summary:
-               //              Overridable function to return CSS styles to display label
-               // returns:
-               //              Object suitable for input to dojo.style() like {color: "red", background: "green"}
-               // tags:
-               //              extension
-       },
-
-       getRowStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
-               // summary:
-               //              Overridable function to return CSS styles to display row
-               // returns:
-               //              Object suitable for input to dojo.style() like {background-color: "#bbb"}
-               // tags:
-               //              extension
-       },
-
-       getTooltip: function(/*dojo.data.Item*/ item){
-               // summary:
-               //              Overridable function to get the tooltip for a tree node (given the item)
-               // tags:
-               //              extension
-               return "";      // String
-       },
-
-       /////////// Keyboard and Mouse handlers ////////////////////
-
-       _onKeyPress: function(/*Event*/ e){
-               // summary:
-               //              Translates keypress events into commands for the controller
-               if(e.altKey){ return; }
-               var dk = dojo.keys;
-               var treeNode = dijit.getEnclosingWidget(e.target);
-               if(!treeNode){ return; }
-
-               var key = e.charOrCode;
-               if(typeof key == "string" && key != " "){       // handle printables (letter navigation)
-                       // Check for key navigation.
-                       if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
-                               this._onLetterKeyNav( { node: treeNode, key: key.toLowerCase() } );
-                               dojo.stopEvent(e);
-                       }
-               }else{  // handle non-printables (arrow keys)
-                       // clear record of recent printables (being saved for multi-char letter navigation),
-                       // because "a", down-arrow, "b" shouldn't search for "ab"
-                       if(this._curSearch){
-                               clearTimeout(this._curSearch.timer);
-                               delete this._curSearch;
-                       }
-
-                       var map = this._keyHandlerMap;
-                       if(!map){
-                               // setup table mapping keys to events
-                               map = {};
-                               map[dk.ENTER]="_onEnterKey";
-                               //On WebKit based browsers, the combination ctrl-enter
-                               //does not get passed through. To allow accessible
-                               //multi-select on those browsers, the space key is
-                               //also used for selection.
-                               map[dk.SPACE]= map[" "] = "_onEnterKey";
-                               map[this.isLeftToRight() ? dk.LEFT_ARROW : dk.RIGHT_ARROW]="_onLeftArrow";
-                               map[this.isLeftToRight() ? dk.RIGHT_ARROW : dk.LEFT_ARROW]="_onRightArrow";
-                               map[dk.UP_ARROW]="_onUpArrow";
-                               map[dk.DOWN_ARROW]="_onDownArrow";
-                               map[dk.HOME]="_onHomeKey";
-                               map[dk.END]="_onEndKey";
-                               this._keyHandlerMap = map;
-                       }
-                       if(this._keyHandlerMap[key]){
-                               this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
-                               dojo.stopEvent(e);
-                       }
-               }
-       },
-
-       _onEnterKey: function(/*Object*/ message){
-               this._publish("execute", { item: message.item, node: message.node } );
-               this.dndController.userSelect(message.node, dojo.isCopyKey( message.evt ), message.evt.shiftKey);
-               this.onClick(message.item, message.node, message.evt);
-       },
-
-       _onDownArrow: function(/*Object*/ message){
-               // summary:
-               //              down arrow pressed; get next visible node, set focus there
-               var node = this._getNextNode(message.node);
-               if(node && node.isTreeNode){
-                       this.focusNode(node);
-               }
-       },
-
-       _onUpArrow: function(/*Object*/ message){
-               // summary:
-               //              Up arrow pressed; move to previous visible node
-
-               var node = message.node;
-
-               // if younger siblings
-               var previousSibling = node.getPreviousSibling();
-               if(previousSibling){
-                       node = previousSibling;
-                       // if the previous node is expanded, dive in deep
-                       while(node.isExpandable && node.isExpanded && node.hasChildren()){
-                               // move to the last child
-                               var children = node.getChildren();
-                               node = children[children.length-1];
-                       }
-               }else{
-                       // if this is the first child, return the parent
-                       // unless the parent is the root of a tree with a hidden root
-                       var parent = node.getParent();
-                       if(!(!this.showRoot && parent === this.rootNode)){
-                               node = parent;
-                       }
-               }
-
-               if(node && node.isTreeNode){
-                       this.focusNode(node);
-               }
-       },
-
-       _onRightArrow: function(/*Object*/ message){
-               // summary:
-               //              Right arrow pressed; go to child node
-               var node = message.node;
-
-               // if not expanded, expand, else move to 1st child
-               if(node.isExpandable && !node.isExpanded){
-                       this._expandNode(node);
-               }else if(node.hasChildren()){
-                       node = node.getChildren()[0];
-                       if(node && node.isTreeNode){
-                               this.focusNode(node);
-                       }
-               }
-       },
-
-       _onLeftArrow: function(/*Object*/ message){
-               // summary:
-               //              Left arrow pressed.
-               //              If not collapsed, collapse, else move to parent.
-
-               var node = message.node;
-
-               if(node.isExpandable && node.isExpanded){
-                       this._collapseNode(node);
-               }else{
-                       var parent = node.getParent();
-                       if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){
-                               this.focusNode(parent);
-                       }
-               }
-       },
-
-       _onHomeKey: function(){
-               // summary:
-               //              Home key pressed; get first visible node, and set focus there
-               var node = this._getRootOrFirstNode();
-               if(node){
-                       this.focusNode(node);
-               }
-       },
-
-       _onEndKey: function(/*Object*/ message){
-               // summary:
-               //              End key pressed; go to last visible node.
-
-               var node = this.rootNode;
-               while(node.isExpanded){
-                       var c = node.getChildren();
-                       node = c[c.length - 1];
-               }
-
-               if(node && node.isTreeNode){
-                       this.focusNode(node);
-               }
-       },
-
-       // multiCharSearchDuration: Number
-       //              If multiple characters are typed where each keystroke happens within
-       //              multiCharSearchDuration of the previous keystroke,
-       //              search for nodes matching all the keystrokes.
-       //
-       //              For example, typing "ab" will search for entries starting with
-       //              "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
-       multiCharSearchDuration: 250,
-
-       _onLetterKeyNav: function(message){
-               // summary:
-               //              Called when user presses a prinatable key; search for node starting with recently typed letters.
-               // message: Object
-               //              Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
-
-               // Branch depending on whether this key starts a new search, or modifies an existing search
-               var cs = this._curSearch;
-               if(cs){
-                       // We are continuing a search.  Ex: user has pressed 'a', and now has pressed
-                       // 'b', so we want to search for nodes starting w/"ab".
-                       cs.pattern = cs.pattern + message.key;
-                       clearTimeout(cs.timer);
-               }else{
-                       // We are starting a new search
-                       cs = this._curSearch = {
-                                       pattern: message.key,
-                                       startNode: message.node
-                       };
-               }
-
-               // set/reset timer to forget recent keystrokes
-               var self = this;
-               cs.timer = setTimeout(function(){
-                       delete self._curSearch;
-               }, this.multiCharSearchDuration);
-
-               // Navigate to TreeNode matching keystrokes [entered so far].
-               var node = cs.startNode;
-               do{
-                       node = this._getNextNode(node);
-                       //check for last node, jump to first node if necessary
-                       if(!node){
-                               node = this._getRootOrFirstNode();
-                       }
-               }while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern));
-               if(node && node.isTreeNode){
-                       // no need to set focus if back where we started
-                       if(node !== cs.startNode){
-                               this.focusNode(node);
-                       }
-               }
-       },
-
-       isExpandoNode: function(node, widget){
-               // summary:
-               //              check whether a dom node is the expandoNode for a particular TreeNode widget
-               return dojo.isDescendant(node, widget.expandoNode);
-       },
-       _onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
-               // summary:
-               //              Translates click events into commands for the controller to process
-
-               var domElement = e.target,
-                       isExpandoClick = this.isExpandoNode(domElement, nodeWidget);
-
-               if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){
-                       // expando node was clicked, or label of a folder node was clicked; open it
-                       if(nodeWidget.isExpandable){
-                               this._onExpandoClick({node:nodeWidget});
-                       }
-               }else{
-                       this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
-                       this.onClick(nodeWidget.item, nodeWidget, e);
-                       this.focusNode(nodeWidget);
-               }
-               dojo.stopEvent(e);
-       },
-       _onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
-               // summary:
-               //              Translates double-click events into commands for the controller to process
-
-               var domElement = e.target,
-                       isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
-
-               if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){
-                       // expando node was clicked, or label of a folder node was clicked; open it
-                       if(nodeWidget.isExpandable){
-                               this._onExpandoClick({node:nodeWidget});
-                       }
-               }else{
-                       this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
-                       this.onDblClick(nodeWidget.item, nodeWidget, e);
-                       this.focusNode(nodeWidget);
-               }
-               dojo.stopEvent(e);
-       },
-
-       _onExpandoClick: function(/*Object*/ message){
-               // summary:
-               //              User clicked the +/- icon; expand or collapse my children.
-               var node = message.node;
-
-               // If we are collapsing, we might be hiding the currently focused node.
-               // Also, clicking the expando node might have erased focus from the current node.
-               // For simplicity's sake just focus on the node with the expando.
-               this.focusNode(node);
-
-               if(node.isExpanded){
-                       this._collapseNode(node);
-               }else{
-                       this._expandNode(node);
-               }
-       },
-
-       onClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
-               // summary:
-               //              Callback when a tree node is clicked
-               // tags:
-               //              callback
-       },
-       onDblClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
-               // summary:
-               //              Callback when a tree node is double-clicked
-               // tags:
-               //              callback
-       },
-       onOpen: function(/* dojo.data */ item, /*TreeNode*/ node){
-               // summary:
-               //              Callback when a node is opened
-               // tags:
-               //              callback
-       },
-       onClose: function(/* dojo.data */ item, /*TreeNode*/ node){
-               // summary:
-               //              Callback when a node is closed
-               // tags:
-               //              callback
-       },
-
-       _getNextNode: function(node){
-               // summary:
-               //              Get next visible node
-
-               if(node.isExpandable && node.isExpanded && node.hasChildren()){
-                       // if this is an expanded node, get the first child
-                       return node.getChildren()[0];           // _TreeNode
-               }else{
-                       // find a parent node with a sibling
-                       while(node && node.isTreeNode){
-                               var returnNode = node.getNextSibling();
-                               if(returnNode){
-                                       return returnNode;              // _TreeNode
-                               }
-                               node = node.getParent();
-                       }
-                       return null;
-               }
-       },
-
-       _getRootOrFirstNode: function(){
-               // summary:
-               //              Get first visible node
-               return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
-       },
-
-       _collapseNode: function(/*_TreeNode*/ node){
-               // summary:
-               //              Called when the user has requested to collapse the node
-
-               if(node._expandNodeDeferred){
-                       delete node._expandNodeDeferred;
-               }
-
-               if(node.isExpandable){
-                       if(node.state == "LOADING"){
-                               // ignore clicks while we are in the process of loading data
-                               return;
-                       }
-
-                       node.collapse();
-                       this.onClose(node.item, node);
-
-                       if(node.item){
-                               this._state(node.item,false);
-                               this._saveState();
-                       }
-               }
-       },
-
-       _expandNode: function(/*_TreeNode*/ node, /*Boolean?*/ recursive){
-               // summary:
-               //              Called when the user has requested to expand the node
-               // recursive:
-               //              Internal flag used when _expandNode() calls itself, don't set.
-               // returns:
-               //              Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
-               //              that were previously opened too
-
-               if(node._expandNodeDeferred && !recursive){
-                       // there's already an expand in progress (or completed), so just return
-                       return node._expandNodeDeferred;        // dojo.Deferred
-               }
-
-               var model = this.model,
-                       item = node.item,
-                       _this = this;
-
-               switch(node.state){
-                       case "UNCHECKED":
-                               // need to load all the children, and then expand
-                               node.markProcessing();
-
-                               // Setup deferred to signal when the load and expand are finished.
-                               // Save that deferred in this._expandDeferred as a flag that operation is in progress.
-                               var def = (node._expandNodeDeferred = new dojo.Deferred());
-
-                               // Get the children
-                               model.getChildren(
-                                       item,
-                                       function(items){
-                                               node.unmarkProcessing();
-
-                                               // Display the children and also start expanding any children that were previously expanded
-                                               // (if this.persist == true).   The returned Deferred will fire when those expansions finish.
-                                               var scid = node.setChildItems(items);
-
-                                               // Call _expandNode() again but this time it will just to do the animation (default branch).
-                                               // The returned Deferred will fire when the animation completes.
-                                               // TODO: seems like I can avoid recursion and just use a deferred to sequence the events?
-                                               var ed = _this._expandNode(node, true);
-
-                                               // After the above two tasks (setChildItems() and recursive _expandNode()) finish,
-                                               // signal that I am done.
-                                               scid.addCallback(function(){
-                                                       ed.addCallback(function(){
-                                                               def.callback();
-                                                       })
-                                               });
-                                       },
-                                       function(err){
-                                               console.error(_this, ": error loading root children: ", err);
-                                       }
-                               );
-                               break;
-
-                       default:        // "LOADED"
-                               // data is already loaded; just expand node
-                               def = (node._expandNodeDeferred = node.expand());
-
-                               this.onOpen(node.item, node);
-
-                               if(item){
-                                       this._state(item, true);
-                                       this._saveState();
-                               }
-               }
-
-               return def;     // dojo.Deferred
-       },
-
-       ////////////////// Miscellaneous functions ////////////////
-
-       focusNode: function(/* _tree.Node */ node){
-               // summary:
-               //              Focus on the specified node (which must be visible)
-               // tags:
-               //              protected
-
-               // set focus so that the label will be voiced using screen readers
-               dijit.focus(node.labelNode);
-       },
-
-       _onNodeFocus: function(/*dijit._Widget*/ node){
-               // summary:
-               //              Called when a TreeNode gets focus, either by user clicking
-               //              it, or programatically by arrow key handling code.
-               // description:
-               //              It marks that the current node is the selected one, and the previously
-               //              selected node no longer is.
-
-               if(node && node != this.lastFocused){
-                       if(this.lastFocused && !this.lastFocused._destroyed){
-                               // mark that the previously focsable node is no longer focusable
-                               this.lastFocused.setFocusable(false);
-                       }
-
-                       // mark that the new node is the currently selected one
-                       node.setFocusable(true);
-                       this.lastFocused = node;
-               }
-       },
-
-       _onNodeMouseEnter: function(/*dijit._Widget*/ node){
-               // summary:
-               //              Called when mouse is over a node (onmouseenter event),
-               //              this is monitored by the DND code
-       },
-
-       _onNodeMouseLeave: function(/*dijit._Widget*/ node){
-               // summary:
-               //              Called when mouse leaves a node (onmouseleave event),
-               //              this is monitored by the DND code
-       },
-
-       //////////////// Events from the model //////////////////////////
-
-       _onItemChange: function(/*Item*/ item){
-               // summary:
-               //              Processes notification of a change to an item's scalar values like label
-               var model = this.model,
-                       identity = model.getIdentity(item),
-                       nodes = this._itemNodesMap[identity];
-
-               if(nodes){
-                       var label = this.getLabel(item),
-                               tooltip = this.getTooltip(item);
-                       dojo.forEach(nodes, function(node){
-                               node.set({
-                                       item: item,             // theoretically could be new JS Object representing same item
-                                       label: label,
-                                       tooltip: tooltip
-                               });
-                               node._updateItemClasses(item);
-                       });
-               }
-       },
-
-       _onItemChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
-               // summary:
-               //              Processes notification of a change to an item's children
-               var model = this.model,
-                       identity = model.getIdentity(parent),
-                       parentNodes = this._itemNodesMap[identity];
-
-               if(parentNodes){
-                       dojo.forEach(parentNodes,function(parentNode){
-                               parentNode.setChildItems(newChildrenList);
-                       });
-               }
-       },
-
-       _onItemDelete: function(/*Item*/ item){
-               // summary:
-               //              Processes notification of a deletion of an item
-               var model = this.model,
-                       identity = model.getIdentity(item),
-                       nodes = this._itemNodesMap[identity];
-
-               if(nodes){
-                       dojo.forEach(nodes,function(node){
-                               // Remove node from set of selected nodes (if it's selected)
-                               this.dndController.removeTreeNode(node);
-
-                               var parent = node.getParent();
-                               if(parent){
-                                       // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
-                                       parent.removeChild(node);
-                               }
-                               node.destroyRecursive();
-                       }, this);
-                       delete this._itemNodesMap[identity];
-               }
-       },
-
-       /////////////// Miscellaneous funcs
-
-       _initState: function(){
-               // summary:
-               //              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);
-                       }
-               }
-       },
-       _state: function(item,expanded){
-               // summary:
-               //              Query or set expanded state for an item,
-               if(!this.persist){
-                       return false;
-               }
-               var id=this.model.getIdentity(item);
-               if(arguments.length === 1){
-                       return this._openedItemIds[id];
-               }
-               if(expanded){
-                       this._openedItemIds[id] = true;
-               }else{
-                       delete this._openedItemIds[id];
-               }
-       },
-       _saveState: function(){
-               // summary:
-               //              Create and save a cookie with the currently expanded nodes identifiers
-               if(!this.persist){
-                       return;
-               }
-               var ary = [];
-               for(var id in this._openedItemIds){
-                       ary.push(id);
-               }
-               dojo.cookie(this.cookieName, ary.join(","), {expires:365});
-       },
-
-       destroy: function(){
-               if(this._curSearch){
-                       clearTimeout(this._curSearch.timer);
-                       delete this._curSearch;
-               }
-               if(this.rootNode){
-                       this.rootNode.destroyRecursive();
-               }
-               if(this.dndController && !dojo.isString(this.dndController)){
-                       this.dndController.destroy();
-               }
-               this.rootNode = null;
-               this.inherited(arguments);
-       },
-
-       destroyRecursive: function(){
-               // A tree is treated as a leaf, not as a node with children (like a grid),
-               // but defining destroyRecursive for back-compat.
-               this.destroy();
-       },
-
-       resize: function(changeSize){
-               if(changeSize){
-                       dojo.marginBox(this.domNode, changeSize);
-               }
-
-               // The only JS sizing involved w/tree is the indentation, which is specified
-               // in CSS and read in through this dummy indentDetector node (tree must be
-               // visible and attached to the DOM to read this)
-               this._nodePixelIndent = dojo._getMarginSize(this.tree.indentDetector).w;
-
-               if(this.tree.rootNode){
-                       // If tree has already loaded, then reset indent for all the nodes
-                       this.tree.rootNode.set('indent', this.showRoot ? 0 : -1);
-               }
-       },
-
-       _createTreeNode: function(/*Object*/ args){
-               // summary:
-               //              creates a TreeNode
-               // description:
-               //              Developers can override this method to define their own TreeNode class;
-               //              However it will probably be removed in a future release in favor of a way
-               //              of just specifying a widget for the label, rather than one that contains
-               //              the children too.
-               return new dijit._TreeNode(args);
-       }
-});
-
-// For back-compat.  TODO: remove in 2.0
-
-}
+//>>built
+require({cache:{"url:dijit/templates/TreeNode.html":"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow\" role=\"presentation\" data-dojo-attach-event=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\n\t\t><img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span data-dojo-attach-point=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span data-dojo-attach-point=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span data-dojo-attach-point=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\" data-dojo-attach-event=\"onfocus:_onLabelFocus\"></span>\n\t\t</span\n\t></div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n","url:dijit/templates/Tree.html":"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\"\n\tdata-dojo-attach-event=\"onkeypress:_onKeyPress\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n"}});define("dijit/Tree",["dojo/_base/array","dojo/_base/connect","dojo/cookie","dojo/_base/declare","dojo/_base/Deferred","dojo/DeferredList","dojo/dom","dojo/dom-class","dojo/dom-geometry","dojo/dom-style","dojo/_base/event","dojo/fx","dojo/_base/kernel","dojo/keys","dojo/_base/lang","dojo/topic","./focus","./registry","./_base/manager","./_Widget","./_TemplatedMixin","./_Container","./_Contained","./_CssStateMixin","dojo/text!./templates/TreeNode.html","dojo/text!./templates/Tree.html","./tree/TreeStoreModel","./tree/ForestStoreModel","./tree/_dndSelector"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b,_c,_d,_e,_f,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_1a,_1b,_1c,_1d){var _1e=_4("dijit._TreeNode",[_14,_15,_16,_17,_18],{item:null,isTreeNode:true,label:"",_setLabelAttr:{node:"labelNode",type:"innerText"},isExpandable:null,isExpanded:false,state:"UNCHECKED",templateString:_19,baseClass:"dijitTreeNode",cssStateNodes:{rowNode:"dijitTreeRow",labelNode:"dijitTreeLabel"},_setTooltipAttr:{node:"rowNode",type:"attribute",attribute:"title"},buildRendering:function(){this.inherited(arguments);this._setExpando();this._updateItemClasses(this.item);if(this.isExpandable){this.labelNode.setAttribute("aria-expanded",this.isExpanded);}this.setSelected(false);},_setIndentAttr:function(_1f){var _20=(Math.max(_1f,0)*this.tree._nodePixelIndent)+"px";_a.set(this.domNode,"backgroundPosition",_20+" 0px");_a.set(this.rowNode,this.isLeftToRight()?"paddingLeft":"paddingRight",_20);_1.forEach(this.getChildren(),function(_21){_21.set("indent",_1f+1);});this._set("indent",_1f);},markProcessing:function(){this.state="LOADING";this._setExpando(true);},unmarkProcessing:function(){this._setExpando(false);},_updateItemClasses:function(_22){var _23=this.tree,_24=_23.model;if(_23._v10Compat&&_22===_24.root){_22=null;}this._applyClassAndStyle(_22,"icon","Icon");this._applyClassAndStyle(_22,"label","Label");this._applyClassAndStyle(_22,"row","Row");},_applyClassAndStyle:function(_25,_26,_27){var _28="_"+_26+"Class";var _29=_26+"Node";var _2a=this[_28];this[_28]=this.tree["get"+_27+"Class"](_25,this.isExpanded);_8.replace(this[_29],this[_28]||"",_2a||"");_a.set(this[_29],this.tree["get"+_27+"Style"](_25,this.isExpanded)||{});},_updateLayout:function(){var _2b=this.getParent();if(!_2b||!_2b.rowNode||_2b.rowNode.style.display=="none"){_8.add(this.domNode,"dijitTreeIsRoot");}else{_8.toggle(this.domNode,"dijitTreeIsLast",!this.getNextSibling());}},_setExpando:function(_2c){var _2d=["dijitTreeExpandoLoading","dijitTreeExpandoOpened","dijitTreeExpandoClosed","dijitTreeExpandoLeaf"],_2e=["*","-","+","*"],idx=_2c?0:(this.isExpandable?(this.isExpanded?1:2):3);_8.replace(this.expandoNode,_2d[idx],_2d);this.expandoNodeText.innerHTML=_2e[idx];},expand:function(){if(this._expandDeferred){return this._expandDeferred;}this._wipeOut&&this._wipeOut.stop();this.isExpanded=true;this.labelNode.setAttribute("aria-expanded","true");if(this.tree.showRoot||this!==this.tree.rootNode){this.containerNode.setAttribute("role","group");}_8.add(this.contentNode,"dijitTreeContentExpanded");this._setExpando();this._updateItemClasses(this.item);if(this==this.tree.rootNode){this.tree.domNode.setAttribute("aria-expanded","true");}var def,_2f=_c.wipeIn({node:this.containerNode,duration:_13.defaultDuration,onEnd:function(){def.callback(true);}});def=(this._expandDeferred=new _5(function(){_2f.stop();}));_2f.play();return def;},collapse:function(){if(!this.isExpanded){return;}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.domNode.setAttribute("aria-expanded","false");}_8.remove(this.contentNode,"dijitTreeContentExpanded");this._setExpando();this._updateItemClasses(this.item);if(!this._wipeOut){this._wipeOut=_c.wipeOut({node:this.containerNode,duration:_13.defaultDuration});}this._wipeOut.play();},indent:0,setChildItems:function(_30){var _31=this.tree,_32=_31.model,_33=[];_1.forEach(this.getChildren(),function(_34){_16.prototype.removeChild.call(this,_34);},this);this.state="LOADED";if(_30&&_30.length>0){this.isExpandable=true;_1.forEach(_30,function(_35){var id=_32.getIdentity(_35),_36=_31._itemNodesMap[id],_37;if(_36){for(var i=0;i<_36.length;i++){if(_36[i]&&!_36[i].getParent()){_37=_36[i];_37.set("indent",this.indent+1);break;}}}if(!_37){_37=this.tree._createTreeNode({item:_35,tree:_31,isExpandable:_32.mayHaveChildren(_35),label:_31.getLabel(_35),tooltip:_31.getTooltip(_35),dir:_31.dir,lang:_31.lang,textDir:_31.textDir,indent:this.indent+1});if(_36){_36.push(_37);}else{_31._itemNodesMap[id]=[_37];}}this.addChild(_37);if(this.tree.autoExpand||this.tree._state(_37)){_33.push(_31._expandNode(_37));}},this);_1.forEach(this.getChildren(),function(_38){_38._updateLayout();});}else{this.isExpandable=false;}if(this._setExpando){this._setExpando(false);}this._updateItemClasses(this.item);if(this==_31.rootNode){var fc=this.tree.showRoot?this:this.getChildren()[0];if(fc){fc.setFocusable(true);_31.lastFocused=fc;}else{_31.domNode.setAttribute("tabIndex","0");}}return new _6(_33);},getTreePath:function(){var _39=this;var _3a=[];while(_39&&_39!==this.tree.rootNode){_3a.unshift(_39.item);_39=_39.getParent();}_3a.unshift(this.tree.rootNode.item);return _3a;},getIdentity:function(){return this.tree.model.getIdentity(this.item);},removeChild:function(_3b){this.inherited(arguments);var _3c=this.getChildren();if(_3c.length==0){this.isExpandable=false;this.collapse();}_1.forEach(_3c,function(_3d){_3d._updateLayout();});},makeExpandable:function(){this.isExpandable=true;this._setExpando(false);},_onLabelFocus:function(){this.tree._onNodeFocus(this);},setSelected:function(_3e){this.labelNode.setAttribute("aria-selected",_3e);_8.toggle(this.rowNode,"dijitTreeRowSelected",_3e);},setFocusable:function(_3f){this.labelNode.setAttribute("tabIndex",_3f?"0":"-1");},_onClick:function(evt){this.tree._onClick(this,evt);},_onDblClick:function(evt){this.tree._onDblClick(this,evt);},_onMouseEnter:function(evt){this.tree._onNodeMouseEnter(this,evt);},_onMouseLeave:function(evt){this.tree._onNodeMouseLeave(this,evt);},_setTextDirAttr:function(_40){if(_40&&((this.textDir!=_40)||!this._created)){this._set("textDir",_40);this.applyTextDir(this.labelNode,this.labelNode.innerText||this.labelNode.textContent||"");_1.forEach(this.getChildren(),function(_41){_41.set("textDir",_40);},this);}}});var _42=_4("dijit.Tree",[_14,_15],{store:null,model:null,query:null,label:"",showRoot:true,childrenAttr:["children"],paths:[],path:[],selectedItems:null,selectedItem:null,openOnClick:false,openOnDblClick:false,templateString:_1a,persist:true,autoExpand:false,dndController:_1d,dndParams:["onDndDrop","itemCreator","onDndCancel","checkAcceptance","checkItemAcceptance","dragThreshold","betweenThreshold"],onDndDrop:null,itemCreator:null,onDndCancel:null,checkAcceptance:null,checkItemAcceptance:null,dragThreshold:5,betweenThreshold:0,_nodePixelIndent:19,_publish:function(_43,_44){_10.publish(this.id,_f.mixin({tree:this,event:_43},_44||{}));},postMixInProperties:function(){this.tree=this;if(this.autoExpand){this.persist=false;}this._itemNodesMap={};if(!this.cookieName&&this.id){this.cookieName=this.id+"SaveStateCookie";}this._loadDeferred=new _5();this.inherited(arguments);},postCreate:function(){this._initState();if(!this.model){this._store2model();}this.connect(this.model,"onChange","_onItemChange");this.connect(this.model,"onChildrenChange","_onItemChildrenChange");this.connect(this.model,"onDelete","_onItemDelete");this._load();this.inherited(arguments);if(this.dndController){if(_f.isString(this.dndController)){this.dndController=_f.getObject(this.dndController);}var _45={};for(var i=0;i<this.dndParams.length;i++){if(this[this.dndParams[i]]){_45[this.dndParams[i]]=this[this.dndParams[i]];}}this.dndController=new this.dndController(this,_45);}},_store2model:function(){this._v10Compat=true;_d.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");var _46={id:this.id+"_ForestStoreModel",store:this.store,query:this.query,childrenAttrs:this.childrenAttr};if(this.params.mayHaveChildren){_46.mayHaveChildren=_f.hitch(this,"mayHaveChildren");}if(this.params.getItemChildren){_46.getChildren=_f.hitch(this,function(_47,_48,_49){this.getItemChildren((this._v10Compat&&_47===this.model.root)?null:_47,_48,_49);});}this.model=new _1c(_46);this.showRoot=Boolean(this.label);},onLoad:function(){},_load:function(){this.model.getRoot(_f.hitch(this,function(_4a){var rn=(this.rootNode=this.tree._createTreeNode({item:_4a,tree:this,isExpandable:true,label:this.label||this.getLabel(_4a),textDir:this.textDir,indent:this.showRoot?0:-1}));if(!this.showRoot){rn.rowNode.style.display="none";this.domNode.setAttribute("role","presentation");rn.labelNode.setAttribute("role","presentation");rn.containerNode.setAttribute("role","tree");}this.domNode.appendChild(rn.domNode);var _4b=this.model.getIdentity(_4a);if(this._itemNodesMap[_4b]){this._itemNodesMap[_4b].push(rn);}else{this._itemNodesMap[_4b]=[rn];}rn._updateLayout();this._expandNode(rn).addCallback(_f.hitch(this,function(){this._loadDeferred.callback(true);this.onLoad();}));}),function(err){console.error(this,": error loading root: ",err);});},getNodesByItem:function(_4c){if(!_4c){return [];}var _4d=_f.isString(_4c)?_4c:this.model.getIdentity(_4c);return [].concat(this._itemNodesMap[_4d]);},_setSelectedItemAttr:function(_4e){this.set("selectedItems",[_4e]);},_setSelectedItemsAttr:function(_4f){var _50=this;this._loadDeferred.addCallback(_f.hitch(this,function(){var _51=_1.map(_4f,function(_52){return (!_52||_f.isString(_52))?_52:_50.model.getIdentity(_52);});var _53=[];_1.forEach(_51,function(id){_53=_53.concat(_50._itemNodesMap[id]||[]);});this.set("selectedNodes",_53);}));},_setPathAttr:function(_54){if(_54.length){return this.set("paths",[_54]);}else{return this.set("paths",[]);}},_setPathsAttr:function(_55){var _56=this;return new _6(_1.map(_55,function(_57){var d=new _5();_57=_1.map(_57,function(_58){return _f.isString(_58)?_58:_56.model.getIdentity(_58);});if(_57.length){_56._loadDeferred.addCallback(function(){_59(_57,[_56.rootNode],d);});}else{d.errback("Empty path");}return d;})).addCallback(_5a);function _59(_5b,_5c,def){var _5d=_5b.shift();var _5e=_1.filter(_5c,function(_5f){return _5f.getIdentity()==_5d;})[0];if(!!_5e){if(_5b.length){_56._expandNode(_5e).addCallback(function(){_59(_5b,_5e.getChildren(),def);});}else{def.callback(_5e);}}else{def.errback("Could not expand path at "+_5d);}};function _5a(_60){_56.set("selectedNodes",_1.map(_1.filter(_60,function(x){return x[0];}),function(x){return x[1];}));};},_setSelectedNodeAttr:function(_61){this.set("selectedNodes",[_61]);},_setSelectedNodesAttr:function(_62){this._loadDeferred.addCallback(_f.hitch(this,function(){this.dndController.setSelection(_62);}));},mayHaveChildren:function(){},getItemChildren:function(){},getLabel:function(_63){return this.model.getLabel(_63);},getIconClass:function(_64,_65){return (!_64||this.model.mayHaveChildren(_64))?(_65?"dijitFolderOpened":"dijitFolderClosed"):"dijitLeaf";},getLabelClass:function(){},getRowClass:function(){},getIconStyle:function(){},getLabelStyle:function(){},getRowStyle:function(){},getTooltip:function(){return "";},_onKeyPress:function(e){if(e.altKey){return;}var _66=_12.getEnclosingWidget(e.target);if(!_66){return;}var key=e.charOrCode;if(typeof key=="string"&&key!=" "){if(!e.altKey&&!e.ctrlKey&&!e.shiftKey&&!e.metaKey){this._onLetterKeyNav({node:_66,key:key.toLowerCase()});_b.stop(e);}}else{if(this._curSearch){clearTimeout(this._curSearch.timer);delete this._curSearch;}var map=this._keyHandlerMap;if(!map){map={};map[_e.ENTER]="_onEnterKey";map[_e.SPACE]=map[" "]="_onEnterKey";map[this.isLeftToRight()?_e.LEFT_ARROW:_e.RIGHT_ARROW]="_onLeftArrow";map[this.isLeftToRight()?_e.RIGHT_ARROW:_e.LEFT_ARROW]="_onRightArrow";map[_e.UP_ARROW]="_onUpArrow";map[_e.DOWN_ARROW]="_onDownArrow";map[_e.HOME]="_onHomeKey";map[_e.END]="_onEndKey";this._keyHandlerMap=map;}if(this._keyHandlerMap[key]){this[this._keyHandlerMap[key]]({node:_66,item:_66.item,evt:e});_b.stop(e);}}},_onEnterKey:function(_67){this._publish("execute",{item:_67.item,node:_67.node});this.dndController.userSelect(_67.node,_2.isCopyKey(_67.evt),_67.evt.shiftKey);this.onClick(_67.item,_67.node,_67.evt);},_onDownArrow:function(_68){var _69=this._getNextNode(_68.node);if(_69&&_69.isTreeNode){this.focusNode(_69);}},_onUpArrow:function(_6a){var _6b=_6a.node;var _6c=_6b.getPreviousSibling();if(_6c){_6b=_6c;while(_6b.isExpandable&&_6b.isExpanded&&_6b.hasChildren()){var _6d=_6b.getChildren();_6b=_6d[_6d.length-1];}}else{var _6e=_6b.getParent();if(!(!this.showRoot&&_6e===this.rootNode)){_6b=_6e;}}if(_6b&&_6b.isTreeNode){this.focusNode(_6b);}},_onRightArrow:function(_6f){var _70=_6f.node;if(_70.isExpandable&&!_70.isExpanded){this._expandNode(_70);}else{if(_70.hasChildren()){_70=_70.getChildren()[0];if(_70&&_70.isTreeNode){this.focusNode(_70);}}}},_onLeftArrow:function(_71){var _72=_71.node;if(_72.isExpandable&&_72.isExpanded){this._collapseNode(_72);}else{var _73=_72.getParent();if(_73&&_73.isTreeNode&&!(!this.showRoot&&_73===this.rootNode)){this.focusNode(_73);}}},_onHomeKey:function(){var _74=this._getRootOrFirstNode();if(_74){this.focusNode(_74);}},_onEndKey:function(){var _75=this.rootNode;while(_75.isExpanded){var c=_75.getChildren();_75=c[c.length-1];}if(_75&&_75.isTreeNode){this.focusNode(_75);}},multiCharSearchDuration:250,_onLetterKeyNav:function(_76){var cs=this._curSearch;if(cs){cs.pattern=cs.pattern+_76.key;clearTimeout(cs.timer);}else{cs=this._curSearch={pattern:_76.key,startNode:_76.node};}var _77=this;cs.timer=setTimeout(function(){delete _77._curSearch;},this.multiCharSearchDuration);var _78=cs.startNode;do{_78=this._getNextNode(_78);if(!_78){_78=this._getRootOrFirstNode();}}while(_78!==cs.startNode&&(_78.label.toLowerCase().substr(0,cs.pattern.length)!=cs.pattern));if(_78&&_78.isTreeNode){if(_78!==cs.startNode){this.focusNode(_78);}}},isExpandoNode:function(_79,_7a){return _7.isDescendant(_79,_7a.expandoNode);},_onClick:function(_7b,e){var _7c=e.target,_7d=this.isExpandoNode(_7c,_7b);if((this.openOnClick&&_7b.isExpandable)||_7d){if(_7b.isExpandable){this._onExpandoClick({node:_7b});}}else{this._publish("execute",{item:_7b.item,node:_7b,evt:e});this.onClick(_7b.item,_7b,e);this.focusNode(_7b);}_b.stop(e);},_onDblClick:function(_7e,e){var _7f=e.target,_80=(_7f==_7e.expandoNode||_7f==_7e.expandoNodeText);if((this.openOnDblClick&&_7e.isExpandable)||_80){if(_7e.isExpandable){this._onExpandoClick({node:_7e});}}else{this._publish("execute",{item:_7e.item,node:_7e,evt:e});this.onDblClick(_7e.item,_7e,e);this.focusNode(_7e);}_b.stop(e);},_onExpandoClick:function(_81){var _82=_81.node;this.focusNode(_82);if(_82.isExpanded){this._collapseNode(_82);}else{this._expandNode(_82);}},onClick:function(){},onDblClick:function(){},onOpen:function(){},onClose:function(){},_getNextNode:function(_83){if(_83.isExpandable&&_83.isExpanded&&_83.hasChildren()){return _83.getChildren()[0];}else{while(_83&&_83.isTreeNode){var _84=_83.getNextSibling();if(_84){return _84;}_83=_83.getParent();}return null;}},_getRootOrFirstNode:function(){return this.showRoot?this.rootNode:this.rootNode.getChildren()[0];},_collapseNode:function(_85){if(_85._expandNodeDeferred){delete _85._expandNodeDeferred;}if(_85.isExpandable){if(_85.state=="LOADING"){return;}_85.collapse();this.onClose(_85.item,_85);this._state(_85,false);}},_expandNode:function(_86,_87){if(_86._expandNodeDeferred&&!_87){return _86._expandNodeDeferred;}var _88=this.model,_89=_86.item,_8a=this;switch(_86.state){case "UNCHECKED":_86.markProcessing();var def=(_86._expandNodeDeferred=new _5());_88.getChildren(_89,function(_8b){_86.unmarkProcessing();var _8c=_86.setChildItems(_8b);var ed=_8a._expandNode(_86,true);_8c.addCallback(function(){ed.addCallback(function(){def.callback();});});},function(err){console.error(_8a,": error loading root children: ",err);});break;default:def=(_86._expandNodeDeferred=_86.expand());this.onOpen(_86.item,_86);this._state(_86,true);}return def;},focusNode:function(_8d){_11.focus(_8d.labelNode);},_onNodeFocus:function(_8e){if(_8e&&_8e!=this.lastFocused){if(this.lastFocused&&!this.lastFocused._destroyed){this.lastFocused.setFocusable(false);}_8e.setFocusable(true);this.lastFocused=_8e;}},_onNodeMouseEnter:function(){},_onNodeMouseLeave:function(){},_onItemChange:function(_8f){var _90=this.model,_91=_90.getIdentity(_8f),_92=this._itemNodesMap[_91];if(_92){var _93=this.getLabel(_8f),_94=this.getTooltip(_8f);_1.forEach(_92,function(_95){_95.set({item:_8f,label:_93,tooltip:_94});_95._updateItemClasses(_8f);});}},_onItemChildrenChange:function(_96,_97){var _98=this.model,_99=_98.getIdentity(_96),_9a=this._itemNodesMap[_99];if(_9a){_1.forEach(_9a,function(_9b){_9b.setChildItems(_97);});}},_onItemDelete:function(_9c){var _9d=this.model,_9e=_9d.getIdentity(_9c),_9f=this._itemNodesMap[_9e];if(_9f){_1.forEach(_9f,function(_a0){this.dndController.removeTreeNode(_a0);var _a1=_a0.getParent();if(_a1){_a1.removeChild(_a0);}_a0.destroyRecursive();},this);delete this._itemNodesMap[_9e];}},_initState:function(){this._openedNodes={};if(this.persist&&this.cookieName){var _a2=_3(this.cookieName);if(_a2){_1.forEach(_a2.split(","),function(_a3){this._openedNodes[_a3]=true;},this);}}},_state:function(_a4,_a5){if(!this.persist){return false;}var _a6=_1.map(_a4.getTreePath(),function(_a7){return this.model.getIdentity(_a7);},this).join("/");if(arguments.length===1){return this._openedNodes[_a6];}else{if(_a5){this._openedNodes[_a6]=true;}else{delete this._openedNodes[_a6];}var ary=[];for(var id in this._openedNodes){ary.push(id);}_3(this.cookieName,ary.join(","),{expires:365});}},destroy:function(){if(this._curSearch){clearTimeout(this._curSearch.timer);delete this._curSearch;}if(this.rootNode){this.rootNode.destroyRecursive();}if(this.dndController&&!_f.isString(this.dndController)){this.dndController.destroy();}this.rootNode=null;this.inherited(arguments);},destroyRecursive:function(){this.destroy();},resize:function(_a8){if(_a8){_9.setMarginBox(this.domNode,_a8);}this._nodePixelIndent=_9.position(this.tree.indentDetector).w;if(this.tree.rootNode){this.tree.rootNode.set("indent",this.showRoot?0:-1);}},_createTreeNode:function(_a9){return new _1e(_a9);},_setTextDirAttr:function(_aa){if(_aa&&this.textDir!=_aa){this._set("textDir",_aa);this.rootNode.set("textDir",_aa);}}});_42._TreeNode=_1e;return _42;});
\ No newline at end of file