import { __extends } from "tslib";
import { targetIsInput } from '../../../utils/event/event';
import { isInElement } from '../../../utils/mouse/mouse';
import { getVNodeAttributeValue } from '../../../utils/vue/directive';
import { dispatchEvent } from '../../../utils/vue/events';
import { DROPPABLE_NAME } from '../../directive-names';
import { MDOMPlugin, MElementDomPlugin } from '../../domPlugin';
import { MSortable, MSortableAction } from '../../sortable/sortable';
import RemoveUserSelectPlugin, { MRemoveUserSelect } from '../../user-select/remove-user-select';
import { MDraggable } from '../draggable/draggable';
import './droppable.scss';
export var MDroppableClassNames;
(function (MDroppableClassNames) {
    MDroppableClassNames["Droppable"] = "m--is-droppable";
    MDroppableClassNames["Overing"] = "m--is-dragover";
    MDroppableClassNames["CanDrop"] = "m--can-drop";
    MDroppableClassNames["CantDrop"] = "m--cant-drop";
})(MDroppableClassNames || (MDroppableClassNames = {}));
export var MDropEffect;
(function (MDropEffect) {
    MDropEffect["MMove"] = "move";
    MDropEffect["MNone"] = "none";
})(MDropEffect || (MDropEffect = {}));
export var MDroppableEventNames;
(function (MDroppableEventNames) {
    MDroppableEventNames["OnDrop"] = "droppable:drop";
    MDroppableEventNames["OnDragEnter"] = "droppable:dragenter";
    MDroppableEventNames["OnDragLeave"] = "droppable:dragleave";
    MDroppableEventNames["OnDragOver"] = "droppable:dragover";
})(MDroppableEventNames || (MDroppableEventNames = {}));
var DEFAULT_ACTION = 'any';
var MDroppable = /** @class */ (function (_super) {
    __extends(MDroppable, _super);
    function MDroppable(element, options) {
        var _this = _super.call(this, element, options) || this;
        _this.grabEvents = ['mousedown', 'touchstart'];
        _this.cancelGrabEvents = ['mouseup', 'touchend', 'click', 'touchcancel'];
        _this.intputTouchUpListener = _this.turnDragOn.bind(_this);
        return _this;
    }
    MDroppable.prototype.attach = function (mount) {
        var _this = this;
        this.setOptions(this.options);
        if (this.options.canDrop) {
            mount(function () {
                _this.attachAll();
            });
        }
        else if (this.options.alwaysMount) {
            mount(function () {
                _this.element.classList.add(MDroppableClassNames.Droppable);
                _this.attachDragOverOnly();
            });
        }
    };
    MDroppable.prototype.update = function (options, refresh) {
        var _this = this;
        this.setOptions(this._options = options);
        if (this.options.canDrop) {
            refresh(function () { _this.element.classList.add(MDroppableClassNames.Droppable); });
        }
        else if (this.options.alwaysMount) {
            refresh(function () {
                _this.detach();
                _this.attachDragOverOnly();
            });
        }
    };
    MDroppable.prototype.detach = function () {
        var _this = this;
        MDOMPlugin.detach(MRemoveUserSelect, this.element);
        this.cleanupCssClasses();
        this.removeAllEvents();
        this.cancelGrabEvents.forEach(function (cancelEventName) { return document.removeEventListener(cancelEventName, _this.intputTouchUpListener); });
        this.element.classList.remove(MDroppableClassNames.Droppable);
    };
    MDroppable.prototype.cleanupCssClasses = function () {
        this.element.classList.remove(MDroppableClassNames.Overing);
        this.element.classList.remove(MDroppableClassNames.CanDrop);
        this.element.classList.remove(MDroppableClassNames.CantDrop);
    };
    MDroppable.prototype.leaveDroppable = function (event) {
        if (MDroppable.currentHoverDroppable === this) {
            MDroppable.previousHoverContainer = this;
            MDroppable.currentHoverDroppable = undefined;
        }
        this.cleanupCssClasses();
        this.dispatchEvent(event, MDroppableEventNames.OnDragLeave);
    };
    MDroppable.prototype.canDropTarget = function (event) {
        var canDropAttr = event && event.currentTarget && event.currentTarget.getAttribute('can-drop');
        var canDropValue = canDropAttr === 'false' ? false : true;
        return canDropValue;
    };
    MDroppable.prototype.canDrop = function (draggable) {
        if (draggable === void 0) { draggable = MDraggable.currentDraggable; }
        if (!draggable) {
            return false;
        }
        var canDrop = this.options.canDrop ? true : false;
        var acceptAny = this.options.acceptedActions.find(function (action) { return action === 'any'; }) !== undefined;
        var draggableAction = draggable.options.action;
        var isAllowedAction = this.options.acceptedActions.find(function (action) { return action === draggableAction; }) !== undefined;
        return canDrop && !this.isHoveringOverDraggedElementChild()
            && (acceptAny || isAllowedAction)
            && !this.isDropRestrictedByEncapsuledSortable();
    };
    MDroppable.prototype.attachAll = function () {
        var _this = this;
        this.element.classList.add(MDroppableClassNames.Droppable);
        MDOMPlugin.attach(MRemoveUserSelect, this.element, true);
        this.addEventListener('dragenter', function (event) { return _this.onDragEnter(event); });
        this.addEventListener('dragleave', function (event) { return _this.onDragLeave(event); });
        // Firefox doesn't handle dragLeave correctly.  We have to declare dragexit AND dragleave for that reason.
        this.addEventListener('dragexit', function (event) { return _this.onDragLeave(event); });
        this.addEventListener('dragover', function (event) { return _this.onDragOver(event); });
        this.addEventListener('drop', function (event) { return _this.onDrop(event); });
        this.allowInputTextSelection();
    };
    MDroppable.prototype.attachDragOverOnly = function () {
        var _this = this;
        this.element.classList.add(MDroppableClassNames.Droppable);
        MDOMPlugin.attach(MRemoveUserSelect, this.element, true);
        this.addEventListener('dragover', function (event) { return _this.onDragOver(event); });
        this.allowInputTextSelection();
    };
    MDroppable.prototype.isDropRestrictedByEncapsuledSortable = function () {
        var activeSortContainer = MSortable.activeSortContainer || MDOMPlugin.getRecursive(MSortable, this.element);
        if ((activeSortContainer && MSortable.fromSortContainer)
            && activeSortContainer !== MSortable.fromSortContainer
            && (activeSortContainer.options.encapsulate || MSortable.fromSortContainer.options.encapsulate)) {
            return true;
        }
        return false;
    };
    MDroppable.prototype.setOptions = function (value) {
        if (value.canDrop === undefined) {
            value.canDrop = true;
        }
        this._options = value;
        this._options.acceptedActions = this.options.canDrop ? this.options.acceptedActions || [DEFAULT_ACTION] : [];
    };
    MDroppable.prototype.onDragLeave = function (event) {
        var leftFor = event.relatedTarget;
        if (leftFor && leftFor !== this.element && leftFor.contains(this.element)) {
            this.leaveDroppable(event);
        }
        else if (!leftFor && this.isLeavingDroppable(event, this)) {
            this.leaveDroppable(event);
        }
    };
    MDroppable.prototype.isLeavingDroppable = function (event, droppable) {
        if (!droppable) {
            return false;
        }
        return !isInElement(event, droppable.element) || MDroppable.previousHoverContainer !== MDroppable.currentHoverDroppable;
    };
    MDroppable.prototype.onDragEnter = function (event) {
        return this.onDragIn(event);
    };
    MDroppable.prototype.onDragOver = function (event) {
        return this.onDragIn(event);
    };
    MDroppable.prototype.onDragIn = function (event) {
        if (document.elementFromPoint) {
            var element = document.elementFromPoint(event.clientX, event.clientY);
            var droppable = MDOMPlugin.getRecursive(MDroppable, element);
            // Firefox sometime fires events on the wrong container for some reasons.  This fix it.
            if (droppable !== this) {
                this.cleanupCssClasses();
                return;
            }
        }
        var className;
        if (this.canDrop()) {
            event.preventDefault();
            event.dataTransfer.dropEffect = MDropEffect.MMove;
            className = MDroppableClassNames.CanDrop;
        }
        else {
            event.dataTransfer.dropEffect = MDropEffect.MNone;
            className = MDroppableClassNames.CantDrop;
        }
        MDroppable.previousHoverContainer = MDroppable.currentHoverDroppable;
        MDroppable.currentHoverDroppable = this;
        if (MDroppable.previousHoverContainer !== MDroppable.currentHoverDroppable || !this.element.classList.contains(className)) {
            this.cleanupCssClasses();
            this.element.classList.add(MDroppableClassNames.Overing);
            this.element.classList.add(className);
            this.dispatchEvent(event, MDroppableEventNames.OnDragEnter);
        }
        this.dispatchEvent(event, MDroppableEventNames.OnDragOver);
    };
    MDroppable.prototype.onDrop = function (event) {
        event.stopPropagation();
        // Important for firefox as it tries to open dropped content as URL by default.
        event.preventDefault();
        this.cleanupCssClasses();
        MDroppable.currentHoverDroppable = undefined;
        this.canDropTarget(event);
        this.dispatchEvent(event, MDroppableEventNames.OnDrop);
    };
    MDroppable.prototype.dispatchEvent = function (event, name) {
        var dropInfo = this.extractDropInfo(event);
        var customEvent = document.createEvent('CustomEvent');
        customEvent.initCustomEvent(name, true, true, Object.assign(event, { dropInfo: dropInfo }));
        customEvent.dropInfo = dropInfo;
        dispatchEvent(this.element, name, customEvent);
    };
    MDroppable.prototype.extractDropInfo = function (event) {
        var data = MDraggable.currentDraggable ? MDraggable.currentDraggable.options.dragData || event.dataTransfer.getData('text') : undefined;
        var action = MDraggable.currentDraggable ? MDraggable.currentDraggable.options.action : DEFAULT_ACTION;
        var grouping = MDraggable.currentDraggable ? MDraggable.currentDraggable.options.grouping : undefined;
        return {
            action: action,
            grouping: action === MSortableAction.MoveGroup || ![MSortableAction.MoveGroup, MSortableAction.Move].find(function (item) { return item === action; }) ? grouping : undefined,
            data: data,
            canDrop: this.canDrop()
        };
    };
    MDroppable.prototype.isHoveringOverDraggedElementChild = function () {
        if (!MDroppable.currentHoverDroppable || !MDraggable.currentDraggable) {
            return false;
        }
        var found = false;
        var element = MDroppable.currentHoverDroppable.element;
        while (!found && element) {
            if (element === MDraggable.currentDraggable.element) {
                found = true;
            }
            element = element.parentNode;
        }
        return found;
    };
    MDroppable.prototype.allowInputTextSelection = function () {
        var _this = this;
        this.grabEvents.forEach(function (eventName) {
            _this.addEventListener(eventName, function (event) {
                if (targetIsInput(_this.element, event)) {
                    MDOMPlugin.detach(MRemoveUserSelect, _this.element);
                    _this.cancelGrabEvents.forEach(function (cancelEventName) { return document.addEventListener(cancelEventName, _this.intputTouchUpListener); });
                }
            });
        });
    };
    MDroppable.prototype.turnDragOn = function () {
        var _this = this;
        MDOMPlugin.attach(MRemoveUserSelect, this.element, true);
        this.cancelGrabEvents.forEach(function (cancelEventName) { return document.removeEventListener(cancelEventName, _this.intputTouchUpListener); });
    };
    MDroppable.defaultMountPoint = '__mdroppable__';
    return MDroppable;
}(MElementDomPlugin));
export { MDroppable };
var extractVnodeAttributes = function (binding, node) {
    return {
        acceptedActions: getVNodeAttributeValue(node, 'accepted-actions'),
        grouping: getVNodeAttributeValue(node, 'grouping'),
        canDrop: binding.value
    };
};
var Directive = {
    inserted: function (element, binding, node) {
        MDOMPlugin.attach(MDroppable, element, extractVnodeAttributes(binding, node));
    },
    update: function (element, binding, node) {
        MDOMPlugin.attach(MDroppable, element, extractVnodeAttributes(binding, node));
    },
    componentUpdated: function (element, binding, node) {
        MDOMPlugin.attach(MDroppable, element, extractVnodeAttributes(binding, node));
    },
    unbind: function (element, binding) {
        MDOMPlugin.detach(MDroppable, element);
    }
};
var DroppablePlugin = {
    install: function (v, options) {
        v.use(RemoveUserSelectPlugin);
        v.directive(DROPPABLE_NAME, Directive);
    }
};
export default DroppablePlugin;
//# sourceMappingURL=droppable.js.map