/* 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", "
\"\"\n\t\t\t\"\"\n\t\t
\n\t
\n
\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