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 | '); |