keyboard-js

JavaScript Key Binding Library

目前為 2016-09-22 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.greasyfork.org/scripts/23404/148849/keyboard-js.js

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

"use strict"
var Keyboard = (function () {
    function __Keyboard() {
        this.keys = {} // to record the pressed key
        this.register_list = {} // to record the registers(key combos)
        this.state = {} // to record every register matching condition. do you want to get this value?
        this.specialKeyString = {
            "altKey": "Alt",
            "ctrlKey": "Control",
            "metaKey": "Meta",
            "shiftKey": "Shift"
        }
    }
    __Keyboard.prototype.listen = function () {
        var option = this.option, element = document
        if (option.element && typeof option.element.addEventListener === 'function') {
            element = option.element
        }
        element.addEventListener('keydown', this.keydown.bind(this), false)
        element.addEventListener('keyup', this.keyup.bind(this), false)
    }

    __Keyboard.prototype.unlisten = function () {
        // maybe you need callback?
        var option = this.option, element = document
        if (option.element && typeof option.element.removeEventListener === 'function') {
            element = option.element
        }
        element.removeEventListener('keydown', function () { })
        element.removeEventListener('keyup', function () { })
    }

    __Keyboard.prototype.test = function (event) {
        return this.testRegisters(event)
    }

    __Keyboard.prototype.testRegisters = function (event) {
        var register_list = this.register_list
        var register_names = Object.keys(register_list)
        var testKeys = this.testKeys.bind(this)
        var state = {}
        for (var i = 0, len = register_names.length; i < len; i++) {
            var regName = register_names[i]
            var reg = register_list[regName]
            var keylist = reg[0]
            var callback = reg[1]

            // hit the target
            if (testKeys(keylist)) {
                if (callback && typeof callback === 'function') {
                    // TODO:
                    // Need event object? or context?
                    var __wrapper_callback = (function () {
                        event.clearKeys = this.clearKeys.bind(this)
                        // inject the event(the last key) object
                        callback(event)

                        // BUG:
                        // when use `alert` or `confirm`, the event(keyup) of the pressed key will lost.
                        // so, you will don't know the key is really pressed or not when you are back.
                        // here code just detects some special keys.
                        // SO DO NOT USE ALERT OR CONFIRM!
                        Array.prototype.map.call(Object.keys(this.specialKeyString), ((function (key) {
                            if (event[key]) this.keys[this.specialKeyString[key]] = true
                        }).bind(this)))
                    }).bind(this)

                    if (window.requestAnimationFrame)
                        window.requestAnimationFrame(__wrapper_callback)
                    else
                        setTimeout(__wrapper_callback, 16)
                }
                state[regName] = true
                // if match successfully, return directly.
                return state
            }
        }
        return state
    }

    // @param keylist Array(Array) [combo1, combo2, ...]
    __Keyboard.prototype.testKeys = function (keylist) {
        var result = [], state = false
        for (var i = 0, len = keylist.length; i < len; i++) {
            var combo = keylist[i]
            var allPressedkeys = Object.keys(this.keys)
            var nowPressedkeys = []
            var __state = 0 // no state. not true or false

            // collect all pressed key now
            allPressedkeys.forEach((function (value, index) {
                if (this.keys[value]) nowPressedkeys.push(value)
            }).bind(this))

            // DEBUG: print the pressing key message
            // console.log(allPressedkeys, this.keys)
            if (this.option.DEBUG === true) {
                var __printKey = nowPressedkeys.map(function (k, i) {
                    if (k === " ") return "Space"
                    else return k
                }).join(" ")
                console.log('[' + Date.now() + '] You hit key: %c' + __printKey, 'color: #ea4335; font-size: 16px')
            }

            // compare nowPressedkeys and combo
            // console.log('compare: ', nowPressedkeys, combo)
            if (nowPressedkeys.length !== combo.length) {
                __state = false
            } else {
                for (var j = 0, len2 = combo.length; j < len2; j++) {
                    if (nowPressedkeys.indexOf(combo[j]) < 0) {
                        // not in the array
                        __state = false
                        break
                    }
                }
                // if j is equal to combo.length, this means that user hit the combo.
                // otherwise, user does't.
                if (j === combo.length && __state !== false) __state = true
            }
            result.push(__state)
        }
        // console.log('> result', result, this.keys)
        result.forEach(function (v, i) {
            if (v === true) state = true
        })
        return state
    }

    __Keyboard.prototype.keydown = function (event) {
        var key = event.key, state = {}, rlt = true, map = Array.prototype.map
        this.keys[key] = event.type === 'keydown'
        // this.keys[key] = true
        // the result of test
        // true: hit the target, then prevent the default action, so return true
        // otherwise, don't prevent it, so return false
        state = this.test(event)
        Object.keys(state).forEach(function (regName, i) {
            if (state[regName] === true) rlt = false
        })
        this.state = state
        if (!rlt) {
            event.preventDefault()
            event.stopPropagation()
            // event.stopImmediatePropagation()
        }
        // console.log(rlt)
        return rlt
    }

    __Keyboard.prototype.keyup = function (event) {
        var key = event.key
        this.keys[key] = false
        return true
    }

    __Keyboard.prototype.register = function (name, callback, keylist) {
        if (typeof name !== 'string') throw new Error('Please input the register name.')
        if (this.register_list[name]) throw new Error('The ' + name + ' has existed!')
        var keylist = Array.prototype.slice.call(arguments, 2)
        if (!(keylist[0] instanceof Array)) keylist = [keylist] // init [combo1:Array, combo2:Array, ....]
        this.register_list[name] = [keylist, callback]
    }

    __Keyboard.prototype.clearRegister = function (name) {
        delete this.register_list[name]
    }
    __Keyboard.prototype.clearRegisterAll = function () {
        this.register_list = {}
    }
    __Keyboard.prototype.clearKeys = function () {
        this.keys = {}
    }
    var k = new __Keyboard()

    var __instance = {
        start: function () { k.listen() },
        end: function () { k.unlisten(); k.clearRegisterAll(); k.clearKeys(); },
        register: function () { k.register.apply(k, arguments) },
        unregister: function () { k.clearRegister.apply(k, arguments) },
        // for test
        __keydown: function () { k.keydown.apply(k, arguments) },
        __keyup: function () { k.keyup.apply(k, arguments) },
    }

    return function (o) {
        k.option = o || {}
        window.addEventListener('focus', function () {
            k.keys = {}
        }, false)
        // window.addEventListener('blur', function () {
        //     k.keys = {}
        // }, false)
        return __instance
    }
})()

if (typeof exports !== "undefined") {
    exports.Keyboard = Keyboard
} else if (typeof define !== 'undefined' && typeof define === 'function') {
    define("Keyboard", [], function () {
        return Keyboard
    })
} else {
    if (window.Keyboard === undefined) window.Keyboard = Keyboard
    else {
        throw new Error('Library Keyboard has existed! Loaded failed.')
    }
}