您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
8chan features script
当前为
// ==UserScript== // @name Fullchan X // @namespace Violentmonkey Scripts // @match https://8chan.moe/*/res/* // @match https://8chan.se/*/res/* // @match https://8chan.moe/*/catalog* // @match https://8chan.se/*/catalog* // @run-at document-end // @grant none // @version 1.8.1 // @author vfyxe // @description 8chan features script // ==/UserScript== class fullChanX extends HTMLElement { constructor() { super(); this.settingsEl = document.querySelector('fullchan-x-settings'); this.settings = this.settingsEl.settings; this.isThread = !!document.querySelector('.opCell'); this.isDisclaimer = window.location.href.includes('disclaimer'); Object.keys(this.settings).forEach(key => { this[key] = this.settings[key]?.value; }); } init() { this.settingsButton = this.querySelector('#fcx-settings-btn'); this.settingsButton.addEventListener('click', () => this.settingsEl.toggle()); this.handleBoardLinks(); if (!this.isThread) return; this.quickReply = document.querySelector('#quick-reply'); this.qrbody = document.querySelector('#qrbody'); this.threadParent = document.querySelector('#divThreads'); this.threadId = this.threadParent.querySelector('.opCell').id; this.thread = this.threadParent.querySelector('.divPosts'); this.posts = [...this.thread.querySelectorAll('.postCell')]; this.postOrder = 'default'; this.postOrderSelect = this.querySelector('#thread-sort'); this.myYousLabel = this.querySelector('.my-yous__label'); this.yousContainer = this.querySelector('#my-yous'); this.gallery = document.querySelector('fullchan-x-gallery'); this.galleryButton = this.querySelector('#fcx-gallery-btn'); this.updateYous(); this.observers(); if (this.enableFileExtentions) this.handleTruncatedFilenames(); } styleUI () { this.style.setProperty('--top', this.uiTopPosition); this.style.setProperty('--right', this.uiRightPosition); this.classList.toggle('fcx--dim', this.uiDimWhenInactive); this.classList.toggle('page-thread', this.isThread); const style = document.createElement('style'); if (this.hideDefaultBoards !== '') { style.textContent += '#navTopBoardsSpan{display:block!important;}' } document.body.appendChild(style); } handleBoardLinks () { const navBoards = document.querySelector('#navTopBoardsSpan'); const customBoardLinks = this.customBoardLinks?.toLowerCase().replace(/\s/g,'').split(','); let hideDefaultBoards = this.hideDefaultBoards?.toLowerCase().replace(/\s/g,'') || ''; const urlCatalog = this.catalogBoardLinks ? '/catalog.html' : ''; if (hideDefaultBoards === 'all') { navBoards.classList.add('hidden'); } else { const waitForNavBoards = setInterval(() => { const navBoards = document.querySelector('#navTopBoardsSpan'); if (!navBoards || !navBoards.querySelector('a')) return; clearInterval(waitForNavBoards); hideDefaultBoards = hideDefaultBoards.split(','); const defaultLinks = [...navBoards.querySelectorAll('a')]; defaultLinks.forEach(link => { link.href += urlCatalog; const linkText = link.textContent; const shouldHide = hideDefaultBoards.includes(linkText) || customBoardLinks.includes(linkText); link.classList.toggle('hidden', shouldHide); }); }, 50); } if (this.customBoardLinks.length > 0) { const customNav = document.createElement('span'); customNav.classList = 'nav-boards nav-boards--custom'; customNav.innerHTML = '<span>[</span>'; customBoardLinks.forEach((board, index) => { const link = document.createElement('a'); link.href = '/' + board + urlCatalog; link.textContent = board; customNav.appendChild(link); if (index < customBoardLinks.length - 1) customNav.innerHTML += '<span>/</span>'; }); customNav.innerHTML += '<span>]</span>'; navBoards.parentNode.insertBefore(customNav, navBoards); } } observers () { this.postOrderSelect.addEventListener('change', (event) => { this.postOrder = event.target.value; this.assignPostOrder(); }); const observerCallback = (mutationsList, observer) => { for (const mutation of mutationsList) { if (mutation.type === 'childList') { this.posts = [...this.thread.querySelectorAll('.postCell')]; if (this.postOrder !== 'default') this.assignPostOrder(); this.updateYous(); this.gallery.updateGalleryImages(); if (this.settings.enableFileExtentions) this.handleTruncatedFilenames(); } } }; const threadObserver = new MutationObserver(observerCallback); threadObserver.observe(this.thread, { childList: true, subtree: false }); if (this.enableNestedQuotes) { this.thread.addEventListener('click', event => { this.handleClick(event); }); } this.galleryButton.addEventListener('click', () => this.gallery.open()); this.myYousLabel.addEventListener('click', (event) => { if (this.myYousLabel.classList.contains('unseen')) { this.yousContainer.querySelector('.unseen').click(); } }); } handleClick (event) { const clicked = event.target; const post = clicked.closest('.innerPost') || clicked.closest('.innerOP'); if (!post) return; const isNested = !!post.closest('.innerNested'); const nestQuote = clicked.closest('.quoteLink') || clicked.closest('.panelBacklinks a'); const postMedia = clicked.closest('a[data-filemime]'); const postId = clicked.closest('.linkQuote'); if (nestQuote) { event.preventDefault(); this.nestQuote(nestQuote, post); } else if (postMedia && isNested) { this.handleMediaClick(event, postMedia); } else if (postId && isNested) { this.handleIdClick(postId); } } handleMediaClick (event, postMedia) { if (postMedia.dataset.filemime === "video/webm") return; event.preventDefault(); const imageSrc = `${postMedia.href}`; const imageEl = postMedia.querySelector('img'); if (!postMedia.dataset.thumbSrc) postMedia.dataset.thumbSrc = `${imageEl.src}`; const isExpanding = imageEl.src !== imageSrc; if (isExpanding) { imageEl.src = imageSrc; imageEl.classList } imageEl.src = isExpanding ? imageSrc : postMedia.dataset.thumbSrc; imageEl.classList.toggle('imgExpanded', isExpanding); } handleIdClick (postId) { const idNumber = '>>' + postId.textContent; this.quickReply.style.display = 'block'; this.qrbody.value += idNumber + '\n'; } handleTruncatedFilenames () { this.postFileNames = [...this.threadParent.querySelectorAll('.originalNameLink[download]:not([data-file-ext])')]; this.postFileNames.forEach(fileName => { const strings = fileName.textContent.split('.'); fileName.textContent = strings[0]; fileName.dataset.fileExt = `.${strings[1]}`; const typeEl = document.createElement('a'); typeEl.textContent = `.${strings[1]}`; typeEl.classList = ('file-ext originalNameLink'); fileName.parentNode.insertBefore(typeEl, fileName.nextSibling); }); } assignPostOrder () { const postOrderReplies = (post) => { const replyCount = post.querySelectorAll('.panelBacklinks a').length; post.style.order = 100 - replyCount; } const postOrderCatbox = (post) => { const postContent = post.querySelector('.divMessage').textContent; const matches = postContent.match(/catbox\.moe/g); const catboxCount = matches ? matches.length : 0; post.style.order = 100 - catboxCount; } if (this.postOrder === 'default') { this.thread.style.display = 'block'; return; } this.thread.style.display = 'flex'; if (this.postOrder === 'replies') { this.posts.forEach(post => postOrderReplies(post)); } else if (this.postOrder === 'catbox') { this.posts.forEach(post => postOrderCatbox(post)); } } updateYous () { this.yous = this.posts.filter(post => post.querySelector('.quoteLink.you')); this.yousLinks = this.yous.map(you => { const youLink = document.createElement('a'); youLink.textContent = '>>' + you.id; youLink.href = '#' + you.id; return youLink; }) let hasUnseenYous = false; this.setUnseenYous(); this.yousContainer.innerHTML = ''; this.yousLinks.forEach(you => { const youId = you.textContent.replace('>>', ''); if (!this.seenYous.includes(youId)) { you.classList.add('unseen'); hasUnseenYous = true } this.yousContainer.appendChild(you) }); this.myYousLabel.classList.toggle('unseen', hasUnseenYous); if (this.replyTabIcon === '') return; const icon = this.replyTabIcon; document.title = hasUnseenYous ? document.title.startsWith(`${icon} `) ? document.title : `${icon} ${document.title}` : document.title.replace(new RegExp(`^${icon} `), ''); } observeUnseenYou(you) { you.classList.add('observe-you'); const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const id = you.id; you.classList.remove('observe-you'); if (!this.seenYous.includes(id)) { this.seenYous.push(id); localStorage.setItem(this.seenKey, JSON.stringify(this.seenYous)); } observer.unobserve(you); this.updateYous(); } }); }, { rootMargin: '0px', threshold: 0.1 }); observer.observe(you); } setUnseenYous() { this.seenKey = `${this.threadId}-seen-yous`; this.seenYous = JSON.parse(localStorage.getItem(this.seenKey)); if (!this.seenYous) { this.seenYous = []; localStorage.setItem(this.seenKey, JSON.stringify(this.seenYous)); } this.unseenYous = this.yous.filter(you => !this.seenYous.includes(you.id)); this.unseenYous.forEach(you => { if (!you.classList.contains('observe-you')) { this.observeUnseenYou(you); } }); } nestQuote(quoteLink, parentPost) { const parentPostMessage = parentPost.querySelector('.divMessage'); const quoteId = quoteLink.href.split('#')[1]; const quotePost = document.getElementById(quoteId); if (!quotePost) return; const quotePostContent = quotePost.querySelector('.innerOP') || quotePost.querySelector('.innerPost'); if (!quotePostContent) return; const existing = parentPost.querySelector(`.nestedPost[data-quote-id="${quoteId}"]`); if (existing) { existing.remove(); return; } const isReply = !quoteLink.classList.contains('quoteLink'); const wrapper = document.createElement('div'); wrapper.classList.add('nestedPost'); wrapper.setAttribute('data-quote-id', quoteId); const clone = quotePostContent.cloneNode(true); clone.style.whiteSpace = 'unset'; clone.classList.add('innerNested'); wrapper.appendChild(clone); if (isReply) { parentPostMessage.insertBefore(wrapper, parentPostMessage.firstChild); } else { quoteLink.insertAdjacentElement('afterend', wrapper); } this.setPostListeners(wrapper); } setPostListeners(parentPost) { const postLinks = [ ...parentPost.querySelectorAll('.quoteLink'), ...parentPost.querySelectorAll('.panelBacklinks a') ]; const hoverPost = (event, link) => { const quoteId = link.href.split('#')[1]; let existingPost = document.querySelector(`.nestedPost[data-quote-id="${quoteId}"]`) || link.closest(`.postCell[id="${quoteId}"]`); if (existingPost) { this.markedPost = existingPost.querySelector('.innerPost') || existingPost.querySelector('.innerOP'); this.markedPost?.classList.add('markedPost'); return; } const quotePost = document.getElementById(quoteId); tooltips.removeIfExists(); const tooltip = document.createElement('div'); tooltip.className = 'quoteTooltip'; document.body.appendChild(tooltip); const rect = link.getBoundingClientRect(); if (!api.mobile) { if (rect.left > window.innerWidth / 2) { const right = window.innerWidth - rect.left - window.scrollX; tooltip.style.right = `${right}px`; } else { const left = rect.right + 10 + window.scrollX; tooltip.style.left = `${left}px`; } } tooltip.style.top = `${rect.top + window.scrollY}px`; tooltip.style.display = 'inline'; tooltips.loadTooltip(tooltip, link.href, quoteId); tooltips.currentTooltip = tooltip; } const unHoverPost = (event, link) => { if (!tooltips.currentTooltip) { this.markedPost?.classList.remove('markedPost'); return false; } if (tooltips.unmarkReply) { tooltips.currentTooltip.classList.remove('markedPost'); Array.from(tooltips.currentTooltip.getElementsByClassName('replyUnderline')) .forEach((a) => a.classList.remove('replyUnderline')) tooltips.unmarkReply = false; } else { tooltips.currentTooltip.remove(); } tooltips.currentTooltip = null; } const addHoverPost = (link => { link.addEventListener('mouseenter', (event) => hoverPost(event, link)); link.addEventListener('mouseleave', (event) => unHoverPost(event, link)); }); postLinks.forEach(link => addHoverPost(link)); } }; window.customElements.define('fullchan-x', fullChanX); class fullChanXGallery extends HTMLElement { constructor() { super(); } init() { this.fullchanX = document.querySelector('fullchan-x'); this.imageContainer = this.querySelector('.gallery__images'); this.mainImageContainer = this.querySelector('.gallery__main-image'); this.mainImage = this.mainImageContainer.querySelector('img'); this.closeButton = this.querySelector('.gallery__close'); this.listeners(); this.addGalleryImages(); this.initalized = true; } addGalleryImages () { this.thumbs = [...this.fullchanX.threadParent.querySelectorAll('.imgLink')].map(thumb => { return thumb.cloneNode(true); }); this.thumbs.forEach(thumb => { this.imageContainer.appendChild(thumb); }); } updateGalleryImages () { if (!this.initalized) return; const newThumbs = [...this.fullchanX.threadParent.querySelectorAll('.imgLink')].filter(thumb => { return !this.thumbs.find(thisThumb.href === thumb.href); }).map(thumb => { return thumb.cloneNode(true); }); newThumbs.forEach(thumb => { this.thumbs.push(thumb); this.imageContainer.appendChild(thumb); }); } listeners () { this.addEventListener('click', event => { const clicked = event.target; let imgLink = clicked.closest('.imgLink'); if (imgLink?.dataset.filemime === 'video/webm') return; if (imgLink) { event.preventDefault(); this.mainImage.src = imgLink.href; } this.mainImageContainer.classList.toggle('active', !!imgLink); if (clicked.closest('.gallery__close')) this.close(); }); } open () { if (!this.initalized) this.init(); this.classList.add('open'); document.body.classList.add('fct-gallery-open'); } close () { this.classList.remove('open'); document.body.classList.remove('fct-gallery-open'); } } window.customElements.define('fullchan-x-gallery', fullChanXGallery); class fullChanXSettings extends HTMLElement { constructor() { super(); this.settingsKey = 'fullchan-x-settings'; this.inputs = []; this.settings = {}; this.settingsTemplate = { enableNestedQuotes: { info: 'Nest posts when clicking backlinks.', type: 'checkbox', value: true }, enableFileExtentions: { info: 'Always show filetype on shortened file names.', type: 'checkbox', value: true }, customBoardLinks: { info: 'List of custom boards in nav (seperate by comma)', type: 'input', value: 'v,a,b' }, hideDefaultBoards: { info: 'List of boards to remove from nav (seperate by comma). Set as "all" to remove all.', type: 'input', value: 'interracial,mlp' }, catalogBoardLinks: { info: 'Redirect nav board links to catalog pages.', type: 'checkbox', value: true }, uiTopPosition: { info: 'Position from top of screen e.g. 100px', type: 'input', value: '50px' }, uiRightPosition: { info: 'Position from right of screen e.g. 100px', type: 'input', value: '25px' }, uiDimWhenInactive: { info: 'Dim UI when not hovering with mouse.', type: 'checkbox', value: true }, replyTabIcon: { info: 'Set the icon/text added to tab title when you get a new (You).', type: 'input', value: '❗' }, }; } init() { this.settingsContainer = this.querySelector('.fcx-settings__settings'); this.getSavedSettings(); this.buildSettingsOptions(); this.listeners(); this.querySelector('.fcx-settings__close').addEventListener('click', () => this.close()); } setSavedSettings (updated) { localStorage.setItem(this.settingsKey, JSON.stringify(this.settings)); if (updated) this.classList.add('fcxs-updated'); } getSavedSettings() { const saved = JSON.parse(localStorage.getItem(this.settingsKey)); if (saved) this.settings = saved; } listeners() { this.inputs.forEach(input => { input.addEventListener('change', () => { const key = input.name; const value = input.type === 'checkbox' ? input.checked : input.value; this.settings[key].value = value; this.setSavedSettings(true); }); }); } buildSettingsOptions() { Object.entries(this.settingsTemplate).forEach(([key, config]) => { const wrapper = document.createElement('div'); const infoWrapper = document.createElement('div'); wrapper.classList.add('fcx-setting'); infoWrapper.classList.add('fcx-setting__info'); wrapper.appendChild(infoWrapper); const label = document.createElement('label'); label.textContent = key .replace(/([A-Z])/g, ' $1') .replace(/^./, str => str.toUpperCase()); label.setAttribute('for', key); infoWrapper.appendChild(label); if (config.info) { const info = document.createElement('p'); info.textContent = config.info; infoWrapper.appendChild(info); } const savedValue = this.settings[key]?.value ?? config.value; let input; if (config.type === 'checkbox') { input = document.createElement('input'); input.type = 'checkbox'; input.checked = savedValue; } else if (config.type === 'input') { input = document.createElement('input'); input.type = 'text'; input.value = savedValue; } else if (config.type === 'select') { input = document.createElement('select'); const options = config.options.split(','); options.forEach(opt => { const option = document.createElement('option'); option.value = opt; option.textContent = opt; if (opt === savedValue) option.selected = true; input.appendChild(option); }); } if (input) { input.id = key; input.name = key; wrapper.appendChild(input); this.inputs.push(input); this.settings[key] = { value: input.type === 'checkbox' ? input.checked : input.value }; } this.settingsContainer.appendChild(wrapper); }); this.setSavedSettings(); } open() { this.classList.add('open'); } close() { this.classList.remove('open'); } toggle() { this.classList.toggle('open'); } } window.customElements.define('fullchan-x-settings', fullChanXSettings); // Create fullchan-x settings const fcxs = document.createElement('fullchan-x-settings'); fcxs.innerHTML = ` <div class="fcxs fcx-settings"> <header> <span class="fcx-settings__title"> Fullchan-X Settings </span> <button class="fcx-settings__close fullchan-x__option">Close</button> </header> <main> <div class="fcxs__updated-message"> <p>Settings updated, refresh page to apply</p> <button class="fullchan-x__option" onClick="location.reload()">Reload page</button> </div> <div class="fcx-settings__settings"></div> </main> <footer> </footer> </div> `; document.body.appendChild(fcxs); fcxs.init(); // Create fullchan-x gallery const fcxg = document.createElement('fullchan-x-gallery'); fcxg.innerHTML = ` <div class="gallery"> <button id="#fcxg-close" class="gallery__close fullchan-x__option">Close</button> <div id="#fcxg-images" class="gallery__images"></div> <div id="#fcxg-main-image" class="gallery__main-image"> <img src="" /> </div> </div> `; document.body.appendChild(fcxg); // Create fullchan-x element const fcx = document.createElement('fullchan-x'); fcx.innerHTML = ` <div class="fcx__controls"> <button id="fcx-settings-btn" class="fullchan-x__option fcx-settings-toggle"> ⚙️<span>Settings</span> </button> <div class="fullchan-x__option thread-only"> ☰ <select id="thread-sort"> <option value="default">Default</option> <option value="replies">Replies</option> <option value="catbox">Catbox</option> </select> </div> <button id="fcx-gallery-btn" class="gallery__toggle fullchan-x__option thread-only"> 🖼️<span>Gallery</span> </button> <div class="fcx__my-yous thread-only"> <p class="my-yous__label fullchan-x__option">💬<span>My (You)s</span></p> <div class="my-yous__yous" id="my-yous"></div> </div> </div> `; document.body.appendChild(fcx); fcx.styleUI() onload = (event) => fcx.init(); // Styles const style = document.createElement('style'); style.innerHTML = ` fullchan-x { --top: 50px; --right: 25px; top: var(--top); right: var(--right); display: block; position: fixed; padding: 10px; background: var(--background-color); border: 1px solid var(--navbar-text-color); color: var(--link-color); font-size: 14px; z-index: 1000; } fullchan-x:not(.page-thread) .thread-only, fullchan-x:not(.page-catalog) .catalog-only{ display: none!important; } fullchan-x:not(:hover):not(:has(select:focus)) { z-index: 3; } fullchan-x.fcx--dim:not(:hover) { opacity: 0.6; } .divPosts { flex-direction: column; } .fcx__controls { display: flex; flex-direction: column; gap: 6px; } fullchan-x:not(:hover):not(:has(select:focus)) span, fullchan-x:not(:hover):not(:has(select:focus)) select { display: none; margin-left: 5px; z-index:3; } .fcx__controls span, .fcx__controls select { margin-left: 5px; } #thread-sort { border: none; background: none; } .my-yous__yous { display: none; flex-direction: column; padding-top: 10px; max-height: calc(100vh - 220px - var(--top)); overflow: auto; } .fcx__my-yous:hover .my-yous__yous { display: flex; } .fullchan-x__option { display: flex; padding: 6px 8px; background: white; border: none !important; border-radius: 0.2rem; transition: all ease 150ms; cursor: pointer; margin: 0; text-align: left; min-width: 18px; min-height: 18px; align-items: center; } .fullchan-x__option, .fullchan-x__option select { font-size: 12px; font-weight: 400; color: #374369; } fullchan-x:not(:hover):not(:has(select:focus)) .fullchan-x__option { display: flex; justify-content: center; } #thread-sort { padding-right: 0; } #thread-sort:hover { display: block; } .innerPost:has(.quoteLink.you) { border-left: solid #dd003e 6px; } .innerPost:has(.youName) { border-left: solid #68b723 6px; } /* --- Nested quotes --- */ .divMessage .nestedPost { display: inline-block; width: 100%; margin-bottom: 14px; white-space: normal!important; overflow-wrap: anywhere; margin-top: 0.5em; border: 1px solid var(--navbar-text-color); } .nestedPost .innerPost, .nestedPost .innerOP { width: 100%; } .nestedPost .imgLink .imgExpanded { width: auto!important; height: auto!important; } .my-yous__label.unseen { background: var(--link-hover-color); color: white; } .my-yous__yous .unseen { font-weight: 900; color: var(--link-hover-color); } /*--- Settings --- */ .fcx-settings { display: block; position: fixed; top: 50vh; left: 50vw; translate: -50% -50%; padding: 20px 0; background: var(--background-color); border: 1px solid var(--navbar-text-color); color: var(--link-color); font-size: 14px; max-width: 480px; max-height: 80vh; overflow: scroll; } fullchan-x-settings:not(.open) { display: none; } .fcx-settings > * { padding: 0 20px; } .fcx-settings header { display: flex; align-items: center; justify-content: space-between; margin: 0 0 15px; padding-bottom: 20px; border-bottom: 1px solid var(--navbar-text-color); } .fcx-settings__title { font-size: 24px; font-size: 24px; letter-spacing: 0.04em; } fullchan-x-settings:not(.fcxs-updated) .fcxs__updated-message { display: none; } .fcx-setting { display: flex; justify-content: space-between; align-items: center; padding: 12px 0; } .fcx-setting__info { max-width: 60%; } .fcx-setting input[type="text"], .fcx-setting select { padding: 4px 6px; min-width: 35%; } .fcx-setting label { font-weight: 600; } .fcx-setting p { margin: 6px 0 0; font-size: 12px; } .fcx-setting + .fcx-setting { border-top: 1px solid var(--navbar-text-color); } .fcxs__updated-message { margin: 10px 0; text-align: center; } .fcxs__updated-message p { font-size: 14px; color: var(--error); } .fcxs__updated-message button { margin: 14px auto 0; } /* --- Gallery --- */ .fct-gallery-open, body.fct-gallery-open, body.fct-gallery-open #mainPanel { overflow: hidden!important; } body.fct-gallery-open fullchan-x { display: none; } fullchan-x-gallery { position: fixed; top: 0; left: 0; width: 100%; background: rgba(0,0,0,0.9); display: none; height: 100%; overflow: auto; } fullchan-x-gallery.open { display: block; } fullchan-x-gallery .gallery { padding: 50px 10px 0 } fullchan-x-gallery .gallery__images { display: flex; width: 100%; height: 100%; justify-content: center; align-content: flex-start; gap: 4px 8px; flex-wrap: wrap; } fullchan-x-gallery .imgLink img { border: solid white 1px; } fullchan-x-gallery .imgLink[data-filemime="video/webm"] img { border: solid #68b723 4px; } fullchan-x-gallery .gallery__close { position: fixed; top: 60px; right: 35px; padding: 6px 14px; min-height: 30px; z-index: 10; } .gallery__main-image { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; justify-content: center; align-content: center; background: rgba(0,0,0,0.5); } .gallery__main-image img { padding: 40px 10px 15px; height: auto; max-width: calc(100% - 20px); object-fit: contain; } .gallery__main-image.active { display: flex; } /*-- Truncated file extentions --*/ .originalNameLink[data-file-ext] { max-width: 65px; } a[data-file-ext]:hover:after { content: attr(data-file-ext); } a[data-file-ext] + .file-ext { pointer-events: none; } a[data-file-ext]:hover + .file-ext { display: none; } /*-- Nav Board Links --*/ .nav-boards--custom { display: flex; gap: 3px; } #navTopBoardsSpan.hidden ~ #navBoardsSpan, #navTopBoardsSpan.hidden ~ .nav-fade, #navTopBoardsSpan a.hidden + span { display: none; } `; document.head.appendChild(style); // Asuka and Eris (fantasy Asuka) are best girls