[483] | 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 | }); |
---|