// ==UserScript==
// @name ResetEra Live Thread
// @namespace http://madjoki.com
// @version 1.9
// @description Update threads without refreshing
// @author Madjoki
// @match https://www.resetera.com/threads/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
var globalSettings = {
rememberThreads: true,
updateTime: 30,
enabledByDefault: false,
useNewMessageMarker: true,
};
var timeoptions = [
{
name: "Fast (15s)",
value: 15,
},
{
name: "Normal (30s)",
value: 30,
},
{
name: "Slow (1m)",
value: 60,
},
{
name: "Very Slow (2m)",
value: 120,
},
];
function addoptions(el, values) {
$(el).find("option").remove();
$(values).each(function (i, o) {
$(el).append($("<option>", {text: o.name, value: o.value}));
})
}
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.useNewMessageMarker = globalSettings.useNewMessageMarker || true;
}
var defaultThreadSettings = {
updateTime: globalSettings.updateTime,
enabled: globalSettings.enabledByDefault,
};
var threadID = parseInt($('[name="type[post][thread_id]"]').val(), 10);
var currentThreadSettings = getSettings(threadID);
var isRememberedThread = globalSettings.rememberThreads;
var hasFocus = true;
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));
updateForm();
updateStatus();
}
var countNew = 0;
var countNewLast = 0;
var errors = 0;
var updating = false;
var lastUrl = window.location;
var currentPage = $('div.PageNav').first().data('page') || 1;
var lastLoadedPage = currentPage;
var lastPage = $('div.PageNav').first().data('last') || 1;
var threadTitle = $('title').text();
var timeToNextUpdate = 60;
calculateNextUpdate();
// Do not enable if no messages (ie. edit / reply page)
if ($('#messageList > li').length === 0)
return;
$('#messageList > li').each(function (i, el) {
var $el = $(el);
$el.data('livethread-page', currentPage);
$el.data('original', $el.find('article').text());
});
var timeout = setInterval(timerTick, 1000);
function updateStatus()
{
$('body').toggleClass('liveThread_enabled', currentThreadSettings.enabled);
var status = "";
if (updating)
status += "Updating";
else if (currentThreadSettings.enabled)
{
status += "Next Update In " + timeToNextUpdate + " seconds - ";
if (countNewLast > 0)
status += countNewLast + " New Messages!";
else
status += "No New Messages";
}
else
status += "Auto Update Disabled";
$("#livethreadStatus").text(status);
if (countNew > 0)
$('title').text("(" + countNew + ") " + threadTitle);
else
$('title').text(threadTitle);
}
function enableDisable(event)
{
event.preventDefault();
currentThreadSettings.enabled = !currentThreadSettings.enabled;
saveSettings();
updateStatus();
}
function timerTick()
{
if (!currentThreadSettings.enabled)
return;
timeToNextUpdate--;
if (timeToNextUpdate === 0)
{
updateMessages();
timeToNextUpdate = currentThreadSettings.updateTime;
}
updateStatus();
}
// 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();
}));
$("#messageList").append($el);
return $el;
}
// Get URL for current page
function getCurrentURL()
{
var pageNav = $('div.PageNav').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 updateMessagesClick(event)
{
event.preventDefault();
updateMessages();
}
function updateMessages()
{
if (updating)
return;
updating = true;
updateStatus();
countNewLast = 0;
var thisUrl = getCurrentURL();
lastUrl = getCurrentURL();
$('body').addClass('liveThread_loading');
$.get(lastUrl, function (data) {
// 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));
var newNav = node.find('div.PageNav').first();
$('div.PageNav').each(function (i, el) {
$(el).replaceWith(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());
node.find('#messageList > li').each(function (i, el) {
var $el = $(el);
var id = $el.attr('id');
var $curr = $('#' + id);
$el.find('noscript').remove();
$el.data('original', $el.find('article').text());
// Update old message if changed
if ($curr.length)
{
var newMessage = $el.find('article');
var oldMessage = $curr.find('article');
if ($el.data('original') != $curr.data('original'))
{
oldMessage.replaceWith(newMessage).xfActivate();
$curr.data('original', $el.data('original'));
$curr.xfActivate();
}
}
// Insert new messages
else
{
if (!hasFocus && globalSettings.useNewMessageMarker && newMessageMarker === undefined)
{
newMessageMarker = insertNotifi("");
}
$el.data('livethread-page', currentPage);
$el.addClass('livethread_unread');
$el.xfInsert('appendTo', $("#messageList"));
countNew++;
countNewLast++;
}
});
// Highlight Own Messages
var username = $('#AccountMenu h3 a').text().trim();
$('.bbCodeQuote[data-author="' + username + '"] .attribution').css("background-color", "rgb(223, 211, 237)");
$('.bbCodeQuote[data-author="' + username + '"] .quoteContainer').css("background-color", "rgb(223, 211, 237)");
if (newMessageMarker !== undefined)
{
newMessageMarker.find('span').text(countNew + " new messages");
}
if ($('[data-cfemail]').length > 0)
{
var emailDecoder = node.find('script[src*="email-decode"]').attr('src');
console.log(emailDecoder);
$.getScript(emailDecoder);
}
}).always(function () {
updating = false;
calculateNextUpdate();
updateStatus();
$('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);
$('#livethreadEnabled > span').text(currentThreadSettings.enabled ? "Turn Off" : "Turn On");
$('#liveThread_currentRemember').attr("checked", isRememberedThread);
}
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);
}
$('Div.pageNavLinkGroup').last().before('\
<style>\
#livethreadControl {\
text-align: center;\
margin-bottom: 5px;\
}\
#livethreadStatus {\
text-align: center; \
}\
#livethreadSettings {\
text-align: center; \
}\
#livethreadGlobalSettings {\
display: none;\
}\
.liveThread_enabled #AjaxProgress {\
display: none !important;\
}\
.livethreadSeparator {\
color: white;\
}\
.livethreadSeparator a:link {\
color: white;\
}\
</style>\
<div id="livethreadPanel" class="DiscussionListOptions secondaryContent">\
<div id="livethreadStatus"></div>\
<div id="livethreadControl" class="publicControls">\
<a id="livethreadEnabled" href="#" class="callToAction"><span>Turn On</span></a>\
<a id="livethreadUpdate" href="#" class="callToAction"><span>Update Now</span></a>\
<a id="livethreadSettingsToggle" href="#" class="callToAction"><span>Settings</span></a>\
</div>\
<div id="livethreadSettings">\
<div class="controlGroup">\
<label for="updateTime">Update Speed:</label>\
<select id="updateTime" class="textCtrl"></select>\
<label><input type="checkbox" id="liveThread_currentRemember" value="1"> Remember this thread</label>\
</div>\
<div id="livethreadGlobalSettings">\
<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>\
</ul>\
</div>\
</div>');
updateForm();
updateStatus();
$('#livethreadEnabled').click(enableDisable);
$('#livethreadUpdate').click(updateMessagesClick);
$('#livethreadSettingsToggle').click(function (event) {
event.preventDefault();
$('#livethreadGlobalSettings').toggle();
});
$('#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();
});
$('#updateTime').change(function () {
currentThreadSettings.updateTime = parseInt($('#updateTime').val());
saveSettings();
if (currentThreadSettings.updateTime < timeToNextUpdate)
calculateNextUpdate();
updateStatus();
});
$('#updateTimeDefault').change(function () {
globalSettings.updateTime = parseInt($('#updateTimeDefault').val());
saveSettings();
});
$(window).scroll(function () {
$('.livethread_unread').each(function (i, el) {
var $el = $(el);
if (isvisible($el.find('div.messageMeta')))
$el.removeClass('livethread_unread');
});
});
$(window).focus(function () {
// Reset new messages on focus
countNew = 0;
updateStatus();
hasFocus = true;
newMessageMarker = undefined;
});
$(window).focusout(function () {
countNew = 0;
updateStatus();
hasFocus = false;
newMessageMarker = undefined;
});
updateStatus();
})();