Selector.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. define("dojo/dnd/Selector", ["../main", "./common", "./Container"], function(dojo) {
  2. // module:
  3. // dojo/dnd/Selector
  4. // summary:
  5. // TODOC
  6. /*
  7. Container item states:
  8. "" - an item is not selected
  9. "Selected" - an item is selected
  10. "Anchor" - an item is selected, and is an anchor for a "shift" selection
  11. */
  12. /*=====
  13. dojo.declare("dojo.dnd.__SelectorArgs", [dojo.dnd.__ContainerArgs], {
  14. // singular: Boolean
  15. // allows selection of only one element, if true
  16. singular: false,
  17. // autoSync: Boolean
  18. // autosynchronizes the source with its list of DnD nodes,
  19. autoSync: false
  20. });
  21. =====*/
  22. dojo.declare("dojo.dnd.Selector", dojo.dnd.Container, {
  23. // summary:
  24. // a Selector object, which knows how to select its children
  25. /*=====
  26. // selection: Set<String>
  27. // The set of id's that are currently selected, such that this.selection[id] == 1
  28. // if the node w/that id is selected. Can iterate over selected node's id's like:
  29. // | for(var id in this.selection)
  30. selection: {},
  31. =====*/
  32. constructor: function(node, params){
  33. // summary:
  34. // constructor of the Selector
  35. // node: Node||String
  36. // node or node's id to build the selector on
  37. // params: dojo.dnd.__SelectorArgs?
  38. // a dictionary of parameters
  39. if(!params){ params = {}; }
  40. this.singular = params.singular;
  41. this.autoSync = params.autoSync;
  42. // class-specific variables
  43. this.selection = {};
  44. this.anchor = null;
  45. this.simpleSelection = false;
  46. // set up events
  47. this.events.push(
  48. dojo.connect(this.node, "onmousedown", this, "onMouseDown"),
  49. dojo.connect(this.node, "onmouseup", this, "onMouseUp"));
  50. },
  51. // object attributes (for markup)
  52. singular: false, // is singular property
  53. // methods
  54. getSelectedNodes: function(){
  55. // summary:
  56. // returns a list (an array) of selected nodes
  57. var t = new dojo.NodeList();
  58. var e = dojo.dnd._empty;
  59. for(var i in this.selection){
  60. if(i in e){ continue; }
  61. t.push(dojo.byId(i));
  62. }
  63. return t; // NodeList
  64. },
  65. selectNone: function(){
  66. // summary:
  67. // unselects all items
  68. return this._removeSelection()._removeAnchor(); // self
  69. },
  70. selectAll: function(){
  71. // summary:
  72. // selects all items
  73. this.forInItems(function(data, id){
  74. this._addItemClass(dojo.byId(id), "Selected");
  75. this.selection[id] = 1;
  76. }, this);
  77. return this._removeAnchor(); // self
  78. },
  79. deleteSelectedNodes: function(){
  80. // summary:
  81. // deletes all selected items
  82. var e = dojo.dnd._empty;
  83. for(var i in this.selection){
  84. if(i in e){ continue; }
  85. var n = dojo.byId(i);
  86. this.delItem(i);
  87. dojo.destroy(n);
  88. }
  89. this.anchor = null;
  90. this.selection = {};
  91. return this; // self
  92. },
  93. forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
  94. // summary:
  95. // iterates over selected items;
  96. // see `dojo.dnd.Container.forInItems()` for details
  97. o = o || dojo.global;
  98. var s = this.selection, e = dojo.dnd._empty;
  99. for(var i in s){
  100. if(i in e){ continue; }
  101. f.call(o, this.getItem(i), i, this);
  102. }
  103. },
  104. sync: function(){
  105. // summary:
  106. // sync up the node list with the data map
  107. dojo.dnd.Selector.superclass.sync.call(this);
  108. // fix the anchor
  109. if(this.anchor){
  110. if(!this.getItem(this.anchor.id)){
  111. this.anchor = null;
  112. }
  113. }
  114. // fix the selection
  115. var t = [], e = dojo.dnd._empty;
  116. for(var i in this.selection){
  117. if(i in e){ continue; }
  118. if(!this.getItem(i)){
  119. t.push(i);
  120. }
  121. }
  122. dojo.forEach(t, function(i){
  123. delete this.selection[i];
  124. }, this);
  125. return this; // self
  126. },
  127. insertNodes: function(addSelected, data, before, anchor){
  128. // summary:
  129. // inserts new data items (see `dojo.dnd.Container.insertNodes()` method for details)
  130. // addSelected: Boolean
  131. // all new nodes will be added to selected items, if true, no selection change otherwise
  132. // data: Array
  133. // a list of data items, which should be processed by the creator function
  134. // before: Boolean
  135. // insert before the anchor, if true, and after the anchor otherwise
  136. // anchor: Node
  137. // the anchor node to be used as a point of insertion
  138. var oldCreator = this._normalizedCreator;
  139. this._normalizedCreator = function(item, hint){
  140. var t = oldCreator.call(this, item, hint);
  141. if(addSelected){
  142. if(!this.anchor){
  143. this.anchor = t.node;
  144. this._removeItemClass(t.node, "Selected");
  145. this._addItemClass(this.anchor, "Anchor");
  146. }else if(this.anchor != t.node){
  147. this._removeItemClass(t.node, "Anchor");
  148. this._addItemClass(t.node, "Selected");
  149. }
  150. this.selection[t.node.id] = 1;
  151. }else{
  152. this._removeItemClass(t.node, "Selected");
  153. this._removeItemClass(t.node, "Anchor");
  154. }
  155. return t;
  156. };
  157. dojo.dnd.Selector.superclass.insertNodes.call(this, data, before, anchor);
  158. this._normalizedCreator = oldCreator;
  159. return this; // self
  160. },
  161. destroy: function(){
  162. // summary:
  163. // prepares the object to be garbage-collected
  164. dojo.dnd.Selector.superclass.destroy.call(this);
  165. this.selection = this.anchor = null;
  166. },
  167. // mouse events
  168. onMouseDown: function(e){
  169. // summary:
  170. // event processor for onmousedown
  171. // e: Event
  172. // mouse event
  173. if(this.autoSync){ this.sync(); }
  174. if(!this.current){ return; }
  175. if(!this.singular && !dojo.isCopyKey(e) && !e.shiftKey && (this.current.id in this.selection)){
  176. this.simpleSelection = true;
  177. if(e.button === dojo.mouseButtons.LEFT){
  178. // accept the left button and stop the event
  179. // for IE we don't stop event when multiple buttons are pressed
  180. dojo.stopEvent(e);
  181. }
  182. return;
  183. }
  184. if(!this.singular && e.shiftKey){
  185. if(!dojo.isCopyKey(e)){
  186. this._removeSelection();
  187. }
  188. var c = this.getAllNodes();
  189. if(c.length){
  190. if(!this.anchor){
  191. this.anchor = c[0];
  192. this._addItemClass(this.anchor, "Anchor");
  193. }
  194. this.selection[this.anchor.id] = 1;
  195. if(this.anchor != this.current){
  196. var i = 0;
  197. for(; i < c.length; ++i){
  198. var node = c[i];
  199. if(node == this.anchor || node == this.current){ break; }
  200. }
  201. for(++i; i < c.length; ++i){
  202. var node = c[i];
  203. if(node == this.anchor || node == this.current){ break; }
  204. this._addItemClass(node, "Selected");
  205. this.selection[node.id] = 1;
  206. }
  207. this._addItemClass(this.current, "Selected");
  208. this.selection[this.current.id] = 1;
  209. }
  210. }
  211. }else{
  212. if(this.singular){
  213. if(this.anchor == this.current){
  214. if(dojo.isCopyKey(e)){
  215. this.selectNone();
  216. }
  217. }else{
  218. this.selectNone();
  219. this.anchor = this.current;
  220. this._addItemClass(this.anchor, "Anchor");
  221. this.selection[this.current.id] = 1;
  222. }
  223. }else{
  224. if(dojo.isCopyKey(e)){
  225. if(this.anchor == this.current){
  226. delete this.selection[this.anchor.id];
  227. this._removeAnchor();
  228. }else{
  229. if(this.current.id in this.selection){
  230. this._removeItemClass(this.current, "Selected");
  231. delete this.selection[this.current.id];
  232. }else{
  233. if(this.anchor){
  234. this._removeItemClass(this.anchor, "Anchor");
  235. this._addItemClass(this.anchor, "Selected");
  236. }
  237. this.anchor = this.current;
  238. this._addItemClass(this.current, "Anchor");
  239. this.selection[this.current.id] = 1;
  240. }
  241. }
  242. }else{
  243. if(!(this.current.id in this.selection)){
  244. this.selectNone();
  245. this.anchor = this.current;
  246. this._addItemClass(this.current, "Anchor");
  247. this.selection[this.current.id] = 1;
  248. }
  249. }
  250. }
  251. }
  252. dojo.stopEvent(e);
  253. },
  254. onMouseUp: function(e){
  255. // summary:
  256. // event processor for onmouseup
  257. // e: Event
  258. // mouse event
  259. if(!this.simpleSelection){ return; }
  260. this.simpleSelection = false;
  261. this.selectNone();
  262. if(this.current){
  263. this.anchor = this.current;
  264. this._addItemClass(this.anchor, "Anchor");
  265. this.selection[this.current.id] = 1;
  266. }
  267. },
  268. onMouseMove: function(e){
  269. // summary:
  270. // event processor for onmousemove
  271. // e: Event
  272. // mouse event
  273. this.simpleSelection = false;
  274. },
  275. // utilities
  276. onOverEvent: function(){
  277. // summary:
  278. // this function is called once, when mouse is over our container
  279. this.onmousemoveEvent = dojo.connect(this.node, "onmousemove", this, "onMouseMove");
  280. },
  281. onOutEvent: function(){
  282. // summary:
  283. // this function is called once, when mouse is out of our container
  284. dojo.disconnect(this.onmousemoveEvent);
  285. delete this.onmousemoveEvent;
  286. },
  287. _removeSelection: function(){
  288. // summary:
  289. // unselects all items
  290. var e = dojo.dnd._empty;
  291. for(var i in this.selection){
  292. if(i in e){ continue; }
  293. var node = dojo.byId(i);
  294. if(node){ this._removeItemClass(node, "Selected"); }
  295. }
  296. this.selection = {};
  297. return this; // self
  298. },
  299. _removeAnchor: function(){
  300. if(this.anchor){
  301. this._removeItemClass(this.anchor, "Anchor");
  302. this.anchor = null;
  303. }
  304. return this; // self
  305. }
  306. });
  307. return dojo.dnd.Selector;
  308. });