"use strict";

Object.defineProperty(exports, "__esModule", {
    value: true
});
exports["default"] = void 0;

var _leaflet = require("leaflet");

var _line = require("./line.js");

require("./bouncing.css");

var _BouncingOptions = _interopRequireDefault(require("./BouncingOptions.js"));

var _Styles = _interopRequireDefault(require("./Styles.js"));

function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : {"default": obj};
}

function _slicedToArray(arr, i) {
    return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}

function _nonIterableRest() {
    throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}

function _unsupportedIterableToArray(o, minLen) {
    if (!o) return;
    if (typeof o === "string") return _arrayLikeToArray(o, minLen);
    var n = Object.prototype.toString.call(o).slice(8, -1);
    if (n === "Object" && o.constructor) n = o.constructor.name;
    if (n === "Map" || n === "Set") return Array.from(o);
    if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}

function _arrayLikeToArray(arr, len) {
    if (len == null || len > arr.length) len = arr.length;
    for (var i = 0, arr2 = new Array(len); i < len; i++) {
        arr2[i] = arr[i];
    }
    return arr2;
}

function _iterableToArrayLimit(arr, i) {
    var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
    if (_i == null) return;
    var _arr = [];
    var _n = true;
    var _d = false;
    var _s, _e;
    try {
        for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
            _arr.push(_s.value);
            if (i && _arr.length === i) break;
        }
    } catch (err) {
        _d = true;
        _e = err;
    } finally {
        try {
            if (!_n && _i["return"] != null) _i["return"]();
        } finally {
            if (_d) throw _e;
        }
    }
    return _arr;
}

function _arrayWithHoles(arr) {
    if (Array.isArray(arr)) return arr;
}

function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}

function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
    }
}

function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    Object.defineProperty(Constructor, "prototype", {writable: false});
    return Constructor;
}

function _classPrivateFieldInitSpec(obj, privateMap, value) {
    _checkPrivateRedeclaration(obj, privateMap);
    privateMap.set(obj, value);
}

function _checkPrivateRedeclaration(obj, privateCollection) {
    if (privateCollection.has(obj)) {
        throw new TypeError("Cannot initialize the same private elements twice on an object");
    }
}

function _defineProperty(obj, key, value) {
    if (key in obj) {
        Object.defineProperty(obj, key, {value: value, enumerable: true, configurable: true, writable: true});
    } else {
        obj[key] = value;
    }
    return obj;
}

function _classPrivateFieldGet(receiver, privateMap) {
    var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get");
    return _classApplyDescriptorGet(receiver, descriptor);
}

function _classApplyDescriptorGet(receiver, descriptor) {
    if (descriptor.get) {
        return descriptor.get.call(receiver);
    }
    return descriptor.value;
}

function _classPrivateFieldSet(receiver, privateMap, value) {
    var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set");
    _classApplyDescriptorSet(receiver, descriptor, value);
    return value;
}

function _classExtractFieldDescriptor(receiver, privateMap, action) {
    if (!privateMap.has(receiver)) {
        throw new TypeError("attempted to " + action + " private field on non-instance");
    }
    return privateMap.get(receiver);
}

function _classApplyDescriptorSet(receiver, descriptor, value) {
    if (descriptor.set) {
        descriptor.set.call(receiver, value);
    } else {
        if (!descriptor.writable) {
            throw new TypeError("attempted to set read only private field");
        }
        descriptor.value = value;
    }
}

var animationNamePrefix = 'l-smooth-marker-bouncing-';
var moveAnimationName = animationNamePrefix + 'move';
var contractAnimationName = animationNamePrefix + 'contract';
/*
 * CSS3 animation runs faster than transform-based animation. We need to reduce speed in order
 * to be compatible with old API.
 */

var speedCoefficient = 0.8;

/**
 * Removes and then resets required classes on the HTML element.
 * Used as hack to restart CSS3 animation.
 *
 * @param element {HTMLElement}  HTML element
 * @param classes {string[]}  names of classes
 */

