<commit_before>/**
 * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
 *
 * @author <NAME> <<EMAIL>>
 */

import { Subject, Observable } from 'rxjs';

import { Vec2 } from '../../mol-math/linear-algebra';

import { BitFlags, noop } from '../../mol-util';

export function getButtons(event: MouseEvent | Touch) {
    if (typeof event === 'object') {
        if ('buttons' in event) {
            return event.buttons
        } else if ('which' in event) {
            const b = (event as any).which  // 'any' to support older browsers
            if (b === 2) {
                return 4
            } else if (b === 3) {
                return 2
            } else if (b > 0) {
                return 1 << (b - 1)
            }
        } else if ('button' in event) {
            const b = (event as any).button  // 'any' to support older browsers
            if (b === 1) {
                return 4
            } else if (b === 2) {
                return 2
            } else if (b >= 0) {
                return 1 << b
            }
        }
    }
    return 0
}

export function getModifiers(event: MouseEvent | Touch) {
    return {
        alt: 'altKey' in event ? event.altKey : false,
        shift: 'shiftKey' in event ? event.shiftKey : false,
        control: 'ctrlKey' in event ? event.ctrlKey : false,
        meta: 'metaKey' in event ? event.metaKey : false
    }
}

export const DefaultInputObserverProps = {
    noScroll: true,
    noContextMenu: true,
    noPinchZoom: true
}
export type InputObserverProps = Partial<typeof DefaultInputObserverProps>

export type ModifiersKeys = {
    shift: boolean,
    alt: boolean,
    control: boolean,
    meta: boolean
}
export namespace ModifiersKeys {
    export const None = create();

    export function areEqual(a: ModifiersKeys, b: ModifiersKeys) {
        return a.shift === b.shift && a.alt === b.alt && a.control === b.control && a.meta === b.meta;
    }

    export function create(modifierKeys: Partial<ModifiersKeys> = {}): ModifiersKeys {
        return {
            shift: !!modifierKeys.shift,
            alt: !!modifierKeys.alt,
            control: !!modifierKeys.control,
            meta: !!modifierKeys.meta
        }
    }
}

export type ButtonsType = BitFlags<ButtonsType.Flag>

export namespace ButtonsType {
    export const has: (btn: ButtonsType, f: Flag) => boolean = BitFlags.has
    export const create: (fs: Flag) => ButtonsType = BitFlags.create

    export const enum Flag {
        /** No button or un-initialized */
        None = 0x0,
        /** Primary button (usually left) */
        Primary = 0x1,
        /** Secondary button (usually right) */
        Secondary = 0x2,
        /** Auxilary button (usually middle or mouse wheel button)  */
        Auxilary = 0x4,
        /** 4th button (typically the "Browser Back" button) */
        Forth = 0x8,
        /** 5th button (typically the "Browser Forward" button) */
        Five = 0x10,
    }
}

type BaseInput = {
    buttons: ButtonsType
    modifiers: ModifiersKeys
}

export type DragInput = {
    x: number,
    y: number,
    dx: number,
    dy: number,
    pageX: number,
    pageY: number,
    isStart: boolean
} & BaseInput

export type WheelInput = {
    dx: number,
    dy: number,
    dz: number,
} & BaseInput

export type ClickInput = {
    x: number,
    y: number,
    pageX: number,
    pageY: number,
} & BaseInput

export type MoveInput = {
    x: number,
    y: number,
    pageX: number,
    pageY: number,
    inside: boolean,
} & BaseInput

export type PinchInput = {
    delta: number,
    fraction: number,
    distance: number,
    isStart: boolean
} & BaseInput

export type ResizeInput = {

}

const enum DraggingState {
    Stopped = 0,
    Started = 1,
    Moving = 2
}

type PointerEvent = {
    clientX: number
    clientY: number
    pageX: number
    pageY: number
}

interface InputObserver {
    noScroll: boolean
    noContextMenu: boolean

    readonly drag: Observable<DragInput>,
    // Equivalent to mouseUp and touchEnd
    readonly interactionEnd: Observable<undefined>,
    readonly wheel: Observable<WheelInput>,
    readonly pinch: Observable<PinchInput>,
    readonly click: Observable<ClickInput>,
    readonly move: Observable<MoveInput>,
    readonly leave: Observable<undefined>,
    readonly enter: Observable<undefined>,
    readonly resize: Observable<ResizeInput>,
    readonly modifiers: Observable<ModifiersKeys>

