Source: events/drag.js

goog.provide('lime.events.Drag');

goog.require('goog.events.EventTarget');
goog.require('lime.animation.MoveTo');


/**
 * Object representing Drag interaction.
 * @constructor
 * @param {lime.events.Event} event Event that started dragging.
 * @param {boolean=} opt_snapToCenter If dragging relates to center position.
 * @param {goog.math.Box=} opt_bounds Drag area limit.
 * @param {lime.Node=} opt_targetObject Different target object.
 * @extends goog.events.EventTarget
 */
lime.events.Drag = function(event, opt_snapToCenter, opt_bounds,
    opt_targetObject) {

    goog.base(this);

    this.target = opt_targetObject || event.targetObject;

    /** @type {Array.<lime.Node>} */
    this.dropTargets_ = [];
    this.dropIndex_ = -1;

    this.x = 0;
    this.y = 0;

    if (!opt_snapToCenter) {
        var centerpos = this.target.localToScreen(
            new goog.math.Coordinate(0, 0));
        this.x = event.screenPosition.x - centerpos.x;
        this.y = event.screenPosition.y - centerpos.y;
    }

    event.swallow(['touchmove', 'mousemove'],
        goog.bind(this.moveHandler_, this));
    event.swallow(['touchend', 'touchcancel', 'mouseup'],
        goog.bind(this.releaseHandler_, this));

    this.setBounds(opt_bounds || null);

    this.dispatchEvent(new goog.events.Event(lime.events.Drag.Event.START));

};
goog.inherits(lime.events.Drag, goog.events.EventTarget);

/**
 * Enum for dragging related events
 * @enum {string}
 */
lime.events.Drag.Event = {
    START: 'start',
    END: 'end',
    MOVE: 'move',
    CHANGE: 'change',
    DROP: 'drop',
    CANCEL: 'cancel'
};

/**
 * @inheritDoc
 */
lime.events.Drag.prototype.disposeInternal = function() {
    goog.base(this, 'disposeInternal');
    this.event_ = null;
    this.target = null;
    this.dropTargets_ = null;
};

/**
 * Return the area limit.
 * @return {goog.math.Box} Bounding box.
 */
lime.events.Drag.prototype.getBounds = function() {
    return this.bounds_;
};

/**
 * Set new limitation area.
 * @param {goog.math.Box} bounds Bounding box.
 */
lime.events.Drag.prototype.setBounds = function(bounds) {
    this.bounds_ = bounds;
};

/**
 * Handle move events.
 * @private
 * @param {lime.events.Event} e Event.
 */
lime.events.Drag.prototype.moveHandler_ = function(e) {
    var pos = e.screenPosition.clone();

    pos.x -= this.x;
    pos.y -= this.y;
    pos = this.target.getParent().screenToLocal(pos);

    var box = this.getBounds();

    if (goog.isDefAndNotNull(box)) {
        //todo: this can be optimized

        /*
        //thinking again testing with bounding box may be actually wrong
        var s = obj.size_, a = obj.anchorPoint_,p = this.position_;
        var rr = new goog.math.Rect(
                    p.x-s.width*a.x,
                    p.y-s.height*a.y ,
                    size.width, size.height
                );


        if(rr.left<box.left) rr.left=box.left;
        if(rr.top<box.top) rr.top=box.top;

        if(rr.left+s.width>box.right) bb.left=box.right-s.width;
        if(rr.top+s.height>box.bottom) bb.top=box.bottom-s.height;

        pos.x = rr.left+s.width*a.x;
        pos.y = rr.top+s.height*a.y;
        */

        //point version
        if (pos.x < box.left) pos.x = box.left;
        else if (pos.x > box.right) pos.x = box.right;

        if (pos.y < box.top) pos.y = box.top;
        else if (pos.y > box.bottom) pos.y = box.bottom;


    }


    this.target.setPosition(pos);

    this.dispatchEvent(new goog.events.Event(lime.events.Drag.Event.MOVE));

    var sel = -1;
    var currect = goog.math.Rect.createFromBox(this.target.getFrame());
    var results = [];
    for (var i = 0; i < this.dropTargets_.length; i++) {
        var loc = this.dropTargets_[i];
        if (goog.isFunction(loc.confirmTargetActive)) {
            if (!loc.confirmTargetActive(this.target)) {
                continue;
            }
        }
        var dropFrame = loc.getFrame();
        var tl = loc.localToNode(new goog.math.Coordinate(
            dropFrame.left, dropFrame.top), this.target);
        var br = loc.localToNode(new goog.math.Coordinate(
            dropFrame.right, dropFrame.bottom), this.target);
        var droprect = goog.math.Rect.createFromBox(
            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)));
        var intersection = goog.math.Rect.intersection(currect, droprect);

        if (intersection) {
            results.push([intersection.width * intersection.height /
                    (droprect.width * droprect.height), i]);
        }
    }

    if (results.length) {
        results = results.sort(function(a, b) {
            return b[0] - a[0];
        });
        sel = results[0][1];
    }

    if (sel != this.dropIndex_) {
        if (this.dropIndex_ != -1) {
            if (goog.isFunction(this.dropTargets_[this.dropIndex_].hideDropHighlight)) {
                this.dropTargets_[this.dropIndex_].hideDropHighlight();
            }
        }
        this.dropIndex_ = sel;
        if (this.dropIndex_ != -1) {
            if (goog.isFunction(
                this.dropTargets_[this.dropIndex_].showDropHighlight)) {
                this.dropTargets_[this.dropIndex_].showDropHighlight();
            }
        }

        var ev = new goog.events.Event(lime.events.Drag.Event.CHANGE);
        ev.activeDropTarget = this.dropIndex_ != -1 ?
            this.dropTargets_[this.dropIndex_] : null;
        this.dispatchEvent(ev);

    }



};

/**
 * Handle release events.
 * @private
 * @param {lime.events.Event} e Event.
 */
lime.events.Drag.prototype.releaseHandler_ = function(e) {

    if (this.dropIndex_ != -1) {
        var ev = new goog.events.Event(lime.events.Drag.Event.DROP);
        ev.activeDropTarget = this.dropTargets_[this.dropIndex_];
        if (goog.isFunction(ev.activeDropTarget.showDropHighlight)) {
            ev.activeDropTarget.hideDropHighlight();
        }
        this.dispatchEvent(ev);
        if (!ev.propagationStopped_) {
            var pos = ev.activeDropTarget.getParent().localToNode(
                ev.activeDropTarget.getPosition(), this.target.getParent());
            var move = new lime.animation.MoveTo(pos)
                .setDuration(.5).enableOptimizations();
            this.target.runAction(move);
            if (goog.isFunction(ev.moveEndedCallback)) {
                goog.events.listen(move, lime.animation.Event.STOP,
                    ev.moveEndedCallback, false, this.target);
            }
        }
    } else {
        this.dispatchEvent(new goog.events.Event(
            lime.events.Drag.Event.CANCEL));
    }

    this.dispatchEvent(new goog.events.Event(lime.events.Drag.Event.END));

    lime.scheduleManager.callAfter(this.dispose, this, 100);

};

/**
 * Add another node as drop target.
 * @param {lime.Node} drop Drop target node.
 */
lime.events.Drag.prototype.addDropTarget = function(drop) {
    this.dropTargets_.push(drop);
};