Greasy Fork镜像 还支持 简体中文。

BetterTeddit

Extensible convenience extension for Teddit instances while keeping general JavaScript disabled.

目前為 2022-10-08 提交的版本,檢視 最新版本

// ==UserScript==
// @name        BetterTeddit
// @namespace   betterteddit
// @match       https://*.teddit.net/*
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_addValueChangeListener
// @grant       GM_addStyle
// @run-at      document-start
// @version     1.3.0
// @author      Hououin Kyōma
// @license     GPLv3
// @description Extensible convenience extension for Teddit instances while keeping general JavaScript disabled.
// @icon        data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAgMAAABGXkYxAAAACVBMVEUAAABHcEz///+5kPJMAAAAAnRSTlP/AOW3MEoAAABySURBVBjTdc+xDYAwDATAV8qMkikYgYJQMAJTUNPT0yCFn5J34ogGXJ1lS34jtxrxhYVsIHkZZuE2aKKZsBoOgQWJl+EEDTO3oO2K+IG9IzqKA2iAo3ScQhKyVlBvqa+38gCPMQYPNkWPmpOHf9/5e/kBEFFfBfn/SWkAAAAASUVORK5CYII=
// ==/UserScript==

/* TODO:
 *
 *  - Cookie Syncing of subs and other settings across instances. This is very trackable, but an option for people who want it regardless?
 *  - The much better idea, custom links in the header without touching any cookies. Use a toggle to allow switching between BT favorites and cookie list.
 *    Cookies are generally a bad idea, if synced subs and saved posts would be especially easy to track someone across IPs.
 *    On that topic...
 *  - Save posts in BetterTeddit instead of cookies. Could be good to backup the content as well in case post is deleted.
 *  - Export/Import favorite subs, saved posts... On that note probably standardize all the other settings on various instances.
 *  - BetterTeddit theme selector for cookie blocking theme support.
 *  - Post hiding, already seen stickies specifically, probably automod replies too.
 *  - Make ID's unique for each thread ID.
 *
 * Things I probably won't do, but someone else might write an extension for:
 * - Anything that requires Reddit or Teddit API or fetching entire pages to grab information that can't be gained with the API.
 *   Anything auto-reloading as well. Basically I don't want to do anything that puts extra strain on instances by making additional or repeated requests.
 *   Not all instances have API enabled either and I don't want to make them incompatible or inferior for not wanting additional strain.
 * - I have absolutely no interest in posting on Reddit so I won't write anything that connects to their API to allow people to DM/Post anything.
 */

/* === README.md ===
# BetterTeddit

Lightweight extensible userscript for Teddit instances for Reddit.

Right now recommend keep Teddit preferences empty, and dark theme. Changes to Teddit may break the script, please let me know if it does.

Access to javascript you can audit in advance. Turn auto-update off, audit before update.

On a Teddit instance if there were any javascript you would have no guarantee the host didn't modify any included javascript at random one day.

You have no guarantees about their backend in regards to tracking, but at least you won't be part of someone's mining botnet.

Not even Forced Anonymous and Hide Points can save this irredeemably useless platform from being destroyed by its own design,
but at least this script will make it ever so slightly more bearable when you are forced to read a Reddit thread for whatever reason.

## Features

Most features can easily be toggled on or off.

- Expand Images toggle.
- Auto expand all.
- Image click and drag resize.
- Fixed navbar with navigation links.
- Choose favorite subs for navbar.
- Forced Anonymous
- Hide Points
- Less branding

[Demo extension with included features for User IDs and colorized IDs](https://gf.qytechs.cn/en/scripts/452561-betterteddit-extension-user-ids).

*/

/* === CHANGELOG.md ===
# Changelog

### v1.3.0
- Favorite subs in navbar.
- Toggle for [Home] and [Top][Bottom] links in header.
- Fixed #bottom.

### v1.2.0 2022-10-08
- Prev/Next links always visible to preserve muscle memory consistency for anything after.
- Moving topbar navigation links to the floating header.
- icon added to userscript meta.
- Added runtime performance timers.
- Fixed content loading event not firing... Probably...

### v1.1.0 2022-10-07
- Two Image Resizing bugs have been dealt with.
This changes the image containers when browsing sub feeds.

### v1.0-Alpha Release
*/

/* === KNOWNBUGS.md ===
# Known Bugs
- ~~DOMContentLoaded very rarely does not fire.~~ Might be fixed..?
*/
var start_totaltime = performance.now();
var events = [];
var header, header_left, header_right;
var nav_home, nav_top, nav_bottom;
var top_links;
var expanded = false;
var images_button;
var pref_button;
var pref_container = false;
var current_sub;
var fav_links;

