Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions code/keebs.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* </div>
* <form>
* <fieldset><input type="radio" name="layers"/>…</fieldset>
* <fieldset><input type="radio" name="geopetry"/>…</fieldset>
* <fieldset><input type="radio" name="geometry"/>…</fieldset>
* </form>
* <dialog>
* <input placeholder="…"/>
Expand Down Expand Up @@ -40,12 +40,12 @@ window.addEventListener('DOMContentLoaded', () => {
* - the first class can be used as the x-keyboard geometry.
*/
const geometryClasses = {
'ISO-A': ['iso', 'intlBackslash', 'am'], // am = angle-mod CSS hack
'ISO': ['iso', 'intlBackslash'], // default / pre-selected value
'ANSI': ['ansi'],
'TMx': ['ol60', 'ergo'],
'4×6': ['ol50', 'ergo'],
'3×6': ['ol40', 'ergo'],
'iso-a': ['iso', 'intlBackslash', 'am'], // am = angle-mod CSS hack
'iso': ['iso', 'intlBackslash'], // default / pre-selected value
'ansi': ['ansi'],
'ol60': ['ol60', 'ergo'],
'ol50': ['ol50', 'ergo'],
'ol40': ['ol40', 'ergo'],
};

for (const keeb of document.querySelectorAll('.keyboard')) {
Expand Down
4 changes: 2 additions & 2 deletions code/layout-analyzer.js
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ window.addEventListener('DOMContentLoaded', () => {

// keyboard state: these <select> element IDs match the x-keyboard properties
// -- but the `layout` property requires a JSON fetch
const IDs = ['layout', 'geometry', 'corpus'];
const IDs = ['layout', 'geometry', 'corpus', 'layers'];
const setProp = (key, value) => {
if (key === 'layout') {
if (value) {
Expand Down Expand Up @@ -601,7 +601,7 @@ window.addEventListener('DOMContentLoaded', () => {
IDs.map(prop => state[prop]).join('/').replace(/\/+$/, '');
};
const applyHashState = () => {
const hash = window.location.hash || '/ergol//en+fr';
const hash = window.location.hash || '/ergol//en+fr/altgr';
const hashState = hash.split('/').slice(1);
IDs.forEach((key, i) => {
setProp(key, hashState[i] || '');
Expand Down
137 changes: 108 additions & 29 deletions code/x-keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,24 @@ function newKeyboardLayout(keyMap = {}, deadKeys = {}, geometry = '') {
let pendingDK;
let platform = '';

// Add 1DK levels
const odk = deadKeys['**'];
if (odk !== undefined) {
keyMap = Object.fromEntries(
Object.entries(keyMap).map(([key, levels]) => {
if (levels.length < 4) {
levels = levels.concat(new Array(4 - levels.length));
}
levels = Array.from(levels);
if (levels.length <= 4) {
levels.push(odk[levels[0]]);
levels.push(odk[levels[1]]);
}
return [key, levels];
})
);
}

return {
get keyMap() {
return keyMap;
Expand Down Expand Up @@ -374,12 +392,15 @@ const keyLevel = (level, label, position) => {
const symbol = symbols[label] || '';
const content = symbol || (label || '').slice(-1);
let className = '';
if (level > 4) {
let levelPrefix = 'level';
if (level < 0) {
className = 'dk';
levelPrefix = 'dk';
level = -level;
} else if (isDeadKey(label)) {
className = `deadKey ${symbol.startsWith(' ') ? 'diacritic' : ''}`;
}
return text(content, `level${level} ${className}`, attrs);
return text(content, `${levelPrefix}${level} ${className}`, attrs);
};

// In order not to overload the `alt` layers visually (AltGr & dead keys),
Expand Down Expand Up @@ -410,17 +431,20 @@ function drawKey(element, keyMap) {
* So if the lowercase version of the `shift` layer does not match the `base`
* layer, we'll show the lowercase letter (e.g. Greek 'ς').
*/
const [l1, l2, l3, l4] = keyChars;
const [l1, l2, l3, l4, l5, l6] = keyChars;
const base = l1.toUpperCase() !== l2 ? l1 : '';
const shift = base || l2.toLowerCase() === l1 ? l2 : l1;
const salt = altUpperChar(l3, l4);
const sodk = altUpperChar(l5, l6);
element.innerHTML = `
${keyLevel(1, base, { x: 0.28, y: 0.79 })}
${keyLevel(2, shift, { x: 0.28, y: 0.41 })}
${keyLevel(3, l3, { x: 0.7, y: 0.79 })}
${keyLevel(4, salt, { x: 0.7, y: 0.41 })}
${keyLevel(5, '', { x: 0.7, y: 0.79 })}
${keyLevel(6, '', { x: 0.7, y: 0.41 })}
${keyLevel(5, l5, { x: 0.7, y: 0.79 })}
${keyLevel(6, sodk, { x: 0.7, y: 0.41 })}
${keyLevel(-1, '', { x: 0.7, y: 0.79 })}
${keyLevel(-2, '', { x: 0.7, y: 0.41 })}
`;
}

Expand All @@ -441,8 +465,8 @@ function drawDK(element, keyMap, deadKey) {
const alt0 = deadKey[keyChars[0]];
const alt1 = deadKey[keyChars[1]];

drawChar(element.querySelector('.level5'), alt0);
drawChar(element.querySelector('.level6'), altUpperChar(alt0, alt1));
drawChar(element.querySelector('.dk1'), alt0);
drawChar(element.querySelector('.dk2'), altUpperChar(alt0, alt1));
}

/**
Expand Down Expand Up @@ -943,8 +967,8 @@ const themes = `
}

/* dimmed AltGr + bold dead keys */
.level3, .level4 { fill: ${KEY_COLOR_L3}; opacity: .5; }
.level5, .level6 { fill: ${KEY_COLOR_L5}; }
.level3, .level4 { fill: ${KEY_COLOR_L3}; opacity: .5; }
.level5, .level6, .dk { fill: ${KEY_COLOR_L5}; }
.deadKey {
fill: ${DEAD_KEY_COLOR};
font-size: 14px;
Expand All @@ -954,25 +978,47 @@ const themes = `
font-weight: bolder;
}

.layers-odk .level3,
.layers-odk .level4 { display: none; }
.layers-odk.altgr .level3,
.layers-odk.altgr .level4 { display: block; }

.layers-altgr .level5,
.layers-altgr .level6 { display: none; }

.layers-mixed .level5 { transform: translate(0, -22.8px); }
.layers-mixed .level6 { display: none; }

/* hide Level4 (Shift+AltGr) unless AltGr is pressed */
.level4 { display: none; }
.altgr .level4 { display: block; }

.altgr .level5,
.altgr .level6 { display: none; }

/* hide Level6 (Shift+AltGr) */
.level6 { display: none; }

/* hide dk1 and dk2 unless a dead key is pressed */
.dk1, .dk2 { display: none; }
.dk .dk1,
.dk .dk2 { display: block; }

/* highlight AltGr + Dead Keys */
.dk .level1, .altgr .level1,
.dk .level2, .altgr .level2 { opacity: 0.25; }
.dk .level5, .altgr .level3,
.dk .level6, .altgr .level4 { opacity: 1; }
.dk .level3,
.dk .level4 { display: none; }
.dk .dk1, .altgr .level3,
.dk .dk2, .altgr .level4 { opacity: 1; }
.dk .level3, .dk level4,
.dk .level5, .dk level6 { display: none; }

@media (prefers-color-scheme: dark) {
rect, path { stroke: #777; fill: #4d4d4d; }
.specialKey, .specialKey rect, .specialKey path { fill: #333; }
g:target rect, .press rect, g:target path, .press path { fill: #558; }
text { fill: #bbb; }
.level3, .level4 { fill: #99f; }
.level5, .level6 { fill: #6d6; }
.level5, .level6, .dk { fill: #6d6; }
.deadKey { fill: #f44; }

[theme="reach"] .pinkyKey rect { fill: hsl( 0, 20%, 30%); }
Expand Down Expand Up @@ -1069,11 +1115,13 @@ class Keyboard extends HTMLElement {
this.root = this.attachShadow({ mode: 'open' });
this.root.appendChild(template.content.cloneNode(true));
this._state = {
geometry: this.getAttribute('geometry') || '',
layers: this.getAttribute('layers') || 'altgr',
geometry: this.getAttribute('geometry') || 'iso',
platform: this.getAttribute('platform') || '',
theme: this.getAttribute('theme') || '',
layout: newKeyboardLayout(),
};
this.layers = this._state.layers;
this.geometry = this._state.geometry;
this.platform = this._state.platform;
this.theme = this._state.theme;
Expand Down Expand Up @@ -1114,6 +1162,24 @@ class Keyboard extends HTMLElement {
});
}

get layers() {
return this._state.layers;
}

set layers(value) {
const supportedLayers = ['odk', 'mixed', 'altgr'];
if (!value && !supportedLayers.includes(value)) {
return;
}
const svg = this.root.querySelector('svg');
const mkClass = suffix => `layers-${suffix}`
if (this._state.layers) {
svg.classList.remove(mkClass(this._state.layers));
}
this._state.layers = value;
svg.classList.add(mkClass(this._state.layers));
}

get geometry() {
return this._state.geometry;
}
Expand All @@ -1135,24 +1201,30 @@ class Keyboard extends HTMLElement {
* OL40 = OLKB Planck
*/
const supportedShapes = {
alt: 'alt intlYen',
ks: 'alt intlYen ks',
jis: 'iso intlYen intlRo jis',
abnt: 'iso intlBackslash intlRo',
iso: 'iso intlBackslash',
ansi: '',
ol60: 'ergo ol60',
ol50: 'ergo ol50',
ol40: 'ergo ol40',
alt: ['alt', 'intlYen'],
ks: ['alt', 'intlYen', 'ks'],
jis: ['iso', 'intlYen', 'intlRo', 'jis'],
abnt: ['iso', 'intlBackslash', 'intlRo'],
iso: ['iso', 'intlBackslash'],
ansi: [],
ol60: ['ergo', 'ol60'],
ol50: ['ergo', 'ol50'],
ol40: ['ergo', 'ol40'],
};
if (value && !(value in supportedShapes)) {
return;
}
this._state.geometry = value;
const geometry = value || this.layout.geometry || 'ansi';
const shape = supportedShapes[geometry];
this.root.querySelector('svg').className.baseVal = shape;
setFingerAssignment(this.root, !shape.startsWith('iso'));
const svg = this.root.querySelector('svg');
Object.values(supportedShapes).forEach(
classes => classes.forEach(cls =>
svg.classList.remove(cls)
)
);
shape.forEach(cls => svg.classList.add(cls));
setFingerAssignment(this.root, !shape.includes('iso'));
}

get platform() {
Expand Down Expand Up @@ -1181,9 +1253,7 @@ class Keyboard extends HTMLElement {
this._state.layout = value;
this._state.layout.platform = this.platform;
this.geometry = this._state.geometry;
Array.from(this.root.querySelectorAll('.key')).forEach(key =>
drawKey(key, value.keyMap),
);
this.draw();
}

get fingerAssignments() {
Expand All @@ -1207,6 +1277,15 @@ class Keyboard extends HTMLElement {
.filter(element => !element.id.startsWith('row_'));
}

/**
* Drawing
*/
draw() {
Array.from(this.root.querySelectorAll('.key')).forEach(key =>
drawKey(key, this._state.layout.keyMap),
);
}

/**
* KeyboardEvent helpers
*/
Expand Down
17 changes: 17 additions & 0 deletions www/content/stats.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@
<div id="sticky-select">
<form>
{{< keymap-list >}}
<select id="layers">
<option
value="odk"
title="caractères obtenus par la touche « Typo » (vue simplifiée)">
Typo
</option>
<option
value="mixed"
title="caractères obtenus par les touches « Typo » et « Symboles » (AltGr)">
Typo+Symboles
</option>
<option
value="altgr" checked
title="caractères obtenus par la touche « Symboles » (AltGr)">
Symboles
</option>
</select>
<select id="geometry">
<option value="iso" selected>ISO</option>
<option value="ansi">ANSI</option>
Expand Down
12 changes: 6 additions & 6 deletions www/layouts/shortcodes/x-keyboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
title="caractères obtenus par la touche « Symboles »">
</fieldset>
<fieldset class="geometry">
<input type="radio" name="geometry" label="3×6" value="3×6"
<input type="radio" name="geometry" label="3×6" value="ol40"
title="géométrie 40 % type OLKB Planck">
<input type="radio" name="geometry" label="4×6" value="4×6"
<input type="radio" name="geometry" label="4×6" value="ol50"
title="géométrie 50 % type OLKB Preonic">
<input type="radio" name="geometry" label="TMx" value="TMx"
<input type="radio" name="geometry" label="TMx" value="ol60"
title="géométrie 60 % type TypeMatrix">
<input type="radio" name="geometry" label="ANSI" value="ANSI"
<input type="radio" name="geometry" label="ANSI" value="ansi"
title="géométrie américaine">
<input type="radio" name="geometry" label="ISO" value="ISO" checked
<input type="radio" name="geometry" label="ISO" value="iso" checked
title="géométrie européenne">
<input type="radio" name="geometry" label="ISO-A" value="ISO-A"
<input type="radio" name="geometry" label="ISO-A" value="iso-a"
title="géométrie européenne avec variante en « A »">
</fieldset>
</form>
Expand Down