95 lines
2.9 KiB
TypeScript
95 lines
2.9 KiB
TypeScript
/*
|
|
* Copyright (c) 2023, networkException <git@nwex.de>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
import { Color } from './html/highlighter/properties/color.js';
|
|
import { Property } from './html/highlighter/property.js';
|
|
import { Span } from './html/highlighter/span.js';
|
|
import { Inspector } from './html/inspector.js';
|
|
|
|
const sameProperties = (a: Array<Property> | undefined, b: Array<Property> | undefined): boolean => {
|
|
if (a === undefined || b === undefined) return false;
|
|
if (a.length !== b.length) return false;
|
|
|
|
for (const property of a) {
|
|
let found = false;
|
|
|
|
for (const otherProperty of b)
|
|
if (property.equals(otherProperty))
|
|
found = true;
|
|
|
|
if (!found) return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
const applyProperties = (element: HTMLSpanElement, properties: Array<Property>): void => {
|
|
for (const property of properties)
|
|
property.apply(element);
|
|
};
|
|
|
|
export function render(text: string, spans: Array<Span>, inspector: Inspector): void {
|
|
console.time('render');
|
|
|
|
const container = document.createElement('pre');
|
|
container.ariaHidden = 'true';
|
|
|
|
for (const child of document.body.children) {
|
|
(child as HTMLElement).style.display = 'none';
|
|
child.ariaHidden = 'false';
|
|
}
|
|
|
|
document.body.appendChild(container);
|
|
|
|
const defaultProperties: Array<Property> = [ Color.Plain ];
|
|
const defaultTagName: keyof HTMLElementTagNameMap = 'span';
|
|
|
|
let lastProperties: Array<Property> = defaultProperties;
|
|
let lastTagName: keyof HTMLElementTagNameMap = defaultTagName;
|
|
let lastElement: HTMLSpanElement = document.createElement(lastTagName);
|
|
|
|
applyProperties(lastElement, lastProperties);
|
|
|
|
container.appendChild(lastElement);
|
|
|
|
for (let characterIndex = 0; characterIndex < text.length; characterIndex++) {
|
|
const character = text[characterIndex];
|
|
|
|
let topMostProperties: Array<Property> = defaultProperties;
|
|
let topMostTagName: keyof HTMLElementTagNameMap = defaultTagName;
|
|
|
|
const matchingSpans = new Array<Span>();
|
|
|
|
for (const span of spans) {
|
|
if (span.contains(characterIndex)) {
|
|
matchingSpans.push(span);
|
|
|
|
topMostProperties = span.properties;
|
|
topMostTagName = span.tagName;
|
|
}
|
|
}
|
|
|
|
if (sameProperties(lastProperties, topMostProperties) && topMostTagName === lastTagName) {
|
|
lastElement.textContent += character;
|
|
|
|
inspector.instrument(lastElement, matchingSpans);
|
|
} else {
|
|
lastElement = document.createElement(topMostTagName);
|
|
lastElement.textContent = character;
|
|
|
|
inspector.instrument(lastElement, matchingSpans);
|
|
|
|
applyProperties(lastElement, topMostProperties);
|
|
|
|
lastProperties = topMostProperties;
|
|
lastTagName = topMostTagName;
|
|
|
|
container.appendChild(lastElement);
|
|
}
|
|
}
|
|
|
|
console.timeEnd('render');
|
|
}
|