User:Unready/ui.refresh.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:Unready/ui.refresh. This user script seems to have an accompanying .css page at User:Unready/ui.refresh.css. |
//__NOINDEX__
/*
* Description:
* Refresh page content periodically
*
* Version 1.0: 27 April 2015
* Original version for Wikipedia use
* Version 1.1: 28 April 2015
* Use jQuery
* Version 1.2: 29 April 2015
* Dynamically determine selector
* Allow interval configuration
* Version 1.3: 30 April 2015
* Support action=history
* Version 1.4: 3 May 2015
* Add programmatic toggle
*
* License: CC-BY-SA
* http://creativecommons.org/licenses/by-sa/3.0/
*/
((window.user = window.user || {}).ui = window.user.ui || {}).refresh =
(function (mw, $)
{
'use strict';
var g_self,
g_selector,
g_jqContent, g_jqInput, g_jqImg, g_jqLi,
g_wAction = mw.config.get('wgAction'),
g_interval = 120, // default refresh interval (seconds)
g_hTimeout = -1; // cannot run = -1; okay to run = 0; running > 0
// process click events on radio buttons for action=history
// mostly "borrowed" from MediaWiki 1.26wmf3
// example:
// $._data( $('li > input[type="radio"]')[0] ).events.click[0].handler
// it's not possible to attach the existing handler, because
// 1. the existing handler cannot deal with
// changing the number of list items
// 2. if the previous page had only one list item,
// there's no reference to the handler available
function updateDiffRadios()
{
var li, inputs, oldidRadio, diffRadio,
nextState = 'before';
if (!g_jqLi.length)
{
return;
}
g_jqLi.each(function()
{
li = $(this);
inputs = li.find('input[type="radio"]');
oldidRadio = inputs.filter('[name="oldid"]').eq(0);
diffRadio = inputs.filter('[name="diff"]').eq(0);
li.removeClass('selected between before after');
if (!oldidRadio.length || !diffRadio.length)
{
return;
}
if (oldidRadio.prop('checked'))
{
li.addClass('selected after');
nextState = 'after';
}
else if (diffRadio.prop('checked'))
{
li.addClass('selected ' + nextState);
nextState = 'between';
}
else
{
li.addClass(nextState);
}
});
}
// get interval (sec) from module properties
// and convert it to msec
function getInterval()
{
if ((typeof g_self.interval === 'number') &&
(g_self.interval > 0) &&
(g_self.interval < 604801 )) // 7 days + 1 sec
{
g_interval = g_self.interval;
}
else
{
g_self.interval = g_interval;
}
return g_interval * 1000;
}
// process checkbox events
function onCheck()
{
if (g_jqInput.prop('checked'))
{
if (g_hTimeout === 0)
{ // schedule a tick
g_jqImg.hide(); // in case it was showing after an error
g_hTimeout = window.setTimeout(onTick, getInterval());
g_self.message = 'OK';
}
}
else
{
if (g_hTimeout > 0)
{ // stop the scheduled tick
window.clearTimeout(g_hTimeout);
g_hTimeout = 0;
g_self.message = 'Stopped';
}
}
}
// process Ajax done event
function onDone(htmlString)
{
var jqNewContent = $(htmlString).find(g_selector);
if (jqNewContent.length !== 1)
{
g_self.message = 'onDone :: ' + jqNewContent.length +
' elements found for (' + g_selector + ')';
g_jqInput.prop('checked', false);
return;
}
// refresh content
g_jqContent.replaceWith(jqNewContent);
g_jqContent = jqNewContent;
// for action=history ...
if (g_wAction === 'history')
{ // ... attach click listeners and fake a click
g_jqLi = g_jqContent.find('li');
g_jqLi.find('input[type="radio"]')
.click(updateDiffRadios);
updateDiffRadios();
}
// go back to sleep
g_jqImg.hide();
g_hTimeout = window.setTimeout(onTick, getInterval());
}
// process Ajax fail event
function onFail(jqDummy, textStatus, errorThrown)
{
g_self.message = 'onFail :: ' + textStatus + ' ' + errorThrown;
g_jqInput.prop('checked', false);
}
// handle timer events,
function onTick()
{
g_hTimeout = 0;
g_jqImg.show();
// ajax defaults to window.location.href
$.ajax({dataType: 'html'})
.done(onDone)
.fail(onFail);
}
// turn refresh on and off programmatically
function toggle(state)
{
if (!!g_jqInput &&
(typeof state === 'boolean') &&
(g_jqInput.prop('checked') !== state))
{
g_jqInput.click();
}
}
// init g_self before document.ready
// in case document.ready executes immediately
g_self =
{
interval : g_interval,
message : 'Initializing',
toggle : toggle,
version : 'Version 1.4.1: 15 May 2015'
};
$(function main()
{
var jqSpan, jqH1,
i, done,
actions =
[
'view',
'history'
],
selectors =
[
'.mw-changeslist', // RecentChanges & Watchlist, all skins
'#pagehistory', // action=history, all skins
'#mw-content-text' // all pages, all skins
],
uriData =
[
'data:image/gif;base64,',
'R0lGODlhKwALAMIAAP///wAAAIKCggAAAP///////////////yH/C05FVFNDQVBF',
'Mi4wAwEAAAAh+QQFCgADACwAAAAAKwALAAADNDiyzPNQtRbhpHfWTCP/mgduYEl+',
'Z8mlGauG1ii+7bzadBejeL64sIfvAtQJR7yioHJsJQAAIfkEBQoAAgAsAAAAAAsA',
'CwAAAw0os8zaMMpJq70YPykSACH5BAUKAAEALAAAAAAbAAsAAAMtGLLM8TCMSalq',
'WERZ+8jY5nVgI45U6URoqmps+71n+8KQPKs1evejC2jzaAUSACH5BAEKAAEALBAA',
'AAAbAAsAAAMtGLLM8TCMSalqWERZ+8jY5nVgI45U6URoqmps+71n+8KQPKs1evej',
'C2jzaAUSADs='
].join(''),
htmlString =
[
'<span id="ui-refresh">',
'<label for="ui-refresh-input"',
' title="Enable auto-refresh">Auto-refresh:</label>',
'<input id="ui-refresh-input" type="checkbox">',
'<img src="' + uriData + '" alt="Refreshing page">',
'</span>'
].join('');
// allow listed actions, else deny
done = false;
for ( i = 0 ; (i < actions.length) && !done ; ++i )
{
done = (actions[i] === g_wAction);
}
if (!done)
{
g_self.message = 'main :: unsupported action : ' + g_wAction;
return;
}
// determine the "best" selector
done = false;
for ( i = 0 ; (i < selectors.length) && !done ; ++i )
{
g_selector = selectors[i];
g_jqContent = $(g_selector);
done = (g_jqContent.length === 1);
}
if (!done)
{
g_self.message = 'main :: No suitable selector';
return;
}
// span with all the new DOM elements
jqSpan = $(htmlString);
// checkbox checked, with handler
g_jqInput = jqSpan.children('input')
.prop('checked', true)
.click(onCheck);
// throbber, initially hidden
g_jqImg = jqSpan.children('img')
.hide();
// add span to h1
jqH1 = $('h1:first:visible'); // Minerva made me do it
if (jqH1.length !== 1)
{
g_self.message = 'main :: Problem with (h1:first:visible)';
g_jqInput = undefined;
g_jqImg = undefined;
return;
}
jqH1.append(jqSpan);
// done with DOM
g_hTimeout = window.setTimeout(onTick, getInterval());
g_self.message = 'OK';
});
return g_self;
}(mediaWiki, jQuery));