SHOW:
|
|
- or go back to the newest paste.
1 | // ==UserScript== | |
2 | // @name TF2 Profile Script | |
3 | // @namespace tfprofile | |
4 | // @version 1.1.3 | |
5 | // @description Mouse over profile links (steamcommunity/etf2l/wireplay/teamfortress.tv) to get links their profiles on otherwebsites | |
6 | // @downloadURL https://github.com/CasualX/UserScripts/raw/master/tfprofile.user.js | |
7 | // @updateURL https://github.com/CasualX/UserScripts/raw/master/tfprofile.user.js | |
8 | // @include http://* | |
9 | // @include https://* | |
10 | // @grant GM_xmlhttpRequest | |
11 | // @run-at document-end | |
12 | // ==/UserScript== | |
13 | ||
14 | // Base conversion of arbitrary precision integers | |
15 | // Minified from http://danvk.org/hex2dec.html | |
16 | function convertBase(e,t,n){function r(e,t,n){var r=[];var i=Math.max(e.length,t.length);var s=0;var o=0;while(o<i||s){var u=o<e.length?e[o]:0;var a=o<t.length?t[o]:0;var f=s+u+a;r.push(f%n);s=Math.floor(f/n);o++}return r}function i(e,t,n){if(e<0)return null;if(e==0)return[];var i=[];var s=t;while(true){if(e&1){i=r(i,s,n)}e=e>>1;if(e===0)break;s=r(s,s,n)}return i}function s(e,t){var n=e.split("");var r=[];for(var i=n.length-1;i>=0;i--){var s=parseInt(n[i],t);if(isNaN(s))return null;r.push(s)}return r}var o=s(e,t);if(o===null)return null;var u=[];var a=[1];for(var f=0;f<o.length;f++){if(o[f]){u=r(u,i(o[f],a,n),n)}a=i(t,a,n)}var l="";for(var f=u.length-1;f>=0;f--){l+=u[f].toString(n)}return l} | |
17 | // Googled this for Chrome compat, minified | |
18 | function addStyle(e){var t=document.createElement("style");t.type="text/css";t.appendChild(document.createTextNode(e));document.getElementsByTagName("head")[0].appendChild(t)} | |
19 | ||
20 | //---------------------------------------------------------------- | |
21 | // Steam ID container | |
22 | function CSteamID() | |
23 | { | |
24 | this.unAccountID = 0; | |
25 | this.unAccountInstance = 0; | |
26 | this.EAccountType = CSteamID.EAccountType.k_EAccountTypeInvalid; | |
27 | this.EUniverse = CSteamID.EUniverse.k_EUniverseInvalid; | |
28 | } | |
29 | CSteamID.prototype.setID = function( sid, type, uni ) | |
30 | { | |
31 | this.unAccountID = sid; | |
32 | this.unAccountInstance = 1; | |
33 | this.EAccountType = type || CSteamID.EAccountType.k_EAccountTypeIndividual; | |
34 | this.EUniverse = uni || CSteamID.EUniverse.k_EUniversePublic; | |
35 | } | |
36 | CSteamID.prototype.setID64 = function( id ) | |
37 | { | |
38 | var hex = "0000000000000000" + convertBase( id, 10, 16 ); | |
39 | ||
40 | // Break down the (hexadecimal) community id | |
41 | this.unAccountID = parseInt(hex.substr(-8,8),16); | |
42 | this.unAccountInstance = parseInt(hex.substr(-13,5),16); | |
43 | this.EAccountType = parseInt(hex.substr(-14,1),16); | |
44 | this.EUniverse = parseInt(hex.substr(-16,2),16); | |
45 | } | |
46 | CSteamID.prototype.render = function() | |
47 | { | |
48 | // Old style U:x:y format | |
49 | return "U:" + (this.unAccountID%2) + ":" + (this.unAccountID>>1); | |
50 | } | |
51 | CSteamID.prototype.toString = function() | |
52 | { | |
53 | function s( num, len ) | |
54 | { | |
55 | var str = num.toString(16); | |
56 | return "0000000000000000".substr(0,len-str.length)+str; | |
57 | } | |
58 | var hex = s(this.EUniverse,2) + s(this.EAccountType,1) + s(this.unAccountInstance,5) + s(this.unAccountID,8); | |
59 | return convertBase( hex, 16, 10 ); | |
60 | } | |
61 | CSteamID.EUniverse = { k_EUniverseInvalid:0, k_EUniversePublic:1, k_EUniverseBeta:2, k_EUniverseInternal:3, k_EUniverseDev:4, k_EUniverseRC:5, k_EUniverseMax:6 }; | |
62 | CSteamID.EAccountType = { k_EAccountTypeInvalid:0, k_EAccountTypeIndividual:1, k_EAccountTypeMultiseat:2, k_EAccountTypeGameServer:3, k_EAccountTypeAnonGameServer:4, k_EAccountTypePending:5, k_EAccountTypeContentServer:6, k_EAccountTypeClan:7, k_EAccountTypeChat:8, k_EAccountTypeP2PSuperSeeder:9, k_EAccountTypeMax:10 }; | |
63 | CSteamID.parse = function( str ) | |
64 | { | |
65 | var re, sid = null; | |
66 | - | // SteamID old format, 2nd part is how etf2l formats it. |
66 | + | // Old SteamID with and without prefix in any of three exciting flavours |
67 | - | if ( re = /^(?:U)?0\:([01])\:(\d+)$/.exec( str ) ) |
67 | + | if ( re = /^(?:STEAM_|Steam_|steam_)?0\:([01])\:(\d+)$/.exec( str ) ) |
68 | { | |
69 | sid = new CSteamID(); | |
70 | sid.setID( parseInt(re[2])*2 + parseInt(re[1]) ); | |
71 | } | |
72 | - | // Looks like a standard 64bit steamid |
72 | + | // New SteamID with and without square brackets |
73 | else if ( re = /^\[?U:1:(\d+)\]?$/.exec( str ) ) | |
74 | { | |
75 | sid = new CSteamID(); | |
76 | sid.setID( parseInt(re[1]) ); | |
77 | } | |
78 | // SteamID64 | |
79 | else if ( re = /^\d+$/.exec( str ) ) | |
80 | { | |
81 | sid = new CSteamID(); | |
82 | sid.setID64( str ); | |
83 | } | |
84 | return sid; | |
85 | } | |
86 | //---------------------------------------------------------------- | |
87 | ||
88 | // Find profiles with search engine because they don't have an API... | |
89 | function searchEngine( site, title, content, fn ) | |
90 | { | |
91 | function search( engine ) | |
92 | { | |
93 | GM_xmlhttpRequest( { | |
94 | method: "GET", | |
95 | url: engine.qurl + encodeURIComponent( 'site:'+site+' title:"'+title+'" "'+content+'"' ), | |
96 | onload: function( resp ) { match( resp.responseText, engine ); }, | |
97 | onerror: function( resp ) { match( "", engine ); } | |
98 | } ); | |
99 | } | |
100 | function match( text, engine ) | |
101 | { | |
102 | var r; | |
103 | while ( r = engine.regex.exec( text ) ) | |
104 | { | |
105 | // Found a valid result | |
106 | if ( r[1].indexOf(site)>=0 ) | |
107 | return fn( r ); | |
108 | } | |
109 | // Not found, try another search engine | |
110 | if ( engine.next ) search( engine.next ); | |
111 | // Tried all engines, nothing found | |
112 | else fn( false ); | |
113 | } | |
114 | var engines = { | |
115 | qurl: "https://ixquick.com/do/search?q=", | |
116 | regex: /<a href='([^']*)' id='title_\d'/, | |
117 | next: { | |
118 | qurl: "https://startpage.com/do/search?q=", | |
119 | regex: /<a href='([^']*)' id='title_\d'/, | |
120 | } | |
121 | }; | |
122 | search( engines ); | |
123 | } | |
124 | ||
125 | //---------------------------------------------------------------- | |
126 | // Websites supported | |
127 | //---------------------------------------------------------------- | |
128 | // Each website may have these 3 functions: | |
129 | // match: Given an url return a non false value if you can handle this profile link | |
130 | // source: Source the player's steamid from this profile url, directly called after match with its returned value (eg, regex result). Callback player.initialize with the steamid on success. | |
131 | // query: Find out given the steamid if this player has a profile on this website. Callback player.addLink with the url and description on success. | |
132 | ||
133 | function siteSetLink( p, url, desc, html ) | |
134 | { | |
135 | var a = document.createElement('a'); | |
136 | a.href = url; | |
137 | a.target = "_blank"; | |
138 | if ( html ) a.innerHTML = html; | |
139 | var text = document.createTextNode(desc); | |
140 | if ( a.firstChild ) a.insertBefore( text, a.firstChild ); | |
141 | else a.appendChild( text ); | |
142 | p.innerHTML = ''; | |
143 | p.appendChild( a ); | |
144 | p.className = 'TFProfile_Done'; | |
145 | } | |
146 | function siteSetMissing( p ) | |
147 | { | |
148 | p.className = 'TFProfile_Missing'; | |
149 | // Hide the parent article if it only has missing links... | |
150 | if ( p.parentNode.children.length==p.parentNode.querySelectorAll(".TFProfile_Missing").length ) | |
151 | p.parentNode.parentNode.removeChild( p.parentNode ); | |
152 | } | |
153 | ||
154 | var sites = { | |
155 | // Steam community support | |
156 | "steamcommunity.com": { | |
157 | group: "steam", | |
158 | match: function( url ) { return /^https?\:\/\/steamcommunity\.com\/(?:profiles|id)\/[^\/]*\/?$/.exec(url); }, | |
159 | source: function( re, player ) | |
160 | { | |
161 | GM_xmlhttpRequest( { | |
162 | method: "GET", | |
163 | url: re[0] + "?xml=1", | |
164 | onload: function( resp ) | |
165 | { | |
166 | var parser = new DOMParser(); | |
167 | var doc = parser.parseFromString( resp.responseText, "text/xml" ).documentElement; | |
168 | var str = doc.querySelector( "steamID64" ).textContent; | |
169 | player.initialize( CSteamID.parse( str ) ); | |
170 | }, | |
171 | onerror: function( resp ) | |
172 | { | |
173 | player.error( resp.responseText ); | |
174 | } | |
175 | } ); | |
176 | }, | |
177 | query: function( sid, player, el ) | |
178 | { | |
179 | el.textContent = 'steamcommunity.com'; | |
180 | ||
181 | var commid = sid.toString(); | |
182 | GM_xmlhttpRequest( { | |
183 | method: "GET", | |
184 | url: "https://steamcommunity.com/profiles/"+commid+"?xml=1", | |
185 | onload: function( resp ) | |
186 | { | |
187 | var parser = new DOMParser(); | |
188 | doc = parser.parseFromString( resp.responseText, "text/xml" ).documentElement; | |
189 | ||
190 | // Profiles that haven't been set up do not have a name, in this case, steamID will be empty | |
191 | var desc = "Steam Community", name = doc.querySelector("steamID"), online = doc.querySelector("onlineState"); | |
192 | if ( name && name.textContent ) desc += " ("+name.textContent+")"; | |
193 | siteSetLink( el, "https://steamcommunity.com/profiles/"+commid, desc, online?'<span style="padding-left:5px;font-size:xx-small;">'+online.textContent+'</span>':undefined ); | |
194 | ||
195 | var gameIP = doc.querySelector("inGameServerIP"); | |
196 | var gameName = doc.querySelector("inGameInfo>gameName") | |
197 | var gameJoin = doc.querySelector("inGameInfo>gameJoinLink"); | |
198 | if ( gameIP && gameIP.textContent && gameName && gameName.textContent && gameJoin && gameJoin.textContent ) | |
199 | { | |
200 | var a = document.createElement('a'); | |
201 | a.href = gameJoin.textContent; | |
202 | a.innerHTML = "In-Game: "+gameName.textContent; | |
203 | el.appendChild( a ); | |
204 | } | |
205 | } | |
206 | } ); | |
207 | } | |
208 | }, | |
209 | // ETF2L Support | |
210 | "etf2l.org": { | |
211 | group: "comptf2", | |
212 | match: function( url ) { return /^http\:\/\/etf2l.org\/forum\/user\/(\d+)\/?$/.exec(url); }, | |
213 | source: function( re, player ) | |
214 | { | |
215 | GM_xmlhttpRequest( { | |
216 | method: "GET", | |
217 | url: "http://etf2l.org/feed/player/?id=" + re[1], | |
218 | onload: function( resp ) | |
219 | { | |
220 | var parser = new DOMParser(); | |
221 | var doc = parser.parseFromString( resp.responseText, "text/xml" ).documentElement; | |
222 | var str = doc.querySelector( "player" ); | |
223 | player.initialize( CSteamID.parse( str ? str.getAttribute("steamid") : "" ) ); | |
224 | }, | |
225 | onerror: function( resp ) | |
226 | { | |
227 | player.error( resp.responseText ); | |
228 | } | |
229 | } ); | |
230 | }, | |
231 | query: function( sid, player, el ) | |
232 | { | |
233 | el.textContent = 'etf2l.org'; | |
234 | ||
235 | GM_xmlhttpRequest( { | |
236 | method: "GET", | |
237 | url: "http://etf2l.org/feed/player/?steamid=" + sid.render(), | |
238 | onload: function( resp ) | |
239 | { | |
240 | try { | |
241 | var parser = new DOMParser(); | |
242 | var doc = parser.parseFromString( resp.responseText, "text/xml" ).documentElement; | |
243 | var id = doc.querySelector("player").getAttribute("id"); | |
244 | var name = doc.querySelector("displayname").textContent; | |
245 | siteSetLink( el, "http://etf2l.org/forum/user/"+id+"/", "ETF2L Profile ("+name+")" ); | |
246 | } catch(e) { | |
247 | siteSetMissing( el ); | |
248 | } | |
249 | } | |
250 | } ); | |
251 | } | |
252 | }, | |
253 | // Wireplay TF2 League | |
254 | "tf2.wireplay.co.uk": { | |
255 | group: "comptf2", | |
256 | match: function( url ) { return /^https?\:\/\/tf2\.wireplay\.co\.uk\/.*?index\.php\?pg=profile\&action=viewprofile\&aid=(\d+)/.exec(url); }, | |
257 | source: function( re, player ) | |
258 | { | |
259 | GM_xmlhttpRequest( { | |
260 | method: "GET", | |
261 | url: re[0], | |
262 | onload: function( resp ) | |
263 | { | |
264 | var r = /<td align=left>(U\:[01]\:\d+)<\/td>/.exec(resp.responseText); | |
265 | player.initialize( CSteamID.parse( r?r[1]:"" ) ); | |
266 | }, | |
267 | onerror: function( resp ) | |
268 | { | |
269 | player.error( resp.responseText ); | |
270 | } | |
271 | } ); | |
272 | }, | |
273 | query: function( sid, player, el ) | |
274 | { | |
275 | el.textContent = 'tf2.wireplay.co.uk'; | |
276 | ||
277 | GM_xmlhttpRequest( { | |
278 | method: "POST", | |
279 | url: "http://tf2.wireplay.co.uk/index.php?pg=search", | |
280 | data: "clantag=false&clanname=false&playername=false&steamid=true&searchterm=" + encodeURIComponent(sid.render()), | |
281 | headers: { "Content-Type": "application/x-www-form-urlencoded" }, | |
282 | onload: function( resp ) | |
283 | { | |
284 | try { | |
285 | var r = /<td><a href="(index.php\?pg=profile\&action=viewprofile\&aid=\d+)">([^<]*)<\/a><\/td>/.exec(resp.responseText); | |
286 | siteSetLink( el, "http://tf2.wireplay.co.uk/"+r[1], "Wireplay Profile ("+r[2]+")" ); | |
287 | } catch(e) { | |
288 | siteSetMissing( el ); | |
289 | } | |
290 | } | |
291 | } ); | |
292 | } | |
293 | }, | |
294 | // UGC League | |
295 | "ugcleague.com": { | |
296 | group: "comptf2", | |
297 | match: function( url ) { return /^https?\:\/\/www.ugcleague.com\/players_page\.cfm\?player_id=(\d+)$/.exec(url); }, | |
298 | source: function( re, player ) { player.initialize( CSteamID.parse( re[1] ) ); }, | |
299 | query: function( sid, player, el ) | |
300 | { | |
301 | el.textContent = 'ugcleague.com'; | |
302 | ||
303 | // Assumption: Just check if the player's steamid is on the page, that means he's played in a team. | |
304 | // FIXME! Use their player search page instead? http://www.ugcleague.com/playersearch.cfm | |
305 | GM_xmlhttpRequest( { | |
306 | method: "GET", | |
307 | url: "http://www.ugcleague.com/players_page.cfm?player_id="+sid.toString(), | |
308 | onload: function( resp ) | |
309 | { | |
310 | if ( resp.responseText.indexOf( "<td>"+sid.render().substr(6)+"</td>" )>=0 ) | |
311 | siteSetLink( el, "http://www.ugcleague.com/players_page.cfm?player_id="+sid.toString(), "UGC League Profile" ); | |
312 | else | |
313 | siteSetMissing( el ); | |
314 | } | |
315 | } ); | |
316 | } | |
317 | }, | |
318 | // TF2Lobby.com | |
319 | "tf2lobby.com": { | |
320 | group: "lobby", | |
321 | match: function( url ) { return /^http\:\/\/(?:www.)?tf2lobby\.com\/profile\?(f?id)=(\d+)$/.exec(url); }, | |
322 | source: function( re, player ) | |
323 | { | |
324 | if ( re[1]==='fid' ) | |
325 | { | |
326 | player.initialize( CSteamID.parse( re[2] ) ); | |
327 | return; | |
328 | } | |
329 | ||
330 | GM_xmlhttpRequest( { | |
331 | method: "GET", | |
332 | url: "http://www.tf2lobby.com/profile?id="+re[2], | |
333 | onload: function( resp ) | |
334 | { | |
335 | var dom = new DOMParser(), doc, el, sid; | |
336 | if ( ( doc = parser.parseFromString( resp.responseText, "text/xml" ).documentElement ) && | |
337 | ( el = doc.querySelector("#otherSites>ul>li>a") ) ) | |
338 | { | |
339 | sid = CSteamID.parse( /\d+/.exec( el.href )[0] ); | |
340 | } | |
341 | player.initialize( sid ); | |
342 | }, | |
343 | onerror: function( resp ) | |
344 | { | |
345 | player.error( resp.responseText ); | |
346 | } | |
347 | } ); | |
348 | }, | |
349 | query: function( sid, player, el ) | |
350 | { | |
351 | el.textContent = 'tf2lobby.com'; | |
352 | ||
353 | GM_xmlhttpRequest( { | |
354 | method: "GET", | |
355 | url: "http://www.tf2lobby.com/profile?fid="+sid.toString(), | |
356 | onload: function( resp ) | |
357 | { | |
358 | try { | |
359 | var dom = new DOMParser(); | |
360 | var doc = dom.parseFromString( resp.responseText, "text/html" ).documentElement; | |
361 | var name = ( doc.querySelector("#otherSites") && doc.querySelector("#player p") ).textContent; | |
362 | siteSetLink( el, "http://www.tf2lobby.com/profile?fid="+sid.toString(), "TF2Lobby Profile ("+name+")" ); | |
363 | } catch(e) { | |
364 | siteSetMissing( el ); | |
365 | } | |
366 | } | |
367 | } ); | |
368 | } | |
369 | }, | |
370 | // TeamFortress.tv profiles (barely works...) | |
371 | "teamfortress.tv": { | |
372 | group: "forum", | |
373 | match: function( url ) { return /^https?\:\/\/teamfortress\.tv\/profile\/user\/[^\/]*\/?$/.exec(url); }, | |
374 | source: function( re, player ) | |
375 | { | |
376 | GM_xmlhttpRequest( { | |
377 | method: "GET", | |
378 | url: re[0], | |
379 | onload: function( resp ) | |
380 | { | |
381 | var r = /(U\:[01]\:\d+)/.exec( resp.responseText ); | |
382 | player.initialize( CSteamID.parse( r?r[1]:0 ) ); | |
383 | }, | |
384 | onerror: function( resp ) | |
385 | { | |
386 | player.error( resp.responseText ); | |
387 | } | |
388 | } ); | |
389 | }, | |
390 | query: function( sid, player, el ) | |
391 | { | |
392 | el.textContent = 'teamfortress.tv'; | |
393 | ||
394 | // Cannot query by steamid... | |
395 | searchEngine( "teamfortress.tv", "Profile", sid.render(), function(r) { | |
396 | try { | |
397 | var url = r[1]; | |
398 | var name = /profile\/user\/(.*?)\/?$/.exec(url)[1]; | |
399 | siteSetLink( el, url, "TeamFortress.tv ("+name+")" ); | |
400 | } catch(e) { | |
401 | siteSetMissing( el ); | |
402 | } | |
403 | } ); | |
404 | } | |
405 | }, | |
406 | // logs.tf | |
407 | "logs.tf": { | |
408 | group: "stats", | |
409 | match: function( url ) { return /^https?\:\/\/(?:www.)?logs\.tf\/profile\/(\d+)\/?$/.exec(url); }, | |
410 | source: function( re, player ) { player.initialize( CSteamID.parse( re[1] ) ); }, | |
411 | query: function( sid, player, el ) | |
412 | { | |
413 | el.textContent = 'logs.tf'; | |
414 | ||
415 | GM_xmlhttpRequest( { | |
416 | method: "GET", | |
417 | url: "http://logs.tf/profile/"+sid.toString(), | |
418 | onload: function( resp ) | |
419 | { | |
420 | if ( !(/<h5>None found\.<\/h5>/.test(resp.responseText)) ) | |
421 | siteSetLink( el, "http://logs.tf/profile/"+sid.toString(), "Logs.tf Profile" ); | |
422 | else | |
423 | siteSetMissing( el ); | |
424 | } | |
425 | } ); | |
426 | } | |
427 | }, | |
428 | // SizzlingStats.com (FIXME! Figure out their query api) | |
429 | "sizzlingstats.com": { | |
430 | group: "stats", | |
431 | match: function( url ) { return /^https?\:\/\/(?:www.)?sizzlingstats\.com\/player\/(\d+)\/?$/.exec(url); }, | |
432 | source: function( re, player ) { player.initialize( CSteamID.parse( re[1] ) ); }, | |
433 | query: function( sid, player ) { return false; } | |
434 | }, | |
435 | // TF2Logs.com (does anyone still use this?) | |
436 | "tf2logs.com": { | |
437 | group: "stats", | |
438 | match: function( url ) { return /^https?\:\/\/(?:www.)?tf2logs\.com\/players\/(\d+)\/?$/.exec(url); }, | |
439 | source: function( re, player ) { player.initialize( CSteamID.parse( re[1] ) ); }, | |
440 | query: function( sid, player, el ) | |
441 | { | |
442 | el.textContent = 'tf2logs.com'; | |
443 | ||
444 | GM_xmlhttpRequest( { | |
445 | method: "GET", | |
446 | url: "http://tf2logs.com/players/"+sid.toString(), | |
447 | onload: function( resp ) | |
448 | { | |
449 | var re = /<meta name="title" content="([^>]*?) - TF2Logs.com" \/>/.exec(resp.responseText); | |
450 | if ( re && re[1]!='Welcome' ) | |
451 | siteSetLink( el, "http://tf2logs.com/players/"+sid.toString(), "TF2Logs.com Profile" ); | |
452 | else | |
453 | siteSetMissing( el ); | |
454 | } | |
455 | } ); | |
456 | } | |
457 | }, | |
458 | }; | |
459 | ||
460 | //---------------------------------------------------------------- | |
461 | // A link resource | |
462 | //---------------------------------------------------------------- | |
463 | function linkPlayer( a ) | |
464 | { | |
465 | this.anchor = a; | |
466 | ||
467 | // Generate html | |
468 | var div = document.createElement('div'); | |
469 | this.div = div; | |
470 | div.classList.add( 'TFProfile' ); | |
471 | div.innerHTML = '<p>Pending...</p>'; | |
472 | } | |
473 | // Initialize from a steamid | |
474 | linkPlayer.prototype.initialize = function( sid ) | |
475 | { | |
476 | if ( sid ) | |
477 | { | |
478 | // Show steam id | |
479 | var span = this.div.querySelector("p"); | |
480 | span.innerHTML = ''; | |
481 | span.appendChild( document.createTextNode( sid.render() ) ); | |
482 | // Collect information about other websites | |
483 | for ( var it in sites ) | |
484 | { | |
485 | var site = sites[it]; | |
486 | ||
487 | // Find the group this belongs in | |
488 | var group = this.div.querySelector("article.TFProfile_"+site.group); | |
489 | if ( !group ) | |
490 | { | |
491 | group = document.createElement('article'); | |
492 | group.className = "TFProfile_"+site.group; | |
493 | this.div.appendChild( group ); | |
494 | } | |
495 | ||
496 | var p = document.createElement('p'); | |
497 | p.className = 'TFProfile_Pending'; | |
498 | ||
499 | if ( site.query( sid, this, p )!==false ) | |
500 | { | |
501 | group.appendChild( p ); | |
502 | } | |
503 | } | |
504 | } | |
505 | else | |
506 | { | |
507 | this.error( "Invalid SteamID!" ); | |
508 | } | |
509 | } | |
510 | // Error happened | |
511 | linkPlayer.prototype.error = function( desc ) | |
512 | { | |
513 | var p = this.div.querySelector("p"); | |
514 | p.innerHTML = ''; | |
515 | p.appendChild( document.createTextNode( 'Error! ' + desc ) ); | |
516 | } | |
517 | // Delay the query on mouse over | |
518 | linkPlayer.prototype.source = function( a, re, site ) | |
519 | { | |
520 | this.show( a, function() { | |
521 | if ( !this.sourced ) | |
522 | { | |
523 | this.sourced = true; | |
524 | site.source( re, this ); | |
525 | } | |
526 | } ); | |
527 | } | |
528 | // Show the UI, delay loading as needed | |
529 | linkPlayer.prototype.show = function( a, fn ) | |
530 | { | |
531 | var self = this; | |
532 | function hover() | |
533 | { | |
534 | // Begin sourcing | |
535 | fn.call( self ); | |
536 | // Show our overlay only | |
537 | Array.prototype.forEach.call( document.querySelectorAll(".TFProfile"), function(div) { div.style.display="none"; } ); | |
538 | clear(); | |
539 | // Compute position of the tooltip | |
540 | var r = a.getBoundingClientRect(); | |
541 | var bottom = r.bottom + ( document.documentElement.scrollTop || document.body.scrollTop ); | |
542 | var left = r.left + ( document.documentElement.scrollLeft || document.body.scrollLeft ); | |
543 | self.div.style.top = bottom + "px"; | |
544 | self.div.style.left = left + "px"; | |
545 | // Show it | |
546 | self.div.style.display = "block"; | |
547 | document.body.appendChild( self.div ); | |
548 | } | |
549 | function clear() | |
550 | { | |
551 | if ( self.timer ) | |
552 | { | |
553 | window.clearTimeout( self.timer ); | |
554 | self.timer = false; | |
555 | } | |
556 | } | |
557 | function timer( fn, ms ) | |
558 | { | |
559 | clear(); | |
560 | self.timer = window.setTimeout( fn, ms ); | |
561 | } | |
562 | function leave() | |
563 | { | |
564 | self.div.style.display = "none"; | |
565 | clear(); | |
566 | } | |
567 | function related( parent, child ) | |
568 | { | |
569 | return !( !child || ( child!==parent && !parent.contains( child ) ) ); | |
570 | } | |
571 | a.addEventListener( 'mouseover', function(e) { timer( hover, 500 ); }, false ); | |
572 | a.addEventListener( 'mouseout', function(e) { if ( !related(this,e.relatedTarget) ) timer( leave, 500 ); }, false ); | |
573 | this.div.addEventListener( 'mouseover', clear, false ); | |
574 | this.div.addEventListener( 'mouseout', function(e) { if ( !related(this,e.relatedTarget ) ) timer( leave, 200 ); }, false ); | |
575 | ||
576 | // Work around for mouseleave not working for chrome... | |
577 | var img = document.createElement('img'); | |
578 | img.alt = "x"; | |
579 | //img.src = "data:image/gif;base64,R0lGODlhDwAPANUAAAAAAP////7+/v39/fz8/Pv7+/j4+Pf39/X19fHx8e/v7+7u7u3t7evr6+rq6ufn5+bm5uXl5eLi4uDg4N/f39zc3Nra2tnZ2dTU1NPT09LS0tDQ0M/Pz87Ozs3NzbOzs39/f3V1dVdXVz4+PiMjI////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAPAA8AAAaawJKwpJhgPJiJYjg0SCqNg+DQqEgMzc2DMOh2CY8NtiSBFM5oNERCtBwMAIBhHjccLMUIAsEBkBAkABx7EUYNCYgfACMAH4gJDUcMC5QLcQCVCwxIEQ6eIQCgIZ4OEUkVEBAgACIQIgAgqRVKGhQUcba3ALYaSxIZF8HCwhlsJQYeGxjLzBgbHmPHEh4dHNYdHldMQkVHSUtDQQA7"; | |
580 | img.addEventListener( 'click', leave, false ); | |
581 | this.div.insertBefore( img, this.div.firstChild ); | |
582 | } | |
583 | ||
584 | //---------------------------------------------------------------- | |
585 | // Apply to all links in the document | |
586 | //---------------------------------------------------------------- | |
587 | // FIXME! Does not work on content loaded after page load! | |
588 | Array.prototype.forEach.call( document.querySelectorAll("a"), function(link) | |
589 | { | |
590 | var url = link.href; | |
591 | for ( var it in sites ) | |
592 | { | |
593 | var site = sites[it]; | |
594 | var re = site.match && site.match( url ); | |
595 | if ( re ) | |
596 | { | |
597 | (new linkPlayer( link )).source( link, re, site ); | |
598 | } | |
599 | } | |
600 | } ); | |
601 | ||
602 | // Make it all look pretty | |
603 | addStyle('\ | |
604 | div.TFProfile { \ | |
605 | position:absolute !important; \ | |
606 | z-index:9999999 !important; \ | |
607 | background-color:#F8F8FF !important; \ | |
608 | border: solid 1px #C0C0C0 !important; \ | |
609 | min-width:200px !important; \ | |
610 | padding:5px 10px; \ | |
611 | -webkit-box-shadow: 1px 1px 1px 0px rgba(0, 0, 0, 0.3); \ | |
612 | box-shadow: 1px 1px 1px 0px rgba(0, 0, 0, 0.3); } \ | |
613 | \ | |
614 | div.TFProfile img { \ | |
615 | float:right !important;\ | |
616 | margin:-5px 0px 0px 0px !important;\ | |
617 | background-color: #cbcbcb;\ | |
618 | display: block;\ | |
619 | height: 18px;\ | |
620 | width: 36px;\ | |
621 | font-family: Verdana, Arial, Helvetica, sans-serif;\ | |
622 | font-size: 11px;\ | |
623 | font-weight: bold;color: #fff;\ | |
624 | text-decoration: none;\ | |
625 | text-align:center;\ | |
626 | cursor: pointer;\ | |
627 | line-height: 16px;\ | |
628 | border: none;\ | |
629 | -webkit-transition: background 100ms ease-in-out;\ | |
630 | -moz-transition: background 100ms ease-in-out;\ | |
631 | -ms-transition: background 100ms ease-in-out;\ | |
632 | -o-transition: background 100ms ease-in-out;\ | |
633 | transition: background 100ms ease-in-out; } \ | |
634 | \ | |
635 | div.TFProfile img:hover { \ | |
636 | background-color: #de5044;\ | |
637 | } \ | |
638 | \ | |
639 | div.TFProfile p { \ | |
640 | letter-spacing:0px !important; \ | |
641 | text-align:left !important; \ | |
642 | color:#555!important; \ | |
643 | padding:0!important; \ | |
644 | margin:0px!important; \ | |
645 | display: block !important; \ | |
646 | border: none !important; \ | |
647 | font-family: Verdana, Arial, Helvetica, sans-serif; \ | |
648 | font-size: 10px; \ | |
649 | font-style: normal; \ | |
650 | line-height: normal; \ | |
651 | font-weight: normal; \ | |
652 | font-variant: normal; \ | |
653 | color: #4c4c4c; \ | |
654 | text-decoration: none; } \ | |
655 | \ | |
656 | div.TFProfile p.TFProfile_Done>a { \ | |
657 | font-family:Verdana, Arial, Helvetica, sans-serif; \ | |
658 | font-size:9px; \ | |
659 | font-style:normal; \ | |
660 | line-height:normal; \ | |
661 | font-weight:700; \ | |
662 | font-variant:normal; \ | |
663 | text-decoration:none; \ | |
664 | letter-spacing:0 !important; \ | |
665 | text-align:left !important; \ | |
666 | color:#f8f8f8; \ | |
667 | border:1px solid #679bf3; \ | |
668 | background-color:#77a7f9; \ | |
669 | width:auto; \ | |
670 | height:auto; \ | |
671 | display:block!important; \ | |
672 | margin:2px 0px!important; \ | |
673 | padding:5px!important } \ | |
674 | \ | |
675 | div.TFProfile p.TFProfile_Done>a:hover { \ | |
676 | color:#fff; \ | |
677 | border:1px solid #4585f3; \ | |
678 | -webkit-box-shadow:1px 1px 2px 0 rgba(0,0,0,0.1);\ | |
679 | box-shadow:1px 1px 2px 0 rgba(0,0,0,0.1);\ | |
680 | background: #77a7f9;\ | |
681 | background: -moz-linear-gradient(top, #77a7f9 0%, #699cf2 100%);\ | |
682 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#77a7f9), color-stop(100%,#699cf2));\ | |
683 | background: -webkit-linear-gradient(top, #77a7f9 0%,#699cf2 100%);\ | |
684 | background: -o-linear-gradient(top, #77a7f9 0%,#699cf2 100%);\ | |
685 | background: -ms-linear-gradient(top, #77a7f9 0%,#699cf2 100%);\ | |
686 | background: linear-gradient(to bottom, #77a7f9 0%,#699cf2 100%);\ | |
687 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr="#77a7f9", endColorstr="#699cf2",GradientType=0 );} \ | |
688 | \ | |
689 | div.TFProfile p.TFProfile_Pending { \ | |
690 | font-family:Verdana, Arial, Helvetica, sans-serif !important; \ | |
691 | font-size:9px !important; \ | |
692 | font-style:normal !important; \ | |
693 | line-height:normal !important; \ | |
694 | font-weight:700 !important; \ | |
695 | font-variant:normal !important; \ | |
696 | text-decoration:none !important; \ | |
697 | letter-spacing:0 !important; \ | |
698 | text-align:left !important; \ | |
699 | color:#b1b1b1 !important; \ | |
700 | border:1px solid #cbcbcb !important; \ | |
701 | background-color:#e5e5e5 !important; \ | |
702 | width:auto !important; \ | |
703 | height:auto !important; \ | |
704 | display:block !important; \ | |
705 | margin:2px 0px !important;\ | |
706 | padding:5px !important } \ | |
707 | \ | |
708 | div.TFProfile p.TFProfile_Missing { \ | |
709 | display:none!important; \ | |
710 | } \ | |
711 | div.TFProfile article { \ | |
712 | border-bottom: 1px dotted #C0C0C0; \ | |
713 | padding: 5px 0px; \ | |
714 | } \ | |
715 | div.TFProfile article:last-child { \ | |
716 | border-bottom: none; \ | |
717 | } \ | |
718 | '); |