dndSource.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. if (!dojo._hasResource["dijit._tree.dndSource"]) { // _hasResource checks added
  2. // by build. Do not use
  3. // _hasResource directly in
  4. // your code.
  5. dojo._hasResource["dijit._tree.dndSource"] = true;
  6. dojo.provide("dijit._tree.dndSource");
  7. dojo.require("dijit._tree.dndSelector");
  8. dojo.require("dojo.dnd.Manager");
  9. dojo.declare("dijit._tree.dndSource", dijit._tree.dndSelector, {
  10. // summary: a Source object, which can be used as a DnD source, or a DnD
  11. // target
  12. // object attributes (for markup)
  13. isSource : true,
  14. copyOnly : false,
  15. skipForm : false,
  16. accept : ["text"],
  17. constructor : function(node, params) {
  18. // summary: a constructor of the Source
  19. // node: Node: node or node's id to build the source on
  20. // params: Object: a dict of parameters, recognized parameters are:
  21. // isSource: Boolean: can be used as a DnD source, if true; assumed
  22. // to be "true" if omitted
  23. // accept: Array: list of accepted types (text strings) for a
  24. // target; assumed to be ["text"] if omitted
  25. // horizontal: Boolean: a horizontal container, if true, vertical
  26. // otherwise or when omitted
  27. // copyOnly: Boolean: always copy items, if true, use a state of
  28. // Ctrl key otherwise
  29. // skipForm: Boolean: don't start the drag operation, if clicked on
  30. // form elements
  31. // the rest of parameters are passed to the selector
  32. if (!params) {
  33. params = {};
  34. }
  35. dojo.mixin(this, params);
  36. this.isSource = typeof params.isSource == "undefined"
  37. ? true
  38. : params.isSource;
  39. var type = params.accept instanceof Array
  40. ? params.accept
  41. : ["text"];
  42. this.accept = null;
  43. if (type.length) {
  44. this.accept = {};
  45. for (var i = 0; i < type.length; ++i) {
  46. this.accept[type[i]] = 1;
  47. }
  48. }
  49. // class-specific variables
  50. this.isDragging = false;
  51. this.mouseDown = false;
  52. this.targetAnchor = null;
  53. this.targetBox = null;
  54. this.before = true;
  55. // states
  56. this.sourceState = "";
  57. if (this.isSource) {
  58. dojo.addClass(this.node, "dojoDndSource");
  59. }
  60. this.targetState = "";
  61. if (this.accept) {
  62. dojo.addClass(this.node, "dojoDndTarget");
  63. }
  64. if (this.horizontal) {
  65. dojo.addClass(this.node, "dojoDndHorizontal");
  66. }
  67. // set up events
  68. this.topics = [
  69. dojo.subscribe("/dnd/source/over", this, "onDndSourceOver"),
  70. dojo.subscribe("/dnd/start", this, "onDndStart"),
  71. dojo.subscribe("/dnd/drop", this, "onDndDrop"),
  72. dojo.subscribe("/dnd/cancel", this, "onDndCancel")];
  73. },
  74. startup : function() {
  75. },
  76. // methods
  77. checkAcceptance : function(source, nodes) {
  78. // summary: checks, if the target can accept nodes from this source
  79. // source: Object: the source which provides items
  80. // nodes: Array: the list of transferred items
  81. return true; // Boolean
  82. },
  83. copyState : function(keyPressed) {
  84. // summary: Returns true, if we need to copy items, false to move.
  85. // It is separated to be overwritten dynamically, if needed.
  86. // keyPressed: Boolean: the "copy" was pressed
  87. return this.copyOnly || keyPressed; // Boolean
  88. },
  89. destroy : function() {
  90. // summary: prepares the object to be garbage-collected
  91. this.inherited("destroy", arguments);
  92. dojo.forEach(this.topics, dojo.unsubscribe);
  93. this.targetAnchor = null;
  94. },
  95. // markup methods
  96. markupFactory : function(params, node) {
  97. params._skipStartup = true;
  98. return new dijit._tree.dndSource(node, params);
  99. },
  100. // mouse event processors
  101. onMouseMove : function(e) {
  102. // summary: event processor for onmousemove
  103. // e: Event: mouse event
  104. if (this.isDragging && this.targetState == "Disabled") {
  105. return;
  106. }
  107. this.inherited("onMouseMove", arguments);
  108. var m = dojo.dnd.manager();
  109. if (this.isDragging) {
  110. // calculate before/after
  111. if (this.allowBetween) { // not implemented yet for tree
  112. // since it has no concept of order
  113. var before = false;
  114. if (this.current) {
  115. if (!this.targetBox
  116. || this.targetAnchor != this.current) {
  117. this.targetBox = {
  118. xy : dojo.coords(this.current, true),
  119. w : this.current.offsetWidth,
  120. h : this.current.offsetHeight
  121. };
  122. }
  123. if (this.horizontal) {
  124. before = (e.pageX - this.targetBox.xy.x) < (this.targetBox.w / 2);
  125. } else {
  126. before = (e.pageY - this.targetBox.xy.y) < (this.targetBox.h / 2);
  127. }
  128. }
  129. if (this.current != this.targetAnchor
  130. || before != this.before) {
  131. this._markTargetAnchor(before);
  132. m.canDrop(!this.current || m.source != this
  133. || !(this.current.id in this.selection));
  134. }
  135. }
  136. } else {
  137. if (this.mouseDown && this.isSource) {
  138. var n = this.getSelectedNodes();
  139. var nodes = [];
  140. for (var i in n) {
  141. nodes.push(n[i]);
  142. }
  143. if (nodes.length) {
  144. m.startDrag(this, nodes, this.copyState(dojo.dnd
  145. .getCopyKeyState(e)));
  146. }
  147. }
  148. }
  149. },
  150. onMouseDown : function(e) {
  151. // summary: event processor for onmousedown
  152. // e: Event: mouse event
  153. this.mouseDown = true;
  154. this.mouseButton = e.button;
  155. this.inherited("onMouseDown", arguments);
  156. },
  157. onMouseUp : function(e) {
  158. // summary: event processor for onmouseup
  159. // e: Event: mouse event
  160. if (this.mouseDown) {
  161. this.mouseDown = false;
  162. this.inherited("onMouseUp", arguments);
  163. }
  164. },
  165. onMouseOver : function(e) {
  166. // summary: event processor for onmouseover
  167. // e: Event: mouse event
  168. var n = e.relatedTarget;
  169. while (n) {
  170. if (n == this.node) {
  171. break;
  172. }
  173. try {
  174. n = n.parentNode;
  175. } catch (x) {
  176. n = null;
  177. }
  178. }
  179. if (!n) {
  180. this._changeState("Container", "Over");
  181. this.onOverEvent();
  182. }
  183. n = this._getChildByEvent(e);
  184. if (this.current == n) {
  185. return;
  186. }
  187. if (this.current) {
  188. this._removeItemClass(this.current, "Over");
  189. }
  190. if (n) {
  191. this._addItemClass(n, "Over");
  192. if (this.isDragging) {
  193. var m = dojo.dnd.manager();
  194. if (this.checkItemAcceptance(n, m.source)) {
  195. m
  196. .canDrop(this.targetState != "Disabled"
  197. && (!this.current || m.source != this || !(this.current.id in this.selection)));
  198. } else {
  199. m.canDrop(false);
  200. }
  201. }
  202. } else {
  203. if (this.isDragging) {
  204. var m = dojo.dnd.manager();
  205. if (m.source
  206. && this.checkAcceptance(m.source, m.source
  207. .getSelectedNodes())) {
  208. m
  209. .canDrop(this.targetState != "Disabled"
  210. && (!this.current || m.source != this || !(this.current.id in this.selection)));
  211. } else {
  212. m.canDrop(false);
  213. }
  214. }
  215. }
  216. this.current = n;
  217. },
  218. checkItemAcceptance : function(node, source) {
  219. // summary: stub funciton to be overridden if one wants to check for
  220. // the ability to drop at the node/item level
  221. return true;
  222. },
  223. // topic event processors
  224. onDndSourceOver : function(source) {
  225. // summary: topic event processor for /dnd/source/over, called when
  226. // detected a current source
  227. // source: Object: the source which has the mouse over it
  228. if (this != source) {
  229. this.mouseDown = false;
  230. if (this.targetAnchor) {
  231. this._unmarkTargetAnchor();
  232. }
  233. } else if (this.isDragging) {
  234. var m = dojo.dnd.manager();
  235. m
  236. .canDrop(this.targetState != "Disabled"
  237. && (!this.current || m.source != this || !(this.current.id in this.selection)));
  238. }
  239. },
  240. onDndStart : function(source, nodes, copy) {
  241. // summary: topic event processor for /dnd/start, called to initiate
  242. // the DnD operation
  243. // source: Object: the source which provides items
  244. // nodes: Array: the list of transferred items
  245. // copy: Boolean: copy items, if true, move items otherwise
  246. if (this.isSource) {
  247. this._changeState("Source", this == source ? (copy
  248. ? "Copied"
  249. : "Moved") : "");
  250. }
  251. var accepted = this.checkAcceptance(source, nodes);
  252. this._changeState("Target", accepted ? "" : "Disabled");
  253. if (accepted) {
  254. dojo.dnd.manager().overSource(this);
  255. }
  256. this.isDragging = true;
  257. },
  258. itemCreator : function(nodes) {
  259. var items = []
  260. for (var i = 0; i < nodes.length; i++) {
  261. items.push({
  262. "name" : nodes[i].textContent,
  263. "id" : nodes[i].id
  264. });
  265. }
  266. return items;
  267. },
  268. onDndDrop : function(source, nodes, copy) {
  269. // summary: topic event processor for /dnd/drop, called to finish
  270. // the DnD operation
  271. // source: Object: the source which provides items
  272. // nodes: Array: the list of transferred items
  273. // copy: Boolean: copy items, if true, move items otherwise
  274. if (this.containerState == "Over") {
  275. this.isDragging = false;
  276. var target = this.current;
  277. var items = this.itemCreator(nodes, target);
  278. if (!target || target == this.tree.domNode) {
  279. for (var i = 0; i < items.length; i++) {
  280. this.tree.store.newItem(items[i], null);
  281. }
  282. } else {
  283. for (var i = 0; i < items.length; i++) {
  284. pInfo = {
  285. parent : dijit.getEnclosingWidget(target).item,
  286. attribute : "children"
  287. };
  288. var newItem = this.tree.store.newItem(items[i], pInfo);
  289. console.log("newItem:", newItem);
  290. }
  291. }
  292. }
  293. this.onDndCancel();
  294. },
  295. onDndCancel : function() {
  296. // summary: topic event processor for /dnd/cancel, called to cancel
  297. // the DnD operation
  298. if (this.targetAnchor) {
  299. this._unmarkTargetAnchor();
  300. this.targetAnchor = null;
  301. }
  302. this.before = true;
  303. this.isDragging = false;
  304. this.mouseDown = false;
  305. delete this.mouseButton;
  306. this._changeState("Source", "");
  307. this._changeState("Target", "");
  308. },
  309. // utilities
  310. onOverEvent : function() {
  311. // summary: this function is called once, when mouse is over our
  312. // container
  313. this.inherited("onOverEvent", arguments);
  314. dojo.dnd.manager().overSource(this);
  315. },
  316. onOutEvent : function() {
  317. // summary: this function is called once, when mouse is out of our
  318. // container
  319. this.inherited("onOutEvent", arguments);
  320. dojo.dnd.manager().outSource(this);
  321. },
  322. _markTargetAnchor : function(before) {
  323. // summary: assigns a class to the current target anchor based on
  324. // "before" status
  325. // before: Boolean: insert before, if true, after otherwise
  326. if (this.current == this.targetAnchor && this.before == before) {
  327. return;
  328. }
  329. if (this.targetAnchor) {
  330. this._removeItemClass(this.targetAnchor, this.before
  331. ? "Before"
  332. : "After");
  333. }
  334. this.targetAnchor = this.current;
  335. this.targetBox = null;
  336. this.before = before;
  337. if (this.targetAnchor) {
  338. this._addItemClass(this.targetAnchor, this.before
  339. ? "Before"
  340. : "After");
  341. }
  342. },
  343. _unmarkTargetAnchor : function() {
  344. // summary: removes a class of the current target anchor based on
  345. // "before" status
  346. if (!this.targetAnchor) {
  347. return;
  348. }
  349. this._removeItemClass(this.targetAnchor, this.before
  350. ? "Before"
  351. : "After");
  352. this.targetAnchor = null;
  353. this.targetBox = null;
  354. this.before = true;
  355. },
  356. _markDndStatus : function(copy) {
  357. // summary: changes source's state based on "copy" status
  358. this._changeState("Source", copy ? "Copied" : "Moved");
  359. }
  360. });
  361. dojo.declare("dijit._tree.dndTarget", dijit._tree.dndSource, {
  362. // summary: a Target object, which can be used as a DnD target
  363. constructor : function(node, params) {
  364. // summary: a constructor of the Target --- see the Source
  365. // constructor for details
  366. this.isSource = false;
  367. dojo.removeClass(this.node, "dojoDndSource");
  368. },
  369. // markup methods
  370. markupFactory : function(params, node) {
  371. params._skipStartup = true;
  372. return new dijit._tree.dndTarget(node, params);
  373. }
  374. });
  375. }