您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
new features for PttChrome (show flags features code by osk2/ptt-comment-flag)
当前为
// ==UserScript== // @name PttChrome Add-on (Ptt) // @namespace https://gf.qytechs.cn/zh-TW/scripts/372391-pttchrome-add-on-ptt // @description new features for PttChrome (show flags features code by osk2/ptt-comment-flag) // @version 1.3.1 // @author avan // @match iamchucky.github.io/PttChrome/* // @match term.ptt.cc/* // @require https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/tippy.js/2.5.4/tippy.min.js // @require https://gf.qytechs.cn/scripts/372458-flags/code/Flags.js?version=630928 // @require https://openuserjs.org/src/libs/sizzle/GM_config.js // @grant GM_getValue // @grant GM_setValue // ==/UserScript== let configStatus = false, flagMap = {}; const gmc = new GM_configStruct({ 'id': 'PttChromeAddOnConfig', // The id used for this instance of GM_config 'title': 'PttChrome Add-on Settings', // Panel Title 'fields': { // Fields object 'isHideAll': { 'label': '是否隱藏黑名單推文', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': false // Default value if user doesn't change it }, 'whenHideAllShowInfo': { 'label': '當隱藏黑名單推文顯示提示訊息', // Appears next to field 'type': 'text', // Makes this setting a text input 'size': 35, // Limit length of input (default is 25) 'default': '<本文作者已被列黑名單>' // Default value if user doesn't change it }, 'whenHideAllShowInfoColor': { 'label': '上述提示訊息之顏色', // Appears next to field 'type': 'text', // Makes this setting a text input 'size': 10, // Limit length of input (default is 25) 'default': '#c0c0c0' // Default value if user doesn't change it }, 'isHideViewImg': { 'label': '是否隱藏黑名單圖片預覽', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, 'isHideViewVideo': { 'label': '是否隱藏黑名單影片預覽', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, 'isReduceHeight': { 'label': '是否調降黑名單推文高度', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, 'reduceHeight': { 'label': '設定高度值(單位em)', // Appears next to field 'type': 'float', // Makes this setting a text input 'min': 0, // Optional lower range limit 'max': 10, // Optional upper range limit 'size': 10, // Limit length of input (default is 25) 'default': 0.4 // Default value if user doesn't change it }, 'isReduceOpacity': { 'label': '是否調降黑名單推文透明值', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': false // Default value if user doesn't change it }, 'reduceOpacity': { 'label': '設定透明值', // Appears next to field 'type': 'float', // Makes this setting a text input 'min': 0, // Optional lower range limit 'max': 1, // Optional upper range limit 'size': 10, // Limit length of input (default is 25) 'default': 0.1 // Default value if user doesn't change it }, 'isAddFloorNum': { 'label': '是否顯示推文樓層', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, 'isShowFlags': { 'label': '看板內若有IP(ex.Gossiping),是否依IP顯示國旗', // Appears next to field 'type': 'checkbox', // Makes this setting a checkbox input 'default': true // Default value if user doesn't change it }, 'whenShowFlagsIgnoreSpecificCountrys': { 'label': '指定國家不顯示 ex.「tw;jp」(ISO 3166-1 alpha-2)', // Appears next to field 'type': 'text', // Makes this setting a text input 'size': 35, // Limit length of input (default is 25) 'default': '' // Default value if user doesn't change it }, }, 'events': { // Callback functions object //'open': function() { 'open': () => { gmc.frame.setAttribute('style', "border: 1px solid #AAA;color: #999;background-color: #111; width: 23em; height: 35em; position: fixed; top: 2.5em; right: 0.5em; z-index: 9999;"); configStatus = true; }, 'close': () => { configStatus = false;}, }, 'css': `#PttChromeAddOnConfig * { color: #999 !important;background-color: #111 !important; } body#PttChromeAddOnConfig { background-color: #111}` }); const HOST = 'https://osk2.me:9977', ipValidation = /(\d{1,3}\.){3}\d{1,3}/, timerArray = []; let timestamp = Math.floor(Date.now() / 1000); const execInterval = () => { if (timerArray.length === 0) { excute(); timerArray.push(setInterval(excute, 3000)); } } const stopInterval = () => { while (timerArray.length > 0) { clearInterval(timerArray .shift()); } } const css = (elements, styles) => { elements = elements.length ? elements : [elements]; elements.forEach(element => { for (var property in styles) { element.style[property] = styles[property]; } }); } const findAll = (elements, selectors) => { let rtnElements = []; elements = elements.length ? elements : [elements]; elements.forEach(element => rtnElements.push.apply(rtnElements, element.querySelectorAll(selectors))); return rtnElements; } const innerHTMLAll = (elements) => { let rtn = ""; elements = elements.length ? elements : [elements]; elements.forEach(element => element.innerHTML ? rtn += element.innerHTML : ""); return rtn; } const show = (elements, specifiedDisplay = 'block') => { elements = elements.length ? elements : [elements]; elements.forEach(element => { if (!element.style) return; element.style.display = specifiedDisplay; }); } const hide = (elements) => { elements = elements.length ? elements : [elements]; elements.forEach(element => { if (!element.style) return; element.style.display = 'none'; }); } const generateImageHTML = (ip, flag) => { if (!flag || !flag.imagePath) return; const countryCode = flag.imagePath.toLowerCase().replace('assets/','').replace('.png',''); const ignoreCountrys = gmc.get('whenShowFlagsIgnoreSpecificCountrys').match(new RegExp(countryCode, 'i')); if (ignoreCountrys && ignoreCountrys.length > 0) return; const imagePath = flag.imagePath ? `${Flags[countryCode]}` : null; const imageTitile = `${flag.locationName || 'N/A'}<br><a href='https://www.google.com/search?q=${ip}' target='_blank'>${ip}</a>`; if (!imagePath) { return; } return `<div data-flag title="${imageTitile}" style="background-image: url('${imagePath}');background-repeat:no-repeat;background-position:left;float:right;height:0.8em;width:0.8em;cursor:pointer !important;"></div>`; }; const chkBlackSpan = () => { let blackSpan = document.querySelectorAll('span[style="opacity:0.2"]'); let whenHideAllShowInfoCss = document.querySelector('#whenHideAllShowInfo'); if (blackSpan.length > 0) { if (gmc.get('isHideAll')) { if (gmc.get('whenHideAllShowInfo').length > 0) { if (whenHideAllShowInfoCss) { whenHideAllShowInfoCss.remove(); } const cssLinkEl = document.createElement('link'); cssLinkEl.setAttribute('rel', 'stylesheet'); cssLinkEl.setAttribute('id', 'whenHideAllShowInfo'); cssLinkEl.setAttribute('type', 'text/css'); cssLinkEl.setAttribute('href', 'data:text/css;charset=UTF-8,' + encodeURIComponent(` span[type="bbsrow"][style="opacity:0.2"] {opacity:1 !important;visibility: hidden;} span[type="bbsrow"][style="opacity:0.2"]:before { visibility: visible;color: ${gmc.get('whenHideAllShowInfoColor')}; content: ' - ${gmc.get('whenHideAllShowInfo')}'; } `)); document.head.appendChild(cssLinkEl); } else { if (whenHideAllShowInfoCss) { whenHideAllShowInfoCss.remove(); } hide(blackSpan); } } else { if (whenHideAllShowInfoCss) { whenHideAllShowInfoCss.remove(); } gmc.get('isHideViewImg') && hide(findAll(blackSpan, 'img:not([style*="display: none"])')); gmc.get('isHideViewVideo') && hide(findAll(blackSpan, '.easyReadingVideo:not([style*="display: none"])')); gmc.get('isReduceHeight') && css(blackSpan, { 'height': gmc.get('reduceHeight') + 'em', 'font-size': (gmc.get('reduceHeight')/2) + 'em', 'line-height': gmc.get('reduceHeight') + 'em' }); gmc.get('isReduceOpacity') && css(blackSpan, {'opacity': gmc.get('reduceOpacity')}); } } } const findPrevious = (element, selectors) => { if (!element) return; element = element.previousElementSibling; if (!element) return; let rtnElement = element.querySelectorAll(selectors) if (rtnElement && rtnElement.length > 0) { return rtnElement; } else { return findPrevious(element, selectors); } } const firstEl = (element) => { if (!element) return; element = element.nextElementSibling; if (!element) return; if (element.classList.toString().startsWith("blu_")) { return element; } else { return firstEl(element); } } const queryPage = (node) => { let rtnPage; if (node && node.length > 0) { rtnPage = node[node.length -1].querySelector('span'); if (!rtnPage) return; rtnPage = rtnPage.innerText.match(/瀏覽[^\d]+(\d+)\/(\d+)/); if (rtnPage && rtnPage.length === 3) { rtnPage = rtnPage[1]; return rtnPage; } } } let currentNum, currentPage, pageData = {}; const excute = async () => { //console.log("do excute"); const currentTS = Math.floor(Date.now() / 1000); if ((currentTS - timestamp) > 5) { stopInterval(); } let firstNode, isHasFirst; chkBlackSpan(); let allNode = document.querySelectorAll('span[type="bbsrow"]'); currentPage = queryPage(allNode); allNode = [].filter.call(allNode, (element, index) => { let node = element.innerHTML.match('※ 文章網址:'); if (node && node.length > 0) { isHasFirst = true; firstNode = firstEl(element); if (firstNode && !firstNode.innerHTML.match(/data-floor/)) { pageData = []; currentNum = -1; //firstNode = firstNode[0]; } } if (innerHTMLAll(findAll(element, "span.q2")).match(ipValidation)) return true; if (element.classList && element.classList.toString().startsWith("blu_")) return true; }); let allIpList = allNode.map(c => { const ip = c.innerHTML.match(ipValidation); if (ip && !flagMap[ip[0]]) return ip[0]; }); allIpList = new Set(allIpList); allIpList.delete(undefined); allIpList.delete(null); allIpList = Array.from(allIpList); if (allIpList && allIpList.length > 0 && allIpList[0]) { const flagsResponse = await axios.post(`${HOST}/ip`, { ip: allIpList}, {headers: {'Content-Type': 'application/json',}}), flags = flagsResponse.data; if (flags && flags.length > 0) { flags.forEach((flag, index) => { const ip = allIpList[index]; if (!flag) { flag = []; } else if (flag.imagePath) { flag.countryCode = flag.imagePath.toLowerCase().replace('assets/','').replace('.png','');; } flag.ip = ip; flagMap[ip] = flag; }); } } allNode.some((comment, index) => { const test = comment.innerHTML.match(/^[ \t]*\d+/); if (test && test.length > 0) return true; if (gmc.get('isAddFloorNum') && comment.classList && comment.classList.toString().startsWith("blu_") && !comment.innerHTML.match(/data-floor/)) { let upstairs = null; if (currentNum > 0) { upstairs = findPrevious(allNode[index], 'div[data-floor]'); if (upstairs && upstairs.length > 0) { let upstairsNum = Number(upstairs[0].innerHTML); if (upstairsNum) { currentNum = Number(upstairs[0].innerHTML) + 1; } } else if (currentPage) { //非好讀模式才有頁數 if (!pageData[currentPage]) pageData[currentPage] = currentNum; currentNum = pageData[currentPage]; } else { currentNum = 1; } } else if (isHasFirst && comment === firstNode) { currentNum = 1; } else if (!isHasFirst) { currentNum = 1; } if (currentNum > 0) { const divCnt = `<div data-floor style="float:left;margin-left: 2.2%;height: 0em;width: 1.5em;font-size: 0.4em;font-weight:bold;text-align: right;">${currentNum}</div>`; comment.innerHTML = divCnt + comment.innerHTML.trim(); } else { const divCnt = `<div data-floor></div>`; comment.innerHTML = divCnt + comment.innerHTML.trim(); } } if (!gmc.get('isShowFlags')) return; const ip = comment.innerHTML.match(ipValidation); if (!ip) return; if (comment.innerHTML.match(/data-flag/)) return; const imageHTML = generateImageHTML(ip[0], flagMap[ip[0]]); if (!imageHTML) return; comment.innerHTML = imageHTML + comment.innerHTML.trim(); timestamp = Math.floor(Date.now() / 1000); }); tippy('[data-flag]', { arrow: true, size: 'large', placement: 'left', interactive: true }); } const CreateMutationObserver = () => { const container = document.querySelector('#mainContainer'); if (!container) { setTimeout(CreateMutationObserver, 2000); return; } const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { const checkNode = document.querySelector('span.q2'); if (checkNode && checkNode.innerHTML.length > 10) { execInterval(); } }); }) observer.observe(container, {childList: true,}); } try { window.addEventListener("load", function(event) { CreateMutationObserver(); }); } catch (ex) { console.error(ex); } const _button = document.createElement("div"); _button.innerHTML = 'Settings'; _button.onclick = event => { if (!configStatus) { configStatus = true; gmc.open(); } else if (configStatus) { configStatus = false; gmc.close(); } event.preventDefault(); event.stopPropagation(); return false; } _button.style = "border: 1px solid #AAA;color: #999;background-color: #111;position: fixed; top: 0.5em; right: 0.5em; z-index: 9999;cursor:pointer !important;" document.body.appendChild(_button) const el = document.createElement('link'); el.rel = 'stylesheet'; el.type = 'text/css'; el.href = "https://cdnjs.cloudflare.com/ajax/libs/tippy.js/2.5.4/tippy.css"; document.head.appendChild(el);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址