// These are just defaults, change the GM values or use the new preferences button in the header.
const config = {
  expand_images: {
    name: "Expand Images Button",
    desc: "Button to toggle expand all images.",
    default: true
  },
  auto_expand_images: {
    name: "Auto Expand Images",
    desc: "Expands all images automatically.",
    default: false
  },
  image_resizer: {
    name: "Image Resizer",
    desc: "Click and drag to resize images.",
    default: true
  },
  show_favorites: {
    name: "Show Favorites",
    desc: "Replace default / subscription links with your favorites.",
    default: true
  },
  header_home: {
    name: "Home",
    desc: "Show [Home] link in the navigation bar",
    default: true
  },
  header_topbottom: {
    name: "Top/Bottom",
    desc: "Show [Top][Bottom] links in the navigation bar",
    default: true
  },
  forced_anonymous: {
    name: "Forced Anonymous",
    desc: "Replace names with Anonymous.",
    default: true
  },
  hide_points: {
    name: "Hide Points",
    desc: "Hides all voting points.",
    default: true
  },
  hide_user_flair: {
    name: "Hide User Flairs",
    desc: "Hides all user flairs.",
    default: true
  },
  hide_submission_flair: {
    name: "Hide Submission Flairs",
    desc: "Hides all submission flairs.",
    default: true
  },
  unbranding: {
    name: "Unbranding",
    desc: "Remove superflous site branding.",
    default: true
  },
  navigator: {
    name: "Navigation Links",
    desc: "Adds [Prev] and [Next] links to header.",
    default: true
  },
  event_mode: {
    name: "Event Mode",
    desc: "Dispatches events for BetterTeddit extensions.",
    default: true
  },
  debug_mode: {
    name: "Debug Mode",
    desc: "Enables console.log(), useful for developers and debugging.",
    default: true
  }
}

function insert_before(new_node, existing_node) {
  existing_node.parentNode.insertBefore(new_node, existing_node);
}

function log(message) {
  if (GM_getValue("debug_mode") == true) {
    if (typeof message == "string") {
      console.log("BetterTeddit: " + message);
    } else {
      console.log(message);
    }
  }
}

function dispatch_event(event_name) {
  if (GM_getValue("event_mode") != true) {
    return false;
  }
  if (events[""+event_name+""] == undefined) {
    events.push(""+event_name+"");
    events[""+event_name+""] = new CustomEvent(""+event_name+"", {
      detail: {
        event_name: ""+event_name+""
      }
    });
    window.addEventListener(""+event_name+"", (event) => {
      log("Dispatched Event: "+event_name+".");
    });
  }
  window.dispatchEvent(events[""+event_name+""]);
}

log("Start of Script");
dispatch_event("before_startup");

function init_values() {
  dispatch_event("before_init_values");
  function check_value(key, value) {
    if (GM_getValue(key) == undefined) {
      GM_setValue(key, value);
      log("Undefined, Setting default: [" + key + "]");
      log(value);
    } else {
      log("Value found, Getting value: [" + key + "]");
      log(GM_getValue(key));
    }
  }
  Object.entries(config).forEach( (key, value) => {
    check_value(key[0], key[1]["default"]);
  });
  dispatch_event("after_init_values");
} init_values();

