keyboard-js

JavaScript Key Binding Library

Tento skript by neměl být instalován přímo. Jedná se o knihovnu, kterou by měly jiné skripty využívat pomocí meta příkazu // @require https://update.greatest.deepsurf.us/scripts/23404/149412/keyboard-js.js

  1. "use strict"
  2.  
  3. var Keyboard = (function () {
  4. function __Keyboard() {
  5. this.keys = {} // to record the pressed key
  6. this.register_list = {} // to record the registers(key combos)
  7. this.state = {} // to record every register matching condition. do you want to get this value?
  8. this.statistic = {} // the keypress statistic
  9. this.specialKeyString = {
  10. "altKey": "Alt",
  11. "ctrlKey": "Control",
  12. "metaKey": "Meta",
  13. "shiftKey": "Shift"
  14. }
  15. }
  16. __Keyboard.prototype.listen = function (keyDown, keyUp) {
  17. var option = this.option, element = document
  18. if (option.element && typeof option.element.addEventListener === 'function') {
  19. element = option.element
  20. }
  21. element.addEventListener('keydown', (function (event) {
  22. this.keydown(event, keyDown)
  23. }).bind(this), false)
  24. element.addEventListener('keyup', (function (event) {
  25. this.keyup(event, keyUp)
  26. }).bind(this), false)
  27. }
  28.  
  29. __Keyboard.prototype.unlisten = function () {
  30. // maybe you need callback?
  31. var option = this.option, element = document
  32. if (option.element && typeof option.element.removeEventListener === 'function') {
  33. element = option.element
  34. }
  35. element.removeEventListener('keydown', function () { })
  36. element.removeEventListener('keyup', function () { })
  37. }
  38.  
  39. __Keyboard.prototype.test = function (event) {
  40. return this.testRegisters(event)
  41. }
  42.  
  43. __Keyboard.prototype.testRegisters = function (event) {
  44. var register_list = this.register_list
  45. var register_names = Object.getOwnPropertySymbols(register_list)
  46. var testKeys = this.testKeys.bind(this)
  47. var state = {}
  48. for (var i = 0, len = register_names.length; i < len; i++) {
  49. var regName = register_names[i]
  50. var reg = register_list[regName]
  51. var keylist = reg[0]
  52. var callback = reg[1]
  53.  
  54. // hit the target
  55. if (testKeys(keylist)) {
  56. if (callback && typeof callback === 'function') {
  57. // TODO:
  58. // Need event object? or context?
  59. // var __wrapper_callback = (function () {
  60. // event.clearKeys = this.clearKeys.bind(this)
  61. // // inject the event(the last key) object
  62. // callback(event)
  63.  
  64. // // BUG:
  65. // // when use `alert` or `confirm`, the event(keyup) of the pressed key will lost.
  66. // // so, you will don't know the key is really pressed or not when you are back.
  67. // // here code just detects some special keys.
  68. // // SO DO NOT USE ALERT OR CONFIRM!
  69. // Array.prototype.map.call(Object.keys(this.specialKeyString), ((function (key) {
  70. // if (event[key]) this.keys[this.specialKeyString[key]] = true
  71. // }).bind(this)))
  72. // }).bind(this)
  73. // if (typeof window === 'object' && window.requestAnimationFrame)
  74. // window.requestAnimationFrame(__wrapper_callback)
  75. // else
  76. // setTimeout(__wrapper_callback, 16)
  77.  
  78. event.clearKeys = this.clearKeys.bind(this)
  79. callback(event)
  80. }
  81. state[regName] = true
  82. // if match successfully, return directly.
  83. return state
  84. }
  85. }
  86. return state
  87. }
  88.  
  89. // @param keylist Array(Array) [combo1, combo2, ...]
  90. __Keyboard.prototype.testKeys = function (keylist) {
  91. var result = [], state = false
  92. for (var i = 0, len = keylist.length; i < len; i++) {
  93. var combo = keylist[i]
  94. var allPressedkeys = Object.keys(this.keys)
  95. var nowPressedkeys = []
  96. var __state = 0 // no state. not true or false
  97.  
  98. // collect all pressed key now
  99. allPressedkeys.forEach((function (value, index) {
  100. if (this.keys[value]) nowPressedkeys.push(value)
  101. }).bind(this))
  102.  
  103. // DEBUG: print the pressing key message
  104. // console.log(allPressedkeys, this.keys)
  105. if (this.option.DEBUG === true) {
  106. var __printKey = nowPressedkeys.map(function (k, i) {
  107. if (k === " ") return "Space"
  108. else return k
  109. }).join(" ")
  110. console.log('[' + Date.now() + '] You hit key: %c' + __printKey, 'color: #ea4335; font-size: 16px')
  111. }
  112.  
  113. // compare nowPressedkeys and combo
  114. // console.log('compare: ', nowPressedkeys, combo)
  115. if (nowPressedkeys.length !== combo.length) {
  116. __state = false
  117. } else {
  118. for (var j = 0, len2 = combo.length; j < len2; j++) {
  119. if (nowPressedkeys.indexOf(combo[j]) < 0) {
  120. // not in the array
  121. __state = false
  122. break
  123. }
  124. }
  125. // if j is equal to combo.length, this means that user hit the combo.
  126. // otherwise, user does't.
  127. if (j === combo.length && __state !== false) __state = true
  128. }
  129. result.push(__state)
  130. }
  131. // console.log('> result', result, this.keys)
  132. result.forEach(function (v, i) {
  133. if (v === true) state = true
  134. })
  135. return state
  136. }
  137.  
  138. __Keyboard.prototype.keydown = function (event, keyDownCallback) {
  139. var key = event.key, state = {}, rlt = true, map = Array.prototype.map
  140. this.keys[key] = event.type === 'keydown'
  141. // this.keys[key] = true
  142. // the result of test
  143. // true: hit the target, then prevent the default action, so return true
  144. // otherwise, don't prevent it, so return false
  145. state = this.test(event)
  146. Object.keys(state).forEach(function (regName, i) {
  147. if (state[regName] === true) rlt = false
  148. })
  149. this.state = state
  150. if (!rlt) {
  151. event.preventDefault()
  152. event.stopPropagation()
  153. // event.stopImmediatePropagation()
  154. }
  155. // console.log(rlt)
  156. // statistic
  157. this.collect(event.key, event.type, event.timeStamp)
  158. if (typeof keyDownCallback === 'function') keyDownCallback(event)
  159. return rlt
  160. }
  161.  
  162. __Keyboard.prototype.keyup = function (event, keyUpCallback) {
  163. var key = event.key
  164. this.keys[key] = false
  165. // statistic
  166. this.collect(event.key, event.type, event.timeStamp)
  167. if (typeof keyUpCallback === 'function') keyUpCallback(event)
  168. return true
  169. }
  170.  
  171. __Keyboard.prototype.collect = function (key, type, timeStamp) {
  172. // lazy calculate
  173. var target = this.statistic[key]
  174. var _timeStamp = !!window.CustomEvent ? new CustomEvent('test').timeStamp : document.createEvent('KeyboardEvent').timeStamp
  175. if (typeof target === 'undefined')
  176. target = this.statistic[key] = {count: 0, total: 0, average: 0}
  177. if (type === 'keydown') {
  178. target.downTimeStamp = timeStamp || _timeStamp
  179. } else if (type === 'keyup') {
  180. target.count = target.count + 1
  181. target.upTimeStamp = timeStamp || _timeStamp
  182. target.total = (target.upTimeStamp - target.downTimeStamp) + target.total
  183. target.total = +target.total.toFixed(2) || 0 // if incorrect, set 0
  184. target.average = target.total / target.count
  185. }
  186. }
  187.  
  188. __Keyboard.prototype.register = function (name, callback/*, keylist*/) {
  189. if (typeof name !== 'string') throw new Error('[from keyboard-js] Please input a register name.')
  190. var sym
  191. if (typeof Symbol !== 'undefined') sym = Symbol.for(name)
  192. else sym = name
  193. if (this.register_list[sym]) throw new Error('[from keyboard-js] The Register[' + name + '] has existed!')
  194. var keylist = Array.prototype.slice.call(arguments, 2)
  195. if (!(keylist[0] instanceof Array)) keylist = [keylist] // init [combo1:Array, combo2:Array, ....]
  196. this.register_list[sym] = [keylist, callback]
  197. }
  198.  
  199. __Keyboard.prototype.clearRegister = function (name) {
  200. delete this.register_list[name]
  201. }
  202. __Keyboard.prototype.clearRegisterAll = function () {
  203. this.register_list = {}
  204. }
  205. __Keyboard.prototype.clearKeys = function () {
  206. this.keys = {}
  207. }
  208. var k = new __Keyboard()
  209.  
  210. var __instance = {
  211. start: function (keyDown, keyUp) { k.listen(keyDown, keyUp) },
  212. end: function () { k.unlisten(); k.clearRegisterAll(); k.clearKeys(); },
  213. register: function () { k.register.apply(k, arguments) },
  214. unregister: function () { k.clearRegister.apply(k, arguments) },
  215. getStatistic: function () { return k.statistic },
  216. // for test
  217. __keydown: function () { k.keydown.apply(k, arguments) },
  218. __keyup: function () { k.keyup.apply(k, arguments) }
  219. }
  220.  
  221. return function (o) {
  222. k.option = o || {}
  223. if (typeof window === 'object') window.addEventListener('focus', function () {
  224. k.keys = {}
  225. }, false)
  226. // window.addEventListener('blur', function () {
  227. // k.keys = {}
  228. // }, false)
  229. return __instance
  230. }
  231. })()
  232.  
  233. if (typeof exports !== "undefined") {
  234. exports.Keyboard = Keyboard
  235. } else if (typeof define === 'function') {
  236. define("Keyboard", [], function () {
  237. return Keyboard
  238. })
  239. } else {
  240. if (window.Keyboard === undefined) window.Keyboard = Keyboard
  241. else {
  242. throw new Error('Library Keyboard has existed! Loaded failed.')
  243. }
  244. }