SideTrackedStats

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

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

  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.1
  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.  
  129. // Don't run on frames or iframes
  130. if(window.top !== window.self){
  131. return false;
  132. }
  133.  
  134. if(/\/my\//.test(location.pathname)){
  135. // On a My Profile page
  136. currentPage = "my";
  137. }else if(/\/account\//.test(location.pathname)){
  138. // On a Profile page
  139. currentPage = "account";
  140. }else{
  141. if(cacheName){
  142. // On a Geocache page...
  143. if(!/SideTracked/i.test(cacheName.innerHTML) && !/side tracked/i.test(cacheName.innerHTML)){
  144. // ...but not a SideTracked cache
  145. return;
  146. }
  147. currentPage = "cache";
  148. }else{
  149. currentPage = "profile";
  150. }
  151. }
  152.  
  153. // We're going to display so we can announce ourselves and prepare the dialogue.
  154. console.info("SideTracked Stats V" + GM_info.script.version);
  155.  
  156. //******* Configuration dialogue *******
  157. // Register the menu item.
  158. GM_registerMenuCommand("Options", function(){
  159. GM_config.open();
  160. }, 'S');
  161.  
  162. GM_config.init({
  163. 'id': 'sts_config', // The id used for this instance of GM_config
  164. 'title': 'SideTracked Stats', // Panel Title
  165. 'fields': { // Fields object
  166. 'sts_branding': { // This is the id of the field
  167. 'label': 'Branding', // Appears next to field
  168. 'type': 'select', // Makes this setting a dropdown
  169. 'options': ['Awards', 'Levels', 'Jobs', 'None'], // Possible choices
  170. 'default': 'Jobs' // Default value if user doesn't change it
  171. }
  172. },
  173. // Dialogue internal styles.
  174. '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;}',
  175. 'events': {
  176. 'open': function(document, window, frame){
  177. // iframe styles.
  178. frame.style.width = '300px';
  179. frame.style.height = '250px';
  180. frame.style.left = parent.document.body.clientWidth / 2 - 150 + 'px';
  181. frame.style.borderWidth = '5px';
  182. frame.style.borderStyle = 'ridge';
  183. frame.style.borderColor = '#999999';
  184. },
  185. 'save': function(){
  186. GM_setValue('sts_branding', GM_config.get('sts_branding'));
  187. location.reload(); // reload the page when configuration was changed
  188. }
  189. }
  190. });
  191.  
  192. var brand = GM_getValue('sts_branding', 'Jobs');
  193. console.info("SideTracked Stats branding: " + brand);
  194. brand = brand.toLowerCase()
  195. //**************************************
  196.  
  197. var hider;
  198. switch(currentPage){
  199. case "profile":
  200. if(profileName){
  201. userNames = [profileName.textContent.trim()];
  202. }else if(profileNameOld){
  203. userNames = [profileNameOld.textContent.trim()];
  204. }
  205. break;
  206. default:
  207. if(userField.length > 0){
  208. userNames.push(userField[0].innerHTML.trim());
  209. }
  210. hider = getHiderName();
  211. if(typeof hider !== 'undefined'){
  212. userNames.push(hider);
  213. }
  214. break;
  215. }
  216.  
  217. for(var i = 0; i < userNames.length; i++){
  218. stats[i] = {name: userNames[i]};
  219. }
  220.  
  221. userName = parseNames(userNames);
  222. if(!userName){
  223. console.error("SideTracked Stats: Aborted - couldn't work out user name");
  224. return;
  225. }
  226.  
  227. // Inject widget styling
  228. stsCSS.type = 'text/css';
  229. if(stsCSS.styleSheet){
  230. stsCSS.styleSheet.cssText = css;
  231. }else{
  232. stsCSS.appendChild(document.createTextNode(css));
  233. }
  234. document.head.appendChild(stsCSS);
  235. displayStats(stats, currentPage, brand);
  236. }());