PTT web enhanced

Enhance user experience of PTT web

目前為 2021-10-24 提交的版本,檢視 最新版本

// ==UserScript==
// @name         PTT web enhanced
// @namespace    2CF9973A-28C9-11EC-9EA6-98F49F6E8EAB
// @version      2.6.1
// @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 (id) {
    return new Promise((resolve) => {
      GM.xmlHttpRequest({
        url: `https://i.imgur.com/${id}.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 searchSameArticle () {
    let titleElement = document.querySelectorAll('.article-metaline')[1].querySelector('.article-meta-value')
    titleElement.className = 'article-meta-tag'
    let title = titleElement.textContent.match(/^(?:(?:Re|Fw): +)?(.+)$/)[1]
    let url = `${location.pathname.match(/^(.+\/).+?$/)[1]}search?q=${encodeURIComponent(`thread:${title}`).replace(/%20/g, '+')}`
    titleElement.parentElement.style.cursor = 'pointer'
    titleElement.parentElement.addEventListener('click', function (e) {
      location.href = url
    })
  }

  function searchSameAuthor () {
    let authorElement = document.querySelectorAll('.article-metaline')[0].querySelector('.article-meta-value')
    authorElement.className = 'article-meta-tag'
    let author = authorElement.textContent.match(/^(.+) +\([^\)]*?\)$/)[1]
    let url = `${location.pathname.match(/^(.+\/).+?$/)[1]}search?q=${encodeURIComponent(`author:${author}`).replace(/%20/g, '+')}`
    authorElement.parentElement.style.cursor = 'pointer'
    authorElement.parentElement.addEventListener('click', function (e) {
      location.href = url
    })
  }

  // == dependent methods ==

  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()
    }

    // == 取消外連資源的 referrer ==
    document.head.appendChild(createElement('<meta name="referrer" content="no-referrer">'))

    // == 建立 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 () {
      pttImageEnhanced()
      searchSameAuthor()
      searchSameArticle()
    }, { 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()
  }
})()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址