hajime/public/assets/js/ldt/TextareaDecorator.js
2023-08-12 00:16:32 +02:00

107 lines
3.6 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;
// 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";
// coloring algorithm
var color = function( input, output, parser ){
var oldTokens = output.childNodes;
var newTokens = parser.tokenize(input);
var firstDiff, lastDiffNew, lastDiffOld;
// find the first difference
for( firstDiff = 0; firstDiff < newTokens.length && firstDiff < oldTokens.length; firstDiff++ )
if( newTokens[firstDiff] !== oldTokens[firstDiff].textContent ) break;
// trim the length of output nodes to the size of the input
while( newTokens.length < oldTokens.length )
output.removeChild(oldTokens[firstDiff]);
// find the last difference
for( lastDiffNew = newTokens.length-1, lastDiffOld = oldTokens.length-1; firstDiff < lastDiffOld; lastDiffNew--, lastDiffOld-- )
if( newTokens[lastDiffNew] !== oldTokens[lastDiffOld].textContent ) break;
// update modified spans
for( ; firstDiff <= lastDiffOld; firstDiff++ ){
oldTokens[firstDiff].className = parser.identify(newTokens[firstDiff]);
oldTokens[firstDiff].textContent = oldTokens[firstDiff].innerText = newTokens[firstDiff];
}
// add in modified spans
for( var insertionPt = oldTokens[firstDiff] || null; firstDiff <= lastDiffNew; firstDiff++ ){
var span = document.createElement("span");
span.className = parser.identify(newTokens[firstDiff]);
span.textContent = span.innerText = newTokens[firstDiff];
output.insertBefore( span, insertionPt );
}
};
api.input = textarea;
api.output = output;
api.update = function(){
var input = textarea.value;
if( input ){
color( input, output, parser );
// 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 + 1;
} 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();
return api;
};