/*!
 * Custom elements for Nanopore-Digital Web applications
 */
/*!
 * Unipointer v2.4.0
 * base class for doing one thing with pointer event
 * MIT license
 */
import { EvEmitter } from "./event-emitter";
function noop() { }
export function Unipointer() { }
// inherit EvEmitter
const pointerProto = (Unipointer.prototype = Object.create(EvEmitter.prototype));
pointerProto.bindStartEvent = function (elem) {
    this._bindStartEvent(elem, true);
};
pointerProto.unbindStartEvent = function (elem) {
    this._bindStartEvent(elem, false);
};
/**
 * Add or remove start event
 * @param {Element} elem
 * @param {Boolean} isAdd - remove if falsey
 */
pointerProto._bindStartEvent = function (elem, isAdd) {
    // munge isAdd, default to true
    isAdd = isAdd === undefined ? true : isAdd;
    const bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
    // default to mouse events
    let startEvent = 'mousedown';
    if ('ontouchstart' in window) {
        // HACK prefer Touch Events as you can preventDefault on touchstart to
        // disable scroll in iOS & mobile Chrome metafizzy/flickity#1177
        startEvent = 'touchstart';
    }
    else if (window.PointerEvent) {
        // Pointer Events
        startEvent = 'pointerdown';
    }
    elem[bindMethod](startEvent, this);
};
// trigger handler methods for events
pointerProto.handleEvent = function (event) {
    const method = 'on' + event.type;
    if (this[method]) {
        this[method](event);
    }
};
// returns the touch that we're keeping track of
pointerProto.getTouch = function (touches) {
    for (let i = 0; i < touches.length; i++) {
        const touch = touches[i];
        if (touch.identifier == this.pointerIdentifier) {
            return touch;
        }
    }
};
// ----- start event ----- //
pointerProto.onmousedown = function (event) {
    // dismiss clicks from right or middle buttons
    const button = event.button;
    if (button && button !== 0 && button !== 1) {
        return;
    }
    this._pointerDown(event, event);
};
pointerProto.ontouchstart = function (event) {
    this._pointerDown(event, event.changedTouches[0]);
};
pointerProto.onpointerdown = function (event) {
    this._pointerDown(event, event);
};
/**
 * pointer start
 * @param {Event} event
 * @param {Event or Touch} pointer
 */
pointerProto._pointerDown = function (event, pointer) {
    // dismiss right click and other pointers
    // button = 0 is okay, 1-4 not
    if (event.button || this.isPointerDown) {
        return;
    }
    this.isPointerDown = true;
    // save pointer identifier to match up touch events
    this.pointerIdentifier =
        pointer.pointerId !== undefined
            ? // pointerId for pointer events, touch.indentifier for touch events
                pointer.pointerId
            : pointer.identifier;
    this.pointerDown(event, pointer);
};
pointerProto.pointerDown = function (event, pointer) {
    this._bindPostStartEvents(event);
    this.emitEvent('pointerDown', [event, pointer]);
};
// hash of events to be bound after start event
const postStartEvents = {
    mousedown: ['mousemove', 'mouseup'],
    touchstart: ['touchmove', 'touchend', 'touchcancel'],
    pointerdown: ['pointermove', 'pointerup', 'pointercancel'],
};
pointerProto._bindPostStartEvents = function (event) {
    if (!event) {
        return;
    }
    // get proper events to match start event
    const events = postStartEvents[event.type];
    // bind events to node
    events.forEach(function (eventName) {
        window.addEventListener(eventName, this);
    }, this);
    // save these arguments
    this._boundPointerEvents = events;
};
pointerProto._unbindPostStartEvents = function () {
    // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug)
    if (!this._boundPointerEvents) {
        return;
    }
    this._boundPointerEvents.forEach(function (eventName) {
        window.removeEventListener(eventName, this);
    }, this);
    delete this._boundPointerEvents;
};
// ----- move event ----- //
pointerProto.onmousemove = function (event) {
    this._pointerMove(event, event);
};
pointerProto.onpointermove = function (event) {
    if (event.pointerId == this.pointerIdentifier) {
        this._pointerMove(event, event);
    }
};
pointerProto.ontouchmove = function (event) {
    const touch = this.getTouch(event.changedTouches);
    if (touch) {
        this._pointerMove(event, touch);
    }
};
/**
 * pointer move
 * @param {Event} event
 * @param {Event or Touch} pointer
 * @private
 */