function resetClasses(element, classes) {
    classes.forEach(function (className) {
        return _leaflet.DomUtil.removeClass(element, className);
    });
    void element.offsetWidth;
    classes.forEach(function (className) {
        return _leaflet.DomUtil.addClass(element, className);
    });
}

var _lastAnimationName = /*#__PURE__*/new WeakMap();

var _classes = /*#__PURE__*/new WeakMap();

var _eventCounter = /*#__PURE__*/new WeakMap();

var _times = /*#__PURE__*/new WeakMap();

var _listener = /*#__PURE__*/new WeakMap();

var BouncingMotionCss3 = /*#__PURE__*/function () {
    /**
     * Constructor.
     *
     * @param marker {Marker}  marker
     * @param position {Point}  marker current position on the map canvas
     * @param bouncingOptions {BouncingOptions}  options of bouncing animation
     */
    function BouncingMotionCss3(marker, position, bouncingOptions) {
        var _this = this;

        _classCallCheck(this, BouncingMotionCss3);

        _defineProperty(this, "marker", void 0);

        _defineProperty(this, "position", void 0);

        _defineProperty(this, "bouncingOptions", void 0);

        _defineProperty(this, "isBouncing", false);

        _defineProperty(this, "iconStyles", void 0);

        _defineProperty(this, "shadowStyles", void 0);

        _defineProperty(this, "bouncingAnimationPlaying", false);

        _classPrivateFieldInitSpec(this, _lastAnimationName, {
            writable: true,
            value: contractAnimationName
        });

        _classPrivateFieldInitSpec(this, _classes, {
            writable: true,
            value: ['bouncing']
        });

        _classPrivateFieldInitSpec(this, _eventCounter, {
            writable: true,
            value: void 0
        });

        _classPrivateFieldInitSpec(this, _times, {
            writable: true,
            value: void 0
        });

        _classPrivateFieldInitSpec(this, _listener, {
            writable: true,
            value: function value(event) {
                return _this.onAnimationEnd(event);
            }
        });

        this.marker = marker;
        this.position = position;
        this.updateBouncingOptions(bouncingOptions);
    }

    _createClass(BouncingMotionCss3, [{
        key: "updateBouncingOptions",
        value: function updateBouncingOptions(options) {
            this.bouncingOptions = options instanceof _BouncingOptions["default"] ? options : this.bouncingOptions.override(options);

            if (this.bouncingOptions.elastic) {
                _classPrivateFieldSet(this, _lastAnimationName, contractAnimationName);

                var index = _classPrivateFieldGet(this, _classes).indexOf('simple');

                if (index > -1) {
                    _classPrivateFieldGet(this, _classes).splice(index, 1);
                }

                if (this.marker._icon) {
                    _leaflet.DomUtil.removeClass(this.marker._icon, 'simple');
                }
            } else {
                _classPrivateFieldSet(this, _lastAnimationName, moveAnimationName);

                _classPrivateFieldGet(this, _classes).push('simple');
            }

            if (this.marker._icon) {
                this.resetStyles(this.marker);
            }
        }
    }, {
        key: "onAnimationEnd",
        value: function onAnimationEnd(event) {
            var _this2 = this;

            if (event.animationName === _classPrivateFieldGet(this, _lastAnimationName)) {
                var _this$eventCounter, _this$eventCounter2;

                _classPrivateFieldSet(this, _eventCounter, (_this$eventCounter = _classPrivateFieldGet(this, _eventCounter), _this$eventCounter2 = _this$eventCounter++, _this$eventCounter)), _this$eventCounter2;

                _classPrivateFieldSet(this, _eventCounter, _classPrivateFieldGet(this, _eventCounter) % 2);

                if (!_classPrivateFieldGet(this, _eventCounter)) {
                    var _this$times;

                    if (this.isBouncing && (_classPrivateFieldGet(this, _times) === null || _classPrivateFieldSet(this, _times, (_this$times = _classPrivateFieldGet(this, _times), --_this$times)))) {
                        resetClasses(this.marker._icon, _classPrivateFieldGet(this, _classes));

                        if (this.marker._shadow && this.bouncingOptions.shadowAngle) {
                            resetClasses(this.marker._shadow, _classPrivateFieldGet(this, _classes));
                        }
                    } else {
                        _classPrivateFieldGet(this, _classes).forEach(function (className) {
                            _leaflet.DomUtil.removeClass(_this2.marker._icon, className);

                            if (_this2.marker._shadow) {
                                _leaflet.DomUtil.removeClass(_this2.marker._shadow, className);
                            }
                        });

                        this.bouncingAnimationPlaying = false;
                        this.marker.fire('bounceend');
                    }
                }
            }
        }
    }, {
        key: "resetStyles",
        value: function resetStyles(marker) {
            var _this$marker$getIcon,
                _this$marker$getIcon$,
                _this$marker,
                _this$marker$_iconObj,
                _this$marker$_iconObj2,
                _this3 = this;

            this.marker = marker;
            this.iconStyles = _Styles["default"].ofMarker(marker);

            if (marker._shadow) {
                this.shadowStyles = _Styles["default"].parse(marker._shadow.style.cssText);
            }

            var iconHeight = ((_this$marker$getIcon = this.marker.getIcon()) === null || _this$marker$getIcon === void 0 ? void 0 : (_this$marker$getIcon$ = _this$marker$getIcon.options) === null || _this$marker$getIcon$ === void 0 ? void 0 : _this$marker$getIcon$.iconSize[1]) || ((_this$marker = this.marker) === null || _this$marker === void 0 ? void 0 : (_this$marker$_iconObj = _this$marker._iconObj) === null || _this$marker$_iconObj === void 0 ? void 0 : (_this$marker$_iconObj2 = _this$marker$_iconObj.options) === null || _this$marker$_iconObj2 === void 0 ? void 0 : _this$marker$_iconObj2.iconSize[1]);
            var iconAnimationParams = BouncingMotionCss3.animationParams(this.position, this.bouncingOptions, iconHeight);
            this.iconStyles = this.iconStyles.withStyles(iconAnimationParams);
            this.marker._icon.style.cssText = this.iconStyles.toString();

            if (this.bouncingAnimationPlaying) {
                resetClasses(this.marker._icon, _classPrivateFieldGet(this, _classes));

                this.marker._icon.addEventListener('animationend', _classPrivateFieldGet(this, _listener));
            }

            var _this$bouncingOptions = this.bouncingOptions,
                bounceHeight = _this$bouncingOptions.bounceHeight,
                contractHeight = _this$bouncingOptions.contractHeight,
                shadowAngle = _this$bouncingOptions.shadowAngle;

            if (this.marker._shadow) {
                if (shadowAngle) {
                    var _this$marker$getIcon2, _this$marker$getIcon3;

                    var _this$position = this.position,
                        x = _this$position.x,
                        y = _this$position.y;
                    var points = (0, _line.calculateLine)(x, y, shadowAngle, bounceHeight + 1);

                    var _points$bounceHeight = _slicedToArray(points[bounceHeight], 2),
                        posXJump = _points$bounceHeight[0],
                        posYJump = _points$bounceHeight[1];

                    var shadowHeight = (_this$marker$getIcon2 = this.marker.getIcon()) === null || _this$marker$getIcon2 === void 0 ? void 0 : (_this$marker$getIcon3 = _this$marker$getIcon2.options) === null || _this$marker$getIcon3 === void 0 ? void 0 : _this$marker$getIcon3.shadowSize[1];
                    var shadowScaleContract = BouncingMotionCss3.contractScale(shadowHeight, contractHeight);
                    this.shadowStyles = this.shadowStyles.withStyles(iconAnimationParams).withStyles({
                        '--pos-x-jump': "".concat(posXJump, "px"),
                        '--pos-y-jump': "".concat(posYJump, "px"),
                        '--scale-contract': shadowScaleContract
                    });
                    this.marker._shadow.style.cssText = this.shadowStyles.toString();

                    if (this.bouncingAnimationPlaying) {
                        resetClasses(this.marker._shadow, _classPrivateFieldGet(this, _classes));
                    }
                } else {
                    _classPrivateFieldGet(this, _classes).forEach(function (className) {
                        _leaflet.DomUtil.removeClass(_this3.marker._shadow, className);
                    });
                }
            }
        }
    }, {
        key: "bounce",
        value: function bounce() {
            var times = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;

            _classPrivateFieldSet(this, _times, times);

            this.isBouncing = true;

            if (this.bouncingAnimationPlaying) {
                return;
            }

            _classPrivateFieldSet(this, _eventCounter, 0);

            this.bouncingAnimationPlaying = true;
            resetClasses(this.marker._icon, _classPrivateFieldGet(this, _classes));

            if (this.marker._shadow && this.bouncingOptions.shadowAngle) {
                resetClasses(this.marker._shadow, _classPrivateFieldGet(this, _classes));
            }

            this.marker._icon.addEventListener('animationend', _classPrivateFieldGet(this, _listener));
        }
    }, {
        key: "stopBouncing",
        value: function stopBouncing() {
            this.isBouncing = false;
        }
        /**
         * Calculates parameters of CSS3 animation of bouncing.
         *
         * @param position {Point}  marker current position on the map canvas
         * @param bouncingOptions {BouncingOptions}  options of bouncing animation
         * @param height {number}  icons height
         * @return {object} CSS3 animation parameters
         */

    }], [{
        key: "animationParams",
        value: function animationParams(position, bouncingOptions, height) {
            var x = position.x,
                y = position.y;
            var bounceHeight = bouncingOptions.bounceHeight,
                contractHeight = bouncingOptions.contractHeight,
                bounceSpeed = bouncingOptions.bounceSpeed,
                contractSpeed = bouncingOptions.contractSpeed;
            var scaleContract = BouncingMotionCss3.contractScale(height, contractHeight);
            var durationJump = BouncingMotionCss3.calculateDuration(bounceHeight, bounceSpeed);
            var durationContract = BouncingMotionCss3.calculateDuration(contractHeight, contractSpeed);
            var delays = [0, durationJump, durationJump * 2, durationJump * 2 + durationContract];
            return {
                '--pos-x': "".concat(x, "px"),
                '--pos-y': "".concat(y, "px"),
                '--pos-y-jump': "".concat(y - bounceHeight, "px"),
                '--pos-y-contract': "".concat(y + contractHeight, "px"),
                '--scale-contract': scaleContract,
                '--duration-jump': "".concat(durationJump, "ms"),
                '--duration-contract': "".concat(durationContract, "ms"),
                '--delays': "0ms, ".concat(delays[1], "ms, ").concat(delays[2], "ms, ").concat(delays[3], "ms")
            };
        }
        /**
         * Calculates scale of contracting.
         *
         * @param {number} height  original height
         * @param {number} contractHeight  how much it must contract
         * @return {number}  contracting scale between 0 and 1
         */

    }, {
        key: "contractScale",
        value: function contractScale(height, contractHeight) {
            return (height - contractHeight) / height;
        }
        /**
         * Calculates duration of animation.
         *
         * @param height {number}  height of movement or resizing (px)
         * @param speed {number}  speed coefficient
         *
         * @return {number} duration of animation (ms)
         */

    }, {
        key: "calculateDuration",
        value: function calculateDuration(height, speed) {
            var duration = Math.round(speed * speedCoefficient);
            var i = height;

            while (--i) {
                duration += Math.round(speed / (height - i));
            }

            return duration;
        }
    }]);

    return BouncingMotionCss3;
}();

exports["default"] = BouncingMotionCss3;