ADV Ride Report De-crufter

Remove cruft from ride reports on ADVRider, i.e. only show posts from the member who started the thread.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ADV Ride Report De-crufter
// version 4.2
// Release Date: 2018.09.11
// http://devourmiles.com
// Copyright (c) 2009-2018, All Rights Reserved (except where
// otherwise noted)
//
// You may modify this script for your own personal use,
// but please contact me first before releasing any changes.
//
// ***** INSTRUCTIONS *****
//
// This is a Greasemonkey user script.
//
// To use this script, get Greasemonkey: http://greasemonkey.mozdev.org/
// After you've installed it, come back to this page. A dialog box will
// appear asking you if you want to install this script. Believe me, you do.
//
// To uninstall, go to Tools->Greasemonkey->Manage User Scripts, select
// "ADV Ride Report De-crufter" from the list on the left, and click
// Uninstall.
//
//
// ==UserScript==
// @name           ADV Ride Report De-crufter
// @namespace      http://devourmiles.com
// @version        4.2
// @description    Remove cruft from ride reports on ADVRider, i.e. only show posts from the member who started the thread.
//
// @include        https://www.advrider.com/*
// @include        https://advrider.com/*
// ==/UserScript==

function Querystring(qs) {
  this.qstr = '';
  this.params = [];

  if (qs == null) qs = location.search.substring(1, location.search.length);
  if (qs.length == 0) return;

  qs = qs.replace(/\+/g, ' ');
  this.qstr = qs;
  this.params = qs.split('/');
}

Querystring.prototype.contains = function(key) {
  for (var i=0; i<this.params.length; i++) {
    if (this.params[i] == key) {
      return true;
    }
    return false;
  }
}

/* xpath helper
 * http://wiki.greasespot.net/Code_snippets#XPath_helper
 */
