| /* |
| Copyright (c) 2004-2006, The Dojo Foundation |
| All Rights Reserved. |
| |
| Licensed under the Academic Free License version 2.1 or above OR the |
| modified BSD license. For more information on Dojo licensing, see: |
| |
| http://dojotoolkit.org/community/licensing.shtml |
| */ |
| |
| |
| 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]); |
| } |
| } |
| |
| }); |
| |
| |
| |