Jump to content

User:Unready/ui.refresh.js

From Wikipedia, the free encyclopedia
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
//__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));