Advertisement
oxocuboid

TF2 Profile Script (update: removed tf2lobby/wireplay)

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