[483] | 1 | define([ |
---|
| 2 | "../_base/array", "../_base/declare", "../_base/lang", "../_base/window", |
---|
| 3 | "../dom-class", "../Evented", "../has", "../keys", "../on", "../topic", "../touch", |
---|
| 4 | "./common", "./autoscroll", "./Avatar" |
---|
| 5 | ], function(array, declare, lang, win, domClass, Evented, has, keys, on, topic, touch, |
---|
| 6 | dnd, autoscroll, Avatar){ |
---|
| 7 | |
---|
| 8 | // module: |
---|
| 9 | // dojo/dnd/Manager |
---|
| 10 | |
---|
| 11 | var Manager = declare("dojo.dnd.Manager", [Evented], { |
---|
| 12 | // summary: |
---|
| 13 | // the manager of DnD operations (usually a singleton) |
---|
| 14 | constructor: function(){ |
---|
| 15 | this.avatar = null; |
---|
| 16 | this.source = null; |
---|
| 17 | this.nodes = []; |
---|
| 18 | this.copy = true; |
---|
| 19 | this.target = null; |
---|
| 20 | this.canDropFlag = false; |
---|
| 21 | this.events = []; |
---|
| 22 | }, |
---|
| 23 | |
---|
| 24 | // avatar's offset from the mouse |
---|
| 25 | OFFSET_X: has("touch") ? 0 : 16, |
---|
| 26 | OFFSET_Y: has("touch") ? -64 : 16, |
---|
| 27 | |
---|
| 28 | // methods |
---|
| 29 | overSource: function(source){ |
---|
| 30 | // summary: |
---|
| 31 | // called when a source detected a mouse-over condition |
---|
| 32 | // source: Object |
---|
| 33 | // the reporter |
---|
| 34 | if(this.avatar){ |
---|
| 35 | this.target = (source && source.targetState != "Disabled") ? source : null; |
---|
| 36 | this.canDropFlag = Boolean(this.target); |
---|
| 37 | this.avatar.update(); |
---|
| 38 | } |
---|
| 39 | topic.publish("/dnd/source/over", source); |
---|
| 40 | }, |
---|
| 41 | outSource: function(source){ |
---|
| 42 | // summary: |
---|
| 43 | // called when a source detected a mouse-out condition |
---|
| 44 | // source: Object |
---|
| 45 | // the reporter |
---|
| 46 | if(this.avatar){ |
---|
| 47 | if(this.target == source){ |
---|
| 48 | this.target = null; |
---|
| 49 | this.canDropFlag = false; |
---|
| 50 | this.avatar.update(); |
---|
| 51 | topic.publish("/dnd/source/over", null); |
---|
| 52 | } |
---|
| 53 | }else{ |
---|
| 54 | topic.publish("/dnd/source/over", null); |
---|
| 55 | } |
---|
| 56 | }, |
---|
| 57 | startDrag: function(source, nodes, copy){ |
---|
| 58 | // summary: |
---|
| 59 | // called to initiate the DnD operation |
---|
| 60 | // source: Object |
---|
| 61 | // the source which provides items |
---|
| 62 | // nodes: Array |
---|
| 63 | // the list of transferred items |
---|
| 64 | // copy: Boolean |
---|
| 65 | // copy items, if true, move items otherwise |
---|
| 66 | |
---|
| 67 | // Tell autoscroll that a drag is starting |
---|
| 68 | autoscroll.autoScrollStart(win.doc); |
---|
| 69 | |
---|
| 70 | this.source = source; |
---|
| 71 | this.nodes = nodes; |
---|
| 72 | this.copy = Boolean(copy); // normalizing to true boolean |
---|
| 73 | this.avatar = this.makeAvatar(); |
---|
| 74 | win.body().appendChild(this.avatar.node); |
---|
| 75 | topic.publish("/dnd/start", source, nodes, this.copy); |
---|
| 76 | |
---|
| 77 | function stopEvent(e){ |
---|
| 78 | e.preventDefault(); |
---|
| 79 | e.stopPropagation(); |
---|
| 80 | } |
---|
| 81 | |
---|
| 82 | this.events = [ |
---|
| 83 | on(win.doc, touch.move, lang.hitch(this, "onMouseMove")), |
---|
| 84 | on(win.doc, touch.release, lang.hitch(this, "onMouseUp")), |
---|
| 85 | on(win.doc, "keydown", lang.hitch(this, "onKeyDown")), |
---|
| 86 | on(win.doc, "keyup", lang.hitch(this, "onKeyUp")), |
---|
| 87 | |
---|
| 88 | // cancel text selection and text dragging |
---|
| 89 | on(win.doc, "dragstart", stopEvent), |
---|
| 90 | on(win.body(), "selectstart", stopEvent) |
---|
| 91 | ]; |
---|
| 92 | var c = "dojoDnd" + (copy ? "Copy" : "Move"); |
---|
| 93 | domClass.add(win.body(), c); |
---|
| 94 | }, |
---|
| 95 | canDrop: function(flag){ |
---|
| 96 | // summary: |
---|
| 97 | // called to notify if the current target can accept items |
---|
| 98 | var canDropFlag = Boolean(this.target && flag); |
---|
| 99 | if(this.canDropFlag != canDropFlag){ |
---|
| 100 | this.canDropFlag = canDropFlag; |
---|
| 101 | this.avatar.update(); |
---|
| 102 | } |
---|
| 103 | }, |
---|
| 104 | stopDrag: function(){ |
---|
| 105 | // summary: |
---|
| 106 | // stop the DnD in progress |
---|
| 107 | domClass.remove(win.body(), ["dojoDndCopy", "dojoDndMove"]); |
---|
| 108 | array.forEach(this.events, function(handle){ handle.remove(); }); |
---|
| 109 | this.events = []; |
---|
| 110 | this.avatar.destroy(); |
---|
| 111 | this.avatar = null; |
---|
| 112 | this.source = this.target = null; |
---|
| 113 | this.nodes = []; |
---|
| 114 | }, |
---|
| 115 | makeAvatar: function(){ |
---|
| 116 | // summary: |
---|
| 117 | // makes the avatar; it is separate to be overwritten dynamically, if needed |
---|
| 118 | return new Avatar(this); |
---|
| 119 | }, |
---|
| 120 | updateAvatar: function(){ |
---|
| 121 | // summary: |
---|
| 122 | // updates the avatar; it is separate to be overwritten dynamically, if needed |
---|
| 123 | this.avatar.update(); |
---|
| 124 | }, |
---|
| 125 | |
---|
| 126 | // mouse event processors |
---|
| 127 | onMouseMove: function(e){ |
---|
| 128 | // summary: |
---|
| 129 | // event processor for onmousemove |
---|
| 130 | // e: Event |
---|
| 131 | // mouse event |
---|
| 132 | var a = this.avatar; |
---|
| 133 | if(a){ |
---|
| 134 | autoscroll.autoScrollNodes(e); |
---|
| 135 | //autoscroll.autoScroll(e); |
---|
| 136 | var s = a.node.style; |
---|
| 137 | s.left = (e.pageX + this.OFFSET_X) + "px"; |
---|
| 138 | s.top = (e.pageY + this.OFFSET_Y) + "px"; |
---|
| 139 | var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e))); |
---|
| 140 | if(this.copy != copy){ |
---|
| 141 | this._setCopyStatus(copy); |
---|
| 142 | } |
---|
| 143 | } |
---|
| 144 | if(has("touch")){ |
---|
| 145 | // Prevent page from scrolling so that user can drag instead. |
---|
| 146 | e.preventDefault(); |
---|
| 147 | } |
---|
| 148 | }, |
---|
| 149 | onMouseUp: function(e){ |
---|
| 150 | // summary: |
---|
| 151 | // event processor for onmouseup |
---|
| 152 | // e: Event |
---|
| 153 | // mouse event |
---|
| 154 | if(this.avatar){ |
---|
| 155 | if(this.target && this.canDropFlag){ |
---|
| 156 | var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e))); |
---|
| 157 | topic.publish("/dnd/drop/before", this.source, this.nodes, copy, this.target, e); |
---|
| 158 | topic.publish("/dnd/drop", this.source, this.nodes, copy, this.target, e); |
---|
| 159 | }else{ |
---|
| 160 | topic.publish("/dnd/cancel"); |
---|
| 161 | } |
---|
| 162 | this.stopDrag(); |
---|
| 163 | } |
---|
| 164 | }, |
---|
| 165 | |
---|
| 166 | // keyboard event processors |
---|
| 167 | onKeyDown: function(e){ |
---|
| 168 | // summary: |
---|
| 169 | // event processor for onkeydown: |
---|
| 170 | // watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag |
---|
| 171 | // e: Event |
---|
| 172 | // keyboard event |
---|
| 173 | if(this.avatar){ |
---|
| 174 | switch(e.keyCode){ |
---|
| 175 | case keys.CTRL: |
---|
| 176 | var copy = Boolean(this.source.copyState(true)); |
---|
| 177 | if(this.copy != copy){ |
---|
| 178 | this._setCopyStatus(copy); |
---|
| 179 | } |
---|
| 180 | break; |
---|
| 181 | case keys.ESCAPE: |
---|
| 182 | topic.publish("/dnd/cancel"); |
---|
| 183 | this.stopDrag(); |
---|
| 184 | break; |
---|
| 185 | } |
---|
| 186 | } |
---|
| 187 | }, |
---|
| 188 | onKeyUp: function(e){ |
---|
| 189 | // summary: |
---|
| 190 | // event processor for onkeyup, watching for CTRL for copy/move status |
---|
| 191 | // e: Event |
---|
| 192 | // keyboard event |
---|
| 193 | if(this.avatar && e.keyCode == keys.CTRL){ |
---|
| 194 | var copy = Boolean(this.source.copyState(false)); |
---|
| 195 | if(this.copy != copy){ |
---|
| 196 | this._setCopyStatus(copy); |
---|
| 197 | } |
---|
| 198 | } |
---|
| 199 | }, |
---|
| 200 | |
---|
| 201 | // utilities |
---|
| 202 | _setCopyStatus: function(copy){ |
---|
| 203 | // summary: |
---|
| 204 | // changes the copy status |
---|
| 205 | // copy: Boolean |
---|
| 206 | // the copy status |
---|
| 207 | this.copy = copy; |
---|
| 208 | this.source._markDndStatus(this.copy); |
---|
| 209 | this.updateAvatar(); |
---|
| 210 | domClass.replace(win.body(), |
---|
| 211 | "dojoDnd" + (this.copy ? "Copy" : "Move"), |
---|
| 212 | "dojoDnd" + (this.copy ? "Move" : "Copy")); |
---|
| 213 | } |
---|
| 214 | }); |
---|
| 215 | |
---|
| 216 | // dnd._manager: |
---|
| 217 | // The manager singleton variable. Can be overwritten if needed. |
---|
| 218 | dnd._manager = null; |
---|
| 219 | |
---|
| 220 | Manager.manager = dnd.manager = function(){ |
---|
| 221 | // summary: |
---|
| 222 | // Returns the current DnD manager. Creates one if it is not created yet. |
---|
| 223 | if(!dnd._manager){ |
---|
| 224 | dnd._manager = new Manager(); |
---|
| 225 | } |
---|
| 226 | return dnd._manager; // Object |
---|
| 227 | }; |
---|
| 228 | |
---|
| 229 | // TODO: for 2.0, store _manager and manager in Manager only. Don't access dnd or dojo.dnd. |
---|
| 230 | |
---|
| 231 | return Manager; |
---|
| 232 | }); |
---|