U.K.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. //------------------键盘处理类
  2. Namespace.register("U.K");
  3. //#region 全局变量区域
  4. //变量来存储_MAP的翻转版从上面
  5. U.K._REVERSE_MAP;
  6. //特殊键码的映射到其对应的键
  7. U.K._MAP = {
  8. 8: 'backspace',
  9. 9: 'tab',
  10. 13: 'enter',
  11. 16: 'shift',
  12. 17: 'ctrl',
  13. 18: 'alt',
  14. 20: 'capslock',
  15. 27: 'esc',
  16. 32: 'space',
  17. 33: 'pageup',
  18. 34: 'pagedown',
  19. 35: 'end',
  20. 36: 'home',
  21. 37: 'left',
  22. 38: 'up',
  23. 39: 'right',
  24. 40: 'down',
  25. 45: 'ins',
  26. 46: 'del',
  27. 91: 'meta',
  28. 93: 'meta',
  29. 224: 'meta'
  30. };
  31. //特殊字符,使他们能够支持映射
  32. U.K._KEYCODE_MAP = {
  33. 106: '*',
  34. 107: '+',
  35. 109: '-',
  36. 110: '.',
  37. 111: '/',
  38. 186: ';',
  39. 187: '=',
  40. 188: ',',
  41. 189: '-',
  42. 190: '.',
  43. 191: '/',
  44. 192: '`',
  45. 219: '[',
  46. 220: '\\',
  47. 221: ']',
  48. 222: '\''
  49. };
  50. //这是需要一个美国小键盘上的shift键的映射
  51. U.K._SHIFT_MAP = {
  52. '~': '`',
  53. '!': '1',
  54. '@': '2',
  55. '#': '3',
  56. '$': '4',
  57. '%': '5',
  58. '^': '6',
  59. '&': '7',
  60. '*': '8',
  61. '(': '9',
  62. ')': '0',
  63. '_': '-',
  64. '+': '=',
  65. ':': ';',
  66. '\"': '\'',
  67. '<': ',',
  68. '>': '.',
  69. '?': '/',
  70. '|': '\\'
  71. };
  72. //这是你可以用它来绘制特殊字符串列表
  73. U.K._SPECIAL_ALIASES = {
  74. 'option': 'alt',
  75. 'command': 'meta',
  76. 'return': 'enter',
  77. 'escape': 'esc',
  78. 'plus': '+',
  79. 'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl'
  80. };
  81. U.K.start = function () {
  82. //遍历F键,F1至F19,并把它们添加到地图
  83. for (var i = 1; i < 20; ++i) {
  84. U.K._MAP[111 + i] = 'f' + i;
  85. }
  86. //数字键盘的数字添加
  87. for (i = 0; i <= 9; ++i) {
  88. U.K._MAP[i + 96] = i.toString();
  89. }
  90. }
  91. //#endregion
  92. //#region 方法使用区域
  93. //取事件,并返回的键值
  94. U.K._characterFromEvent = function (e) {
  95. //为按键事件,我们应该回到该字符是
  96. if (e.type == 'keypress') {
  97. var character = String.fromCharCode(e.which);
  98. // 如果没有然后按下Shift键它是安全的假设
  99. // 我们想要的字符是小写。这意味着,如果
  100. // 你不小心有大写锁定,然后键绑定
  101. // 将继续工作
  102. //
  103. // 可能是不希望的唯一副作用是,如果你
  104. // 绑定类似'A',因为你要触发
  105. // 事件被按下大写字母A时,大写锁定将不再
  106. // 触发事件。尽管绑定了shift+a。
  107. if (!e.shiftKey) {
  108. character = character.toLowerCase();
  109. }
  110. return character;
  111. }
  112. // 非按键事件所需的专用地图
  113. if (U.K._MAP[e.which]) {
  114. return U.K._MAP[e.which];
  115. }
  116. if (U.K._KEYCODE_MAP[e.which]) {
  117. return U.K._KEYCODE_MAP[e.which];
  118. }
  119. // 如果它不是在特殊的地图
  120. // 与KEYDOWN和KeyUp事件的性质似乎总
  121. // 进来作为一个大写字符无论您是按住Shift
  122. // 或不。我们应该确保它始终是小写的比较
  123. return String.fromCharCode(e.which).toLowerCase();
  124. }
  125. /**
  126. * 检查,如果两个数组相等,
  127. *
  128. * @param {Array} modifiers1
  129. * @param {Array} modifiers2
  130. * @returns {boolean}
  131. */
  132. U.K._modifiersMatch = function (modifiers1, modifiers2) {
  133. return modifiers1.sort().join(',') === modifiers2.sort().join(',');
  134. }
  135. /**
  136. * 需要一个关键事件,并计算出的修饰符是什么
  137. *
  138. * @param {Event} e
  139. * @returns {Array}
  140. */
  141. U.K._eventModifiers = function (e) {
  142. var modifiers = [];
  143. if (e.shiftKey) {
  144. modifiers.push('shift');
  145. }
  146. if (e.altKey) {
  147. modifiers.push('alt');
  148. }
  149. if (e.ctrlKey) {
  150. modifiers.push('ctrl');
  151. }
  152. if (e.metaKey) {
  153. modifiers.push('meta');
  154. }
  155. return modifiers;
  156. }
  157. /**
  158. * 确定指定的键码是一个修改键或不
  159. *
  160. * @param {string} key
  161. * @returns {boolean}
  162. */
  163. U.K._isModifier = function (key) {
  164. return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
  165. }
  166. /**
  167. * 颠倒了地图查找,这样我们可以寻找特定键
  168. *看看有什么可以和不能使用的按键
  169. *
  170. * @return {Object}
  171. */
  172. U.K._getReverseMap = function () {
  173. if (!_REVERSE_MAP) {
  174. _REVERSE_MAP = {};
  175. for (var key in U.K._MAP) {
  176. //从这里拉出数字小键盘按键的事业宜
  177. //能够从字符检测的按键
  178. if (key > 95 && key < 112) {
  179. continue;
  180. }
  181. if (U.K._MAP.hasOwnProperty(key)) {
  182. _REVERSE_MAP[U.K._MAP[key]] = key;
  183. }
  184. }
  185. }
  186. return _REVERSE_MAP;
  187. }
  188. /**
  189. * 挑选基础上,组合键最佳动作
  190. *
  191. * @param {string} key - character for key
  192. * @param {Array} modifiers
  193. * @param {string=} action passed in
  194. */
  195. U.K._pickBestAction = function (key, modifiers, action) {
  196. //如果没有动作,拿起我们应该尽量挑选一个
  197. //我们认为将工作最适合这一关键
  198. if (!action) {
  199. action = U.K._getReverseMap()[key] ? 'keydown' : 'keypress';
  200. }
  201. //与预期的按键组合键不起作用,
  202. //切换到KEYDOWN
  203. if (action == 'keypress' && modifiers.length) {
  204. action = 'keydown';
  205. }
  206. return action;
  207. }
  208. /**
  209. * *从一个字符串组合键转换到一个数组
  210. *
  211. * @param {string} 的组合,如 "command+shift+l"
  212. * @return {Array}
  213. */
  214. U.K._keysFromString = function (combination) {
  215. if (combination === '+') {
  216. return ['+'];
  217. }
  218. combination = combination.replace(/\+{2}/g, '+plus');
  219. return combination.split('+');
  220. }
  221. /**
  222. * Gets 获取信息的特定的组合键
  223. *
  224. * @param {string} combination key combination ("command+s" or "a" or "*")
  225. * @param {string=} action
  226. * @returns {Object}
  227. */
  228. U.K._getKeyInfo = function (combination, action) {
  229. var keys;
  230. var key;
  231. var i;
  232. var modifiers = [];
  233. // 从这种模式带钥匙,弄清实际
  234. // 模式是所有
  235. keys = U.K._keysFromString(combination);
  236. for (i = 0; i < keys.length; ++i) {
  237. key = keys[i];
  238. // normalize key names
  239. if (U.K._SPECIAL_ALIASES[key]) {
  240. key = U.K._SPECIAL_ALIASES[key];
  241. }
  242. //如果这不是一个按键事件那么我们应该
  243. //聪明地使用shift键
  244. //这只会为我们工作不过键盘
  245. if (action && action != 'keypress' && U.K._SHIFT_MAP[key]) {
  246. key = U.K._SHIFT_MAP[key];
  247. modifiers.push('shift');
  248. }
  249. //如果该键是一个修改,然后将其添加到修改器列表
  250. if (U.K._isModifier(key)) {
  251. modifiers.push(key);
  252. }
  253. }
  254. //根据密钥的组合是什么
  255. //我们会尽力挑选最好的事件它
  256. action = U.K._pickBestAction(key, modifiers, action);
  257. return {
  258. key: key,
  259. modifiers: modifiers,
  260. action: action
  261. };
  262. }
  263. //判断监听来源
  264. U.K._belongsTo = function (element, ancestor) {
  265. if (element === null || element === document) {
  266. return false;
  267. }
  268. if (element === ancestor) {
  269. return true;
  270. }
  271. return U.K._getReverseMap(element.parentNode, ancestor);
  272. }
  273. //#endregion
  274. //#region 对外接口区域
  275. //对外公布函数
  276. U.K.Mousetrap = function (targetElement) {
  277. var self = this;
  278. targetElement = targetElement || document;
  279. if (!(self instanceof U.K.Mousetrap)) {
  280. return new U.K.Mousetrap(targetElement);
  281. }
  282. /**
  283. * 元素附加关键事件
  284. *
  285. * @type {Element}
  286. */
  287. self.target = targetElement;
  288. /**
  289. * 通过Mousetrap.bind所有的回调设置列表()
  290. *
  291. * @type {Object}
  292. */
  293. self._callbacks = {};
  294. /**
  295. * 字符串组合的直接映射到用于触发回调()
  296. *
  297. * @type {Object}
  298. */
  299. self._directMap = {};
  300. /**
  301. * 跟踪什么级别的每个序列是因为多个
  302. *序列可以开始时具有相同的序列
  303. *
  304. * @type {Object}
  305. */
  306. var _sequenceLevels = {};
  307. /**
  308. * 变量来存储方法setTimeout
  309. *
  310. * @type {null|number}
  311. */
  312. var _resetTimer;
  313. /**
  314. * 临时的状态,我们会忽略下一个KEYUP
  315. *
  316. * @type {boolean|string}
  317. */
  318. var _ignoreNextKeyup = false;
  319. /**
  320. * 临时的状态,我们会忽略下一个按键
  321. *
  322. * @type {boolean}
  323. */
  324. var _ignoreNextKeypress = false;
  325. /**
  326. * 是我们目前的序列里面?
  327. * acticon(“KEYUP”或“的keydown”或“keypress”)或flase的类型
  328. *
  329. * @type {boolean|string}
  330. */
  331. var _nextExpectedAction = false;
  332. /**
  333. * 重置所有序列柜台,除了传递的那些
  334. *
  335. * @param {Object} doNotReset
  336. * @returns void
  337. */
  338. function _resetSequences(doNotReset) {
  339. doNotReset = doNotReset || {};
  340. var activeSequences = false,
  341. key;
  342. for (key in _sequenceLevels) {
  343. if (doNotReset[key]) {
  344. activeSequences = true;
  345. continue;
  346. }
  347. _sequenceLevels[key] = 0;
  348. }
  349. if (!activeSequences) {
  350. _nextExpectedAction = false;
  351. }
  352. }
  353. /**
  354. * *查找匹配基础上的keyCode,所有的callback,modifiers,action
  355. *
  356. * @param {string} character
  357. * @param {Array} modifiers
  358. * @param {Event|Object} e
  359. * @param {string=} sequenceName - name of the sequence we are looking for
  360. * @param {string=} combination
  361. * @param {number=} level
  362. * @returns {Array}
  363. */
  364. function _getMatches(character, modifiers, e, sequenceName, combination, level) {
  365. var i;
  366. var callback;
  367. var matches = [];
  368. var action = e.type;
  369. // 如果没有与此相关的键码的事件
  370. if (!self._callbacks[character]) {
  371. return [];
  372. }
  373. // 如果修改键快到了自身,我们应该允许它
  374. if (action == 'keyup' && U.K._isModifier(character)) {
  375. modifiers = [character];
  376. }
  377. // 通过被按下该键所有的回调循环
  378. //,看看其中是否匹配
  379. for (i = 0; i < self._callbacks[character].length; ++i) {
  380. callback = self._callbacks[character][i];
  381. //如果一个序列名未指定,但是这是一个序列的
  382. //错误的水平,那么移动到下一个match
  383. if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) {
  384. continue;
  385. }
  386. // //如果我们正在寻找的action不符合我们的action
  387. //那么我们就应该继续执行
  388. if (action != callback.action) {
  389. continue;
  390. }
  391. //如果这是一个keypress event和meta和control key
  392. //没有按下这意味着我们只需要看
  393. //字符,否则检查改性剂以及
  394. //
  395. // Chrome浏览器不会触发一个keypress如果meta or control is down
  396. // Safari会触发一个 keypress 如果 meta or meta+shift is down
  397. // Firefox会触发一个keypress 如果 meta or control is down
  398. if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || U.K._modifiersMatch(modifiers, callback.modifiers)) {
  399. //当你绑定的组合或序列的第二次它
  400. //应该覆盖的第一个。如果sequenceName或
  401. //组合在这个指定的调用它做到了这一点
  402. //
  403. //@todo使删除它自己的方法?
  404. var deleteCombo = !sequenceName && callback.combo == combination;
  405. var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level;
  406. if (deleteCombo || deleteSequence) {
  407. self._callbacks[character].splice(i, 1);
  408. }
  409. matches.push(callback);
  410. }
  411. }
  412. return matches;
  413. }
  414. /**
  415. * 实际调用回调函数
  416. *
  417. *
  418. *
  419. *如果您的回调函数返回false,这将使用jQuery的
  420. * convention - 防止违约和对事件停止传播史
  421. *
  422. * @param {Function} callback
  423. * @param {Event} e
  424. * @returns void
  425. */
  426. function _fireCallback(callback, e, combo, sequence) {
  427. // //如果此事件不应该发生到此为止
  428. if (self.stopCallback(e, e.target || e.srcElement, combo, sequence)) {
  429. return;
  430. }
  431. if (callback(e, combo) === false) {
  432. U.M.StopDefault(e);
  433. U.M.StopBubble(e);
  434. }
  435. }
  436. /**
  437. * 处理一个字符键事件
  438. *
  439. * @param {string} character
  440. * @param {Array} modifiers
  441. * @param {Event} e
  442. * @returns void
  443. */
  444. self._handleKey = function (character, modifiers, e) {
  445. var callbacks = _getMatches(character, modifiers, e);
  446. var i;
  447. var doNotReset = {};
  448. var maxLevel = 0;
  449. var processedSequenceCallback = false;
  450. // 计算maxLevel的序列,所以我们只能执行时间最长的回调序列
  451. for (i = 0; i < callbacks.length; ++i) {
  452. if (callbacks[i].seq) {
  453. maxLevel = Math.max(maxLevel, callbacks[i].level);
  454. }
  455. }
  456. //通过匹配回调回路这一关键事件
  457. for (i = 0; i < callbacks.length; ++i) {
  458. // 触发有序回调
  459. // 这是因为例如,如果您有多个序列
  460. // 结合诸如“G i”和“G t”,他们都需要触发
  461. // 回调匹配摹原因,否则你永远只能
  462. // 匹配到第一个
  463. if (callbacks[i].seq) {
  464. //只触发回调的maxLevel防止
  465. //
  466. //
  467. //例如 “a option b' 应该不会造成'option b' 触发
  468. //即使'option b'为其它序列的一部分
  469. //
  470. //这里不匹配任何序列都将被丢弃
  471. //下面的_resetSequences通话
  472. if (callbacks[i].level != maxLevel) {
  473. continue;
  474. }
  475. processedSequenceCallback = true;
  476. // //保留其中的序列是匹配为以后列表
  477. doNotReset[callbacks[i].seq] = 1;
  478. _fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq);
  479. continue;
  480. }
  481. // 如果没有序列相匹配,但我们还在这里
  482. // 这意味着这是一个普通的匹配,所以我们应该触发了
  483. if (!processedSequenceCallback) {
  484. _fireCallback(callbacks[i].callback, e, callbacks[i].combo);
  485. }
  486. }
  487. // if the key you pressed matches the type of sequence without
  488. // being a modifier (ie "keyup" or "keypress") then we should
  489. // reset all sequences that were not matched by this event
  490. //
  491. // this is so, for example, if you have the sequence "h a t" and you
  492. // type "h e a r t" it does not match. in this case the "e" will
  493. // cause the sequence to reset
  494. //
  495. // modifier keys are ignored because you can have a sequence
  496. // that contains modifiers such as "enter ctrl+space" and in most
  497. // cases the modifier key will be pressed before the next key
  498. //
  499. // also if you have a sequence such as "ctrl+b a" then pressing the
  500. // "b" key will trigger a "keypress" and a "keydown"
  501. //
  502. // the "keydown" is expected when there is a modifier, but the
  503. // "keypress" ends up matching the _nextExpectedAction since it occurs
  504. // after and that causes the sequence to reset
  505. //
  506. // we ignore keypresses in a sequence that directly follow a keydown
  507. // for the same character
  508. //如果你按下键序列无类型相匹配
  509. //是一个修改(即“KEYUP”或“按键”),那么,我们应该
  510. //重新设置那些没有此事件相匹配的所有序列
  511. //
  512. //是这样,例如,如果你有序列“哈t”和你
  513. //型“听到T”不匹配。在这种情况下,将“e”的意愿
  514. //导致序列复位
  515. //
  516. //修饰键被忽略,因为你可以有一个序列
  517. //包含修饰,如“输入Ctrl +空格”,并在最
  518. //情况下,修改键将在下键之前按下
  519. //
  520. //此外,如果你有一个序列,例如“CTRL + B A”,然后按
  521. //“b”的键将触发一个“按键”和一个“的keydown”
  522. //
  523. //在“的keydown”时,有一个改性剂预期,但
  524. //“按键”结束了,因为它发生_nextExpectedAction匹配
  525. //之后和使该序列重新设置
  526. //
  527. //我们忽略了一个顺序按键直接遵循的keydown
  528. //对于相同的字符
  529. var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress;
  530. if (e.type == _nextExpectedAction && !U.K._isModifier(character) && !ignoreThisKeypress) {
  531. _resetSequences(doNotReset);
  532. }
  533. _ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown';
  534. };
  535. /**
  536. *处理一个keydown事件
  537. *
  538. * @param {Event} e
  539. * @returns void
  540. */
  541. function _handleKeyEvent(e) {
  542. // 为正常化的关键事件e.which
  543. if (typeof e.which !== 'number') {
  544. e.which = e.keyCode;
  545. }
  546. var character = U.K._characterFromEvent(e);
  547. //没有字符发现后停止
  548. if (!character) {
  549. return;
  550. }
  551. //需要使用===用于字符检查,因为该字符可以是0
  552. if (e.type == 'keyup' && _ignoreNextKeyup === character) {
  553. _ignoreNextKeyup = false;
  554. return;
  555. }
  556. self.handleKey(character, U.K._eventModifiers(e), e);
  557. }
  558. /**
  559. * 称为设置1秒超时指定的顺序
  560. *
  561. *这是为了让序列中的每个按键后,你长为1秒
  562. *按下一个键,你必须重新开始之前
  563. *
  564. * @returns void
  565. */
  566. function _resetSequenceTimer() {
  567. clearTimeout(_resetTimer);
  568. _resetTimer = setTimeout(_resetSequences, 1000);
  569. }
  570. /**
  571. * 绑定一个键序列事件
  572. *
  573. * @param {string} combo - combo specified in bind call
  574. * @param {Array} keys
  575. * @param {Function} callback
  576. * @param {string=} action
  577. * @returns void
  578. */
  579. function _bindSequence(combo, keys, callback, action) {
  580. // 通过增加一个序列水平记录这个组合开始
  581. // 和level设定为0
  582. _sequenceLevels[combo] = 0;
  583. /**
  584. * *回调,以增加该序列的序列水平和复位
  585. * 处于活动状态的所有其他序列
  586. *
  587. * @param {string} nextAction
  588. * @returns {Function}
  589. */
  590. function _increaseSequence(nextAction) {
  591. return function () {
  592. _nextExpectedAction = nextAction;
  593. ++_sequenceLevels[combo];
  594. _resetSequenceTimer();
  595. };
  596. }
  597. /**
  598. * 包装指定的回调另一个函数内,以便
  599. *尽快此序列完成后重置所有序列计数器
  600. *
  601. * @param {Event} e
  602. * @returns void
  603. */
  604. function _callbackAndReset(e) {
  605. _fireCallback(callback, e, combo);
  606. //我们应该忽略下一个关键了,如果动作键不放
  607. //或按键。这是如此,如果你完成一个序列
  608. //松开按键的最后一个关键不会触发KEYUP
  609. if (action !== 'keyup') {
  610. _ignoreNextKeyup = U.K._characterFromEvent(e);
  611. }
  612. //怪异的竞争条件,如果一个序列与该键结束
  613. //另一序列始于
  614. setTimeout(_resetSequences, 10);
  615. }
  616. //通过按键一次,并结合适当的回调循环
  617. //功能。任何关键领导到最后,就应
  618. //增加的顺序。决赛之后,应该重置所有序列
  619. //
  620. //如果在原来的绑定调用指定的操作那么就会
  621. //在整个使用。否则,我们将通过动作的
  622. //下一个关键应该匹配。这允许顺序
  623. //混搭按键和KEYDOWN事件,这取决于
  624. //的是更好地适合于提供的密钥
  625. for (var i = 0; i < keys.length; ++i) {
  626. var isFinal = i + 1 === keys.length;
  627. var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || U.K._getKeyInfo(keys[i + 1]).action);
  628. _bindSingle(keys[i], wrappedCallback, action, combo, i);
  629. }
  630. }
  631. /**
  632. * 绑定一个键盘组合
  633. *
  634. * @param {string} combination
  635. * @param {Function} callback
  636. * @param {string=} action
  637. * @param {string=} sequenceName - name of sequence if part of sequence
  638. * @param {number=} level - what part of the sequence the command is
  639. * @returns void
  640. */
  641. function _bindSingle(combination, callback, action, sequenceName, level) {
  642. // //存储与Mousetrap.trigger使用直接映射参考
  643. self._directMap[combination + ':' + action] = callback;
  644. // 使连续务必多个空格成为一个空格
  645. combination = combination.replace(/\s+/g, ' ');
  646. var sequence = combination.split(' ');
  647. var info;
  648. //如果该模式是键的序列然后通过此方法运行
  649. //重新处理每个模式一键在同一时间
  650. if (sequence.length > 1) {
  651. _bindSequence(combination, sequence, callback, action);
  652. return;
  653. }
  654. info = U.K._getKeyInfo(combination, action);
  655. //确保初始化数组,如果这是第一次
  656. //回调增加了对这一关键
  657. self._callbacks[info.key] = self._callbacks[info.key] || [];
  658. //删除现有的比赛,如果有一个
  659. _getMatches(info.key, info.modifiers, { type: info.action }, sequenceName, combination, level);
  660. //后面添加此调用数组
  661. //如果它是一个序列把它在开始时
  662. //如果不把它在最后
  663. //
  664. //这是重要的,因为这些方式处理预期
  665. //序列的人是第一位的
  666. self._callbacks[info.key][sequenceName ? 'unshift' : 'push']({
  667. callback: callback,
  668. modifiers: info.modifiers,
  669. action: info.action,
  670. seq: sequenceName,
  671. level: level,
  672. combo: combination
  673. });
  674. }
  675. /**
  676. * 结合多种组合到同一个回调
  677. *
  678. * @param {Array} combinations
  679. * @param {Function} callback
  680. * @param {string|undefined} action
  681. * @returns void
  682. */
  683. self._bindMultiple = function (combinations, callback, action) {
  684. for (var i = 0; i < combinations.length; ++i) {
  685. _bindSingle(combinations[i], callback, action);
  686. }
  687. };
  688. // start!
  689. U.M.AddEvent(targetElement, 'keypress', _handleKeyEvent);
  690. U.M.AddEvent(targetElement, 'keydown', _handleKeyEvent);
  691. U.M.AddEvent(targetElement, 'keyup', _handleKeyEvent);
  692. }
  693. U.K.Mousetrap = function () { };
  694. /**
  695. * / **
  696. *绑定的事件捕鼠器
  697. *
  698. *可以是单一的键,以+分离键的组合,
  699. *键的阵列,或由空格分隔键序列
  700. *
  701. *请务必先列出的组合键,以确保
  702. *正确的密钥最终得到的约束(在模式的最后一个键)
  703. *
  704. * @param {string|Array} keys
  705. * @param {Function} callback
  706. * @param {string=} action - 'keypress', 'keydown', or 'keyup'
  707. * @returns void
  708. */
  709. U.K.Mousetrap.prototype.bind = function (keys, callback, action) {
  710. var self = this;
  711. keys = keys instanceof Array ? keys : [keys];
  712. self._bindMultiple.call(self, keys, callback, action);
  713. return self;
  714. };
  715. /**
  716. **
  717. *解除绑定的事件捕鼠器
  718. *
  719. *在解除绑定设置指定组合键的回调函数
  720. *到一个空的功能,并在删除相应的键
  721. * _directMap字典。
  722. *
  723. * TODO:其实从_callbacks词典中删除这个代替
  724. 结合一个空函数*
  725. *
  726. *在keycombo +操作必须是完全一样
  727. *它在绑定方法定义
  728. *
  729. * @param {string|Array} keys
  730. * @param {string} action
  731. * @returns void
  732. */
  733. U.K.Mousetrap.prototype.unbind = function (keys, action) {
  734. var self = this;
  735. return self.bind.call(self, keys, function () { }, action);
  736. };
  737. /**
  738. * *触发器已被绑定的事件
  739. *
  740. * @param {string} keys
  741. * @param {string=} action
  742. * @returns void
  743. */
  744. U.K.Mousetrap.prototype.trigger = function (keys, action) {
  745. var self = this;
  746. if (self._directMap[keys + ':' + action]) {
  747. self._directMap[keys + ':' + action]({}, keys);
  748. }
  749. return self;
  750. };
  751. /**
  752. * *重置库恢复到初始状态。这是非常有用的
  753. *如果您想清除出当前的键盘快捷键和绑定
  754. *新的 - 例如,如果您切换到另一页
  755. * @returns void
  756. */
  757. U.K.Mousetrap.prototype.reset = function () {
  758. var self = this;
  759. self._callbacks = {};
  760. self._directMap = {};
  761. return self;
  762. };
  763. /**
  764. * *我们应该发射了回调之前停止该事件
  765. *
  766. * @param {Event} e
  767. * @param {Element} element
  768. * @return {boolean}
  769. */
  770. U.K.Mousetrap.prototype.stopCallback = function (e, element) {
  771. var self = this;
  772. // if the element has the class "mousetrap" then no need to stop
  773. if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
  774. return false;
  775. }
  776. if (U.K._getReverseMap(element, self.target)) {
  777. return false;
  778. }
  779. // stop for input, select, and textarea
  780. return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable;
  781. };
  782. /**
  783. * 公开暴露_handleKey,因此它可以通过扩展覆盖
  784. */
  785. U.K.Mousetrap.prototype.handleKey = function () {
  786. var self = this;
  787. return self._handleKey.apply(self, arguments);
  788. };
  789. /**
  790. * 允许自定义键映射
  791. */
  792. U.K.Mousetrap.addKeycodes = function (object) {
  793. for (var key in object) {
  794. if (object.hasOwnProperty(key)) {
  795. U.K._MAP[key] = object[key];
  796. }
  797. }
  798. _REVERSE_MAP = null;
  799. };
  800. /**
  801. * *初始化全球捕鼠器功能
  802. *
  803. *需要使用此方法来允许在全球捕鼠器职能工作
  804. *现在的捕鼠器是一个构造函数。
  805. */
  806. U.K.Mousetrap.init = function () {
  807. var documentMousetrap = Mousetrap(document);
  808. for (var method in documentMousetrap) {
  809. if (method.charAt(0) !== '_') {
  810. Mousetrap[method] = (function (method) {
  811. return function () {
  812. return documentMousetrap[method].apply(documentMousetrap, arguments);
  813. };
  814. } (method));
  815. }
  816. }
  817. };
  818. //#endregion