返回首頁 

Greasy Fork is available in English.

PTT web enhanced

Enhance user experience of PTT web

ของเมื่อวันที่ 19-10-2021 ดู เวอร์ชันล่าสุด

// ==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 = htmlreturn 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.nextSiblingswitch (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])breakcase Node.ELEMENT_NODE:case undefined:positionElement.insertAdjacentElement('afterend', element)breakdefault: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}/// 取得 idswitch (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}}breakdefault:reject(imgurInfo)retrun}isUrlExist(`https://i.imgur.com/${imgurInfo.id}.mp4`, infoHeaders)// 確認是否有影片格式的存在.then(hasVideo => {imgurInfo.hasVideo = hasVideoresolve(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.targetvideo.src = video.dataset.srcvideo.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 })}})()