Last active
April 24, 2025 17:31
-
-
Save derekmc/1ca2a78e450d379190d6fd76ae9843e6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<style> | |
/* CSS */ | |
#status-div{ | |
font-family: monospace; | |
text-align: center; | |
font-size: 12px; | |
} | |
.log-out{ | |
font-family: monospace; | |
text-align: justify; | |
font-size: 11px; | |
white-space: pre-wrap; | |
} | |
body{ | |
font-family: monospace; | |
} | |
/* END */ | |
</style> | |
</head> | |
<body> | |
<div class="markdown-src"> | |
<!-- HTML --> | |
<div id='status-div'></div> | |
<button onclick="clearLogs()">Clear</button> | |
<hr> | |
<div id='text-div' class='log-out'></div> | |
<hr> | |
<div id='log2-div' class='log-out'></div> | |
<hr> | |
<div id='log1-div' class='log-out'></div> | |
<!-- END --> | |
</div> | |
<div style="display: none;"></div> | |
<script> | |
let datakey = "__HTML_NOTES__:notedataid=x7Pa3QckWb" | |
let inframe = (window!=window.top) | |
window.Note = {} | |
window.Note.data = | |
/* JSON */ | |
{"virtualKeys":[74,75,76,186,85,73,79,80,32,222],"virtualKeyCount":10,"layout":"\n : r n o e a t i l space shift\n r: XX right s u up * c h esc _\n n: left XX m d pgup = p x + -\n o: s m XX / w g : $ { }\n e: u d \\ XX f k [ ] alt select\n a: down pgdn w f XX q j v ~ !\n t: < > g k z XX b y ( )\n i: c p ^ # j b XX ` ' \"\n l: h x & | v y % 0 @\n space: ; ctrl , . tab back5 back enter XX ?\n shift: 1 2 3 4 5 6 7 8 9 XX","textout":"^^to be or not to be that isthe atuquestion.[enter][enter]^^this irr[back] i[back][back][back]is a test. function main(t^^[back][back]"}; | |
/* END */ | |
(function(){ | |
window.Note.save = saveData | |
window.Note.load = loadData | |
window.Note.autoSave = autoSave | |
function getNoteModule(){ | |
if(!window['Note']) window.Note = {} | |
return window.Note | |
} | |
function loadData(){ | |
let note = getNoteModule() | |
if(!inframe){ | |
try{ | |
let savedata = localStorage.getItem(datakey) | |
if(savedata && savedata.length){ | |
note.data = JSON.parse(savedata) | |
} | |
window.addEventListener("message", onMessage) | |
} catch(e){ | |
console.log("No local data") | |
console.log(e) | |
} | |
} else { | |
return note.data | |
} | |
} | |
let firstLogCall = true | |
let originalLogFunc = console.log | |
function log(x, ...rest){ | |
if(firstLogCall) | |
document.body.appendChild(document.createElement("hr")) | |
else | |
document.body.appendChild(document.createElement("br")) | |
document.body.appendChild( | |
document.createTextNode(x + " " + rest.join(" "))) | |
firstLogCall = false | |
originalLogFunc(x, ...rest) | |
} | |
console.log = log | |
let SaveInterval = 500 | |
window.addEventListener("load", ()=>{ | |
convertMarkdown() | |
autoSave() | |
loadData() // dont wait for async | |
}) | |
let saveIntervalRef = null | |
function autoSave(enable){ | |
if(enable === undefined) enable = true | |
clearInterval(saveIntervalRef) | |
if(enable){ | |
saveIntervalRef = window.setInterval(saveData, SaveInterval) | |
} | |
} | |
// this is preferred over a 'localstorage' polyfill for a frame, | |
// so the programmer doesn't assume this is the browsers localstorage | |
function saveData(){ | |
let note = getNoteModule() | |
if(inframe){ | |
let msg_obj = { | |
action: "saveData", | |
data: JSON.stringify(note['data']) | |
} | |
let message = JSON.stringify(msg_obj) | |
window.parent.postMessage(message) | |
} else { | |
localStorage.setItem(datakey, JSON.stringify(note['data'])) | |
} | |
} | |
let md_subs = [ | |
/(\n|^)\s*######\s([^\s#].*)\n*/g, "\n<h6 id=\"$2\">$2</h6>\n", | |
/(\n|^)\s*#####\s([^\s#].*)\n*/g, "\n<h5 id=\"$2\">$2</h5>\n", | |
/(\n|^)\s*####\s*([^\s#].*)\n*/g, "\n<h4 id=\"$2\">$2</h4>\n", | |
/(\n|^)\s*###\s*([^\s#].*)\n*/g, "\n<h3 id=\"$2\">$2</h3>\n", | |
/(\n|^)\s*##\s*([^\s#].*)\n*/g, "\n<h2 id=\"$2\">$2</h2>\n", | |
/(\n|^)\s*#\s*([^\s#].*)\n*/g, "\n<h1 id=\"$2\">$2</h1>\n", | |
/\n(\s*)[\*\-](.*)/g, '\n<ul><li>$2</li></ul>', | |
/\n+\n(?=[^#\n])/g, "\n\n<br><br>", | |
/\n+\n/g, "\n", | |
/__([^_\n]*)__/g, "<b>$1</b>", | |
/\*\*([^_\n]*)\*\*/g, "<b>$1</b>", | |
/_([^_\n]*)_/g, "<i>$1</i>", | |
/\*([^_\n]*)\*/g, "<i>$1</i>", | |
/\!\[([^\]\n]*)\]\(([^\)\n]*)\)/g, "<img src=\"$2\" alt=\"$1\"></img>", | |
/\[([^\]\n]*)\]\(([^\)\n]*)\)/g, "<a href=\"$2\" target=\"_blank\">$1</a>", | |
] | |
function onMessage(){ | |
try{ | |
let message = JSON.parse(e.data) | |
// console.log('received message', message) | |
if(message.hasOwnProperty("event")){ | |
if(message.event == "keydown" && typeof keydown != "undefined"){ | |
keydown(message) | |
} | |
if(message.event == "keyup" && typeof keyup != "undefined"){ | |
keydown(message) | |
} | |
} | |
} catch(e){ | |
console.warn("error processing message: " + e.data) | |
} | |
} | |
function convertMarkdown(){ | |
let containers = document.getElementsByClassName("markdown-src") | |
for(let j=0; j<containers.length; ++j){ | |
let container = containers[j] | |
let src = container.innerHTML | |
for(var i=0; i<md_subs.length-1; i += 2){ | |
var search = md_subs[i] | |
var replace = md_subs[i+1] | |
src = src.replace(search, replace) | |
} | |
container.innerHTML = src | |
} | |
} | |
})() | |
//window.addEventListener('load', htmlNotesMainFunc) | |
//function htmlNotesMainFunc(){ | |
/* JS */ | |
/* | |
Commas separate simultanous actions | |
Bichord Keyboard | |
Algorithm flow chart | |
1. serialize key actions | |
2. map physical keys to virtual keys | |
3. interpret chord sequence | |
4. translate chord sequence | |
Step 1 | |
+65 +67 -88,-65 +99 | |
*/ | |
let data = Note.data | |
let env = {} // constants | |
let temp = {} // reset | |
let ref = {} // dont reset | |
function getTemp(name, _default){ | |
if(!temp.hasOwnProperty(name)) | |
temp[name] = _default | |
return temp[name] | |
} | |
let defaultLayout = ` | |
: r n o e a t i l space shift | |
r: XX right s u up * c h esc _ | |
n: left XX m d pgup = p x + - | |
o: s m XX / w g : $ { } | |
e: u d \\ XX f k [ ] alt select | |
a: down pgdn w f XX q j v ~ ! | |
t: < > g k z XX b y ( ) | |
i: c p ^ # j b XX \` ' " | |
l: h x & | v y % 0 @ | |
space: ; ctrl , . tab back5 back enter XX ? | |
shift: 1 2 3 4 5 6 7 8 9 XX` | |
function reset(){ | |
temp = {} | |
} | |
main() | |
function json(x){ | |
return JSON.stringify(x) | |
} | |
function clearLogs(){ | |
temp.keylog = "" | |
temp.virtlog = "" | |
} | |
function statusText(){ | |
if(data.virtualKeys.length < data.virtualKeyCount){ | |
return `Map key #${data.virtualKeys.length + 1}` | |
} | |
return getTemp("lastMessage", "") | |
} | |
function statusUpdate(){ | |
let div = id('status-div') | |
div.innerHTML = "" | |
let txt = document.createTextNode(statusText()) | |
div.appendChild(txt) | |
} | |
function logUpdate(){ | |
let div = id('log1-div') | |
div.innerHTML = "" | |
let txt = document.createTextNode( | |
" KeyLog: " + getTemp("keylog", "")) | |
div.appendChild(txt) | |
div = id('log2-div') | |
div.innerHTML = "" | |
txt = document.createTextNode( | |
"VirtualLog: " + getTemp("virtlog", "")) | |
div.appendChild(txt) | |
div = id('text-div') | |
data.textout = replaceChords( | |
getTemp("chordSubs", []), temp.virtlog) | |
div.innerHTML = "" | |
txt = document.createTextNode( | |
" Text: " + data.textout) | |
div.appendChild(txt) | |
} | |
function init(){ | |
ref.logCall = multiFilter([ | |
logKey('keylog'), virtualKeyFilter(logKey('virtlog'))]) | |
data.virtualKeys ??= [] | |
data.virtualKeyCount ??= 10 | |
data.layout ??= defaultLayout | |
//console.log(data.layout) | |
temp.lastMessage = "init called" | |
setInterval(statusUpdate, 300) | |
setInterval(logUpdate, 300) | |
temp.chordMap = parseLayout(data.layout) | |
temp.chordSubs = chordSubs(temp.chordMap) | |
//console.info("chordMap", temp.chordMap) | |
//console.info("chordSubs", json(temp.chordSubs)) | |
//console.info(json(allOrders([1,2,3,4]))) | |
//window.addEventListener('keydown', keydown) | |
//window.addEventListener('keyup', keyup) | |
//setInterval(process, 1000) | |
} | |
function main(){ | |
init() | |
window.addEventListener('keydown', keydown) | |
window.addEventListener('keyup', keyup) | |
} | |
function getKeyId(e){ | |
return e.keyCode | |
} | |
function logKey(logname){ | |
return function(action, keyid){ | |
if(action != "+" && action != "-"){ | |
throw new Error('invalid keylog action: ' + action)} | |
getTemp('keylog', "") | |
temp[logname] += " " + action + keyid + ";" | |
} | |
} | |
function keydown(e){ | |
let keyid = getKeyId(e) | |
if(getTemp('activeKeys', {})[keyid]) return | |
if(data.virtualKeys.length < data.virtualKeyCount){ | |
data.virtualKeys.push(keyid) | |
return | |
} | |
temp.activeKeys[keyid] = true | |
ref.logCall('+', keyid) | |
} | |
function keyup(e){ | |
let keyid = getKeyId(e) | |
getTemp('activeKeys', {}) | |
if(!temp.activeKeys.hasOwnProperty(keyid)){ | |
return } | |
ref.logCall('-', keyid) | |
delete temp.activeKeys[keyid] | |
} | |
function virtualKeyFilter(callback){ | |
return function(action, keyid){ | |
let virtid = mapKey(keyid) | |
if(virtid > 0) callback(action, virtid) | |
} | |
} | |
function multiFilter(callbacks){ | |
return function(action, keyid){ | |
callbacks.forEach(f=>f(action,keyid))} | |
} | |
// premute the array | |
function allOrders(array, results, prefix){ | |
if(!results) results = [] | |
if(!prefix) prefix = [] | |
for(let i=0; i<array.length; ++i){ | |
let rest = array.slice(0) | |
rest.splice(i, 1) | |
let prefix2 = prefix.slice(0) | |
prefix2.push(array[i]) | |
if(array.length == 1) results.push(prefix2) | |
else allOrders(rest, results, prefix2) | |
} | |
return results | |
} | |
function parseLayout(s){ | |
let keymap = {} | |
let keynames = [] | |
keymap.__keylist = [] | |
let lines = s.split('\n') | |
let unordered = false | |
for(let i=0; i<lines.length; ++i){ | |
let line = lines[i].trim() | |
let j = line.indexOf(":") | |
if(j < 0){ | |
if(line.toLowerCase() == "!undordered"){ | |
unordered = true } | |
continue | |
} | |
let prefix = line.substring(0, j).trim().split(/\s+/).join(' ') | |
let rest = line.substring(j+1).trim().split(/\s+/) | |
//console.log(prefix, rest) | |
if(prefix.length == 0){ | |
keynames = rest | |
keymap.__keylist = keymap.__keylist.concat(keynames) | |
} else { | |
for(let k=0; k<rest.length; ++k){ | |
let chord = prefix + ' ' + keynames[k] | |
let value = rest[k].trim() | |
//if(!unordered){ | |
keymap[chord] = value | |
//if(unordered){ | |
// let orders = allOrders(chord.split(/\s+/)).map(x=>x.join(' ')) | |
// for(let i=0; i<orders.length; ++i){ | |
// keymap[orders[i]] = value } | |
//} | |
} | |
} | |
} | |
return keymap | |
} | |
function chordSubs(chordmap){ | |
let result = [] | |
for(let input in chordmap){ | |
let output = chordmap[input] | |
if(output == "XX") continue | |
if(input[0] == '_') continue | |
let sequence = input.split(/\s+/) | |
let prefix = "" | |
let suffix = "" | |
for(let i=0; i<sequence.length; ++i){ | |
let char = sequence[i] | |
let j = chordmap.__keylist.indexOf(char) + 1 | |
if(j < 0){ | |
prefix = "" | |
break | |
} | |
if(prefix.length) prefix += '; ' | |
if(suffix.length) suffix = '; ' + suffix | |
prefix += '+' + j | |
suffix = '-' + j + suffix | |
} | |
let pattern = " " + prefix + "; " + suffix + ";" | |
if(prefix.length) result.push([pattern, output]) | |
} | |
chordmap.__keylist.forEach((ch,j)=> | |
result.push([' +' + (j+1) + ";", ch])) | |
return result | |
} | |
function emit(chord){ | |
let k = chord.join(' ') | |
let keyaction = temp.chordMap[k] | |
} | |
function mapKey(x){ | |
let i = data.virtualKeys.indexOf(x) | |
return i+1 | |
} | |
function replaceChords(subs, log){ | |
subs.forEach(([pattern, result])=>{ | |
log = log.replaceAll(pattern, " ~" + result + ";") | |
}) | |
log = log.replaceAll(/ -[0-9]+;/g, "") | |
log = log.replace(/ ~space;/g, " ") | |
log = log.replace(/ ~shift;/g, "^^") | |
log = log.replace(/ ~(.);/g, "$1") | |
log = log.replace(/ ~(.[^;]*);/g, "[$1]") | |
//log = log.replace(/. ~back;/g, "") | |
//log = log.replace(/..... ~back5;/g, "") | |
return log | |
} | |
// event handler | |
function keyaction(action, keyid){ | |
let actionMap = {'down': '+', 'up': '-'} | |
let a = actionMap[action] | |
let virtualkey = mapKey(keyid) | |
keyQueue.push(a + virtualkey) | |
} | |
function id(x){ | |
return document.getElementById(x) | |
} | |
/* END */ | |
//} | |
</script> | |
</body> | |
<html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment