您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Update threads without refreshing
// ==UserScript== // @name ResetEra Live Thread // @namespace http://madjoki.com // @version 4.0.15 // @description Update threads without refreshing // @author Madjoki // @match https://metacouncil.com/threads/* // @match https://www.resetera.com/threads/* // @match https://bbs.io-tech.fi/threads/* // @require https://cdnjs.cloudflare.com/ajax/libs/favico.js/0.3.10/favico.min.js // @grant none // ==/UserScript== (function () { 'use strict'; let favicon; // This is to disable scrolldown behaviour when XenForo insert "new messages" box. const original = XF.Message.insertMessages; XF.Message.insertMessages = function (dataHtml, $container, ascending, onInsert) { console.log(dataHtml, $container, ascending, onInsert); if (dataHtml.content.indexOf('js-newMessagesIndicator') > -1) return; original(dataHtml, $container, ascending, onInsert); } var favIconUpdate = function (count) { favicon.badge(count); favicon.badge(count); } if (window.location.host === 'www.resetera.com') { // Chrome Fix //let icon = $('link[rel*=icon]').first().clone(); //$('link[rel*=icon]').remove(); //$('head').append(icon); favicon = new Favico({ animation: 'none', fontFamily: 'FontAwesome', fontStyle: 'normal' }); } else { favicon = new Favico({ animation: 'none', fontFamily: 'FontAwesome', fontStyle: 'normal' }); } favIconUpdate(); var timeoptions = [ { name: "5s", value: 5, }, { name: "10s", value: 10, }, { name: "15s", value: 15, }, { name: "30s", value: 30, }, { name: "1m", value: 60, }, { name: "2m", value: 120, }, ]; let defaults = { timer: 5, enabledByDefault: false, }; let threadID = $('html').data('content-key'); let userSettings = {} let threadSettings = {} let recentErrors = 0; let countNewMessages = 0; let updating = false; let enabled = false; let paused = false; let currentTimer = 120; let hasFocus = true; // Read Global Settings let settingsJson = localStorage.getItem("livethreadSettings"); if (settingsJson !== null) userSettings = JSON.parse(settingsJson) || {}; // Read Thread Settings let threadJson = localStorage.getItem("livethread_" + threadID); if (threadJson !== null) threadSettings = JSON.parse(threadJson) || {}; let currentSettings = {} function updateSettings() { currentSettings = { ...defaults, ...userSettings, ...threadSettings } if (!("enabled" in currentSettings)) currentSettings.enabled = currentSettings.enabledByDefault; if (!currentSettings.timer || currentSettings.timer < 0) currentSettings.enabled = false; enabled = currentSettings.enabled; paused = !enabled; } updateSettings(); currentTimer = currentSettings.timer; function getPages(dom) { return { current: parseInt(dom.find('li.pageNav-page--current').first().text(), 10) || 0, next: parseInt(dom.find('.pageNav-page.pageNav-page--later').first().text()) || parseInt(dom.find('.pageNav-page').last().text()) || 0, last: parseInt(dom.find('.pageNav-page').last().text()) || 0 }; } function updateFavIcon() { if (countNewMessages > 0) { favIconUpdate(countNewMessages); } else if (currentSettings.enabled && !paused) { favIconUpdate(''); } else { favIconUpdate(0); } } function addoptions(el, values) { $(el).find("option").remove(); $(values).each(function (i, o) { $(el).append($("<option>", {text: o.name, value: o.value})); }) } // CSS $('body').append(`<style> #livethreadPanel { display: none; text-align: center; } #livethreadPanel ul { display: inline-block; margin-bottom: 15px; } #livethreadPanel ul li { display: block; text-align: left; } #updateTime { margin-left: 5px; padding: 0px; } #updateTimeDefault { margin-left: 5px; padding: 0px; } body.darktheme #livethreadPanel ul { color: #8e50be; /*dark theme only*/ } .livethreadStatus { text-align: center; } .liveThread_enabled .globalAction { display: none !important; } .liveThreadControls a { padding: 5px; } </style>`); function getTimeOfLastMessage() { return $('article.message time').last().data('time'); } function getPageUrl(page) { $('meta[property="og:url"]').attr('content') + `page-${page}`; } // Get date of last message var $lastDate = $('input[name="last_date"]'); var $container = $('.js-replyNewMessageContainer'); var pages = getPages($('body')); var lastPageWithData = pages.current; // If zero messages, it's non thread page like reply page if ($('article.message').length === 0) return; // Pause if this isn't last page if (pages.current !== pages.last) paused = true; // Create Control Panel var controlsContainer = $('<div>', { class: 'block-outer-opposite liveThreadControls' }); var statusText = $('<a>', { href: '#', class: 'livethreadStatus livethreadRefresh postsRemaining' }); var startPauseBtn = $('<a>', { href: '#', class: 'livethreadStartPause' }).append($('<i>', { class: 'fa' })); var settingsBtn = $('<a>', { href: '#', class: 'livethreadSettings' }).append($('<i>', { class: 'fa fa-cog' })); var refreshBtn = $('<a>', { href: '#', class: 'livethreadRefresh' }).append($('<i>', { class: 'fa fa-refresh' })); controlsContainer.append(statusText); controlsContainer.append(startPauseBtn); controlsContainer.append(refreshBtn); controlsContainer.append(settingsBtn); $('.block-outer.block-outer--after').append(controlsContainer); // Build Settings $('.block-outer.block-outer--after').last().after('\ <div id="livethreadPanel" class="DiscussionListOptions secondaryContent">\ <h2 class="heading h1">This Thread</h2>\ <ul>\ <li><label for="updateTime">Update Speed:</label> <select id="updateTime" class="textCtrl"></select></li>\ </ul>\ <h2 class="heading h1">Global Settings</h2>\ <ul>\ <li style="display: none"><label><input type="checkbox" id="liveThread_remember" value="1"> Remember New Threads by Default</label></li>\ <li><label><input type="checkbox" id="liveThread_enableByDefault" value="1"> Enable By Default</label></li>\ <li><label>Default Update Speed: <select id="updateTimeDefault" class="textCtrl"></select></label></li>\ <li style="display: none"><label><input type="checkbox" id="liveThread_debug" value="1"> Log Debug Data to Console (only for testing)</label></li>\ </ul>\ </div>'); function saveSettings() { localStorage.setItem("livethread_" + threadID, JSON.stringify(threadSettings)); localStorage.setItem("livethreadSettings", JSON.stringify(userSettings)); updateForm(); } $('#liveThread_enableByDefault').change(function () { userSettings.enabledByDefault = $('#liveThread_enableByDefault').is(':checked'); saveSettings(); }); $('#liveThread_messageMarkers').change(function () { userSettings.useNewMessageMarker = $('#liveThread_messageMarkers').is(':checked'); saveSettings(); }); $('#liveThread_remember').change(function () { userSettings.rememberThreads = $('#liveThread_remember').is(':checked'); saveSettings(); }); $('#liveThread_debug').change(function () { userSettings.enableDebug = $('#liveThread_debug').is(':checked'); saveSettings(); }); $('#updateTime').change(function () { const time = parseInt($('#updateTime').val()); if (time) threadSettings.timer = parseInt(time); else delete threadSettings.timer; saveSettings(); }); $('#updateTimeDefault').change(function () { userSettings.timer = parseInt($('#updateTimeDefault').val()); saveSettings(); }); // Control Panel function updateForm() { addoptions($("#updateTime"), [{ name: "Default", value: 0, }, { name: "Disabled", value: -1, }, ...timeoptions]); addoptions($("#updateTimeDefault"), timeoptions); $("#updateTime option[value='" + threadSettings.timer + "']").attr("selected", true); $("#updateTimeDefault option[value='" + userSettings.timer + "']").attr("selected", true); $("#liveThread_remember").attr("checked", userSettings.rememberThreads); //$("#liveThread_messageMarkers").attr("checked", userSettings.useNewMessageMarker); $("#liveThread_enableByDefault").attr("checked", userSettings.enabledByDefault); //$('#liveThread_currentRemember').attr("checked", isRememberedThread); //$("#liveThread_debug").attr("checked", globalSettings.enableDebug); } updateForm(); function insertMessagesAlternative(data) { console.log(data); var html = $.parseHTML(data.html.content); var $html = $(html); var pagesNew = getPages($html); if (pagesNew.current !== pages.current) { console.log("page changed", pagesNew, pages); pages = pagesNew; lastPageWithData = pages.current; history.pushState({}, "", `page-${pages.current}`); var $navNew = $html.find('.pageNavWrapper').first(); $('.pageNavWrapper').html($navNew.html()); } $html.find('article.message').each(function () { insertMessage($(this), $container, true); }); updateFavIcon(); updating = false; } function insertMessages(data) { if (data.message) { recentErrors++; return; } if (data.lastDate) $lastDate.val(data.lastDate); recentErrors = 0; if (data.html) { XF.setupHtmlInsert(data.html, function ($html, container, onComplete, onInsert) { // TODO: Check if DIV is there and load additional messages automatically var div = $html.children('div'); if (div.length) { console.log(div); } $html.each(function () { if (!this.tagName) { return; } if (this.tagName === 'DIV') { console.log(this); var $msg = $(this); // TEMP $container.append(this); } insertMessage($(this), $container, true); }); if (onInsert) { onInsert($html); } updateFavIcon(); updating = false; }); } } function insertMessage($message, $container, ascending) { if (!$message.data('author')) // Fix for empty messages return; // post-15795528 var id = $message.attr('id'); var $msg = $(`#${id}`); if ($msg.length) { console.log(`not inserting ${id}, already in page`); // TODO: update return; } countNewMessages++; var $firstChild = $container.children().first(); //$message.hide(); if ($firstChild.is('form') && !ascending) { $message.insertAfter($firstChild); } else if (!ascending) { $container.prepend($message); } else { $container.append($message); } //$message.xfFadeDown(); $message.addClass('livethread_unread'); XF.activate($message); } function loadMessages() { if (updating) return; if (lastPageWithData !== pages.last) loadMessagesAlternative(); else loadMessagesFast(); updating = true; } // Use api to get messages function loadMessagesFast() { XF.ajax('GET', 'new-posts', { after: getTimeOfLastMessage() }, insertMessages).always(function () { updating = false }, {useError: false}); updateControls(); } function loadMessagesAlternative() { const page = pages.next || pages.current; XF.ajax('GET', `page-${page}`, insertMessagesAlternative).always(function () { updating = false }, {useError: false}); } function timer() { if (paused || !enabled) return; currentTimer--; // Delay if there's recent errors var errorDelay = 10000 * Math.min(5, recentErrors); if (currentTimer === 0) { loadMessages(); currentTimer = currentSettings.timer + errorDelay; } updateControls(); } function getStatusText() { var status = ""; if (updating) status += "Updating"; else if (currentTimer && !paused && enabled) { status += "Next Update In " + currentTimer + " seconds"; //if (countNewLast > 0) // status += " - " + countNewLast + " New Messages!"; } else status = "Disabled"; return status; } function updateControls() { $(".livethreadStartPause i").toggleClass('fa-pause', !paused); $(".livethreadStartPause i").toggleClass('fa-play', paused); $(".livethreadRefresh i").toggleClass('fa-spin', updating); $(".livethreadStatus").text(getStatusText()); $('body').toggleClass('liveThread_enabled', !paused); } function isvisible($ele) { var lBound = $(window).scrollTop(), uBound = lBound + $(window).height(), top = $ele.offset().top, bottom = top + $ele.outerHeight(true); return (top > lBound && top < uBound) || (bottom > lBound && bottom < uBound) || (lBound >= top && lBound <= bottom) || (uBound >= top && uBound <= bottom); } function handleScroll() { $('.livethread_unread').each(function (i, el) { var $el = $(el); if (isvisible($el)) { $el.removeClass('livethread_unread'); $el.prevAll('.livethread_unread').removeClass('livethread_unread'); } }); countNewMessages = $('.livethread_unread').length; updateFavIcon(); } $(window).scroll(function () { handleScroll(); }); $(window).focus(function () { handleScroll(); hasFocus = true; }); $(window).focusout(function () { handleScroll(); hasFocus = false; }); updateControls(); setInterval(timer, 1000); $('.livethreadRefresh').click(function (event) { event.preventDefault(); loadMessages(); }); $('.livethreadStartPause').click(function (event) { event.preventDefault(); enabled = true; paused = !paused; updateControls(); }); $('.livethreadSettings').click(function (event) { event.preventDefault(); $('#livethreadPanel').toggle(); //$('#livethreadPanel').scrollintoview(); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址