dojo.require("dojo.lang.declare");
dojo.provide("dojo.widget.TreeWithNode");
dojo.declare(
"dojo.widget.TreeWithNode",
null,
function(){ },
{
/*
* dynamic loading-related stuff.
* When an empty folder node appears, it is "UNCHECKED" first,
* then after Rpc call it becomes LOADING and, finally LOADED
*
* tree may be dynamically loaded also
*/
loadStates: {
UNCHECKED: "UNCHECKED",
LOADING: "LOADING",
LOADED: "LOADED"
},
state: "UNCHECKED", // after creation will change to loadStates: "loaded/loading/unchecked"
//RpcUrl: "", // user can override rpc url for specific nodes
objectId: "", // the widget represents an object
// I need this to parse children
isContainer: true,
lockLevel: 0, // lock ++ unlock --, so nested locking works fine
lock: function() {
this.lockLevel++;
},
unlock: function() {
if (!this.lockLevel) {
//dojo.debug((new Error()).stack);
dojo.raise(this.widgetType+" unlock: not locked");
}
this.lockLevel--;
},
expandLevel: 0, // expand to level automatically
loadLevel: 0, // load to level automatically
hasLock: function() {
return this.lockLevel>0;
},
isLocked: function() {
var node = this;
while (true) {
if (node.lockLevel) {
return true;
}
if (!node.parent || node.isTree) {
break;
}
node = node.parent;
}
return false;
},
flushLock: function() {
this.lockLevel = 0;
//this.unMarkLoading();
},
actionIsDisabled: function(action) {
var disabled = false;
if (dojo.lang.inArray(this.actionsDisabled, action)) {
disabled = true;
}
//dojo.debug("Check "+this+" "+disabled)
if (this.isTreeNode) {
if (!this.tree.allowAddChildToLeaf && action == this.actions.ADDCHILD && !this.isFolder) {
disabled = true;
}
}
return disabled;
},
actionIsDisabledNow: function(action) {
return this.actionIsDisabled(action) || this.isLocked();
},
/**
* childrenArray is array of Widgets or array of Objects
* widgets may be both attached and detached
*
* Use Cases
* 1) lots of widgets are packed and passed in.
* - widgets are created
* - widgets have no parent (detached or not attached yet)
*
* 2) array of widgets and data objects passed in with flag makeWidgetsFromChildren
* - some widgets are not created
* - all objects have no parent
*
* 3) expand is called with makeWidgetsFromChildren=true
* - some objects need to be turned into widgets
* - some widgets have parent (e.g markup), some widgets and objects do not
*
* Will folderize a node as side-effect.
*/
setChildren: function(childrenArray) {
//dojo.profile.start("setChildren "+this);
//dojo.debug("setChildren in "+this);
if (this.isTreeNode && !this.isFolder) {
//dojo.debug("folder parent "+parent+ " isfolder "+parent.isFolder);
this.setFolder();
} else if (this.isTreeNode) {
this.state = this.loadStates.LOADED;
}
var hadChildren = this.children.length > 0;
if (hadChildren && childrenArray){
// perf: most of time setChildren used for empty nodes, so save function call
this.destroyChildren()
}
if (childrenArray) {
this.children = childrenArray;
}
var hasChildren = this.children.length > 0;
if (this.isTreeNode && hasChildren != hadChildren) {
// call only when hasChildren state changes
this.viewSetHasChildren();
}
for(var i=0; i<this.children.length; i++) {
var child = this.children[i];
//dojo.profile.start("setChildren - create "+this);
if (!(child instanceof dojo.widget.Widget)) {
child = this.children[i] = this.tree.createNode(child);
var childWidgetCreated = true;
//dojo.debugShallow(child)
//dojo.debug("setChildren creates node "+child);
} else {
var childWidgetCreated = false;
}
//dojo.profile.end("setChildren - create "+this);
//dojo.profile.start("setChildren - attach "+this);
if (!child.parent) { // detached child
//dojo.debug("detached child "+child);
child.parent = this;
//dojo.profile.start("setChildren - updateTree "+this);
if (this.tree !== child.tree) {
child.updateTree(this.tree);
}
//dojo.profile.end("setChildren - updateTree "+this);
//dojo.debug("Add layout for "+child);
child.viewAddLayout();
this.containerNode.appendChild(child.domNode);
var message = {
child: child,
index: i,
parent: this,
childWidgetCreated: childWidgetCreated
}
delete dojo.widget.manager.topWidgets[child.widgetId];
//dojo.profile.start("setChildren - event "+this);
//dojo.debug("publish "+this.tree.eventNames.afterAddChild)
dojo.event.topic.publish(this.tree.eventNames.afterAddChild, message);
//dojo.profile.end("setChildren - event "+this);
}
if (this.tree.eagerWidgetInstantiation) {
dojo.lang.forEach(this.children, function(child) {
child.setChildren();
});
}
//dojo.profile.end("setChildren - attach "+this);
}
//dojo.profile.end("setChildren "+this);
},
doAddChild: function(child, index) {
return this.addChild(child, index, true);
},
addChild: function(child, index, dontPublishEvent) {
if (dojo.lang.isUndefined(index)) {
index = this.children.length;
}
//dojo.debug("doAddChild "+index+" called for "+this+" child "+child+" existing children "+(this.children.length ? this.children : "<no children>"));
if (!child.isTreeNode){
dojo.raise("You can only add TreeNode widgets to a "+this.widgetType+" widget!");
return;
}
this.children.splice(index, 0, child);
child.parent = this;
child.addedTo(this, index, dontPublishEvent);
// taken from DomWidget.registerChild
// delete from widget list that are notified on resize etc (no parent)
delete dojo.widget.manager.topWidgets[child.widgetId];
},
/**
* does not inform children about resize (skips onShow),
* because on large trees that's slow
*/
onShow: function() {
this.animationInProgress=false;
},
onHide: function() {
this.animationInProgress=false;
}
});