$(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(); }); builder.removeAllSubKeys = function() { $('#sub-key-groups .key').remove(); } builder.prepareKeyCapTypes = function() { let types = $('#selKeyCapType'), subTypes = $('#selSubKeyCapType'), opts = ''; for(let name of builder.specialKeyNames) { opts += ''; } $(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 */ this.addKeyAnnotations = function(key) { const longpress = $(key).data('longpress'), flick = $(key).data('flick'), multitap = $(key).data('multitap'), hint = $(key).data('hint'); $('.has-flick', key).remove(); if (flick && flick.length) { for(let f of flick) { $(key).append('
'); } } // 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('
'); } $('.has-multitap', key).remove(); if (multitap && multitap.length) { $(key).append('
'); } if(hint && hint.length) { $('.hint', key).addClass('custom-hint'); } else { $('.hint', key).removeClass('custom-hint'); } } this.inferKeyText = function(key) { let code = key.id ? String.fromCharCode(parseInt(key.id.substring(2), 16)) : 0; let text = typeof key.text == 'string' ? key.text : (code == '' || code.charCodeAt(0) < 32 ? '' : code); return text; } this.inferKeyHintText = function(keyHint, longpress, flick, multitap) { let hint = keyHint; if(!hint) { switch(KVKL[builder.lastPlatform].defaultHint ?? 'dot') { case 'none': break; case 'dot': // Default for Keyman 15 if(longpress && longpress.length) hint = '•'; break; case 'longpress': if(longpress && longpress.length) hint = builder.inferKeyText(longpress[0]); break; case 'multitap': if(multitap && multitap.length) hint = builder.inferKeyText(multitap[0]); break; case 'flick': for(let direction of ['n','ne','e','se','s','sw','w','nw']) { if(flick && flick[direction]) { hint = builder.inferKeyText(flick[direction]); break; } } break; case 'flick-n': case 'flick-ne': case 'flick-e': case 'flick-se': case 'flick-s': case 'flick-sw': case 'flick-w': case 'flick-nw': let direction = source.substr(6); if(flick && flick[direction]) hint = builder.inferKeyText(flick[direction]); break; } } if(hint == null) return ''; return hint; } 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; let text = builder.inferKeyText(key); 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('hint', key.hint) .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)); $('.hint', nkey).text(this.inferKeyHintText(key.hint, key.sk, key.flick, key.multitap)); 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.updateKeySizeInfo(); }; 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
' + keyWidth + ' key width
' + gapWidth + ' padding
' + 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); } 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() - 5, top: offset.top - 8 }); 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]); } } 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 keyCapChange = function(val) { const k = builder.selectedKey(); $('.text', k).text(builder.renameSpecialKey(val)); k.data('text', val); if(builder.specialCharacters[val]) { k.addClass('key-special-text'); } else { k.removeClass('key-special-text'); } builder.updateCharacterMap(val, false); } const inpKeyCapChange = builder.wrapChange(function (e) { const val = $(this).val(); $('#inpKeyCapUnicode').val(builder.toUnicodeString(val)); keyCapChange(val); }, {saveOnce: true}); const inpKeyCapUnicodeChange = builder.wrapChange(function (e) { const val = builder.fromUnicodeString($(this).val()); $('#inpKeyCap').val(val); keyCapChange(val); }, {saveOnce: true}); const keyHintChange = function(val) { const key = builder.selectedKey(); key.data('hint', val); $('.hint', key).text(builder.inferKeyHintText(val, $(key).data('longpress'), $(key).data('flick'), $(key).data('multitap'))); if(val && val.length) { $('.hint', key).addClass('custom-hint'); } else { $('.hint', key).removeClass('custom-hint'); } builder.updateCharacterMap(val, false); } const inpKeyHintChange = builder.wrapChange(function (e) { const val = $(this).val(); $('#inpKeyHintUnicode').val(builder.toUnicodeString(val)); keyHintChange(val); }, {saveOnce: true}); const inpKeyHintUnicodeChange = builder.wrapChange(function (e) { const val = builder.fromUnicodeString($(this).val()); $('#inpKeyHint').val(val); keyHintChange(val); }, {saveOnce: true}); const selKeyCapTypeChange = builder.wrapChange(function () { var val = $(this).val(); $('#inpKeyCap').val(val); $('#inpKeyCapUnicode').val(builder.toUnicodeString(val)); keyCapChange(val); // We only EnableControls here because if the user types *BkSp* into the // text field, we shouldn't hide the text field until next time the key is selected builder.enableKeyControls(); }); $('#selKeyCapType').on('change', selKeyCapTypeChange); $('#inpKeyCap') .on('input', inpKeyCapChange) .change(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; }); $('#inpKeyHint') .on('input', inpKeyHintChange) .change(inpKeyHintChange) .mouseup(function () { builder.updateCharacterMap($(this).val(), false); }).focus(function () { builder.updateCharacterMap($(this).val(), false); }).blur(function () { builder.hasSavedKeyUndo = false; }); $('#inpKeyHintUnicode') .on('input', inpKeyHintUnicodeChange) .change(inpKeyHintUnicodeChange) .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'); $('#key-cap-unicode-toolbar-item, #key-cap-toolbar-item').css('display', 'none'); } else { 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' : ''); } } 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, ' '); 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'); key.hint = $(this).data('hint'); 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.cleanupKVKL(); 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); }); $('input').focus(function () { builder.lastFocus = this; }); $('#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 option[value="'+data.presentation+'"]').length) { $('#selPlatformPresentation').val(data.presentation); } else { // The last selected presentation is no longer available; select the first option instead $('#selPlatformPresentation').val($('#selPlatformPresentation option:first').val()); } 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.textControlsInToolbar = function() { return $('body').hasClass('text-controls-in-toolbar'); } }.bind(builder)); function initBuilder() { $(function() { builder.cleanupKVKL(); builder.prepareKeyCapTypes(); builder.preparePlatforms(); builder.enableUndoControls(); builder.loadState(); }); }