function $x() {
  var x='',          // default values
  node=document,
  type=0,
  fix=true,
  i=0,
  toAr=function(xp){      // XPathResult to array
    var final=[], next;
    while(next=xp.iterateNext())
      final.push(next);
    return final
  },
  cur;
  while (cur=arguments[i++])      // argument handler
    switch(typeof cur) {
  case "string":x+=(x=='') ? cur : " | " + cur;continue;
  case "number":type=cur;continue;
  case "object":node=cur;continue;
  case "boolean":fix=cur;continue;
  }
  if (fix) {      // array conversion logic
    if (type==6) type=4;
    if (type==7) type=5;
  }
  if (!/^\//.test(x)) x="//"+x;                 // selection mistake helper
  if (node!=document && !/^\./.test(x)) x="."+x;  // context mistake helper
  var temp=document.evaluate(x,node,null,type,null); //evaluate!
  if (fix)
    switch(type) {                   // automatically return special type
  case 1:return temp.numberValue;
  case 2:return temp.stringValue;
  case 3:return temp.booleanValue;
  case 8:return temp.singleNodeValue;
  case 9:return temp.singleNodeValue;
  }
  return fix ? toAr(temp) : temp;
}

/*!
 * JavaScript Cookie v2.2.0
 * https://github.com/js-cookie/js-cookie
 *
 * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
 * Released under the MIT license
 */
;(function (factory) {
  var registeredInModuleLoader;
  if (typeof define === 'function' && define.amd) {
    define(factory);
    registeredInModuleLoader = true;
  }
  if (typeof exports === 'object') {
    module.exports = factory();
    registeredInModuleLoader = true;
  }
  if (!registeredInModuleLoader) {
    var OldCookies = window.Cookies;
    var api = window.Cookies = factory();
    api.noConflict = function () {
      window.Cookies = OldCookies;
      return api;
    };
  }
}(function () {
  function extend () {
    var i = 0;
    var result = {};
    for (; i < arguments.length; i++) {
      var attributes = arguments[ i ];
      for (var key in attributes) {
        result[key] = attributes[key];
      }
    }
    return result;
  }

  function decode (s) {
    return s.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent);
  }

  function init (converter) {
    function api() {}

    function set (key, value, attributes) {
      if (typeof document === 'undefined') {
        return;
      }

      attributes = extend({
        path: '/'
      }, api.defaults, attributes);

      if (typeof attributes.expires === 'number') {
        attributes.expires = new Date(new Date() * 1 + attributes.expires * 864e+5);
      }

      // We're using "expires" because "max-age" is not supported by IE
      attributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';

      try {
        var result = JSON.stringify(value);
        if (/^[\{\[]/.test(result)) {
          value = result;
        }
      } catch (e) {}

      value = converter.write ?
        converter.write(value, key) :
        encodeURIComponent(String(value))
          .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);

      key = encodeURIComponent(String(key))
        .replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent)
        .replace(/[\(\)]/g, escape);

      var stringifiedAttributes = '';
      for (var attributeName in attributes) {
        if (!attributes[attributeName]) {
          continue;
        }
        stringifiedAttributes += '; ' + attributeName;
        if (attributes[attributeName] === true) {
          continue;
        }

        // Considers RFC 6265 section 5.2:
        // ...
        // 3.  If the remaining unparsed-attributes contains a %x3B (";")
        //     character:
        // Consume the characters of the unparsed-attributes up to,
        // not including, the first %x3B (";") character.
        // ...
        stringifiedAttributes += '=' + attributes[attributeName].split(';')[0];
      }

      return (document.cookie = key + '=' + value + stringifiedAttributes);
    }

    function get (key, json) {
      if (typeof document === 'undefined') {
        return;
      }

      var jar = {};
      // To prevent the for loop in the first place assign an empty array
      // in case there are no cookies at all.
      var cookies = document.cookie ? document.cookie.split('; ') : [];
      var i = 0;

      for (; i < cookies.length; i++) {
        var parts = cookies[i].split('=');
        var cookie = parts.slice(1).join('=');

        if (!json && cookie.charAt(0) === '"') {
          cookie = cookie.slice(1, -1);
        }

        try {
          var name = decode(parts[0]);
          cookie = (converter.read || converter)(cookie, name) ||
            decode(cookie);

          if (json) {
            try {
              cookie = JSON.parse(cookie);
            } catch (e) {}
          }

          jar[name] = cookie;

          if (key === name) {
            break;
          }
        } catch (e) {}
      }

      return key ? jar[key] : jar;
    }

    api.set = set;
    api.get = function (key) {
      return get(key, false /* read as raw */);
    };
    api.getJSON = function (key) {
      return get(key, true /* read as json */);
    };
    api.remove = function (key, attributes) {
      set(key, '', extend(attributes, {
        expires: -1
      }));
    };

    api.defaults = {};

    api.withConverter = init;

    return api;
  }

  return init(function () {});
}));

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

/* begin de-crufter code... */

