Show:
/**
  @module ember-flexberry-designer
*/

import Ember from 'ember';

import FdEditformRow from '../objects/fd-editform-row';
import FdEditformControl from '../objects/fd-editform-control';
import FdEditformGroup from '../objects/fd-editform-group';
import FdEditformTabgroup from '../objects/fd-editform-tabgroup';

/**
  Mixin with the support `drag and drop` for controls in the edit form constructor.

  @class FdDraggableControlMixin
  @uses <a href="http://emberjs.com/api/classes/Ember.Mixin.html">Ember.Mixin</a>
*/
export default Ember.Mixin.create({
  /**
    The `X` coordinate with which the dragging started.

    @private
    @property _initialX
    @type Number
  */
  _initialX: undefined,

  /**
    The `Y` coordinate with which the dragging started.

    @private
    @property _initialY
    @type Number
  */
  _initialY: undefined,

  /**
    In dependent on `dragDirection` returns `_initialX` or `_initialY`.

    @private
    @property _initialXY
    @readOnly
    @type Number
  */
  _initialXY: Ember.computed('_initialX', '_initialY', 'dragDirection', function() {
    return this.get(`_initial${this.get('dragDirection')}`);
  }).readOnly(),

  /**
    The 'X' or 'Y' coordinate of the line through which you need to drag the item to move.

    @private
    @property _centerXY
    @readOnly
    @type Number
  */
  _centerXY: Ember.computed('dragDirection', function() {
    let clientRect = this.get('element').getBoundingClientRect();
    switch (this.get('dragDirection')) {
      case 'X':
        let width = clientRect.right - clientRect.left;
        return clientRect.left + width / 2;

      case 'Y':
        let height = clientRect.bottom - clientRect.top;
        return clientRect.top + height / 2;

      default:
        throw new Error(`The drag direction must be 'X' or 'Y'.`);
    }
  }).readOnly(),

  /**
    Returns the item to be dragged.

    @private
    @property _draggableProperty
    @readOnly
    @type FdEditformRow|FdEditformControl|FdEditformGroup|FdEditformTabgroup|FdEditformTab
  */
  _draggableProperty: Ember.computed('draggableProperty', function() {
    return this.get(this.get('draggableProperty'));
  }).readOnly(),

  /**
    Specifies the property name in the component, in which draggable item is stored.

    @property draggableProperty
    @type String
  */
  draggableProperty: undefined,

  /**
    Specifies the direction that will be used to sort the draggable items.
    Available values: `X` or `Y`.

    @property dragDirection
    @type String
  */
  dragDirection: undefined,

  /**
    The property to bind to `draggable` HTML attribute.

    @property draggable
    @type Boolean
    @default false
  */
  draggable: false,

  /**
    See [EmberJS API](https://emberjs.com/api/).

    @property attributeBindings
  */
  attributeBindings: ['draggable'],

  /**
    The event handler is `dragStart`.

    @method dragStart
    @param {JQuery.Event} event
  */
  dragStart(event) {
    event.stopPropagation();
    this.get('setDragItemAction')(this.get('_draggableProperty'));
  },

  /**
    The event handler is `dragEnd`.

    @method dragEnd
    @param {JQuery.Event} event
  */
  dragEnd(event) {
    event.stopPropagation();
    this.get('setDragItemAction')(null);
  },

  /**
    The event handler is `dragEnter`.

    @method dragEnter
    @param {JQuery.Event} event
  */
  dragEnter(event) {
    let draggedItem = this.get('getDragItemAction')();
    let draggableItem = this.get('_draggableProperty');
    if (draggedItem === draggableItem) {
      event.stopPropagation();
    } else if (this._draggingIsSupported(draggableItem, draggedItem)) {
      this.set('_initialX', event.originalEvent.clientX);
      this.set('_initialY', event.originalEvent.clientY);

      event.stopPropagation();
      return false;
    }
  },

  /**
    The event handler is `dragOver`.

    @method dragOver
    @param {JQuery.Event} event
  */
  dragOver(event) {
    if (!this.get('_initialXY')) {
      let draggedItem = this.get('getDragItemAction')();
      let draggableItem = this.get('_draggableProperty');
      if (draggedItem === draggableItem) {
        event.stopPropagation();
      } else if (this._draggingIsSupported(draggableItem, draggedItem)) {
        this.set('_initialX', event.originalEvent.clientX);
        this.set('_initialY', event.originalEvent.clientY);

        event.stopPropagation();
        return false;
      }
    } else {
      let clientXY = Ember.get(event.originalEvent, `client${this.get('dragDirection')}`);
      let centerXY = this.get('_centerXY');
      let motion = this.get('_initialXY') - clientXY;

      let direction;
      if (motion >= 5 && clientXY < centerXY) {
        direction = 'up';
      } else if (motion <= -5 && clientXY > centerXY) {
        direction = 'down';
      }

      if (direction) {
        this.get('moveDragItemAction')(this.get('_draggableProperty'), direction);
        this.notifyPropertyChange('_centerXY');
        this.set('_initialX', undefined);
        this.set('_initialY', undefined);
      }

      return false;
    }
  },

  /**
    @private
    @method _draggingIsSupported
    @param {FdEditformRow|FdEditformControl} over Item over which is dragged.
    @param {FdEditformRow|FdEditformControl} dragged Dragged item.
    @return {Boolean} If dragging makes sense `true`, else `false`.
  */
  _draggingIsSupported(over, dragged) {
    let result = over instanceof FdEditformRow && dragged instanceof FdEditformRow;

    result = result || over instanceof FdEditformControl && dragged instanceof FdEditformControl;
    result = result || over instanceof FdEditformControl && dragged instanceof FdEditformGroup;
    result = result || over instanceof FdEditformControl && dragged instanceof FdEditformTabgroup;

    result = result || over instanceof FdEditformGroup && dragged instanceof FdEditformGroup;
    result = result || over instanceof FdEditformGroup && dragged instanceof FdEditformControl;
    result = result || over instanceof FdEditformGroup && dragged instanceof FdEditformTabgroup;

    result = result || over instanceof FdEditformTabgroup && dragged instanceof FdEditformTabgroup;
    result = result || over instanceof FdEditformTabgroup && dragged instanceof FdEditformControl;
    result = result || over instanceof FdEditformTabgroup && dragged instanceof FdEditformGroup;

    return result;
  },
});