Selector.js 9.0 KB

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