[Dojo-checkins] bill - r27124 - in dijit/trunk: . tests/tree
dojo-checkins-admin at dojotoolkit.org
dojo-checkins-admin at dojotoolkit.org
Fri Nov 25 03:39:59 EST 2011
Author: bill
Date: Fri Nov 25 00:39:59 2011
New Revision: 27124
Modified:
dijit/trunk/Tree.js
dijit/trunk/tests/tree/Tree_ObjectStoreModel.html
dijit/trunk/tests/tree/test_Tree.html
Log:
Add expandAll() and collapseAll() methods, and rewrite _expandNode() for more sane usage of Deferreds (fixing a bug my new regression tests found), fixes #14287 !strict.
Modified: dijit/trunk/Tree.js
==============================================================================
--- dijit/trunk/Tree.js (original)
+++ dijit/trunk/Tree.js Fri Nov 25 00:39:59 2011
@@ -239,7 +239,10 @@
}
// cancel in progress collapse operation
- this._wipeOut && this._wipeOut.stop();
+ if(this._collapseDeferred){
+ this._collapseDeferred.cancel();
+ delete this._collapseDeferred;
+ }
// All the state information for when a node is expanded, maybe this should be
// set when the animation completes instead
@@ -257,9 +260,10 @@
var def,
wipeIn = fxUtils.wipeIn({
- node: this.containerNode, duration: manager.defaultDuration,
+ node: this.containerNode,
+ duration: manager.defaultDuration,
onEnd: function(){
- def.callback(true);
+ def.resolve(true);
}
});
@@ -278,7 +282,10 @@
// summary:
// Collapse this node (if it's expanded)
- if(!this.isExpanded){ return; }
+ if(this._collapseDeferred){
+ // Node is already collapsed, or there's a collapse in progress, just return that Deferred
+ return this._collapseDeferred;
+ }
// cancel in progress expand operation
if(this._expandDeferred){
@@ -295,12 +302,24 @@
this._setExpando();
this._updateItemClasses(this.item);
- if(!this._wipeOut){
- this._wipeOut = fxUtils.wipeOut({
- node: this.containerNode, duration: manager.defaultDuration
+ var def,
+ wipeOut = fxUtils.wipeOut({
+ node: this.containerNode,
+ duration: manager.defaultDuration,
+ onEnd: function(){
+ def.resolve(true);
+ }
});
- }
- this._wipeOut.play();
+
+ // Deferred that fires when expand is complete
+ def = (this._collapseDeferred = new Deferred(function(){
+ // Canceller
+ wipeOut.stop();
+ }));
+
+ wipeOut.play();
+
+ return def; // dojo.Deferred
},
// indent: Integer
@@ -870,7 +889,7 @@
// Load top level children (and if persist=true all nodes
// that were previously opened)
- this._expandNode(rn).addCallback(lang.hitch(this, function(){
+ this._expandNode(rn).then(lang.hitch(this, function(){
// Then, select the nodes that were selected last time, or
// the ones specified by params.paths[].
@@ -885,7 +904,7 @@
// Do the selection and then fire onLoad()
Deferred.when(this.set("paths", paths), lang.hitch(this, function(){
- this._loadDeferred.callback(true);
+ this._loadDeferred.resolve(true);
this.onLoad();
}));
}));
@@ -918,7 +937,7 @@
// 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( lang.hitch(this, function(){
+ this._loadDeferred.then( lang.hitch(this, function(){
var identities = array.map(items, function(item){
return (!item || lang.isString(item)) ? item : tree.model.getIdentity(item);
});
@@ -964,7 +983,7 @@
// We may need to wait for some nodes to expand, so setting
// each path will involve a Deferred. We bring those deferreds
// together with a DeferredList.
- return new DeferredList(array.map(paths, function(path, idx){
+ var dl = new DeferredList(array.map(paths, function(path, idx){
var d = new Deferred();
// normalize path to use identity
@@ -975,10 +994,12 @@
if(path.length){
selectPath(path, [tree.rootNode], d);
}else{
- d.errback("Empty path");
+ d.reject("Empty path");
}
return d;
- })).addCallback(setNodes);
+ }));
+ dl.then(setNodes);
+ return dl;
function selectPath(path, nodes, def){
// Traverse path; the next path component should be among "nodes".
@@ -988,13 +1009,13 @@
})[0];
if(!!nextNode){
if(path.length){
- tree._expandNode(nextNode).addCallback(function(){ selectPath(path, nextNode.getChildren(), def); });
+ tree._expandNode(nextNode).then(function(){ selectPath(path, nextNode.getChildren(), def); });
}else{
// Successfully reached the end of this path
- def.callback(nextNode);
+ def.resolve(nextNode);
}
}else{
- def.errback("Could not expand path at " + nextPath);
+ def.reject("Could not expand path at " + nextPath);
}
}
@@ -1019,6 +1040,75 @@
},
+ expandAll: function(){
+ // summary:
+ // Expand all nodes in the tree
+ // returns:
+ // Deferred that fires when all nodes have expanded
+
+ var _this = this;
+
+ function expand(node){
+ var def = new dojo.Deferred();
+
+ // Expand the node
+ _this._expandNode(node).then(function(){
+ // When node has expanded, call expand() recursively on each non-leaf child
+ var childBranches = array.filter(node.getChildren() || [], function(node){
+ return node.isExpandable;
+ }),
+ defs = array.map(childBranches, expand);
+
+ // And when all those recursive calls finish, signal that I'm finished
+ new dojo.DeferredList(defs).then(function(){
+ def.resolve(true);
+ });
+ });
+
+ return def;
+ }
+
+ return expand(this.rootNode);
+ },
+
+ collapseAll: function(){
+ // summary:
+ // Collapse all nodes in the tree
+ // returns:
+ // Deferred that fires when all nodes have collapsed
+
+ var _this = this;
+
+ function collapse(node){
+ var def = new dojo.Deferred();
+ def.label = "collapseAllDeferred";
+
+ // Collapse children first
+ var childBranches = array.filter(node.getChildren() || [], function(node){
+ return node.isExpandable;
+ }),
+ defs = array.map(childBranches, collapse);
+
+ // And when all those recursive calls finish, collapse myself, unless I'm the invisible root node,
+ // in which case collapseAll() is finished
+ new dojo.DeferredList(defs).then(function(){
+ if(!node.isExpanded || (node == _this.rootNode && !_this.showRoot)){
+ def.resolve(true);
+ }else{
+ _this._collapseNode(node).then(function(){
+ // When node has collapsed, signal that call is finished
+ def.resolve(true);
+ });
+ }
+ });
+
+
+ return def;
+ }
+
+ return collapse(this.rootNode);
+ },
+
////////////// Data store related functions //////////////////////
// These just get passed to the model; they are here for back-compat
@@ -1454,35 +1544,40 @@
_collapseNode: function(/*_TreeNode*/ node){
// summary:
// Called when the user has requested to collapse the node
+ // returns:
+ // Deferred that fires when the node is closed
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;
- }
+ 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.isExpanded){
+ var ret = node.collapse();
+ this.onClose(node.item, node);
this._state(node, false);
+
+ return ret;
}
},
- _expandNode: function(/*_TreeNode*/ node, /*Boolean?*/ recursive){
+ _expandNode: function(/*_TreeNode*/ node){
// 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
+ // Signal that this call is complete
+ var def = new Deferred();
+
+ if(node._expandNodeDeferred){
+ // there's already an expand in progress, or completed, so just return
return node._expandNodeDeferred; // dojo.Deferred
}
@@ -1490,52 +1585,45 @@
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 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());
+ // Load data if it's not already loaded
+ if(!node._loadDeferred){
+ // need to load all the children before expanding
+ 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.
+ node._loadDeferred = new 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.
+ node.setChildItems(items).then(function(){
+ node._loadDeferred.resolve(items);
+ });
+ },
+ function(err){
+ console.error(_this, ": error loading " + node.label + " children: ", err);
+ node._loadDeferred.reject(err);
+ }
+ );
+ }
- this.onOpen(node.item, node);
+ // Expand the node after data has loaded
+ node._loadDeferred.then(lang.hitch(this, function(){
+ node.expand().then(function(){
+ def.resolve(true); // signal that this _expandNode() call is complete
+ });
- this._state(node, true);
- }
+ // seems like these should be inside of then(), but left here for back-compat about
+ // when this.isOpen flag gets set (ie, at the beginning of the animation)
+ this.onOpen(node.item, node);
+ this._state(node, true);
+ }));
return def; // dojo.Deferred
},
Modified: dijit/trunk/tests/tree/Tree_ObjectStoreModel.html
==============================================================================
--- dijit/trunk/tests/tree/Tree_ObjectStoreModel.html (original)
+++ dijit/trunk/tests/tree/Tree_ObjectStoreModel.html Fri Nov 25 00:39:59 2011
@@ -34,7 +34,8 @@
"dijit/tree/dndSource",
"doh/runner",
"dijit/tests/helpers" // functions to help test
- ], function(array, aspect, domClass, on, ready, win, Memory, Observable, Tree, ObjectStoreModel, dndSource, doh){
+ ], function(array, aspect, domClass, on, ready, win, Memory, Observable,
+ Tree, ObjectStoreModel, dndSource, doh, helpers){
var myStore, myModel, myTree, myTree2;
@@ -530,6 +531,135 @@
}
]);
+ doh.register("expand/contract", [
+ function initiallyExpanded(){
+ var d = new doh.Deferred();
+
+ myTree = new dijit.Tree({
+ id: "myTreeExpand",
+ model: myModel,
+ persist: false, // persist==true is too hard to test
+ autoExpand: true
+ }).placeAt(win.body());
+ doh.t(myTree, "tree created");
+
+ myTree.startup();
+
+ world = myTree.rootNode;
+
+ myTree._loadDeferred.then(d.getTestCallback(function(){
+ doh.t(world, "root node exists");
+ doh.t(world.isExpanded, "root node is expanded");
+
+ var children = world.getChildren();
+ doh.is(6, children.length, "world children");
+ doh.t(children[0].isExpanded, "Africa expanded");
+ doh.t(children[0].getChildren()[2].isExpanded, "Kenya expanded too")
+ doh.is(2, children[0].getChildren()[2].getChildren().length, "Kenya children")
+ doh.t(children[4].isExpanded, "North America expanded");
+ doh.is(3, children[4].getChildren().length, "North America children");
+ }));
+
+ return d;
+ },
+ {
+ name: "collapseAll",
+ timeout: 5000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ myTree.collapseAll().then(d.getTestCallback(function(){
+ doh.t(world, "root node exists");
+ doh.f(world.isExpanded, "root node collapsed");
+
+ var children = world.getChildren();
+ doh.is(6, children.length, "world children");
+ doh.f(children[0].isExpanded, "Africa collapsed");
+ doh.f(children[0].getChildren()[2].iscollapsed, "Kenya collapsed too")
+ doh.is(2, children[0].getChildren()[2].getChildren().length, "Kenya children")
+ doh.f(children[4].isExpanded, "North America collapsed");
+ doh.is(3, children[4].getChildren().length, "North America children");
+ }));
+
+ return d;
+ }
+ },
+ {
+ name: "expandAll",
+ timeout: 5000,
+ runTest: function(){
+ var d = new doh.Deferred();
+
+ myTree.destroy();
+
+ myTree = new dijit.Tree({
+ id: "myTreeExpand",
+ model: myModel,
+ persist: false, // persist==true is too hard to test
+ autoExpand: false
+ }).placeAt(win.body());
+ doh.t(myTree, "tree created");
+
+ myTree.startup();
+
+ world = myTree.rootNode;
+
+ myTree._loadDeferred.then(d.getTestErrback(function(){
+ doh.t(world, "root node exists");
+ doh.t(world.isExpanded, "root node is expanded");
+
+ var children = world.getChildren();
+ doh.is(6, children.length, "world children");
+ doh.f(children[0].isExpanded, "Africa collapsed");
+
+ myTree.expandAll().then(d.getTestCallback(function(){
+ var children = world.getChildren();
+ doh.t(children[0].isExpanded, "Africa expanded");
+ doh.t(children[0].getChildren()[2].isExpanded, "Kenya expanded too")
+ doh.t(children[4].isExpanded, "North America expanded");
+ doh.is(3, children[4].getChildren().length, "North America children");
+ }));
+ }));
+
+ return d;
+ }
+ },
+ function collapseShowRootFalseTree(){
+ var d = new doh.Deferred();
+
+ myTree.destroy();
+
+ myTree = new dijit.Tree({
+ id: "myTreeExpand",
+ model: myModel,
+ persist: false, // persist==true is too hard to test
+ showRoot: false
+ }).placeAt(win.body());
+ doh.t(myTree, "tree created");
+
+ myTree.startup();
+
+ world = myTree.rootNode;
+
+ myTree._loadDeferred.then(d.getTestErrback(function(){
+ myTree._expandNode(myTree.rootNode.getChildren()[0]).then(d.getTestErrback(function(){
+ var children = world.getChildren();
+ doh.t(children[0].isExpanded, "Africa collapsed");
+
+ myTree.collapseAll().then(d.getTestCallback(function(){
+ doh.t(world, "root node exists");
+ doh.t(world.isExpanded, "root node expanded (because it's hidden");
+
+ doh.f(children[0].isExpanded, "Africa collapsed");
+ doh.t(helpers.isVisible(children[0]), "Africa node visible");
+ }));
+ }));
+ }));
+
+ return d;
+ }
+ ]);
+
doh.run();
}); // end of ready()
}); // end of require()
Modified: dijit/trunk/tests/tree/test_Tree.html
==============================================================================
--- dijit/trunk/tests/tree/test_Tree.html (original)
+++ dijit/trunk/tests/tree/test_Tree.html Fri Nov 25 00:39:59 2011
@@ -128,7 +128,9 @@
console.log("Execute of node " + this.model.getLabel(item)
+", population=" + continentStore.getValue(item, "population"));
</script>
-</div>
+ </div>
+ <button onclick="dijit.byId('tree2').expandAll();">expand all</button>
+ <button onclick="dijit.byId('tree2').collapseAll();">collapse all</button>
<h2>Double click, expand on load, direct style setting, tooltip test</h2>
<p>
More information about the Dojo-checkins
mailing list