@@ -29,107 +29,161 @@ import { dom, qs$ } from './dom.js';
2929
3030/******************************************************************************/ 
3131
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+ } ) ; 
3985
40-     const  response  =  await  fetch ( url ) ; 
41-     const  text  =  await  response . text ( ) ; 
86+ /******************************************************************************/ 
4287
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+     } 
4399    let  mime  =  response . headers . get ( 'Content-Type' )  ||  '' ; 
44100    mime  =  mime . replace ( / \s * ; .* $ / ,  '' ) . trim ( ) ; 
45-     let  value  =  '' ; 
46101    switch  (  mime  )  { 
47102        case  'text/css' :
48-             value  =  beautifier . css ( text ,  {  indent_size : 2  } ) ; 
103+             text  =  beautifier . css ( text ,  {  indent_size : 2  } ) ; 
49104            break ; 
50105        case  'text/html' :
51106        case  'application/xhtml+xml' :
52107        case  'application/xml' :
53108        case  'image/svg+xml' :
54-             value  =  beautifier . html ( text ,  {  indent_size : 2  } ) ; 
109+             text  =  beautifier . html ( text ,  {  indent_size : 2  } ) ; 
55110            break ; 
56111        case  'text/javascript' :
57112        case  'application/javascript' :
58113        case  'application/x-javascript' :
59-             value  =  beautifier . js ( text ,  {  indent_size : 4  } ) ; 
114+             text  =  beautifier . js ( text ,  {  indent_size : 4  } ) ; 
60115            break ; 
61116        case  'application/json' :
62-             value  =  beautifier . js ( text ,  {  indent_size : 2  } ) ; 
117+             text  =  beautifier . js ( text ,  {  indent_size : 2  } ) ; 
63118            break ; 
64119        default :
65-             value  =  text ; 
66120            break ; 
67121    } 
122+     urlToTextMap . set ( url ,  {  mime,  text } ) ; 
123+     return  {  mime,  text } ; 
124+ } 
125+ 
126+ /******************************************************************************/ 
68127
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 ; 
86136    } 
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+ } 
87144
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