@@ -7,8 +7,13 @@ if (!window.CFPredictorInjected) {
77 const tbody = document . querySelector ( "tbody" ) ;
88 if ( ! tbody ) {
99 setTimeout ( setEventListener , 100 ) ;
10+ return ;
1011 }
1112 const trs = tbody . querySelectorAll ( "tr" ) ;
13+ if ( ! trs ) {
14+ setTimeout ( setEventListener , 100 ) ;
15+ return ;
16+ }
1217 if ( trs . length <= 1 ) {
1318 // listen only if there is more than one row
1419 isListenerActive = false ;
@@ -19,33 +24,54 @@ if (!window.CFPredictorInjected) {
1924 isListenerActive = false ;
2025 return ;
2126 }
27+ if ( isListenerActive ) return ;
2228 isListenerActive = true ;
23- tds [ 1 ] . addEventListener ( "DOMCharacterDataModified" , ( ) => {
29+ tds [ 1 ] . addEventListener ( "DOMCharacterDataModified" , async ( ) => {
2430 window . clearTimeout ( predictionsTimer ) ;
25- predictionsTimer = setTimeout ( fetchPredictions , 1000 ) ;
31+ predictionsTimer = setTimeout ( fetchPredictions , 500 ) ;
2632 } ) ;
2733 } catch ( err ) {
2834 console . error ( err ) ;
2935 }
3036 } ;
3137
38+ const isContestRankingPage = ( url ) => {
39+ return / ^ h t t p s : \/ \/ l e e t c o d e .c o m \/ c o n t e s t \/ .* \/ r a n k i n g / . test ( url ) ;
40+ } ;
41+
3242 let rowsChanged = new Map ( ) ;
33- let colInserted = false ;
43+ let deltaTHInserted = false ;
3444
3545 const fetchPredictions = async ( ) => {
3646 const thead = document . querySelector ( "thead" ) ;
3747 if ( ! thead ) {
38- predictionsTimer = setTimeout ( fetchPredictions , 100 ) ;
48+ predictionsTimer = setTimeout ( fetchPredictions , 500 ) ;
3949 return ;
4050 }
51+
4152 const tbody = document . querySelector ( "tbody" ) ;
53+ if ( ! tbody ) {
54+ predictionsTimer = setTimeout ( fetchPredictions , 500 ) ;
55+ return ;
56+ }
57+
4258 const rows = tbody . querySelectorAll ( "tr" ) ;
43- const contestId = document
44- . querySelector ( ".ranking-title-wrapper" )
45- . querySelector ( "span" )
46- . querySelector ( "a" )
47- . innerHTML . toLowerCase ( )
48- . replace ( / \s / g, "-" ) ;
59+ if ( ! rows ) {
60+ predictionsTimer = setTimeout ( fetchPredictions , 500 ) ;
61+ return ;
62+ }
63+ let contestId ;
64+ try {
65+ contestId = document
66+ . querySelector ( ".ranking-title-wrapper" )
67+ . querySelector ( "span" )
68+ . querySelector ( "a" )
69+ . innerHTML . toLowerCase ( )
70+ . replace ( / \s / g, "-" ) ;
71+ } catch {
72+ predictionsTimer = setTimeout ( fetchPredictions , 500 ) ;
73+ return ;
74+ }
4975 const handlesMap = new Map ( ) ;
5076
5177 const handles = [ ...rows ] . map ( ( row , index ) => {
@@ -70,90 +96,114 @@ if (!window.CFPredictorInjected) {
7096 return handle ;
7197 }
7298 } catch ( err ) {
73- console . error ( err ) ;
99+ console . debug ( err ) ;
74100 }
75101 } ) ;
76102
77103 chrome . runtime . sendMessage (
78104 {
79105 message : "get_predictions" ,
80- data : { contestId, handles } ,
106+ data : {
107+ contestId,
108+ handles,
109+ } ,
81110 } ,
82111 ( response ) => {
83- if ( response . status === "OK" ) {
84- if ( ! colInserted ) {
85- const th = document . createElement ( "th" ) ;
86- th . innerText = "Δ" ;
87- thead . querySelector ( "tr" ) . appendChild ( th ) ;
88- colInserted = true ;
89- }
90- const rowsUpdated = new Map ( ) ;
91- for ( item of response . items ) {
92- try {
93- const id = (
94- item . data_region +
95- "/" +
96- item . _id
97- ) . toLowerCase ( ) ;
98- if ( handlesMap . has ( id ) ) {
99- const rowIndex = handlesMap . get ( id ) ;
100- const row = rows [ rowIndex ] ;
101- let td ;
102- if ( rowsChanged . has ( rowIndex ) ) {
103- td = row . lastChild ;
104- } else {
105- td = document . createElement ( "td" ) ;
106- }
107- if ( item . delta == null ) {
108- td . innerText = "?" ;
109- td . style . color = "gray" ;
110- } else {
111- const delta =
112- Math . round ( item . delta * 100 ) / 100 ;
113- td . innerText =
114- delta > 0 ? "+" + delta : delta ;
115- if ( delta > 0 ) {
116- td . style . color = "green" ;
112+ if ( ! response ) {
113+ return ;
114+ }
115+ try {
116+ if ( response . status === "OK" ) {
117+ if ( ! deltaTHInserted && response . meta . total_count ) {
118+ const th = document . createElement ( "th" ) ;
119+ th . innerText = "Δ" ;
120+ thead . querySelector ( "tr" ) . appendChild ( th ) ;
121+ deltaTHInserted = true ;
122+ }
123+ const rowsUpdated = new Map ( ) ;
124+ for ( item of response . items ) {
125+ try {
126+ const id = (
127+ item . data_region +
128+ "/" +
129+ item . _id
130+ ) . toLowerCase ( ) ;
131+ if ( handlesMap . has ( id ) ) {
132+ const rowIndex = handlesMap . get ( id ) ;
133+ const row = rows [ rowIndex ] ;
134+ let td ;
135+ if ( rowsChanged . has ( rowIndex ) ) {
136+ td = row . lastChild ;
117137 } else {
138+ td = document . createElement ( "td" ) ;
139+ }
140+ if ( item . delta == null ) {
141+ td . innerText = "?" ;
118142 td . style . color = "gray" ;
143+ } else {
144+ const delta =
145+ Math . round ( item . delta * 100 ) / 100 ;
146+ td . innerText =
147+ delta > 0 ? "+" + delta : delta ;
148+ if ( delta > 0 ) {
149+ td . style . color = "green" ;
150+ } else {
151+ td . style . color = "gray" ;
152+ }
153+ // td.style.fontWeight = "bold";
119154 }
120- // td.style.fontWeight = "bold";
121- }
122- if ( ! rowsChanged . has ( rowIndex ) ) {
123- row . appendChild ( td ) ;
155+ if ( ! rowsChanged . has ( rowIndex ) ) {
156+ row . appendChild ( td ) ;
157+ }
158+ rowsUpdated . set ( rowIndex , true ) ;
124159 }
125- rowsUpdated . set ( rowIndex , true ) ;
160+ } catch ( err ) {
161+ console . warn ( err ) ;
126162 }
127- } catch ( err ) {
128- console . error ( err ) ;
129163 }
130- }
131- for ( rowIndex of rowsChanged . keys ( ) ) {
132- if ( ! rowsUpdated . has ( rowIndex ) ) {
133- try {
134- const row = rows [ rowIndex ] ;
135- row . lastChild . innerText = "" ;
136- } catch ( err ) {
137- console . error ( err ) ;
164+ for ( rowIndex of rowsChanged . keys ( ) ) {
165+ if (
166+ ! rowsUpdated . has ( rowIndex ) &&
167+ rowIndex < rows . length
168+ ) {
169+ try {
170+ const row = rows [ rowIndex ] ;
171+ row . lastChild . innerText = "" ;
172+ } catch { }
173+ }
174+ if ( rowIndex >= rows . length ) {
175+ rowsChanged . delete ( rowIndex ) ;
138176 }
139177 }
178+ for ( rowIndex of rowsUpdated . keys ( ) ) {
179+ rowsChanged . set ( rowIndex , true ) ;
180+ }
140181 }
141- for ( rowIndex of rowsUpdated . keys ( ) ) {
142- rowsChanged . set ( rowIndex , true ) ;
143- }
182+ } catch ( err ) {
183+ console . warn ( err ) ;
144184 }
145185 }
146186 ) ;
147187 } ;
148- fetchPredictions ( ) ;
149- setEventListener ( ) ;
150188
151- // event listener for "click" event on page-btn class items
152- [ ...document . querySelectorAll ( ".page-btn" ) ] . forEach ( ( item ) => {
153- item . addEventListener ( "click" , ( e ) => {
154- if ( ! isListenerActive ) {
189+ // listen to url changes
190+ chrome . runtime . onMessage . addListener ( function (
191+ request ,
192+ sender ,
193+ sendResponse
194+ ) {
195+ if ( request . message === "url_updated" ) {
196+ if ( ! isListenerActive && isContestRankingPage ( request . url ) ) {
197+ window . clearTimeout ( predictionsTimer ) ;
198+ predictionsTimer = setTimeout ( fetchPredictions , 500 ) ;
199+ }
200+ if ( ! isContestRankingPage ( request . url ) ) {
201+ isListenerActive = false ;
202+ rowsChanged . clear ( ) ;
203+ deltaTHInserted = false ;
204+ } else {
155205 setEventListener ( ) ;
156206 }
157- } ) ;
207+ }
158208 } ) ;
159209}
0 commit comments