spiegel-keyman/developer/src/tike/xml/layoutbuilder/builder.js
Marc Durdin a637b58066 feat(developer): rework touch layout editor
* Restrucure touch layout editor to make space for additional gesture
  types:
  * Move control bar to right hand side
  * Move key cap edits to control bar.
* Adds flick and multitap gestures:
  * Adds buttons to add longpress keys, flicks or multitaps.
* Adds Unicode value boxes which mirror the key cap text box.
* Updates key width values in real time when resizing
* Maintain selected key as far as possible across layer switch, platform
  switch and presentation view switch.
* Starts to split 'special' key cap icons from normal key caps.
2022-07-14 10:54:59 +10:00

1017 lines
No EOL
32 KiB
JavaScript

$(function() {
(function(search) {
var q = search.match(/Filename=(.+)(&|$)/);
if(q) {
builder.filename = decodeURIComponent(q[1]);
}
})(location.search);
this.xscale = 1;
this.yscale = 1;
this.uniqId = 1;
this.getPresentation = function () {
var platform = $('#selPlatformPresentation').val();
//if(platform == 'tablet') return 'tablet-ipad';
//if(platform == 'phone') return 'phone-iphone5';
return platform;
}
this.saveSelection = function() {
let key = builder.selectedKey(), subKey = builder.selectedSubKey();
return {
id: key ? $(key).data('id') : null,
subId: subKey ? $(subKey).data('id') : null
};
}
this.restoreSelection = function(selection) {
let key = $('#kbd .key').filter(function (_index) { return $(this).data('id') === selection.id; }).first();
if(!key || !key.length) {
let keys = $('#kbd div.key');
if(!keys.length) return;
key = keys[0];
}
builder.selectKey(key);
let subKey = $('#sub-key-groups .key').filter(function (_index) { return $(this).data('id') === selection.subId; }).first();
if(!subKey || !subKey.length) {
let subKeys = $('#sub-key-groups div.key');
if(!subKeys.length) return;
subKey = subKeys[0];
}
builder.selectSubKey(subKey);
}
$('#selPlatformPresentation').change(function () {
let lastSelection = builder.saveSelection();
builder.selectKey(null, false);
builder.selectSubKey(null);
builder.prepareLayer();
builder.restoreSelection(lastSelection);
builder.saveState();
});
$('#chkShowAllModifierOptions').click(function () {
builder.showAllModifierCombinations = $('#chkShowAllModifierOptions')[0].checked;
builder.fillModifierSelect();
builder.prepareKey();
});
builder.removeAllSubKeys = function() {
$('#sub-key-groups .key').remove();
}
builder.prepareKeyCapTypes = function() {
let types = $('#selKeyCapType'), subTypes = $('#selKeyCapSubType'), opts = '';
for(let name of builder.specialKeyNames) {
opts += '<option value="'+name+'">'+builder.renameSpecialKey(name)+'\t'+name+'</option>';
}
$(types).append(opts);
// $(subTypes).append(opts);
};
this.preparePlatforms = function () {
var firstPlatform = null;
$('#platforms').empty();
for (var platform in KVKL) {
firstPlatform = firstPlatform || platform;
var ul = document.createElement('ul');
var li = document.createElement('li');
var a = document.createElement('a');
$(a).data('platform', platform).text(platform).click(function () {
builder.selectPlatform($(this).data('platform'));
}).attr('href', 'javascript:void(0)');
$(li).append(a).append(ul);
$('#platforms').append(li);
for (var layer in KVKL[platform].layer) {
li = document.createElement('li');
$(li).data('platform', platform).data('layer', layer).text(KVKL[platform].layer[layer].id).click(function () {
builder.selectPlatform($(this).data('platform'));
builder.selectLayer($(this).data('layer'));
});
$(ul).append(li);
}
}
$('#selPlatform option').remove();
for (var platform in KVKL) {
var opt = document.createElement('option');
$(opt).append(platform);
$('#selPlatform').append(opt);
}
builder.selectPlatform(firstPlatform);
}
/**
* Replace default key names by special font codes for modifier keys (copied from kmwosk.js)
*
* @param {string} oldText
* @return {string}
**/
this.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 this.specialCharacters[oldText] ?
String.fromCharCode(0xE000 + this.specialCharacters[oldText]) :
oldText;
}
this.prepareLayers = function () {
this.fillLayerSelect();
this.fillModifierSelect();
};
this.fillLayerSelect = function() {
$('#selLayer option').remove();
$('#selKeyNextLayer option').remove();
$('#selSubKeyNextLayer option').remove();
opt = document.createElement('option');
$(opt).attr('value', '').text('(none)');
$('#selKeyNextLayer').append(opt);
opt = document.createElement('option');
$(opt).attr('value', '').text('(none)');
$('#selSubKeyNextLayer').append(opt);
for (var layer in KVKL[builder.lastPlatform].layer) {
var opt = document.createElement('option');
$(opt).attr('value', layer)
.append(KVKL[builder.lastPlatform].layer[layer].id);
$('#selLayer').append(opt);
opt = document.createElement('option');
$(opt).append(KVKL[builder.lastPlatform].layer[layer].id);
$('#selKeyNextLayer').append(opt);
opt = document.createElement('option');
$(opt).append(KVKL[builder.lastPlatform].layer[layer].id);
$('#selSubKeyNextLayer').append(opt);
}
};
this.fillModifierSelect = function() {
var modifiers = this.showAllModifierCombinations ? this.validModifierCombinations : this.minimalModifierCombinations;
var
$selKeyLayerOverride = $('#selKeyLayerOverride'),
$selSubKeyLayerOverride = $('#selSubKeyLayerOverride'),
$addLayerList = $('#addLayerList');
var add = function(e, val, text) {
var opt = document.createElement('option');
if(typeof text != 'undefined') {
$(opt).attr('value', val);
$(opt).text(text);
} else {
$(opt).text(val);
}
e.append(opt);
};
$('#selKeyLayerOverride option').remove();
$('#selSubKeyLayerOverride option').remove();
$('#addLayerList option').remove();
add($selKeyLayerOverride, '', '(current layer)');
add($selSubKeyLayerOverride, '', '(current layer)');
add($addLayerList, '', '(custom)');
for (var modifier = 0; modifier < modifiers.length; modifier++) {
var name = this.getModifierCombinationName(modifiers[modifier]);
add($selKeyLayerOverride, name);
add($selSubKeyLayerOverride, name);
add($addLayerList, name);
}
var alreadyAdded = function(modifier) {
return builder.minimalModifierCombinations.indexOf(modifier) >= 0;
};
// check all keys for modifier usage
var modifierNames = [];
for(var i = 0; i < KVKL[builder.lastPlatform].layer.length; i++) {
var layer = KVKL[builder.lastPlatform].layer[i];
for(var j = 0; j < layer.row.length; j++) {
var row = layer.row[j];
for(var k = 0; k < row.key.length; k++) {
if(typeof row.key[k].layer != 'undefined' && modifierNames.indexOf(row.key[k].layer) < 0) {
modifierNames.push(row.key[k].layer);
}
if(typeof row.key[k].sk != 'undefined') {
for(var l = 0; l < row.key[k].sk.length; l++) {
if(typeof row.key[k].sk[l].layer != 'undefined' && modifierNames.indexOf(row.key[k].sk[l].layer) < 0) {
modifierNames.push(row.key[k].sk[l].layer);
}
}
}
}
}
}
var isUsed = function(modifierName) {
return modifierNames.indexOf(modifierName) >= 0;
};
if(!this.showAllModifierCombinations) {
// Add any layer names that are already referenced
for(modifier = 0; modifier < this.validModifierCombinations.length; modifier++) {
var name = this.getModifierCombinationName(this.validModifierCombinations[modifier]);
if(!alreadyAdded(this.validModifierCombinations[modifier]) && isUsed(name)) {
add($selKeyLayerOverride, name);
add($selSubKeyLayerOverride, name);
}
}
}
};
this.getModifierCombinationFromLayerId = function(id) {
for(var i = 0; i < this.validModifierCombinations.length; i++) {
if(this.getModifierCombinationName(this.validModifierCombinations[i]) == id) {
return this.validModifierCombinations[i];
}
}
return 0;
};
this.isLayerIdShifted = function(id) {
return (builder.getModifierCombinationFromLayerId(id) & this.modifierCodes.SHIFT) != 0;
};
/**
* Adds annotating images to a key cap to indicate when it has sub keys
* @param {*} key
* @param {*} longpress
* @param {*} flick
* @param {*} multitap
*/
this.addKeyAnnotations = function(key) {
const longpress = $(key).data('longpress'), flick = $(key).data('flick'), multitap = $(key).data('multitap');
$('.has-flick', key).remove();
if (flick && flick.length) {
for(let f of flick) {
$(key).append('<div class="has-flick has-flick-'+f.direction+'"></div>');
}
}
/* We add longpress after flick so that the longpress icon can be shifted down if there is a visual conflict */
$('.has-longpress', key).remove();
if (longpress && longpress.length) {
$(key).append('<div class="has-longpress"></div>');
}
$(key).remove('.has-multitap');
if (multitap && multitap.length) {
$(key).append('<div class="has-multitap"></div>');
}
}
this.prepareLayer = function () {
var layer = KVKL[builder.lastPlatform].layer[builder.lastLayerIndex];
var isLayerShifted = builder.isLayerIdShifted(layer.id);
var width = 0; // calculate the widest and rescale
for (var i = 0; i < layer.row.length; i++) {
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);
}
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
//
// Scaling for different platform images
//
var pres = this.presentations[builder.getPresentation()];
if (pres) {
this.xscale = pres.x / width;
this.yscale = pres.y / height;
} else {
this.xscale = 0.5;
this.yscale = 0.5;
}
$('#kbd,#sub-key-groups')
.css('font-family', KVKL[builder.lastPlatform].font)
.css('font-size', KVKL[builder.lastPlatform].fontsize || "1em");
$('#inpKeyCap,#inpSubKeyCap')
.css('font-family', KVKL[builder.lastPlatform].font)
.css('font-size', '18pt');
$('#kbd div').remove();
for (var i = 0; i < layer.row.length; i++) {
var row = builder.addRow('bottom');
var calcKeyWidth = 0, calcGapWidth = 0;
for (var j = 0; j < layer.row[i].key.length; j++) {
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) * this.xscale;
var code = key.id ? String.fromCharCode(parseInt(key.id.substring(2), 16)) : 0;
var text = typeof key.text == 'string' ? key.text : (code == '' || code.charCodeAt(0) < 32 ? '' : code);
calcKeyWidth += parseInt(w, 10);
calcGapWidth += parseInt(key.pad ? key.pad : builder.keyMargin, 10);
$(nkey)
.data('id', key.id)
.data('pad', key.pad)
.data('width', key.width)
.data('sp', key.sp)
.data('font', key.font)
.data('fontsize', key.fontsize)
.data('nextlayer', key.nextlayer)
.data('layer', key.layer)
.data('text', text)
.data('longpress', key.sk)
.data('flick', builder.translateFlickObjectToArray(key.flick))
.data('multitap', key.multitap)
.data('type', 'key')
.css('width', (w * this.xscale) + 'px')
.css('height', (100 * this.yscale) + 'px')
.css('margin-top', (builder.keyMargin * this.yscale) + 'px')
.css('margin-left', p + 'px')
.css('font-family', key.font)
.css('font-size', key.fontsize);
if(this.specialCharacters[text])
$(nkey).addClass('key-special-text');
builder.addKeyAnnotations(nkey);
$('.text', nkey).text(this.renameSpecialKey(text));
if(KVKL[builder.lastPlatform].displayUnderlying) $('.underlying', nkey).text(this.getStandardKeyCap(key.id, key.layer ? builder.isLayerIdShifted(key.layer) : isLayerShifted));
builder.updateKeyId(nkey);
}
var calcWidth = calcKeyWidth + calcGapWidth;
$('#kbd').attr('class', builder.getPresentation()); //builder.lastPlatform); //css('background-position-y', '-200px');
}
builder.updateKeySizeInfo(); //row, layer.row[i].key.length, calcKeyWidth, calcGapWidth);
// var div = document.createElement('div');
// $(div).css('clear', 'both');
// $('#kbd').append(div);
};
this.updateKeySizeInfo = function() {
let rows = $('#kbd div.row');
rows.each(function() {
let keys = $('.key', this), keyWidth = 0, gapWidth = 0;
keys.each(function() {
let width = $(this).data('width'), pad = $(this).data('pad');
keyWidth += width ? parseInt(width, 10) : 100;
gapWidth += pad ? parseInt(pad, 10) : builder.keyMargin;
});
let count = keys.length, totalWidth = keyWidth + gapWidth;
$('.key-size', this).html(count + ' keys<br>' +
keyWidth + ' key width<br>' +
gapWidth + ' padding<br>' +
totalWidth + ' total');
});
}
this.getStandardKeyCap = function (id, shifted) {
id = id ? id.toUpperCase() : '';
var i = this.standardKeyNames.findIndex(function(x) { return x.toUpperCase() == id });
return i >= 0 ? this.standardKeyCaps[i][shifted ? 1 : 0] : '';
};
this.updateKeyId = function (nkey) {
$('.id', nkey).text(($(nkey).data('layer') || '') + ' ' + $(nkey).data('id'));
};
this.selectPlatform = function (val) {
builder.lastPlatform = val || $('#selPlatform').val();
var listContainer = $('#selPlatformPresentation');
$('option', listContainer).remove();
for (var i in this.presentations) {
if (i.substring(0, builder.lastPlatform.length) != builder.lastPlatform) {
continue;
}
var option = $(document.createElement('option'));
option.attr('value', i).text(this.presentations[i].name);
listContainer.append(option);
}
$('#chkDisplayUnderlying')[0].checked = KVKL[builder.lastPlatform].displayUnderlying;
builder.prepareLayers();
builder.selectLayer(0);
}
this.selectLayer = function (val) {
let selection = builder.saveSelection();
if(val) $('#selLayer').val(val);
builder.lastLayerIndex = $('#selLayer').val();
builder.prepareLayer();
builder.restoreSelection(selection);
}
this.selectLayerByName = function (name) {
var layerOption = $('#selLayer option').filter(function (index) { return $(this).text() === name; });
if (layerOption.length == 0) {
alert('Layer ' + name + ' not found.');
} else {
builder.selectLayer(layerOption.attr('value'));
}
}
this.commands = [];
this.command = function (cmd) {
if (navigator.userAgent.indexOf('Keyman') < 0) {
return false;
}
// queue commands to build a single portmanteau command when we return to idle
if (builder.commands.length == 0) {
window.setTimeout(function() {
try {
location.href = 'keyman:command?' + builder.commands.reduce(function(a,v) { return a + '&' + v; });
builder.commands = [];
} catch (e) {
// ignore errors - saves weirdness happening when testing outside TIKE.exe
}
}, 10);
}
builder.commands.push(cmd);
}
this.addRow = function (position) {
var row = document.createElement('div');
$(row).addClass('row');
var parentRow = builder.selectedKey().parent();
if (position == 'below') {
$(parentRow).after(row);
} else if (position == 'above') {
$(parentRow).before(row);
} else {
$('#kbd').append(row);
}
var rowKeySize = document.createElement('div');
$(rowKeySize).addClass('key-size');
$(rowKeySize).text('15');
$(row).append(rowKeySize);
return row;
};
this.delSelectedKey = function () {
var key = builder.selectedKey();
var nextKey = $(key).next('.key');
if (nextKey.length == 0) nextKey = $(key).prev('.key');
if (nextKey.length == 0) {
return this.delSelectedRow();
}
$(key).remove();
builder.selectKey(nextKey);
}
this.delSelectedRow = function () {
var parentRow = builder.selectedKey().parent();
var nextKey = $(parentRow).next().children('.key').first();
if (nextKey.length == 0) {
nextKey = $(parentRow).prev().children('.key').first();
}
if (nextKey.length == 0) {
return;
}
builder.selectKey(nextKey);
$(parentRow).remove();
}
this.selectKey = function (key, adjustResizable) {
if (arguments.length == 1 || adjustResizable)
builder.selectedKey().resizable('destroy');
if(!builder.textControlsInToolbar()) {
$('input#inpSubKeyCap').css('display', '');
}
$('.skcontrol.wedge-horz,.skcontrol.wedge-vert,div#btnDelSubKey,input#inpSubKeyCap').css('display', '');
builder.selectedKey().removeClass('selected');
if (key && $(key).length) {
$(key).addClass('selected');
if(!builder.textControlsInToolbar()) {
$('input#inpKeyCap').css('display', 'block');
}
$('.kcontrol.wedge-horz,.kcontrol.wedge-vert,div#btnDelKey').css('display', 'block');
var rowOffset = $(key).parent().offset();
var offset = $(key).offset();
$('#wedgeAddRowAbove').offset({ left: rowOffset.left - 18, top: rowOffset.top - 7 });
$('#wedgeAddRowBelow').offset({ left: rowOffset.left - 18, top: rowOffset.top + $(key).parent().outerHeight() - 7 });
$('#wedgeAddKeyLeft').offset({ left: offset.left - 9, top: offset.top + $(key).outerHeight() + 2 });
$('#wedgeAddKeyRight').offset({ left: offset.left + $(key).outerWidth() - 7, top: offset.top + $(key).outerHeight() + 2 });
$('div#btnDelKey').offset({ left: offset.left + $(key).outerWidth() - 14, top: offset.top + 3 });
if(!builder.textControlsInToolbar()) {
$('input#inpKeyCap').offset({ left: offset.left + 16, top: offset.top + 4 }).width($(key).width() - 32);
}
this.prepareKey();
let subKeys = $('#sub-key-groups div.key');
if(subKeys.length) {
builder.selectSubKey(subKeys[0]);
}
//builder.lastFocus = $('input#inpKeyCap');
} else {
if(!builder.textControlsInToolbar()) {
$('input#inpKeyCap').css('display', '');
}
$('.kcontrol.wedge-horz,.kcontrol.wedge-vert,div#btnDelKey').css('display', '');
builder.removeAllSubKeys();
builder.enableKeyControls();
builder.enableSubKeyControls();
}
builder.saveState();
};
this.selectedKey = function () {
return $('#kbd .selected');
}
this.wrapChange = function(f, opt) {
return function() {
if(typeof opt == 'object' && opt.saveOnce) {
if (!builder.hasSavedKeyUndo) {
builder.saveUndo();
builder.hasSavedKeyUndo = true;
} else {
builder.command('modified');
}
} else {
builder.saveUndo();
}
var r = f.apply(this, arguments);
if(typeof opt == 'object' && opt.rescale) {
builder.rescale();
}
builder.generate();
return r;
}
};
const inpKeyPaddingChange = builder.wrapChange(function () {
builder.selectedKey().data('pad', $(this).val())
.css('margin-left', $(this).val() + 'px');
}, {rescale: true});
$('#inpKeyPadding')
.change(inpKeyPaddingChange)
.on('input', inpKeyPaddingChange);
const inpKeyWidthChange = builder.wrapChange(function () {
builder.selectedKey().data('width', $(this).val())
.css('width', parseInt($(this).val(), 10) * this.xscale + 'px');
}, {rescale: true});
$('#inpKeyWidth')
.change(inpKeyWidthChange)
.on('input', inpKeyWidthChange);
const inpKeyNameChange = builder.wrapChange(function () {
builder.selectedKey().data('id', $(this).val());
builder.updateKeyId(builder.selectedKey());
}, {saveOnce: true});
builder.wrapInstant = function(f) {
return function() {
window.setTimeout(f.bind(this), 0);
}
}
$('#inpKeyName')
.change(inpKeyNameChange)
.autocomplete({
source: builder.lookupKeyNames,
change: inpKeyNameChange,
select: builder.wrapInstant(inpKeyNameChange)
})
.on('input', inpKeyNameChange)
.blur(function () {
builder.hasSavedKeyUndo = false;
});
this.updateCharacterMap = function (val, fromSubKey) {
// Update character map
var src = $(fromSubKey ? '#inpSubKeyCap' : '#inpKeyCap')[0];
var x1 = src.selectionStart;
var x2 = src.selectionEnd;
if (x2 != x1 || x1 > 0) {
var ch = val.charCodeAt(x2 - 1);
if (x2 > 1 && ch >= 0xDC00 && ch < 0xE000) {
// yep, surrogate pairs again!
var chHigh = val.charCodeAt(x2 - 2), chLow = ch;
ch = ((chHigh - 0xD800) << 10) + (chLow - 0xDC00) + 0x10000;
}
builder.command('selected-char,' + ch.toString());
}
}
builder.toUnicodeString = function(s) {
if(typeof s != 'string') return '';
let r = '';
for(let ch of s) {
let code = ch.codePointAt(0).toString(16).toUpperCase();
r += 'U+' + code.padStart(4, '0') + ' ';
}
return r.trim();
}
builder.fromUnicodeString = function(s) {
if(typeof s != 'string') return '';
let chars = s.split(' '), r = '';
for(let ch of chars) {
if(!ch.match(/^u\+[0-9a-f]{1,6}$/i)) continue;
r += String.fromCodePoint(parseInt(ch.substring(2), 16));
}
return r;
}
const inpKeyCapChange = builder.wrapChange(function (e) {
const val = $(this).val();
var k = builder.selectedKey();
$('.text', k).text(builder.renameSpecialKey(val));
k.data('text', val);
$('#inpKeyCapUnicode').val(builder.toUnicodeString(val));
if(builder.specialCharacters[val]) {
k.addClass('key-special-text');
} else {
k.removeClass('key-special-text');
}
builder.updateCharacterMap(val, false);
}, {saveOnce: true});
const inpKeyCapUnicodeChange = builder.wrapChange(function (e) {
const val = builder.fromUnicodeString($(this).val());
var k = builder.selectedKey();
$('.text', k).text(builder.renameSpecialKey(val));
k.data('text', val);
$('#inpKeyCap').val(val);
if(builder.specialCharacters[val]) {
k.addClass('key-special-text');
} else {
k.removeClass('key-special-text');
}
builder.updateCharacterMap(val, false);
}, {saveOnce: true});
$('#inpKeyCap')
.on('input', inpKeyCapChange)
.change(inpKeyCapChange)
/*.autocomplete({
source: builder.specialKeyNames,
change: inpKeyCapChange,
select: builder.wrapInstant(inpKeyCapChange)
})*/
.mouseup(function () {
builder.updateCharacterMap($(this).val(), false);
}).focus(function () {
builder.updateCharacterMap($(this).val(), false);
}).blur(function () {
builder.hasSavedKeyUndo = false;
});
$('#inpKeyCapUnicode')
.on('input', inpKeyCapUnicodeChange)
.change(inpKeyCapUnicodeChange)
.mouseup(function () {
builder.updateCharacterMap(builder.fromUnicodeString($(this).val()), false);
}).focus(function () {
builder.updateCharacterMap(builder.fromUnicodeString($(this).val()), false);
}).blur(function () {
builder.hasSavedKeyUndo = false;
});
const selKeyTypeChange = builder.wrapChange(function () {
var sp = $(this).val();
if (sp == 0) {
builder.selectedKey().removeData('sp');
} else {
builder.selectedKey().data('sp', $(this).val());
}
builder.formatKey(builder.selectedKey(), $(this).val());
});
$('#selKeyType').change(selKeyTypeChange);
const selKeyNextLayerChange = builder.wrapChange(function () {
$(this).val() === '' ?
builder.selectedKey().removeData('nextlayer') :
builder.selectedKey().data('nextlayer', $(this).val());
});
$('#selKeyNextLayer').change(selKeyNextLayerChange);
const selKeyLayerOverrideChange = builder.wrapChange(function () {
$(this).val() === '' ?
builder.selectedKey().removeData('layer') :
builder.selectedKey().data('layer', $(this).val());
builder.updateKeyId(builder.selectedKey());
});
$('#selKeyLayerOverride').change(selKeyLayerOverrideChange);
this.enableKeyControls = function () {
var key = builder.selectedKey();
if (key.length == 0) {
$('#keyToolbar *').attr('disabled', 'disabled');
$('#sub-key-container').css('display', 'none');
} else {
$('#keyToolbar *').removeAttr('disabled');
$('#sub-key-container').css('display', '');
}
}
this.rescale = function () {
builder.saveUndo();
var keyId = builder.selectedKey().data('id');
builder.prepareLayer();
if (keyId !== null)
builder.selectKey($('#kbd .key').filter(function (index) { return $(this).data('id') === keyId; }).first());
};
this.translateFlickArrayToObject = function(flicks) {
let res = {};
for(let flick of flicks) {
const {direction, ...item} = flick;
res[direction] = item;
}
return res;
};
this.translateFlickObjectToArray = function(flicks) {
if(!flicks) {
return [];
}
return Object.getOwnPropertyNames(flicks).map(
direction => {
return {...flicks[direction], direction: direction};
}
);
}
this.generate = function (display, force) {
var json = JSON.stringify(KVKL, null, ' ');
if(!display) {
// Save changed settings -- only when not saving undo
KVKL[builder.lastPlatform].displayUnderlying = $('#chkDisplayUnderlying')[0].checked;
}
var layer = KVKL[builder.lastPlatform].layer[builder.lastLayerIndex];
layer.row = [];
var rows = $('#kbd > div.row'), n = 1;
$(rows).each(function () {
var row = { id: n++, key: [] };
$(this).children(".key").each(function () {
if ($(this).hasClass('ui-draggable-dragging')) return;
var key = { "id": $(this).data('id'), "text": $(this).data('text') };
key.pad = $(this).data('pad');
key.width = $(this).data('width');
key.sp = $(this).data('sp');
key.font = $(this).data('font');
key.fontsize = $(this).data('fontsize');
key.nextlayer = $(this).data('nextlayer');
key.layer = $(this).data('layer');
let longpress = $(this).data('longpress');
if(longpress && longpress.length) key.sk = longpress;
let flick = $(this).data('flick');
if(flick && flick.length) key.flick = builder.translateFlickArrayToObject(flick);
let multitap = $(this).data('multitap');
if(multitap && multitap.length) key.multitap = multitap;
row.key.push(key);
});
layer.row.push(row);
});
builder.saveJSON(json, force);
};
this.saveJSON = function(force) {
builder.removeEmptyRows();
var newJson = JSON.stringify(KVKL, null, ' ');
if(force || newJson != json) {
// When adding or deleting layers and platforms, we need to force because
// that will not result in changes to the rows.
$.ajax('/app/source/file', {
'type': 'POST',
'data': {
'Filename': builder.filename,
'Data': newJson
}
});
$('#data').val(newJson);
}
};
$('#wedgeAddRowAbove').click(builder.wrapChange(function () {
var row = builder.addRow('above'); builder.selectKey(builder.addKey('key', row));
}, {rescale: true}));
$('#wedgeAddRowBelow').click(builder.wrapChange(function () {
var row = builder.addRow('below'); builder.selectKey(builder.addKey('key', row));
}, {rescale: true}));
$('#wedgeAddKeyLeft').click(builder.wrapChange(function () {
builder.selectKey(builder.addKey('key', 'before'));
}, {rescale: true}));
$('#wedgeAddKeyRight').click(builder.wrapChange(function () {
builder.selectKey(builder.addKey('key', 'after'));
}, {rescale: true}));
$('#btnDelKey').click(builder.wrapChange(function () {
builder.delSelectedKey();
}, {rescale: true}));
$('#btnGenerate').click(function () { builder.generate(false,false); });
$('#chkDisplayUnderlying').click(function () {
builder.saveUndo();
var platform = $('#selPlatform').val();
builder.generate();
builder.prepareLayer();
});
$('input').blur(function () {
//builder.lastFocus = this;
});
$('input').focus(function () {
builder.lastFocus = this;
});
$('input').click(function (event) {
event.stopImmediatePropagation();
});
$('#btnTemplate').click(function () {
builder.command('template');
});
$('#btnImport').click(function () {
builder.command('import');
});
$('#kbd').click(function () {
builder.selectKey(null);
});
builder.ctrlDown = false;
builder.nextKeySelects = false;
builder.selectKeyByCode = function (code) {
if (code >= 0 && code < 256) {
var keyName = builder.standardKeyNames[code];
var key = $('.key').filter(function (index) { return $(this).data('id') === keyName; });
if (key.length > 0) builder.selectKey(key[0]);
}
}
$(document).keydown(function (event) {
if(!event.originalEvent.repeat) {
// We will get this event repeatedly while ctrl is held down,
// so if ctrlDown is cancelled, we don't want to set it again
builder.ctrlDown = (event.which == 17);
}
if (builder.nextKeySelects) {
$('#selectKeyDialog').dialog('close');
event.preventDefault();
event.stopImmediatePropagation();
builder.nextKeySelects = false;
builder.selectKeyByCode(event.which);
}
});
$(document).keyup(function (event) {
if (builder.ctrlDown && event.which == 17) {
event.preventDefault();
event.stopImmediatePropagation();
builder.nextKeySelects = true;
$('#selectKeyDialog').dialog('open');
}
builder.ctrlDown = false;
});
builder.loadingState = true;
builder.saveState = function() {
if(builder.loadingState) return;
var state = {
platform: builder.lastPlatform,
layer: builder.lastLayerIndex,
presentation: $('#selPlatformPresentation').val()
};
var key = builder.selectedKey();
if(key.length > 0) {
state.key = key.data('id');
}
var subkey = builder.selectedSubKey();
if (subkey.length > 0) {
state.subkey = $(subkey).data('id');
}
$.post('/app/source/toucheditor/state', {
'Filename': builder.filename,
'State': JSON.stringify(state)
});
};
builder.loadState = function() {
$.get('/app/source/toucheditor/state',
{
'Filename': builder.filename
},
function(data) {
builder.loadingState = false;
if(typeof data === 'object') {
if(data.platform && KVKL[data.platform]) {
$('#selPlatform').val(data.platform);
}
builder.selectPlatform();
if(data.presentation && builder.presentations[data.presentation]) {
$('#selPlatformPresentation').val(data.presentation);
builder.prepareLayer();
}
if(data.layer && KVKL[builder.lastPlatform][data.layer]) {
$('#selLayer').val(data.layer);
builder.selectLayer();
}
if(data.key) {
builder.selectKey($('#kbd .key').filter(function (index) { return $(this).data('id') === data.key; }).first());
}
if(data.subkey) {
builder.selectSubKey($('#sub-key-groups .key').filter(function (index) { return $(this).data('id') === data.subkey; }).first());
}
}
}
);
};
builder.removeEmptyRows = function() {
var json = JSON.stringify(KVKL, null, ' ');
for (var platform in KVKL) {
for (let layer of KVKL[platform].layer) {
let newRow = [], n = 1;
for(let row of layer.row) {
if(row.key.length > 0) {
row.id = n;
n++;
newRow.push(row);
}
}
layer.row = newRow;
}
}
};
builder.textControlsInToolbar = function() {
return $('body').hasClass('text-controls-in-toolbar');
}
}.bind(builder));
function initBuilder() {
$(function() {
builder.prepareKeyCapTypes();
builder.preparePlatforms();
builder.enableUndoControls();
builder.loadState();
});
}