littlebigcomputer/assembler/ldt/lib/TextareaDecorator.js
2023-10-30 17:50:05 +01:00

111 lines
2.9 KiB
JavaScript

/* TextareaDecorator.js
* written by Colin Kuebler 2012
* Part of LDT, dual licensed under GPLv3 and MIT
* Builds and maintains a styled output layer under a textarea input layer
*/
function TextareaDecorator(textarea, parser) {
/* INIT */
var api = this;
this.parser = parser;
this.errorMap = {};
// construct editor DOM
var parent = document.createElement("div");
var output = document.createElement("pre");
parent.appendChild(output);
var label = document.createElement("label");
parent.appendChild(label);
// replace the textarea with RTA DOM and reattach on label
textarea.parentNode.replaceChild(parent, textarea);
label.appendChild(textarea);
// transfer the CSS styles to our editor
parent.className = 'ldt ' + textarea.className;
textarea.className = '';
// turn off built-in spellchecking in firefox
textarea.spellcheck = false;
// turn off word wrap
textarea.wrap = "off";
var getParser = () => {
return this.parser;
}
var getErrorMap = () => {
return this.errorMap;
}
// coloring algorithm
var color = function (input, output, parser) {
var oldTokens = output.childNodes;
var newTokens = parser.tokenize(input);
var firstDiff, lastDiffNew, lastDiffOld;
output.innerHTML = '';
// add in modified spans
for (const token of newTokens) {
var span = document.createElement("span");
span.className = token.tag;
if (getErrorMap()[token.lineNumber]) {
span.classList.add('error');
}
span.textContent = span.innerText = token.text;
output.appendChild(span);
}
};
api.input = textarea;
api.output = output;
api.update = function () {
var input = textarea.value;
if (input) {
color(input, output, getParser());
// determine the best size for the textarea
var lines = input.split('\n');
// find the number of columns
var maxlen = 0, curlen;
for (var i = 0; i < lines.length; i++) {
// calculate the width of each tab
var tabLength = 0, offset = -1;
while ((offset = lines[i].indexOf('\t', offset + 1)) > -1) {
tabLength += 7 - (tabLength + offset) % 8;
}
var curlen = lines[i].length + tabLength;
// store the greatest line length thus far
maxlen = maxlen > curlen ? maxlen : curlen;
}
textarea.cols = maxlen + 1;
textarea.rows = lines.length + 2;
} else {
// clear the display
output.innerHTML = '';
// reset textarea rows/cols
textarea.cols = textarea.rows = 1;
}
};
// detect all changes to the textarea,
// including keyboard input, cut/copy/paste, drag & drop, etc
if (textarea.addEventListener) {
// standards browsers: oninput event
textarea.addEventListener("input", api.update, false);
} else {
// MSIE: detect changes to the 'value' property
textarea.attachEvent("onpropertychange",
function (e) {
if (e.propertyName.toLowerCase() === 'value') {
api.update();
}
}
);
}
// initial highlighting
api.update();
api.setErrorMap = (errors) => {
this.errorMap = errors;
api.update();
}
return api;
};