window.Doghole && (function(Doghole) {
    'use strict'
    const
    NAMESPACE = '.scrollLock',
    ANIMATION_NAMESPACE = NAMESPACE + '.effect',
    WHEEL = ('onwheel' in document.createElement('div') ? 'wheel' : document.onmousewheel !== undefined ? 'mousewheel' : 'DOMMouseScroll') + NAMESPACE,
    ANIMATION = ['webkitAnimationEnd', 'mozAnimationEnd', 'MSAnimationEnd', 'oanimationend', 'animationend', ''].join(ANIMATION_NAMESPACE + ' ').trim(),
    TOUCHMOVE = 'touchmove' + NAMESPACE,
    KEYDOWN = 'keydown' + NAMESPACE
    
    var kbdResult = function(e, el) {
        var
        scrollTop = el.scrollTop,
        top = scrollTop === 0 && [33, 36, 38].indexOf(e.keyCode) !== -1,
        bottom = !top && el.scrollHeight === scrollTop + el.clientHeight &&
            [32, 34, 35, 40].indexOf(e.keyCode) !== -1
        return {
            top: top,
            bottom: bottom
        }
    }
    function dataset(el) {
        var result = {}
        for (var key in el.dataset) {
            if (el.dataset.hasOwnProperty(key)) {
                var value = el.dataset[key];
                try {
                    value = JSON.parse(value);
                }
                catch(_){}
                result[key] = value
            }
        }
        return result
    }
    var ScrollLock = function(el, options) {
        this.element = el;
        this.options = Doghole.Utils.extend({}, {
            strict: !1,
            strictFn: el => el.scrollHeight > el.clientHeight,
            selector: undefined,
            animation: undefined,
            touch: 'ontouchstart' in window,
            keyboard: !1,
            unblock: undefined
        }, dataset(el), options)
        this.enabled = !0
        this.startClientY = 0;
        
        
        let blockHandler = (e) => {
            if (this.enabled && !e.ctrlKey) {
                if (this.options.strict !== true || this.options.strictFn(e.currentTarget)) {
                    e.stopPropagation()
                    var result = scrollResult(e, e.currentTarget) 
                    if (result.prevent && e._unblockedTarget)
                        result.prevent &= scrollResult(e, e._unblockedTarget).prevent
                    if (result.prevent) {
                        e.preventDefault()
                        if (result.deltaY) {
                            e.target.scrollTop = result.scrollTop + result.deltaY
                        }
                        var key = result.top ? 'top' : 'bottom'
                        if (this.options.animation) {
                            setTimeout(onAnimation.bind(this, e.currentTarget, key), 0)
                        }
                        e.target.trigger(key + NAMESPACE)
                    }
                }
            }
        },
        onTouch = e => {
            this.startClientY = e.touches[0].clientY
        },
        onAnimation = (el, key) => {
            var css = this.options.animation[key],
            opClass = (el, op, clazzes) => {
                clazzes.split(' ').filter(v => v !== '').forEach(clazz => {
                    el.classList[op](clazz)
                })
            }
            el.off(ANIMATION_NAMESPACE);
            opClass(el, 'remove', this.options.animation.top + ' ' + this.options.animation.bottom);
            opClass(el, 'add', css);
            el.on(ANIMATION, e => {
                opClass(el, 'remove', css);
                el.off(ANIMATION)
            })
        },
        onKeyboard = e => {
            var scrollTop = e.target.scrollTop
              , result = kbdResult(e, e.target)
            if ((result.top || result.bottom) && e._unblockedTarget) {
                var result2 = kbdResult(e, e._unblockedTarget)
                result.top &= result2.top
                result.bottom &= result2.bottom
            }
            var key = result.top ? 'top' : bottom ? 'bottom' : !1
            if (key) {
                e.target.trigger(key + NAMESPACE)
                if (this.options.animation) {
                    setTimeout(onAnimation.bind(this, e.target, type), 0)
                }
                return false
            }
        },
        scrollResult = (e, el) => {
            var scrollTop = el.scrollTop
              , scrollHeight = el.scrollHeight
              , clientHeight = el.clientHeight
              , delta = e.wheelDelta || -e.detail || -e.deltaY
              , deltaY = 0
            if (e.type === 'wheel') {
                var elementHeight = 
                    parseFloat(window.getComputedStyle(el).height),
                ratio = elementHeight / window.innerHeight;
                deltaY = e.deltaY * ratio
            }
            else if (this.options.touch && e.type === 'touchmove') {
                delta = e.changedTouches[0].clientY - this.startClientY
            }
            var top, prevent = (top = delta > 0 && scrollTop + deltaY <= 0) || delta < 0 && scrollTop + deltaY >= scrollHeight - clientHeight
            return {
                prevent: prevent,
                top: top,
                scrollTop: scrollTop,
                deltaY: deltaY
            }
        }
        
        if (this.options.unblock) {
            let unblockEventName = WHEEL
            if (this.options.touch) unblockEventName += ' ' + TOUCHMOVE
            if (this.options.keyboard) unblockEventName += ' ' + KEYDOWN
            el.on(
                unblockEventName,
                this.options.unblock,
                e => {
                    e._unblockedTarget = e.target;
                })
        }
        let blockEventName = WHEEL
        if (this.options.touch) {
            blockEventName += ' ' + TOUCHMOVE
            el.on(
                'touchstart' + NAMESPACE,
                this.options.selector,
                onTouch)
        }
        
        el.on(
            blockEventName,
            this.options.selector,
            blockHandler)
            
        if (this.options.keyboard) {
            if (!el.getAttribute('tabindex'))
                el.setAttribute('tabindex', this.options.keyboard.tabindex || 0)
            el.on(
                KEYDOWN,
                this.options.selector,
                onKeyboard)
        }
    }
    ScrollLock.NAMESPACE = NAMESPACE
    ScrollLock.ANIMATION_NAMESPACE = ANIMATION_NAMESPACE
    ScrollLock.WHEEL = WHEEL
    ScrollLock.ANIMATION = ANIMATION
    ScrollLock.prototype.toggleStrict = function() {
        this.options.strict = !this.options.strict
    }
    ScrollLock.prototype.toggle = function () {
        this.enabled = !this.enabled
    }
    ScrollLock.prototype.enable = function() {
        this.enabled = !0
    }
    ScrollLock.prototype.disable = function() {
        this.enabled = !1
    }
    ScrollLock.prototype.destroy = function() {
        this.disable()
        this.element.off(NAMESPACE)
        this.element = null
        this.options = null
    }
    Doghole.ScrollLock = ScrollLock
    Element.prototype.scrollLock = function (option) {
        return new ScrollLock(this, option)
    }
    return !0
})(Doghole) || console.log('Doghole is required!');
