@@ -29,107 +29,161 @@ import { dom, qs$ } from './dom.js';
29
29
30
30
/******************************************************************************/
31
31
32
- ( async ( ) => {
33
- const params = new URLSearchParams ( document . location . search ) ;
34
- const url = params . get ( 'url' ) ;
35
-
36
- const a = qs$ ( '.cm-search-widget .sourceURL' ) ;
37
- dom . attr ( a , 'href' , url ) ;
38
- dom . attr ( a , 'title' , url ) ;
32
+ const urlToTextMap = new Map ( ) ;
33
+ const params = new URLSearchParams ( document . location . search ) ;
34
+ let fromURL = '' ;
35
+
36
+ const cmEditor = new CodeMirror ( qs$ ( '#content' ) , {
37
+ autofocus : true ,
38
+ gutters : [ 'CodeMirror-linenumbers' ] ,
39
+ lineNumbers : true ,
40
+ lineWrapping : true ,
41
+ matchBrackets : true ,
42
+ styleActiveLine : {
43
+ nonEmpty : true ,
44
+ } ,
45
+ } ) ;
46
+
47
+ uBlockDashboard . patchCodeMirrorEditor ( cmEditor ) ;
48
+ if ( dom . cl . has ( dom . html , 'dark' ) ) {
49
+ dom . cl . add ( '#content .cm-s-default' , 'cm-s-night' ) ;
50
+ dom . cl . remove ( '#content .cm-s-default' , 'cm-s-default' ) ;
51
+ }
52
+
53
+ // Convert resource URLs into clickable links to code viewer
54
+ cmEditor . addOverlay ( {
55
+ re : / \b (?: h r e f | s r c ) = [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / g,
56
+ match : null ,
57
+ token : function ( stream ) {
58
+ if ( stream . sol ( ) ) {
59
+ this . re . lastIndex = 0 ;
60
+ this . match = this . re . exec ( stream . string ) ;
61
+ }
62
+ if ( this . match === null ) {
63
+ stream . skipToEnd ( ) ;
64
+ return null ;
65
+ }
66
+ const end = this . re . lastIndex - 1 ;
67
+ const beg = end - this . match [ 1 ] . length ;
68
+ if ( stream . pos < beg ) {
69
+ stream . pos = beg ;
70
+ return null ;
71
+ }
72
+ if ( stream . pos < end ) {
73
+ stream . pos = end ;
74
+ return 'href' ;
75
+ }
76
+ if ( stream . pos < this . re . lastIndex ) {
77
+ stream . pos = this . re . lastIndex ;
78
+ this . match = this . re . exec ( stream . string ) ;
79
+ return null ;
80
+ }
81
+ stream . skipToEnd ( ) ;
82
+ return null ;
83
+ } ,
84
+ } ) ;
39
85
40
- const response = await fetch ( url ) ;
41
- const text = await response . text ( ) ;
86
+ /******************************************************************************/
42
87
88
+ async function fetchResource ( url ) {
89
+ if ( urlToTextMap . has ( url ) ) {
90
+ return urlToTextMap . get ( url ) ;
91
+ }
92
+ let response , text ;
93
+ try {
94
+ response = await fetch ( url ) ;
95
+ text = await response . text ( ) ;
96
+ } catch ( reason ) {
97
+ return ;
98
+ }
43
99
let mime = response . headers . get ( 'Content-Type' ) || '' ;
44
100
mime = mime . replace ( / \s * ; .* $ / , '' ) . trim ( ) ;
45
- let value = '' ;
46
101
switch ( mime ) {
47
102
case 'text/css' :
48
- value = beautifier . css ( text , { indent_size : 2 } ) ;
103
+ text = beautifier . css ( text , { indent_size : 2 } ) ;
49
104
break ;
50
105
case 'text/html' :
51
106
case 'application/xhtml+xml' :
52
107
case 'application/xml' :
53
108
case 'image/svg+xml' :
54
- value = beautifier . html ( text , { indent_size : 2 } ) ;
109
+ text = beautifier . html ( text , { indent_size : 2 } ) ;
55
110
break ;
56
111
case 'text/javascript' :
57
112
case 'application/javascript' :
58
113
case 'application/x-javascript' :
59
- value = beautifier . js ( text , { indent_size : 4 } ) ;
114
+ text = beautifier . js ( text , { indent_size : 4 } ) ;
60
115
break ;
61
116
case 'application/json' :
62
- value = beautifier . js ( text , { indent_size : 2 } ) ;
117
+ text = beautifier . js ( text , { indent_size : 2 } ) ;
63
118
break ;
64
119
default :
65
- value = text ;
66
120
break ;
67
121
}
122
+ urlToTextMap . set ( url , { mime, text } ) ;
123
+ return { mime, text } ;
124
+ }
125
+
126
+ /******************************************************************************/
68
127
69
- const cmEditor = new CodeMirror ( qs$ ( '#content' ) , {
70
- autofocus : true ,
71
- gutters : [ 'CodeMirror-linenumbers' ] ,
72
- lineNumbers : true ,
73
- lineWrapping : true ,
74
- matchBrackets : true ,
75
- mode : mime ,
76
- styleActiveLine : {
77
- nonEmpty : true ,
78
- } ,
79
- value,
80
- } ) ;
81
-
82
- uBlockDashboard . patchCodeMirrorEditor ( cmEditor ) ;
83
- if ( dom . cl . has ( dom . html , 'dark' ) ) {
84
- dom . cl . add ( '#content .cm-s-default' , 'cm-s-night' ) ;
85
- dom . cl . remove ( '#content .cm-s-default' , 'cm-s-default' ) ;
128
+ function updatePastURLs ( url ) {
129
+ const list = qs$ ( '#pastURLs' ) ;
130
+ let current ;
131
+ for ( let i = 0 ; i < list . children . length ; i ++ ) {
132
+ const span = list . children [ i ] ;
133
+ dom . cl . remove ( span , 'selected' ) ;
134
+ if ( span . textContent !== url ) { continue ; }
135
+ current = span ;
86
136
}
137
+ if ( current === undefined ) {
138
+ current = document . createElement ( 'span' ) ;
139
+ current . textContent = url ;
140
+ list . prepend ( current ) ;
141
+ }
142
+ dom . cl . add ( current , 'selected' ) ;
143
+ }
87
144
88
- // Convert resource URLs into clickable links to code viewer
89
- cmEditor . addOverlay ( {
90
- re : / \b (?: h r e f | s r c ) = [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / g,
91
- match : null ,
92
- token : function ( stream ) {
93
- if ( stream . sol ( ) ) {
94
- this . re . lastIndex = 0 ;
95
- this . match = this . re . exec ( stream . string ) ;
96
- }
97
- if ( this . match === null ) {
98
- stream . skipToEnd ( ) ;
99
- return null ;
100
- }
101
- const end = this . re . lastIndex - 1 ;
102
- const beg = end - this . match [ 1 ] . length ;
103
- if ( stream . pos < beg ) {
104
- stream . pos = beg ;
105
- return null ;
106
- }
107
- if ( stream . pos < end ) {
108
- stream . pos = end ;
109
- return 'href' ;
110
- }
111
- if ( stream . pos < this . re . lastIndex ) {
112
- stream . pos = this . re . lastIndex ;
113
- this . match = this . re . exec ( stream . string ) ;
114
- return null ;
115
- }
116
- stream . skipToEnd ( ) ;
117
- return null ;
118
- } ,
119
- } ) ;
120
-
121
- dom . on ( '#content' , 'click' , '.cm-href' , ev => {
122
- const href = ev . target . textContent ;
123
- try {
124
- const toURL = new URL ( href , url ) ;
125
- vAPI . messaging . send ( 'codeViewer' , {
126
- what : 'gotoURL' ,
127
- details : {
128
- url : `code-viewer.html?url=${ encodeURIComponent ( toURL . href ) } ` ,
129
- select : true ,
130
- } ,
131
- } ) ;
132
- } catch ( ex ) {
133
- }
134
- } ) ;
135
- } ) ( ) ;
145
+ /******************************************************************************/
146
+
147
+ async function setURL ( resourceURL ) {
148
+ const input = qs$ ( '#header input[type="url"]' ) ;
149
+ let to ;
150
+ try {
151
+ to = new URL ( resourceURL , fromURL || undefined ) ;
152
+ } catch ( ex ) {
153
+ }
154
+ if ( to === undefined ) { return ; }
155
+ if ( / ^ h t t p s ? : \/ \/ ./ . test ( to . href ) === false ) { return ; }
156
+ if ( to . href === fromURL ) { return ; }
157
+ let r ;
158
+ try {
159
+ r = await fetchResource ( to . href ) ;
160
+ } catch ( reason ) {
161
+ }
162
+ if ( r === undefined ) { return ; }
163
+ fromURL = to . href ;
164
+ dom . attr ( input , 'value' , to . href ) ;
165
+ input . value = to ;
166
+ const a = qs$ ( '.cm-search-widget .sourceURL' ) ;
167
+ dom . attr ( a , 'href' , to ) ;
168
+ dom . attr ( a , 'title' , to ) ;
169
+ cmEditor . setOption ( 'mode' , r . mime || '' ) ;
170
+ cmEditor . setValue ( r . text ) ;
171
+ updatePastURLs ( to . href ) ;
172
+ cmEditor . focus ( ) ;
173
+ }
174
+
175
+ /******************************************************************************/
176
+
177
+ setURL ( params . get ( 'url' ) ) ;
178
+
179
+ dom . on ( '#header input[type="url"]' , 'change' , ev => {
180
+ setURL ( ev . target . value ) ;
181
+ } ) ;
182
+
183
+ dom . on ( '#pastURLs' , 'mousedown' , 'span' , ev => {
184
+ setURL ( ev . target . textContent ) ;
185
+ } ) ;
186
+
187
+ dom . on ( '#content' , 'click' , '.cm-href' , ev => {
188
+ setURL ( ev . target . textContent ) ;
189
+ } ) ;
0 commit comments