Jump to content

MediaWiki:Gadget-PageNumbers-core.js

From Wikisource
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.

/* eslint-disable camelcase */

( function ( mw, $ ) {

	function initSetting( cookie_name, init ) {
		/* Sets JS variable to (in order of preference):
			2. current cookie value
			3. provided init parameter
			4. false
		*/

		// get current value of appropriate cookie
		var cookie_val = mw.cookie.get( cookie_name );

		// If JS variable still has no value, use provided init value. If no init
		// value provided, use false.
		if ( typeof cookie_val === 'undefined' || cookie_val === null ) {
			cookie_val = init || false;
		}

		// If JS variable is now the string "false", convert to boolean false
		// (to fix JS confusion where "false" string evaluates to true).
		if ( cookie_val === 'false' ) {
			cookie_val = false;
		}

		return cookie_val;
	}

	function saveSetting( name, value ) {
		mw.cookie.set( name, value );
	}

	/**
	 * Messages are configurable here
	 */
	mw.messages.set( {
		do: 'Display Options',
		displayOptions: 'Display Options',
		optlist: 'Display Options',
		'p-do': 'Display Options',
		page_numbers_hidden: 'Page links hidden',
		page_numbers_displayed: 'Page links displayed',
		page_numbers_inline: 'Page links within text',
		page_numbers_beside: 'Page links beside text',
		layout_name: 'layout_1',
		layout: 'Layout',
		use_serif: 'Use serif fonts',
		use_sans_serif: 'Use sans-serif fonts',
		serif_text_title: 'Change between serif and sans-serif fonts',
		default_layout_on: 'Default layouts on',
		default_layout_off: 'Default layouts off',
		default_layout_title: 'Default layouts allow pages to choose a specific layout for you. Turn it off if you always want the layout you set.',
		default_layout_suffix: 'default',
		what_is_this_title: 'What is this?',
		what_is_this_symbol: '?'
	} );

	var standard_layouts = [
		{
			id: 'layout_1',
			name: 'Layout 1'
		},
		{
			id: 'layout_2',
			name: 'Layout 2'
		},
		{
			id: 'layout_3',
			name: 'Layout 3'
		},
		{
			id: 'layout_4',
			name: 'Layout 4'
		}
	];

	// eslint-disable-next-line no-jquery/no-global-selector
	var $classedContainer = $( '#mw-content-text' );

	var containers = {};

	function removeClassesWithPrefix( el, prefix ) {
		if (!el) {
			console.log("el is undefined!");
			return;
		}
		var classes = el.className.split( ' ' ).filter( function ( c ) {
			return c.lastIndexOf( prefix, 0 ) !== 0;
		} );
		// eslint-disable-next-line mediawiki/class-doc
		el.className = classes.join( ' ' ).trim();
	}

	/*
	 * The display control options:
	 *  * serif/sans serif fonts
	 */
	var display = ( function () {

		// My kingdom for Vuex
		var State = {
			serif: false,

			cache: {}
		};

		function updateSerifs() {
			// tthe relevant class on the content
			containers.$page.toggleClass( 'ws-display-serif', State.serif );

			var msg = mw.msg( State.serif ? 'use_sans_serif' : 'use_serif' );
			State.cache.$serifSwitch.children( 'a' ).html( msg );
		}

		function init() {

			State.cache.$serifSwitch = $( mw.util.addPortletLink(
				'p-do', '#', '', 'd-serif', mw.msg( 'serif_text_title' ) )
			)
				.on( 'click', function () {
					State.serif = !State.serif;
					updateSerifs();
					saveSetting( 'ws-display-serif', State.serif );
				} );

			State.serif = initSetting( 'ws-display-serif', false );
			updateSerifs();
		}

		return {
			init: init
		};
	}() );

	var layout = ( function () {
			var State = {
				allow_default: true,
				default_applied: false,
				layout_name: 'Layout 1',
				layouts: [],
				cache: {}
			};

			function set_by_name( name ) {

				var selected_layout;
				for ( var i = 0; i < State.layouts.length; ++i ) {
					if ( State.layouts[ i ].name.toLowerCase() === name.toLowerCase() ) {
						selected_layout = State.layouts[ i ];
					}
				}

				if ( !selected_layout ) {
					return false; // does not exist
				}

				State.layout_name = selected_layout.name;

				var layoutText = selected_layout.name;
				if ( State.allow_default && State.default_applied ) {
					layoutText += ' (' + mw.msg( 'default_layout_suffix' ) + ')';
				}

				State.cache.$layoutSwitch.children( 'a' ).html( layoutText );

				removeClassesWithPrefix( $classedContainer[ 0 ], 'dynlayout-' );
				// eslint-disable-next-line mediawiki/class-doc
				$classedContainer.addClass( 'dynlayout-' + selected_layout.id );

				pagenumbers.refresh_offsets();
				return true;
			}

			function updateLayout() {
				var name;
				State.default_applied = false;
				if ( State.allow_default || !mw.cookie.get( 'layout' ) ) {
					var overrider = State.cache.$overrider || $classedContainer.find( '#dynamic_layout_overrider' );
					name = overrider.text();
					if ( name ) {
						State.default_applied = true;
					}
				} else {
					name = State.layout_name || mw.cookie.get( 'layout' );
				}
				if ( !set_by_name( name || State.layout_name ) ) {
					// failed to set the default: maybe it's not a valid layout
					State.default_applied = false;
					set_by_name( State.layout_name );
				}
			}

			function set_by_number( number ) {
				State.layout_name = State.layouts[ number ].name;
				updateLayout();
			}

			function getLayoutIndexWithName( name ) {
				for ( var i = 0; i < State.layouts.length; ++i ) {
					if ( State.layouts[ i ].name === name ) {
						return i;
					}
				}

				return -1;
			}

			function toggle() {
				var cur = getLayoutIndexWithName( State.layout_name );

				// disable override for this page only (no persistence)
				State.allow_default = false;

				set_by_number( ( cur + 1 ) % State.layouts.length );

				// store the changed layout
				mw.cookie.set( 'layout', State.layout_name );
			}

			function updateDefault() {
				var msg = mw.msg( State.allow_default ? 'default_layout_on' : 'default_layout_off' );
				State.cache.$defaultLayoutSwitch.children( 'a' ).html( msg );

				updateLayout();
			}

			function init() {
				// do return if we're already set up
				if ($(".ws-page-container").length) {
					return;
				}

				// collect any user or other gadget layouts
				mw.hook( 'ws.layouts.register' ).fire( {
					layouts: standard_layouts
				} );

				State.layouts = standard_layouts;

				// If cookie is not set, default layout is first available option.
				// Use index "0" in case layout name is ever changed.
				State.layout_name = initSetting( 'layout', '0' );

				State.allow_default = initSetting( 'ws-display-default-layouts', true );

				State.cache.$layoutSwitch = $( mw.util.addPortletLink(
					'p-do',
					'#',
					mw.msg( 'layout' ),
					'd-textLayout',
					'The designation of the dynamic layout being applied',
					'l',
					'#d-defaultLayouts'
				) )
					.on( 'click', function ( e ) {

						e.preventDefault();
						toggle();
					} );

				State.cache.$defaultLayoutSwitch = $( mw.util.addPortletLink(
					'p-do',
					'#',
					'',
					'd-defaultLayouts',
					mw.msg( 'default_layout_title' )
				) )
					.on( 'click', function ( e ) {
						State.allow_default = !State.allow_default;

						// if we just turned the default off, use the cookie value
						if ( !State.allow_default ) {
							State.layout_name = mw.cookie.get( 'layout' );
						}

						updateDefault();
						saveSetting( 'ws-display-default-layouts', State.allow_default );
						e.preventDefault();
					} );

				// remove all these classes to maintain backwards-compatibility
				$classedContainer
					.find( 'div.text, .lefttext, .centertext, .indented-page, .prose' )
					.removeClass();

				// DynamicFlaw - a independent Div should have been the parent
				// to this 3-into-1 step
				var $parserOutput = $('.mw-parser-output', $classedContainer)
					.contents().not('.dynlayout-exempt')
					.wrapAll(
						$('
').addClass('mw-parser-output ws-page-container').append( $('
').addClass('ws-region-container').append( $('
').addClass('ws-column-container') ) ) ); // cache the containers containers.$column = $parserOutput.parent(); containers.$region = containers.$column.parent(); containers.$page = containers.$region.parent(); // If layouts have changed, the cookie might refer to a missing layout // in which case, set the first one if ( getLayoutIndexWithName( State.layout_name ) === -1 ) { set_by_number( 0, true ); } // set the layout by default (override) layout, or from the user's setting updateDefault(); mw.hook( 'ws.layouts.ready' ).fire(); } return { init: init }; }() ), pagenumbers = ( function () { // some shared variables to avoid selecting these elements repeatedly var $div_pagenumbers, dp_y, y_prev, $pagenumbers_collection, $div_ss, $div_highlight, show_params = { link_text: mw.msg( 'page_numbers_displayed' ), visible: true }, hide_params = { link_text: mw.msg( 'page_numbers_hidden' ), visible: false }; function pagenum_in() { if ( self.proofreadpage_disable_highlighting ) { return false; } if ( !$div_highlight ) { return false; // could not find it } var id = this.id.substring( 11 ), $page_span = $( document.getElementById( id ) ), $next = self.$pagenum_ml.eq( self.$pagenum_ml.index( $page_span ) + 1 ); if ( $next.length === 0 ) { $next = $div_ss; } var $container = containers.$column; // we need to use document offsets in case a page break occurs within // a positioned element var c_os = $container.offset(), ps_os = $page_span.offset(), n_os = $next.offset(); ps_os = { top: ps_os.top - c_os.top, left: ps_os.left - c_os.left }; n_os = { top: n_os.top - c_os.top, left: n_os.left - c_os.left }; $div_highlight.css( { display: 'block', top: ps_os.top + 'px' } ); $div_highlight.children().eq( 0 ).css( { height: $page_span.height() + 'px', width: ( ps_os.left < 1 ) ? '100%' : ( ( $container.width() - ps_os.left ) + 'px' ) } ); // div_ss.height() ~= height of 1 line of text $div_highlight.children().eq( 1 ).css( 'height', ( n_os.top - ps_os.top - $page_span.height() ) + 'px' ); $div_highlight.children().eq( 2 ).css( { height: $next.height() + 'px', width: n_os.left + 'px' } ); return true; } function pagenum_out() { if ( self.proofreadpage_disable_highlighting ) { return false; } if ( !$div_highlight ) { return false; // could not find it } $div_highlight.css( 'display', 'none' ); $div_highlight.children().eq( 0 ).css( 'width', '0px' ); $div_highlight.children().eq( 1 ).css( 'height', '0px' ); $div_highlight.children().eq( 2 ).css( 'width', '0px' ); return true; } function refresh_elem_offset( page_span, $pagenumber ) { var y = $( page_span ).offset().top; $pagenumber.css( 'top', y - dp_y ); if ( self.proofreadpage_numbers_visible && y - y_prev.val > 5 ) { y_prev.val = y; $pagenumber.removeClass( 'pagenumber-invisible' ); } else { $pagenumber.addClass( 'pagenumber-invisible' ); } } function refresh_offsets() { // do nothing if container is not set up if ( self.proofreadpage_numbers_inline || !$div_pagenumbers ) { return false; } dp_y = $div_pagenumbers.offset().top; y_prev = { val: -10 }; var $pagenumber = $pagenumbers_collection.first(); self.$pagenum_ml.each( function ( i, page_span ) { refresh_elem_offset( page_span, $pagenumber ); $pagenumber = $pagenumber.next(); } ); return true; } var inline_params = { elem: 'span', link_pre: ' [', link_post: ']' }, beside_params = { elem: 'div', link_pre: '[', link_post: ']' }; function setup_elem(i, page_span) { const params = self.proofreadpage_numbers_inline ? inline_params : beside_params; // styled also by classes: div.pagenumber or span.pagenumber let $pagenumber = $('<' + params.elem + '>') .attr('id', $(page_span).data('pagenumber_id')) .addClass('pagenumber noprint') .append(params.link_pre + $(page_span).data('link_str') + params.link_post) .toggleClass('pagenumber-invisible', !self.proofreadpage_numbers_visible); if (!self.proofreadpage_numbers_inline) { refresh_elem_offset(page_span, $pagenumber); } // clear the span provided by [[MediaWiki:Proofreadpage pagenum template]] $(page_span).find('.pagenum-inner').empty(); $pagenumber.appendTo(self.proofreadpage_numbers_inline ? page_span : $div_pagenumbers ); $pagenumbers_collection = $pagenumbers_collection.add($pagenumber); } function init_elem(i, page_span) { const name = $(page_span).data('page-number').toString() || page_span.id; let pagenumber_id = 'pagenumber_' + page_span.id; let count; // what if two pages have the same number? increment the id if ($pagenumbers_collection.is('#' + $.escapeSelector(pagenumber_id))) { count = ($pagenumbers_collection.filter("[id ^= '" + pagenumber_id + "']").length + 1); page_span.id += ('_' + count); pagenumber_id += ('_' + count); } if (!page_span.title) { // there's no page to link to - just set plain text $(page_span).data('link_str', mw.html.escape(name)); } else { $(page_span).data('pagenumber_id', pagenumber_id); const page_title = $(page_span).data('page-name'); const page_url = mw.config.get('wgArticlePath') .replace('$1', encodeURIComponent(page_title.replace(/ /g, '_'))) .replace(/%2F/g, '/'); // encodeURIComponent encodes '/', which breaks subpages // if transcluded Page: (ll) is a redlink then make page // class (class_str) a redlink also let class_str = ''; let action_str = ''; let $ll = $(page_span).parent().next('a.new'); if ($ll.length && $ll.attr('href').includes(page_title)) { class_str = ' class="new" '; action_str = '?action=edit&redlink=1'; } $(page_span).data('link_str', ' + page_url + action_str + '"' + class_str + ' title= "' + mw.html.escape(page_title) + '">' + mw.html.escape(name) + '' ); } setup_elem(i, page_span); } function refresh_display() { // determine if we need to set things up var inited = !$pagenumbers_collection; // JQuery collection of all pagenumber elements if ( !inited ) { $pagenumbers_collection.remove(); } $pagenumbers_collection = $(); if ( $div_pagenumbers ) { $div_pagenumbers.remove(); } if ( !self.proofreadpage_numbers_inline ) { // html div container for page numbers stored in shared variable div_pagenumbers // put pagenumbers container div in the outermost layout container $div_pagenumbers = $( '
' ) .attr( 'id', 'ct-pagenumbers' ) .appendTo( containers.$page ); dp_y = $div_pagenumbers.offset().top; y_prev = { val: -10 }; } self.$pagenum_ml.each( inited ? init_elem : setup_elem ); if ( self.proofreadpage_numbers_inline ) { $pagenumbers_collection.off( 'mouseenter mouseleave' ); } else { $pagenumbers_collection.on( { mouseenter: pagenum_in, mouseleave: pagenum_out } ); } } function toggle_visible() { var params = self.proofreadpage_numbers_visible ? hide_params : show_params; $pagenumbers_collection.toggleClass( 'pagenumber-invisible', !params.visible ); $( '#d-pageNumbers_visible' ).children( 'a' ).html( params.link_text ); self.proofreadpage_numbers_visible = params.visible; mw.cookie.set( 'pagenums_visible', params.visible ); } function toggle_inline() { // toggle inline view unless layouts are not set up self.proofreadpage_numbers_inline = !layout || !self.proofreadpage_numbers_inline; $( '#d-pageNumbers_inline' ).children( 'a' ) .html( mw.msg( self.proofreadpage_numbers_inline ? 'page_numbers_inline' : 'page_numbers_beside' ) ); mw.cookie.set( 'pagenums_inline', self.proofreadpage_numbers_inline ); refresh_display(); } function doInit() { // Mark the container as having pagenumbers. // Some layouts can use that information. $( containers.$page ) .addClass( 'dynlayout-haspagenums' ); // get_optlist(); self.proofreadpage_numbers_visible = initSetting( 'pagenums_visible', true ); var portletLink = mw.util.addPortletLink( 'p-do', '#', self.proofreadpage_numbers_visible ? mw.msg( 'page_numbers_displayed' ) : mw.msg( 'page_numbers_hidden' ), 'd-pageNumbers_visible', 'The current state of embedded link visibility', 'n', '#d-serif' ); $( portletLink ).on( 'click', function ( e ) { e.preventDefault(); toggle_visible(); } ); self.proofreadpage_numbers_inline = initSetting( 'pagenums_inline', false ); // if layouts are not initialized show pagenumbers inline since // "beside" view won't work if ( !layout ) { self.proofreadpage_numbers_inline = true; } portletLink = mw.util.addPortletLink( 'p-do', '#', self.proofreadpage_numbers_inline ? mw.msg( 'page_numbers_inline' ) : mw.msg( 'page_numbers_beside' ), 'd-pageNumbers_inline', 'The current positioning used for embedded link presentation', 'i', '#d-pageNumbers_visible' ); $( portletLink ).on( 'click', function ( e ) { e.preventDefault(); toggle_inline(); } ); // store container for the highlight to shared variable "div_highlight" $div_highlight = $( '
' + '
'
+
'
'
+
'
'
+
'
'
); // assign new div element to shared variable "div_ss" $div_ss = $( '
'
); // empty span following some text
// put divs in the innermost dynamic layout container if ( layout ) { containers.$column .append( $div_highlight ); $classedContainer.append( $div_ss ); } else { $classedContainer.append( $div_highlight, $div_ss ); } self.$pagenum_ml = $classedContainer.find( '.pagenum' ); refresh_display(); } function init() { // skip if pagenumbers are already set up if ( $pagenumbers_collection ) { return false; } // wait for the layouts code to signal that the containers are ready mw.hook( 'ws.layouts.ready' ).add( function () { doInit(); } ); } return { init: init, refresh_offsets: refresh_offsets }; }() ); if ( [ 'view', 'submit', 'purge' ].indexOf( mw.config.get( 'wgAction' ) ) !== -1 ) { if ( !self.debug_page_layout && // don't do anything on DoubleWiki or difference comparison views document.URL.indexOf( 'match=' ) === -1 ) { layout.init(); display.init(); $( function () { if ( $classedContainer.find( '.pagenum' ).length ) { pagenumbers.init(); if ( document.readyState === 'complete' ) { $( pagenumbers.refresh_offsets ); } else { $( window ).on( 'load', pagenumbers.refresh_offsets ); } } } ); // Add a "what's this" helper to display options // eslint-disable-next-line no-jquery/no-global-selector $( '#p-do-label' ).append( $( '' ) .css( { float: 'right' } ) .append( $( '' ) .attr( { href: '/wiki/Help:Layout', title: mw.msg( 'what_is_this_title' ) } ) .append( mw.msg( 'what_is_this_symbol' ) ) ) ); } var position = window.location.hash.substring( 1 ); if ( position && document.getElementById( position ) ) { document.getElementById( position ).scrollIntoView(); } /** * Install the DOM-ready hook to force header and footer content out of * Dynamic Layouts */ $( function () { var $c = $classedContainer; $c.find( '.acContainer' ).insertAfter( $c.find( 'div.printfooter' ) ); $( '
' ) .addClass( 'mw-parser-output dynlayout-exempt dynlayout-exempt-footer' ) .insertBefore( 'div#catlinks' ) .append( $c.find( '.acContainer' ) ) .append( $c.find( 'div.licenseContainer' ).not( 'div.licenseContainer div.licenseContainer' ) ) .append( $c.find( '#editform' ) ) .append( $c.find( '.ws-footer' ) ); $( '
' ) .addClass( 'mw-parser-output dynlayout-exempt dynlayout-exempt-header' ) .insertBefore( containers.$page ) .prepend( $c.find( '.ws-header' ).not( '#headerContainer' ) ) .prepend( $c.find( '#headerContainer' ) ) .prepend( $c.find( '.similar' ) ) .prepend( $c.find( '.ambox' ) ) .prepend( $c.find( '#mw-previewheader' ) ); } ); } /* eslint-disable-next-line no-undef */ }( mediaWiki, jQuery ) );