User:Arcorann/runningheader.js

From Wikisource
Jump to navigation Jump to search
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.

/*
 * 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) *\|(.*?)\|(.*?)(?:\|(.*?))?\}\}/,
			'{{running header|$4|$3|$2}}' );
		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 ) );