1+ ( function ( ) {
2+ var global = window ;
3+ if ( global . _encodedRW ) {
4+ console . log ( '/!\\ ignoring encoded ' + location . pathname ) ;
5+ return ;
6+ }
7+ global . _encodedRW = true ;
8+
9+ var p = location . pathname ;
10+ var i = p . indexOf ( '/' , 4 ) ;
11+ var base = p . substring ( 4 , i ) ;
12+ var opts = p . substring ( i + 1 , p . indexOf ( '/' , i + 1 ) ) ;
13+ console . log ( 'base path is ' + base + '/' + opts + ' (' + location . pathname + ')' ) ;
14+
15+ function b64ec ( c ) {
16+ return c === '-' ? '+' : ( c === '_' ? '/' : '' ) ;
17+ }
18+ function b64e ( s ) {
19+ return btoa ( s ) . replace ( / [ - _ = ] / g, b64ec ) ;
20+ }
21+ function b64dc ( c ) {
22+ return c === '+' ? '-' : ( c === '/' ? '_' : '' ) ;
23+ }
24+ function b64d ( s ) {
25+ return atob ( s . replace ( / [ + \/ ] / g, b64dc ) ) ;
26+ }
27+ function encodeHref ( href ) {
28+ var u = URL . parse ( href ) ;
29+ if ( u ) {
30+ if ( u . protocol === 'https:' || u . protocol === 'http:' ) {
31+ var b = b64e ( u . protocol + '//' + u . host ) ;
32+ if ( b !== base && opts . indexOf ( 'o' ) !== - 1 ) {
33+ return '/RW/static/not-found' ;
34+ }
35+ return '/RW/' + b + '/' + opts + u . pathname + u . search + u . hash ;
36+ }
37+ } else if ( href . charAt ( 0 ) === '/' ) {
38+ if ( href . charAt ( 1 ) === '/' ) {
39+ var d = b64d ( base ) ;
40+ var i = d . indexOf ( ':' ) ;
41+ if ( i ) {
42+ return encodeHref ( d . substring ( 0 , i + 1 ) + href )
43+ }
44+ } else if ( href . lastIndexOf ( '/RW/' , 0 ) !== 0 ) {
45+ return '/RW/' + base + '/' + opts + href ;
46+ }
47+ }
48+ return null ;
49+ }
50+ function proxyHref ( href , fallback ) {
51+ var e = encodeHref ( '' + href ) ;
52+ if ( e ) {
53+ console . log ( 'replacing fetch ' + href + ' => ' + e ) ;
54+ return e ;
55+ }
56+ return fallback ;
57+ }
58+ function blockTagName ( tagName ) {
59+ if ( tagName ) {
60+ var n = tagName . toUpperCase ( ) ;
61+ if ( n === 'SCRIPT' || n === 'IFRAME' ) {
62+ console . log ( 'blocking element creation ' + n ) ;
63+ return 'rw-' + tagName ;
64+ }
65+ }
66+ return tagName ;
67+ }
68+ function transformHtml ( content ) {
69+ if ( content ) {
70+ var c = content . toUpperCase ( ) ;
71+ // TODO rewrite HTML when possible
72+ if ( c . indexOf ( 'HREF=' ) > 0 || c . indexOf ( 'SRC=' ) > 0 ) {
73+ console . log ( 'blocking HTML ' + content ) ;
74+ return '' ;
75+ }
76+ }
77+ return content ;
78+ }
79+
80+ /*
81+ * Wrapping fetch and XMLHttpRequest to proxy URLs
82+ */
83+ if ( global . fetch ) {
84+ var rawFetch = global . fetch ;
85+ global . fetch = function ( resource , options ) {
86+ return rawFetch ( proxyHref ( resource instanceof Request ? resource . url : resource , resource ) , options ) ;
87+ } ;
88+ }
89+ if ( global . XMLHttpRequest ) {
90+ var rawXHROpen = global . XMLHttpRequest . prototype . open ;
91+ global . XMLHttpRequest . prototype . open = function ( method , url , async , user , password ) {
92+ return rawXHROpen . call ( this , method , proxyHref ( url , url ) , async , user , password ) ;
93+ } ;
94+ }
95+
96+ /*
97+ * Wrapping various element creation
98+ */
99+ if ( global . Document ) {
100+ var rawCreateElement = global . Document . prototype . createElement ;
101+ global . Document . prototype . createElement = function ( tagName , options ) {
102+ return rawCreateElement . call ( this , blockTagName ( tagName ) , options ) ;
103+ } ;
104+ var rawCreateElementNS = global . Document . prototype . createElementNS ;
105+ global . Document . prototype . createElementNS = function ( nsUri , tagName , options ) {
106+ return rawCreateElementNS . call ( this , nsUri , blockTagName ( tagName ) , options ) ;
107+ } ;
108+ var rawInsertAdjacentHTML = global . Document . prototype . insertAdjacentHTML ;
109+ global . Document . prototype . insertAdjacentHTML = function ( position , input ) {
110+ return rawInsertAdjacentHTML . call ( this , position , transformHtml ( input ) ) ;
111+ } ;
112+ var rawWrite = global . Document . prototype . write ;
113+ global . Document . prototype . write = function ( ) {
114+ var args = [ ] ;
115+ for ( var i = 0 ; i < arguments . length ; i ++ ) {
116+ args . push ( transformHtml ( arguments [ i ] ) ) ;
117+ }
118+ rawWrite . apply ( this , args ) ;
119+ } ;
120+ }
121+ if ( global . Element && Object . getOwnPropertyDescriptor ) {
122+ var rawInner = Object . getOwnPropertyDescriptor ( Element . prototype , 'innerHTML' ) ;
123+ if ( rawInner && rawInner . set ) {
124+ Object . defineProperty ( Element . prototype , 'innerHTML' , {
125+ configurable : true ,
126+ enumerable : rawInner . enumerable ,
127+ get : function ( ) {
128+ return rawInner . get . call ( this ) ;
129+ } ,
130+ set : function ( html ) {
131+ rawInner . set . call ( this , transformHtml ( html ) ) ;
132+ }
133+ } ) ;
134+ }
135+ }
136+
137+ if ( global . MutationObserver ) {
138+ function getUrlAttributeName ( node ) {
139+ var n = node . nodeName . toUpperCase ( ) ;
140+ // the original resource may have already been loaded
141+ if ( n === 'SCRIPT' || n === 'IFRAME' || n === 'SOURCE' || n === 'TRACK' || n === 'EMBED' ) {
142+ return 'src' ;
143+ } else if ( n === 'IMG' ) {
144+ var srcset = node . getAttribute ( 'srcset' ) ;
145+ if ( srcset ) {
146+ node . removeAttribute ( 'srcset' ) ;
147+ if ( ! node . hasAttribute ( 'src' ) ) {
148+ var i = srcset . indexOf ( ' ' ) ;
149+ if ( i < 0 ) {
150+ i = srcset . indexOf ( ',' ) ;
151+ }
152+ if ( i > 0 ) {
153+ node . setAttribute ( 'src' , srcset . substring ( 0 , i ) ) ;
154+ }
155+ }
156+ }
157+ return 'src' ;
158+ } else if ( n === 'A' || n === 'LINK' || n === 'AREA' || n === 'BASE' ) {
159+ return 'href' ;
160+ } else if ( n === 'FORM' ) {
161+ return 'action' ;
162+ }
163+ return null ;
164+ }
165+ function processAttribute ( node , name ) {
166+ var value = node . getAttribute ( name ) ;
167+ if ( value ) {
168+ var v = encodeHref ( value ) ;
169+ if ( v ) {
170+ console . log ( 'replacing ' + node . nodeName + '.' + name + ': ' + value + ' => ' + v ) ;
171+ node . setAttribute ( name , v ) ;
172+ }
173+ }
174+ }
175+ function rewriteElement ( node ) {
176+ if ( node . nodeName . toUpperCase ( ) . lastIndexOf ( 'RW-' , 0 ) === 0 && rawCreateElement ) {
177+ console . log ( 'blocked element detected ' + node . nodeName ) ;
178+ var e = rawCreateElement . call ( global . document , node . nodeName . substring ( 3 ) ) ;
179+ if ( node . attributes ) {
180+ for ( var ai = 0 ; ai < node . attributes . length ; ai ++ ) {
181+ var attr = node . attributes [ ai ] ;
182+ var value = attr . value ;
183+ var t = attr . name . toLowerCase ( ) ;
184+ if ( t === 'href' || t === 'src' ) {
185+ var v = encodeHref ( value ) ;
186+ if ( v ) {
187+ console . log ( 'replacing ' + node . nodeName + '.' + attr . name + ': ' + value + ' => ' + v ) ;
188+ value = v ;
189+ }
190+ }
191+ e . setAttribute ( attr . name , value ) ;
192+ }
193+ }
194+ while ( node . firstChild ) {
195+ e . appendChild ( node . firstChild ) ;
196+ }
197+ if ( node . parentNode ) {
198+ node . parentNode . replaceChild ( e , node ) ;
199+ return true ;
200+ }
201+ }
202+ return false ;
203+ }
204+ function processElement ( node ) {
205+ if ( rewriteElement ( node ) ) {
206+ return ;
207+ }
208+ var t = getUrlAttributeName ( node ) ;
209+ if ( t ) {
210+ processAttribute ( node , t ) ;
211+ }
212+ for ( var i = 0 ; i < node . childElementCount ; i ++ ) {
213+ processElement ( node . children [ i ] ) ;
214+ }
215+ }
216+ function observerCb ( mutationList , observer ) {
217+ for ( var i = 0 ; i < mutationList . length ; i ++ ) {
218+ var mutation = mutationList [ i ] ;
219+ if ( mutation . type === 'childList' ) {
220+ for ( var j = 0 ; j < mutation . addedNodes . length ; j ++ ) {
221+ var addedNode = mutation . addedNodes [ j ] ;
222+ if ( addedNode . nodeType === 1 ) {
223+ processElement ( addedNode ) ;
224+ }
225+ }
226+ } else if ( mutation . type === 'attributes' ) {
227+ var t = mutation . attributeName . toLowerCase ( ) ;
228+ if ( t === 'href' || t === 'src' ) {
229+ processAttribute ( mutation . target , t ) ;
230+ }
231+ }
232+ }
233+ }
234+ console . log ( 'connecting observer' ) ;
235+ var observer = new MutationObserver ( observerCb ) ;
236+ observer . observe ( document . getRootNode ( ) , { attributes : true , childList : true , subtree : true } ) ;
237+ document . addEventListener ( 'DOMContentLoaded' , function ( ) {
238+ setTimeout ( function ( ) {
239+ console . log ( 'disconnecting observer' ) ;
240+ let mutationList = observer . takeRecords ( ) ;
241+ observer . disconnect ( ) ;
242+ if ( mutationList . length > 0 ) {
243+ observerCb ( mutationList ) ;
244+ }
245+ } , 500 ) ;
246+ } ) ;
247+ }
248+ } ) ( ) ;
0 commit comments