function add_style() {
  dispatch_event("before_add_style");

  style = `
    html {
     --header-size: 10pt;
     --header-color: rgb(117, 117, 117);
     --header-background: #252525;
     --header-border-bottom: 1px solid #000;
     --header-box-shadow: 0 0 2px 0 #000;
     --header-a-color: rgb(199, 199, 199);
     --header-a-color-hover: #fff;
     --pref-color: #cacaca;
    }

    body {
      margin: calc(var(--header-size) + 4px) 0 0 0;
      padding: 0;
      /*background: #0F0F0F;*/
      font-family: sans-serif;
    }

    a.noclick {
      text-decoration: none !important;
      color: inherit !important;
      font-size: inherit!important;
    }

    div#bt_header {
      line-height: 1.1;
      font-size: var(--header-size);
      color: var(--header-color);
      display: flex;
      justify-content: space-between;
      width: 100%;
      position: fixed;
      top: 0;
      background: var(--header-background);
      z-index: 1000;
      padding: 2px 4px;
      border-bottom: var(--header-border-bottom);
      box-shadow: var(--header-box-shadow);
    }
    div#bt_header a, div#bt_header a:visited, div#pref_preferences a, div#pref_preferences a:visited {
      cursor: pointer;
      color: var(--header-a-color);
      text-decoration: none;
    }
    div#bt_header a:hover, div#pref_preferences a:hover {
      color: var(--header-a-color-hover);
    }
    div#bt_header span {
      display: inline-flex;
    }
    span.header_bracket {
      display: inline-flex;
      margin: 0 2px;
      -webkit-user-select:none;
      user-select: none;
    }
    span#bt_header_right {
      padding-right: 1em;
    }

    #pref_container {
      z-index: 100;
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
    }
    #pref_backdrop {
      z-index: 100;
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background: rgba(0, 0, 0, 0.5);
    }
    #pref_preferences {
      z-index: 1000;
      position: fixed;
      top: calc( var(--header-size) + 10px);
      left: 0;
      right: 0;
      max-width: 500px;
      min-width: 500px;
      min-height: 500px;
      margin: auto;
      color: var(--pref-color);
      background: var(--header-background);
      border: var(--header-border-bottom);
      box-shadow: var(--header-box-shadow);
    }
    #pref_nav_container {
      display: flex;
      margin: 10px;
      border-bottom: 1px solid #000;
      padding-bottom: 2px;
      font-size: 10pt;
    }
    #pref_nav, #pref_nav_right {
      display:flex;
    }
    #pref_nav > span, #pref_nav_right > span {
      margin: 0 4px;
    }
    #pref_nav span[selected], #pref_nav_right span[selected] {
      font-weight:bold;
    }
    #pref_nav_right {
      margin: 0 1em 0 auto;
    }
    #pref_close {
      font-size: 1.5em;
      font-weight: bold;
      line-height: 0.5;
    }
    #pref_content {
      margin: 1em;
      max-height: 500px;
      padding-bottom: 1em;
      overflow-y: scroll;
    }
    #pref_content fieldset {
      border: 1px solid #000;
      font-size: 0.8em;
      margin-bottom: 0.5em;
    }
    #pref_content a {
      text-decoration: underline!important;
    }
    #pref_content fieldset legend {
      margin-left: 0.5em;
      font-weight: bold;s
    }
    #pref_content ul {
      list-style: none;
      margin-left: 1em;
    }
    #pref_content li input, #pref_content li label, #pref_content li span {
      margin: 0 0.1em;
    }
    #pref_preferences label {
      -webkit-user-select: none;
              user-select: none;
    }
    #pref_preferences label::after {
      content: ':'
    }

    .bt_image_container {
      cursor: default;
    }
    .bt_image {
      cursor: pointer;
    }

    .bt_top_links {
      width: 100%;
      float: left;
      overflow: hidden;
      position: relative;
    }
    .bt_top_links a {
      text-transform: uppercase;
      margin: 0 0.15em;
    }
    .bt_top_links a#sr-more-link {
      background: var(--header-background);
      padding: 0 1em;
      margin: 0;
      box-shadow: none;
    }

  `;
  GM_addStyle(style);

  dispatch_event("after_add_style");
}

function create_header() {
  dispatch_event("before_create_header");
  header = document.createElement("div");
  header.id = "bt_header";

  header_left = document.createElement("span");
  header_left.id = "bt_header_left";
  header.appendChild(header_left);
  header_right = document.createElement("span");
  header_right.id = "bt_header_right";
  header.appendChild(header_right);

  if (GM_getValue("header_home") == true) {
    nav_home = document.createElement("span");
    nav_home.id = "header_home";
    nav_home.classList.add("header_bracket");
    nav_home.innerHTML = `[<a href="/">Home</a>]`;
    header_left.appendChild(nav_home);
  }

  if (GM_getValue("header_topbottom") == true) {
    nav_top = document.createElement("span");
    nav_top.id = "header_top";
    nav_top.classList.add("header_bracket");
    nav_top.innerHTML = `[<a href="#top">Top</a>]`;
    nav_bottom = document.createElement("span");
    nav_bottom.id = "header_bottom";
    nav_bottom.classList.add("header_bracket");
    nav_bottom.innerHTML = `[<a href="#bottom">Bottom</a>]`;
    header_left.appendChild(nav_top);
    header_left.appendChild(nav_bottom);
  }

  dispatch_event("during_create_header"); // After fully defined, but before placed on page.
  document.body.prepend(header);
  dispatch_event("after_create_header");

}

function user_tag() {
  dispatch_event("before_user_tag");
  var users = document.querySelectorAll("div.link p.submitted a,div.title p.submitted a,div.comment p.author a:not([title='submitter'])");
  [...users].forEach( (user) => {
    if (user.textContent == "[deleted]") {
      return false;
    }
    user.setAttribute("user", user.getAttribute("href"));
  });
  dispatch_event("after_user_tag");
}

function move_links() {
  dispatch_event("before_move_links");
  var threads = document.querySelectorAll("div.entry");
  [...threads].forEach( (thread) => {
    var links_container = document.createElement("div");
    links_container.classList.add("links_container");
    var links = thread.querySelectorAll("div.links > a, div.links > span.tag");
    [...links].forEach( (link) => {
      links_container.appendChild(link);
    });
    thread.querySelector("div.links").prepend(links_container);
  });
  dispatch_event("after_move_links");
}

function move_topbar_links() {
  dispatch_event("before_move_topbar_links");
  top_links = document.querySelector("#topbar .top-links");
  if (top_links != null) {
    insert_before(top_links, header_right);
    top_links.classList.add("bt_top_links");
    top_links.classList.remove("top-links");
  }
  dispatch_event("after_move_topbar_links");
}

