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.

393 lines
9.2KB

  1. import {EventCaller} from "/app/js/common/EventCaller.js";
  2. // Keycode list based on...
  3. // https://keycode.info/
  4. var KEYBYCODE = {
  5. 3:"break",
  6. 8:"backspace",
  7. 9:"tab",
  8. 13:"enter",
  9. 16:"shift",
  10. 17:"ctrl",
  11. 18:"alt",
  12. 19:"pause",
  13. 20:"capslock",
  14. 27:"esc",
  15. 32:"space",
  16. 33:"pageup",
  17. 34:"pagedown",
  18. 35:"end",
  19. 36:"home",
  20. 37:"left",
  21. 38:"up",
  22. 39:"right",
  23. 40:"down",
  24. 41:"select",
  25. 42:"print",
  26. 43:"execute",
  27. 44:"printscreen",
  28. 45:"insert",
  29. 46:"delete",
  30. 47:"help",
  31. 48:"0",
  32. 49:"1",
  33. 50:"2",
  34. 51:"3",
  35. 52:"4",
  36. 53:"5",
  37. 54:"6",
  38. 55:"7",
  39. 56:"8",
  40. 57:"9",
  41. 65:"a",
  42. 66:"b",
  43. 67:"c",
  44. 68:"d",
  45. 69:"e",
  46. 70:"f",
  47. 71:"g",
  48. 72:"h",
  49. 73:"i",
  50. 74:"j",
  51. 75:"k",
  52. 76:"l",
  53. 77:"m",
  54. 78:"n",
  55. 79:"o",
  56. 80:"p",
  57. 81:"q",
  58. 82:"r",
  59. 83:"s",
  60. 84:"t",
  61. 85:"u",
  62. 86:"v",
  63. 87:"w",
  64. 88:"x",
  65. 89:"y",
  66. 90:"z",
  67. 91:"leftmod", // Window key (left)
  68. 92:"rightwin",// Window key (right)
  69. 93:"rightmod",// Window key (right)
  70. 96:"num0",
  71. 97:"num1",
  72. 98:"num2",
  73. 99:"num3",
  74. 100:"num4",
  75. 101:"num5",
  76. 102:"num6",
  77. 103:"num7",
  78. 104:"num8",
  79. 105:"num9",
  80. 112:"f1",
  81. 113:"f2",
  82. 114:"f3",
  83. 115:"f4",
  84. 116:"f5",
  85. 117:"f6",
  86. 118:"f7",
  87. 119:"f8",
  88. 120:"f9",
  89. 121:"f10",
  90. 122:"f11",
  91. 123:"f12",
  92. 144:"numlock",
  93. 145:"scrolllock",
  94. };
  95. var KEYBYNAME = (function(){
  96. var keys = Object.keys(KEYBYCODE);
  97. var o = {};
  98. for (var i=0; i < keys.length; i++){
  99. if (KEYBYCODE.hasOwnProperty(keys[i])){
  100. o[KEYBYCODE[keys[i]]] = keys[i];
  101. }
  102. }
  103. return o;
  104. })();
  105. var KEYTYPE = {
  106. "number":[48,49,50,51,52,53,54,55,56,57,96,97,98,99,100,101,102,103,104,105],
  107. "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],
  108. "mod":[16,17,18],
  109. "arrow":[37,38,39,40],
  110. "wasd":[87,65,83,68],
  111. "fn":[112,113,114,115,116,117,118,119,120,121,122,123],
  112. "n1":[48,96],
  113. "n2":[49,97],
  114. "n3":[50,98],
  115. "n4":[51,99],
  116. "n5":[52,100],
  117. "n6":[53,101],
  118. "n7":[54,102],
  119. "n8":[55,103],
  120. "n9":[56,104],
  121. "n0":[57,105]
  122. };
  123. var KEYMAP = {
  124. "lastcode":null,
  125. "lastaction":"",
  126. "currentcodes":[]
  127. };
  128. // TODO: Reeval this idea.
  129. const KEYPRESS_DELAY = 350; // Time in milliseconds. NOTE: May make this a variable in future.
  130. function AssignCodeName(code, name){
  131. name = name.toLowerCase();
  132. var remove = (code in Object.keys(KEYBYCODE));
  133. if (name in Object.keys(KEYBYNAME)){
  134. if (remove && KEYBYCODE[code] === name){
  135. return; // We're being asked to replace the exact same thing. SKIP!
  136. }
  137. throw new ValueError("Key name '" + name + "' already assigned. Cannot use duplicate key names.");
  138. }
  139. if (remove){
  140. delete KEYBYNAME[KEYBYCODE[code]];
  141. }
  142. KEYBYCODE[code] = name;
  143. KEYBYNAME[name] = code;
  144. }
  145. function KeymapContains(code){
  146. return KEYMAP["currentcodes"].findIndex(c=>c[0] == code) >= 0;
  147. }
  148. function AddToKeymap(code, action){
  149. KEYMAP["lastcode"] = code;
  150. KEYMAP["lastaction"] = action;
  151. if (KeymapContains(code) == false){
  152. KEYMAP["currentcodes"].push([code, Math.floor(Date.now())]);
  153. if (KEYMAP["currentcodes"].length > 1){
  154. KEYMAP["currentcodes"].sort(function(a, b){return a[0] - b[0];});
  155. }
  156. return true;
  157. }
  158. return false;
  159. }
  160. function RemoveFromKeymap(code, action){
  161. KEYMAP["lastcode"] = code;
  162. KEYMAP["lastaction"] = action;
  163. var ctime = Math.floor(Date.now());
  164. for (var i=0; i < KEYMAP["currentcodes"].length; i++){
  165. if (KEYMAP["currentcodes"][i][0] === code){
  166. var timediff = ctime - KEYMAP["currentcodes"][i][1];
  167. KEYMAP["currentcodes"].splice(i, 1);
  168. return timediff;
  169. }
  170. }
  171. return -1;
  172. }
  173. function KeyNameToCode(key){
  174. return (key in Object.keys(KEYBYNAME)) ? KEYBYNAME[key] : -1;
  175. }
  176. function CodeToKeyName(code){
  177. return (code in Object.keys(KEYBYCODE)) ? KEYBYCODE[code] : "" + code;
  178. }
  179. function CodesToEventName(codes){
  180. var ename = "";
  181. for (var i=0; i < codes.length; i++){
  182. ename += ((ename !== "") ? "+" : "") + CodeToKeyName(codes[i]);
  183. }
  184. return ename;
  185. }
  186. function KeymapEventName(){
  187. return CodesToEventName(KEYMAP["currentcodes"].map(e=>e[0]));
  188. }
  189. function ReorderEventName(ename){
  190. // This function takes a keyboard event name and reorders it into key-code order.
  191. // This way users can write the event any way they want, but should still result in proper
  192. // event being called.
  193. var elist = ename.split("+");
  194. var ecodes = [];
  195. for (var i=0; i < elist.length; i++){
  196. var key = elist[i].trim().toLowerCase();
  197. if (!(key in Object.keys(KEYBYNAME))){
  198. if (!Number.isNaN(key))
  199. ecodes.push(parseInt(key));
  200. else
  201. return ""; // This event name does not include valid key name!
  202. } else {
  203. ecodes.push(KEYBYNAME[key]);
  204. }
  205. }
  206. if (ecodes.length > 0){
  207. ecodes.sort(function(a, b){return a-b;});
  208. return CodesToEventName(ecodes);
  209. }
  210. return "";
  211. }
  212. export default class Input{
  213. constructor(){
  214. this.__emitter = new EventCaller();
  215. this.__preventDefaults = false;
  216. this.enableKeyboardInput = (function(){
  217. var handle_keydown = (function(e){
  218. if (AddToKeymap(e.keyCode, "keydown")){
  219. var ename = KeymapEventName();
  220. var edata = {
  221. source: this,
  222. iscombo: (ename.indexOf("+") >= 0),
  223. keys: ename,
  224. keycode:e.keyCode,
  225. keyname:CodeToKeyName(e.keyCode),
  226. action:"keydown"
  227. }
  228. this.__emitter.emit(ename, edata);
  229. this.__emitter.emit("keydown", edata);
  230. }
  231. }).bind(this);
  232. var handle_keyup = (function(e){
  233. var timediff = RemoveFromKeymap(e.keyCode, "keyup");
  234. if (timediff < 0){
  235. console.log("WARNING: Failed to find keycode '" + e.keyCode + "' in the Key Map.");
  236. } else {
  237. var ename = KeymapEventName();
  238. var edata = {
  239. source: this,
  240. iscombo: (ename.indexOf("+") >= 0),
  241. keys: ename,
  242. keycode: e.keyCode,
  243. keyname: CodeToKeyName(e.keyCode),
  244. action:"keyup"
  245. }
  246. if (timediff <= KEYPRESS_DELAY && KEYMAP["currentcodes"].length <= 0){
  247. this.__emitter.emit("keypress", edata);
  248. }
  249. this.__emitter.emit("keyup", edata);
  250. }
  251. }).bind(this);
  252. return (function(enable){
  253. enable = (enable !== false);
  254. if (enable){
  255. window.addEventListener("keydown", handle_keydown, false);
  256. //window.addEventListener("keypress", handle_keypress, false);
  257. window.addEventListener("keyup", handle_keyup, false);
  258. } else {
  259. window.removeEventListener("keydown", handle_keydown);
  260. window.removeEventListener("keyup", handle_keyup);
  261. }
  262. }).bind(this);
  263. }).apply(this);
  264. this.enableMouseInput = (function(){
  265. var handle_mousemove = (function(e){
  266. // TODO: Finish me!
  267. }).bind(this);
  268. var handle_mousedown = (function(e){
  269. // TODO: Finish me!
  270. }).bind(this);
  271. var handle_mouseup = (function(e){
  272. // TODO: Finish me!
  273. }).bind(this);
  274. var handle_mousewheel = (function(e){
  275. // TODO: Finish me!
  276. }).bind(this);
  277. return (function(enable){
  278. enable = (enable !== false);
  279. if (enable){
  280. window.addEventListener("mousemove", handle_mousemove);
  281. window.addEventListener("mousedown", handle_mousedown);
  282. window.addEventListener("mouseup", handle_mouseup);
  283. window.addEventListener("mousewheel", handle_mousewheel);
  284. } else {
  285. window.removeEventListener("mousemove", handle_mousemove);
  286. window.removeEventListener("mousedown", handle_mousedown);
  287. window.removeEventListener("mouseup", handle_mouseup);
  288. window.removeEventListener("mousewheel", handle_mousewheel);
  289. }
  290. }).bind(this);
  291. }).apply(this);
  292. this.enableKeyboardInput();
  293. }
  294. get lastkey(){
  295. if (KEYMAP["lastcode"] !== null){
  296. if (KEYMAP["lastcode"] in Object.keys(KEYBYCODE)){
  297. return KEYBYCODE[KEYMAP["lastcode"]];
  298. }
  299. return "" + KEYMAP["lastcode"];
  300. }
  301. return "0";
  302. }
  303. get lastaction(){
  304. return KEYMAP["lastaction"];
  305. }
  306. get currentKeys(){
  307. return KeymapEventName();
  308. }
  309. get currentCodes(){
  310. return KEYMAP["currentcodes"].map(e=>e[0]);
  311. }
  312. get preventDefaults(){return this.__preventDefaults;}
  313. set preventDefaults(p){
  314. this.__preventDefaults = (p === true);
  315. }
  316. isKeyDown(key){
  317. if (typeof(key) === 'string'){
  318. key = KeyNameToCode(key);
  319. }
  320. for (var i=0; i < KEYMAP["currentcodes"].length; i++){
  321. if (KEYMAP["currentcodes"][i][0] === key){
  322. return true;
  323. }
  324. }
  325. return false;
  326. }
  327. listen(ename, func, owner=null, once=false){
  328. if ((["keyup", "keydown", "keypress", "mousemove", "mousedown", "mouseup"]).indexOf(ename) >= 0){
  329. this.__emitter.listen(ename, func, owner, once);
  330. } else {
  331. ename = ReorderEventName(ename);
  332. if (ename === ""){
  333. throw new ValueError("Failed to parse key or key combination.");
  334. }
  335. this.__emitter.listen(ename, func, owner, once);
  336. }
  337. return this;
  338. }
  339. unlisten(ename, func, owner=null){
  340. if ((["keyup", "keydown", "keypress", "mousemove", "mousedown", "mouseup"]).indexOf(ename) >= 0){
  341. this.__emitter.unlisten(ename, func, owner);
  342. } else {
  343. ename = ReorderEventName(ename);
  344. if (ename !== "")
  345. this.__emitter.unlisten(ename, func, owner);
  346. }
  347. return this;
  348. }
  349. }