From bbd9470a986f1c89f5b05856518c903d642baf9a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 8 Mar 2023 12:31:39 -0500 Subject: [PATCH] Convert resource URLs into clickable links in code viewer --- src/css/code-viewer.css | 7 +++++ src/js/code-viewer.js | 53 +++++++++++++++++++++++++++++++++++++ src/js/codemirror/search.js | 5 ++-- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/css/code-viewer.css b/src/css/code-viewer.css index 44ac9bc1b..a3c4d0f70 100644 --- a/src/css/code-viewer.css +++ b/src/css/code-viewer.css @@ -11,3 +11,10 @@ body { #content { flex-grow: 1; } + +.cm-href { + cursor: pointer; + } +.cm-href:hover { + text-decoration: underline; + } diff --git a/src/js/code-viewer.js b/src/js/code-viewer.js index cebec5367..15a9698c6 100644 --- a/src/js/code-viewer.js +++ b/src/js/code-viewer.js @@ -32,11 +32,14 @@ import { dom, qs$ } from './dom.js'; (async ( ) => { const params = new URLSearchParams(document.location.search); const url = params.get('url'); + const a = qs$('.cm-search-widget .sourceURL'); dom.attr(a, 'href', url); dom.attr(a, 'title', url); + const response = await fetch(url); const text = await response.text(); + let mime = response.headers.get('Content-Type') || ''; mime = mime.replace(/\s*;.*$/, '').trim(); let value = ''; @@ -62,6 +65,7 @@ import { dom, qs$ } from './dom.js'; value = text; break; } + const cmEditor = new CodeMirror(qs$('#content'), { autofocus: true, gutters: [ 'CodeMirror-linenumbers' ], @@ -74,9 +78,58 @@ import { dom, qs$ } from './dom.js'; }, value, }); + uBlockDashboard.patchCodeMirrorEditor(cmEditor); if ( dom.cl.has(dom.html, 'dark') ) { dom.cl.add('#content .cm-s-default', 'cm-s-night'); dom.cl.remove('#content .cm-s-default', 'cm-s-default'); } + + // Convert resource URLs into clickable links to code viewer + cmEditor.addOverlay({ + re: /\b(?:href|src)=["']([^"']+)["']/g, + match: null, + token: function(stream) { + if ( stream.sol() ) { + this.re.lastIndex = 0; + this.match = this.re.exec(stream.string); + } + if ( this.match === null ) { + stream.skipToEnd(); + return null; + } + const end = this.re.lastIndex - 1; + const beg = end - this.match[1].length; + if ( stream.pos < beg ) { + stream.pos = beg; + return null; + } + if ( stream.pos < end ) { + stream.pos = end; + return 'href'; + } + if ( stream.pos < this.re.lastIndex ) { + stream.pos = this.re.lastIndex; + this.match = this.re.exec(stream.string); + return null; + } + stream.skipToEnd(); + return null; + }, + }); + + dom.on('#content', 'click', '.cm-href', ev => { + const href = ev.target.textContent; + try { + const toURL = new URL(href, url); + vAPI.messaging.send('codeViewer', { + what: 'gotoURL', + details: { + url: `code-viewer.html?url=${encodeURIComponent(toURL.href)}`, + select: true, + }, + }); + } catch(ex) { + } + }); })(); diff --git a/src/js/codemirror/search.js b/src/js/codemirror/search.js index 125bd28b4..c25e90889 100644 --- a/src/js/codemirror/search.js +++ b/src/js/codemirror/search.js @@ -27,7 +27,8 @@ 'use strict'; -(function(CodeMirror) { +{ + const CodeMirror = self.CodeMirror; const searchOverlay = function(query, caseInsensitive) { if ( typeof query === 'string' ) @@ -449,4 +450,4 @@ CodeMirror.defineInitHook(function(cm) { getSearchState(cm); }); -})(self.CodeMirror); +}