function move_details() {
  dispatch_event("before_move_details");
  var threads = document.querySelectorAll("div.entry");
  [...threads].forEach( (thread) => {
    var details_container = document.createElement("div");
    details_container.classList.add("details_container");
    var details = thread.querySelectorAll("div.links > details, div.links > style");
    [...details].forEach( (detail) => {
      details_container.appendChild(detail);
    });
    thread.querySelector("div.links").appendChild(details_container);
  });
  dispatch_event("after_move_details");
}

function forced_anonymous() {
  if (GM_getValue("forced_anonymous") != true) {
    return false;
  }
  dispatch_event("before_forced_anonyous");
  var users = document.querySelectorAll("a[user]");
  [...users].forEach( (user) => {
    if (user.textContent == "[deleted]") {
      return false;
    }
    user.removeAttribute("href");
    user.textContent = "Anonymous";
    user.classList.add("noclick");
  });
  dispatch_event("after_forced_anonymous");
}

function hide_points() {
  if (GM_getValue("hide_points") != true) {
    return false;
  }
  dispatch_event("before_hide_points");
  var style = document.createElement("style");
  style.innerHTML = ".score, .upvotes {visibility: hidden}.ups {display: none!important;}";
  style.id = "bt_hide_points";
  document.head.appendChild(style);
  GM_addValueChangeListener("hide_points", function() {
    if (arguments[2] == true) {
      style.innerHTML = ".score, .upvotes {visibility: hidden}.ups {display: none!important;}";
    } else {
      style.innerHTML = "/* hide_points disabled */";
    }
  });
  dispatch_event("after_hide_points");
}

function hide_user_flair() {
  if (GM_getValue("hide_user_flair") != true) {
    return false;
  }
  dispatch_event("before_hide_user_flair");
  var style = document.createElement("style");
  style.innerHTML = "p.submitted .flair, .meta p .flair { display: none!important; }";
  style.id = "bt_hide_user_flair";
  document.head.appendChild(style);
  GM_addValueChangeListener("hide_user_flair", function() {
    if (arguments[2] == true) {
      style.innerHTML = "p.submitted .flair, .meta p .flair { display: none!important; }";
    } else {
      style.innerHTML = "/* hide_user_flair disabled */";
    }
  });
  dispatch_event("after_hide_user_flair");
}

function hide_submission_flair() {
  if (GM_getValue("hide_submission_flair") != true) {
    return false;
  }
  dispatch_event("before_hide_submission_flair");
  var style = document.createElement("style");
  style.innerHTML = ".title .flair { display: none!important; }";
  style.id = "bt_hide_submission_flair";
  document.head.appendChild(style);
  GM_addValueChangeListener("hide_submission_flair", function() {
    if (arguments[2] == true) {
      style.innerHTML = ".title .flair { display: none!important; }";
    } else {
      style.innerHTML = "/* hide_submission_flair disabled */";
    }
  });
  dispatch_event("after_hide_submission_flair");
}

function unbranding() {
  if (GM_getValue("unbranding") != true) {
    return false;
  }
  dispatch_event("before_unbranding");

  var teddit_text = document.querySelector("header a.main");
  if (teddit_text != null) { teddit_text.remove(); }

  var subreddit_text = document.querySelector("header div p");
  if (subreddit_text != null) { subreddit_text.remove(); }

  var sub_link = document.querySelector("header div a p");
  if (sub_link != null) { sub_link.textContent = sub_link.textContent.substring(3); }

  var sub_title = document.querySelector("div#sidebar div.content div.heading p.title");
  if (sub_title != null) { document.title = sub_title.textContent; }

  var teddit_intro = document.getElementById("intro");
  if (teddit_intro != null) { teddit_intro.remove(); }

  GM_addValueChangeListener("unbranding", function() {
    if (arguments[2] == true) {

    } else {

    }
  });
  dispatch_event("after_unbranding");
}

function expand_images() {
  dispatch_event("before_expand_images")

  //var images = document.querySelectorAll("div.links details.preview-container"); //images only
  var images = document.querySelectorAll("div.links details"); //text too
  log("Found "+images.length+" images.");
  [...images].forEach( (image) => {
    if (expanded != true) {
      image.setAttribute("open", "open");
    } else {
      image.setAttribute("open", "");
      image.removeAttribute("open");
    }
  });
  expanded = expanded?false:true;

  dispatch_event("after_expand_images");
}

function expand_images_button() {
  if (GM_getValue("expand_images") != true) {
    return false;
  }
  dispatch_event("before_expand_images_button");

  images_button = document.createElement("span");
  images_button.id = "header_images_button";
  images_button.classList.add("header_bracket");
  images_button.innerHTML = `
    [<a>Expand</a>]
  `;
  images_button.addEventListener("click", expand_images);

  header_left.appendChild(images_button);

  GM_addValueChangeListener("expand_images", function() {
    if (arguments[2] == true) {
      images_button.style.display = "unset";
    } else {
      images_button.style.display = "none";
    }
  });

  dispatch_event("after_expand_images_button");
}