pointerProto._pointerMove = function (event, pointer) {
    this.pointerMove(event, pointer);
};
// public
pointerProto.pointerMove = function (event, pointer) {
    this.emitEvent('pointerMove', [event, pointer]);
};
// ----- end event ----- //
pointerProto.onmouseup = function (event) {
    this._pointerUp(event, event);
};
pointerProto.onpointerup = function (event) {
    if (event.pointerId == this.pointerIdentifier) {
        this._pointerUp(event, event);
    }
};
pointerProto.ontouchend = function (event) {
    const touch = this.getTouch(event.changedTouches);
    if (touch) {
        this._pointerUp(event, touch);
    }
};
/**
 * pointer up
 * @param {Event} event
 * @param {Event or Touch} pointer
 * @private
 */
pointerProto._pointerUp = function (event, pointer) {
    this._pointerDone();
    this.pointerUp(event, pointer);
};
// public
pointerProto.pointerUp = function (event, pointer) {
    this.emitEvent('pointerUp', [event, pointer]);
};
// ----- pointer done ----- //
// triggered on pointer up & pointer cancel
pointerProto._pointerDone = function () {
    this._pointerReset();
    this._unbindPostStartEvents();
    this.pointerDone();
};
pointerProto._pointerReset = function () {
    // reset properties
    this.isPointerDown = false;
    delete this.pointerIdentifier;
};
pointerProto.pointerDone = noop;
// ----- pointer cancel ----- //
pointerProto.onpointercancel = function (event) {
    if (event.pointerId == this.pointerIdentifier) {
        this._pointerCancel(event, event);
    }
};
pointerProto.ontouchcancel = function (event) {
    const touch = this.getTouch(event.changedTouches);
    if (touch) {
        this._pointerCancel(event, touch);
    }
};
/**
 * pointer cancel
 * @param {Event} event
 * @param {Event or Touch} pointer
 * @private
 */
pointerProto._pointerCancel = function (event, pointer) {
    this._pointerDone();
    this.pointerCancel(event, pointer);
};
// public
pointerProto.pointerCancel = function (event, pointer) {
    this.emitEvent('pointerCancel', [event, pointer]);
};
// -----  ----- //
// utility function for getting x/y coords from event
Unipointer.getPointerPoint = function (pointer) {
    return {
        x: pointer.pageX,
        y: pointer.pageY,
    };
};
/*!
 * Unidragger v2.4.0
 * Draggable base class
 * MIT license
 */
export function Unidragger() { }
// inherit Unipointer & EvEmitter
const draggerProto = (Unidragger.prototype = Object.create(Unipointer.prototype));
// ----- bind start ----- //
draggerProto.bindHandles = function () {
    this._bindHandles(true);
};
draggerProto.unbindHandles = function () {
    this._bindHandles(false);
};
/**
 * Add or remove start event
 * @param {Boolean} isAdd
 */
draggerProto._bindHandles = function (isAdd) {
    // munge isAdd, default to true
    isAdd = isAdd === undefined ? true : isAdd;
    // bind each handle
    const bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
    const touchAction = isAdd ? this._touchActionValue : '';
    for (let i = 0; i < this.handles.length; i++) {
        const handle = this.handles[i];
        this._bindStartEvent(handle, isAdd);
        handle[bindMethod]('click', this);
        // touch-action: none to override browser touch gestures. metafizzy/flickity#540
        if (window.PointerEvent) {
            handle.style.touchAction = touchAction;
        }
    }
};
// prototype so it can be overwriteable by Flickity
draggerProto._touchActionValue = 'none';
// ----- start event ----- //
/**
 * pointer start
 * @param {Event} event
 * @param {Event or Touch} pointer
 */
