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