1 goog.provide('lime.events.Drag'); 2 3 goog.require('goog.events.EventTarget'); 4 goog.require('lime.animation.MoveTo'); 5 6 7 /** 8 * Object representing Drag interaction. 9 * @constructor 10 * @param {lime.events.Event} event Event that started dragging. 11 * @param {boolean=} opt_snapToCenter If dragging relates to center position. 12 * @param {goog.math.Box=} opt_bounds Drag area limit. 13 * @param {lime.Node=} opt_targetObject Different target object. 14 * @implements {goog.Disposable} 15 * extends goog.events.EventTarget 16 */ 17 lime.events.Drag = function(event, opt_snapToCenter, opt_bounds, 18 opt_targetObject) { 19 20 goog.base(this); 21 22 this.target = opt_targetObject || event.targetObject; 23 24 this.dropTargets_ = []; 25 this.dropIndex_ = -1; 26 27 this.x = 0; 28 this.y = 0; 29 30 if (!opt_snapToCenter) { 31 var centerpos = this.target.localToScreen( 32 new goog.math.Coordinate(0, 0)); 33 this.x = event.screenPosition.x - centerpos.x; 34 this.y = event.screenPosition.y - centerpos.y; 35 } 36 37 event.swallow(['touchmove', 'mousemove'], 38 goog.bind(this.moveHandler_, this)); 39 event.swallow(['touchend', 'touchcancel', 'mouseup'], 40 goog.bind(this.releaseHandler_, this)); 41 42 this.setBounds(opt_bounds || null); 43 44 this.dispatchEvent(new goog.events.Event(lime.events.Drag.Event.START)); 45 46 }; 47 goog.inherits(lime.events.Drag, goog.events.EventTarget); 48 49 /** 50 * Enum for dragging related events 51 * @enum {string} 52 */ 53 lime.events.Drag.Event = { 54 START: 'start', 55 END: 'end', 56 MOVE: 'move', 57 CHANGE: 'change', 58 DROP: 'drop', 59 CANCEL: 'cancel' 60 }; 61 62 /** 63 * @inheritDoc 64 */ 65 lime.events.Drag.prototype.disposeInternal = function() { 66 goog.base(this, 'disposeInternal'); 67 this.event_ = null; 68 this.target = null; 69 this.dropTargets_ = null; 70 }; 71 72 /** 73 * Return the area limit. 74 * @return {goog.math.Box} Bounding box. 75 */ 76 lime.events.Drag.prototype.getBounds = function() { 77 return this.bounds_; 78 }; 79 80 /** 81 * Set new limitation area. 82 * @param {goog.math.Box} bounds Bounding box. 83 */ 84 lime.events.Drag.prototype.setBounds = function(bounds) { 85 this.bounds_ = bounds; 86 }; 87 88 /** 89 * Handle move events. 90 * @private 91 * @param {lime.events.Event} e Event. 92 */ 93 lime.events.Drag.prototype.moveHandler_ = function(e) { 94 var pos = e.screenPosition.clone(); 95 96 pos.x -= this.x; 97 pos.y -= this.y; 98 pos = this.target.getParent().screenToLocal(pos); 99 100 var box = this.getBounds(); 101 102 if (goog.isDefAndNotNull(box)) { 103 //todo: this can be optimized 104 105 /* 106 //thinking again testing with bounding box may be actually wrong 107 var s = obj.size_, a = obj.anchorPoint_,p = this.position_; 108 var rr = new goog.math.Rect( 109 p.x-s.width*a.x, 110 p.y-s.height*a.y , 111 size.width, size.height 112 ); 113 114 115 if(rr.left<box.left) rr.left=box.left; 116 if(rr.top<box.top) rr.top=box.top; 117 118 if(rr.left+s.width>box.right) bb.left=box.right-s.width; 119 if(rr.top+s.height>box.bottom) bb.top=box.bottom-s.height; 120 121 pos.x = rr.left+s.width*a.x; 122 pos.y = rr.top+s.height*a.y; 123 */ 124 125 //point version 126 if (pos.x < box.left) pos.x = box.left; 127 else if (pos.x > box.right) pos.x = box.right; 128 129 if (pos.y < box.top) pos.y = box.top; 130 else if (pos.y > box.bottom) pos.y = box.bottom; 131 132 133 } 134 135 136 this.target.setPosition(pos); 137 138 this.dispatchEvent(new goog.events.Event(lime.events.Drag.Event.MOVE)); 139 140 var sel = -1; 141 var currect = goog.math.Rect.createFromBox(this.target.getFrame()); 142 var results = []; 143 for (var i = 0; i < this.dropTargets_.length; i++) { 144 var loc = this.dropTargets_[i]; 145 if(goog.isFunction(loc.confirmTargetActive)){ 146 if(!loc.confirmTargetActive(this.target)){ 147 continue; 148 } 149 } 150 var dropFrame = loc.getFrame(); 151 var tl = loc.localToNode(new goog.math.Coordinate( 152 dropFrame.left, dropFrame.top), this.target); 153 var br = loc.localToNode(new goog.math.Coordinate( 154 dropFrame.right, dropFrame.bottom), this.target); 155 var droprect = goog.math.Rect.createFromBox( 156 new goog.math.Box(Math.min(tl.y, br.y), Math.max(tl.x, br.x), Math.max(tl.y,br.y), Math.min(br.x,tl.x))); 157 var intersection; 158 159 if (intersection = goog.math.Rect.intersection(currect, droprect)) { 160 results.push([intersection.width * intersection.height / 161 (droprect.width * droprect.height), i]); 162 } 163 } 164 165 if (results.length) { 166 results = results.sort(function(a, b) {return b[0] - a[0]}); 167 sel = results[0][1]; 168 } 169 170 if (sel != this.dropIndex_) { 171 if (this.dropIndex_ != -1) { 172 if (goog.isFunction( 173 this.dropTargets_[this.dropIndex_].hideDropHightLight)) { 174 this.dropTargets_[this.dropIndex_].hideDropHightLight(); 175 } 176 } 177 this.dropIndex_ = sel; 178 if (this.dropIndex_ != -1) { 179 if (goog.isFunction( 180 this.dropTargets_[this.dropIndex_].showDropHightLight)) { 181 this.dropTargets_[this.dropIndex_].showDropHightLight(); 182 } 183 } 184 185 var ev = new goog.events.Event(lime.events.Drag.Event.CHANGE); 186 ev.activeDropTarget = this.dropIndex_ != -1 ? 187 this.dropTargets_[this.dropIndex_] : null; 188 this.dispatchEvent(ev); 189 190 } 191 192 193 194 }; 195 196 /** 197 * Handle release events. 198 * @private 199 * @param {lime.events.Event} e Event. 200 */ 201 lime.events.Drag.prototype.releaseHandler_ = function(e) { 202 203 if (this.dropIndex_ != -1) { 204 var ev = new goog.events.Event(lime.events.Drag.Event.DROP); 205 ev.activeDropTarget = this.dropTargets_[this.dropIndex_]; 206 if (goog.isFunction(ev.activeDropTarget.showDropHightLight)) { 207 ev.activeDropTarget.hideDropHightLight(); 208 } 209 this.dispatchEvent(ev); 210 if (!ev.propagationStopped_) { 211 var pos = ev.activeDropTarget.getParent().localToNode( 212 ev.activeDropTarget.getPosition(), this.target.getParent()); 213 var move = new lime.animation.MoveTo(pos) 214 .setDuration(.5).enableOptimizations(); 215 this.target.runAction(move); 216 if (goog.isFunction(ev.moveEndedCallback)) { 217 goog.events.listen(move, lime.animation.Event.STOP, 218 ev.moveEndedCallback, false, this.target); 219 } 220 } 221 } 222 else { 223 this.dispatchEvent(new goog.events.Event( 224 lime.events.Drag.Event.CANCEL)); 225 } 226 227 this.dispatchEvent(new goog.events.Event(lime.events.Drag.Event.END)); 228 229 lime.scheduleManager.callAfter(this.dispose, this, 100); 230 231 }; 232 233 /** 234 * Add another node as drop target. 235 * @param {lime.Node} drop Drop target node. 236 */ 237 lime.events.Drag.prototype.addDropTarget = function(drop) { 238 this.dropTargets_.push(drop); 239 }; 240 241