doc = document
elbyid = (id) => doc.getElementById(id)
var tung = ""
var topToggle = false
function TopToggle(show = null) {
topToggle = show === null ? !topToggle : show
if (topToggle) {
$('#tung').show()
} else {
$('#tung').hide()
}
layout()
}
const sfx = new Audio('sfx-click.ogg')
$(document).ready(() => {
if (localStorage.getItem('tung') === null || localStorage.getItem('tung') == "") {
tung = "201C 2014 00D7 00B0 00B7 2022 221E 00B1 2023 201D"
localStorage.setItem('tung', tung)
intro.showModal()
}
else {
tung = localStorage.getItem('tung')
}
elbyid('tung').value = tung
layout()
// persistent text
if (localStorage.getItem('text') === null || localStorage.getItem('text') == "") {
localStorage.setItem('text', "")
}
else {
elbyid('text').value = localStorage.getItem('text')
}
// $('body').on('keydown', '[contenteditable]', function(e) {
// if (e.keyCode === 13) {
// e.preventDefault() // prevent the default newline insertion
// var range = window.getSelection().getRangeAt(0) // get the current selection range
// var newline = document.createTextNode("\n") // create a new text node with the newline character
// range.insertNode(newline) // insert the new text node at the current cursor position
// range.setStartAfter(newline) // set the cursor after the new text node
// range.setEndAfter(newline) // set the end of the selection after the new text node
// window.getSelection().removeAllRanges() // remove the old selection
// window.getSelection().addRange(range) // set the new selection to the updated range
// layout()
// }
// console.log(e.keyCode)
// });
let ua = navigator.userAgent.toLowerCase()
let safari = ua.indexOf('safari') != -1 && ua.indexOf('chrome') == -1
$('#disclaimer').toggle(safari)
// dev toggle override
// safari = !safari
$('html').css('--bg', safari ? '214, 216, 221' : '16, 16, 16')
$('html').css('--fg', safari ? '214, 216, 221' : '240, 240, 240')
$('html').css('--key', safari ? '255, 255, 255' : '48, 48, 48')
$('html').css('--key-height', safari ? '40px' : '48px')
$('html').css('--key-radius', safari ? '5px' : '8px')
$('html').css('--txt', safari ? '0, 0, 0' : '255, 255, 255')
$('html').css('--txt-size', safari ? '22px' : '26px')
$('html').css('--field', safari ? '255, 255, 255' : '0, 0, 0')
$('html').css('--gap', safari ? '6px' : '5px')
let url = '/tungs.txt'
$.get(url, function (data) {
lines = data.split('\n')
nav()
})
})
var lines = null
var indexPath = [ 0 ]
function indentation(line) {
let lineIndent = 0
while (line.charAt(lineIndent) == '\t') { lineIndent++ }
return lineIndent
}
function nav() {
let rootIndex = indexPath[indexPath.length - 1]
let root = lines[rootIndex]
let rootIndent = indentation(root)
let html = ``
for (let i = rootIndex; i < lines.length; i++) {
let line = lines[i]
let lineIndent = indentation(line)
if (lineIndent < rootIndent) {
break
}
if (lineIndent > rootIndent) {
continue
}
let option = line.trim()
let pick = false
if (option.includes(':')) {
option = option.split(':')[0].trim()
pick = true
}
let disabled = false
// if next line has doesn't have a greater indent, disable the option and make it gray
if (!pick && i + 1 < lines.length) {
let nextLineIndent = indentation(lines[i + 1])
if (nextLineIndent <= rootIndent) {
disabled = true
}
}
let classes = `class="option
${disabled ? 'disabled' : ''}
${pick ? 'pick' : ''}
${(selected != null && i == selected) ? 'selected' : ''}
"`
let click = `${disabled ? '' : `onclick="select(${i}, ${pick})"`}`
html += `
${option}
`
}
let top = indexPath.length < 2
let from = top ? 'tungs' : lines[rootIndex - 1].trim()
let back = `
${from}
`
let head = `
tung-tap
`
let links = `
`
$('#nav').html(links + back + html)
}
function back() {
if (indexPath.length == 1) {
$('#nav').toggle()
return
}
indexPath.pop()
nav()
}
var selected = null
function select(index, pick) {
let line = lines[index]
if (pick) {
elbyid('tung').value = line.split(':')[1].trim()
layout()
selected = index
} else {
indexPath.push(index + 1)
}
nav()
}
function getLines(divId) {
var div = document.getElementById(divId)
var lineHeight = parseInt(window.getComputedStyle(div).getPropertyValue('line-height'))
var height = div.getBoundingClientRect().height
var lineCount = Math.floor(height / lineHeight)
var newLines = []
for (var i = 0; i < lineCount; i++) {
// var lineTop = i * lineHeight
// var lineBottom = (i + 1) * lineHeight
var lineText = div.innerText.substring(
div.getClientRects()[i].left - div.getClientRects()[0].left,
div.getClientRects()[i].right - div.getClientRects()[0].left
)
newLines.push(lineText)
}
return newLines
}
function layout() {
// keymap (tung is the proprietary name)
let tungText = elbyid('tung').value //.replace('\r\n', '\n')
tungText = tungText.replace(//g, '\n').replace(/<\/div>/g, '')
if (tung != tungText) {
tung = tungText
localStorage.setItem('tung', tung)
}
// console.log(tungText)
let html = ``
let rows = tung.split('\n')
rows.forEach(row => {
row = row.trim()
if (row.length > 0) {
html += `
\n`
let keys = row.split(' ')
keys.forEach(key => {
// print the newkey string
// and the unicode character
html += `
${key}
${String.fromCharCode("0x" + key)}
\n`
})
html += `
\n`
}
})
$('#keyboard').html(html)
$('.key').click(key => {
// override default behavior
key.preventDefault()
// if text is not focused then set the cursor to the end of #text
// let textEl = elbyid('text')
// if (document.activeElement != textEl && window.getSelection().rangeCount == 0) {
// let selection = window.getSelection()
// let range = document.createRange()
// range.selectNodeContents(textEl)
// range.collapse(false)
// selection.removeAllRanges()
// selection.addRange(range)
// }
// textEl.focus()
// insert key character at cursor position in textarea
let char = String.fromCharCode("0x" + $(key.target).attr('charcode'))
insertAtCursor(char)
// navigator.vibrate(20);
// sfx.play()
})
}
function insertAtCursor(text) {
const textarea = elbyid('text');
const scrollPos = textarea.scrollTop;
const caretPos = textarea.selectionStart;
const front = textarea.value.substring(0, caretPos);
const back = textarea.value.substring(textarea.selectionEnd, textarea.value.length);
textarea.value = front + text + back;
textarea.selectionStart = caretPos + text.length;
textarea.selectionEnd = caretPos + text.length;
textarea.scrollTop = scrollPos;
textarea.focus();
}
function copyToClipboard(id) {
var textarea = elbyid(id)
// select all the text in the textarea to show it was copied
// textarea.focus()
textarea.select()
if (!navigator.clipboard) {
console.error('Clipboard API not supported');
return;
}
navigator.clipboard.writeText(textarea.value)
.then(() => {
console.log('Text copied to clipboard')
})
.catch((err) => {
console.error('Failed to copy text: ', err)
})
}
// const newPosition = toolbar.getBoundingClientRect().top
// toolbar.classList.add('down')
function getVisibleHeight() {
var pixelRatio = window.devicePixelRatio || 1
var viewportHeight = window.visualViewport.height * pixelRatio
var windowHeight = window.innerHeight * pixelRatio
var keyboardHeight = viewportHeight - windowHeight
// $('#tung').html(`viewport ${viewportHeight}px | window ${windowHeight}px | keyboard ${keyboardHeight}px`)
return Math.abs(keyboardHeight) / pixelRatio || 0
}
var out = 400;
function loop(timestamp) {
var delta = timestamp - lastRender
time += delta
let height = getVisibleHeight()
// use the bottom of the #tung element position to use for the body top
// to be able to toggle the top of the page
let top = $('#tung').outerHeight(true) //+ $('#links').outerHeight(true) // + $('#home').outerHeight(true)
if (topToggle)
top *= 0
// lerp out to top
if (time > 100) {
out = lerp(out, top, 0.01 * delta)
}
// 'bottom': height + 'px',
// $('body').css({
// 'top': -out + 'px'
// })
// $('#keyboard').css({
// 'bottom': height + 'px',
// })
// check if need to backup the text
let text = elbyid('text').value
if (text != lastText) {
console.log(text)
localStorage.setItem('text', text)
lastText = text
}
lastRender = timestamp
window.requestAnimationFrame(loop)
}
var lastText = ''
var lastRender = 0
var time = 0
window.requestAnimationFrame(loop)
window.addEventListener("beforeinstallprompt", (e) => {
console.log('beforeinstallprompt')
// // Prevent Chrome 67 and earlier from automatically showing the prompt
// e.preventDefault()
// // Stash the event so it can be triggered later.
// deferredPrompt = e
// // // Update UI to notify the user they can add to home screen
// // addBtn.style.display = "block"
// // addBtn.addEventListener("click", (e) => {
// // // hide our user interface that shows our A2HS button
// // addBtn.style.display = "none"
// // })
// // Show the prompt
// deferredPrompt.prompt()
// // Wait for the user to respond to the prompt
// deferredPrompt.userChoice.then((choiceResult) => {
// if (choiceResult.outcome === "accepted") {
// console.log("User accepted the A2HS prompt")
// } else {
// console.log("User dismissed the A2HS prompt")
// }
// deferredPrompt = null
// })
})
function lerp(a, b, n) {
return (1 - n) * a + n * b
}