draggerProto.pointerDown = function (event, pointer) {
    const isOkay = this.okayPointerDown(event);
    if (!isOkay) {
        return;
    }
    // track start event position
    // Safari 9 overrides pageX and pageY. These values needs to be copied. flickity#842
    this.pointerDownPointer = {
        pageX: pointer.pageX,
        pageY: pointer.pageY,
    };
    event.preventDefault();
    this.pointerDownBlur();
    // bind move and end events
    this._bindPostStartEvents(event);
    this.emitEvent('pointerDown', [event, pointer]);
};
// nodes that have text fields
const cursorNodes = {
    TEXTAREA: true,
    INPUT: true,
    SELECT: true,
    OPTION: true,
};
// input types that do not have text fields
const clickTypes = {
    radio: true,
    checkbox: true,
    button: true,
    submit: true,
    image: true,
    file: true,
};
// dismiss inputs with text fields. flickity#403, flickity#404
draggerProto.okayPointerDown = function (event) {
    const isCursorNode = cursorNodes[event.target.nodeName];
    const isClickType = clickTypes[event.target.type];
    const isOkay = !isCursorNode || isClickType;
    if (!isOkay) {
        this._pointerReset();
    }
    return isOkay;
};
// kludge to blur previously focused input
draggerProto.pointerDownBlur = function () {
    const focused = document.activeElement;
    // do not blur body for IE10, metafizzy/flickity#117
    const canBlur = focused && focused.blur && focused != document.body;
    if (canBlur) {
        focused.blur();
    }
};
// ----- move event ----- //
/**
 * drag move
 * @param {Event} event
 * @param {Event or Touch} pointer
 */
draggerProto.pointerMove = function (event, pointer) {
    const moveVector = this._dragPointerMove(event, pointer);
    this.emitEvent('pointerMove', [event, pointer, moveVector]);
    this._dragMove(event, pointer, moveVector);
};
// base pointer move logic
draggerProto._dragPointerMove = function (event, pointer) {
    const moveVector = {
        x: pointer.pageX - this.pointerDownPointer.pageX,
        y: pointer.pageY - this.pointerDownPointer.pageY,
    };
    // start drag if pointer has moved far enough to start drag
    if (!this.isDragging && this.hasDragStarted(moveVector)) {
        this._dragStart(event, pointer);
    }
    return moveVector;
};
// condition if pointer has moved far enough to start drag
draggerProto.hasDragStarted = function (moveVector) {
    return Math.abs(moveVector.x) > 3 || Math.abs(moveVector.y) > 3;
};
// ----- end event ----- //
/**
 * pointer up
 * @param {Event} event
 * @param {Event or Touch} pointer
 */
draggerProto.pointerUp = function (event, pointer) {
    this.emitEvent('pointerUp', [event, pointer]);
    this._dragPointerUp(event, pointer);
};
draggerProto._dragPointerUp = function (event, pointer) {
    if (this.isDragging) {
        this._dragEnd(event, pointer);
    }
    else {
        // pointer didn't move enough for drag to start
        this._staticClick(event, pointer);
    }
};
// -------------------------- drag -------------------------- //
// dragStart
draggerProto._dragStart = function (event, pointer) {
    this.isDragging = true;
    // prevent clicks
    this.isPreventingClicks = true;
    this.dragStart(event, pointer);
};
draggerProto.dragStart = function (event, pointer) {
    this.emitEvent('dragStart', [event, pointer]);
};
// dragMove
draggerProto._dragMove = function (event, pointer, moveVector) {
    // do not drag if not dragging yet
    if (!this.isDragging) {
        return;
    }
    this.dragMove(event, pointer, moveVector);
};
draggerProto.dragMove = function (event, pointer, moveVector) {
    event.preventDefault();
    this.emitEvent('dragMove', [event, pointer, moveVector]);
};
// dragEnd
draggerProto._dragEnd = function (event, pointer) {
    // set flags
    this.isDragging = false;
    // re-enable clicking async
    setTimeout(function () {
        delete this.isPreventingClicks;
    }.bind(this));
    this.dragEnd(event, pointer);
};
draggerProto.dragEnd = function (event, pointer) {
    this.emitEvent('dragEnd', [event, pointer]);
};
// ----- onclick ----- //
// handle all clicks and prevent clicks when dragging
draggerProto.onclick = function (event) {
    if (this.isPreventingClicks) {
        event.preventDefault();
    }
};
// ----- staticClick ----- //
// triggered after pointer down & up with no/tiny movement
draggerProto._staticClick = function (event, pointer) {
    // ignore emulated mouse up clicks
    if (this.isIgnoringMouseUp && event.type == 'mouseup') {
        return;
    }
    this.staticClick(event, pointer);
    // set flag for emulated clicks 300ms after touchend
    if (event.type != 'mouseup') {
        this.isIgnoringMouseUp = true;
        // reset flag after 300ms
        setTimeout(function () {
            delete this.isIgnoringMouseUp;
        }.bind(this), 400);
    }
};
draggerProto.staticClick = function (event, pointer) {
    this.emitEvent('staticClick', [event, pointer]);
};
// ----- utils ----- //
Unidragger.getPointerPoint = Unipointer.getPointerPoint;
