DailyCrosswordLinks Filtering

Filters links from Daily Crossword Links (e.g., subscription-only, appstore only, etc.)

当前为 2025-04-17 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        DailyCrosswordLinks Filtering
// @namespace   https://github.com/Inevitabby/DailyCrosswordLinks-Filtering/raw/refs/heads/main/script.user.js
// @match       https://dailycrosswordlinks.com/*
// @grant       none
// @version     1.2
// @author      Inevitabby
// @description Filters links from Daily Crossword Links (e.g., subscription-only, appstore only, etc.)
// @license     Unlicense; https://unlicense.org/
// ==/UserScript==
(function () {
  "use strict";
  const CONFIG = {
    hidePaid: {
      action: "remove", // valid actions: "remove", "mark", "none"
    },
    hideApp: {
      action: "remove",
    },
    hideCryptic: {
      action: "remove",
    },
    hideFiletype: {
      action: "mark",
      whitelist: [ ".puz" ]
    },
  };

  // ===============================
  // === Entry Testing Functions ===
  // ===============================

  // Returns if entry requires subscription or purchase
  function hidePaid(elem) {
    const strong = elem.querySelector("strong");
    return strong && strong.textContent.includes(": ($)");
  }
  // Returns if entry requires appstore (exclusively)
  function hideApp(elem) {
    const links = elem.querySelectorAll("a");
    return links.length > 0 && Array.from(links).every(link =>
      link.href.startsWith("https://play.google.com") ||
      link.href.startsWith("https://apps.apple.com")
    );
  }
  // Returns if entry is a cryptic
  function hideCryptic(elem) {
    const strong = elem.querySelector("strong");
    return strong && strong.textContent.includes("Cryptic");
  }
  // Returns if entry lacks wanted filetypes
  function hideFiletype(elem) {
    const em = elem.querySelector("em");
    if (em === null) return true;
    return !CONFIG.hideFiletype.whitelist.some((ft) => em.textContent.includes(ft));
  }
  // Local function map
  const MAP = {
    hidePaid,
    hideApp,
    hideCryptic,
    hideFiletype,
  };

  // ====================================
  // === Entry Modification Functions ===
  // ====================================

  // Delete an entry
  function remove(elem) {
    while (elem) {
      const next = elem.nextSibling;
      elem.remove();
      if (elem.nodeName === "BR") break;
      elem = next;
    }
  }
  // Mark an entry
  function mark(elem) {
    while (elem) {
      const next = elem.nextSibling;
      if (elem.style) elem.style.opacity = "0.5";
      if (elem.nodeName === "BR") break;
      elem = next;
    }
  }

  // ============================
  // === Iterate over Entries ===
  // ============================

  // Scan through all entries
  const entries = document.querySelectorAll("span.fetched, span.unfetched");
  entries.forEach((entry) => {
    const matchKey = Object.keys(CONFIG).find((key) => {
      return MAP[key](entry);
    });
    if (!matchKey) return;
    const { action } = CONFIG[matchKey];
    if (action === "remove") remove(entry);
    if (action === "mark") mark(entry);
  });

})();