|
|
|
@ -11,19 +11,44 @@ |
|
|
|
|
return elements.length < 2 ? elements[0] : elements; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const output = $("#output"); |
|
|
|
|
const input = $("#input"); |
|
|
|
|
const numbers = $(".linenumbers"); |
|
|
|
|
let timeout; |
|
|
|
|
const output = $("#output"); |
|
|
|
|
const input = $("#input"); |
|
|
|
|
const numbers = $(".linenumbers"); |
|
|
|
|
const position = $("#position"); |
|
|
|
|
const percent = $("#percent"); |
|
|
|
|
const editor = $(".editor"); |
|
|
|
|
let lastLine = 1; |
|
|
|
|
let timeout; |
|
|
|
|
let lastSelection; |
|
|
|
|
|
|
|
|
|
const onLineChange = event => { |
|
|
|
|
const start = input.selectionStart; |
|
|
|
|
if (start === lastSelection) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
const linesToCursor = input.value.substr(0, start).split("\n"); |
|
|
|
|
const currentLine = linesToCursor.length; |
|
|
|
|
const char = linesToCursor[linesToCursor.length - 1].length; |
|
|
|
|
const ll = $(`#line_${lastLine}`); |
|
|
|
|
if (ll) |
|
|
|
|
ll.classList.remove("active"); |
|
|
|
|
$(`#line_${currentLine}`).classList.add("active"); |
|
|
|
|
|
|
|
|
|
position.innerHTML = `[${currentLine}:${char}]`; |
|
|
|
|
|
|
|
|
|
lastLine = currentLine; |
|
|
|
|
lastSelection = start; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const updateOutput = (event) => { |
|
|
|
|
const text = event ? event.target.value : input.value; |
|
|
|
|
const lines = text.split("\n").length; |
|
|
|
|
let html = ""; |
|
|
|
|
for (let i = 1; i <= lines; i++) { |
|
|
|
|
html += `${i}<br>`; |
|
|
|
|
html += `<span id="line_${i}">${i}</span>`; |
|
|
|
|
} |
|
|
|
|
numbers.innerHTML = html; |
|
|
|
|
onLineChange(); |
|
|
|
|
|
|
|
|
|
timeout = setTimeout(() => { |
|
|
|
|
|
|
|
|
@ -40,6 +65,9 @@ |
|
|
|
|
document.addEventListener("DOMContentLoaded", () => { |
|
|
|
|
updateOutput(); |
|
|
|
|
|
|
|
|
|
for (event of ["click", "change", "keydown", "focus"]) |
|
|
|
|
input.addEventListener(event, () => onLineChange()); |
|
|
|
|
|
|
|
|
|
input.addEventListener("keydown", event => { |
|
|
|
|
if (event.key !== "Tab") |
|
|
|
|
return; |
|
|
|
@ -56,6 +84,16 @@ |
|
|
|
|
clearTimeout(timeout); |
|
|
|
|
updateOutput(event); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
editor.addEventListener("scroll", () => { |
|
|
|
|
const height = input.clientHeight - editor.clientHeight; |
|
|
|
|
const top = editor.scrollTop; |
|
|
|
|
|
|
|
|
|
const fromTop = Math.min(100, Math.max(0, Math.round(top * 100 / height))); |
|
|
|
|
percent.innerHTML = `${fromTop}%`; |
|
|
|
|
|
|
|
|
|
console.log("SCROOLLL", fromTop, height, top); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
window.highlight = (col, row) => { |
|
|
|
@ -102,6 +140,7 @@ |
|
|
|
|
body { |
|
|
|
|
display: grid; |
|
|
|
|
grid-template-columns: 1fr 1fr; |
|
|
|
|
grid-template-rows: auto 30px; |
|
|
|
|
font-family: sans-serif; |
|
|
|
|
margin: 0; |
|
|
|
|
padding: 0; |
|
|
|
@ -146,28 +185,57 @@ |
|
|
|
|
height: 100%; |
|
|
|
|
border-right: none; |
|
|
|
|
overflow-y: auto; |
|
|
|
|
border-bottom: none; |
|
|
|
|
overflow-x: hidden; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.linenumbers { |
|
|
|
|
font-size: 1.2rem; |
|
|
|
|
text-align: right; |
|
|
|
|
padding: 1rem; |
|
|
|
|
padding: 1rem 0; |
|
|
|
|
color: #aaa; |
|
|
|
|
font-family: monospace; |
|
|
|
|
background: #222; |
|
|
|
|
} |
|
|
|
|
.linenumbers span { |
|
|
|
|
display: block; |
|
|
|
|
padding: 0 1rem; |
|
|
|
|
} |
|
|
|
|
.linenumbers span.active { |
|
|
|
|
color: yellow; |
|
|
|
|
background: rgba(255,255,255,0.05); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.statusbar { |
|
|
|
|
font-size: 0.6rem; |
|
|
|
|
line-height: 30px; |
|
|
|
|
padding: 0 1rem; |
|
|
|
|
background: rgba(0,0,0,0.2); |
|
|
|
|
text-align: right; |
|
|
|
|
border: none; |
|
|
|
|
color: #888; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#position { |
|
|
|
|
color: dodgerblue; |
|
|
|
|
font-weight: bold; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#output { |
|
|
|
|
overflow-y: auto; |
|
|
|
|
padding: 4rem; |
|
|
|
|
max-width: 100%; |
|
|
|
|
grid-row: span 2; |
|
|
|
|
} |
|
|
|
|
#output code { |
|
|
|
|
word-break: break-word; |
|
|
|
|
white-space: break-spaces; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#output table { |
|
|
|
|
width: 100%; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#output img { |
|
|
|
|
max-width: 100%; |
|
|
|
|
height: auto; |
|
|
|
@ -395,6 +463,10 @@ Unit tests can be run via `composer`: |
|
|
|
|
</textarea> |
|
|
|
|
</section> |
|
|
|
|
<section id="output"></section> |
|
|
|
|
<section class="statusbar"> |
|
|
|
|
<span id="percent">0%</span> |
|
|
|
|
<span id="position">[1:2]</span> |
|
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
</body> |
|
|
|
|
</html> |