function auto_expand_images() {
  if (GM_getValue("auto_expand_images") != true) {
    return false;
  }
  dispatch_event("before_auto_expand_images");
  expand_images(true);
  dispatch_event("after_auto_expand_images");
}

function image_resizer() {
  if (GM_getValue("image_resizer") != true) {
    return false;
  }
  dispatch_event("before_image_resizer");

  function resize(image, event) {
    log("Image Resize: Mouse Down");
    event.preventDefault();
    var mousedown = true;
    var imageX, imageY, posX, posY, newposX, newposY, diffX, diffY, newimageX, newimageY;

    image.style.minWidth = "25px";
    image.style.minHeight = "25px";
    imageX = image.offsetWidth;
    imageY = image.offsetHeight;
    posX = event.clientX;
    posY = event.clientY;
    //log(imageX); log(imageY);
    //log(posX);   log(posY);
    image.style.width = imageX + "px";
    image.style.height = imageY + "px";
    image.style.maxWidth = "unset";
    image.style.maxHeight = "unset";

    function mousemove(e) {
      if (mousedown == false) {
        log("Image Resize: Mouse Down is false.");
      }
      log("Image Resize: Mouse Move");
      newposX = e.clientX;
      newposY = e.clientY;
      diffX = newposX - posX;
      diffY = newposY - posY;
      newimageX = imageX + diffX + diffY;
      newimageY = imageY + diffX + diffY;
      image.style.width = newimageX + "px";
      image.style.height = "auto";
    }
    window.addEventListener("mousemove", mousemove);

    function mouseup() {
      mousedown = false;
      log("Image Resize: Mouse Up");
      window.removeEventListener("mouseup", mouseup);
      window.removeEventListener("mousemove", mousemove);
    }
    window.addEventListener("mouseup", mouseup );

  }
  // Sub images
  var sub_images = document.querySelectorAll("details.preview-container");
  [...sub_images].forEach( (details) => {
    details.querySelector("div.preview").remove();
    details.style.zIndex = 2;
    details.style.position = "relative";
    details.classList.remove("preview-container");
    details.classList.add("bt_image_container");
    var image_div = document.createElement("div");
    image_div.classList.add("bt_image_container");
    var image = document.createElement("img");
    image.classList.add("bt_image");
    image.src = details.getAttribute("data-url");
    image.style.opacity = 1;
    image_div.appendChild(image);
    details.appendChild(image_div);
    image.style.maxWidth = "100%";
    image.style.maxHeight = "600px";
    image.style.width = "auto";
    image.style.height = "100%";
    image.addEventListener("mousedown", (event) => {
      if (event.which != "1") {
        return; // no right click
      }
      resize(image, event); //set size first
      image.classList.add("image_resized");
    });
  });

  //Thread images
  var thread_images = document.querySelectorAll("div#post div.image");
  [...thread_images].forEach( (image) => {
    var a = image.querySelector("a");
    a.addEventListener("click", (event) => {
      event.preventDefault();
    });
    var img = image.querySelector("a img");
    img.addEventListener("mousedown", (event) => {
      if (event.which != "1") {
        return; // no right click
      }
      resize(img, event);
      img.classList.add("image_draggable");
    });
  });

  dispatch_event("after_image_resizer");
}

function navigator() {
  if (GM_getValue("navigator") != true) {
    return false;
  }
  dispatch_event("before_navigator");
  var morelinks = document.querySelector(".view-more-links");

  var navigator_container = document.createElement("span");
  navigator_container.id = "header_navigator_container";

  if (morelinks != null) {
    var links = document.querySelectorAll(".view-more-links a");
    var prev = document.createElement("span");
    prev.classList.add("header_bracket");
    var next = document.createElement("span");
    next.classList.add("header_bracket");
    if (links.length > 1) {
      prev.innerHTML = "[<a href='"+links[0].getAttribute('href')+"'>Prev</a>]";
      next.innerHTML = "[<a href='"+links[1].getAttribute('href')+"'>Next</a>]";
    } else {
      prev.innerHTML = "[Prev]";
      next.innerHTML = "[<a href='"+links[0].getAttribute('href')+"'>Next</a>]";
    }
    navigator_container.appendChild(prev);
    navigator_container.appendChild(next);
  } else {
    var prev = document.createElement("span");
    prev.classList.add("header_bracket");
    prev.innerHTML = "[Prev]";
    var next = document.createElement("span");
    next.classList.add("header_bracket");
    next.innerHTML = "[Next]";
    navigator_container.appendChild(prev);
    navigator_container.appendChild(next);
  }
  header_left.appendChild(navigator_container);
  dispatch_event("during_navigator");
}

