A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

769 lines
21KB

  1. import {EventCaller} from "/app/js/common/EventCaller.js";
  2. import Utils from "/app/js/common/Utils.js";
  3. // Keycode list based on...
  4. // https://keycode.info/
  5. var KEYBYCODE = {
  6. 3:"break",
  7. 8:"backspace",
  8. 9:"tab",
  9. 13:"enter",
  10. 16:"shift",
  11. 17:"ctrl",
  12. 18:"alt",
  13. 19:"pause",
  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. 41:"select",
  26. 42:"print",
  27. 43:"execute",
  28. 44:"printscreen",
  29. 45:"insert",
  30. 46:"delete",
  31. 47:"help",
  32. 48:"0",
  33. 49:"1",
  34. 50:"2",
  35. 51:"3",
  36. 52:"4",
  37. 53:"5",
  38. 54:"6",
  39. 55:"7",
  40. 56:"8",
  41. 57:"9",
  42. 65:"a",
  43. 66:"b",
  44. 67:"c",
  45. 68:"d",
  46. 69:"e",
  47. 70:"f",
  48. 71:"g",
  49. 72:"h",
  50. 73:"i",
  51. 74:"j",
  52. 75:"k",
  53. 76:"l",
  54. 77:"m",
  55. 78:"n",
  56. 79:"o",
  57. 80:"p",
  58. 81:"q",
  59. 82:"r",
  60. 83:"s",
  61. 84:"t",
  62. 85:"u",
  63. 86:"v",
  64. 87:"w",
  65. 88:"x",
  66. 89:"y",
  67. 90:"z",
  68. 91:"leftmod", // Window key (left)
  69. 92:"rightwin",// Window key (right)
  70. 93:"rightmod",// Window key (right)
  71. 96:"num0",
  72. 97:"num1",
  73. 98:"num2",
  74. 99:"num3",
  75. 100:"num4",
  76. 101:"num5",
  77. 102:"num6",
  78. 103:"num7",
  79. 104:"num8",
  80. 105:"num9",
  81. 112:"f1",
  82. 113:"f2",
  83. 114:"f3",
  84. 115:"f4",
  85. 116:"f5",
  86. 117:"f6",
  87. 118:"f7",
  88. 119:"f8",
  89. 120:"f9",
  90. 121:"f10",
  91. 122:"f11",
  92. 123:"f12",
  93. 144:"numlock",
  94. 145:"scrolllock",
  95. };
  96. var KEYBYNAME = (function(){
  97. var keys = Object.keys(KEYBYCODE);
  98. var o = {};
  99. for (var i=0; i < keys.length; i++){
  100. if (KEYBYCODE.hasOwnProperty(keys[i])){
  101. o[KEYBYCODE[keys[i]]] = keys[i];
  102. }
  103. }
  104. return o;
  105. })();
  106. var KEYTYPE = {
  107. "number":[48,49,50,51,52,53,54,55,56,57,96,97,98,99,100,101,102,103,104,105],
  108. "letter":[65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90],
  109. "mod":[16,17,18],
  110. "arrow":[37,38,39,40],
  111. "wasd":[87,65,83,68],
  112. "fn":[112,113,114,115,116,117,118,119,120,121,122,123],
  113. "n1":[48,96],
  114. "n2":[49,97],
  115. "n3":[50,98],
  116. "n4":[51,99],
  117. "n5":[52,100],
  118. "n6":[53,101],
  119. "n7":[54,102],
  120. "n8":[55,103],
  121. "n9":[56,104],
  122. "n0":[57,105]
  123. };
  124. var KEYMAP = {
  125. "lastcode":null,
  126. "lastaction":"",
  127. "currentcodes":[]
  128. };
  129. var MOUSEBYCODE = {
  130. 0: "mbl", // (M)ouse (B)utton (L)eft
  131. 1: "mbm", // (M)ouse (B)utton (M)iddle
  132. 2: "mbr" // (M)ouse (B)utton (R)ight
  133. };
  134. var MOUSEBYNAME = {
  135. "mbl": 0,
  136. "mbm": 1,
  137. "mbr": 2
  138. };
  139. // TODO: Reeval this idea.
  140. const KEYPRESS_DELAY = 350; // Time in milliseconds. NOTE: May make this a variable in future.
  141. const MOUSECLICK_DELAY = 350; // Time in milliseconds.
  142. function AssignCodeName(code, name){
  143. name = name.toLowerCase();
  144. var remove = (code in Object.keys(KEYBYCODE));
  145. if (name in Object.keys(KEYBYNAME)){
  146. if (remove && KEYBYCODE[code] === name){
  147. return; // We're being asked to replace the exact same thing. SKIP!
  148. }
  149. throw new ValueError("Key name '" + name + "' already assigned. Cannot use duplicate key names.");
  150. }
  151. if (remove){
  152. delete KEYBYNAME[KEYBYCODE[code]];
  153. }
  154. KEYBYCODE[code] = name;
  155. KEYBYNAME[name] = code;
  156. }
  157. function KeymapContains(code){
  158. return KEYMAP["currentcodes"].findIndex(c=>c[0] == code) >= 0;
  159. }
  160. function AddToKeymap(code, action){
  161. KEYMAP["lastcode"] = code;
  162. KEYMAP["lastaction"] = action;
  163. if (KeymapContains(code) == false){
  164. KEYMAP["currentcodes"].push([code, Math.floor(Date.now())]);
  165. if (KEYMAP["currentcodes"].length > 1){
  166. KEYMAP["currentcodes"].sort(function(a, b){return a[0] - b[0];});
  167. }
  168. return true;
  169. }
  170. return false;
  171. }
  172. function RemoveFromKeymap(code, action){
  173. KEYMAP["lastcode"] = code;
  174. KEYMAP["lastaction"] = action;
  175. var ctime = Math.floor(Date.now());
  176. for (var i=0; i < KEYMAP["currentcodes"].length; i++){
  177. if (KEYMAP["currentcodes"][i][0] === code){
  178. var timediff = ctime - KEYMAP["currentcodes"][i][1];
  179. KEYMAP["currentcodes"].splice(i, 1);
  180. return timediff;
  181. }
  182. }
  183. return -1;
  184. }
  185. function KeyNameToCode(key){
  186. return (key in Object.keys(KEYBYNAME)) ? KEYBYNAME[key] : -1;
  187. }
  188. function CodeToKeyName(code){
  189. return (code in Object.keys(KEYBYCODE)) ? KEYBYCODE[code] : "" + code;
  190. }
  191. function CodesToEventName(codes, mouse){
  192. var ename = "";
  193. mouse = (mouse === true);
  194. for (var i=0; i < codes.length; i++){
  195. if (mouse){
  196. switch(codes[i]){
  197. case 0:
  198. ename += ((ename !== "") ? "+" : "") + "mouseleft";
  199. break;
  200. case 1:
  201. ename += ((ename !== "") ? "+" : "") + "mouseright";
  202. break;
  203. case 2:
  204. ename += ((ename !== "") ? "+" : "") + "mousemiddle";
  205. break;
  206. case 8000:
  207. ename += ((ename !== "") ? "+" : "") + "mousemove";
  208. break;
  209. case 8001:
  210. ename += ((ename !== "") ? "+" : "") + "wheel";
  211. default:
  212. ename += ((ename !== "") ? "+" : "") + "mousebtn" + codes[i].toString();
  213. }
  214. } else {
  215. ename += ((ename !== "") ? "+" : "") + CodeToKeyName(codes[i]);
  216. }
  217. }
  218. return ename;
  219. }
  220. function KeymapEventName(){
  221. return CodesToEventName(KEYMAP["currentcodes"].map(e=>e[0]));
  222. }
  223. function ReorderEventName(ename){
  224. // This function takes a keyboard and mouse event name and reorders it into key-code order.
  225. // This way users can write the event any way they want, but should still result in proper
  226. // event being called.
  227. var elist = ename.split("+");
  228. // TODO: Need to test for duplicate event names for both keyboard and mouse event names.
  229. var ecodes = [];
  230. var mcodes = [];
  231. for (var i=0; i < elist.length; i++){
  232. var key = elist[i].trim().toLowerCase();
  233. // Check for mouse events first. These are hardcoded for now.
  234. if (key === "mouseleft"){
  235. mcodes.push(0);
  236. } else if (key === "mouseright"){
  237. mcodes.push(1);
  238. } else if (key === "mousemiddle"){
  239. mcodes.push(2);
  240. } else if (key.startsWith("mousebtn")){
  241. var sub = key.substring(8);
  242. if (!isNaN(sub)){
  243. mcodes.push(parseInt(sub));
  244. } else {
  245. return ""; // This event name does not include valid mouse button code.
  246. }
  247. } else if (key === "mousemove"){
  248. mcodes.push(8000);
  249. } else if (key === "wheel"){
  250. mcodes.push(8001);
  251. }
  252. // Now handle keyboard event names.
  253. else if (!(key in Object.keys(KEYBYNAME))){
  254. if (!isNaN(key))
  255. ecodes.push(parseInt(key));
  256. else
  257. return ""; // This event name does not include valid key name!
  258. } else {
  259. ecodes.push(KEYBYNAME[key]);
  260. }
  261. }
  262. if (ecodes.length > 0 || mcodes.length > 0){
  263. var rename = "";
  264. if (ecodes.length > 0){
  265. ecodes.sort(function(a, b){return a-b;});
  266. rename = CodesToEventName(ecodes);
  267. }
  268. if (mcodes.length > 0){
  269. mcodes.sort(function(a, b){return a-b;});
  270. rename += ((rename !== "") ? "+" : "") + CodesToEventName(mcodes, true);
  271. }
  272. return rename;
  273. }
  274. return "";
  275. }
  276. export default class Input{
  277. constructor(){
  278. this.__emitter = new EventCaller();
  279. this.__preventDefaults = false;
  280. // Internet Explorer... that fudged up p.o.s. has to make mouse button detection difficult
  281. // with different button values from ALL other browsers. So... if Input has this value set to
  282. // true, then mouseup and mousedown events will base it's detection of IE button values, instead
  283. // of the real values.
  284. this.__ieMouseMode = false;
  285. // If set, this is the element that the mouse will focus on and adjust it's position against.
  286. this.__mouseTarget = null;
  287. this.__mousePosition = null;
  288. this.__mouseLastButton = -1;
  289. this.__mouseLastAction = "";
  290. this.__mouseButtons = [];
  291. this.__mouseInBounds = false;
  292. this.__keyboardEnabled = false;
  293. this.__mouseEnabled = false;
  294. var buttonID = (function(e){
  295. var btn = e.button;
  296. if ((this.__ieMouseMode && btn === 1) || (!this.__ieMouseMode && btn === 0)){
  297. btn = 0;
  298. } else if ((this.__ieMouseMode && e.button === 4) || (!this.__ieMouseMode && e.button === 1)){
  299. btn = 1;
  300. }
  301. return btn;
  302. }).bind(this);
  303. var addMouseButton = (function(btn){
  304. if (this.__mouseButtons.findIndex(b=>b[0]===btn) < 0){
  305. this.__mouseButtons.push([btn, Math.floor(Date.now())]);
  306. return true;
  307. }
  308. return false;
  309. }).bind(this);
  310. var removeMouseButton = (function(btn){
  311. var idx = this.__mouseButtons.findIndex(b=>b[0]===btn);
  312. var diff = -1;
  313. if (idx >= 0){
  314. diff = Math.floor(Date.now()) - this.__mouseButtons[idx][1];
  315. this.__mouseButtons.splice(idx, 1);
  316. }
  317. return diff;
  318. }).bind(this);
  319. // ---------------------------------------------------------------------
  320. // Handling keyboard events.
  321. this.enableKeyboardInput = (function(){
  322. var handle_keydown = (function(e){
  323. if (AddToKeymap(e.keyCode, "keydown")){
  324. var ename = KeymapEventName();
  325. var edata = {
  326. source: this,
  327. iscombo: (ename.indexOf("+") >= 0),
  328. keys: ename,
  329. keycode:e.keyCode,
  330. keyname:CodeToKeyName(e.keyCode),
  331. action:"keydown"
  332. }
  333. this.__emitter.emit(ename, edata);
  334. this.__emitter.emit("keydown", edata);
  335. }
  336. }).bind(this);
  337. var handle_keyup = (function(e){
  338. var timediff = RemoveFromKeymap(e.keyCode, "keyup");
  339. if (timediff < 0){
  340. console.log("WARNING: Failed to find keycode '" + e.keyCode + "' in the Key Map.");
  341. } else {
  342. var ename = KeymapEventName();
  343. var edata = {
  344. source: this,
  345. iscombo: (ename.indexOf("+") >= 0),
  346. keys: ename,
  347. keycode: e.keyCode,
  348. keyname: CodeToKeyName(e.keyCode),
  349. action:"keyup"
  350. }
  351. if (timediff <= KEYPRESS_DELAY && KEYMAP["currentcodes"].length <= 0){
  352. this.__emitter.emit("keypress", edata);
  353. }
  354. this.__emitter.emit("keyup", edata);
  355. }
  356. }).bind(this);
  357. return (function(enable){
  358. enable = (enable !== false);
  359. // NOTE: There shouldn't be any harm if the user repeatedly enables or disables keyboard.
  360. if (enable){
  361. this.__keyboardEnabled = true;
  362. window.addEventListener("keydown", handle_keydown, false);
  363. window.addEventListener("keyup", handle_keyup, false);
  364. } else {
  365. this.__keyboardEnabled = false;
  366. window.removeEventListener("keydown", handle_keydown);
  367. window.removeEventListener("keyup", handle_keyup);
  368. }
  369. }).bind(this);
  370. }).apply(this);
  371. // ---------------------------------------------------------------------------------------
  372. // Handling mouse events.
  373. var MouseButtonsEventName = (function(){
  374. var e = "";
  375. for (var i=0; i < this.__mouseButtons.length; i++){
  376. e += (e !== "") ? "+" : "";
  377. switch (this.__mouseButtons[i][0]){
  378. case 0:
  379. e += "mouseleft";
  380. break;
  381. case 1:
  382. e += "mouseright";
  383. break;
  384. case 2:
  385. e += "mousemiddle";
  386. break;
  387. default:
  388. e += "mousebtn" + this.__mouseButtons[i][0].toString();
  389. }
  390. }
  391. return e;
  392. }).bind(this);
  393. // This function will only return an event name if keyboard
  394. var MouseEventName = (function(addon){
  395. var ename = KeymapEventName();
  396. var mname = MouseButtonsEventName();
  397. if (mname !== "")
  398. ename += ((ename !== "") ? "+" : "") + mname;
  399. if (typeof(addon) === "string")
  400. ename += ((ename !== "") ? "+" : "") + addon;
  401. return ename;
  402. }).bind(this);
  403. this.enableMouseInput = (function(){
  404. var mousePosition = (function(e){
  405. var pos = {
  406. lastX: (this.__mousePosition !== null) ? this.__mousePosition.x : null,
  407. lastY: (this.__mousePosition !== null) ? this.__mousePosition.y : null,
  408. x: e.clientX,
  409. y: e.clientY,
  410. inbounds: true
  411. }
  412. if (this.__mouseTarget !== null){
  413. var rect = this.__mouseTarget.getBoundingClientRect();
  414. pos.x -= rect.left;
  415. pos.y -= rect.top;
  416. pos.inbounds = (pos.x >= 0 && pos.x < rect.width && pos.y >= 0 && pos.y < rect.height);
  417. }
  418. pos.x = Math.floor(pos.x);
  419. pos.y = Math.floor(pos.y);
  420. this.__mouseInBounds = pos.inbounds;
  421. return pos;
  422. }).bind(this);
  423. var handle_mousemove = (function(e){
  424. var pos = mousePosition(e);
  425. if (pos.inbounds){
  426. if (this.__preventDefaults){
  427. e.preventDefault();
  428. if (e.stopPropagation)
  429. e.stopPropagation();
  430. e.cancelBubble = true;
  431. }
  432. this.__mousePosition = pos;
  433. this.__mouseLastAction = "mousemove";
  434. var ename = MouseEventName("mousemove");
  435. var data = {
  436. source: this,
  437. isCombo: (ename.indexOf("+") >= 0),
  438. lastX: pos.lastX,
  439. lastY: pos.lastY,
  440. x: pos.x,
  441. y: pos.y,
  442. button: this.__mouseLastButton,
  443. delta: 0,
  444. action: "mousemove"
  445. };
  446. if (ename !== "")
  447. this.__emitter.emit(ename, data);
  448. }
  449. return false;
  450. }).bind(this);
  451. var handle_mousedown = (function(e){
  452. var button = buttonID(e);
  453. var pos = mousePosition(e);
  454. if (pos.inbounds){
  455. if (this.__preventDefaults){
  456. e.preventDefault();
  457. if (e.stopPropagation)
  458. e.stopPropagation();
  459. e.cancelBubble = true;
  460. }
  461. if (addMouseButton(button)){
  462. var ename = MouseEventName();
  463. var data = {
  464. source: this,
  465. isCombo: (ename.indexOf("+") >= 0),
  466. lastX: pos.lastX,
  467. lastY: pos.lastY,
  468. x: pos.x,
  469. y: pos.y,
  470. button: button,
  471. delta: 0,
  472. action: "mousedown"
  473. };
  474. this.__mousePosition = pos;
  475. this.__mouseLastButton = button;
  476. this.__mouseLastAction = "mousedown";
  477. if (ename !== "")
  478. this.__emitter.emit(ename, data);
  479. this.__emitter.emit("mousedown", data);
  480. }
  481. }
  482. return false;
  483. }).bind(this);
  484. var handle_mouseup = (function(e){
  485. var button = buttonID(e);
  486. var pos = mousePosition(e);
  487. // NOTE: I still want to check for button removal, even before testing if an event should
  488. // fire, so that I don't have any phantom buttons listed as "pressed" in the mouseButtons list.
  489. var diff = removeMouseButton(button);
  490. if (pos.inbounds){
  491. if (this.__preventDefaults){
  492. e.preventDefault();
  493. if (e.stopPropagation)
  494. e.stopPropagation();
  495. e.cancelBubble = true;
  496. }
  497. if (diff >= 0){
  498. this.__mousePosition = pos;
  499. this.__mouseLastButton = button;
  500. this.__mouseLastAction = "mouseup";
  501. var data = {
  502. source: this,
  503. isCombo: false,
  504. lastX: pos.lastX,
  505. lastY: pos.lastY,
  506. x: pos.x,
  507. y: pos.y,
  508. button: button,
  509. delta: 0,
  510. action: "mouseup"
  511. }
  512. this.__emitter.emit("mouseup", data);
  513. if (diff <= MOUSECLICK_DELAY && this.__mouseButtons.length <= 0){
  514. this.__emitter.emit("mouseclick", data);
  515. }
  516. }
  517. }
  518. return false;
  519. }).bind(this);
  520. var handle_mousewheel = (function(e){
  521. var pos = mousePosition(e);
  522. if (pos.inbounds === true){
  523. if (this.__preventDefaults)
  524. e.preventDefault();
  525. var ename = MouseEventName("wheel");
  526. var data = {
  527. source: this,
  528. isCombo: (ename.indexOf("+") >= 0),
  529. lastX: pos.lastX,
  530. lastY: pos.lastY,
  531. x: pos.x,
  532. y: pos.y,
  533. button: this.__mouseLastButton,
  534. delta: Math.sign(e.deltaY),
  535. action: "wheel"
  536. };
  537. if (ename !== "wheel")
  538. this.__emitter.emit(ename, data);
  539. if (data.delta < 0)
  540. this.__emitter.emit("wheeldown", data);
  541. if (data.delta > 0)
  542. this.__emitter.emit("wheelup", data);
  543. this.__emitter.emit("wheel", data);
  544. }
  545. }).bind(this);
  546. // This event is purely for preventing Default behaviors on mouse events we're not using.
  547. var handle_mouseprevdef = (function(e){
  548. var pos = mousePosition(e);;
  549. if (this.__preventDefaults && (pos === null || pos.inbounds)){
  550. e.preventDefault();
  551. if (e.stopPropagation)
  552. e.stopPropagation();
  553. e.cancelBubble = true;
  554. }
  555. return false;
  556. }).bind(this);
  557. return (function(enable){
  558. enable = (enable !== false);
  559. // NOTE: There shouldn't be any harm if the user repeatedly enables or disables mouse.
  560. if (enable){
  561. this.__mouseEnabled = true;
  562. window.addEventListener("mousemove", handle_mousemove);
  563. window.addEventListener("mousedown", handle_mousedown);
  564. window.addEventListener("mouseup", handle_mouseup);
  565. window.addEventListener("mousewheel", handle_mousewheel); // For older browsers?
  566. window.addEventListener("wheel", handle_mousewheel);
  567. window.addEventListener("click", handle_mouseprevdef);
  568. window.addEventListener("dblclick", handle_mouseprevdef);
  569. window.addEventListener("contextmenu", handle_mouseprevdef);
  570. } else {
  571. this.__mouseEnabled = false;
  572. window.removeEventListener("mousemove", handle_mousemove);
  573. window.removeEventListener("mousedown", handle_mousedown);
  574. window.removeEventListener("mouseup", handle_mouseup);
  575. window.removeEventListener("mousewheel", handle_mousewheel); // For older browsers?
  576. window.removeEventListener("wheel", handle_mousewheel);
  577. window.removeEventListener("click", handle_mouseprevdef);
  578. window.removeEventListener("dblclick", handle_mouseprevdef);
  579. window.removeEventListener("contextmenu", handle_mouseprevdef);
  580. }
  581. }).bind(this);
  582. }).apply(this);
  583. this.enableKeyboardInput();
  584. this.enableMouseInput();
  585. }
  586. get mouseInputEnabled(){return this.__mouseEnabled;}
  587. get keyboardInputEnabled(){return this.__keyboardEnabled;}
  588. get lastkey(){
  589. if (KEYMAP["lastcode"] !== null){
  590. if (KEYMAP["lastcode"] in Object.keys(KEYBYCODE)){
  591. return KEYBYCODE[KEYMAP["lastcode"]];
  592. }
  593. return "" + KEYMAP["lastcode"];
  594. }
  595. return "0";
  596. }
  597. get lastkeyaction(){
  598. return KEYMAP["lastaction"];
  599. }
  600. get currentKeys(){
  601. return KeymapEventName();
  602. }
  603. get currentKeyCodes(){
  604. return KEYMAP["currentcodes"].map(e=>e[0]);
  605. }
  606. get lastMouseAction(){
  607. return this.__mouseLastAction;
  608. }
  609. get lastMouseButton(){
  610. return this.__mouseLastButton;
  611. }
  612. get lastMousePosition(){
  613. if (this.__mousePosition === null || this.__mousePosition.lastX === null)
  614. return null;
  615. return {
  616. x: this.__mousePosition.lastX,
  617. y: this.__mousePosition.lastY
  618. };
  619. }
  620. get currentMousePosition(){
  621. if (this.__mousePosition === null)
  622. return null;
  623. return {
  624. x: this.__mousePosition.x,
  625. y: this.__mousePosition.y
  626. };
  627. }
  628. get preventDefaults(){return this.__preventDefaults;}
  629. set preventDefaults(p){
  630. this.__preventDefaults = (p === true);
  631. }
  632. get ieMouseMode(){return this.__ieMouseMode;}
  633. set ieMouseMode(m){this.__ieMouseMode = (m === true);}
  634. get mouseTargetElement(){return this.__mouseTarget;}
  635. set mouseTargetElement(el){
  636. if (el === null || Utils.isElement(el)){
  637. this.__mouseTarget = el;
  638. } else {
  639. throw new TypeError("Expected Mouse Target Element to be null or an HTMLElement object.");
  640. }
  641. }
  642. isKeyDown(key){
  643. if (typeof(key) === 'string'){
  644. key = KeyNameToCode(key);
  645. }
  646. for (var i=0; i < KEYMAP["currentcodes"].length; i++){
  647. if (KEYMAP["currentcodes"][i][0] === key){
  648. return true;
  649. }
  650. }
  651. return false;
  652. }
  653. isMouseInBounds(){return this.__mouseInBounds;}
  654. lastMousePosition(){
  655. return [this.__mousePosition.x, this.__mousePosition.y];
  656. }
  657. listen(ename, func, owner=null, once=false){
  658. if (([
  659. "keyup",
  660. "keydown",
  661. "keypress",
  662. "mousemove",
  663. "mousedown",
  664. "mouseup",
  665. "mouseclick",
  666. "wheel",
  667. "wheelup",
  668. "wheeldown"
  669. ]).indexOf(ename) >= 0){
  670. this.__emitter.listen(ename, func, owner, once);
  671. } else {
  672. ename = ReorderEventName(ename);
  673. if (ename === ""){
  674. throw new ValueError("Failed to parse key or key combination.");
  675. }
  676. this.__emitter.listen(ename, func, owner, once);
  677. }
  678. return this;
  679. }
  680. unlisten(ename, func, owner=null){
  681. if (([
  682. "keyup",
  683. "keydown",
  684. "keypress",
  685. "mousemove",
  686. "mousedown",
  687. "mouseup",
  688. "mouseclick",
  689. "wheel",
  690. "wheelup",
  691. "wheeldown"
  692. ]).indexOf(ename) >= 0){
  693. this.__emitter.unlisten(ename, func, owner);
  694. } else {
  695. ename = ReorderEventName(ename);
  696. if (ename !== "")
  697. this.__emitter.unlisten(ename, func, owner);
  698. }
  699. return this;
  700. }
  701. }