User:Arcorann/runningheader.js

/* * Running header auto-extraction, edited to adapt to recto-verso header usage * Does a lookup one page previous for rvh, then two pages previous for rh, * then tries other pages (-4, +2, +4, +1) * * Currently WIP * * Derived from MediaWiki:Gadget-RunningHeader.js (User:Inductiveload/Running_header.js) * Originally at User:Phe/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_rvh = [ -1, -2, -4, 1, 2, 4 ]; this.offsets_rh = [ -2, -4, 2, 4, -1, 1 ];// offsets to try, in order this.offsets_fallback = [ -2, -4, 2, 4, -1, 1 ]; 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) *\|(.*?)\|(.*?)(?:\|(.*?))?\}\}/,			'' );		return rh;	};

RunningHeaderAutoComplete.prototype.add_rh = function ( rh ) {

// remove existing RH		this.header.value = this.header.value.replace(			/\{\{ *(rh|rh\/\d|running header|rvh|recto-verso header) *\|.*\}\}\n?/i, '' );

// 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( /\{\{ *(rh|running ?header) *\|/i, '{{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 = /\{\{ *(rh|running ?header|rvh|recto-verso header) *\|[^\n<]*\}\}/i; 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 );

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.check_header = function {

if ( !this.header ) { return; }		var rc_rvh = /\{\{(rvh|recto-verso header)/i; var rc_rh = /\{\{(rh|running ?header)/i; var rc_temp = /(odd|even|chapter\S)/i; // check prefilled running header if (rc_rvh.exec(this.header)) { this.offsets = this.offsets_rvh; } else if (rc_rh.exec(this.header)) { this.offsets = this.offsets_rh; } else { this.offsets = this.offsets_fallback; }	};

RunningHeaderAutoComplete.prototype.set_running_header = function {

if ( !this.header ) { return; }		this.check_header; this.try_offset; };

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 ) .on( 'click', function ( e ) {				e.preventDefault;				callback;			} ); }

function handle_alt_click( elem, callback ) { $( elem ).on( '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 ) );