function bottom_link() { // Run after document_loaded
  dispatch_event("before_bottom_link");
  var bottom = document.querySelector("footer");
  if (bottom != null) {
    bottom.id = "bottom";
  } else {
    log("No footer found.");
  }
  dispatch_event("after_bottom_link");
}

function get_current_sub() {
  if (window.location.pathname.split('/')[1].toLowerCase() == "r") {
    current_sub = window.location.pathname.split('/')[2].toLowerCase();
    log("Current sub: "+current_sub+".");
  }
}

function show_favorites() {
  dispatch_event("before_show_favorites");
  if (GM_getValue("show_favorites") != true ) {
    return false;
  }

  if (GM_getValue("list_favorites") == null) {
    GM_setValue("list_favorites", []);
  }

  function is_favorite_sub(subname) {
    var subs = GM_getValue("list_favorites");
    var _subname = subname.toLowerCase()
    if ( Array.from(subs).includes(_subname) ) {
      return true;
    } else {
      return false;
    }
  }

  if (current_sub) {
    var is_current_fav = is_favorite_sub(current_sub);
  }

  function toggle_favorite_sub(subname) {
    dispatch_event("before_toggle_favorite_sub");
    var subs = GM_getValue("list_favorites");
    var _subname = subname.toLowerCase()
    var newsubs;
    log(Array.from(subs));
    if ( !Array.from(subs).includes(_subname) ) {
      newsubs = Array.from(subs);
      newsubs.push(_subname);
      GM_setValue("list_favorites", newsubs);
      log("Added "+_subname+" to favorites.");
    } else {
      newsubs = Array.from(subs).filter( (sub) => {
        return sub != _subname;
      });
      GM_setValue("list_favorites", newsubs);
      log("Removed "+_subname+" from favorites.");
    }
    log(Array.from(GM_getValue("list_favorites")));
    dispatch_event("after_toggle_favorite_sub");
  }

  function favorite_sub_link() {
    dispatch_event("before_favorite_sub_link");

    var fav_style = `
      a#bt_toggle_favorite {
        text-decoration: none;
        cursor: pointer;
        font-size: 16pt;
        vertical-align: middle;
        line-height: 1;
        -webkit-user-select: none;
                user-select: none;
      }
      span.toggle_list {
        display: inline!important;
      }
      a.toggle_list {
        margin: 0!important;
        font-weight: bold;
      }
      `;
    GM_addStyle(fav_style);

    var subscribe_container = document.querySelector("#sidebar .subscribe.content");
    if (subscribe_container != null) {
      var favorite_link = document.createElement("a");
      favorite_link.id = "bt_toggle_favorite";

      function change_fav_link() {
        if (is_current_fav == false) {
          favorite_link.classList.add("is_not_favorite");
          favorite_link.classList.remove("is_favorite");
          favorite_link.innerHTML = "♡";
          favorite_link.title = "Add "+current_sub+" to favorites.";
        } else {
          favorite_link.classList.add("is_favorite");
          favorite_link.classList.remove("is_not_favorite");
          favorite_link.innerHTML = "♥";
          favorite_link.title = "Remove "+current_sub+" from favorites.";
        }
      }
      change_fav_link();

      favorite_link.addEventListener("click", () => {
        toggle_favorite_sub(current_sub);
        is_current_fav = is_current_fav?false:true;
        change_fav_link();
        update_fav_nav_links();
      });

      subscribe_container.appendChild(favorite_link);
    }
    dispatch_event("after_favorite_sub_link");
  }

  function create_fav_nav_links() {
    fav_links = document.createElement("div");
    fav_links.id = "bt_fav_nav_links";
    fav_links.classList.add("bt_top_links");
    update_fav_nav_links();
    insert_before(fav_links, header_right);
    top_links.style.display = "none";
  }

  function make_show_favs() {
    var show_favs = document.createElement("span");
    show_favs.classList.add("header_bracket");
    show_favs.classList.add("toggle_list");
    show_favs.innerHTML = `[<a class="toggle_list show_favs">―</a>]`; //Fullwidth Hyphen-Minus, &#65293;
    show_favs.title = "Show Favorites";
    show_favs.addEventListener("click", () => {
      top_links.style.display = "none";
      fav_links.style.display = "block";
      log("Toggle Show Favorites");
    });
    top_links.prepend(show_favs);
  }

  function make_show_subs() {
    var show_subs = document.createElement("span");
    show_subs.classList.add("header_bracket");
    show_subs.classList.add("toggle_list");
    show_subs.innerHTML = `[<a class="toggle_list show_subs">+</a>]`; // Fullwidth Plus &#65291;
    show_subs.title = "Show Subscriptions";
    show_subs.addEventListener("click", () => {
      top_links.style.display = "block";
      fav_links.style.display = "none";
      log("Toggle Show Subscriptions");
    });
    fav_links.prepend(show_subs);
  }

  function update_fav_nav_links() {
    var html = `<a href="/r/popular">Popular</a><a href="/r/all">All</a>`;
    var subs = GM_getValue("list_favorites");
    Array.from(subs).forEach((sub) => {
      html = html + `<a href="/r/${sub}">${sub}</a>`;
    });
    fav_links.innerHTML = html;
    make_show_favs();
  }

  favorite_sub_link();
  create_fav_nav_links();
  make_show_subs();

  dispatch_event("after_show_favorites");
}

