]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dojo/dnd/Mover", [ |
2 | "../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../sniff", "../_base/window", | |
3 | "../dom", "../dom-geometry", "../dom-style", "../Evented", "../on", "../touch", "./common", "./autoscroll" | |
4 | ], function(array, declare, event, lang, has, win, dom, domGeom, domStyle, Evented, on, touch, dnd, autoscroll){ | |
5 | ||
6 | // module: | |
7 | // dojo/dnd/Mover | |
8 | ||
9 | return declare("dojo.dnd.Mover", [Evented], { | |
10 | // summary: | |
11 | // an object which makes a node follow the mouse, or touch-drag on touch devices. | |
12 | // Used as a default mover, and as a base class for custom movers. | |
13 | ||
14 | constructor: function(node, e, host){ | |
15 | // node: Node | |
16 | // a node (or node's id) to be moved | |
17 | // e: Event | |
18 | // a mouse event, which started the move; | |
19 | // only pageX and pageY properties are used | |
20 | // host: Object? | |
21 | // object which implements the functionality of the move, | |
22 | // and defines proper events (onMoveStart and onMoveStop) | |
23 | this.node = dom.byId(node); | |
24 | this.marginBox = {l: e.pageX, t: e.pageY}; | |
25 | this.mouseButton = e.button; | |
26 | var h = (this.host = host), d = node.ownerDocument; | |
27 | this.events = [ | |
28 | // At the start of a drag, onFirstMove is called, and then the following | |
29 | // listener is disconnected. | |
30 | on(d, touch.move, lang.hitch(this, "onFirstMove")), | |
31 | ||
32 | // These are called continually during the drag | |
33 | on(d, touch.move, lang.hitch(this, "onMouseMove")), | |
34 | ||
35 | // And these are called at the end of the drag | |
36 | on(d, touch.release, lang.hitch(this, "onMouseUp")), | |
37 | ||
38 | // cancel text selection and text dragging | |
39 | on(d, "dragstart", event.stop), | |
40 | on(d.body, "selectstart", event.stop) | |
41 | ]; | |
42 | ||
43 | // Tell autoscroll that a drag is starting | |
44 | autoscroll.autoScrollStart(d); | |
45 | ||
46 | // notify that the move has started | |
47 | if(h && h.onMoveStart){ | |
48 | h.onMoveStart(this); | |
49 | } | |
50 | }, | |
51 | // mouse event processors | |
52 | onMouseMove: function(e){ | |
53 | // summary: | |
54 | // event processor for onmousemove/ontouchmove | |
55 | // e: Event | |
56 | // mouse/touch event | |
57 | autoscroll.autoScroll(e); | |
58 | var m = this.marginBox; | |
59 | this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY}, e); | |
60 | event.stop(e); | |
61 | }, | |
62 | onMouseUp: function(e){ | |
63 | if(has("webkit") && has("mac") && this.mouseButton == 2 ? | |
64 | e.button == 0 : this.mouseButton == e.button){ // TODO Should condition be met for touch devices, too? | |
65 | this.destroy(); | |
66 | } | |
67 | event.stop(e); | |
68 | }, | |
69 | // utilities | |
70 | onFirstMove: function(e){ | |
71 | // summary: | |
72 | // makes the node absolute; it is meant to be called only once. | |
73 | // relative and absolutely positioned nodes are assumed to use pixel units | |
74 | var s = this.node.style, l, t, h = this.host; | |
75 | switch(s.position){ | |
76 | case "relative": | |
77 | case "absolute": | |
78 | // assume that left and top values are in pixels already | |
79 | l = Math.round(parseFloat(s.left)) || 0; | |
80 | t = Math.round(parseFloat(s.top)) || 0; | |
81 | break; | |
82 | default: | |
83 | s.position = "absolute"; // enforcing the absolute mode | |
84 | var m = domGeom.getMarginBox(this.node); | |
85 | // event.pageX/pageY (which we used to generate the initial | |
86 | // margin box) includes padding and margin set on the body. | |
87 | // However, setting the node's position to absolute and then | |
88 | // doing domGeom.marginBox on it *doesn't* take that additional | |
89 | // space into account - so we need to subtract the combined | |
90 | // padding and margin. We use getComputedStyle and | |
91 | // _getMarginBox/_getContentBox to avoid the extra lookup of | |
92 | // the computed style. | |
93 | var b = win.doc.body; | |
94 | var bs = domStyle.getComputedStyle(b); | |
95 | var bm = domGeom.getMarginBox(b, bs); | |
96 | var bc = domGeom.getContentBox(b, bs); | |
97 | l = m.l - (bc.l - bm.l); | |
98 | t = m.t - (bc.t - bm.t); | |
99 | break; | |
100 | } | |
101 | this.marginBox.l = l - this.marginBox.l; | |
102 | this.marginBox.t = t - this.marginBox.t; | |
103 | if(h && h.onFirstMove){ | |
104 | h.onFirstMove(this, e); | |
105 | } | |
106 | ||
107 | // Disconnect touch.move that call this function | |
108 | this.events.shift().remove(); | |
109 | }, | |
110 | destroy: function(){ | |
111 | // summary: | |
112 | // stops the move, deletes all references, so the object can be garbage-collected | |
113 | array.forEach(this.events, function(handle){ handle.remove(); }); | |
114 | // undo global settings | |
115 | var h = this.host; | |
116 | if(h && h.onMoveStop){ | |
117 | h.onMoveStop(this); | |
118 | } | |
119 | // destroy objects | |
120 | this.events = this.node = this.host = null; | |
121 | } | |
122 | }); | |
123 | ||
124 | }); |