]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dojo/dnd/Manager", [ |
2 | "../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../_base/window", | |
3 | "../dom-class", "../Evented", "../has", "../keys", "../on", "../topic", "../touch", | |
4 | "./common", "./autoscroll", "./Avatar" | |
5 | ], function(array, declare, event, 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 | this.events = [ | |
77 | on(win.doc, touch.move, lang.hitch(this, "onMouseMove")), | |
78 | on(win.doc, touch.release, lang.hitch(this, "onMouseUp")), | |
79 | on(win.doc, "keydown", lang.hitch(this, "onKeyDown")), | |
80 | on(win.doc, "keyup", lang.hitch(this, "onKeyUp")), | |
81 | // cancel text selection and text dragging | |
82 | on(win.doc, "dragstart", event.stop), | |
83 | on(win.body(), "selectstart", event.stop) | |
84 | ]; | |
85 | var c = "dojoDnd" + (copy ? "Copy" : "Move"); | |
86 | domClass.add(win.body(), c); | |
87 | }, | |
88 | canDrop: function(flag){ | |
89 | // summary: | |
90 | // called to notify if the current target can accept items | |
91 | var canDropFlag = Boolean(this.target && flag); | |
92 | if(this.canDropFlag != canDropFlag){ | |
93 | this.canDropFlag = canDropFlag; | |
94 | this.avatar.update(); | |
95 | } | |
96 | }, | |
97 | stopDrag: function(){ | |
98 | // summary: | |
99 | // stop the DnD in progress | |
100 | domClass.remove(win.body(), ["dojoDndCopy", "dojoDndMove"]); | |
101 | array.forEach(this.events, function(handle){ handle.remove(); }); | |
102 | this.events = []; | |
103 | this.avatar.destroy(); | |
104 | this.avatar = null; | |
105 | this.source = this.target = null; | |
106 | this.nodes = []; | |
107 | }, | |
108 | makeAvatar: function(){ | |
109 | // summary: | |
110 | // makes the avatar; it is separate to be overwritten dynamically, if needed | |
111 | return new Avatar(this); | |
112 | }, | |
113 | updateAvatar: function(){ | |
114 | // summary: | |
115 | // updates the avatar; it is separate to be overwritten dynamically, if needed | |
116 | this.avatar.update(); | |
117 | }, | |
118 | ||
119 | // mouse event processors | |
120 | onMouseMove: function(e){ | |
121 | // summary: | |
122 | // event processor for onmousemove | |
123 | // e: Event | |
124 | // mouse event | |
125 | var a = this.avatar; | |
126 | if(a){ | |
127 | autoscroll.autoScrollNodes(e); | |
128 | //autoscroll.autoScroll(e); | |
129 | var s = a.node.style; | |
130 | s.left = (e.pageX + this.OFFSET_X) + "px"; | |
131 | s.top = (e.pageY + this.OFFSET_Y) + "px"; | |
132 | var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e))); | |
133 | if(this.copy != copy){ | |
134 | this._setCopyStatus(copy); | |
135 | } | |
136 | } | |
137 | if(has("touch")){ | |
138 | // Prevent page from scrolling so that user can drag instead. | |
139 | e.preventDefault(); | |
140 | } | |
141 | }, | |
142 | onMouseUp: function(e){ | |
143 | // summary: | |
144 | // event processor for onmouseup | |
145 | // e: Event | |
146 | // mouse event | |
147 | if(this.avatar){ | |
148 | if(this.target && this.canDropFlag){ | |
149 | var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e))); | |
150 | topic.publish("/dnd/drop/before", this.source, this.nodes, copy, this.target, e); | |
151 | topic.publish("/dnd/drop", this.source, this.nodes, copy, this.target, e); | |
152 | }else{ | |
153 | topic.publish("/dnd/cancel"); | |
154 | } | |
155 | this.stopDrag(); | |
156 | } | |
157 | }, | |
158 | ||
159 | // keyboard event processors | |
160 | onKeyDown: function(e){ | |
161 | // summary: | |
162 | // event processor for onkeydown: | |
163 | // watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag | |
164 | // e: Event | |
165 | // keyboard event | |
166 | if(this.avatar){ | |
167 | switch(e.keyCode){ | |
168 | case keys.CTRL: | |
169 | var copy = Boolean(this.source.copyState(true)); | |
170 | if(this.copy != copy){ | |
171 | this._setCopyStatus(copy); | |
172 | } | |
173 | break; | |
174 | case keys.ESCAPE: | |
175 | topic.publish("/dnd/cancel"); | |
176 | this.stopDrag(); | |
177 | break; | |
178 | } | |
179 | } | |
180 | }, | |
181 | onKeyUp: function(e){ | |
182 | // summary: | |
183 | // event processor for onkeyup, watching for CTRL for copy/move status | |
184 | // e: Event | |
185 | // keyboard event | |
186 | if(this.avatar && e.keyCode == keys.CTRL){ | |
187 | var copy = Boolean(this.source.copyState(false)); | |
188 | if(this.copy != copy){ | |
189 | this._setCopyStatus(copy); | |
190 | } | |
191 | } | |
192 | }, | |
193 | ||
194 | // utilities | |
195 | _setCopyStatus: function(copy){ | |
196 | // summary: | |
197 | // changes the copy status | |
198 | // copy: Boolean | |
199 | // the copy status | |
200 | this.copy = copy; | |
201 | this.source._markDndStatus(this.copy); | |
202 | this.updateAvatar(); | |
203 | domClass.replace(win.body(), | |
204 | "dojoDnd" + (this.copy ? "Copy" : "Move"), | |
205 | "dojoDnd" + (this.copy ? "Move" : "Copy")); | |
206 | } | |
207 | }); | |
208 | ||
209 | // dnd._manager: | |
210 | // The manager singleton variable. Can be overwritten if needed. | |
211 | dnd._manager = null; | |
212 | ||
213 | Manager.manager = dnd.manager = function(){ | |
214 | // summary: | |
215 | // Returns the current DnD manager. Creates one if it is not created yet. | |
216 | if(!dnd._manager){ | |
217 | dnd._manager = new Manager(); | |
218 | } | |
219 | return dnd._manager; // Object | |
220 | }; | |
221 | ||
222 | return Manager; | |
223 | }); |