import { NODE_ID, COMMENT_NODE_CLASSNAME_PREFIX } from 'common/constants.config'; import { parseMessage, postMessageToIframe } from 'utils/post-message'; import { createIframe } from 'utils/create-iframe'; import type { Theme } from 'common/types'; import { closeProfile, openProfile } from 'profile'; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } function init() { window.REMARK42 = window.REMARK42 || {}; window.REMARK42.createInstance = createInstance; createInstance(window.remark_config); window.dispatchEvent(new Event('REMARK42::ready')); } function createInstance(config: typeof window.remark_config) { const root = document.getElementById(NODE_ID); if (!root) { throw new Error("Remark42: Can't find root node."); } if (!window.remark_config) { throw new Error('Remark42: Config object is undefined.'); } if (!window.remark_config.site_id) { throw new Error('Remark42: Site ID is undefined.'); } let titleObserver: MutationObserver | null = null; config.url = (config.url || `${window.location.origin}${window.location.pathname}`).split('#')[0]; const iframe = (root.firstElementChild as HTMLIFrameElement) || createIframe(config); root.appendChild(iframe); window.addEventListener('message', handleReceiveMessage); window.addEventListener('hashchange', handleHashChange); document.addEventListener('click', postClickOutsideToIframe); const titleElement = document.querySelector('title'); if (titleElement) { titleObserver = new MutationObserver((mutations) => postTitleToIframe(mutations[0].target.textContent!)); titleObserver.observe(titleElement, { subtree: true, characterData: true, childList: true, }); } function handleReceiveMessage(event: MessageEvent): void { const data = parseMessage(event); if (typeof data.height === 'number') { iframe.style.height = `${data.height}px`; } if (typeof data.scrollTo === 'number') { window.scrollTo(window.pageXOffset, data.scrollTo + iframe.getBoundingClientRect().top + window.pageYOffset); } if (typeof data.profile === 'object') { if (data.profile === null) { closeProfile(); } else { openProfile({ ...config, ...data.profile }); } } if (data.signout === true) { postMessageToIframe(iframe, { signout: true }); } if (data.inited === true) { postHashToIframe(); postTitleToIframe(document.title); } } function postHashToIframe(hash = window.location.hash) { if (!hash.startsWith(`#${COMMENT_NODE_CLASSNAME_PREFIX}`)) { return; } postMessageToIframe(iframe, { hash }); } function handleHashChange(evt: HashChangeEvent) { const url = new URL(evt.newURL); postHashToIframe(url.hash); } function postTitleToIframe(title: string) { postMessageToIframe(iframe, { title }); } function postClickOutsideToIframe(evt: MouseEvent) { if (iframe.contains(evt.target as Node)) { return; } postMessageToIframe(iframe, { clickOutside: true }); } function changeTheme(theme: Theme) { window.remark_config.theme = theme; postMessageToIframe(iframe, { theme }); } function destroy() { window.removeEventListener('message', handleReceiveMessage); window.removeEventListener('hashchange', handleHashChange); document.removeEventListener('click', postClickOutsideToIframe); if (titleObserver) { titleObserver.disconnect(); // Allow browser to drop observer and iframe captured in callback // to prevent attempts to send messages to detached frame titleObserver = null; } iframe.remove(); } // TODO: These do not appear in Chrome DevTools window.REMARK42.changeTheme = changeTheme; window.REMARK42.destroy = () => { destroy(); delete window.REMARK42.changeTheme; delete window.REMARK42.destroy; }; return { changeTheme, destroy, }; }