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