change(developer): refactor constants into separate object in touch layout editor
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled

In order to resolve initialization order (see instigating property
`builder.lastPresentations`), split all constants into a separate object
`builderConstants`, inverting the dependency, which is both cleaner and
the first step of removing the multi-module initialization of a single
`builder` variable.
This commit is contained in:
Marc Durdin 2026-09-07 07:12:54 +02:00
parent d86b2b5926
commit 98fee32e90
4 changed files with 107 additions and 88 deletions

View file

@ -1,6 +1,8 @@
/// <reference path="ext/index.d.cts"/>
/// <reference path="ext/jquery-ui/index.d.cts"/>
import { builderConstants } from "./constants.js";
// temporary interface to streamline transform to Typescript
interface Builder {
[key: string]: any;
@ -15,8 +17,9 @@ export const builder: Builder = {
xscale: 1,
yscale: 1,
uniqId: 1,
lastPresentations: {...builder.defaultPresentations}, // TODO: this has an initialization order problem
lastPresentations: {...builderConstants.defaultPresentations},
lastLayers: {},
showAllModifierCombinations: false,
getPresentation: function () {
return $('#selPlatformPresentation').val();
},
@ -62,7 +65,7 @@ export const builder: Builder = {
prepareKeyCapTypes: function() {
let types = $('#selKeyCapType'), subTypes = $('#selSubKeyCapType'), opts = '';
for(let name of builder.specialKeyNames) {
for(let name of builderConstants.specialKeyNames) {
opts += '<option value="'+name+'">'+builder.renameSpecialKey(name)+' &nbsp; &nbsp; '+name+'</option>';
}
$(types).append(opts);
@ -111,8 +114,8 @@ export const builder: Builder = {
renameSpecialKey: function (oldText) {
//Note: U+E000 *is* PUA but was not accepted by IE as a character in the EOT font, so Alt recoded as U+E019
return builder.specialCharacters[oldText] ?
String.fromCharCode(0xE000 + builder.specialCharacters[oldText]) :
return builderConstants.specialCharacters[oldText] ?
String.fromCharCode(0xE000 + builderConstants.specialCharacters[oldText]) :
oldText;
},
@ -151,7 +154,7 @@ export const builder: Builder = {
},
fillModifierSelect: function() {
var modifiers = builder.showAllModifierCombinations ? builder.validModifierCombinations : builder.minimalModifierCombinations;
var modifiers = builder.showAllModifierCombinations ? builderConstants.validModifierCombinations : builderConstants.minimalModifierCombinations;
var
$selKeyLayerOverride = $('#selKeyLayerOverride'),
@ -179,7 +182,7 @@ export const builder: Builder = {
for (var modifier = 0; modifier < modifiers.length; modifier++) {
var name = builder.getModifierCombinationName(modifiers[modifier]);
var name = builderConstants.getModifierCombinationName(modifiers[modifier]);
add($selKeyLayerOverride, name);
add($selSubKeyLayerOverride, name);
@ -187,7 +190,7 @@ export const builder: Builder = {
}
var alreadyAdded = function(modifier) {
return builder.minimalModifierCombinations.indexOf(modifier) >= 0;
return builderConstants.minimalModifierCombinations.indexOf(modifier) >= 0;
};
@ -219,9 +222,9 @@ export const builder: Builder = {
if(!builder.showAllModifierCombinations) {
// Add any layer names that are already referenced
for(modifier = 0; modifier < builder.validModifierCombinations.length; modifier++) {
var name = builder.getModifierCombinationName(builder.validModifierCombinations[modifier]);
if(!alreadyAdded(builder.validModifierCombinations[modifier]) && isUsed(name)) {
for(modifier = 0; modifier < builderConstants.validModifierCombinations.length; modifier++) {
var name = builderConstants.getModifierCombinationName(builderConstants.validModifierCombinations[modifier]);
if(!alreadyAdded(builderConstants.validModifierCombinations[modifier]) && isUsed(name)) {
add($selKeyLayerOverride, name);
add($selSubKeyLayerOverride, name);
}
@ -230,16 +233,16 @@ export const builder: Builder = {
},
getModifierCombinationFromLayerId: function(id) {
for(var i = 0; i < builder.validModifierCombinations.length; i++) {
if(builder.getModifierCombinationName(builder.validModifierCombinations[i]) == id) {
return builder.validModifierCombinations[i];
for(var i = 0; i < builderConstants.validModifierCombinations.length; i++) {
if(builderConstants.getModifierCombinationName(builderConstants.validModifierCombinations[i]) == id) {
return builderConstants.validModifierCombinations[i];
}
}
return 0;
},
isLayerIdShifted: function(id) {
return (builder.getModifierCombinationFromLayerId(id) & builder.modifierCodes.SHIFT) != 0;
return (builder.getModifierCombinationFromLayerId(id) & builderConstants.modifierCodes.SHIFT) != 0;
},
/**
@ -370,19 +373,19 @@ export const builder: Builder = {
var row = builder.addRow('bottom'), rowWidth = 0;
for (var j = 0; j < layer.row[i].key.length; j++) {
var key = layer.row[i].key[j];
rowWidth += (key.width ? parseInt(key.width, 10) : 100) + (key.pad ? parseInt(key.pad, 10) : builder.keyMargin);
rowWidth += (key.width ? parseInt(key.width, 10) : 100) + (key.pad ? parseInt(key.pad, 10) : builderConstants.keyMargin);
}
width = Math.max(width, rowWidth);
}
width += builder.keyMargin; // add right hand margin
var height = layer.row.length * (100 + builder.keyMargin); // 50% of tablet height, 100 px per row, 5 px margin
width += builderConstants.keyMargin; // add right hand margin
var height = layer.row.length * (100 + builderConstants.keyMargin); // 50% of tablet height, 100 px per row, 5 px margin
//
// Scaling for different platform images
//
var pres = builder.presentations[builder.getPresentation()];
var pres = builderConstants.presentations[builder.getPresentation()];
if (pres) {
builder.xscale = pres.x / width;
builder.yscale = pres.y / height;
@ -407,11 +410,11 @@ export const builder: Builder = {
var key = layer.row[i].key[j];
var nkey = builder.addKey('key', row, key.sp);
var w = key.width ? key.width : 100;
var p = (key.pad ? key.pad : builder.keyMargin) * builder.xscale;
var p = (key.pad ? key.pad : builderConstants.keyMargin) * builder.xscale;
let text = builder.inferKeyText(key.text, key.id);
calcKeyWidth += parseInt(w, 10);
calcGapWidth += parseInt(key.pad ? key.pad : builder.keyMargin, 10);
calcGapWidth += parseInt(key.pad ? key.pad : builderConstants.keyMargin, 10);
$(nkey)
.data('id', key.id)
@ -431,12 +434,12 @@ export const builder: Builder = {
.css('width', (w * builder.xscale) + 'px')
.css('height', (100 * builder.yscale) + 'px')
.css('margin-top', (builder.keyMargin * builder.yscale) + 'px')
.css('margin-top', (builderConstants.keyMargin * builder.yscale) + 'px')
.css('margin-left', p + 'px')
.css('font-family', builder.escapeFontName(key.font))
.css('font-size', key.fontsize);
if(builder.specialCharacters[text])
if(builderConstants.specialCharacters[text])
$(nkey).addClass('key-special-text');
builder.addKeyAnnotations(nkey);
@ -463,7 +466,7 @@ export const builder: Builder = {
keys.each(function(_index, keyElem) {
let width = $(keyElem).data('width'), pad = $(keyElem).data('pad');
keyWidth += width ? parseInt(width, 10) : 100;
gapWidth += pad ? parseInt(pad, 10) : builder.keyMargin;
gapWidth += pad ? parseInt(pad, 10) : builderConstants.keyMargin;
});
let count = keys.length, totalWidth = keyWidth + gapWidth;
$('.key-size', rowElem).html(count + ' keys<br>' +
@ -475,8 +478,8 @@ export const builder: Builder = {
getStandardKeyCap: function (id, shifted) {
id = id ? id.toUpperCase() : '';
var i = builder.standardKeyNames.findIndex(function(x) { return x.toUpperCase() == id });
return i >= 0 ? builder.standardKeyCaps[i][shifted ? 1 : 0] : '';
var i = builderConstants.standardKeyNames.findIndex(function(x) { return x.toUpperCase() == id });
return i >= 0 ? builderConstants.standardKeyCaps[i][shifted ? 1 : 0] : '';
},
updateKeyId: function (nkey) {
@ -489,21 +492,21 @@ export const builder: Builder = {
var listContainer = $('#selPlatformPresentation');
$('option', listContainer).remove();
for (var i in builder.presentations) {
for (var i in builderConstants.presentations) {
if (i.substring(0, builder.lastPlatform.length) != builder.lastPlatform) {
continue;
}
var option = $(document.createElement('option'));
option.attr('value', i).text(builder.presentations[i].name);
option.attr('value', i).text(builderConstants.presentations[i].name);
if(builder.lastPresentations[builder.lastPlatform] == i) {
option.attr('selected', true);
option.prop('selected', true);
}
listContainer.append(option);
}
builder.prepareLayers();
builder.selectLayer(builder.lastLayers[builder.lastPlatform] ?? 0);
}
},
selectLayer: function (val) {
let selection = builder.saveSelection();
@ -713,7 +716,7 @@ export const builder: Builder = {
k.data('text', val);
let text = builder.inferKeyText(val, k.data('id'));
$('.text', k).text(builder.renameSpecialKey(text));
if(builder.specialCharacters[text]) {
if(builderConstants.specialCharacters[text]) {
k.addClass('key-special-text');
} else {
k.removeClass('key-special-text');
@ -731,7 +734,7 @@ export const builder: Builder = {
} else {
hintElement.removeClass('custom-hint');
}
if(builder.specialCharacters[val]) {
if(builderConstants.specialCharacters[val]) {
hintElement.addClass('key-special-text');
} else {
hintElement.removeClass('key-special-text');
@ -754,7 +757,7 @@ export const builder: Builder = {
let val = $(key).data('text');
$('#keyToolbar *').removeAttr('disabled');
$('#sub-key-container').css('display', '');
$('#key-cap-unicode-toolbar-item, #key-cap-toolbar-item').css('display', builder.specialCharacters[val] ? 'none' : '');
$('#key-cap-unicode-toolbar-item, #key-cap-toolbar-item').css('display', builderConstants.specialCharacters[val] ? 'none' : '');
}
},
@ -850,7 +853,7 @@ export const builder: Builder = {
selectKeyByCode: function (code) {
if (code >= 0 && code < 256) {
var keyName = builder.standardKeyNames[code];
var keyName = builderConstants.standardKeyNames[code];
var key = $('.key').filter(function (_index, elem) { return $(elem).data('id') === keyName; });
if (key.length > 0) builder.selectKey(key[0]);
}
@ -900,7 +903,7 @@ export const builder: Builder = {
}
builder.selectPlatform();
if(data.presentation &&
builder.presentations[data.presentation] &&
builderConstants.presentations[data.presentation] &&
$('#selPlatformPresentation option[value="'+data.presentation+'"]').length) {
$('#selPlatformPresentation').val(data.presentation);
} else {
@ -908,7 +911,7 @@ export const builder: Builder = {
$('#selPlatformPresentation').val($('#selPlatformPresentation option:first').val());
}
builder.lastPresentations = {...(data.lastPresentations ?? this.defaultPresentations)};
builder.lastPresentations = {...(data.lastPresentations ?? builderConstants.defaultPresentations)};
builder.lastLayers = {...(data.lastLayers ?? {})};
let selection = builder.saveSelection();
@ -972,7 +975,7 @@ const inpKeyNameChange = builder.wrapChange(function (evt: JQueryEventObject) {
$('#inpKeyName')
.change(inpKeyNameChange)
.autocomplete({
source: builder.lookupKeyNames,
source: builderConstants.lookupKeyNames,
change: inpKeyNameChange,
select: builder.wrapInstant(inpKeyNameChange)
})

View file

@ -1,8 +1,7 @@
/// <reference path="ext/index.d.cts"/>
/// <reference path="ext/jquery-ui/index.d.cts"/>
import { builder } from './builder.js';
builder.standardKeyNames = [
const standardKeyNames = [
'K_?00', // &H0
'K_LBUTTON', // &H1
'K_RBUTTON', // &H2
@ -273,7 +272,7 @@ builder.standardKeyNames = [
'K_?FF' // &HFF
];
builder.standardKeyCaps = [
const standardKeyCaps = [
['',''], // 'K_?00', // &H0
['',''], // 'K_LBUTTON', // &H1
['',''], // 'K_RBUTTON', // &H2
@ -544,18 +543,18 @@ builder.standardKeyCaps = [
['',''], // 'K_?FF' // &HFF
];
builder.lookupKeyNames = [];
const lookupKeyNames = [];
for (var i = 0; i < builder.standardKeyNames.length; i++) {
if (builder.standardKeyNames[i].indexOf('?') < 0) {
builder.lookupKeyNames.push(builder.standardKeyNames[i]);
for (var i = 0; i < standardKeyNames.length; i++) {
if (standardKeyNames[i].indexOf('?') < 0) {
lookupKeyNames.push(standardKeyNames[i]);
}
}
builder.lookupKeyNames.sort();
lookupKeyNames.sort();
// Defines the PUA code mapping for the various 'special' modifier/control/non-printing keys on keyboards.
// This is lifted directly from specialCharacters.ts and must be kept in sync. See also CompileKeymanWeb.pas: CSpecialText10
builder.specialCharacters = {
const specialCharacters = {
'*Shift*': 8,
'*Enter*': 5,
'*Tab*': 6,
@ -623,9 +622,9 @@ builder.specialCharacters = {
// TODO: Skipping size references
};
builder.specialKeyNames = Object.entries(builder.specialCharacters).map(ch => ch[0]);
const specialKeyNames = Object.entries(specialCharacters).map(ch => ch[0]);
builder.presentations = {
const presentations = {
"tablet-ipad-landscape": { "x": 829, "y": 299, "name": "iPad (landscape)" }, // 829x622 = iPad tablet box size; (97,101)-(926,723)
"tablet-ipad-portrait": { "x": 605, "y": 300, "name": "iPad (portrait)" }, // 605x806 = iPad tablet box size; (98,94)-(703,900)
"phone-iphone5-landscape": { "x": 731, "y": 196, "name": "iPhone 5 (landscape)" }, // 731x412 = iPhone box size; (144,39)-(875,451)
@ -633,12 +632,12 @@ builder.presentations = {
"desktop": { "x": 640, "y": 300, "name": "Desktop" },
};
builder.defaultPresentations = { "tablet": "tablet-ipad-landscape", "phone": "phone-iphone5-landscape", "desktop": "desktop" };
const defaultPresentations = { "tablet": "tablet-ipad-landscape", "phone": "phone-iphone5-landscape", "desktop": "desktop" };
builder.keyMargin = 15;
const keyMargin = 15;
// from kmwosk.js:
builder.modifierCodes = {
const modifierCodes = {
"LCTRL":0x0001,
"RCTRL":0x0002,
"LALT":0x0004,
@ -657,47 +656,46 @@ builder.modifierCodes = {
// Lists the combinations that we allow users to use. Some are mutually exclusive,
// such as Left+Right modifiers, or chiral and non-chiral modifiers (excl. Shift)
builder.validModifierCombinations = [
const validModifierCombinations = [
0,
builder.modifierCodes.LCTRL,
builder.modifierCodes.RCTRL,
modifierCodes.LCTRL,
modifierCodes.RCTRL,
builder.modifierCodes.LALT,
builder.modifierCodes.LCTRL | builder.modifierCodes.LALT,
modifierCodes.LALT,
modifierCodes.LCTRL | modifierCodes.LALT,
builder.modifierCodes.RALT,
builder.modifierCodes.RCTRL | builder.modifierCodes.RALT,
modifierCodes.RALT,
modifierCodes.RCTRL | modifierCodes.RALT,
builder.modifierCodes.SHIFT,
modifierCodes.SHIFT,
builder.modifierCodes.LCTRL | builder.modifierCodes.SHIFT,
builder.modifierCodes.LALT | builder.modifierCodes.SHIFT,
builder.modifierCodes.LCTRL | builder.modifierCodes.LALT | builder.modifierCodes.SHIFT,
modifierCodes.LCTRL | modifierCodes.SHIFT,
modifierCodes.LALT | modifierCodes.SHIFT,
modifierCodes.LCTRL | modifierCodes.LALT | modifierCodes.SHIFT,
builder.modifierCodes.RCTRL | builder.modifierCodes.SHIFT,
builder.modifierCodes.RALT | builder.modifierCodes.SHIFT,
builder.modifierCodes.RCTRL | builder.modifierCodes.RALT | builder.modifierCodes.SHIFT,
modifierCodes.RCTRL | modifierCodes.SHIFT,
modifierCodes.RALT | modifierCodes.SHIFT,
modifierCodes.RCTRL | modifierCodes.RALT | modifierCodes.SHIFT,
builder.modifierCodes.CTRL,
builder.modifierCodes.CTRL | builder.modifierCodes.SHIFT,
modifierCodes.CTRL,
modifierCodes.CTRL | modifierCodes.SHIFT,
builder.modifierCodes.ALT,
builder.modifierCodes.CTRL | builder.modifierCodes.ALT,
builder.modifierCodes.SHIFT | builder.modifierCodes.ALT,
builder.modifierCodes.SHIFT | builder.modifierCodes.CTRL | builder.modifierCodes.ALT,
builder.modifierCodes.CAPS
modifierCodes.ALT,
modifierCodes.CTRL | modifierCodes.ALT,
modifierCodes.SHIFT | modifierCodes.ALT,
modifierCodes.SHIFT | modifierCodes.CTRL | modifierCodes.ALT,
modifierCodes.CAPS
];
builder.minimalModifierCombinations = [
const minimalModifierCombinations = [
0,
builder.modifierCodes.RALT,
builder.modifierCodes.SHIFT,
builder.modifierCodes.RALT | builder.modifierCodes.SHIFT,
builder.modifierCodes.CAPS
modifierCodes.RALT,
modifierCodes.SHIFT,
modifierCodes.RALT | modifierCodes.SHIFT,
modifierCodes.CAPS
];
builder.showAllModifierCombinations = false;
// Add CAPS variants for all of the above
(function addCapsCombinations(validCombinations, CAPS) {
@ -708,9 +706,9 @@ builder.showAllModifierCombinations = false;
validCombinations.push(newCombination);
}
}
})(builder.validModifierCombinations, builder.modifierCodes.CAPS);
})(validModifierCombinations, modifierCodes.CAPS);
builder.modifierNames = [
const modifierNames =[
'leftctrl', // 0x001
'rightctrl', // 0x002
'leftalt', // 0x004
@ -729,11 +727,27 @@ builder.modifierNames = [
* @param {Number} c
* @return {string}
**/
builder.getModifierCombinationName = function(c) {
const getModifierCombinationName = function(c) {
var r = '';
if(c == 0) return 'default';
for(var i = 0; i < builder.modifierNames.length; i++) {
if(c & (1<<i)) r += (r == '' ? '' : '-') + builder.modifierNames[i];
for(var i = 0; i < modifierNames.length; i++) {
if(c & (1<<i)) r += (r == '' ? '' : '-') + modifierNames[i];
}
return r;
};
export const builderConstants = {
standardKeyNames,
standardKeyCaps,
specialCharacters,
specialKeyNames,
presentations,
defaultPresentations,
keyMargin,
modifierCodes,
validModifierCombinations,
minimalModifierCombinations,
modifierNames,
lookupKeyNames,
getModifierCombinationName,
};

View file

@ -1,6 +1,7 @@
/// <reference path="ext/index.d.cts"/>
/// <reference path="ext/jquery-ui/index.d.cts"/>
import { builder } from './builder.js';
import { builderConstants } from './constants.js';
builder.addKey = function (type, position, sp) {
var key = document.createElement('div');
@ -95,7 +96,7 @@ builder.prepareKey = function () {
var key = builder.selectedKey();
builder.hasSavedKeyUndo = false;
let val = $(key).data('text');
$('#selKeyCapType').val(builder.specialCharacters[val] ? val : '');
$('#selKeyCapType').val(builderConstants.specialCharacters[val] ? val : '');
$('#inpKeyCap').val(val);
$('#inpKeyCapUnicode').val(builder.toUnicodeString(val));
@ -145,7 +146,7 @@ builder.prepareKey = function () {
.css('font-family',builder.escapeFontName(key.font))
.css('font-size', key.fontsize);
if(builder.specialCharacters[text]) {
if(builderConstants.specialCharacters[text]) {
$(nkey).addClass('key-special-text');
}

View file

@ -1,6 +1,7 @@
/// <reference path="ext/index.d.cts"/>
/// <reference path="ext/jquery-ui/index.d.cts"/>
import { builder } from './builder.js';
import { builderConstants } from './constants.js';
//
// Popup keys
@ -68,7 +69,7 @@ builder.prepareSubKey = function () {
$('#inpSubKeyGestureType').val(gestureType);
let val = $(key).data('text');
$('#selSubKeyCapType').val(builder.specialCharacters[val] ? val : '');
$('#selSubKeyCapType').val(builderConstants.specialCharacters[val] ? val : '');
$('#inpSubKeyCap').val(val);
$('#inpSubKeyCapUnicode').val(builder.toUnicodeString(val));
$('#inpSubKeyName').val($(key).data('id'));
@ -91,7 +92,7 @@ $('#inpSubKeyName')
.change(subKeyNameChange)
.on('input', subKeyNameChange)
.autocomplete({
source: builder.lookupKeyNames,
source: builderConstants.lookupKeyNames,
change: subKeyNameChange,
select: builder.wrapInstant(subKeyNameChange)
}).blur(function () {
@ -104,7 +105,7 @@ builder.subKeyCapChange = function(val) {
k.data('text', val);
let text = builder.inferKeyText(val, k.data('id'));
$('.text', k).text(builder.renameSpecialKey(text));
if(builder.specialCharacters[text]) {
if(builderConstants.specialCharacters[text]) {
k.addClass('key-special-text');
} else {
k.removeClass('key-special-text');
@ -293,7 +294,7 @@ builder.enableSubKeyControls = function () {
let val = $(key).data('text');
$('#subKeyToolbar *').removeAttr('disabled');
$('#subKeyToolbar #inpSubKeyGestureType').attr('disabled', 'disabled');
$('#sub-key-cap-unicode-toolbar-item, #sub-key-cap-toolbar-item').css('display', builder.specialCharacters[val] ? 'none' : '');
$('#sub-key-cap-unicode-toolbar-item, #sub-key-cap-toolbar-item').css('display', builderConstants.specialCharacters[val] ? 'none' : '');
$('#chkSubKeyIsDefault').prop('disabled', $(key).data('type') != 'longpress');
}
}