- // ==UserScript==
- // @name 游戏社区(TapTap)列表页贴子预览
- // @namespace 游戏社区(TapTap)列表页贴子预览
- // @license GPL-3.0 License
- // @version 1.3.3
- // @description TapTap游戏社区列表页贴子卡片新增预览按钮,可在列表页直接预览贴子内容。
- // @author QIAN
- // @match *://www.taptap.cn/app/*/topic*
- // @grant GM_addStyle
- // @grant GM_info
- // @require https://scriptcat.org/lib/513/2.0.1/ElementGetter.js?#sha256=V0EUYIfbOrr63nT8+W7BP1xEmWcumTLWu2PXFJHh5dg=
- // @supportURL https://github.com/QIUZAIYOU/Taptap-PostPreview
- // @homepageURL https://github.com/QIUZAIYOU/Taptap-PostPreview
- // @icon https://assets.tapimg.com/cupid-apps/web-app/favicon.2.ico
- // ==/UserScript==
- // ?#sha256=V0EUYIfbOrr63nT8+W7BP1xEmWcumTLWu2PXFJHh5dg=
- (function () {
- 'use strict';
- const selector = {
- tap: '#__nuxt',
- momentCardFooter: '.moment-card__footer',
- previewWrapper: '#previewWrapper',
- previewIframe: '#previewIframe',
- previewIframeMask: '#previewIframeMask',
- previewContentHeader: 'header',
- previewContentMain: 'main',
- previewButton: '.previewButton',
- voteButton: '.vote-button.moment-card__footer-button',
- }
- const styles = {
- PreviewWrapperStyle: '#previewWrapper{position:fixed;top:50%;left:50%;overflow:hidden;box-sizing:border-box;width:600px;height:97vh;border:2px solid #00d9c5;border-radius:10px;background:#191919;transform:translate(-50%,-50%)}#previewWrapper::backdrop{backdrop-filter:blur(3px)}#previewIframe{width:100%;height:100%;border:none;background:#191919}#previewIframeMask{position:absolute;display:flex;background:#191919;inset:0;align-items:center;justify-content:center}.previewButton{cursor: pointer}',
- PreviewIframeStyle: 'body{background:#191919!important}header{display:none!important}main{margin-left:0!important}'
- }
- const utils = {
- /**
- * 休眠
- * @param {Number} 时长
- * @returns
- */
- sleep(times) {
- return new Promise(resolve => setTimeout(resolve, times))
- },
- logger: {
- info(content) {
- console.info('%cTapTap预览', 'color:white;background:#006aff;padding:2px;border-radius:2px', content);
- },
- warn(content) {
- console.warn('%cTapTap贴子预览', 'color:white;background:#ff6d00;padding:2px;border-radius:2px', content);
- },
- error(content) {
- console.error('%cTapTap贴子预览', 'color:white;background:#f33;padding:2px;border-radius:2px', content);
- },
- debug(content) {
- console.info('%cTapTap贴子预览(调试)', 'color:white;background:#cc00ff;padding:2px;border-radius:2px', content);
- },
- },
- createElementAndInsert(HtmlString, target, method) {
- const element = elmGetter.create(HtmlString, target)
- target[method](element)
- return element
- },
- insertStyleToDocument(id, css) {
- const styleElement = GM_addStyle(css)
- styleElement.id = id
- },
- htmlStringToDom(htmlString) {
- const tempDiv = document.createElement('div');
- tempDiv.innerHTML = htmlString.trim();
- return tempDiv.firstChild;
- },
- isAsyncFunction(targetFunction) {
- return targetFunction.constructor.name === 'AsyncFunction'
- },
- reloadCurrentTab(...args) {
- if (args && args[0] === true) {
- location.reload()
- } else if (vals.auto_reload()) location.reload()
- },
- executeFunctionsSequentially(functionsArray) {
- if (functionsArray.length > 0) {
- const currentFunction = functionsArray.shift()
- if (utils.isAsyncFunction(currentFunction)) {
- currentFunction().then(result => {
- if (result) {
- const { message, callback } = result
- if (message) utils.logger.info(message)
- if (callback && Array.isArray(callback)) utils.executeFunctionsSequentially(callback)
- }
- utils.executeFunctionsSequentially(functionsArray)
- }).catch(error => {
- utils.logger.error(error)
- utils.reloadCurrentTab()
- })
- } else {
- const result = currentFunction()
- if (result) {
- const { message } = result
- if (message) utils.logger.info(message)
- }
- }
- }
- },
- checkElementExistence(elementsArray) {
- if (Array.isArray(elementsArray)) {
- return elementsArray.map(element => Boolean(element))
- } else {
- return [Boolean(elementsArray)]
- }
- },
- async getElementAndCheckExistence(selectors, ...args) {
- let delay = 7000, debug = false
- if (args.length === 1) {
- const type = typeof args[0]
- if (type === 'number') delay = args[0]
- if (type === 'boolean') debug = args[0]
- }
- if (args.length === 2) {
- delay = args[0]
- debug = args[1]
- }
- const result = await elmGetter.get(selectors, delay)
- if (debug) utils.logger.debug(utils.checkElementExistence(result))
- return result
- },
- }
- const modules = {
- async insertPreviewElementToDocument() {
- const existingPreviewWrapper = await utils.getElementAndCheckExistence(selector.previewWrapper);
- if (existingPreviewWrapper) existingPreviewWrapper.remove();
-
- const previewElementHtml = `
- <div id="previewWrapper" popover>
- <div id="perViewFloat">
- <div id="previewClose"></div>
- <div id="previewEnterPost"><a href=""></a></div>
- </div>
- <div id="previewIframeMask">
- <div class="loading-dots__wrapper" type="dots" loading="true">
- <span class="loading-dots__dot" style="font-size: 6px;"></span>
- <span class="loading-dots__dot" style="font-size: 6px;"></span>
- <span class="loading-dots__dot" style="font-size: 6px;"></span>
- </div>
- </div>
- <iframe id="previewIframe" title="previewIframe"></iframe>
- </div>
- `;
- const previewWrapper = utils.createElementAndInsert(previewElementHtml, document.body, 'append');
- const previewIframe = previewWrapper.querySelector(selector.previewIframe);
- const previewIframeMask = previewWrapper.querySelector(selector.previewIframeMask);
- const previewIframeWindow = previewIframe.contentWindow;
-
- previewWrapper.addEventListener('toggle', (event) => {
- if (event.newState === 'closed') {
- previewIframe.src = '';
- previewIframeMask.style.display = 'flex';
- document.querySelector(selector.tap).style.pointerEvents = 'auto';
- }
- });
-
- previewIframe.addEventListener('load', () => {
- const previewContentHeader = previewIframeWindow.document.querySelector(selector.previewContentHeader);
- const previewContentMain = previewIframeWindow.document.querySelector(selector.previewContentMain);
- if (previewContentHeader && previewContentMain) {
- previewContentHeader.style.display = 'none';
- previewContentMain.style.marginLeft = '0';
- previewIframeMask.style.display = 'none';
- }
- });
- },
- async insertPreviewButtonToMomentCard() {
- const previewButtonHtml = `
- <div class="moment-card__footer-button">
- <span class="previewButton icon-button flex-center--y" data-booth-item="" data-track-prevent="click" data-booth-level="1">
- <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" class="icon" viewBox="0 0 1024 1024">
- <path fill="#d6d6d6" d="M935.253 494.933C849.067 294.827 686.933 170.667 512 170.667S174.933 294.827 88.747 494.933a42.667 42.667 0 0 0 0 34.134C174.933 729.173 337.067 853.333 512 853.333s337.067-124.16 423.253-324.266a42.667 42.667 0 0 0 0-34.134zM512 768c-135.253 0-263.253-97.707-337.067-256C248.747 353.707 376.747 256 512 256s263.253 97.707 337.067 256C775.253 670.293 647.253 768 512 768zm0-426.667A170.667 170.667 0 1 0 682.667 512 170.667 170.667 0 0 0 512 341.333zm0 256A85.333 85.333 0 1 1 597.333 512 85.333 85.333 0 0 1 512 597.333z"/>
- </svg>
- </span>
- </div>
- `;
-
- const [previewWrapper, previewIframe, voteButton] = await utils.getElementAndCheckExistence([selector.previewWrapper, selector.previewIframe, selector.voteButton]);
- const versionData = voteButton.dataset;
- await elmGetter.each(selector.momentCardFooter, async (momentCardFooter) => {
- const existingPreviewButton = momentCardFooter.querySelector(selector.previewButton);
- if (existingPreviewButton) existingPreviewButton.remove();
- const previewButton = utils.createElementAndInsert(previewButtonHtml, momentCardFooter, 'append');
- for (let version in versionData) {
- if (versionData.hasOwnProperty(version)) {
- previewButton.setAttribute(`data-${version}`, versionData[version]);
- }
- }
- const tapElement = document.querySelector(selector.tap);
- const momentId = momentCardFooter.parentElement.dataset.eventObjKey.split(":")[1]
- const momentLink = `https://www.taptap.cn/moment/${momentId}`;
- previewButton.addEventListener('click', (event) => {
- event.preventDefault();
- event.stopPropagation();
- previewWrapper.showPopover();
- previewIframe.src = momentLink;
- tapElement.style.pointerEvents = 'none';
- });
- });
- },
- thePrepFunction() {
- utils.insertStyleToDocument('PreviewWrapperStyle', styles.PreviewWrapperStyle)
- },
- theMainFunction() {
- modules.thePrepFunction()
- utils.logger.info(`脚本版本|${GM_info.script.version}`)
- utils.logger.info('当前标签|已激活|开始应用配置')
- const functionsArray = [
- modules.insertPreviewElementToDocument,
- modules.insertPreviewButtonToMomentCard
- ]
- utils.executeFunctionsSequentially(functionsArray)
- }
- }
- modules.theMainFunction()
- })();