A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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. }