您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Enhance user experience of PTT web
当前为
// ==UserScript== // @name PTT web enhanced // @namespace 2CF9973A-28C9-11EC-9EA6-98F49F6E8EAB // @version 2.5 // @description Enhance user experience of PTT web // @author Rick0 // @match https://www.ptt.cc/bbs/*/*.html* // @match https://www.ptt.cc/ask/over18?* // @exclude https://www.ptt.cc/bbs/*/index.html // @grant GM.xmlHttpRequest // @connect imgur.com // @run-at document-start // ==/UserScript== (function() { 'use strict' // == independent methods == function createElement(html) { let template = document.createElement('template') template.innerHTML = html return template.content.firstChild } function isUrlExist (url, headers = {}) { return new Promise((resolve) => { GM.xmlHttpRequest({ url, method: 'HEAD', headers, 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 getImgurInfo (originalUrl) { return new Promise((resolve, reject) => { let imgurInfo = { id: undefined, hasVideo: undefined, get imgurUrl () { return this.id !== undefined ? `https://i.imgur.com/${this.id}.jpg` : undefined }, get embedUrl () { if (this.id !== undefined) { return this.hasVideo ? `https://i.imgur.com/${this.id}.mp4` : `https://i.imgur.com/${this.id}h.jpg` } else { return undefined } }, } let infoHeaders = { referer: 'https://imgur.com/', } let link = new URL(originalUrl) // URL 的 pathname 最少會有 / ,所以利用正則來去頭尾 / 後切割,最後面的 / 的後面如果沒有值不會被列入 let pathList = link.pathname !== '/' ? link.pathname.match(/^\/(.*?)\/?$/)[1].split('/') : [] let imgurIdRegExp = /^\w{7}/ // 取得 id switch (pathList.length) { // 按照 pathname 的層數來分類處理 // 只有一層,只可能是 id / id.ext 的格式 case 1: { let idMatchList = pathList[0].match(imgurIdRegExp) if (idMatchList !== null) { imgurInfo.id = idMatchList[0] } else { reject(imgurInfo) retrun } } break default: reject(imgurInfo) retrun } isUrlExist(`https://i.imgur.com/${imgurInfo.id}.mp4`, infoHeaders) // 確認是否有影片格式的存在 .then(hasVideo => { imgurInfo.hasVideo = hasVideo resolve(imgurInfo) }) .catch(err => { reject(imgurInfo) }) }) } 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])}`) } // == dependent methods == function pttImageEnhanced () { function embedImg (href, prevRichcontentElement) { getImgurInfo(href) .then(imgurInfo => { let richcontent = createElement('<div class="richcontent"></div>') if (imgurInfo.hasVideo) { richcontent.innerHTML = `<video data-src="${imgurInfo.embedUrl}" autoplay loop muted style="max-width: 100%;max-height: 800px;"></video>` videoLazyObserver.observe(richcontent.querySelector(':scope > video')) } else { richcontent.innerHTML = `<img src="${imgurInfo.embedUrl}" alt loading="lazy">` } insertElementToNextLine(prevRichcontentElement, richcontent) }) .catch(err => err) } // == 取消所有 ptt web 原生的 imgur 圖片載入 == for (let img of document.querySelectorAll('.richcontent > img[src*="imgur.com"]')) { img.src = '' img.parentElement.remove() } // == 取消外連資源的 referrer == document.head.appendChild(createElement('<meta name="referrer" content="no-referrer">')) // == 建立 video lazy observer == let onEnterView = function (entries, observer) { for (let entry of entries) { if (entry.isIntersecting) { // 目標進入畫面 let video = entry.target video.src = video.dataset.src video.removeAttribute('data-src') observer.unobserve(video) } } } let options = { rootMargin: '50%', } let videoLazyObserver = new IntersectionObserver(onEnterView, options) // == 處理內文的部分 == for (let a of document.querySelectorAll('.bbs-screen.bbs-content > a[href*="imgur.com"]')) { embedImg(a.href, a) } // == 處理推/噓文的部分 == for (let a of document.querySelectorAll('.f3.push-content > a[href*="imgur.com"]')) { embedImg(a.href, a.closest('.push')) } } function searchSameArticle () { let titleElement = document.querySelectorAll('.article-metaline')[1].querySelector('.article-meta-value') titleElement.className = 'article-meta-tag' let title = titleElement.textContent.match(/^(?:Re: +)?(.+)$/)[1] let url = `${location.pathname.match(/^(.+\/).+?$/)[1]}search?q=${encodeURIComponent(title)}` titleElement.outerHTML = `<a href="${url}">${titleElement.outerHTML}</a>` } // == main == if (location.pathname === '/ask/over18') { agreeOver18() } else { document.addEventListener('DOMContentLoaded', function () { pttImageEnhanced() searchSameArticle() }, { once: true }) } })()