var advRR = {
  showOptionsDiv: function() {
    // get current settings, if any
    var settings = Cookies.get(advRR.getThreadId());
    if (settings) {
      settings = settings.split(/:/);
      advRR.author = settings[0];
      advRR.filter = (settings[1] == "1" ? true : false);
      if (typeof(settings[2]) != "undefined" || settings[2] != "") {
        if (settings[2] == "") {
          var exception_list = "";
        } else {
          advRR.exceptions = settings[2].split(/,/);
          var exception_list = advRR.exceptions.join(",");
        }
      }
      var op_username = settings[0];
      var advRR_chk = (settings[1] == "1" ? "checked " : "");
    } else {
      var op = $x("//p[@id='pageDescription']/a[@class='username']", XPathResult.FIRST_ORDERED_NODE_TYPE);
      var op_username = op.textContent;
      var advRR_chk = "";
      var exception_list = "";
    }

    if (typeof(op_username) != "undefined") {
      var newNode = document.createElement("div");
      var innerHTML = "Only show <b>" + op_username + "</b>'s posts? <input type='checkbox' id='advRR_chk_show' " + advRR_chk + "> <span style='padding-left:20px'>Exceptions: [<span id='exception_span'>" + exception_list + "</span>] <a href='#' id='exception_edit' style='margin-left:10px'>edit</a> | <a href='#' id='exception_clear'>clear</a></span>";
      newNode.innerHTML = innerHTML;
      newNode.addEventListener ('click', function () {
        advRRToggle(op_username);
      });

      var target = $x("//p[@id='pageDescription']", XPathResult.ANY_UNORDERED_NODE_TYPE);
      target.appendChild(newNode);

      var exceptionEdit = $x("//a[@id='exception_edit']", XPathResult.ANY_UNORDERED_NODE_TYPE);
      exceptionEdit.addEventListener ('click', function (e) {
        e.preventDefault();
        editExceptions();
      });

      var exceptionClear = $x("//a[@id='exception_clear']", XPathResult.ANY_UNORDERED_NODE_TYPE);
      exceptionClear.addEventListener ('click', function (e) {
        e.preventDefault();
        clearExceptions();
      });
    }
  },
  getPosts: function () {
    advRR.posts = $x("//li[contains(@id, 'post')]", XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
  },
  getThreadId: function () {
    return advRR.urlParts[5];
  },
  isFirstPage: function () {
    var pageNavHeader = $x("//span[@class='pageNavHeader']", XPathResult.FIRST_ORDERED_NODE_TYPE);
    if (/Page 1/.test(pageNavHeader.innerHTML)) {
      return true;
    } else {
      return false;
    }
  },
  showError: function (message) {
    var newNode = document.createElement("div");
    newNode.innerHTML = message;
    var target = $x("//p[@id='pageDescription']", XPathResult.ANY_UNORDERED_NODE_TYPE);
    target.appendChild(newNode);
  },
  setGMOption: function (key, value) {
    Cookies.set(key, value, { expires: 3650 });
  },
author: "",
filter: false,
queryStr: {},
urlParts: [],
exceptions: [],
posts: []
} // end advRR

deCruftify = function() {
  for (var i=0; i < advRR.posts.length; i++) {
    var post = advRR.posts[i];
    if (advRR.filter && post.dataset.author != advRR.author && !advRR.exceptions.includes(post.dataset.author)) {
      post.style.display = "none";
    } else {
      post.style.display = "";
    }
  }
}

advRRToggle = function(username) {
  var advRR_chk_show = $x("//input[@id='advRR_chk_show']", XPathResult.FIRST_ORDERED_NODE_TYPE);

  if (advRR_chk_show.checked) {
    advRR.setGMOption(advRR.getThreadId(), username + ":1:" + advRR.exceptions.join(','));
    advRR.author = username;
    advRR.filter = true;
  } else {
    advRR.setGMOption(advRR.getThreadId(), username + ":0:" + advRR.exceptions.join(','));
    advRR.author = username;
    advRR.filter = false;
  }

  // refresh display of posts
  deCruftify();
}

editExceptions = function() {
  var exception_span = $x("//span[@id='exception_span']", XPathResult.FIRST_ORDERED_NODE_TYPE);
  var input = prompt("Make exceptions for? (separate multiple usernames with commas)", advRR.exceptions.join(","));
  if (input == null || input == "") {
    return false;
  } else {
    var newSettings = advRR.author + ":" + (advRR.filter ? "1" : "0") + ":" + input;
    advRR.setGMOption(advRR.getThreadId(), newSettings);
    advRR.exceptions = input.split(",");

    exception_span.innerHTML = input;

    deCruftify();
  }
}

clearExceptions = function() {
  var exception_span = $x("//span[@id='exception_span']", XPathResult.FIRST_ORDERED_NODE_TYPE);
  var c = confirm("Clear exceptions for " + advRR.exceptions.join(",") + "?");
  if (!c) {
    return false;
  } else {
    var newSettings = advRR.author + ":" + (advRR.filter ? "1" : "0") + ":";
    advRR.setGMOption(advRR.getThreadId(), newSettings);
    advRR.exceptions = [];

    exception_span.innerHTML = "";

    deCruftify();
  }
}

async function doIt() {
  console.log("Firing up the ADV RR De-crufter...");
  await sleep(1200); // wait for XenForo to do its thing
  console.log("ADV RR De-crufter ready to go.");

  // get URL and split into an array of parts
  advRR.urlParts = document.URL.split("/");

  // are we even viewing a thread?
  if (!advRR.urlParts[4] == "threads") {
    // nope, so GTFO
    return;
  }

  console.log("De-crufting...");
  advRR.getPosts();
  advRR.showOptionsDiv();
  deCruftify();
}
doIt();