function create_toggle(config_name) {
  var _toggle = config[config_name];

  var _li = document.createElement("li");
  _li.id = "pref_li_"+config_name+"";

  var _checkbox = document.createElement("input");
  _checkbox.id = "pref_check_"+config_name+"";
  _checkbox.setAttribute("type", "checkbox");
  _li.appendChild(_checkbox);

  if (GM_getValue(config_name) == true) {
    _checkbox.checked = true;
  }

  GM_addValueChangeListener(""+config_name+"", function(key, old_val, new_val, remote) {
    if (arguments[2] == true) {
      _checkbox.checked = true;
    } else {
      _checkbox.checked = false;
    }
    log("Set GM Value "+config_name+" to "+arguments[2]+".");
  });

  _checkbox.addEventListener("change", (event) => {
    if (_checkbox.checked == true) {
      GM_setValue(""+config_name+"", true);
    } else {
      GM_setValue(""+config_name+"", false);
    }
    log("Set "+config_name+" config checkbox to "+_checkbox.checked+".");
  });

  var _label = document.createElement("label");
  _label.id = "pref_label_"+config_name+"";
  _label.setAttribute("for", _checkbox.id);
  var _a = document.createElement("a");
  _a.textContent = _toggle["name"];
  _label.appendChild(_a);
  _li.appendChild(_label);

  var _desc = document.createElement("span");
  _desc.id = "pref_desc_"+config_name+"";
  _desc.textContent = _toggle["desc"];
  _li.appendChild(_desc);

  return _li;
}

function open_preferences() {

  if (pref_container != false) {
    if (pref_container.style.display != "block") {
      dispatch_event("before_show_preferences");
      pref_container.style.display = "block";
      dispatch_event("after_show_preferences");
    } else {
      hide_preferences();
    }
    return false;
  }

  dispatch_event("before_open_preferences");
  pref_container = document.createElement("div");
  pref_container.id = "pref_container";
  pref_container.style.display = "block";

  var pref_backdrop = document.createElement("div");
  pref_backdrop.id = "pref_backdrop";
  pref_backdrop.addEventListener("click", hide_preferences);
  pref_container.appendChild(pref_backdrop);

  var pref_preferences = document.createElement("div");
  pref_preferences.id = "pref_preferences";

  var pref_nav_container = document.createElement("div");
  pref_nav_container.id = "pref_nav_container";

  var pref_nav = document.createElement("span");
  pref_nav.id = "pref_nav";
  pref_nav.innerHTML = `
    <span tab="general" selected=""><a>General</a></span>
    <span tab="style"><a>Style</a></span>
  `;
  pref_nav_container.appendChild(pref_nav);

  var pref_nav_right = document.createElement("span");
  pref_nav_right.id = "pref_nav_right";
  pref_nav_right.innerHTML = `
    <span tab="storage"><a>Storage</a></span>
  `;
  pref_nav_container.appendChild(pref_nav_right);

  var pref_close = document.createElement("span");
  pref_close.id = "pref_close";
  pref_close.innerHTML = `<a aria-label="Close Preferences">×</a>`;
  pref_close.addEventListener("click", hide_preferences);
  pref_nav_container.appendChild(pref_close);

  pref_preferences.appendChild(pref_nav_container);

  var pref_content = document.createElement("div");
  pref_content.id = "pref_content";
  // TABS IN HERE

  var general_tab = document.createElement("div");
  general_tab.id = "pref_general_content";
  general_tab.setAttribute("tab", "general");
  pref_content.appendChild(general_tab);

    //IMAGES
  var pref_images_fieldset = document.createElement("fieldset");
  pref_images_fieldset.id = "pref_images_fieldset";
  general_tab.appendChild(pref_images_fieldset);
  var pref_images_legend = document.createElement("legend");
  pref_images_legend.textContent = "Images";
  pref_images_fieldset.appendChild(pref_images_legend);
  var pref_images_ul = document.createElement("ul");
  pref_images_fieldset.appendChild(pref_images_ul);
  pref_images_ul.appendChild( create_toggle("expand_images") );
  pref_images_ul.appendChild( create_toggle("auto_expand_images") );
  pref_images_ul.appendChild( create_toggle("image_resizer") );
    //NAVIGATION
  var pref_navigation_fieldset = document.createElement("fieldset");
  pref_navigation_fieldset.id = "pref_navigation_fieldset";
  general_tab.appendChild(pref_navigation_fieldset);
  var pref_navigation_legend = document.createElement("legend");
  pref_navigation_legend.textContent = "Navigation";
  pref_navigation_fieldset.appendChild(pref_navigation_legend);
  var pref_navigation_ul = document.createElement("ul");
  pref_navigation_fieldset.appendChild(pref_navigation_ul);
  pref_navigation_ul.appendChild( create_toggle("header_home") );
  pref_navigation_ul.appendChild( create_toggle("header_topbottom") );
  pref_navigation_ul.appendChild( create_toggle("navigator") );
  pref_navigation_ul.appendChild( create_toggle("show_favorites") );
    //MISCELLANEOUS
  var pref_misc_fieldset = document.createElement("fieldset");
  pref_misc_fieldset.id = "pref_misc_fieldset";
  general_tab.appendChild(pref_misc_fieldset);
  var pref_misc_legend = document.createElement("legend");
  pref_misc_legend.textContent = "Miscellaneous";
  pref_misc_fieldset.appendChild(pref_misc_legend);
  var pref_misc_ul = document.createElement("ul");
  pref_misc_fieldset.appendChild(pref_misc_ul);
  pref_misc_ul.appendChild( create_toggle("forced_anonymous") );
  pref_misc_ul.appendChild( create_toggle("hide_points") );
  pref_misc_ul.appendChild( create_toggle("hide_user_flair") );
  pref_misc_ul.appendChild( create_toggle("hide_submission_flair") );
  pref_misc_ul.appendChild( create_toggle("unbranding") );
  pref_misc_ul.appendChild( create_toggle("event_mode") );
  pref_misc_ul.appendChild( create_toggle("debug_mode") );

  pref_preferences.appendChild(pref_content);
  pref_container.appendChild(pref_preferences);

  dispatch_event("during_open_preferences");
  document.body.appendChild(pref_container);

  /*window.addEventListener("create_new_tab", (event) => {
    //event.detail
  });
  window.addEventListener("create_new_toggle", (event) => {
    //event.detail.name, event.detail.tab, event.detail.fieldset
    var tab = document.getElementById("pref_"+event.detail.tab+"_content");
    var fieldset = document.getElementById("pref_"+event.detail.fieldset+"_fieldset");
    var ul = fieldset.querySelector("ul");
    ul.appendChild( create_toggle("user_ids") );
  });*/ // actually this was a really cool unfinished idea to reuse code, but would need to replace GM with localstorage to share values with other scripts

  dispatch_event("after_open_preferences");
}

