PlottedDnd.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. // wrapped by build app
  2. define("dojox/layout/dnd/PlottedDnd", ["dijit","dojo","dojox","dojo/require!dojo/dnd/Source,dojo/dnd/Manager,dojox/layout/dnd/Avatar"], function(dijit,dojo,dojox){
  3. dojo.provide("dojox.layout.dnd.PlottedDnd");
  4. dojo.require("dojo.dnd.Source");
  5. dojo.require("dojo.dnd.Manager");
  6. dojo.require("dojox.layout.dnd.Avatar");
  7. dojo.declare("dojox.layout.dnd.PlottedDnd", [dojo.dnd.Source], {
  8. // summary:
  9. // dnd source handling plotted zone to show the dropping area
  10. GC_OFFSET_X: dojo.dnd.manager().OFFSET_X,
  11. GC_OFFSET_Y: dojo.dnd.manager().OFFSET_Y,
  12. constructor: function(/*Node*/node, /*Object*/params){
  13. this.childBoxes = null;
  14. this.dropIndicator = new dojox.layout.dnd.DropIndicator("dndDropIndicator", "div");
  15. this.withHandles = params.withHandles;
  16. this.handleClasses = params.handleClasses;
  17. this.opacity = params.opacity;
  18. this.allowAutoScroll = params.allowAutoScroll;//MODIF MYS
  19. this.dom = params.dom;
  20. this.singular = true;
  21. this.skipForm = true;
  22. this._over = false;
  23. this.defaultHandleClass = "GcDndHandle";
  24. this.isDropped = false;
  25. this._timer = null;
  26. //Initialize the params to calculate offset
  27. this.isOffset = (params.isOffset)?true:false;
  28. this.offsetDrag = (params.offsetDrag) ? params.offsetDrag : {x:0,y:0};
  29. this.hideSource = params.hideSource ? params.hideSource : true;
  30. this._drop = this.dropIndicator.create();
  31. },
  32. _calculateCoords : function(/*Boolean*/height){
  33. // summary: Calculate each position of children
  34. dojo.forEach(this.node.childNodes, function(child){
  35. var c = dojo.coords(child, true);
  36. child.coords = {
  37. xy: c,
  38. w: child.offsetWidth / 2,
  39. h: child.offsetHeight / 2,
  40. mw: c.w
  41. };
  42. if(height){
  43. child.coords.mh = c.h;
  44. }
  45. }, this);
  46. },
  47. _legalMouseDown: function(/*Event*/e){
  48. // summary: Checks if user clicked on "approved" items.
  49. if(!this.withHandles){ return true; }
  50. for(var node = (e.target); node && node != this.node; node = node.parentNode){
  51. if(dojo.hasClass(node, this.defaultHandleClass)){
  52. return true;
  53. }
  54. }
  55. return false; // Boolean
  56. },
  57. setDndItemSelectable: function(/*Node*/node, /*Boolean*/isSelectable) {
  58. // summary: set an item as selectable
  59. for(var _node = node; _node && node != this.node; _node = _node.parentNode) {
  60. if (dojo.hasClass(_node,"dojoDndItem")) {
  61. dojo.setSelectable(_node, isSelectable);
  62. return;
  63. }
  64. }
  65. },
  66. getDraggedWidget: function(/*Node*/node) {
  67. // summary: Return one or more widget selected during the drag.
  68. var _node = node;
  69. while (_node && _node.nodeName.toLowerCase()!="body" && !dojo.hasClass(_node,"dojoDndItem")) {
  70. _node = _node.parentNode;
  71. }
  72. return (_node) ? dijit.byNode(_node) : null;
  73. },
  74. isAccepted: function(/*Node*/ node) {
  75. // summary: test if this node can be accepted
  76. var _dndType = (node) ? node.getAttribute("dndtype") : null;
  77. return (_dndType && _dndType in this.accept);
  78. },
  79. onDndStart:function(/*Object*/source, /*Array*/nodes, /*Object*/copy){
  80. // summary: Called to initiate the DnD operation.
  81. this.firstIndicator = (source == this);
  82. this._calculateCoords(true);
  83. //this.isDropped = true;
  84. var m = dojo.dnd.manager();
  85. if(nodes[0].coords){
  86. this._drop.style.height = nodes[0].coords.mh + "px";
  87. dojo.style(m.avatar.node, "width", nodes[0].coords.mw + "px");
  88. }else{
  89. this._drop.style.height = m.avatar.node.clientHeight+"px";
  90. }
  91. this.dndNodes = nodes;
  92. dojox.layout.dnd.PlottedDnd.superclass.onDndStart.call(this,source, nodes, copy);
  93. if(source == this && this.hideSource){
  94. dojo.forEach(nodes, function(n){
  95. dojo.style(n, "display","none");
  96. });
  97. }
  98. },
  99. onDndCancel:function(){
  100. // summary: Called to cancel the DnD operation.
  101. var m = dojo.dnd.manager();
  102. if(m.source == this && this.hideSource){
  103. var nodes = this.getSelectedNodes();
  104. dojo.forEach(nodes, function(n){
  105. dojo.style(n, "display","");
  106. });
  107. }
  108. dojox.layout.dnd.PlottedDnd.superclass.onDndCancel.call(this);
  109. this.deleteDashedZone();
  110. },
  111. onDndDrop: function(source,nodes,copy,target) {
  112. // summary: Called to finish the DnD operation
  113. try{
  114. if(!this.isAccepted(nodes[0])){
  115. this.onDndCancel();
  116. }else{
  117. if(source == this && this._over && this.dropObject){
  118. this.current = this.dropObject.c;
  119. }
  120. dojox.layout.dnd.PlottedDnd.superclass.onDndDrop.call(this, source, nodes, copy, target);
  121. this._calculateCoords(true);
  122. }
  123. }catch(e){
  124. console.warn(e);
  125. }
  126. },
  127. onMouseDown: function(/*Event*/e) {
  128. // summary: Event processor for onmousedown.
  129. if(this.current == null){
  130. this.selection = {};
  131. }else{
  132. if(this.current == this.anchor){
  133. this.anchor = null;
  134. }
  135. }
  136. if(this.current !== null){
  137. var c = dojo.coords(this.current, true);
  138. this.current.coords = {
  139. xy: c,
  140. w: this.current.offsetWidth / 2,
  141. h: this.current.offsetHeight / 2,
  142. mh: c.h,
  143. mw: c.w
  144. };
  145. this._drop.style.height = this.current.coords.mh + "px";
  146. if(this.isOffset){
  147. if(this.offsetDrag.x == 0 && this.offsetDrag.y == 0){
  148. var NoOffsetDrag = true;
  149. var coords = dojo.coords(this._getChildByEvent(e));
  150. this.offsetDrag.x = coords.x - e.pageX;
  151. this.offsetDrag.y = coords.y - e.clientY;
  152. }
  153. if(this.offsetDrag.y < 16 && this.current != null){
  154. this.offsetDrag.y = this.GC_OFFSET_Y;
  155. }
  156. var m = dojo.dnd.manager();
  157. m.OFFSET_X = this.offsetDrag.x;
  158. m.OFFSET_Y = this.offsetDrag.y;
  159. if (NoOffsetDrag) {
  160. this.offsetDrag.x = 0;
  161. this.offsetDrag.y = 0;
  162. }
  163. }
  164. }
  165. if(dojo.dnd.isFormElement(e)){
  166. this.setDndItemSelectable(e.target,true);
  167. }else{
  168. this.containerSource = true;
  169. var _draggedWidget = this.getDraggedWidget(e.target);
  170. if(_draggedWidget && _draggedWidget.dragRestriction){
  171. // FIXME: not clear what this was supposed to mean ... this code needs more cleanups.
  172. // dragRestriction = true;
  173. }else{
  174. dojox.layout.dnd.PlottedDnd.superclass.onMouseDown.call(this,e);
  175. }
  176. }
  177. },
  178. onMouseUp: function(/*Event*/e) {
  179. // summary: Event processor for onmouseup.
  180. dojox.layout.dnd.PlottedDnd.superclass.onMouseUp.call(this,e);
  181. this.containerSource = false;
  182. if (!dojo.isIE && this.mouseDown){
  183. this.setDndItemSelectable(e.target,true);
  184. }
  185. var m = dojo.dnd.manager();
  186. m.OFFSET_X = this.GC_OFFSET_X;
  187. m.OFFSET_Y = this.GC_OFFSET_Y;
  188. },
  189. onMouseMove: function(e) {
  190. // summary: Event processor for onmousemove
  191. var m = dojo.dnd.manager();
  192. if(this.isDragging) {
  193. var before = false;
  194. if(this.current != null || (this.current == null && !this.dropObject)){
  195. if(this.isAccepted(m.nodes[0]) || this.containerSource){
  196. before = this.setIndicatorPosition(e);
  197. }
  198. }
  199. if(this.current != this.targetAnchor || before != this.before){
  200. this._markTargetAnchor(before);
  201. m.canDrop(!this.current || m.source != this || !(this.current.id in this.selection));
  202. }
  203. if(this.allowAutoScroll){
  204. this._checkAutoScroll(e);
  205. }
  206. }else{
  207. if(this.mouseDown && this.isSource){
  208. var nodes = this.getSelectedNodes();
  209. if(nodes.length){
  210. m.startDrag(this, nodes, this.copyState(dojo.isCopyKey(e)));
  211. }
  212. }
  213. if(this.allowAutoScroll){
  214. this._stopAutoScroll();
  215. }
  216. }
  217. },
  218. _markTargetAnchor: function(/*Boolean*/before){
  219. // summary: Assigns a class to the current target anchor based on "before" status
  220. if(this.current == this.targetAnchor && this.before == before){ return; }
  221. this.targetAnchor = this.current;
  222. this.targetBox = null;
  223. this.before = before;
  224. },
  225. _unmarkTargetAnchor: function(){
  226. // summary: Removes a class of the current target anchor based on "before" status.
  227. if(!this.targetAnchor){ return; }
  228. this.targetAnchor = null;
  229. this.targetBox = null;
  230. this.before = true;
  231. },
  232. setIndicatorPosition: function(/*Event*/e) {
  233. // summary: set the position of the drop indicator
  234. var before = false;
  235. if(this.current){
  236. if (!this.current.coords || this.allowAutoScroll) {
  237. this.current.coords = {
  238. xy: dojo.coords(this.current, true),
  239. w: this.current.offsetWidth / 2,
  240. h: this.current.offsetHeight / 2
  241. };
  242. }
  243. before = this.horizontal ?
  244. (e.pageX - this.current.coords.xy.x) < this.current.coords.w :
  245. (e.pageY - this.current.coords.xy.y) < this.current.coords.h
  246. this.insertDashedZone(before);
  247. }else{
  248. if(!this.dropObject /*|| dojo.isIE*/){ this.insertDashedZone(false); }
  249. }
  250. return before;
  251. },
  252. onOverEvent:function(){
  253. this._over = true;
  254. dojox.layout.dnd.PlottedDnd.superclass.onOverEvent.call(this);
  255. if (this.isDragging) {
  256. var m = dojo.dnd.manager();
  257. if (!this.current && !this.dropObject && this.getSelectedNodes()[0] && this.isAccepted(m.nodes[0]))
  258. this.insertDashedZone(false);
  259. }
  260. },
  261. onOutEvent: function() {
  262. this._over = false;
  263. this.containerSource = false;
  264. dojox.layout.dnd.PlottedDnd.superclass.onOutEvent.call(this);
  265. if (this.dropObject) this.deleteDashedZone();
  266. },
  267. deleteDashedZone: function() {
  268. // summary: hide the dashed zone
  269. this._drop.style.display = "none";
  270. var next = this._drop.nextSibling;
  271. while (next != null) {
  272. next.coords.xy.y -= parseInt(this._drop.style.height);
  273. next = next.nextSibling;
  274. }
  275. delete this.dropObject;
  276. },
  277. insertDashedZone: function(/*Boolean*/before) {
  278. // summary: Insert the dashed zone at the right place
  279. if(this.dropObject){
  280. if( before == this.dropObject.b &&
  281. ((this.current && this.dropObject.c == this.current.id) ||
  282. (!this.current && !this.dropObject.c))
  283. ){
  284. return;
  285. }else{
  286. this.deleteDashedZone();
  287. }
  288. }
  289. this.dropObject = { n: this._drop, c: this.current ? this.current.id : null, b: before};
  290. if(this.current){
  291. dojo.place(this._drop, this.current, before ? "before" : "after");
  292. if(!this.firstIndicator){
  293. var next = this._drop.nextSibling;
  294. while(next != null){
  295. next.coords.xy.y += parseInt(this._drop.style.height);
  296. next = next.nextSibling;
  297. }
  298. }else{
  299. this.firstIndicator = false;
  300. }
  301. }else{
  302. this.node.appendChild(this._drop);
  303. }
  304. this._drop.style.display = "";
  305. },
  306. insertNodes: function(/*Boolean*/addSelected, /*Array*/data, /*Boolean*/before, /*Node*/anchor){
  307. // summary: Inserts new data items (see Dojo Container's insertNodes method for details).
  308. if(this.dropObject){
  309. dojo.style(this.dropObject.n,"display","none");
  310. dojox.layout.dnd.PlottedDnd.superclass.insertNodes.call(this,true,data,true,this.dropObject.n);
  311. this.deleteDashedZone();
  312. }else{
  313. return dojox.layout.dnd.PlottedDnd.superclass.insertNodes.call(this,addSelected,data,before,anchor);
  314. }
  315. var _widget = dijit.byId(data[0].getAttribute("widgetId"));
  316. if (_widget) {
  317. dojox.layout.dnd._setGcDndHandle(_widget, this.withHandles, this.handleClasses);
  318. if(this.hideSource)
  319. dojo.style(_widget.domNode, "display", "");
  320. }
  321. },
  322. _checkAutoScroll: function(e){
  323. if(this._timer){
  324. clearTimeout(this._timer);
  325. }
  326. this._stopAutoScroll();
  327. var node = this.dom,
  328. y = this._sumAncestorProperties(node,"offsetTop")
  329. ;
  330. //Down
  331. if( (e.pageY - node.offsetTop +30 ) > node.clientHeight ){
  332. this.autoScrollActive = true;
  333. this._autoScrollDown(node);
  334. } else if ( (node.scrollTop > 0) && (e.pageY - y) < 30){
  335. //Up
  336. this.autoScrollActive = true;
  337. this._autoScrollUp(node);
  338. }
  339. },
  340. _autoScrollUp: function(node){
  341. if( this.autoScrollActive && node.scrollTop > 0) {
  342. node.scrollTop -= 30;
  343. this._timer = setTimeout(dojo.hitch(this,"_autoScrollUp",node), 100);
  344. }
  345. },
  346. _autoScrollDown: function(node){
  347. if( this.autoScrollActive && (node.scrollTop < (node.scrollHeight-node.clientHeight))){
  348. node.scrollTop += 30;
  349. this._timer = setTimeout(dojo.hitch(this, "_autoScrollDown",node), 100);
  350. }
  351. },
  352. _stopAutoScroll: function(){
  353. this.autoScrollActive = false;
  354. },
  355. _sumAncestorProperties: function(node, prop){
  356. // summary
  357. // Returns the sum of the passed property on all ancestors of node.
  358. node = dojo.byId(node);
  359. if(!node){ return 0; }
  360. var retVal = 0;
  361. while(node){
  362. var val = node[prop];
  363. if(val){
  364. retVal += val - 0;
  365. if(node == dojo.body()){ break; }// opera and khtml #body & #html has the same values, we only need one value
  366. }
  367. node = node.parentNode;
  368. }
  369. return retVal; // integer
  370. }
  371. });
  372. dojox.layout.dnd._setGcDndHandle = function(service,withHandles,handleClasses, first) {
  373. var cls = "GcDndHandle";
  374. if(!first){
  375. dojo.query(".GcDndHandle", service.domNode).removeClass(cls);
  376. }
  377. if(!withHandles){
  378. dojo.addClass(service.domNode, cls);
  379. }else{
  380. var _hasHandle = false;
  381. for(var i = handleClasses.length - 1; i >= 0; i--){
  382. var _node = dojo.query("." + handleClasses[i], service.domNode)[0];
  383. if(_node){
  384. _hasHandle = true;
  385. if(handleClasses[i] != cls){
  386. var _gripNode = dojo.query("." + cls, service.domNode);
  387. if(_gripNode.length == 0){
  388. dojo.removeClass(service.domNode, cls);
  389. }else{
  390. _gripNode.removeClass(cls);
  391. }
  392. dojo.addClass(_node, cls);
  393. }
  394. }
  395. }
  396. if(!_hasHandle){
  397. dojo.addClass(service.domNode, cls);
  398. }
  399. }
  400. };
  401. dojo.declare("dojox.layout.dnd.DropIndicator", null, {
  402. // summary: An empty widget to show at the user the drop zone of the widget.
  403. constructor: function(/*String*/cn, /*String*/tag) {
  404. this.tag = tag || "div";
  405. this.style = cn || null;
  406. },
  407. isInserted : function(){
  408. return (this.node.parentNode && this.node.parentNode.nodeType==1);
  409. },
  410. create : function(/*Node*//*nodeRef*/){
  411. if(this.node && this.isInserted()){ return this.node; }
  412. var h = "90px",
  413. el = dojo.doc.createElement(this.tag);
  414. if(this.style){
  415. el.className = this.style;
  416. el.style.height = h;
  417. }else{
  418. // FIXME: allow this to be done mostly in CSS?
  419. dojo.style(el, {
  420. position:"relative",
  421. border:"1px dashed #F60",
  422. margin:"2px",
  423. height: h
  424. })
  425. }
  426. this.node = el;
  427. return el;
  428. },
  429. destroy : function(){
  430. if(!this.node || !this.isInserted()){ return; }
  431. this.node.parentNode.removeChild(this.node);
  432. this.node = null;
  433. }
  434. });
  435. dojo.extend(dojo.dnd.Manager, {
  436. canDrop: function(flag){
  437. var canDropFlag = this.target && flag;
  438. if(this.canDropFlag != canDropFlag){
  439. this.canDropFlag = canDropFlag;
  440. if(this.avatar){ this.avatar.update(); }
  441. }
  442. },
  443. makeAvatar: function(){
  444. //summary: Makes the avatar, it is separate to be overwritten dynamically, if needed.
  445. return (this.source.declaredClass == "dojox.layout.dnd.PlottedDnd") ?
  446. new dojox.layout.dnd.Avatar(this, this.source.opacity) :
  447. new dojo.dnd.Avatar(this)
  448. ;
  449. }
  450. });
  451. if(dojo.isIE){
  452. dojox.layout.dnd.handdleIE = [
  453. dojo.subscribe("/dnd/start", null, function(){
  454. IEonselectstart = document.body.onselectstart;
  455. document.body.onselectstart = function(){ return false; };
  456. }),
  457. dojo.subscribe("/dnd/cancel", null, function(){
  458. document.body.onselectstart = IEonselectstart;
  459. }),
  460. dojo.subscribe("/dnd/drop", null, function(){
  461. document.body.onselectstart = IEonselectstart;
  462. })
  463. ];
  464. dojo.addOnWindowUnload(function(){
  465. dojo.forEach(dojox.layout.dnd.handdleIE, dojo.unsubscribe);
  466. });
  467. }
  468. });