    dispose: () => void
}

function createEvents() {
    return {
        drag: new Subject<DragInput>(),
        interactionEnd: new Subject<undefined>(),
        click: new Subject<ClickInput>(),
        move: new Subject<MoveInput>(),
        wheel: new Subject<WheelInput>(),
        pinch: new Subject<PinchInput>(),
        resize: new Subject<ResizeInput>(),
        leave: new Subject<undefined>(),
        enter: new Subject<undefined>(),
        modifiers: new Subject<ModifiersKeys>(),
    }
}

namespace InputObserver {
    export function create(props: InputObserverProps = {}): InputObserver {
        const { noScroll, noContextMenu } = { ...DefaultInputObserverProps, ...props }
        return {
            noScroll,
            noContextMenu,

            ...createEvents(),

            dispose: noop
        }
    }

    export function fromElement(element: Element, props: InputObserverProps = {}): InputObserver {
        let { noScroll, noContextMenu, noPinchZoom } = { ...DefaultInputObserverProps, ...props }

        let lastTouchDistance = 0
        const pointerDown = Vec2.zero()
        const pointerStart = Vec2.zero()
        const pointerEnd = Vec2.zero()
        const pointerDelta = Vec2.zero()
        const rectSize = Vec2.zero()
        const modifierKeys: ModifiersKeys = {
            shift: false,
            alt: false,
            control: false,
            meta: false
        }

        function getModifierKeys(): ModifiersKeys {
            return { ...modifierKeys };
        }

        let dragging: DraggingState = DraggingState.Stopped
        let disposed = false
        let buttons = 0 as ButtonsType
        let isInside = false

        const events = createEvents()
        const { drag, interactionEnd, wheel, pinch, click, move, leave, enter, resize, modifiers } = events

        attach()

        return {
            get noScroll () { return noScroll },
            set noScroll (value: boolean) { noScroll = value },
            get noContextMenu () { return noContextMenu },
            set noContextMenu (value: boolean) { noContextMenu = value },

            ...events,

            dispose
        }

        function attach () {
            element.addEventListener('contextmenu', onContextMenu, false )

            element.addEventListener('wheel', onMouseWheel as any, false)
            element.addEventListener('mousedown', onMouseDown as any, false)
            // for dragging to work outside canvas bounds,
            // mouse move/up events have to be added to a parent, i.e. window
            window.addEventListener('mousemove', onMouseMove as any, false)
            window.addEventListener('mouseup', onMouseUp as any, false)

            element.addEventListener('mouseenter', onMouseEnter as any, false)
            element.addEventListener('mouseleave', onMouseLeave as any, false)

            element.addEventListener('touchstart', onTouchStart as any, false)
            element.addEventListener('touchmove', onTouchMove as any, false)
            element.addEventListener('touchend', onTouchEnd as any, false)

            element.addEventListener('blur', handleBlur)
            window.addEventListener('keyup', handleKeyUp as EventListener, false)
            window.addEventListener('keydown', handleKeyDown as EventListener, false)

            window.addEventListener('resize', onResize, false)
        }

        function dispose () {
            if (disposed) return
            disposed = true

            element.removeEventListener( 'contextmenu', onContextMenu, false )

            element.removeEventListener('wheel', onMouseWheel as any, false)
            element.removeEventListener('mousedown', onMouseDown as any, false)
            window.removeEventListener('mousemove', onMouseMove as any, false)
            window.removeEventListener('mouseup', onMouseUp as any, false)

            element.removeEventListener('mouseenter', onMouseEnter as any, false)
            element.removeEventListener('mouseleave', onMouseLeave as any, false)

            element.removeEventListener('touchstart', onTouchStart as any, false)
            element.removeEventListener('touchmove', onTouchMove as any, false)
            element.removeEventListener('touchend', onTouchEnd as any, false)

            element.removeEventListener('blur', handleBlur)
            window.removeEventListener('keyup', handleKeyUp as EventListener, false)
            window.removeEventListener('keydown', handleKeyDown as EventListener, false)

            window.removeEventListener('resize', onResize, false)<commit_msg>reset buttons and modifier keys state when browser window looses focus<commit_after>/**
 * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
 *
 * @author <NAME> <<EMAIL>>
 */

import { Subject, Observable } from 'rxjs';

import { Vec2 } from '../../mol-math/linear-algebra';

import { BitFlags, noop } from '../../mol-util';

export function getButtons(event: MouseEvent | Touch) {
    if (typeof event === 'object') {
        if ('buttons' in event) {
            return event.buttons
        } else if ('which' in event) {
            const b = (event as any).which  // 'any' to support older browsers
            if (b === 2) {
                return 4
            } else if (b === 3) {
                return 2
            } else if (b > 0) {
                return 1 << (b - 1)
            }
        } else if ('button' in event) {
            const b = (event as any).button  // 'any' to support older browsers
            if (b === 1) {
                return 4
            } else if (b === 2) {
                return 2
            } else if (b >= 0) {
                return 1 << b
            }
        }
    }
    return 0
}

export function getModifiers(event: MouseEvent | Touch) {
    return {
        alt: 'altKey' in event ? event.altKey : false,
        shift: 'shiftKey' in event ? event.shiftKey : false,
        control: 'ctrlKey' in event ? event.ctrlKey : false,
        meta: 'metaKey' in event ? event.metaKey : false
    }
}

export const DefaultInputObserverProps = {
    noScroll: true,
    noContextMenu: true,
    noPinchZoom: true
}
export type InputObserverProps = Partial<typeof DefaultInputObserverProps>

export type ModifiersKeys = {
    shift: boolean,
    alt: boolean,
    control: boolean,
    meta: boolean
}
export namespace ModifiersKeys {
    export const None = create();

    export function areEqual(a: ModifiersKeys, b: ModifiersKeys) {
        return a.shift === b.shift && a.alt === b.alt && a.control === b.control && a.meta === b.meta;
    }

    export function create(modifierKeys: Partial<ModifiersKeys> = {}): ModifiersKeys {
        return {
            shift: !!modifierKeys.shift,
            alt: !!modifierKeys.alt,
            control: !!modifierKeys.control,
            meta: !!modifierKeys.meta
        }
    }
}

export type ButtonsType = BitFlags<ButtonsType.Flag>

export namespace ButtonsType {
    export const has: (btn: ButtonsType, f: Flag) => boolean = BitFlags.has
    export const create: (fs: Flag) => ButtonsType = BitFlags.create

    export const enum Flag {
        /** No button or un-initialized */
        None = 0x0,
        /** Primary button (usually left) */
        Primary = 0x1,
        /** Secondary button (usually right) */
        Secondary = 0x2,
        /** Auxilary button (usually middle or mouse wheel button)  */
        Auxilary = 0x4,
        /** 4th button (typically the "Browser Back" button) */
        Forth = 0x8,
        /** 5th button (typically the "Browser Forward" button) */
        Five = 0x10,
    }
}

type BaseInput = {
    buttons: ButtonsType
    modifiers: ModifiersKeys
}

export type DragInput = {
    x: number,
    y: number,
    dx: number,
    dy: number,
    pageX: number,
    pageY: number,
    isStart: boolean
} & BaseInput

export type WheelInput = {
    dx: number,
    dy: number,
    dz: number,
} & BaseInput

export type ClickInput = {
    x: number,
    y: number,
    pageX: number,
    pageY: number,
} & BaseInput

export type MoveInput = {
    x: number,
    y: number,
    pageX: number,
    pageY: number,
    inside: boolean,
} & BaseInput

export type PinchInput = {
    delta: number,
    fraction: number,
    distance: number,
    isStart: boolean
} & BaseInput

export type ResizeInput = {

}

const enum DraggingState {
    Stopped = 0,
    Started = 1,
    Moving = 2
}

type PointerEvent = {
    clientX: number
    clientY: number
    pageX: number
    pageY: number
}

interface InputObserver {
    noScroll: boolean
    noContextMenu: boolean

    readonly drag: Observable<DragInput>,
    // Equivalent to mouseUp and touchEnd
    readonly interactionEnd: Observable<undefined>,
    readonly wheel: Observable<WheelInput>,
    readonly pinch: Observable<PinchInput>,
    readonly click: Observable<ClickInput>,
    readonly move: Observable<MoveInput>,
    readonly leave: Observable<undefined>,
    readonly enter: Observable<undefined>,
    readonly resize: Observable<ResizeInput>,
    readonly modifiers: Observable<ModifiersKeys>

    dispose: () => void
}

function createEvents() {
    return {
        drag: new Subject<DragInput>(),
        interactionEnd: new Subject<undefined>(),
        click: new Subject<ClickInput>(),
        move: new Subject<MoveInput>(),
        wheel: new Subject<WheelInput>(),
        pinch: new Subject<PinchInput>(),
        resize: new Subject<ResizeInput>(),
        leave: new Subject<undefined>(),
        enter: new Subject<undefined>(),
        modifiers: new Subject<ModifiersKeys>(),
    }
}

namespace InputObserver {
    export function create(props: InputObserverProps = {}): InputObserver {
        const { noScroll, noContextMenu } = { ...DefaultInputObserverProps, ...props }
        return {
            noScroll,
            noContextMenu,

            ...createEvents(),

            dispose: noop
        }
    }

    export function fromElement(element: Element, props: InputObserverProps = {}): InputObserver {
        let { noScroll, noContextMenu, noPinchZoom } = { ...DefaultInputObserverProps, ...props }

        let lastTouchDistance = 0
        const pointerDown = Vec2.zero()
        const pointerStart = Vec2.zero()
        const pointerEnd = Vec2.zero()
        const pointerDelta = Vec2.zero()
        const rectSize = Vec2.zero()
        const modifierKeys: ModifiersKeys = {
            shift: false,
            alt: false,
            control: false,
            meta: false
        }

        function getModifierKeys(): ModifiersKeys {
            return { ...modifierKeys };
        }

        let dragging: DraggingState = DraggingState.Stopped
        let disposed = false
        let buttons = 0 as ButtonsType
        let isInside = false

        const events = createEvents()
        const { drag, interactionEnd, wheel, pinch, click, move, leave, enter, resize, modifiers } = events

        attach()

        return {
            get noScroll () { return noScroll },
            set noScroll (value: boolean) { noScroll = value },
            get noContextMenu () { return noContextMenu },
            set noContextMenu (value: boolean) { noContextMenu = value },

            ...events,

            dispose
        }

        function attach () {
            element.addEventListener('contextmenu', onContextMenu, false )

            element.addEventListener('wheel', onMouseWheel as any, false)
            element.addEventListener('mousedown', onMouseDown as any, false)

            // for dragging to work outside canvas bounds,
            // mouse move/up events have to be added to a parent, i.e. window
            window.addEventListener('mousemove', onMouseMove as any, false)
            window.addEventListener('mouseup', onMouseUp as any, false)

            element.addEventListener('mouseenter', onMouseEnter as any, false)
            element.addEventListener('mouseleave', onMouseLeave as any, false)

            element.addEventListener('touchstart', onTouchStart as any, false)
            element.addEventListener('touchmove', onTouchMove as any, false)
            element.addEventListener('touchend', onTouchEnd as any, false)

            // reset buttons and modifier keys state when browser window looses focus
            window.addEventListener('blur', handleBlur)
            window.addEventListener('keyup', handleKeyUp as EventListener, false)
            window.addEventListener('keydown', handleKeyDown as EventListener, false)

            window.addEventListener('resize', onResize, false)
        }

        function dispose () {
            if (disposed) return
            disposed = true

            element.removeEventListener( 'contextmenu', onContextMenu, false )

            element.removeEventListener('wheel', onMouseWheel as any, false)
            element.removeEventListener('mousedown', onMouseDown as any, false)
            window.removeEventListener('mousemove', onMouseMove as any, false)
            window.removeEventListener('mouseup', onMouseUp as any, false)

            element.removeEventListener('mouseenter', onMouseEnter as any, false)
            element.removeEventListener('mouseleave', onMouseLeave as any, false)

            element.removeEventListener('touchstart', onTouchStart as any, false)
            element.removeEventListener('touchmove', onTouchMove as any, false)
            element.removeEventListener('touchend', onTouchEnd as any, false)

            window.removeEventListener('blur', handleBlur)
            window.removeEventListener('keyup', handleKeyUp as EventListener, false)
            window.removeEventListener('keydown', handleKeyDown as EventListener, false)

            window.removeEventListener('resize', onResize, false)
{'fraction_non_alphanumeric': 0.10220813292522955, 'fraction_numerical': 0.005247048535198951, 'mean_word_length': 2.3340014577259476, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '804973', 'n_tokens_mistral': 4984, 'n_tokens_neox': 4801, 'n_words': 1468}