hotbox.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. /*!
  2. * ====================================================
  3. * Hot Box UI - v1.0.15 - 2017-05-05
  4. * https://github.com/fex-team/hotbox
  5. * GitHub: https://github.com/fex-team/hotbox.git
  6. * Copyright (c) 2017 Baidu FEX; Licensed BSD
  7. * ====================================================
  8. */
  9. (function () {
  10. var _p = {
  11. r: function(index) {
  12. if (_p[index].inited) {
  13. return _p[index].value;
  14. }
  15. if (typeof _p[index].value === "function") {
  16. var module = {
  17. exports: {}
  18. }, returnValue = _p[index].value(null, module.exports, module);
  19. _p[index].inited = true;
  20. _p[index].value = returnValue;
  21. if (returnValue !== undefined) {
  22. return returnValue;
  23. } else {
  24. for (var key in module.exports) {
  25. if (module.exports.hasOwnProperty(key)) {
  26. _p[index].inited = true;
  27. _p[index].value = module.exports;
  28. return module.exports;
  29. }
  30. }
  31. }
  32. } else {
  33. _p[index].inited = true;
  34. return _p[index].value;
  35. }
  36. }
  37. };
  38. //src/expose.js
  39. _p[0] = {
  40. value: function(require, exports, module) {
  41. module.exports = window.HotBox = _p.r(1);
  42. }
  43. };
  44. //src/hotbox.js
  45. _p[1] = {
  46. value: function(require, exports, module) {
  47. var key = _p.r(2);
  48. var KeyControl = _p.r(3);
  49. /**** Dom Utils ****/
  50. function createElement(name) {
  51. return document.createElement(name);
  52. }
  53. function setElementAttribute(element, name, value) {
  54. element.setAttribute(name, value);
  55. }
  56. function getElementAttribute(element, name) {
  57. return element.getAttribute(name);
  58. }
  59. function addElementClass(element, name) {
  60. element.classList.add(name);
  61. }
  62. function removeElementClass(element, name) {
  63. element.classList.remove(name);
  64. }
  65. function appendChild(parent, child) {
  66. parent.appendChild(child);
  67. }
  68. /*******************/
  69. var IDLE = HotBox.STATE_IDLE = "idle";
  70. var div = "div";
  71. /**
  72. * Simple Formatter
  73. */
  74. function format(template, args) {
  75. if (typeof args != "object") {
  76. args = [].slice.apply(arguments, 1);
  77. }
  78. return String(template).replace(/\{(\w+)\}/g, function(match, name) {
  79. return args[name] || match;
  80. });
  81. }
  82. /**
  83. * Hot Box Class
  84. */
  85. function HotBox($container) {
  86. if (typeof $container == "string") {
  87. $container = document.querySelector($container);
  88. }
  89. if (!$container || !($container instanceof HTMLElement)) {
  90. throw new Error("No container or not invalid container for hot box");
  91. }
  92. // 创建 HotBox Dom 解构
  93. var $hotBox = createElement(div);
  94. addElementClass($hotBox, "hotbox");
  95. appendChild($container, $hotBox);
  96. // 保存 Dom 解构和父容器
  97. this.$element = $hotBox;
  98. this.$container = $container;
  99. // 标示是否是输入法状态
  100. this.isIME = false;
  101. /**
  102. * @Desc: 增加一个browser用于判断浏览器类型,方便解决兼容性问题
  103. * @Editor: Naixor
  104. * @Date: 2015.09.14
  105. */
  106. this.browser = {
  107. sg: /se[\s\S]+metasr/.test(navigator.userAgent.toLowerCase())
  108. };
  109. /*
  110. * added by zhangbobell
  111. * 2015.09.22
  112. * 增加父状态机,以解决在父 FSM 下状态控制的问题,最好的解决办法是增加一个函数队列
  113. * 将其中的函数一起执行。//TODO
  114. * */
  115. this._parentFSM = {};
  116. // 记录位置
  117. this.position = {};
  118. // 已定义的状态(string => HotBoxState)
  119. var _states = {};
  120. // 主状态(HotBoxState)
  121. var _mainState = null;
  122. // 当前状态(HotBoxState)
  123. var _currentState = IDLE;
  124. // 当前状态堆栈
  125. var _stateStack = [];
  126. // 实例引用
  127. var _this = this;
  128. var _controler;
  129. /**
  130. * Controller: {
  131. * constructor(hotbox: HotBox),
  132. * active: () => void
  133. * }
  134. */
  135. function _control(Controller) {
  136. if (_controler) {
  137. _controler.active();
  138. return;
  139. }
  140. Controller = Controller || KeyControl;
  141. _controler = new Controller(_this);
  142. _controler.active();
  143. $hotBox.onmousedown = function(e) {
  144. e.stopPropagation();
  145. e.preventDefault();
  146. };
  147. return _this;
  148. }
  149. function _dispatchKey(e) {
  150. var type = e.type.toLowerCase();
  151. e.keyHash = key.hash(e);
  152. e.isKey = function(keyExpression) {
  153. if (!keyExpression) return false;
  154. var expressions = keyExpression.split(/\s*\|\s*/);
  155. while (expressions.length) {
  156. if (e.keyHash == key.hash(expressions.shift())) return true;
  157. }
  158. return false;
  159. };
  160. e[type] = true;
  161. // Boot: keyup and activeKey pressed on IDLE, active main state.
  162. if (e.keyup && _this.activeKey && e.isKey(_this.activeKey) && _currentState == IDLE && _mainState) {
  163. _activeState("main", {
  164. x: $container.clientWidth / 2,
  165. y: $container.clientHeight / 2
  166. });
  167. return;
  168. }
  169. var handleState = _currentState == IDLE ? _mainState : _currentState;
  170. if (handleState) {
  171. var handleResult = handleState.handleKeyEvent(e);
  172. if (typeof _this.onkeyevent == "function") {
  173. e.handleResult = handleResult;
  174. _this.onkeyevent(e, handleResult);
  175. }
  176. return handleResult;
  177. }
  178. return null;
  179. }
  180. function _addState(name) {
  181. if (!name) return _currentState;
  182. if (name == IDLE) {
  183. throw new Error("Can not define or use the `idle` state.");
  184. }
  185. _states[name] = _states[name] || new HotBoxState(this, name);
  186. if (name == "main") {
  187. _mainState = _states[name];
  188. }
  189. return _states[name];
  190. }
  191. function _activeState(name, position) {
  192. _this.position = position;
  193. // 回到 IDLE
  194. if (name == IDLE) {
  195. if (_currentState != IDLE) {
  196. _stateStack.shift().deactive();
  197. _stateStack = [];
  198. }
  199. _currentState = IDLE;
  200. } else if (name == "back") {
  201. if (_currentState != IDLE) {
  202. _currentState.deactive();
  203. _stateStack.shift();
  204. _currentState = _stateStack[0];
  205. if (_currentState) {
  206. _currentState.active();
  207. } else {
  208. _currentState = "idle";
  209. }
  210. }
  211. } else {
  212. if (_currentState != IDLE) {
  213. _currentState.deactive();
  214. }
  215. var newState = _states[name];
  216. _stateStack.unshift(newState);
  217. if (typeof _this.position == "function") {
  218. position = _this.position(position);
  219. }
  220. newState.active(position);
  221. _currentState = newState;
  222. }
  223. }
  224. function setParentFSM(fsm) {
  225. _this._parentFSM = fsm;
  226. }
  227. function getParentFSM() {
  228. return _this._parentFSM;
  229. }
  230. this.control = _control;
  231. this.state = _addState;
  232. this.active = _activeState;
  233. this.dispatch = _dispatchKey;
  234. this.setParentFSM = setParentFSM;
  235. this.getParentFSM = getParentFSM;
  236. this.activeKey = "space";
  237. this.actionKey = "space";
  238. }
  239. /**
  240. * 表示热盒某个状态,包含这些状态需要的 Dom 对象
  241. */
  242. function HotBoxState(hotBox, stateName) {
  243. var BUTTON_SELECTED_CLASS = "selected";
  244. var BUTTON_PRESSED_CLASS = "pressed";
  245. var STATE_ACTIVE_CLASS = "active";
  246. // 状态容器
  247. var $state = createElement(div);
  248. // 四种可见的按钮容器
  249. var $center = createElement(div);
  250. var $ring = createElement(div);
  251. var $ringShape = createElement("div");
  252. var $top = createElement(div);
  253. var $bottom = createElement(div);
  254. // 添加 CSS 类
  255. addElementClass($state, "state");
  256. addElementClass($state, stateName);
  257. addElementClass($center, "center");
  258. addElementClass($ring, "ring");
  259. addElementClass($ringShape, "ring-shape");
  260. addElementClass($top, "top");
  261. addElementClass($bottom, "bottom");
  262. // 摆放容器
  263. appendChild(hotBox.$element, $state);
  264. appendChild($state, $ringShape);
  265. appendChild($state, $center);
  266. appendChild($state, $ring);
  267. appendChild($state, $top);
  268. appendChild($state, $bottom);
  269. // 记住状态名称
  270. this.name = stateName;
  271. // 五种按钮:中心,圆环,上栏,下栏,幕后
  272. var buttons = {
  273. center: null,
  274. ring: [],
  275. top: [],
  276. bottom: [],
  277. behind: []
  278. };
  279. var allButtons = [];
  280. var selectedButton = null;
  281. var pressedButton = null;
  282. var stateActived = false;
  283. // 布局,添加按钮后,标记需要布局
  284. var needLayout = true;
  285. function layout() {
  286. var radius = buttons.ring.length * 15;
  287. layoutRing(radius);
  288. layoutTop(radius);
  289. layoutBottom(radius);
  290. indexPosition();
  291. needLayout = false;
  292. function layoutRing(radius) {
  293. var ring = buttons.ring;
  294. var step = 2 * Math.PI / ring.length;
  295. if (buttons.center) {
  296. buttons.center.indexedPosition = [ 0, 0 ];
  297. }
  298. $ringShape.style.marginLeft = $ringShape.style.marginTop = -radius + "px";
  299. $ringShape.style.width = $ringShape.style.height = radius + radius + "px";
  300. var $button, angle, x, y;
  301. for (var i = 0; i < ring.length; i++) {
  302. $button = ring[i].$button;
  303. angle = step * i - Math.PI / 2;
  304. x = radius * Math.cos(angle);
  305. y = radius * Math.sin(angle);
  306. ring[i].indexedPosition = [ x, y ];
  307. $button.style.left = x + "px";
  308. $button.style.top = y + "px";
  309. }
  310. }
  311. function layoutTop(radius) {
  312. var xOffset = -$top.clientWidth / 2;
  313. var yOffset = -radius * 2 - $top.clientHeight / 2;
  314. $top.style.marginLeft = xOffset + "px";
  315. $top.style.marginTop = yOffset + "px";
  316. buttons.top.forEach(function(topButton) {
  317. var $button = topButton.$button;
  318. topButton.indexedPosition = [ xOffset + $button.offsetLeft + $button.clientWidth / 2, yOffset ];
  319. });
  320. }
  321. function layoutBottom(radius) {
  322. var xOffset = -$bottom.clientWidth / 2;
  323. var yOffset = radius * 2 - $bottom.clientHeight / 2;
  324. $bottom.style.marginLeft = xOffset + "px";
  325. $bottom.style.marginTop = yOffset + "px";
  326. buttons.bottom.forEach(function(bottomButton) {
  327. var $button = bottomButton.$button;
  328. bottomButton.indexedPosition = [ xOffset + $button.offsetLeft + $button.clientWidth / 2, yOffset ];
  329. });
  330. }
  331. function indexPosition() {
  332. var positionedButtons = allButtons.filter(function(button) {
  333. return button.indexedPosition;
  334. });
  335. positionedButtons.forEach(findNeightbour);
  336. function findNeightbour(button) {
  337. var neighbor = {};
  338. var coef = 0;
  339. var minCoef = {};
  340. var homePosition = button.indexedPosition;
  341. var candidatePosition, dx, dy, ds;
  342. var possible, dir;
  343. var abs = Math.abs;
  344. positionedButtons.forEach(function(candidate) {
  345. if (button == candidate) return;
  346. candidatePosition = candidate.indexedPosition;
  347. possible = [];
  348. dx = candidatePosition[0] - homePosition[0];
  349. dy = candidatePosition[1] - homePosition[1];
  350. ds = Math.sqrt(dx * dx + dy * dy);
  351. if (abs(dx) > 2) {
  352. possible.push(dx > 0 ? "right" : "left");
  353. possible.push(ds + abs(dy));
  354. }
  355. if (abs(dy) > 2) {
  356. possible.push(dy > 0 ? "down" : "up");
  357. possible.push(ds + abs(dx));
  358. }
  359. while (possible.length) {
  360. dir = possible.shift();
  361. coef = possible.shift();
  362. if (!neighbor[dir] || coef < minCoef[dir]) {
  363. neighbor[dir] = candidate;
  364. minCoef[dir] = coef;
  365. }
  366. }
  367. });
  368. button.neighbor = neighbor;
  369. }
  370. }
  371. }
  372. function alwaysEnable() {
  373. return true;
  374. }
  375. // 为状态创建按钮
  376. function createButton(option) {
  377. var $button = createElement(div);
  378. addElementClass($button, "button");
  379. var render = option.render || defaultButtonRender;
  380. $button.innerHTML = render(format, option);
  381. switch (option.position) {
  382. case "center":
  383. appendChild($center, $button);
  384. break;
  385. case "ring":
  386. appendChild($ring, $button);
  387. break;
  388. case "top":
  389. appendChild($top, $button);
  390. break;
  391. case "bottom":
  392. appendChild($bottom, $button);
  393. break;
  394. }
  395. return {
  396. action: option.action,
  397. enable: option.enable || alwaysEnable,
  398. beforeShow: option.beforeShow,
  399. key: option.key,
  400. next: option.next,
  401. label: option.label,
  402. data: option.data || null,
  403. $button: $button
  404. };
  405. }
  406. // 默认按钮渲染
  407. function defaultButtonRender(format, option) {
  408. return format('<span class="label">{label}</span><span class="key">{key}</span>', {
  409. label: option.label,
  410. key: option.key && option.key.split("|")[0]
  411. });
  412. }
  413. // 为当前状态添加按钮
  414. this.button = function(option) {
  415. var button = createButton(option);
  416. if (option.position == "center") {
  417. buttons.center = button;
  418. } else if (buttons[option.position]) {
  419. buttons[option.position].push(button);
  420. }
  421. allButtons.push(button);
  422. needLayout = true;
  423. };
  424. function activeState(position) {
  425. position = position || {
  426. x: hotBox.$container.clientWidth / 2,
  427. y: hotBox.$container.clientHeight / 2
  428. };
  429. if (position) {
  430. $state.style.left = position.x + "px";
  431. $state.style.top = position.y + "px";
  432. }
  433. allButtons.forEach(function(button) {
  434. var $button = button.$button;
  435. if ($button) {
  436. $button.classList[button.enable() ? "add" : "remove"]("enabled");
  437. }
  438. if (button.beforeShow) {
  439. button.beforeShow();
  440. }
  441. });
  442. addElementClass($state, STATE_ACTIVE_CLASS);
  443. if (needLayout) {
  444. layout();
  445. }
  446. if (!selectedButton) {
  447. select(buttons.center || buttons.ring[0] || buttons.top[0] || buttons.bottom[0]);
  448. }
  449. stateActived = true;
  450. }
  451. function deactiveState() {
  452. removeElementClass($state, STATE_ACTIVE_CLASS);
  453. select(null);
  454. stateActived = false;
  455. }
  456. // 激活当前状态
  457. this.active = activeState;
  458. // 反激活当前状态
  459. this.deactive = deactiveState;
  460. function press(button) {
  461. if (pressedButton && pressedButton.$button) {
  462. removeElementClass(pressedButton.$button, BUTTON_PRESSED_CLASS);
  463. }
  464. pressedButton = button;
  465. if (pressedButton && pressedButton.$button) {
  466. addElementClass(pressedButton.$button, BUTTON_PRESSED_CLASS);
  467. }
  468. }
  469. function select(button) {
  470. if (selectedButton && selectedButton.$button) {
  471. if (selectedButton.$button) {
  472. removeElementClass(selectedButton.$button, BUTTON_SELECTED_CLASS);
  473. }
  474. }
  475. selectedButton = button;
  476. if (selectedButton && selectedButton.$button) {
  477. addElementClass(selectedButton.$button, BUTTON_SELECTED_CLASS);
  478. }
  479. }
  480. $state.onmouseup = function(e) {
  481. if (e.button) return;
  482. var target = e.target;
  483. while (target && target != $state) {
  484. if (target.classList.contains("button")) {
  485. allButtons.forEach(function(button) {
  486. if (button.$button == target) {
  487. execute(button);
  488. }
  489. });
  490. }
  491. target = target.parentNode;
  492. }
  493. };
  494. this.handleKeyEvent = function(e) {
  495. var handleResult = null;
  496. /**
  497. * @Desc: 搜狗浏览器下esc只触发keyup,因此做兼容性处理
  498. * @Editor: Naixor
  499. * @Date: 2015.09.14
  500. */
  501. if (hotBox.browser.sg) {
  502. if (e.isKey("esc")) {
  503. if (pressedButton) {
  504. // 若存在已经按下的按钮,则取消操作
  505. if (!e.isKey(pressedButton.key)) {
  506. // the button is not esc
  507. press(null);
  508. }
  509. } else {
  510. hotBox.active("back", hotBox.position);
  511. }
  512. return "back";
  513. }
  514. }
  515. if (e.keydown || hotBox.isIME && e.keyup) {
  516. allButtons.forEach(function(button) {
  517. if (button.enable() && e.isKey(button.key)) {
  518. if (stateActived || hotBox.hintDeactiveMainState) {
  519. select(button);
  520. press(button);
  521. handleResult = "buttonpress";
  522. // 如果是 keyup 事件触发的,因为没有后续的按键事件,所以就直接执行
  523. if (e.keyup) {
  524. execute(button);
  525. handleResult = "execute";
  526. return handleResult;
  527. }
  528. } else {
  529. execute(button);
  530. handleResult = "execute";
  531. }
  532. e.preventDefault();
  533. e.stopPropagation();
  534. if (!stateActived && hotBox.hintDeactiveMainState) {
  535. hotBox.active(stateName, hotBox.position);
  536. }
  537. }
  538. });
  539. if (stateActived) {
  540. if (e.isKey("esc")) {
  541. if (pressedButton) {
  542. // 若存在已经按下的按钮,则取消操作
  543. if (!e.isKey(pressedButton.key)) {
  544. // the button is not esc
  545. press(null);
  546. }
  547. } else {
  548. hotBox.active("back", hotBox.position);
  549. }
  550. return "back";
  551. }
  552. [ "up", "down", "left", "right" ].forEach(function(dir) {
  553. if (!e.isKey(dir)) return;
  554. if (!selectedButton) {
  555. select(buttons.center || buttons.ring[0] || buttons.top[0] || buttons.bottom[0]);
  556. return;
  557. }
  558. var neighbor = selectedButton.neighbor[dir];
  559. while (neighbor && !neighbor.enable()) {
  560. neighbor = neighbor.neighbor[dir];
  561. }
  562. if (neighbor) {
  563. select(neighbor);
  564. }
  565. handleResult = "navigate";
  566. });
  567. // 若是由 keyup 触发的,则直接执行选中的按钮
  568. if (e.isKey("space") && e.keyup) {
  569. execute(selectedButton);
  570. e.preventDefault();
  571. e.stopPropagation();
  572. handleResult = "execute";
  573. } else if (e.isKey("space") && selectedButton) {
  574. press(selectedButton);
  575. handleResult = "buttonpress";
  576. } else if (pressedButton && pressedButton != selectedButton) {
  577. press(null);
  578. handleResult = "selectcancel";
  579. }
  580. }
  581. } else if (e.keyup && (stateActived || !hotBox.hintDeactiveMainState)) {
  582. if (pressedButton) {
  583. if (e.isKey("space") && selectedButton == pressedButton || e.isKey(pressedButton.key)) {
  584. execute(pressedButton);
  585. e.preventDefault();
  586. e.stopPropagation();
  587. handleResult = "execute";
  588. }
  589. }
  590. }
  591. /*
  592. * Add by zhangbobell 2015.09.06
  593. * 增加了下面这一个判断因为 safari 下开启输入法后,所有的 keydown 的 keycode 都为 229,
  594. * 只能以 keyup 的 keycode 进行判断
  595. * */
  596. hotBox.isIME = e.keyCode == 229 && e.keydown;
  597. return handleResult;
  598. };
  599. function execute(button) {
  600. if (button) {
  601. if (!button.enable || button.enable()) {
  602. if (button.action) button.action(button);
  603. hotBox.active(button.next || IDLE, hotBox.position);
  604. }
  605. press(null);
  606. select(null);
  607. }
  608. }
  609. }
  610. module.exports = HotBox;
  611. }
  612. };
  613. //src/key.js
  614. _p[2] = {
  615. value: function(require, exports, module) {
  616. var keymap = _p.r(4);
  617. var CTRL_MASK = 4096;
  618. var ALT_MASK = 8192;
  619. var SHIFT_MASK = 16384;
  620. function hash(unknown) {
  621. if (typeof unknown == "string") {
  622. return hashKeyExpression(unknown);
  623. }
  624. return hashKeyEvent(unknown);
  625. }
  626. function is(a, b) {
  627. return a && b && hash(a) == hash(b);
  628. }
  629. exports.hash = hash;
  630. exports.is = is;
  631. function hashKeyEvent(keyEvent) {
  632. var hashCode = 0;
  633. if (keyEvent.ctrlKey || keyEvent.metaKey) {
  634. hashCode |= CTRL_MASK;
  635. }
  636. if (keyEvent.altKey) {
  637. hashCode |= ALT_MASK;
  638. }
  639. if (keyEvent.shiftKey) {
  640. hashCode |= SHIFT_MASK;
  641. }
  642. // Shift, Control, Alt KeyCode ignored.
  643. if ([ 16, 17, 18, 91 ].indexOf(keyEvent.keyCode) == -1) {
  644. hashCode |= keyEvent.keyCode;
  645. }
  646. return hashCode;
  647. }
  648. function hashKeyExpression(keyExpression) {
  649. var hashCode = 0;
  650. keyExpression.toLowerCase().split(/\s*\+\s*/).forEach(function(name) {
  651. switch (name) {
  652. case "ctrl":
  653. case "cmd":
  654. hashCode |= CTRL_MASK;
  655. break;
  656. case "alt":
  657. hashCode |= ALT_MASK;
  658. break;
  659. case "shift":
  660. hashCode |= SHIFT_MASK;
  661. break;
  662. default:
  663. hashCode |= keymap[name];
  664. }
  665. });
  666. return hashCode;
  667. }
  668. }
  669. };
  670. //src/keycontrol.js
  671. _p[3] = {
  672. value: function(require, exports, module) {
  673. var key = _p.r(2);
  674. var FOCUS_CLASS = "hotbox-focus";
  675. var RECEIVER_CLASS = "hotbox-key-receiver";
  676. function KeyControl(hotbox) {
  677. var _this = this;
  678. var _receiver;
  679. var _actived = true;
  680. var _receiverIsSelfCreated = false;
  681. var $container = hotbox.$container;
  682. _createReceiver();
  683. _bindReceiver();
  684. _bindContainer();
  685. _active();
  686. function _createReceiver() {
  687. _receiver = document.createElement("input");
  688. _receiver.classList.add(RECEIVER_CLASS);
  689. $container.appendChild(_receiver);
  690. _receiverIsSelfCreated = true;
  691. }
  692. function _bindReceiver() {
  693. _receiver.onkeyup = _handle;
  694. _receiver.onkeypress = _handle;
  695. _receiver.onkeydown = _handle;
  696. _receiver.onfocus = _active;
  697. _receiver.onblur = _deactive;
  698. if (_receiverIsSelfCreated) {
  699. _receiver.oninput = function(e) {
  700. _receiver.value = null;
  701. };
  702. }
  703. }
  704. function _bindContainer() {
  705. $container.onmousedown = function(e) {
  706. _active();
  707. e.preventDefault();
  708. };
  709. }
  710. function _handle(keyEvent) {
  711. if (!_actived) return;
  712. hotbox.dispatch(keyEvent);
  713. }
  714. function _active() {
  715. _receiver.select();
  716. _receiver.focus();
  717. _actived = true;
  718. $container.classList.add(FOCUS_CLASS);
  719. }
  720. function _deactive() {
  721. _receiver.blur();
  722. _actived = false;
  723. $container.classList.remove(FOCUS_CLASS);
  724. }
  725. this.handle = _handle;
  726. this.active = _active;
  727. this.deactive = _deactive;
  728. }
  729. module.exports = KeyControl;
  730. }
  731. };
  732. //src/keymap.js
  733. _p[4] = {
  734. value: function(require, exports, module) {
  735. var keymap = {
  736. Shift: 16,
  737. Control: 17,
  738. Alt: 18,
  739. CapsLock: 20,
  740. BackSpace: 8,
  741. Tab: 9,
  742. Enter: 13,
  743. Esc: 27,
  744. Space: 32,
  745. PageUp: 33,
  746. PageDown: 34,
  747. End: 35,
  748. Home: 36,
  749. Insert: 45,
  750. Left: 37,
  751. Up: 38,
  752. Right: 39,
  753. Down: 40,
  754. Direction: {
  755. 37: 1,
  756. 38: 1,
  757. 39: 1,
  758. 40: 1
  759. },
  760. Delete: 46,
  761. NumLock: 144,
  762. Cmd: 91,
  763. CmdFF: 224,
  764. F1: 112,
  765. F2: 113,
  766. F3: 114,
  767. F4: 115,
  768. F5: 116,
  769. F6: 117,
  770. F7: 118,
  771. F8: 119,
  772. F9: 120,
  773. F10: 121,
  774. F11: 122,
  775. F12: 123,
  776. "`": 192,
  777. "=": 187,
  778. "-": 189,
  779. "/": 191,
  780. ".": 190
  781. };
  782. // 小写适配
  783. for (var key in keymap) {
  784. if (keymap.hasOwnProperty(key)) {
  785. keymap[key.toLowerCase()] = keymap[key];
  786. }
  787. }
  788. var aKeyCode = 65;
  789. var aCharCode = "a".charCodeAt(0);
  790. // letters
  791. "abcdefghijklmnopqrstuvwxyz".split("").forEach(function(letter) {
  792. keymap[letter] = aKeyCode + (letter.charCodeAt(0) - aCharCode);
  793. });
  794. // numbers
  795. var n = 9;
  796. do {
  797. keymap[n.toString()] = n + 48;
  798. } while (n--);
  799. module.exports = keymap;
  800. }
  801. };
  802. var moduleMapping = {
  803. expose: 0
  804. };
  805. function use(name) {
  806. _p.r([ moduleMapping[name] ]);
  807. }
  808. use('expose');
  809. })();