var KeyHandler: KeyHandler = { elements: { keys: {} }, selected: [], imagecache: {}, ghostImage: null, init() { KeyHandler.ghostImage = new Image(); KeyHandler.ghostImage.src = '/stc/icon/ghost.png'; KeyHandler.ghostImage.onload = () => { for (var y = 0; y < 4; y++) { if (KeyHandler.elements.keys[y] == undefined) KeyHandler.elements.keys[y] = {}; for (var x = 0; x < 8; x++) { var keyCheck: HTMLDivElement = document.querySelector(`.key[y="${y}"][x="${x}"]`); if (keyCheck) { ((keyX: number, keyY: number, key: HTMLDivElement) => { var canvas = key.querySelector('canvas'); var context = canvas.getContext('2d'); KeyHandler.elements.keys[keyY][keyX] = { key, canvas, context }; context.textBaseline = 'middle'; context.textAlign = 'center'; key.onclick = () => { if (altDown == false) KeyHandler.select(String(keyX), String(keyY)); else { socket.emit('page', 'executekey', PageHandler.currentPageID, keyX, keyY); } }; })(x, y, keyCheck); } } } }; }, clear() { for (var y in KeyHandler.elements.keys) { for (var x in KeyHandler.elements.keys[y]) { var key = KeyHandler.elements.keys[y][x]; key.context.clearRect(0, 0, 100, 100); key.key.classList.remove('selected'); } } KeyHandler.selected = []; Editor.close(); }, render(x: string, y: string, data: Page_Key) { if (KeyHandler.elements.keys[String(y)]) { if (KeyHandler.elements.keys[String(y)][String(x)]) { var context = KeyHandler.elements.keys[String(y)][String(x)].context; context.clearRect(0, 0, renderQuality, renderQuality); if (data.state.type == 'custom') { var appearence = data.appearence; render(appearence); } else if (data.state.type == 'ghost') { var appearence = data.appearence; if (appearence == undefined) appearence = {}; appearence.system = { ghost: true }; render(appearence); } else if (data.state.type == 'pageup') { render({ text: { value: 'Up', color: '#ffffff', size: 18, offsetX: 0, offsetY: 25 }, background: { color: '#4676b7' }, image: { size: 100, rotation: 0, offsetX: 0, offsetY: -15, iconid: 'keyboard_arrow_up', iconstyle: 'white' }, system: { border: { color: '#253e5e', thickness: 8 } } }); } else if (data.state.type == 'pagedown') { render({ text: { value: 'Down', color: '#ffffff', size: 18, offsetX: 0, offsetY: -25 }, background: { color: '#4676b7' }, image: { size: 100, rotation: 0, offsetX: 0, offsetY: 15, iconid: 'keyboard_arrow_down', iconstyle: 'white' }, system: { border: { color: '#253e5e', thickness: 8 } } }); } else if (data.state.type == 'currentpage') { render({ text: { value: `Page\\n\\n${PageHandler.currentIndex + 1}`, color: '#ffffff', size: 22, offsetX: 0, offsetY: 0 }, background: { color: '#4676b7' }, system: { border: { color: '#253e5e', thickness: 8 } } }); } } else console.error(`Invalid x '${x}'`); } else console.error(`Invalid y '${y}'`); function render(appearence: Page_Key_Appearence) { if (appearence.background != undefined) { context.fillStyle = appearence.background.color; context.fillRect(0, 0, renderQuality, renderQuality); context.fill(); } if (appearence.text != undefined) { context.fillStyle = appearence.text.color; context.font = `800 ${appearence.text.size * fontSizeRatio}px "Montserrat"`; var text = appearence.text.value; var lineHeight = appearence.text.size * fontSizeRatio; var centerX = renderQuality / 2 + appearence.text.offsetX / 100 * (renderQuality * 2); var centerY = renderQuality / 2 + appearence.text.offsetY / 100 * renderQuality; var canvasYCounter = centerY; var words = text.replace(/\\n/g, ' \\n ').split(' '); var line = ''; var totalLineHeight = 0; for (var n = 0; n < words.length; n++) { if (words[n].length == 0) continue; var testLine = line + words[n] + ' '; var metrics = context.measureText(testLine); var testWidth = metrics.width; if (words[n] != '\\n') if (testWidth > renderQuality && n > 0) { line = words[n] + ' '; totalLineHeight += lineHeight; } else { line = testLine; } else { totalLineHeight += lineHeight; line = ''; } } line = ''; canvasYCounter = canvasYCounter - totalLineHeight / 2; var firstSkip = false; for (var n = 0; n < words.length; n++) { if (words[n].length == 0) continue; var testLine = line + words[n] + ' '; var metrics = context.measureText(testLine); var testWidth = metrics.width; if (words[n] != '\\n') if (testWidth > renderQuality && n > 0) { context.fillText(line, centerX, canvasYCounter); line = words[n] + ' '; canvasYCounter += lineHeight; } else { line = testLine; } else { context.fillText(line, centerX, canvasYCounter); line = ''; canvasYCounter += firstSkip ? lineHeight * 2 : lineHeight; if (firstSkip) firstSkip = false; } } context.fillText(line, centerX, canvasYCounter); } if (appearence.image != undefined) { var imageAddress = appearence.image.address != undefined ? appearence.image.address : appearence.image.iconid != undefined ? `/stc/materialicons/white/${appearence.image.iconid}.png` : null; var imageSize = appearence.image.size != undefined ? appearence.image.size / 100 * renderQuality : renderQuality; if (imageAddress) { var centerX = renderQuality / 2 + appearence.image.offsetX / 100 * renderQuality; var centerY = renderQuality / 2 + appearence.image.offsetY / 100 * renderQuality; if (KeyHandler.imagecache[imageAddress] != undefined) renderImage(KeyHandler.imagecache[imageAddress]); else { KeyHandler.imagecache[imageAddress] = new Image(); KeyHandler.imagecache[imageAddress].src = imageAddress; KeyHandler.imagecache[imageAddress].onload = () => renderImage(KeyHandler.imagecache[imageAddress]); } function renderImage(image) { context.save(); context.translate(centerX, centerY); context.rotate(appearence.image.rotation * Math.PI / 180); context.drawImage( image, imageSize / 2 - imageSize, imageSize / 2 - imageSize, imageSize, imageSize ); context.restore(); } } } if (appearence.system != undefined) { if (appearence.system.border != undefined) { var relativeThickness = appearence.system.border.thickness / 100 * renderQuality; context.fillStyle = appearence.system.border.color; context.fillRect(0, 0, renderQuality, relativeThickness); context.rect(0, renderQuality - relativeThickness, renderQuality, relativeThickness); context.rect(0, 0, relativeThickness, renderQuality); context.rect(renderQuality - relativeThickness, 0, relativeThickness, renderQuality); context.fill(); } if (appearence.system.ghost == true) { var size = 50 / 100 * renderQuality; context.save(); context.globalAlpha = 0.7; context.translate(renderQuality / 2, renderQuality / 2); context.drawImage(KeyHandler.ghostImage, size / 2 - size, size / 2 - size, size, size); context.restore(); } } } }, select(x: string, y: string, multi: boolean = false) { if (PageHandler.currentPage != null) { if (PageHandler.currentPage.keys[y] != undefined && PageHandler.currentPage.keys[y][x] != undefined) { // var keyConfig = PageHandler.currentPage.keys[y][x]; var query = `${x},${y}`; if (multi) { if (!KeyHandler.selected.includes(query)) KeyHandler.selected.push(query); } else KeyHandler.selected = [ query ]; document.querySelectorAll('.key').forEach((key: HTMLDivElement) => { var checkX = key.getAttribute('x'); var checkY = key.getAttribute('y'); var checkQuery = `${checkX},${checkY}`; if (KeyHandler.selected.includes(checkQuery)) key.classList.add('selected'); else key.classList.remove('selected'); }); if (KeyHandler.selected.length > 1) Editor.close(); else { socket.emit('page', 'getkey', PageHandler.currentPageID, x, y, (key: Page_Key) => { Editor.open(x, y, key); }); } } } } }; KeyHandler.init(); var altDown = false; window.addEventListener('keydown', (e: KeyboardEvent) => { if (e.key == 'alt' || e.altKey == true) altDown = true; }); window.addEventListener('keyup', (e: KeyboardEvent) => { if (e.key == 'alt' || e.altKey == false) altDown = false; }); interface KeyHandler { elements: { keys: { [y: string]: { [x: string]: { key: HTMLDivElement; canvas: HTMLCanvasElement; context: CanvasRenderingContext2D; }; }; }; }; selected: string[]; imagecache: { [iconID: string]: HTMLImageElement }; ghostImage: any; init: () => void; clear: () => void; render: (x: string, y: string, data: Page_Key) => void; select: (x: string, y: string, multi?: boolean) => void; } interface Page_Key { id: string; state: { type: KeyTypes; toggle: boolean; confirm: boolean; }; actions: Page_Key_ActionTypes; appearence: Page_Key_Appearence; } interface Page_Key_Appearence { text?: Page_Key_Text; image?: Page_Key_Image; background?: Page_Key_Background; system?: { border?: { color: string; thickness: number; }; ghost?: boolean; }; } interface Page_Key_Text { value: string; size: number; color: string; offsetX: number; offsetY: number; } interface Page_Key_Image { address?: string; iconid?: string; iconstyle?: 'black' | 'white'; size: number; offsetX: number; offsetY: number; rotation: number; } interface Page_Key_Background { color: string; } type KeyTypes = 'empty' | 'custom' | 'pageup' | 'pagedown' | 'currentpage' | 'ghost';