SideTrackedStats

Adds your SideTracked stats badge onto your profile page and SideTracked cache pages on geocaching.com.

目前为 2019-08-22 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name SideTrackedStats
  3. // @namespace http://www.cryotest.com/
  4. // @description Adds your SideTracked stats badge onto your profile page and SideTracked cache pages on geocaching.com.
  5. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  6. // @copyright 2015-2019, Cryo99
  7. // @attribution SideTracked stats provided by Chris AKA Bus.Stop (http://www.sidetrackedseries.info/)
  8. // @attribution Icon image extracted from the SideTracked banner by Chris AKA Bus.Stop
  9. // @icon https://raw.githubusercontent.com/Cryo99/SideTrackedStats/master/icon48.png
  10. // @icon64 https://raw.githubusercontent.com/Cryo99/SideTrackedStats/master/icon64.png
  11. // @include /^https?://www\.geocaching\.com/(account|my|default|geocache|profile|seek/cache_details|p)/
  12. // @exclude /^https?://www\.geocaching\.com/(login|about|articles|myfriends|account/*)/
  13. // @version 0.2.0
  14. // @supportURL https://github.com/Cryo99/SideTrackedStats
  15. // @require https://openuserjs.org/src/libs/sizzle/GM_config.js
  16. // @grant GM_xmlhttpRequest
  17. // @grant GM_registerMenuCommand
  18. // @grant GM_setValue
  19. // @grant GM_getValue
  20. // ==/UserScript==
  21.  
  22.  
  23. (function (){
  24. "use strict";
  25. var cacheName = document.getElementById("ctl00_ContentBody_CacheName"),
  26. stsCSS = document.createElement("style"),
  27. // ST images can be wider when level names are long. overflow: hidden; on sts-container prevents images from overlaying the div border.
  28. css = 'div.sts-container { border: 1px solid #b0b0b0; margin-top: 1.5em; padding: 0; text-align: center; overflow: hidden;} .WidgetBody div.sts-container { border: none; } #ctl00_ContentBody_ProfilePanel1_pnlProfile div.sts-container { border: none; text-align: inherit;} a.sts-badge { background-color: white;} #ctl00_ContentBody_ProfilePanel1_pnlProfile div.sts-container {float: left}',
  29. currentPage,
  30. profileNameOld = document.getElementById("ctl00_ContentBody_ProfilePanel1_lblMemberName"),
  31. profileName = document.getElementById("ctl00_ProfileHead_ProfileHeader_lblMemberName"),
  32. userFieldOld = document.getElementsByClassName("li-user-info"),
  33. userField = document.getElementsByClassName("user-name"),
  34. userName = "",
  35. userNames = [],
  36. stats = [];
  37.  
  38. function displayStats(stats, page, brand){
  39. function getHtml(uname, brand){
  40. return "<a class='sts-badge' href='https://www.sidetrackedseries.info' title='SideTracked stats.'><img src='https://img.sidetrackedseries.info/awards/st_F_award.php?name=" + uname + "&brand=" + brand + "' /></a>";
  41. }
  42. var stsWidget = document.createElement("div"),
  43. html = "",
  44. i,
  45. target;
  46.  
  47. for(i = 0; i < stats.length; i++){
  48. var name = (stats[i].name + "")
  49. .replace(/;/g, ",")
  50. .replace(/'/g, "&apos;")
  51. .replace(/"/g, "&quot;");
  52. if(i === 0 || stats[i].name !== stats[0].name){
  53. html += getHtml(name, brand);
  54. }
  55. }
  56.  
  57. switch(page){
  58. case "my":
  59. target = document.getElementById("ctl00_ContentBody_lnkProfile");
  60. break;
  61. case "account":
  62. target = document.getElementsByClassName('sidebar-right')[0];
  63. break;
  64. case "cache":
  65. target = document.getElementsByClassName('sidebar')[0];
  66. break;
  67. case "profile":
  68. if(profileName){
  69. target = document.getElementById("ctl00_ContentBody_ProfilePanel1_lblProfile");
  70. if (target) {
  71. target = target.parentNode;
  72. }
  73. }else if(profileNameOld){
  74. target = document.getElementById("HiddenProfileContent");
  75. }
  76. break;
  77. }
  78.  
  79. if(!target){
  80. console.warn("SideTracked Stats: Aborted - couldn't find where to insert widget. You might not be logged in.");
  81. return;
  82. }
  83.  
  84. if(html){
  85. stsWidget.className = "sts-container";
  86. stsWidget.innerHTML = html;
  87. switch(page){
  88. case "my":
  89. case "profile":
  90. target.parentNode.insertBefore(stsWidget, target.nextSibling);
  91. break;
  92. default:
  93. target.insertBefore(stsWidget, target.firstChild.nextSibling.nextSibling);
  94. break;
  95. }
  96. }else{
  97. console.warn("SideTracked Stats: didn't generate an award badge.");
  98. }
  99. }
  100. function getHiderName(){
  101. var i,
  102. links = document.getElementsByTagName("a"),
  103. pos;
  104. if(links){
  105. for(i = 0; i < links.length; i++){
  106. pos = links[i].href.indexOf("/seek/nearest.aspx?u=");
  107. if(pos !== -1){
  108. return decodeURIComponent(links[i].href.substr(pos + 21).replace(/\+/g, '%20'));
  109. }
  110. }
  111. }
  112. }
  113.  
  114. function parseNames(names){
  115. // Filter out null or undefined entries, convert commas to semicolons, then convert to a comma-separated string.
  116. return encodeURIComponent(names
  117. .filter(function (n){
  118. return n !== undefined;
  119. })
  120. .map(function (n){
  121. return (n + "").replace(/,/g, ";");
  122. })
  123. .join());
  124. }
  125.  
  126.  
  127. //// EXECUTION STARTS HERE
  128. console.info("SideTracked Stats V" + GM_info.script.version);
  129.  
  130. //******* Configuration dialogue *******
  131. // Register the menu item.
  132. GM_registerMenuCommand("Options", function(){
  133. GM_config.open();
  134. }, 'S');
  135.  
  136. GM_config.init({
  137. 'id': 'sts_config', // The id used for this instance of GM_config
  138. 'title': 'SideTracked Stats', // Panel Title
  139. 'fields': { // Fields object
  140. 'sts_branding': { // This is the id of the field
  141. 'label': 'Branding', // Appears next to field
  142. 'type': 'select', // Makes this setting a dropdown
  143. 'options': ['Awards', 'Levels', 'Jobs', 'None'], // Possible choices
  144. 'default': 'Jobs' // Default value if user doesn't change it
  145. }
  146. },
  147. // Dialogue internal styles.
  148. 'css': '#sts_config {position: static !important; width: 75% !important; margin: 1.5em auto !important; border: 10 !important;} #sts_config_sts_branding_var {padding-top: 30px;}',
  149. 'events': {
  150. 'open': function(document, window, frame){
  151. // iframe styles.
  152. frame.style.width = '300px';
  153. frame.style.height = '250px';
  154. frame.style.left = parent.document.body.clientWidth / 2 - 150 + 'px';
  155. frame.style.borderWidth = '5px';
  156. frame.style.borderStyle = 'ridge';
  157. frame.style.borderColor = '#999999';
  158. },
  159. 'save': function(){
  160. GM_setValue('sts_branding', GM_config.get('sts_branding'));
  161. location.reload(); // reload the page when configuration was changed
  162. }
  163. }
  164. });
  165.  
  166. var brand = GM_getValue('sts_branding', 'jobs').toLowerCase();
  167. console.info("SideTracked Stats branding: " + brand);
  168. //**************************************
  169.  
  170. // Don't run on frames or iframes
  171. if(window.top !== window.self){
  172. return false;
  173. }
  174.  
  175. if(/\/my\//.test(location.pathname)){
  176. // On a My Profile page
  177. currentPage = "my";
  178. }else if(/\/account\//.test(location.pathname)){
  179. // On a Profile page
  180. currentPage = "account";
  181. }else{
  182. if(cacheName){
  183. // On a Geocache page...
  184. if(!/SideTracked/i.test(cacheName.innerHTML) && !/side tracked/i.test(cacheName.innerHTML)){
  185. // ...but not a SideTracked cache
  186. return;
  187. }
  188. currentPage = "cache";
  189. }else{
  190. currentPage = "profile";
  191. }
  192. }
  193.  
  194. var hider;
  195. switch(currentPage){
  196. case "profile":
  197. if(profileName){
  198. userNames = [profileName.textContent.trim()];
  199. }else if(profileNameOld){
  200. userNames = [profileNameOld.textContent.trim()];
  201. }
  202. break;
  203. default:
  204. if(userField.length > 0){
  205. userNames.push(userField[0].innerHTML.trim());
  206. }
  207. hider = getHiderName();
  208. if(typeof hider !== 'undefined'){
  209. userNames.push(hider);
  210. }
  211. break;
  212. }
  213.  
  214. for(var i = 0; i < userNames.length; i++){
  215. stats[i] = {name: userNames[i]};
  216. }
  217.  
  218. userName = parseNames(userNames);
  219. if(!userName){
  220. console.error("SideTracked Stats: Aborted - couldn't work out user name");
  221. return;
  222. }
  223.  
  224. // Inject widget styling
  225. stsCSS.type = 'text/css';
  226. if(stsCSS.styleSheet){
  227. stsCSS.styleSheet.cssText = css;
  228. }else{
  229. stsCSS.appendChild(document.createTextNode(css));
  230. }
  231. document.head.appendChild(stsCSS);
  232. displayStats(stats, currentPage, brand);
  233. }());