// ==UserScript== // @name NGA 图片浏览器 // @namespace https://greasyfork.org/zh-CN/users/164691-shy07 // @version 0.03 // @description 收集指定楼层的图片,改善图片浏览体验 // @author Shy07 // @match *://nga.178.com/* // @match *://bbs.ngacn.cc/* // @match *://bbs.nga.cn/* // @match *://ngabbs.com/* // @grant GM.xmlHttpRequest // @grant GM_xmlHttpRequest // jshint esversion:6 // @downloadURL none // ==/UserScript== ((n, self) => { 'use strict' if (n === undefined) return const targetNode = document.querySelector('body') const config = { childList: true } let imageSources = [] let currentImage = 0 const callerId = '_shy07_gallery_caller' const containerClass = '_shy07_gallery_container' const imgClass = '_shy07_gallery_img' const galleryContainerStyle = ` display: block; top: 0; left: 0; width: 100vw; height: 100vh; position: fixed; background: rgba(0, 0, 0, 0.9); ` const galleryImgStyle = ` display: block; top: 0; left: 0; width: 100vw; height: 100vh; background-repeat: no-repeat; background-size: contain; background-position: center; ` const arrowStyle = ` display: block; position: fixed; top: 0; line-height: 100vh; color: #fff; font-size: 5rem; text-decoration-line: none; ` const leftArrowStyle = ` left: 0; padding-left: 1rem; ` const rightArrowStyle = ` right: 0; padding-right: 1rem; ` const closeBtnStyle = ` display: text-block; position: fixed; top: 0; right: 0; padding: 1rem 2rem; color: #fff; text-decoration-line: none; ` const topLeftMenuStyle = ` position: fixed; top: 0; left: 0; padding: 1rem 2rem; ` const topLeftMenuItemStyle = ` display: text-block; margin-right: 1rem; color: #fff; text-decoration-line: none; ` const showCollapseContent = container => { const elements = container.querySelectorAll('.collapse_btn') if (elements && elements.length > 0) { elements.forEach(ele => { const btn = ele.querySelector('button') btn && btn.click() }) } } const setImageSrc = src => { const img = document.querySelector('.' + imgClass) if (img) img.style.backgroundImage = `url(${src})` } const prevImage = () => { currentImage = currentImage === 0 ? imageSources.length - 1 : currentImage - 1 setImageSrc(imageSources[currentImage]) } const nextImage = () => { currentImage = currentImage === imageSources.length - 1 ? 0 : currentImage + 1 setImageSrc(imageSources[currentImage]) } const handleKeydown = ev => { const code = ev.keyCode if (code === 37) { prevImage() } else if (code === 39) { nextImage() } } const ajaxDownload = (url, callback, args, tryTimes) => { tryTimes = tryTimes || 0 const GM_download = GM.xmlHttpRequest || GM_xmlHttpRequest const clearUrl = url.replace(/[&\?]?download_timestamp=\d+/, '') const retryUrl = clearUrl + (clearUrl.indexOf('?') === -1 ? '?' : '&') + 'download_timestamp=' + new Date().getTime() GM_download({ method: 'GET', responseType: 'blob', url: url, onreadystatechange: (responseDetails) => { if (responseDetails.readyState === 4) { if (responseDetails.status === 200 || responseDetails.status === 304 || responseDetails.status === 0) { const blob = responseDetails.response, size = blob && blob.size if (size && (size / 1024 >= 5)) { callback(blob, args) } else if (tryTimes++ == 3) { callback(blob, args) } else { ajaxDownload(retryUrl, callback, args, tryTimes) } } else { if (tryTimes++ == 3) { callback(null, args) } else { ajaxDownload(retryUrl, callback, args, tryTimes) } } } }, onerror: (responseDetails) => { if (tryTimes++ == 3) { callback(null, args) } else { ajaxDownload(retryUrl, callback, args, tryTimes) } console.log(responseDetails.status) } }) } const fileNameFromHeader = (disposition, url) => { if (disposition && /filename=.*/ig.test(disposition)) { const result = disposition.match(/filename=.*/ig) return decodeURI(result[0].split("=")[1]) } return url.substring(url.lastIndexOf('/') + 1) } const downloadBlobFile = (content, fileName) => { if ('msSaveOrOpenBlob' in navigator) { navigator.msSaveOrOpenBlob(content, fileName) } else { const aLink = document.createElement('a') aLink.download = fileName aLink.style = 'display:none;' const blob = new Blob([content]) aLink.href = window.URL.createObjectURL(blob) document.body.appendChild(aLink) if (document.all) { aLink.click() //IE } else { const evt = document.createEvent('MouseEvents') evt.initEvent('click', true, true) aLink.dispatchEvent(evt) // 其它浏览器 } window.URL.revokeObjectURL(aLink.href) document.body.removeChild(aLink) } } const downloadUrlFile = (url, fileName) => { const aLink = document.createElement('a') if (fileName) { aLink.download = fileName } else { aLink.download = url.substring(url.lastIndexOf('/') + 1) } aLink.target = '_blank' aLink.style = 'display:none;' aLink.href = url document.body.appendChild(aLink) if (document.all) { aLink.click() //IE } else { const evt = document.createEvent('MouseEvents') evt.initEvent('click', true, true) aLink.dispatchEvent(evt) // 其它浏览器 } document.body.removeChild(aLink) } const downloadImage = () => { const url = imageSources[currentImage] const tmp = url.split('/') const filename = tmp[tmp.length - 1] ajaxDownload(url, downloadBlobFile, filename) } const downloadAllImage = (blob = null, { list, filename } = {}) => { if (blob && filename) downloadBlobFile(blob, filename) const [first, ...newList] = list || imageSources const tmp = first.split('/') const f = tmp[tmp.length - 1] ajaxDownload(first, downloadAllImage, { list: newList, filename: f }) } const openInNewTab = () => { window.open(imageSources[currentImage], '_blank') } const fixedMMC = value => { const ele = document.querySelector('#mmc') if (ele) ele.style.position = value ? 'fixed' : 'inherit' } const createGalleryImage = () => { currentImage = 0 const img = document.createElement('div') img.className = imgClass img.style = galleryImgStyle img.style.backgroundImage = `url(${imageSources[0]})` return img } const createLeftArrow = () => { const ele = document.createElement('a') ele.style = arrowStyle + leftArrowStyle ele.innerHTML = '<' ele.href = 'javascript:void(0)' ele.addEventListener('click', prevImage) return ele } const createRightArrow = () => { const ele = document.createElement('a') ele.style = arrowStyle + rightArrowStyle ele.innerHTML = '>' ele.href = 'javascript:void(0)' ele.addEventListener('click', nextImage) return ele } const createCloseBtn = () => { const ele = document.createElement('a') ele.style = closeBtnStyle ele.innerHTML = '关闭' ele.href = 'javascript:void(0)' ele.addEventListener('click', hideGallery) return ele } const createTopLeftMenu = () => { const ele = document.createElement('div') ele.style = topLeftMenuStyle const downloadBtn = document.createElement('a') downloadBtn.style= topLeftMenuItemStyle downloadBtn.innerHTML = '下载' downloadBtn.href = 'javascript:void(0)' downloadBtn.addEventListener('click', downloadImage) const downloadAllBtn = document.createElement('a') downloadAllBtn.style= topLeftMenuItemStyle downloadAllBtn.innerHTML = '全部下载' downloadAllBtn.href = 'javascript:void(0)' downloadAllBtn.addEventListener('click', downloadAllImage) const newTabBtn = document.createElement('a') newTabBtn.style= topLeftMenuItemStyle newTabBtn.innerHTML = '新页面打开' newTabBtn.href = 'javascript:void(0)' newTabBtn.addEventListener('click', openInNewTab) ele.appendChild(downloadBtn) // ele.appendChild(downloadAllBtn) ele.appendChild(newTabBtn) return ele } const showGallery = () => { if (!imageSources.length) { window.alert('这层楼没有图片  ̄□ ̄||') return } document.addEventListener('keydown', handleKeydown) const galleryMask = document.querySelector('.' + containerClass) // fixedMMC(true) if (galleryMask) { currentImage = 0 setImageSrc(imageSources[currentImage]) galleryMask.style = galleryContainerStyle } else { const ele = document.createElement('div') ele.className = containerClass ele.style = galleryContainerStyle const img = createGalleryImage() const leftArrow = createLeftArrow() const rightArrow = createRightArrow() const closeBtn = createCloseBtn() const topLeftMenu = createTopLeftMenu() ele.appendChild(img) ele.appendChild(leftArrow) ele.appendChild(rightArrow) ele.appendChild(closeBtn) ele.appendChild(topLeftMenu) document.body.appendChild(ele) } } const hideGallery = () => { document.removeEventListener('keydown', handleKeydown) // fixedMMC(false) const galleryMask = document.querySelector('.' + containerClass) if (galleryMask) { galleryMask.style.display = 'none' } } const collectImages = container => { showCollapseContent(container) imageSources = [] const imgs = container.querySelectorAll('img') const temp = [] imgs.forEach(img => { const src = img.src const lazySrc = img.dataset ? img.dataset.srclazy : '' if (lazySrc) { imageSources.push(lazySrc) return } if (src.includes('/attachments/')) { const arr = img.src.replace(/https:/g, 'http:').split('http:') const src = arr.filter(s => !!s) imageSources.push(src[0]) } }) } const callerButton = container => { const a = document.createElement('a') const handleClick = () => { collectImages(container.parentElement) showGallery() } a.addEventListener('click', handleClick) a.id = callerId + container.id a.className = 'small_colored_text_btn block_txt_c0 stxt' a.href = 'javascript:void(0)' a.title = '图片浏览' a.innerHTML = `   图集   ` return a } const createCallerButton = container => { const checkExist = document.querySelector('#' + callerId + container.id) if (!checkExist) { container.appendChild(callerButton(container)) } } const createCallerButtons = () => { const elements = document.querySelectorAll('.postInfo') if (elements && elements.length > 0) { elements.forEach(createCallerButton) } } const observer = new MutationObserver((mutationsList, observer) => { createCallerButtons() }) observer.observe(targetNode, config) console.log('hello') })(commonui, __CURRENT_UID)