Note: After saving, changes may not occur immediately. Click here to learn how to bypass your browser's cache.
  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Cmd-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (Cmd-Shift-R on a Mac)
  • Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Clear the cache in Tools → Preferences

For details and instructions about other browsers, see Wikipedia:Bypass your cache.

/*
 * Does a lookup two previous Page:* from the current, extract the rh from
 * its content, insert it in the header after trying to increase the rh page
 * number. See the FIXME comment(s) for caveats.
 *
 * Originally at User:Phe/Running header.js
 *
 * To use this script, add the following to your .js:
 *	 importScript('User:Inductiveload/Running header.js');
 */

(function($, mw) {

  "use strict";

  // Roman numeral functions
  // From http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter
  function int_to_roman(num) {
    if (Number(num) === 0) {
      return false;
    }

    var digits = String(Number(num)).split(""),
      key = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM",
        "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC",
        "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"
      ],
      roman = "",
      i = 3;

    while (i--) {
      roman = (key[+digits.pop() + (i * 10)] || "") + roman;
    }

    return Array(+digits.join("") + 1).join("M") + roman;
  }

  function roman_to_int(in_str) {
    var str = in_str.toUpperCase(),
      validator =
      /^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/,
      token = /[MDLV]|C[MD]?|X[CL]?|I[XV]?/g,
      key = {
        M: 1000,
        CM: 900,
        D: 500,
        CD: 400,
        C: 100,
        XC: 90,
        L: 50,
        XL: 40,
        X: 10,
        IX: 9,
        V: 5,
        IV: 4,
        I: 1
      },
      num = 0,
      m;
    if (!(str && validator.test(str)))
      return false;
    while ((m = token.exec(str)))
      num += key[m[0]];
    return num;
  }

  var RunningHeaderAutoComplete = function() {
    this.offsets = [-2, -4, 2, 4, -1, 1]; //offsets to try, in order
    this.offset = 0;

    // swap the header around if the offset is odd
    this.recto_verso_swap_sides = true;
    this.flash_textbox = true;


    this.header = document.getElementsByName('wpHeaderTextbox')[0];
  };

  RunningHeaderAutoComplete.prototype.increment_arabic = function(rh, inc) {

    try {
      // now attempt to increment the number
      var r = new RegExp(
        "^(.*[\\|][\\[\\( ]*)(\\d+)([\\]\\)\\. ]*[\\|}].*)$");
      var regex_res = r.exec(rh);

      var page = Number(r.exec(rh)[2]) + inc;
      rh = regex_res[1] + String(page) + regex_res[3];

      this.add_rh(rh);

      return true;
    } catch (err) {
      return false;
    }
  };

  RunningHeaderAutoComplete.prototype.increment_roman = function(rh, inc) {
    try {
      // now attempt to increment a roman numeral

      var r = new RegExp(
        "^(.*[\\|] *)([ivxlcm]+|[IVXLCM]+)(\\.? *[\\|}].*)$");
      var regex_res = r.exec(rh);

      var numeral = regex_res[2];

      var lower_case = false;
      if (numeral.charCodeAt(0) > 96 && numeral.charCodeAt(0) < 123) {
        lower_case = true;
      }


      var number = roman_to_int(numeral) + inc;

      numeral = int_to_roman(number);

      if (lower_case) {
        numeral = numeral.toLowerCase();
      }

      //insert the numeral back into the RH
      rh = regex_res[1] + numeral + regex_res[3];

      this.add_rh(rh);

      return true;
    } catch (err) {
      return false;
    }
  };

  RunningHeaderAutoComplete.prototype.swap_rh = function(rh) {

    rh = rh.replace(/\{\{ *([Rr]h|[Rr]unning header|[[Rr]unningHeader) *\|(.*?)\|(.*?)(?:\|(.*?))?\}\}/,
      "{{running header|$4|$3|$2}}");
    return rh;
  }


  RunningHeaderAutoComplete.prototype.add_rh = function(rh) {

      //remove existing RH
    this.header.value = this.header.value.replace(
      /{{([Rr]h|[Rr]unning ?[Hh]eader) *\|.*}}\n?/, '');

    // swap sides if the offset is odd
    if (this.recto_verso_swap_sides && (this.offsets[this.offset] % 2) !== 0) {
      rh = this.swap_rh(rh);
    } else {
      // normalise the template name
      rh = rh.replace(/\{\{ *([Rr]h|[Rr]unning header|[[Rr]unningHeader) */, "{{running header");
    }

    var r = new RegExp(/^\s*$/);

    if (r.test(this.header.value)) {
      this.header.value = rh;
    } else {
      this.header.value = rh + '\n' + this.header.value;
    }
    // flash the textbox
    if (this.flash_textbox) {
      this.header.animate({ backgroundColor: "lightgreen"}, 500);
    }
  };

  RunningHeaderAutoComplete.prototype.fill_rh = function(data) {

    var success = false;

    if (this.header && !data.query.pages[0].missing) {
      var content = data.query.pages[0].revisions[0].slots.main.content;
      var r = new RegExp("{" +
        "{ *([Rr]h|[Rr]unning header|[[Rr]unningHeader) *\\|[^\n<]*}}");
      var match = r.exec(content);

      if (match) {
        // FIXME: needs to be tweaked, actually works only if the first
        // rh parameters consisting of only digits or roman numerals is the one to increment
        var rh = match[0];

        var pg_offset = this.offsets[this.offset];

        //try to increment the number
        success = this.increment_arabic(rh, -pg_offset);

        if (!success) { //we failed to get a number, see if there is a roman numeral
          success = this.increment_roman(rh, -pg_offset);
        }

        if (!success) {
          //we didn't find a number to increment
          //set equal to the previous page's header, the user needs to edit by hand
          this.add_rh(rh);
        }

        if ($("prp_header").css('display') == 'none') {
          pr_toggle_visibility();
        }
      }
    }
    return success;
  };


  RunningHeaderAutoComplete.prototype.next_offset = function() {
    if (this.offset < this.offsets.length -1) {
      this.offset++;
      return true;
    }
    return false;
  }


  RunningHeaderAutoComplete.prototype.try_offset = function() {

    var rhac = this;

    var r = new RegExp("(\\d+)$");
    var page = Number(r.exec(mw.config.get("wgPageName"))[1]) + this.offsets[
      this.offset];
    var pagename = mw.config.get("wgPageName").replace(/\d+$/g, page);

    var offset = this.offsets[this.offset];
    // console.log("Trying offset: ", offset);

    mw.loader.using(['mediawiki.api'], function() {
      var api = new mw.Api();
      api.get({
        'action': 'query',
        'prop': 'revisions',
        'rvprop': 'content',
        'rvslots': 'main',
        'titles': pagename,
        'format': 'json',
        'formatversion': 2,
      })
      .done(function(jsondata) {
        var success = rhac.fill_rh(jsondata);

        if (!success && rhac.next_offset()) {
          rhac.try_offset();
        }
      })
      .fail(function() {
        if (rhac.next_offset()) {
          rhac.try_offset();
        }
      });
    });
  };


  RunningHeaderAutoComplete.prototype.set_running_header = function() {

    if (!this.header) {
      return;
    }

    this.try_offset();
  };

  function install_css(css) {
    $('head').append('<style type="text/css">' + css + '</style>');
  }

  function add_portlet(callback) {
    var portlet = mw.util.addPortletLink(
      'p-tb',
      '#',
      "Running header",
      't-running-header',
      'Add a running header based on the running headers on surrounding pages',
    );

    $(portlet)
      .click(function(e) {
        e.preventDefault();
        callback();
      });
  }

  function handle_alt_click(elem, callback) {
    $(elem).click(function(event) {
      if (event.altKey) {
        callback();
      }
    });
  }

  if (["Page"].indexOf(mw.config.get("wgCanonicalNamespace")) !== -1) {
    var RunningHeaderAutoCompleteInstance = new RunningHeaderAutoComplete();

    var go_fn = function() {
      RunningHeaderAutoCompleteInstance.set_running_header();
    };

    add_portlet(go_fn);
    handle_alt_click(RunningHeaderAutoCompleteInstance.header, go_fn);
  }


}(jQuery, mediaWiki));