您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Update threads without refreshing
当前为
// ==UserScript== // @name ResetEra Live Thread // @namespace http://madjoki.com // @version 3.9.1 // @description Update threads without refreshing // @author Madjoki // @match https://www.resetera.com/threads/* // @match https://metacouncil.com/threads/* // @grant none // ==/UserScript== (function() { 'use strict'; var globalSettings = { rememberThreads: true, updateTime: 30, enabledByDefault: false, useNewMessageMarker: true, enableDebug: false, }; var windowID = Math.floor(Math.random() * 100000); var timeoptions = [ { name: "5s", value: 5, }, { name: "10s", value: 10, }, { name: "15s", value: 15, }, { name: "30s (Default)", value: 30, }, { name: "1m", value: 60, }, { name: "2m", value: 120, }, ]; $('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 #AjaxProgress {\ display: none !important;\ }\ .liveThreadControls a {\ padding: 5px;\ }\ </style>'); var highlightScript = $('script:contains("data-author")').first().text(); function addoptions(el, values) { $(el).find("option").remove(); $(values).each(function (i, o) { $(el).append($("<option>", {text: o.name, value: o.value})); }) } // Read Global Settings var settingsJson = localStorage.getItem("livethreadSettings"); if (settingsJson !== null) { globalSettings = JSON.parse(settingsJson); // Update settings by defaults globalSettings.updateTime = globalSettings.updateTime || 30; globalSettings.enabledByDefault = globalSettings.enabledByDefault || false; globalSettings.enableDebug = globalSettings.enableDebug || false; if (globalSettings.useNewMessageMarker === undefined) globalSettings.useNewMessageMarker = true; } var defaultThreadSettings = { updateTime: globalSettings.updateTime, enabled: globalSettings.enabledByDefault, }; var pageType = ""; if (location.path == "/") return; pageType = "thread"; var threadID = parseInt($('[data-lb-id]').data('lb-id').replace("thread-", ""), 10); console.log(threadID); var currentThreadSettings = getSettings(threadID); var isRememberedThread = globalSettings.rememberThreads; var pauseReason = null; var hasFocus = true; var newMessageMarkerLast; var newMessageMarker; if (currentThreadSettings === null) { isRememberedThread = globalSettings.rememberThreads && !hasSettings(threadID); currentThreadSettings = defaultThreadSettings; } else { isRememberedThread = true } function calculateNextUpdate() { var timer = currentThreadSettings.updateTime; // If we have more pages to load, use shorter timer if (currentPage < lastPage) timer = 5; timeToNextUpdate = timer; } function hasSettings(thread) { var key = "livethread_" + thread; return key in localStorage; } function getSettings(thread) { var stored = localStorage.getItem("livethread_" + thread); if (stored === null) return null; return JSON.parse(stored); } function setSettings(thread, settings) { localStorage.setItem("livethread_" + thread, JSON.stringify(settings)); } function saveSettings() { if (isRememberedThread) setSettings(threadID, currentThreadSettings); else setSettings(threadID, null); localStorage.setItem("livethreadSettings", JSON.stringify(globalSettings)); redraw(); } var countNewLast = 0; var errors = 0; var updating = false; var lastUrl = window.location; var currentPage = parseInt($('li.pageNav-page--current').first().text() || "1", 10); var lastLoadedPage = currentPage; var lastPage = $('.pageNav-main li').last().text() || 1; var threadTitle = $('title').text(); var timeToNextUpdate = 60; calculateNextUpdate(); if (currentPage != lastPage) currentThreadSettings.enabled = false; // Do not enable if no messages (ie. edit / reply page) if ($('article.message').length === 0) { console.log("disabled - not message page"); return; } $('article.message').each(function (i, el) { var $el = $(el); $el.data('livethread-page', currentPage); }); var timeout = setInterval(timerTick, 1000); function timerTick() { if (!currentThreadSettings.enabled) return; timeToNextUpdate--; if (timeToNextUpdate === 0) { updateMessages(); timeToNextUpdate = currentThreadSettings.updateTime; } redraw(); } // Inserts marker after last message function insertNotifi(text) { var $el = $('<div>', {'class': "newMessagesNotice livethreadSeparator"}); $el.append($('<span>').text(text)); $el.append($('<a href="#" class="pull-right"><i class="fa fa-close"></a>').click(function (event) { event.preventDefault(); $el.prevAll('div.newMessagesNotice').remove(); $el.prevAll('li.message').removeClass('livethread_unread').hide(); $el.remove(); })); $("div.block-body").append($el); return $el; } // Get URL for current page function getCurrentURL() { var pageNav = $('div.PageNav[data-page]').first(); currentPage = pageNav.data('page') || 1; lastPage = pageNav.data('last') || 1; if (pageNav.data('baseurl') === undefined) return window.location; if (lastPage > currentPage) currentPage++; return pageNav.data('baseurl').replace('{{sentinel}}', currentPage); } function updateMessages() { if (updating) return; updating = true; redraw(); countNewLast = 0; var thisUrl = getCurrentURL(); lastUrl = getCurrentURL(); $('body').addClass('liveThread_loading'); $.get(lastUrl, function (data) { var pageOpenTime = (new Date().getTime() / 1000), pageOpenLength = pageOpenTime - 0//XenForo._pageLoadTime; // If new page insert marker and update history if (lastLoadedPage < currentPage) { window.history.pushState(null, null, thisUrl); lastUrl = thisUrl; insertNotifi("Page " + currentPage); lastLoadedPage = currentPage; } var node = $($.parseHTML(data, document, true)); function decodeEmails() { if ($('[data-cfemail]').length > 0) { var emailDecoder = node.find('script[src*="email-decode"]').attr('src'); $.getScript(emailDecoder); } } var topGroup = $('Div.pageNavLinkGroup').last(); var botGroup = $('Div.pageNavLinkGroup').first(); var newNav = node.find('div.PageNav').first(); if (newNav.length) { var topNav = topGroup.find('div.PageNav'); var botNav = botGroup.find('div.PageNav'); if (topNav.length) topNav.replaceWith(newNav.clone()); else topGroup.append(newNav.clone()); if (botNav.length) botNav.replaceWith(newNav.clone()); else botGroup.append(newNav.clone()); } // To avoid reloading mesasges after posting $('input[name="last_date"]').val(node.find('input[name="last_date"]').val()); $('input[name="last_known_date"]').val(node.find('input[name="last_known_date"]').val()); var anyChanges = false; node.find('article.message').each(function (i, el) { var $el = $(el); var id = $el.attr('id'); var $curr = $('#' + id); var forchecks = $el.find('div.message-userContent').clone(); forchecks.find('iframe').remove(); forchecks.find('noscript').remove(); // Sometimes bbcode has "X Said: <" and other times "X Said:<". // This makes them considered equal // Also IDs var newData = forchecks.html().replace(/\s+</g, "<").replace(/>\s+/g, ">").replace(/\W([a-f0-9]{4,})\W/g, ""); // Update old message if changed if ($curr.length) { var newMessage = $el.find('.messageInfo'); var oldMessage = $curr.find('.messageInfo'); var postDateEl = newMessage.find('.postDate abbr'); var editDateEl = newMessage.find('.editDate abbr'); // XenForo Assumes timestamps are relative to page open time postDateEl.data('diff', postDateEl.data('diff') - pageOpenLength); if (editDateEl !== undefined) editDateEl.data('diff', editDateEl.data('diff') - pageOpenLength); var embeds = $curr.find('iframe'); var editTimeNew = $el.find('.editDate abbr').data('time'); var editTimeOld = $curr.find('.editDate abbr').data('time'); var hasDifferentContent = newData !== $curr.data('original') && $curr.data('original') !== undefined; var hasEditTimeChanged = editTimeNew !== editTimeOld; // For debugging: if (globalSettings.enableDebug) { console.log("Message Updated. Before: "); console.log($curr.data('original')); console.log("After: "); console.log(newData); } // Update if there's zero embeds if (embeds.length == 0 && (hasDifferentContent || editTimeNew != editTimeOld)) { anyChanges = true; oldMessage.replaceWith(newMessage); $curr.xfActivate(); } else if ((hasDifferentContent) || (editTimeNew != editTimeOld)) { anyChanges = true; var edit = $('<a>', {'href': '#', 'class': 'refresh', 'text': 'Show Edited (Reset Embeds)'}); edit.click(function (ev) { ev.preventDefault(); oldMessage.replaceWith(newMessage); $curr.xfActivate(); decodeEmails(); }); $curr.find('.postDate .refresh').remove(); $curr.find('.postDate').append(edit); } $curr.data('original', newData); } // Insert new messages else { anyChanges = true; if (!hasFocus && globalSettings.useNewMessageMarker && newMessageMarker === undefined) { newMessageMarker = insertNotifi(""); newMessageMarker.data('count', 0); } $el.data('livethread-page', currentPage); $el.addClass('livethread_unread'); var postDateEl = $el.find('.postDate abbr'); var editDateEl = $el.find('.editDate abbr'); // XenForo Assumes timestamps are relative to page open time postDateEl.data('diff', postDateEl.data('diff') - pageOpenLength); if (editDateEl !== undefined) editDateEl.data('diff', editDateEl.data('diff') - pageOpenLength); //$el.xfInsert('appendTo', $("div.block-body")); $(".block-body.js-replyNewMessageContainer").append($el); XF.activate($el); $el.data('original', newData); countNewLast++; } }); if (anyChanges) { try { document.dispatchEvent(new CustomEvent("LiveThreadUpdate")); } catch (ignore) {} } decodeEmails(); // Update message times //XenForo._TimestampRefresh.refresh(null, true); // Highlight Own Messages jQuery.globalEval(highlightScript); var count = countNewLast; if (newMessageMarker !== undefined) { count = newMessageMarker.data('count') + countNewLast; newMessageMarker.data('count', count); newMessageMarker.find('span').text(count + " new messages"); } }).always(function () { updating = false; calculateNextUpdate(); redraw(); $('body').removeClass('liveThread_loading'); }); } // Control Panel function updateForm() { addoptions($("#updateTime"), timeoptions); addoptions($("#updateTimeDefault"), timeoptions); $("#updateTime option[value='" + currentThreadSettings.updateTime + "']").attr("selected", true); $("#updateTimeDefault option[value='" + globalSettings.updateTime + "']").attr("selected", true); $("#liveThread_remember").attr("checked", globalSettings.rememberThreads); $("#liveThread_messageMarkers").attr("checked", globalSettings.useNewMessageMarker); $("#liveThread_enableByDefault").attr("checked", globalSettings.enabledByDefault); $('#liveThread_currentRemember').attr("checked", isRememberedThread); $("#liveThread_debug").attr("checked", globalSettings.enableDebug); } 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 getStatusText() { var status = ""; if (updating) status += "Updating"; else if (currentThreadSettings.enabled) { status += "Next Update In " + timeToNextUpdate + " seconds"; if (countNewLast > 0) status += " - " + countNewLast + " New Messages!"; } else return pauseReason || ""; return status; } console.log("buildingg controls"); // Build Controls $('a.postsRemaining').hide(); 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); $('.block-outer').first().append(controlsContainer.clone()); $('.livethreadStartPause').click(function (event) { event.preventDefault(); pauseReason = ""; currentThreadSettings.enabled = !currentThreadSettings.enabled; saveSettings(); redraw(); }); $('.livethreadRefresh').click(function (event) { event.preventDefault(); updateMessages(); }); $('.livethreadSettings').click(function (event) { event.preventDefault(); $('#livethreadPanel').toggle(); $('#livethreadPanel').scrollintoview(); }); // Update Controls function updateControls() { $(".livethreadStartPause i").toggleClass('fa-pause', currentThreadSettings.enabled); $(".livethreadStartPause i").toggleClass('fa-play', !currentThreadSettings.enabled); $(".livethreadRefresh i").toggleClass('fa-spin', updating); $(".livethreadStatus").text(getStatusText()); } // 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>\ <li><label><input type="checkbox" id="liveThread_currentRemember" value="1"> Remember this thread</label></li>\ </ul>\ <h2 class="heading h1">Global Settings</h2>\ <ul>\ <li><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><input type="checkbox" id="liveThread_messageMarkers" value="1"> Insert Marker for New Messages</label></li>\ <li><label>Default Update Speed: <select id="updateTimeDefault" class="textCtrl"></select></label></li>\ <li><label><input type="checkbox" id="liveThread_debug" value="1"> Log Debug Data to Console (only for testing)</label></li>\ </ul>\ </div>'); $('#liveThread_currentRemember').change(function () { isRememberedThread = $('#liveThread_currentRemember').is(':checked'); saveSettings(); }); $('#liveThread_enableByDefault').change(function () { globalSettings.enabledByDefault = $('#liveThread_enableByDefault').is(':checked'); saveSettings(); }); $('#liveThread_messageMarkers').change(function () { globalSettings.useNewMessageMarker = $('#liveThread_messageMarkers').is(':checked'); saveSettings(); }); $('#liveThread_remember').change(function () { globalSettings.rememberThreads = $('#liveThread_remember').is(':checked'); saveSettings(); }); $('#liveThread_debug').change(function () { globalSettings.enableDebug = $('#liveThread_debug').is(':checked'); saveSettings(); }); $('#updateTime').change(function () { currentThreadSettings.updateTime = parseInt($('#updateTime').val()); saveSettings(); if (currentThreadSettings.updateTime < timeToNextUpdate) calculateNextUpdate(); redraw(); }); 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'); } }); } $('#updateTimeDefault').change(function () { globalSettings.updateTime = parseInt($('#updateTimeDefault').val()); saveSettings(); }); $(window).scroll(function () { handleScroll(); newMessageMarker = undefined; redraw(); }); $(window).focus(function () { // Reset new messages on focus handleScroll(); redraw(); hasFocus = true; newMessageMarker = undefined; }); $(window).focusout(function () { handleScroll(); redraw(); hasFocus = false; newMessageMarker = undefined; }); function redraw() { updateControls(); $('body').toggleClass('liveThread_enabled', currentThreadSettings.enabled); var unreadMessages = $('.livethread_unread').length; var newTitle = document.title; if (unreadMessages > 0) newTitle = "(" + unreadMessages + ") " + threadTitle; else newTitle = threadTitle; if (newTitle != document.title) document.title = newTitle; } redraw(); updateForm(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址