function hide_preferences() {
  dispatch_event("before_hide_preferences")
  pref_container.style.display = "none";
  dispatch_event("after_hide_preferences");
}

function preferences_button() {
  dispatch_event("before_preferences_button");
  pref_button = document.createElement("span");
  pref_button.id = "pref_button";
  pref_button.classList.add("header_bracket");
  pref_button.innerHTML = `
    [<a href="javascript:void">Config</a>]
  `;
  pref_button.addEventListener("click", () => {
    open_preferences();
  });
  header_right.appendChild(pref_button);
  dispatch_event("after_preferences_button");
}

dispatch_event("before_document_loaded");

function DOMContentLoaded() {
  dispatch_event("during_document_loaded");
  var end_totaltime = performance.now();
  var start_runtime = performance.now();
  // START NATIVE ONDOMLOAD FUNCTIONS
  add_style();
  get_current_sub();
  create_header();
  open_preferences();
  hide_preferences();
  preferences_button();
  bottom_link();
  user_tag();
  move_links();
  move_details();
  move_topbar_links();

  expand_images_button();
  auto_expand_images();
  image_resizer();
  navigator();
  forced_anonymous();
  hide_points();
  hide_user_flair();
  hide_submission_flair();
  unbranding();
  show_favorites();
  // ENDOF NATIVE ONDOMLOAD FUNCTIONS

  dispatch_event("after_document_loaded");
  dispatch_event("after_startup");

  log("BetterTeddit: "+runtype+". If loading, added eventlistener for complete, else run. Probably cache speed is difference."); //
  var end_runtime = performance.now();
  var totaltime = end_totaltime - start_totaltime;
  var runtime = end_runtime - start_runtime;

  log("BetterTeddit: Wait for DOMContentLoaded: "+totaltime+"ms."); // Parsing of native Teddit HTML is included.
  log("BetterTeddit: After DOMContentLoaded: "+runtime+"ms."); //BetterTeddit modifying runtime performance.
  log("End of Script");
}
var runtype = 0;
if (document.readyState != "loading") {
  runtype = document.readyState;
  //alert(runtype);  //for testing purposes on rare load type interactive/complete.
  DOMContentLoaded();
} else {
  runtype = document.readyState;
  document.addEventListener("DOMContentLoaded", DOMContentLoaded);
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址