Skip to content

Instantly share code, notes, and snippets.

@derekmc
Last active March 3, 2023 19:47
Show Gist options
  • Select an option

  • Save derekmc/afc4db5425d94531ec34996df95457f2 to your computer and use it in GitHub Desktop.

Select an option

Save derekmc/afc4db5425d94531ec34996df95457f2 to your computer and use it in GitHub Desktop.
repeat on swipe too
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* CSS */
:root{
--fgcol: rgb(255, 255, 255);
--bgcol: rgb(0, 0, 0);
--midcol: rgb(45, 45, 45);
}
h1, h2, h3, h4{
font-family: sans-serif;
}
body{
background: var(--midcol);
color: var(--fgcol);
padding: 0px;
font-family: monospace;
word-wrap: break-word;
}
.select{
background: var(--bgcol);
color: var(--fgcol);
}
body{
margin: 0;
}
button{
padding: 1.5em 2em;
color: #fff;
background: #000;
font-weight: bold;
border: none;
}
#bb1, #bb4, #bb5, #bb8, #bb9{
background: #335;
}
.left{ float: left; }
.right{ float: right; }
.left, .right{
display: flex;
flex-flow: column;
}
#textout{
white-space: pre-wrap;
position: absolute;
font-size: 16px;
left: 100px;
right: 98px;
top: 0;
background: var(--bgcol);
padding: 6px;
margin: 0;
}
/* END */
</style>
</head>
<body>
<div class="markdown-src">
<!-- HTML -->
<!--span id="bufferout">buffer</span-->
<div id="textout" class="select"> Text goes here </div>
<!--You could have the buffer display the current
letter highlighted.-->
<!-- END -->
</div>
<div style="display: none;"></div>
<script>
let datakey = "__ORBITAL_NOTES__:notedataid=OKrcPSO9RJ"
let inframe = (window!=window.top)
let data = window.data =
/* JSON */
{
"text": "t8UUUUUUUU|",
"cursor": 10
};
/* END */
(function(){
if(!inframe){
try{
let savedata = localStorage.getItem(datakey);
if(savedata && savedata.length){
data = JSON.parse(savedata)
}
} catch(e){
console.log("No local data")
console.log(e)
}
}
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();
window.setInterval(saveData, SaveInterval);
})
function saveData(){
if(inframe){
let msg_obj = {
action: "saveData",
data: JSON.stringify(window.data),
}
let message = JSON.stringify(msg_obj);
window.parent.postMessage(message);
} else {
localStorage.setItem(datakey, JSON.stringify(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\">$1</a>",
];
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;
}
}
})();
/* JS */
const alphabet =
" eronlaitscuZpChJbQywkPxHjB-+" +
"XfvLmGgVdqUzMWDFKYERONAITS" +
"0162734859^!&@*#$`%)(}{|_=><~?][\\/\"':.;,"
const pixsize = 1.0;
const bwidth = 1.0;
const hidelabels = false;
const hsize = 0.3;
const vsize = 0.20;
const hoffset = 0.25;
const voffset = 0.10;
let temp = {};
temp.touchemitted = false;
temp.activeButtons = [];
temp.buffer = "";
//data.cursor = data.text?.length ?? 0;
temp.ctrl = false;
temp.lastchar = "";
temp.repeatInterval;
temp.mousedown = false;
temp.repeatDelay = 0;
const textwindow = 800; // number of characters on screen
const repeatdt = Math.round(1000/6);
//const textwindow = 1800;
//const windowlines = 14
//initial character for each button
const buttonAlphabet = "eronlaitsc";
// the display labels for the buttons
const buttonLabels =
["1", "2", "3", "4", "5",
"6", "7", "8", "9", "10"]
const delay = 1800;
let table = {}
initTable();
let div = document.createElement("div");
document.body.appendChild(div);
div.innerHTML =
"<canvas id='cc' class='overlay'></canvas>" +
"<span id='status-div'>status</span>" +
"<style>" +
" #cc{ border: 2px solid green; }" +
" .overlay{ position: fixed; top: 0; left: 0;" +
" width: calc(100vw - 0px);" +
" height: calc(100vh - 0px);" +
" box-sizing: border-box;" +
" }" +
"</style>"
const cc = document.querySelector("#cc")
// the canvas fits better if you use css for layout
// and don't require pixel perfect mapping
init();
function init(){
data.text ??= ""
data.cursor ??= data.text.length;
cc.addEventListener("mousedown", mousedown);
cc.addEventListener("mouseup", mouseup);
cc.addEventListener("mouseout", mouseout);
cc.addEventListener("mousemove", mousemove);
cc.addEventListener("touchstart", touchstart);
cc.addEventListener("touchmove", touchmove);
cc.addEventListener("touchend", touchend);
status("init")
drawOverlay();
status("init finished.");
setTimeout(()=>status(""), 1000);
//repeat("cs");
}
function activateButtons(touches, rebuffer){
temp.activeButtons = []
if(rebuffer){
if(temp.buffer.length > 1)
temp.buffer = temp.buffer.substr(0,1);
}
if(touches){
for(let i=0; i<touches.length; ++i){
let touch = touches[i];
let x = touch.clientX;
let y = touch.clientY;
let b = buttonMap(x, y);
if(b > 0 && !temp.activeButtons.includes(b)){
temp.activeButtons.push(b);
}
if(rebuffer && i >= temp.buffer.length){
temp.buffer += buttonAlphabet[b-1]
}
}
}
status(`active buttons: ${temp.activeButtons.join(", ")}`)
}
function buttonMap(x, y){
let W = window.innerWidth;
let H = window.innerHeight;
let vert = H>W
let MinSize = Math.min(W,H);
let MaxSize = Math.max(W,H);
let s = vert? vsize * H/3 : hsize * H/3; //square
// button map
let bmap = "243168750--9".split("");
//console.log(bmap)
bmap[9] = bmap[10] = 0;
bmap[8] = 10;
let offset = vert? (1-voffset)*H : (1-hoffset)*H;
let direct = vert? -1 : -1;
let j = Math.floor((offset + direct * y)/s)
if(j < 0) return 0;
if(x < 2*bwidth*s){
let i = Math.floor(x/bwidth/s)
return bmap[i + 4*j];
}
if(x > W - 2 * bwidth*s){
let i = Math.floor(4 + (x - W)/bwidth/s);
return bmap[i + 4*j];
}
return 0;
}
function drawOverlay(){
let W = window.innerWidth;
let H = window.innerHeight;
let MinSize = Math.min(W,H);
let MaxSize = Math.max(W,H);
let vert = H>W;
let s = vert? vsize * H/3 : hsize * H/3; //square
let m = 2; // margin
textout.style.left = (vert? 0 : 2*s + m) + "px";
textout.style.right = (vert? 0: 2*s) + "px";
cc.width = (W - 4)/pixsize;
cc.height = (H - 4)/pixsize;
let ctx = cc.getContext("2d");
let blue = "rgba(0,0,255,0.2)";
let blue2 = "rgba(0,0,255,0.4)";
let blue3 = "rgba(0,0,255,0.6)";
let white = "rgba(255,255,255, 1.0)";
let white2 = "rgba(255,255,255, 0.35)";
let black = "rgba(0,0,0, 0.35)";
let black2 = "rgba(0,0,0, 0.5)";
ctx.fillStyle = white;
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.font = "bold 15pt Arial";
// button map
let bmap = "243168750--9".split("");
//console.log(bmap)
bmap[9] = bmap[10] = -1;
bmap[8] = 10;
let offset = vert? (1-voffset)*H : (1-hoffset)*H;
let direct = vert? -1 : -1;
for(let i = 0; i<3; ++i){
for(let j=0; j<4; ++j){
let x = j<2? bwidth*s*j : W - bwidth*(4*s - s*j);
let y = offset + direct*s*(i+1);
let k = j + 4*i;
let b = bmap[k];
let highlight = temp.activeButtons.length > 0 && temp.activeButtons.includes(b);
//ctx.strokeText("A", 10, 40);
let label = buttonLabels[b - 1];
if(!label) continue;
if(temp.ctrl) label = "*" + label
ctx.fillStyle = highlight? blue : (vert? white2 : white);
ctx.fillRect(x+m, y+m, bwidth*s-2*m, s-2*m);
if(!hidelabels){
ctx.strokeStyle = black2;
if(highlight){
ctx.fillStyle = white;
} else {
if(vert) //j==1 || j==2)
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "black";
}
ctx.fillText(label, x + bwidth*s/2, y + s/2);
}
ctx.globalCompositeOperation = "source-over";
}
}
}
function mousedown(e){
temp.mousedown = true;
e.changedTouches = [e];
touchstart(e);
//let x = e.clientX;
//let y = e.clientY;
//temp.mousedown = true;
//let b = buttonMap(x, y);
//temp.buffer += buttonAlphabet[b-1];
//activateButtons([e]);
//show();
//console.log("mousedown", x, y, temp.buffer);
}
function mousemove(e){
if(!temp.mousedown) return;
e.touches = [e];
touchmove(e);
}
function mouseout(e){
mouseup(e);
}
function mouseup(e){
temp.mousedown = false;
e.changedTouches = [e];
touchend(e);
}
function touchstart(e){
e.preventDefault();
temp.touchemitted = false;
let touches = e.changedTouches;
//console.log(touches.length);
for(let i=0; i<touches.length; ++i){
if(temp.buffer.length >= 2) break;
let touch = touches[i];
let b = buttonMap(touch.clientX, touch.clientY);
temp.buffer += buttonAlphabet[b-1];
}
activateButtons(e.touches);
let char = table[temp.buffer];
if(char.length && char == temp.lastchar){
repeat(temp.buffer);
}else{
halt();
}
show();
}
function touchend(e){
e.preventDefault();
halt();
if(!temp.buffer.length){ 0; }
else if(temp.buffer.length >= 2){
temp.touchemitted = true;
emit();
}else if(!temp.touchemitted && temp.buffer.length == 1){
let touch = e.changedTouches[0];
let b = buttonMap(touch.clientX, touch.clientY);
temp.buffer += buttonAlphabet[b-1];
temp.touchemitted = true;
emit();
}
if(e.touches && e.touches.length){
activateButtons(e.touches, true)
} else {
temp.touchemitted = false;
temp.buffer = ""
activateButtons([]);
}
show();
}
function status(msg){
let text = document.createTextNode(msg);
let statusdiv = document.querySelector("#status-div");
statusdiv.innerHTML = "";
statusdiv.appendChild(text);
}
function touchmove(e){
e.preventDefault();
pause();
if(e.touches.length == 1){
activateButtons(e.touches);
let touch = e.touches[0];
let b = buttonMap(touch.clientX, touch.clientY);
let a = buttonAlphabet[b-1];
if(a != temp.buffer){
let char = table[temp.buffer + a];
//console.log('char: ' + char);
if(char && char.length && char == temp.lastchar){
//console.log("repeat");
repeat(temp.buffer + a);
pause();
}else{
halt();
}
}
} else {
activateButtons(e.touches, true)
}
drawOverlay();
}
// thumb keyboard
//console.log("here")
function initTable(){
let base = 10;
let maxlen = 2;
//table[alphabet[0]] = alphabet[0];
let codes=["ctrl", "enter", "tab", "back", "back5", "c", "space"]
for(let i=1, k=0; i<alphabet.length+codes.length; ++i){
let char = i<alphabet.length?
alphabet[i] : codes[i-alphabet.length];
let x = i+k;
let code = "";
while(x > 0){
let j = x%base;
if(j == 0) j = base;
code = alphabet[j] + code;
x = (x - j)/base;
}
if(code[0] == code[1]){
++k; --i;
table[code] = code[0];
} else {
table[code] = char;
}
}
table["jj"] = "j";
table["cc"] = "space";
}
let timeout = null;
function clearBuffer(){
temp.buffer = ""
show();
}
function repeat(_buffer){
halt();
if(table[_buffer] == "ctrl") return;
temp.repeatInterval = setInterval(()=>{
if(temp.repeatDelay){
--temp.repeatDelay;
return;
}
temp.buffer = _buffer; emit();
}, repeatdt);
}
function halt(){
clearInterval(temp.repeatInterval);
}
function pause(){
temp.repeatDelay = 2;
}
function emit(){
let char = table[temp.buffer] ?? "_";
data.text ??= "";
let curs = data.cursor;
let pre = data.text.substr(0, curs);
let post = data.text.substr(curs + 1);
if(temp.ctrl){
//status("TODO");
let back = (n)=>()=>{
let s = Math.min(pre.length, n);
if(!s) return;
status("<-");
post = pre.substr(curs-s) + post;
pre = pre.substr(0, curs-s); }
let forward = (n)=>()=>{
let s = Math.min(post.length, n);
if(!s) return;
pre += post.substr(0, s);
post = post.substr(s); }
let action = ({
"e": forward(1),
"o": back(1),
"l": forward(12),
"i": back(12),
"ctrl": ()=>{ temp.ctrl = false; },
})[char];
if(action) action();
else temp.ctrl = false;
}else if(char.length == 1){
pre += char;
}else{
if(char=="ctrl"){
temp.ctrl = true;
status("ctrl");
//return;
}
else if(char=="tab") pre += " ";
else if(char=="enter") pre += "\n";
else if(char=="space") pre += " ";
else if(char=="back")
pre = pre.substr(0, pre.length - 1);
else if(char=="back5")
pre = pre.substr(0,
Math.max(0, pre.length - 5));
else if(char=="back10")
pre = pre.substr(0,
Math.max(0, pre.length - 10));
}
if(temp.buffer.length > 1)
temp.buffer = temp.buffer.substr(0,1);
data.cursor = pre.length;
data.text = pre + "|" + post;
temp.lastchar = char;
show();
}
function show(){
let node;
textout.innerHTML = "";
let curs = data.cursor;
let s = textwindow/2
let start = Math.max(0, curs - s);
let end = Math.min(curs + 100, data.text.length);
let text = data.text.substr(start, end)
textout.appendChild(node = document.createTextNode(text));
updateButtons()
drawOverlay()
window.scrollTo(0, document.body.scrollHeight);
}
function updateButtons(){
for(let i=0; i < 10; ++i){
let char = buttonAlphabet[i];
let prefix = temp.buffer.length? temp.buffer[0] : char;
buttonLabels[i] = table[prefix + char] ?? "_";
}
}
show();
/* END */
</script>
</body>
<html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment