(function (win, doc) { const sourceMap = {}; const sourceMapReverse = {}; let cursor = 0; let timer = null; function openVscode(node) { let path = null; if (node.dataset.sid) { path = sidToURI(node.dataset.sid); } if (node.dataset.source) { path = 'vscode://file/' + node.dataset.source; } if (!path) { return console.warn('Not found data-source'); } win.location.href = path; } function sourceToId(node) { if (!node.dataset.source) return; const source = node.dataset.source; const splits = source.split(':'); const column = splits.pop(); const row = splits.pop(); const file = splits.join(':'); if (!sourceMap[file]) { cursor++; sourceMap[file] = cursor; sourceMapReverse[cursor] = file; } const id = sourceMap[file]; node.removeAttribute('data-source'); node.setAttribute('data-sid', `${id}:${row}:${column}`); } function sidToURI(sid) { const [id, row, column] = sid.split(':'); const path = 'vscode://file/' + sourceMapReverse[id] + ':' + row + ':' + column; return path; } class Selector { constructor(node) { this.sids = []; this.containerId = '__source-ref-panel'; this.getAncestorSids = (node) => { const sids = []; let cur = node; while (cur !== doc.body) { if (cur.dataset.sid) { sids.push(cur.dataset.sid); } cur = cur.parentElement; } return sids; }; this.focusBlock = null; this.setFocusBlock = (target) => { if (target === null) { // clear if target is null if (this.focusBlock) { doc.body.removeChild(this.focusBlock); this.focusBlock = null; } return; } if (!this.focusBlock) { this.focusBlock = doc.createElement('div'); this.focusBlock.className = '__source-ref-mask'; this.focusBlock.style.position = 'absolute'; this.focusBlock.style.backgroundColor = 'rgba(134, 185, 242, 0.5)'; doc.body.appendChild(this.focusBlock); } const rect = target.getBoundingClientRect(); this.focusBlock.style.height = rect.height + 'px'; this.focusBlock.style.width = rect.width + 'px'; this.focusBlock.style.left = rect.x + 'px'; this.focusBlock.style.top = rect.y + 'px'; }; this.getContainer = () => { const container = doc.getElementById(this.containerId); if (!container) { const div = doc.createElement('div'); div.id = this.containerId; doc.body.appendChild(div); // Add dom red border on hover div.addEventListener('mouseover', (e) => { const node = e.target; if (node.dataset.tid) { const target = doc.querySelector( `[data-sid="${node.dataset.tid}"]` ); if (target) { target.classList.add('__source-ref-selected'); this.setFocusBlock(target); } } }); // Remove dom red border when leave div.addEventListener('mouseout', (e) => { const node = e.target; if (node.dataset.tid) { const target = doc.querySelector( `[data-sid="${node.dataset.tid}"]` ); if (target) { target.classList.remove('__source-ref-selected'); } } }); const close = () => { this.setFocusBlock(null); doc.body.removeChild(div); }; // click event div.addEventListener('click', (e) => { const node = e.target; const command = node.dataset.command; switch (command) { case 'close': { e.stopPropagation(); close(); return; } default: console.warn('Unknown command', command); } }); // keyboard event function escKeyHandler(e) { if (e.key === 'Escape') { e.stopPropagation(); close(); doc.removeEventListener('keydown', escKeyHandler); } } doc.addEventListener('keydown', escKeyHandler); return div; } return container; }; this.renderHTML = () => { const html = `
X
${this.sids .map((sid) => { const uri = sidToURI(sid); // 这里加了一个左向省略,暂时没用上,先放着 return `source-ref: ${uri}`; }) .join('')}
`; const container = this.getContainer(); container.innerHTML = html; }; this.sids = this.getAncestorSids(node); } } function init() { win.vscode = (node = win.$0) => { openVscode(node); }; doc.body.addEventListener( 'click', (e) => { if (e.altKey) { e.preventDefault(); e.stopPropagation(); const selector = new Selector(e.target); selector.renderHTML(); } }, true ); const mo = new MutationObserver(() => { if (timer) { clearTimeout(timer); } timer = setTimeout(() => { // recal sid doc .querySelectorAll('[data-source]') .forEach((node) => sourceToId(node)); }, 500); }); mo.observe(doc.body, { attributes: true, childList: true, subtree: true, }); } init(); })(window, document);