text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  var ReactDOM = require("react-dom");
  var Utilities_1 = require("../../Utilities");
  var MOUSEDOWN_PRIMARY_BUTTON = 0; // for mouse down event we are using ev.button property, 0 means left button
  var MOUSEMOVE_PRIMARY_BUTTON = 1; // for mouse move event we are using ev.buttons property, 1 means left button
  var DragDropHelper = /** @class */ (function () {
      function DragDropHelper(params) {
          this._selection = params.selection;
          this._dragEnterCounts = {};
          this._activeTargets = {};
          this._lastId = 0;
          this._events = new Utilities_1.EventGroup(this);
          // clear drag data when mouse up, use capture event to ensure it will be run
          this._events.on(document.body, 'mouseup', this._onMouseUp.bind(this), true);
          this._events.on(document, 'mouseup', this._onDocumentMouseUp.bind(this), true);
      }
      DragDropHelper.prototype.dispose = function () {
          this._events.dispose();
      };
      DragDropHelper.prototype.subscribe = function (root, events, dragDropOptions) {
          var _this = this;
          var _a = dragDropOptions.key, key = _a === void 0 ? "" + ++this._lastId : _a;
          var handlers = [];
          var onDragStart;
          var onDragLeave;
          var onDragEnter;
          var onDragEnd;
          var onDrop;
          var onDragOver;
          var onMouseDown;
          var isDraggable;
          var isDroppable;
          var activeTarget;
          if (dragDropOptions && root) {
              var eventMap = dragDropOptions.eventMap, context = dragDropOptions.context, updateDropState_1 = dragDropOptions.updateDropState;
              var dragDropTarget = {
                  root: root,
                  options: dragDropOptions,
                  key: key
              };
              isDraggable = this._isDraggable(dragDropTarget);
              isDroppable = this._isDroppable(dragDropTarget);
              if (isDraggable || isDroppable) {
                  if (eventMap) {
                      for (var _i = 0, eventMap_1 = eventMap; _i < eventMap_1.length; _i++) {
                          var event_1 = eventMap_1[_i];
                          var handler = {
                              callback: event_1.callback.bind(null, context),
                              eventName: event_1.eventName
                          };
                          handlers.push(handler);
                          this._events.on(root, handler.eventName, handler.callback);
                      }
                  }
              }
              if (isDroppable) {
                  // If the target is droppable, wire up global event listeners to track drop-related events.
                  onDragLeave = function (event) {
                      if (!event.isHandled) {
                          event.isHandled = true;
                          _this._dragEnterCounts[key]--;
                          if (_this._dragEnterCounts[key] === 0) {
                              updateDropState_1(false /* isDropping */, event);
                          }
                      }
                  };
                  onDragEnter = function (event) {
                      event.preventDefault(); // needed for IE
                      if (!event.isHandled) {
                          event.isHandled = true;
                          _this._dragEnterCounts[key]++;
                          if (_this._dragEnterCounts[key] === 1) {
                              updateDropState_1(true /* isDropping */, event);
                          }
                      }
                  };
                  onDragEnd = function (event) {
                      _this._dragEnterCounts[key] = 0;
                      updateDropState_1(false /* isDropping */, event);
                  };
                  onDrop = function (event) {
                      _this._dragEnterCounts[key] = 0;
                      updateDropState_1(false /* isDropping */, event);
                      if (dragDropOptions.onDrop) {
                          dragDropOptions.onDrop(dragDropOptions.context.data, event);
                      }
                  };
                  onDragOver = function (event) {
                      event.preventDefault();
                      if (dragDropOptions.onDragOver) {
                          dragDropOptions.onDragOver(dragDropOptions.context.data, event);
                      }
                  };
                  this._dragEnterCounts[key] = 0;
                  // dragenter and dragleave will be fired when hover to the child element
                  // but we only want to change state when enter or leave the current element
                  // use the count to ensure it.
                  events.on(root, 'dragenter', onDragEnter);
                  events.on(root, 'dragleave', onDragLeave);
                  events.on(root, 'dragend', onDragEnd);
                  events.on(root, 'drop', onDrop);
                  events.on(root, 'dragover', onDragOver);
              }
              if (isDraggable) {
                  // If the target is draggable, wire up local event listeners for mouse events.
                  onMouseDown = this._onMouseDown.bind(this, dragDropTarget);
                  onDragEnd = this._onDragEnd.bind(this, dragDropTarget);
                  // We need to add in data so that on Firefox we show the ghost element when dragging
                  onDragStart = function (event) {
                      var options = _this._dragData.dragTarget.options;
                      if (options && options.onDragStart) {
                          options.onDragStart(options.context.data, options.context.index, _this._selection.getSelection(), event);
                      }
                      _this._isDragging = true;
                      event.dataTransfer.setData('id', root.id);
                  };
                  events.on(root, 'dragstart', onDragStart);
                  events.on(root, 'mousedown', onMouseDown);
                  events.on(root, 'dragend', onDragEnd);
              }
              activeTarget = {
                  target: dragDropTarget,
                  dispose: function () {
                      if (_this._activeTargets[key] === activeTarget) {
                          delete _this._activeTargets[key];
                      }
                      if (root) {
                          for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) {
                              var handler = handlers_1[_i];
                              _this._events.off(root, handler.eventName, handler.callback);
                          }
                          if (isDroppable) {
                              events.off(root, 'dragenter', onDragEnter);
                              events.off(root, 'dragleave', onDragLeave);
                              events.off(root, 'dragend', onDragEnd);
                              events.off(root, 'dragover', onDragOver);
                              events.off(root, 'drop', onDrop);
                          }
                          if (isDraggable) {
                              events.off(root, 'dragstart', onDragStart);
                              events.off(root, 'mousedown', onMouseDown);
                              events.off(root, 'dragend', onDragEnd);
                          }
                      }
                  }
              };
              this._activeTargets[key] = activeTarget;
          }
          return {
              key: key,
              dispose: function () {
                  if (activeTarget) {
                      activeTarget.dispose();
                  }
              }
          };
      };
      DragDropHelper.prototype.unsubscribe = function (root, key) {
          var activeTarget = this._activeTargets[key];
          if (activeTarget) {
              activeTarget.dispose();
          }
      };
      DragDropHelper.prototype._onDragEnd = function (target, event) {
          var options = target.options;
          if (options.onDragEnd) {
              options.onDragEnd(options.context.data, event);
          }
      };
      /**
       * clear drag data when mouse up on body
       */
      DragDropHelper.prototype._onMouseUp = function (event) {
          this._isDragging = false;
          if (this._dragData) {
              for (var _i = 0, _a = Object.keys(this._activeTargets); _i < _a.length; _i++) {
                  var key = _a[_i];
                  var activeTarget = this._activeTargets[key];
                  if (activeTarget.target.root) {
                      this._events.off(activeTarget.target.root, 'mousemove');
                      this._events.off(activeTarget.target.root, 'mouseleave');
                  }
              }
              if (this._dragData.dropTarget) {
                  // raise dragleave event to let dropTarget know it need to remove dropping style
                  Utilities_1.EventGroup.raise(this._dragData.dropTarget.root, 'dragleave');
                  Utilities_1.EventGroup.raise(this._dragData.dropTarget.root, 'drop');
              }
          }
          this._dragData = null;
      };
      /**
       * clear drag data when mouse up outside of the document
       */
      DragDropHelper.prototype._onDocumentMouseUp = function (event) {
          if (event.target === document.documentElement) {
              this._onMouseUp(event);
          }
      };
      /**
       * when mouse move over a new drop target while dragging some items,
       * fire dragleave on the old target and fire dragenter to the new target
       * The target will handle style change on dragenter and dragleave events.
       */
      DragDropHelper.prototype._onMouseMove = function (target, event) {
          var 
          // use buttons property here since ev.button in some edge case is not updating well during the move.
          // but firefox doesn't support it, so we set the default value when it is not defined.
          _a = event.buttons, 
          // use buttons property here since ev.button in some edge case is not updating well during the move.
          // but firefox doesn't support it, so we set the default value when it is not defined.
          buttons = _a === void 0 ? MOUSEMOVE_PRIMARY_BUTTON : _a;
          if (this._dragData && buttons !== MOUSEMOVE_PRIMARY_BUTTON) {
              // cancel mouse down event and return early when the primary button is not pressed
              this._onMouseUp(event);
              return;
          }
          var root = target.root, key = target.key;
          if (this._isDragging) {
              if (this._isDroppable(target)) {
                  // we can have nested drop targets in the DOM, like a folder inside a group. In that case, when we drag into
                  // the inner target (folder), we first set dropTarget to the inner element. But the same event is bubbled to the
                  // outer target too, and we need to prevent the outer one from taking over.
                  // So, check if the last dropTarget is not a child of the current.
                  if (this._dragData) {
                      if (this._dragData.dropTarget && this._dragData.dropTarget.key !== key && !this._isChild(root, this._dragData.dropTarget.root)) {
                          if (this._dragEnterCounts[this._dragData.dropTarget.key] > 0) {
                              Utilities_1.EventGroup.raise(this._dragData.dropTarget.root, 'dragleave');
                              Utilities_1.EventGroup.raise(root, 'dragenter');
                              this._dragData.dropTarget = target;
                          }
                      }
                  }
              }
          }
      };
      /**
       * when mouse leave a target while dragging some items, fire dragleave to the target
       */
      DragDropHelper.prototype._onMouseLeave = function (target, event) {
          if (this._isDragging) {
              if (this._dragData && this._dragData.dropTarget && this._dragData.dropTarget.key === target.key) {
                  Utilities_1.EventGroup.raise(target.root, 'dragleave');
                  this._dragData.dropTarget = undefined;
              }
          }
      };
      /**
       * when mouse down on a draggable item, we start to track dragdata.
       */
      DragDropHelper.prototype._onMouseDown = function (target, event) {
          if (event.button !== MOUSEDOWN_PRIMARY_BUTTON) {
              // Ignore anything except the primary button.
              return;
          }
          if (this._isDraggable(target)) {
              this._dragData = {
                  clientX: event.clientX,
                  clientY: event.clientY,
                  eventTarget: event.target,
                  dragTarget: target
              };
              for (var _i = 0, _a = Object.keys(this._activeTargets); _i < _a.length; _i++) {
                  var key = _a[_i];
                  var activeTarget = this._activeTargets[key];
                  if (activeTarget.target.root) {
                      this._events.on(activeTarget.target.root, 'mousemove', this._onMouseMove.bind(this, activeTarget.target));
                      this._events.on(activeTarget.target.root, 'mouseleave', this._onMouseLeave.bind(this, activeTarget.target));
                  }
              }
          }
          else {
              this._dragData = null;
          }
      };
      /**
       * determine whether the child target is a descendant of the parent
       */
      DragDropHelper.prototype._isChild = function (parent, child) {
          var parentElement = ReactDOM.findDOMNode(parent);
          var childElement = ReactDOM.findDOMNode(child);
          while (childElement && childElement.parentElement) {
              if (childElement.parentElement === parentElement) {
                  return true;
              }
              childElement = childElement.parentElement;
          }
          return false;
      };
      DragDropHelper.prototype._isDraggable = function (target) {
          var options = target.options;
          return !!(options.canDrag && options.canDrag(options.context.data));
      };
      DragDropHelper.prototype._isDroppable = function (target) {
          // TODO: take the drag item into consideration to prevent dragging an item into the same group
          var options = target.options;
          var dragContext = this._dragData && this._dragData.dragTarget ? this._dragData.dragTarget.options.context : undefined;
          return !!(options.canDrop && options.canDrop(options.context, dragContext));
      };
      return DragDropHelper;
  }());
  exports.DragDropHelper = DragDropHelper;
  //# sourceMappingURL=DragDropHelper.js.map