dojo.provide("dojo.widget.TreeSelectorV3");
dojo.require("dojo.widget.HtmlWidget");
dojo.require("dojo.widget.TreeCommon");
dojo.widget.defineWidget(
"dojo.widget.TreeSelectorV3",
[dojo.widget.HtmlWidget, dojo.widget.TreeCommon],
function() {
this.eventNames = {};
this.listenedTrees = {};
this.selectedNodes = [];
this.lastClicked = {}
},
{
// TODO: add multiselect
listenTreeEvents: ["afterTreeCreate","afterCollapse","afterChangeTree", "afterDetach", "beforeTreeDestroy"],
listenNodeFilter: function(elem) { return elem instanceof dojo.widget.Widget},
allowedMulti: true,
/**
* if time between clicks < dblselectTimeout => its dblselect
*/
dblselectTimeout: 300,
eventNamesDefault: {
select : "select",
deselect : "deselect",
dblselect: "dblselect" // select already selected node.. Edit or whatever
},
onAfterTreeCreate: function(message) {
var tree = message.source;
dojo.event.browser.addListener(tree.domNode, "onclick", dojo.lang.hitch(this, this.onTreeClick));
if (dojo.render.html.ie) {
dojo.event.browser.addListener(tree.domNode, "ondblclick", dojo.lang.hitch(this, this.onTreeDblClick));
}
dojo.event.browser.addListener(tree.domNode, "onKey", dojo.lang.hitch(this, this.onKey));
},
onKey: function(e) {
if (!e.key || e.ctrkKey || e.altKey) { return; }
switch(e.key) {
case e.KEY_ENTER:
var node = this.domElement2TreeNode(e.target);
if (node) {
this.processNode(node, e);
}
}
},
onAfterChangeTree: function(message) {
if (!message.oldTree && message.node.selected) {
this.select(message.node);
}
if (!message.newTree || !this.listenedTrees[message.newTree.widgetId]) {
// moving from our trfee to new one that we don't listen
if (this.selectedNode && message.node.children) {
this.deselectIfAncestorMatch(message.node);
}
}
},
initialize: function(args) {
for(name in this.eventNamesDefault) {
if (dojo.lang.isUndefined(this.eventNames[name])) {
this.eventNames[name] = this.widgetId+"/"+this.eventNamesDefault[name];
}
}
},
onBeforeTreeDestroy: function(message) {
this.unlistenTree(message.source);
},
// deselect node if ancestor is collapsed
onAfterCollapse: function(message) {
this.deselectIfAncestorMatch(message.source);
},
// IE will throw select -> dblselect. Need to transform to select->select
onTreeDblClick: function(event) {
this.onTreeClick(event);
},
checkSpecialEvent: function(event) {
return event.shiftKey || event.ctrlKey;
},
onTreeClick: function(event) {
var node = this.domElement2TreeNode(event.target);
if (!node) {
return;
}
var checkLabelClick = function(domElement) {
return domElement === node.labelNode;
}
if (this.checkPathCondition(event.target, checkLabelClick)) {
this.processNode(node, event);
}
},
/**
* press on selected with ctrl => deselect it
* press on selected w/o ctrl => dblselect it and deselect all other
*
* press on unselected with ctrl => add it to selection
*
* event may be both mouse & keyboard enter
*/
processNode: function(node, event) {
if (node.actionIsDisabled(node.actions.SELECT)) {
return;
}
//dojo.debug("click "+node+ "special "+this.checkSpecialEvent(event));
if (dojo.lang.inArray(this.selectedNodes, node)) {
if(this.checkSpecialEvent(event)){
// If the node is currently selected, and they select it again while holding
// down a meta key, it deselects it
this.deselect(node);
return;
}
var _this = this;
var i=0;
var selectedNode;
/* remove all nodes from selection excepts this one */
while(this.selectedNodes.length > i) {
selectedNode = this.selectedNodes[i];
if (selectedNode !== node) {
//dojo.debug("Deselect "+selectedNode);
this.deselect(selectedNode);
continue;
}
i++; // skip the doubleclicked node
}
/* lastClicked.node may be undefined if node was selected(before) programmatically */
var wasJustClicked = this.checkRecentClick(node)
eventName = wasJustClicked ? this.eventNames.dblselect : this.eventNames.select;
if (wasJustClicked) {
eventName = this.eventNames.dblselect;
/* after dblselect, next select is usual select */
this.forgetLastClicked();
} else {
eventName = this.eventNames.select;
this.setLastClicked(node)
}
dojo.event.topic.publish(eventName, { node: node });
return;
}
/* if unselected node.. */
this.deselectIfNoMulti(event);
//dojo.debug("select");
this.setLastClicked(node);
this.select(node);
},
forgetLastClicked: function() {
this.lastClicked = {}
},
setLastClicked: function(node) {
this.lastClicked.date = new Date();
this.lastClicked.node = node;
},
checkRecentClick: function(node) {
var diff = new Date() - this.lastClicked.date;
//dojo.debug(new Date())
//dojo.debug("check old "+this.lastClicked.node+" now "+(new Date())+" diff "+diff)
if (this.lastClicked.node && diff < this.dblselectTimeout) {
return true;
} else {
return false;
}
},
// deselect all if no meta key or disallowed
deselectIfNoMulti: function(event) {
if (!this.checkSpecialEvent(event) || !this.allowedMulti) {
//dojo.debug("deselect All");
this.deselectAll();
}
},
deselectIfAncestorMatch: function(ancestor) {
/* deselect all nodes with this ancestor */
var _this = this;
dojo.lang.forEach(this.selectedNodes, function(node) {
var selectedNode = node;
node = node.parent
while (node && node.isTreeNode) {
//dojo.debug("ancestor try "+node);
if (node === ancestor) {
_this.deselect(selectedNode);
return;
}
node = node.parent;
}
});
},
onAfterDetach: function(message) {
this.deselectIfAncestorMatch(message.child);
},
select: function(node) {
var index = dojo.lang.find(this.selectedNodes, node, true);
if (index >=0 ) {
return; // already selected
}
//dojo.debug("select "+node);
this.selectedNodes.push(node);
dojo.event.topic.publish(this.eventNames.select, {node: node} );
},
deselect: function(node){
var index = dojo.lang.find(this.selectedNodes, node, true);
if (index < 0) {
//dojo.debug("not selected");
return; // not selected
}
//dojo.debug("deselect "+node);
//dojo.debug((new Error()).stack);
this.selectedNodes.splice(index, 1);
dojo.event.topic.publish(this.eventNames.deselect, {node: node} );
//dojo.debug("deselect");
},
deselectAll: function() {
//dojo.debug("deselect all "+this.selectedNodes);
while (this.selectedNodes.length) {
this.deselect(this.selectedNodes[0]);
}
}
});