|
| 1 | +import { getSupportedChars, analyzeKeyboardLayout } from './layout-analyzer.js'; |
| 2 | + |
| 3 | +window.addEventListener('DOMContentLoaded', () => { |
| 4 | + const inputField = document.querySelector('input'); |
| 5 | + const keyboard = document.querySelector('x-keyboard'); |
| 6 | + |
| 7 | + const headingColor = getComputedStyle(document.querySelector('h1')).color; |
| 8 | + |
| 9 | + let corpusName = ''; |
| 10 | + let corpus = {}; |
| 11 | + let keyChars = {}; |
| 12 | + let impreciseData = false; |
| 13 | + |
| 14 | + // display a percentage value |
| 15 | + const fmtPercent = (num, p) => `${Math.round(10 ** p * num) / 10 ** p}%`; |
| 16 | + const showPercent = (sel, num, precision) => { |
| 17 | + document.querySelector(sel).innerText = fmtPercent(num, precision); |
| 18 | + }; |
| 19 | + const showPercentAll = (sel, nums, precision) => { |
| 20 | + document.querySelector(sel).innerText = |
| 21 | + nums.map(value => fmtPercent(value, precision)).join(' / '); |
| 22 | + }; |
| 23 | + |
| 24 | + const showNGrams = (ngrams) => { |
| 25 | + const sum = dict => Object.entries(dict).reduce((acc, [_, e]) => acc + e, 0); |
| 26 | + |
| 27 | + showPercent('#sfu-total', sum(ngrams.sfb), 2); |
| 28 | + showPercent('#sku-total', sum(ngrams.skb), 2); |
| 29 | + |
| 30 | + showPercent('#sfu-all', sum(ngrams.sfb), 2); |
| 31 | + showPercent('#extensions-all', sum(ngrams.lsb), 2); |
| 32 | + showPercent('#scissors-all', sum(ngrams.scissor), 2); |
| 33 | + |
| 34 | + showPercent('#inward-all', sum(ngrams.inwardRoll), 1); |
| 35 | + showPercent('#outward-all', sum(ngrams.outwardRoll), 1); |
| 36 | + showPercent('#sku-all', sum(ngrams.skb), 2); |
| 37 | + |
| 38 | + showPercent('#sks-all', sum(ngrams.sks), 1); |
| 39 | + showPercent('#sfs-all', sum(ngrams.sfs), 1); |
| 40 | + showPercent('#redirect-all', sum(ngrams.redirect), 1); |
| 41 | + showPercent('#bad-redirect-all', sum(ngrams.badRedirect), 2); |
| 42 | + |
| 43 | + const achoppements = document.querySelector('#achoppements stats-table'); |
| 44 | + achoppements.updateTableData('#sfu-bigrams', ngrams.sfb, 2); |
| 45 | + achoppements.updateTableData('#extended-rolls', ngrams.lsb, 2); |
| 46 | + achoppements.updateTableData('#scissors', ngrams.scissor, 2); |
| 47 | + |
| 48 | + const bigrammes = document.querySelector('#bigrammes stats-table'); |
| 49 | + bigrammes.updateTableData('#sku-bigrams', ngrams.skb, 2); |
| 50 | + bigrammes.updateTableData('#inward', ngrams.inwardRoll, 2); |
| 51 | + bigrammes.updateTableData('#outward', ngrams.outwardRoll, 2); |
| 52 | + |
| 53 | + const trigrammes = document.querySelector('#trigrammes stats-table'); |
| 54 | + trigrammes.updateTableData('#sks', ngrams.sks, 2); |
| 55 | + trigrammes.updateTableData('#sfs', ngrams.sfs, 2); |
| 56 | + trigrammes.updateTableData('#redirect', ngrams.redirect, 2); |
| 57 | + trigrammes.updateTableData('#bad-redirect', ngrams.badRedirect, 2); |
| 58 | + }; |
| 59 | + |
| 60 | + const showReport = () => { |
| 61 | + const report = analyzeKeyboardLayout(keyboard, corpus, keyChars, headingColor); |
| 62 | + |
| 63 | + document.querySelector('#sfu stats-canvas').renderData({ |
| 64 | + values: report.totalSfuSkuPerFinger, |
| 65 | + maxValue: 4, |
| 66 | + precision: 2, |
| 67 | + flipVertically: true, |
| 68 | + detailedValues: true, |
| 69 | + }); |
| 70 | + |
| 71 | + document.querySelector('#load stats-canvas').renderData({ |
| 72 | + values: report.loadGroups, |
| 73 | + maxValue: 25, |
| 74 | + precision: 1 |
| 75 | + }); |
| 76 | + |
| 77 | + const sumUpBar = bar => bar.good + bar.meh + bar.bad; |
| 78 | + const sumUpBarGroup = group => group.reduce((acc, bar) => acc + sumUpBar(bar), 0); |
| 79 | + |
| 80 | + showPercentAll('#load small', report.loadGroups.map(sumUpBarGroup), 1); |
| 81 | + showPercent('#unsupported-all', report.totalUnsupportedChars, 3); |
| 82 | + |
| 83 | + document.querySelector('#imprecise-data').style.display |
| 84 | + = report.impreciseData ? 'block' : 'none'; |
| 85 | + |
| 86 | + document |
| 87 | + .querySelector('#achoppements stats-table') |
| 88 | + .updateTableData('#unsupported', report.unsupportedChars, 3); |
| 89 | + |
| 90 | + showNGrams(report.ngrams); |
| 91 | + }; |
| 92 | + |
| 93 | + // keyboard state: these <select> element IDs match the x-keyboard properties |
| 94 | + // -- but the `layout` property requires a JSON fetch |
| 95 | + const IDs = ['layout', 'geometry', 'corpus']; |
| 96 | + const setProp = (key, value) => { |
| 97 | + if (key === 'layout') { |
| 98 | + if (value) { |
| 99 | + const layoutFolder = document |
| 100 | + .querySelector(`#layout option[value="${value}"]`).dataset.folder; |
| 101 | + fetch(`../keymaps/${layoutFolder}/${value}.json`) |
| 102 | + .then(response => response.json()) |
| 103 | + .then(data => { |
| 104 | + const selectedOption = document |
| 105 | + .querySelector('#layout option:checked') |
| 106 | + .textContent.trim() || value; |
| 107 | + inputField.placeholder = `zone de saisie ${selectedOption}`; |
| 108 | + keyboard.setKeyboardLayout( |
| 109 | + data.keymap, |
| 110 | + data.deadkeys, |
| 111 | + data.geometry.replace('ergo', 'iso'), |
| 112 | + ); |
| 113 | + data.keymap.Enter = ['\r', '\n']; |
| 114 | + keyChars = getSupportedChars(data.keymap, data.deadkeys); |
| 115 | + showReport(); |
| 116 | + }); |
| 117 | + } else { |
| 118 | + keyboard.setKeyboardLayout(); |
| 119 | + keyChars = {}; |
| 120 | + inputField.placeholder = 'select a keyboard layout'; |
| 121 | + } |
| 122 | + } else if (key === 'corpus') { |
| 123 | + if (value && value !== corpusName) { |
| 124 | + fetch(`../corpus/${value}.json`) |
| 125 | + .then(response => response.json()) |
| 126 | + .then(data => { |
| 127 | + corpus = data; |
| 128 | + showReport(); |
| 129 | + }); |
| 130 | + corpusName = value; |
| 131 | + } |
| 132 | + } else { |
| 133 | + keyboard[key] = value; |
| 134 | + } |
| 135 | + document.getElementById(key).value = value; |
| 136 | + }; |
| 137 | + |
| 138 | + // store the keyboard state in the URL hash like it's 1995 again! :-) |
| 139 | + const state = {}; |
| 140 | + const updateHashState = (key, value) => { |
| 141 | + state[key] = value; |
| 142 | + window.location.hash = '/' + |
| 143 | + IDs.map(prop => state[prop]).join('/').replace(/\/+$/, ''); |
| 144 | + }; |
| 145 | + const applyHashState = () => { |
| 146 | + const hash = window.location.hash || '/ergol//en+fr'; |
| 147 | + const hashState = hash.split('/').slice(1); |
| 148 | + IDs.forEach((key, i) => { |
| 149 | + setProp(key, hashState[i] || ''); |
| 150 | + state[key] = hashState[i] || ''; |
| 151 | + }); |
| 152 | + }; |
| 153 | + IDs.forEach(key => { |
| 154 | + document |
| 155 | + .getElementById(key) |
| 156 | + .addEventListener('change', event => { |
| 157 | + updateHashState(key, event.target.value); |
| 158 | + }); |
| 159 | + }); |
| 160 | + window.addEventListener('hashchange', applyHashState); |
| 161 | + applyHashState(); |
| 162 | +}); |
0 commit comments