您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Enhance user experience of PTT web
当前为
// ==UserScript== // @name PTT web enhanced // @namespace 2CF9973A-28C9-11EC-9EA6-98F49F6E8EAB // @version 2.7 // @description Enhance user experience of PTT web // @author Rick0 // @match https://www.ptt.cc/* // @grant GM.xmlHttpRequest // @connect imgur.com // @run-at document-start // @compatible firefox Tampermonkey, Violentmonkey // @compatible chrome Tampermonkey, Violentmonkey // @license Beerware // ==/UserScript== (function() { 'use strict' // == independent methods == function createElement(html) { let template = document.createElement('template') template.innerHTML = html return template.content.firstChild } function isImgurMp4Exist (imgurId) { return new Promise((resolve) => { GM.xmlHttpRequest({ url: `https://i.imgur.com/${imgurId}.mp4`, method: 'HEAD', headers: { referer: 'https://imgur.com/', }, onload: function (res) { if ([200, 304].includes(res.status) && res.finalUrl !== 'https://i.imgur.com/removed.png') { resolve(true) } else { resolve(false) } }, onerror: function (err) { resolve(false) }, }) }) } function insertElementToNextLine (positionElement, element) { let positionNextSibling = positionElement.nextSibling switch (positionNextSibling?.nodeType) { case Node.TEXT_NODE: positionNextSibling.parentNode.replaceChild(element, positionNextSibling) let textMatchList = positionNextSibling.data.match(/^([^\n]*)\n?(.*)$/s) if (textMatchList[1] !== undefined) element.insertAdjacentText('beforebegin', textMatchList[1]) if (textMatchList[2] !== undefined) element.insertAdjacentText('afterend', textMatchList[2]) break case Node.ELEMENT_NODE: case undefined: positionElement.insertAdjacentElement('afterend', element) break default: throw new Error('insertElementToNextLine receive invalid positionElement') } } function agreeOver18 () { document.cookie = `over18=1;path=/;expires=${(new Date(2100, 0)).toUTCString()}` location.replace(`https://www.ptt.cc/${decodeURIComponent(location.search.match(/[?&]from=([^&]+)/)[1])}`) } function addStyle (cssCode) { document.head.append(createElement(cssCode)) } function setNoReferrer () { document.head.append(createElement('<meta name="referrer" content="no-referrer">')) } // == dependent methods == function addSearch () { // 系列文 let title = document.querySelectorAll('.article-metaline')[1] .querySelector('.article-meta-value') .textContent.match(/^(?:(?:Re|Fw): +)?(.+)$/)[1] let titleEl = createElement(`<a class="board ellipsis" href="javascript:void(0);">系列 ${title}</a>`) let titleUrl = `${location.pathname.match(/^(.+\/).+?$/)[1]}search?q=${encodeURIComponent(`thread:${title}`).replace(/%20/g, '+')}` titleEl.addEventListener('click', function (e) { location.href = titleUrl }) // 同作者 let author = document.querySelectorAll('.article-metaline')[0] .querySelector('.article-meta-value') .textContent.match(/^(.+) +\([^\)]*?\)$/)[1] let authorEl = createElement(`<a class="board" href="javascript:void(0);">作者 ${author}</a>`) let authorUrl = `${location.pathname.match(/^(.+\/).+?$/)[1]}search?q=${encodeURIComponent(`author:${author}`).replace(/%20/g, '+')}` authorEl.addEventListener('click', function (e) { location.href = authorUrl }) // 插入到畫面中 let navigation = document.querySelector('#navigation') navigation.firstElementChild.remove() navigation.insertAdjacentElement('afterbegin', titleEl) navigation.insertAdjacentElement('afterbegin', createElement('<div class="bar"></div>')) navigation.insertAdjacentElement('beforeend', authorEl) } function pttImageEnhanced () { function getPrevRichcontentEl (el) { while (el.parentElement.id !== 'main-content') { el = el.parentElement } return el } // == 取消所有 ptt web 原生的 imgur 圖片載入 == for (let img of document.querySelectorAll('.richcontent > img[src*="imgur.com"]')) { img.src = '' img.parentElement.remove() } // == 建立 lazy observer == let onEnterView = function (entries, observer) { for (let entry of entries) { if (entry.isIntersecting) { // 目標進入畫面 let triggerRichcontent = entry.target let imgurId = triggerRichcontent.dataset.imgurId isImgurMp4Exist(imgurId) .then(hasVideo => { let attachment if (hasVideo) { attachment = createElement(`<video src="https://i.imgur.com/${imgurId}.mp4" autoplay loop muted style="max-width: 100%;max-height: 800px;"></video>`) attachment.addEventListener('loadedmetadata', function (e) { triggerRichcontent.removeAttribute('style') }) } else { attachment = createElement(`<img src="https://i.imgur.com/${imgurId}h.jpg" alt>`) attachment.addEventListener('load', function (e) { triggerRichcontent.removeAttribute('style') }) } triggerRichcontent.append(attachment) }) .catch(err => err) observer.unobserve(triggerRichcontent) } } } let options = { rootMargin: '200%', } let lazyObserver = new IntersectionObserver(onEnterView, options) for (let link of document.querySelectorAll('.bbs-screen.bbs-content a[href*="imgur.com"]')) { let urlData = new URL(link) if (globalRegExpData.imgurIdExtRegExp.test(urlData.pathname)) { let imgurId = RegExp.$1 // 建立 richcontent let prevRichcontentEl = getPrevRichcontentEl(link) let richcontent = createElement(`<div class="richcontent" style="min-height: 30vh;" data-imgur-id="${imgurId}"></div>`) lazyObserver.observe(richcontent) insertElementToNextLine(prevRichcontentEl, richcontent) } } } // == main == var globalRegExpData = { imgurIdExtRegExp: /^\/(\w{7})\w?(?:\.(\w+))?$/, } if (/^(?:\/[^\/]+?)+\/(?:M|G)\.\d+\.A\.[0-9A-F]{3}\.html/.test(location.pathname)) { document.addEventListener('DOMContentLoaded', function () { addStyle( `<style> #navigation { display: flex; } #navigation > * { white-space: nowrap; } .ellipsis { text-overflow: ellipsis; overflow: hidden; } </style>` ) setNoReferrer() pttImageEnhanced() addSearch() // document.querySelector('.article-metaline-right')?.remove?.() }, { once: true }) // 看板主頁 // } else if (/^\/bbs\/[^\/]+?\/index.html/.test(location.pathname)) { // console.log('看板主頁') // // 搜尋結果 // } else if (/^\/bbs\/[^\/]+?\/search/.test(location.pathname)) { // console.log('搜尋頁面') // 已成年同意 } else if (location.pathname === '/ask/over18') { agreeOver18() } })()