您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Allow users to add tags to links.
当前为
// ==UserScript== // @name UTags - Add usertags to links // @name:zh-CN 小鱼标签 (UTags) - 为链接添加用户标签 // @namespace http://tampermonkey.net/ // @version 0.0.1 // @description Allow users to add tags to links. // @description:zh-cn 此插件允许用户为网站的链接添加自定义标签。比如,可以给论坛的用户或帖子添加标签。 // @author Pipecraft // @license MIT // @match https://*/* // @match http://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addValueChangeListener // ==/UserScript== (function () { ("use strict"); document.GM_getValue = GM_getValue; document.GM_setValue = GM_setValue; document.GM_addValueChangeListener = GM_addValueChangeListener; // import React from "react"; // import ReactDOM from "react-dom"; // import App from "./app"; const app = document.createElement("div"); app.id = "app-utags-65076"; // document.body.appendChild(app) // ReactDOM.render(<App />, app) const STORAGE = { getValue: document.GM_getValue, setValue: document.GM_setValue, addValueChangeListener: document.GM_addValueChangeListener, }; const uniq = (arr) => [...new Set(arr)]; const patterns = [/v2ex.com\/member\/(\w+)($|\?)/]; const v2ex = { matchedNodes: function () { const patterns = [ '.topic_info a[href*="/member/"]', "a.topic-link", '#Main strong a.dark[href*="/member/"]', '.topic_content a[href*="/member/"]', '.reply_content a[href*="/member/"]', '.header small a[href*="/member/"]', '.dock_area a[href*="/member/"]', '.dock_area a[href*="/t/"]', ]; const elements = document.querySelectorAll(patterns.join(",")); function getCanonicalUrl(url) { return url.replace(/[?#].*/, ""); } const nodes = [...elements].map((element) => { const key = getCanonicalUrl(element.href); return { element, key }; }); if (location.pathname.includes("/member/")) { const profile = document.querySelector("h1"); if (profile) { const key = "https://www.v2ex.com/member/" + profile.textContent; nodes.push({ element: profile, key }); } } if (location.pathname.includes("/t/")) { const header = document.querySelector(".topic_content"); if (header) { const key = getCanonicalUrl( "https://www.v2ex.com" + location.pathname ); nodes.push({ element: header, key }); } } return nodes; }, }; function getCanonicalUrl(url) { return url; } // Migration data from "v2ex user tag" plugin // https://greasyfork.org/en/scripts/437891-v2ex-user-tag function migrationFromV2exUserTag() { const TAG_JSON_STR_STORE_KEY = "plugin.user_tag.tag_json_str.v0.1"; const data = window.localStorage.getItem(TAG_JSON_STR_STORE_KEY); if (data && confirm("[UTags] 发现 v2ex user tag 插件的数据,要导入吗?")) { try { const jsonObj = JSON.parse(data); const tagMap = getTagMap(); for (let key in jsonObj) { if (jsonObj.hasOwnProperty(key)) { const oldTags = jsonObj[key].split(/\s*[,,]\s*/); const newkey = "https://www.v2ex.com/member/" + key; const tags = tagMap[newkey] ? tagMap[newkey].concat(oldTags) : oldTags; tagMap[newkey] = uniq(tags.filter(Boolean)); } } STORAGE.setValue(STORE_KEY, JSON.stringify(tagMap)); window.localStorage.setItem( TAG_JSON_STR_STORE_KEY + "__migrationed", data ); window.localStorage.removeItem(TAG_JSON_STR_STORE_KEY); alert("[UTags] 数据导入成功,现在可以停用或删除 v2ex user tag 插件"); } catch (e) { console.error(e); alert("导入失败,请查看控制台里输出日志。"); } } } const STORE_KEY = "plugin.utags.tags.v1"; function initStorage() { migrationFromV2exUserTag(); STORAGE.addValueChangeListener( STORE_KEY, function (key, oldValue, newValue, remote) { console.log("[UTags] The value of tags has chenged."); // console.log( // "The value of the '" + // key + // "' key has changed from '" + // oldValue + // "' to '" + // newValue + // "'" // ); displayTags(); } ); } function getTagMap() { const tagJsonStr = STORAGE.getValue(STORE_KEY, "{}"); try { return JSON.parse(tagJsonStr); } catch (e) { console.error("Invalid JSON string.", tagJsonStr); return {}; } } function getTags(key) { const tagMap = getTagMap(); return tagMap[key] || []; } function saveTags(key, tags) { const tagMap = getTagMap(); tagMap[key] = uniq(tags.map((v) => (v ? v.trim() : v)).filter(Boolean)); if (tagMap[key].length === 0) { delete tagMap[key]; } STORAGE.setValue(STORE_KEY, JSON.stringify(tagMap)); } function appendTagsToPage(element, key, tags) { if ( element.nextSibling && element.nextSibling.classList && element.nextSibling.classList.contains("utags_ul") ) { element.nextSibling.remove(); } let ul = document.createElement("ul"); let li = document.createElement("li"); let a = document.createElement("a"); // a.textContent = "添加标签🏷️"; a.textContent = "🏷️"; a.setAttribute( "class", tags.length === 0 ? "utags_text_tag utags_captain_tag" : "utags_text_tag utags_captain_tag2" ); a.addEventListener("click", function () { let newTags = prompt( "[UTags] 请输入标签,用逗号分开多个标签", tags.join(", ") ); if (newTags) { let newTagsArray = newTags.split(/\s*[,,]\s*/); console.log(newTagsArray); saveTags(key, newTagsArray); } }); li.append(a); ul.append(li); for (let tag of tags) { li = document.createElement("li"); a = document.createElement("a"); a.textContent = tag; a.setAttribute("data-tag", tag); a.setAttribute("href", "https://utags.pipecraft.net/tags/#" + tag); a.setAttribute("target", "_blank"); a.setAttribute("class", "utags_text_tag"); li.append(a); ul.append(li); } ul.setAttribute("class", "utags_ul"); element.insertAdjacentElement("afterend", ul); } function displayTags() { // Display tags for matched components on matched pages const nodes = v2ex.matchedNodes(); nodes.forEach((node) => { const tags = getTags(node.key); appendTagsToPage(node.element, node.key, tags); }); } function main() { initStorage(); const style0 = document.createElement("style"); style0.id = "utags_style0"; style0.textContent = ` .utags_ul { display: none; } `; document.head.append(style0); const style = document.createElement("link"); style.id = "utags_style"; style.rel = "stylesheet"; style.type = "text/css"; style.setAttribute("data-utags_site_domain", location.hostname); // style.href = "http://localhost:8081/style.css"; style.href = "https://utags.pipecraft.net/style.css"; document.head.append(style); // const style2 = document.createElement("link"); // style2.id = "utags_style"; // style2.rel = "stylesheet"; // style2.type = "text/css"; // style2.setAttribute("data-utags_site_domain", location.hostname); // style2.href = "https://utags.github.io/" + location.hostname + "style.css"; // document.head.append(style2); displayTags(); } main(); })();