您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
trace browsing history and preserve tables (HTML passthrough)
当前为
- // ==UserScript==
- // @name MemTrace
- // @namespace Violentmonkey Scripts
- // @version 0.4
- // @description trace browsing history and preserve tables (HTML passthrough)
- // @author fankaidev
- // @match *://*/*
- // @exclude *://cubox.pro/*
- // @exclude *://localhost:*/*
- // @exclude *://127.0.0.1:*/*
- // @grant GM_xmlhttpRequest
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_registerMenuCommand
- // @require https://cdnjs.cloudflare.com/ajax/libs/turndown/7.1.1/turndown.min.js
- // @require https://unpkg.com/turndown-plugin-gfm@1.0.2/dist/turndown-plugin-gfm.js
- // @require https://cdn.jsdelivr.net/npm/dompurify@3.1.5/dist/purify.min.js
- // @require https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/crypto-js.js
- // @require https://cdn.jsdelivr.net/npm/@mozilla/readability@0.5.0/Readability.min.js
- // @license MIT
- // ==/UserScript==
- (function () {
- "use strict";
- function md5(input) {
- return CryptoJS.MD5(input).toString();
- }
- let hrefHistory = [];
- // Function to get or initialize global state
- function getGlobalState(key, defaultValue) {
- return GM_getValue(key, defaultValue);
- }
- // Function to update global state
- function updateGlobalState(key, value) {
- GM_setValue(key, value);
- }
- // Function to get the endpoint
- function getEndpoint() {
- let endpoint = getGlobalState("endpoint", null);
- if (!endpoint) {
- endpoint = prompt("[MemTrace] Please enter the endpoint URL:", "https://api.example.com/endpoint");
- if (endpoint) {
- updateGlobalState("endpoint", endpoint);
- } else {
- console.error("[MemTrace] No endpoint provided. Script will not function correctly.");
- }
- }
- return endpoint;
- }
- // Function to change the endpoint
- function changeEndpoint() {
- let newEndpoint = prompt("[MemTrace] Enter new endpoint URL:", getGlobalState("endpoint", ""));
- if (newEndpoint) {
- updateGlobalState("endpoint", newEndpoint);
- console.log("[MemTrace] Endpoint updated to", newEndpoint);
- }
- }
- // Register menu command to change endpoint
- GM_registerMenuCommand("Change MemTrace Endpoint", changeEndpoint);
- function processPage() {
- const article = new Readability(document.cloneNode(true)).parse().content;
- // console.log("article", article);
- const turndownService = new TurndownService({
- keepReplacement: function (content, node) {
- return node.isBlock ? "\n\n" + node.outerHTML + "\n\n" : node.outerHTML;
- },
- });
- // Add a rule to keep tables
- turndownService.addRule("tables", {
- filter: ["table"],
- replacement: function (content, node) {
- return node.outerHTML;
- },
- });
- // Uncomment the following line if you want to use the GFM table plugin instead
- // turndownService.use(turndownPluginGfm.tables);
- return turndownService.turndown(article);
- }
- function parseRedditReply(reply, depth) {
- let replyText = "";
- replyText += "\n---\n";
- replyText += ">".repeat(depth) + `**${reply.data.author}**\n`;
- replyText += ">".repeat(depth) + "\n";
- const lines = reply.data.body.split("\n");
- for (const line of lines) {
- replyText += (">".repeat(depth), line);
- }
- if (!reply.data.replies) {
- return replyText;
- }
- for (const child of reply.data.replies.data.children) {
- replyText += parseRedditReply(child, depth + 1);
- }
- return replyText;
- }
- function savePage(markdown) {
- const url = window.location.href.split("#")[0];
- let data = {
- title: document.title,
- source: "chrome",
- id: md5(url),
- markdown: markdown,
- url: url,
- };
- console.log("[MemTrace] saving page", data);
- GM_xmlhttpRequest({
- method: "POST",
- url: getEndpoint(),
- data: JSON.stringify(data),
- headers: {
- "Content-Type": "application/json",
- },
- onload: function (response) {
- if (response.status === 200) {
- console.log("[MemTrace] saved page");
- } else {
- console.error("Failed to save to MemTrace", response.responseText);
- }
- },
- onerror: function (error) {
- console.error("Request failed:", error);
- },
- });
- }
- function processRedditPage() {
- console.log("[MemTrace] processing reddit page");
- fetch(window.location.href + ".json")
- .then((response) => response.json())
- .then((responseJson) => {
- const page = responseJson;
- const post = page[0].data.children[0].data;
- let markdown = `*${post["subreddit_name_prefixed"]}*\n\n**${post["author"]}**\n\n${post["selftext"]}\n\n`;
- for (const reply of page[1].data.children) {
- markdown += parseRedditReply(reply, 1);
- }
- savePage(markdown);
- });
- }
- function process() {
- const url = window.location.href.split("#")[0];
- if (hrefHistory.includes(url)) {
- console.log("[MemTrace] skip processed url", url);
- return;
- }
- console.log("[MemTrace] processing url", url);
- hrefHistory.push(url);
- if (/reddit.com\/r\/[^/]+\/comments/.test(url)) {
- processRedditPage();
- } else {
- processPage();
- if (markdown.length < 100) {
- console.log("[MemTrace] fail to parse page");
- return;
- }
- savePage(markdown);
- }
- }
- function scheduleProcess() {
- if (window.self === window.top) {
- console.log(`[MemTrace] current href is`, window.location.href);
- setTimeout(() => {
- process();
- }, 5000);
- }
- }
- // Intercept pushState and replaceState
- const originalPushState = history.pushState;
- const originalReplaceState = history.replaceState;
- history.pushState = function () {
- originalPushState.apply(this, arguments);
- scheduleProcess();
- };
- history.replaceState = function () {
- originalReplaceState.apply(this, arguments);
- scheduleProcess();
- };
- window.addEventListener("load", function () {
- scheduleProcess();
- });
- window.addEventListener("popstate", function (event) {
- scheduleProcess();
- });
- })();