diff --git a/app/modules/niklan/assets/js/hljs.init.js b/app/modules/niklan/assets/js/hljs.init.js index b690da4b..d74a20b5 100644 --- a/app/modules/niklan/assets/js/hljs.init.js +++ b/app/modules/niklan/assets/js/hljs.init.js @@ -1,5 +1,8 @@ ((Drupal, once, drupalSettings) => { + let worker; + let requestId = 0; + const pendingRequests = new Map(); const createHighlightedEvent = (element, html) => new CustomEvent('niklan-highlight:highlighted', { @@ -11,35 +14,54 @@ bubbles: true }); - const handleIntersection = (entries, observer) => { + const handleIntersection = (entries) => { entries.forEach(({ isIntersecting, target: codeElement }) => { if (!isIntersecting) return; const preElement = codeElement.parentElement; codeElement.classList.add('hljs'); + const currentRequestId = requestId; + requestId += 1; + pendingRequests.set(currentRequestId, codeElement); + worker.postMessage({ + requestId: currentRequestId, code: codeElement.textContent, language: preElement.dataset.language, esModulesBasePath: drupalSettings.highlightJs.esModulesBasePath }); - - worker.addEventListener('message', ({ data }) => { - codeElement.innerHTML = data; - codeElement.dispatchEvent(createHighlightedEvent(codeElement, data)); - observer.unobserve(codeElement); - }, { once: true }); }); }; + const handleWorkerMessage = ({ data }) => { + // eslint-disable-next-line no-shadow + const { requestId, result, error } = data; + const codeElement = pendingRequests.get(requestId); + + if (!codeElement) return; + + if (error) { + console.error(error); + return; + } + + codeElement.innerHTML = result; + codeElement.dispatchEvent(createHighlightedEvent(codeElement, result)); + pendingRequests.delete(requestId); + }; + const registerIntersectionObserver = () => { const observer = new IntersectionObserver(handleIntersection); Array .from(once('code-highlight', 'pre > code')) .forEach(code => observer.observe(code)); + worker = new Worker(drupalSettings.highlightJs.workerPath, { type: 'module' }); + worker.addEventListener('message', handleWorkerMessage); }; + const lazyCallback = callback => window.requestIdleCallback ? requestIdleCallback(callback) : callback(); diff --git a/app/modules/niklan/assets/js/hljs.worker.js b/app/modules/niklan/assets/js/hljs.worker.js index d770402d..7bd2e0be 100644 --- a/app/modules/niklan/assets/js/hljs.worker.js +++ b/app/modules/niklan/assets/js/hljs.worker.js @@ -14,30 +14,34 @@ const supportedLanguages = { onmessage = async (event) => { try { - const hljsModule = await import(`${event.data.esModulesBasePath}/highlight.min.js`); + const { requestId, code, language, esModulesBasePath } = event.data; + + const hljsModule = await import(`${esModulesBasePath}/highlight.min.js`); const hljs = hljsModule.default; - if (event.data.language && supportedLanguages[event.data.language]) { - const langModule = await import(`${event.data.esModulesBasePath}/${supportedLanguages[event.data.language]}`); - hljs.registerLanguage(event.data.language, langModule.default); + if (language && supportedLanguages[language]) { + const langModule = await import(`${esModulesBasePath}/${supportedLanguages[language]}`); + hljs.registerLanguage(language, langModule.default); } let result; - if (event.data.language && hljs.getLanguage(event.data.language)) { - result = hljs.highlight(event.data.code, { - language: event.data.language, - }); + if (language && hljs.getLanguage(language)) { + result = hljs.highlight(code, { language }); } else { - result = hljs.highlightAuto(event.data.code); + result = hljs.highlightAuto(code); } - postMessage(result.value); + postMessage({ + requestId, + result: result.value + }); } catch (error) { postMessage({ + requestId: event.data.requestId, error: `Processing failed: ${error.message}` }); } - }; +