145 lines
3.9 KiB
TypeScript
145 lines
3.9 KiB
TypeScript
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,
|
|
};
|
|
}
|