Last active
March 3, 2023 19:47
-
-
Save derekmc/afc4db5425d94531ec34996df95457f2 to your computer and use it in GitHub Desktop.
repeat on swipe too
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 */ | |
| :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