您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
This library provides the core functionality to add a stats badge onto profile and cache pages on geocaching.com.
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/389508/880219/GC%20Stats%20Banner%20Library.js
- // ==UserScript==
- // @exclude *
- // @supportURL https://github.com/Cryo99/GCStatsBannerLib
- // @version 1.0.2
- // @include /^https?://www\.geocaching\.com/(account/dashboard|my|default|geocache|profile|seek/cache_details|p)/
- // @exclude /^https?://www\.geocaching\.com/(login|about|articles|myfriends)/
- // @require https://openuserjs.org/src/libs/sizzle/GM_config.js
- // ==UserLibrary==
- // @name GC Stats Banner Library
- // @description This library provides the core functionality for adding a stats banner onto profile and cache pages on geocaching.com.
- // @copyright 2019-2020, Cryo99 (https://github.com/Cryo99)
- // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
- // ==/UserScript==
- // ==/UserLibrary==
- /*jshint esversion: 6 */
- var GCStatsBanner = function(cfg){
- // ========================= Private members =========================
- var _cfg = {},
- _cfgDefault = {
- cacheTitles: false,
- callerVersion: 'Unknown',
- elPrefix: '',
- imgScript: 'find-badge.php',
- logLevel: 'normal',
- seriesLevels: ['None'],
- seriesLevelDefault: 'None',
- seriesName: false,
- seriesURL: false
- };
- // Internal vars.
- _cacheName = document.getElementById("ctl00_ContentBody_CacheName"),
- // Images can be wider when level names are long. overflow: hidden; on <series>-container prevents images from overlaying the div border.
- _css = '',
- _profileNameOld = document.getElementById("ctl00_ContentBody_ProfilePanel1_lblMemberName"),
- _profileName = document.getElementById("ctl00_ProfileHead_ProfileHeader_lblMemberName"),
- _userField = document.getElementsByClassName("user-name");
- // ========================= Private methods =========================
- // Constructor.
- function _const(){
- for(cfgItem in _cfgDefault){
- if(typeof cfg[cfgItem] === 'undefined' || cfg[cfgItem] === null){
- // Required variable is undefined or null
- if(_cfgDefault[cfgItem] === false){
- throw new Error('GCStatsBannerLib: ' + cfgItem + ' is a required configuration element.');
- }
- // Use the default value.
- _cfg[cfgItem] = _cfgDefault[cfgItem];
- // console.warn('GCStatsBannerLib: ' + cfgItem + ' undefined. Using value: ' + _cfgDefault[cfgItem] + '.')
- }else{
- // Use the configured value.
- _cfg[cfgItem] = cfg[cfgItem];
- }
- }
- // If elPrefix is still empty, generate it.
- if(!_cfg.elPrefix){
- _cfg.elPrefix = _getPrefix(cfg.seriesName);
- }
- _log(cfg, 'Passed config');
- _log(_cfg, 'Generated config');
- _generateCSS();
- }
- // Run the constructor on creation.
- _const();
- function _getPrefix(name){
- var matches = name.match(/\b(\w)/g);
- return matches.join('').toLowerCase().padStart(3, '_');
- }
- function _generateCSS(){
- _css = 'div.' + _cfg.elPrefix + '-container { border: 1px solid #b0b0b0; margin-top: 1.5em; padding: 0; text-align: center; overflow: hidden;} ' +
- '.WidgetBody div.' + _cfg.elPrefix + '-container { border: none; } ' +
- '#ctl00_ContentBody_ProfilePanel1_pnlProfile div.' + _cfg.elPrefix + '-container { border: none; text-align: inherit;} ' +
- 'a.' + _cfg.elPrefix + '-badge { background-color: white;} ' +
- '#ctl00_ContentBody_ProfilePanel1_pnlProfile div.' + _cfg.elPrefix + '-container {float: left}' +
- '#StatsComponents {background: white;}';
- }
- function _log(msg, desc){
- if(_cfg.logLevel === 'debug'){
- var msgStart = '%c' + _cfg.seriesName + ' Stats Debug (%s):';
- console.log(msgStart, "color: yellow; font-style: italic; background-color: red;padding: 2px", desc, msg);
- }
- }
- function _getHiderName(){
- var i,
- links = document.getElementsByTagName("a"),
- pos;
- if(links){
- for(i = 0; i < links.length; i++){
- pos = links[i].href.indexOf("/seek/nearest.aspx?u=");
- if(pos !== -1){
- return decodeURIComponent(links[i].href.substr(pos + 21).replace(/\+/g, '%20'));
- }
- }
- }
- };
- function _parseNames(names){
- // Filter out null or undefined entries, convert commas to semicolons, then convert to a comma-separated string.
- return encodeURIComponent(names
- .filter(function (n){
- return n !== undefined;
- })
- .map(function (n){
- return (n + "").replace(/,/g, ";");
- })
- .join());
- };
- function _getHtml(uname, brand){
- return "<a class='" + _cfg.elPrefix + "-badge' href='https://www." + _cfg.seriesURL + "' title='" + _cfg.seriesName +
- " stats.'><img src='https://img." + _cfg.seriesURL + "/awards/" + _cfg.imgScript + "?name=" + uname + "&brand=" + brand + "' /></a>";
- };
- function _displayStats(stats, page, brand){
- var widget = document.createElement("div"),
- html = "",
- i,
- target;
- for(i = 0; i < stats.length; i++){
- var name = (stats[i].name + "")
- .replace(/;/g, ",")
- .replace(/'/g, "'")
- .replace(/"/g, """);
- if(i === 0 || stats[i].name !== stats[0].name){
- html += _getHtml(name, brand);
- }
- }
- _log(html, 'Banner HTML');
- switch(page){
- case "my":
- target = document.getElementById("ctl00_ContentBody_lnkProfile");
- break;
- case "account":
- // New account dashboard.
- // The WidgetPanel is too slow to load and causes scripts to block so follow GClhII and just append the widget to the sidebar.
- target = document.querySelector(".sidebar-right");
- break;
- case "cache":
- target = document.getElementsByClassName('sidebar')[0];
- break;
- case "profile":
- if(_profileName){
- target = document.getElementById("ctl00_ContentBody_ProfilePanel1_lblProfile");
- if (target) {
- target = target.parentNode;
- }
- }else if(_profileNameOld){
- target = document.getElementById("HiddenProfileContent");
- }
- break;
- }
- if(!target){
- console.warn(_cfg.seriesName + " Stats: Aborted - couldn't find where to insert widget. You might not be logged in.");
- return;
- }
- if(html){
- widget.className = _cfg.elPrefix + "-container";
- widget.innerHTML = html;
- switch(page){
- case "my":
- case "profile":
- target.parentNode.insertBefore(widget, target.nextSibling);
- break;
- case "account":
- // If the StatsWidget isn't present, create it.
- var el = document.getElementById("StatsWidget");
- if(!el){
- _log('Creating widget.', 'StatsWidget');
- var divStats = document.createElement('div');
- divStats.id = "StatsWidget";
- divStats.classList.add("panel", "collapsible");
- divStats.innerHTML = '<div class="panel-header isActive" aria-expanded="true">\
- <h1 id="stats-widget-label" class="h5 no-margin">Statistics</h1>\
- <button aria-controls="StatsWidget" aria-labelledby="stats-widget-label">\
- <svg height="22" width="22" class="opener" role="img">\
- <use xlink:href="/account/app/ui-icons/sprites/global.svg#icon-expand-svg-fill"></use>\
- </svg>\
- </button>\
- </div>\
- <div id="StatsComponents" class="panel-body">\
- <div id="StatsPanel" class="widget-panel"></div>\
- </div>';
- target.append(divStats);
- // Hide the panel if it was previously hidden.
- if (!GM_getValue('statsWidget_visible', false)) {
- document.querySelector('#StatsWidget .panel-body').style.display = "none";
- document.querySelector('#StatsWidget .panel-header').classList.remove('isActive');
- _fadeOut(document.querySelector('#StatsWidget .panel-body'));
- }
- // Add the click handler.
- document.querySelector('#StatsWidget .panel-header').addEventListener('click', function() {
- if (GM_getValue('statsWidget_visible', true)) {
- document.querySelector('#StatsWidget .panel-header').classList.remove('isActive');
- _fadeOut(document.querySelector('#StatsWidget .panel-body'));
- GM_setValue('statsWidget_visible', false);
- }else{
- document.querySelector('#StatsWidget .panel-header').classList.add('isActive');
- _fadeIn(document.querySelector('#StatsWidget .panel-body'));
- GM_setValue('statsWidget_visible', true);
- }
- });
- }
- // Finally, append the banner.
- document.querySelector('#StatsPanel').appendChild(widget);
- break;
- default:
- target.insertBefore(widget, target.firstChild.nextSibling.nextSibling);
- break;
- }
- }else{
- console.warn(_cfg.seriesName + " Stats: didn't generate an award badge.");
- }
- };
- function _fadeOut(element) {
- var op = 1; // initial opacity
- var timer = setInterval(function () {
- if (op <= 0.1){
- clearInterval(timer);
- element.style.display = "none";
- }
- element.style.opacity = op;
- op -= 0.1;
- }, 30);
- };
- function _fadeIn(element) {
- var op = 0.1; // initial opacity
- element.style.display = "block";
- var timer = setInterval(function () {
- if (op >= 1){
- clearInterval(timer);
- }
- element.style.opacity = op;
- op += 0.1;
- }, 30);
- };
- function _createConfigDlg(){
- // Register the menu item.
- GM_registerMenuCommand("Options", function(){
- GM_config.open();
- });
- GM_config.init({
- 'id': _cfg.elPrefix + '_config', // The id used for this instance of GM_config
- 'title': _cfg.seriesName + ' Stats', // Panel Title
- 'fields': { // Fields object
- 'branding': { // This is the id of the field
- 'label': 'Branding', // Appears next to field
- 'type': 'select', // Makes this setting a dropdown
- 'options': _cfg.seriesLevels, // Possible choices
- 'default': _cfg.seriesLevelDefault // Default value if user doesn't change it
- }
- },
- // Dialogue internal styles.
- 'css': '#' + _cfg.elPrefix + '_config {position: static !important; width: 75% !important; margin: 1.5em auto !important; border: 10 !important;}' +
- '#' + _cfg.elPrefix + '_config_' + _cfg.elPrefix + '_branding_var {padding-top: 30px;} #' + _cfg.elPrefix + '_config button {color: black;}',
- 'events': {
- 'open': function(document, window, frame){
- // iframe styles.
- frame.style.width = '300px';
- frame.style.height = '250px';
- frame.style.left = parent.document.body.clientWidth / 2 - 150 + 'px';
- frame.style.borderWidth = '5px';
- frame.style.borderStyle = 'ridge';
- frame.style.borderColor = '#999999';
- },
- 'save': function(){
- GM_setValue(_cfg.elPrefix + '_branding', GM_config.get('branding'));
- location.reload(); // reload the page when configuration was changed
- }
- }
- });
- };
- function _init(){
- var currentPage,
- elCSS = document.createElement("style"),
- userName = "",
- userNames = [],
- stats = [];
- // Don't run on frames or iframes
- if(window.top !== window.self){
- return false;
- }
- if(/\/my\//.test(location.pathname)){
- // On a My Profile page
- currentPage = "my";
- }else if(/\/account\//.test(location.pathname)){
- // On a Profile page
- currentPage = "account";
- }else{
- if(_cacheName){
- // On a Geocache page...
- // var matcher = new RegExp(_cfg.seriesName, "i");
- // if(!matcher.test(_cacheName.innerHTML)){
- // ...but not the right cache series.
- // return;
- // }
- var titleFound = false;
- for(title in _cfg.cacheTitles){
- var matcher = new RegExp(_cfg.cacheTitles[title], "i");
- if(matcher.test(_cacheName.innerHTML)){
- titleFound = true;
- }
- }
- if(!titleFound){
- // ...but not the right cache series.
- return;
- }
- currentPage = "cache";
- }else{
- currentPage = "profile";
- }
- }
- _log(currentPage, 'Detected page');
- // We're going to display so we can announce ourselves and prepare the configuration dialogue.
- console.info(_cfg.seriesName + " Stats V" + _cfg.callerVersion);
- //CONFIG
- _createConfigDlg();
- var brand = GM_getValue(_cfg.elPrefix + '_branding', _cfg.seriesLevelDefault);
- _log(brand, 'Stats branding');
- brand = brand.toLowerCase()
- // Get hider details.
- var hider;
- switch(currentPage){
- case "profile":
- if(_profileName){
- userNames = [_profileName.textContent.trim()];
- }else if(_profileNameOld){
- userNames = [_profileNameOld.textContent.trim()];
- }
- break;
- default:
- if(_userField.length > 0){
- userNames.push(_userField[0].innerHTML.trim());
- }
- hider = _getHiderName();
- if(typeof hider !== 'undefined'){
- userNames.push(hider);
- }
- break;
- }
- _log(userNames[0], "Finder's name");
- _log(userNames[1], "Hider's name");
- for(var i = 0; i < userNames.length; i++){
- stats[i] = {name: userNames[i]};
- }
- _log(stats, 'Statistics');
- userName = _parseNames(userNames);
- if(!userName){
- console.error(_cfg.seriesName + " Stats: Aborted - couldn't work out user name");
- return;
- }
- // Inject widget styling
- elCSS.setAttribute('type', 'text/css');
- if(elCSS.styleSheet){
- elCSS.styleSheet.cssText = _css;
- }else{
- elCSS.appendChild(document.createTextNode(_css));
- }
- document.head.appendChild(elCSS);
- _displayStats(stats, currentPage, brand);
- }
- return {
- // ========================= Public members =========================
- // ========================= Public methods =========================
- init: _init
- };
- };