UNPKG

@bhsd/codemirror-mediawiki

Version:

Modified CodeMirror mode based on wikimedia/mediawiki-extensions-CodeMirror

109 lines (108 loc) 4.01 kB
import { EditorView } from '@codemirror/view'; import { ensureSyntaxTree } from '@codemirror/language'; import { tokens } from './config'; import { hasTag } from './mediawiki'; const { vendor, userAgent, maxTouchPoints, platform } = navigator; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition export const isMac = vendor?.includes('Apple Computer') && (userAgent.includes('Mobile/') || maxTouchPoints > 2) || platform.includes('Mac'); const modKey = isMac ? 'metaKey' : 'ctrlKey', key = isMac ? 'Meta' : 'Control', tags = ['extLinkProtocol', 'extLink', 'freeExtLinkProtocol', 'freeExtLink', 'magicLink', 'pageName'], links = ['extlink-protocol', 'extlink', 'free-extlink-protocol', 'free-extlink', 'magic-link'], wikiLinks = [ 'template-name', 'link-pagename', 'parserfunction.cm-mw-pagename', 'exttag-attribute-value.cm-mw-pagename', 'file-text.cm-mw-pagename', ]; const toggleOpenLinks = (toggle) => { for (const ele of document.querySelectorAll('.cm-content')) { if (toggle) { ele.style.setProperty('--codemirror-cursor', 'pointer'); } else { ele.style.removeProperty('--codemirror-cursor'); } } }; /* eslint-disable @typescript-eslint/no-unnecessary-condition */ globalThis.document?.addEventListener('keydown', e => { if (e.key === key) { toggleOpenLinks(true); } }); globalThis.document?.addEventListener('keyup', e => { if (e.key === key) { toggleOpenLinks(); } }); globalThis.document?.addEventListener('visibilitychange', () => { if (document.hidden) { toggleOpenLinks(); } }); /* eslint-enable @typescript-eslint/no-unnecessary-condition */ const wrapURL = (url) => url.startsWith('//') ? location.protocol + url : url; export const mouseEventListener = (e, view, langConfig) => { if (!e[modKey] || !(e.target instanceof Element && getComputedStyle(e.target).textDecorationLine === 'underline')) { return undefined; } const position = view.posAtCoords(e); if (!position) { return undefined; } const { state } = view, tree = ensureSyntaxTree(state, position); if (!tree) { return undefined; } let node = tree.resolve(position, -1); if (node.name.includes(tokens.linkToSection)) { node = node.prevSibling; } else if (!hasTag(new Set(node.name.split('_')), tags)) { node = tree.resolve(position, 1); } const { name, from, to } = node; if (name.includes(tokens.pageName) && typeof langConfig?.titleParser === 'function') { return langConfig.titleParser(state, node); } else if (name.includes('-extlink-protocol')) { return wrapURL(state.sliceDoc(from, node.nextSibling.to)); } else if (/-extlink(?:_|$)/u.test(name)) { return wrapURL(state.sliceDoc(node.prevSibling.from, to)); } else if (name.includes(tokens.magicLink)) { const link = state.sliceDoc(from, to); if (link.startsWith('RFC')) { return `https://datatracker.ietf.org/doc/html/rfc${link.slice(3).trim()}`; } else if (link.startsWith('PMID')) { return `https://pubmed.ncbi.nlm.nih.gov/${link.slice(4).trim()}`; } else if (typeof langConfig?.isbnParser === 'function') { return langConfig.isbnParser(link); } } return undefined; }; export default ({ langConfig }) => [ EditorView.domEventHandlers({ mousedown(e, view) { if (e.button !== 0) { return undefined; } const url = mouseEventListener(e, view, langConfig); if (url) { open(url, '_blank'); return true; } return undefined; }, }), EditorView.theme({ [[...links, ...langConfig?.titleParser ? wikiLinks : []].map(type => `.cm-mw-${type}`).join()]: { cursor: 'var(--codemirror-cursor)', }, }), ];