MediaWiki:Common.js

/* Any JavaScript here will be loaded for all users on every page load. */ // extension req //v2.0, 2017, user:fngplg. //set active tab: https://jojowiki.com/page#activeTab (function ($){   var nstarget = window.location.hash.replace('#', );    if (nstarget === ) return;    //convert wiki-utf 2 ansi    nstarget = nstarget.replace(/\./g, '%');    nstarget = decodeURIComponent(nstarget).replace(/_/g, ' ');    //console.log('trgt:'+nstarget);    $(function{ setTimeout(function {           var $nt2a = $('.tabberlive>.tabbernav>Li>a[title="' + nstarget + '"]');            $nt2a.click;            $nt2a.get(0).scrollIntoView({inline: "nearest"});        }, 100);//settimeout });//doc.rdy   })(jQuery);

/* QuickDiff - quickly view any diff link */ /*jslint browser, long */ /*global jQuery, mediaWiki, dev */ (function ($, mw) {   "use strict";    // double-run protection    if (window.quickDiffLoaded) {        return;    }    window.quickDiffLoaded = true;    var diffStylesModule = "mediawiki.action.history.diff";    var i18n;    var modal;    var special = {};    // "Special:Diff/12345" and "Special:ComparePages" link detection    function initSpecialPageStrings {        special.diffDefault = mw.util.getUrl("Special:Diff/");        special.compareDefault = mw.util.getUrl("Special:ComparePages");        var wiki = mw.config.get("wgDBname");        var storageKeyDiff = "QuickDiff-specialdiff_" + wiki;        var storageKeyCompare = "QuickDiff-specialcompare_" + wiki;        try {            special.diff = localStorage.getItem(storageKeyDiff);            special.compare = localStorage.getItem(storageKeyCompare);        } catch (ignore) {}        if (special.diff && special.compare) {            // using stored values - no need for api request return; }       $.getJSON(mw.util.wikiScript("api"), {            action: "parse",            format: "json",            prop: "text",            text: " Special:Diff/ Special:ComparePages ",            disablepp: "" // note: deprecated in MW 1.26, but needed for older versions        }).done(function (data) {            var $parsed = $(data.parse.text["*"]);            special.diff = $parsed.find(".diff > a").attr("href");            special.compare = $parsed.find(".compare > a").attr("href");            try {                localStorage.setItem(storageKeyDiff, special.diff);                localStorage.setItem(storageKeyCompare, special.compare);            } catch (ignore) {}        }); }   function getDiffTitle($diff) { var prevTitle = $diff.find("#mw-diff-otitle1 a").attr("title"); var currTitle = $diff.find("#mw-diff-ntitle1 a").attr("title"); if (prevTitle && prevTitle !== currTitle) { return i18n("differences-multipage", prevTitle, currTitle).plain; }       return i18n("differences", currTitle).plain; }   function loadDiff(url) { modal.show({           loading: true,            title: !modal.visible && i18n("loading").plain        }); // add 'action=render' and 'diffonly' params to save some bytes on each request url.extend({           action: "render",            diffonly: "1"        }); // pass through 'bot' param for rollback links if it's in use on the current page if (mw.util.getParamValue("bot")) { url.extend({bot: "1"}); }       $.when(            $.get(url.getRelativePath),            mw.loader.using(diffStylesModule)        ).always(function (response) {            delete url.query.action;            delete url.query.diffonly;            delete url.query.bot;            var data = {                buttons: [{                    text: i18n("link").plain,                    href: url.toString,                    attr: {"data-disable-quickdiff": ""}                }]            };            var $diff;            if (typeof response[0] === "string") {                var $content = $(response[0]);                $diff = $content.filter("table.diff, #mw-rev-deleted-no-diff");                if (!$diff.length) {                    // $content is a complete page - see if a diff can be found                    // needed for diffs from special pages as they ignore action=render URL parameter                    $diff = $content.find("table.diff"); }           }            if ($diff && $diff.length) { data.content = $diff; data.hook = "quickdiff.ready"; data.title = getDiffTitle($diff); } else { data.content = i18n("error", url.toString).escape; }           modal.show(data); });   }    function linkClickHandler(event) {        // ignore clicks with modifier keys to avoid overriding browser features        if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {            return;        }        // ignore click if link has "data-disable-quickdiff" attribute set        if (event.currentTarget.dataset.disableQuickdiff !== undefined) {            return;        }        var url = event.currentTarget.href;        try {            url = new mw.Uri(url);        } catch (ignore) {            // quit if url couldn't be parsed            // it wouldn't be a link QuickDiff could handle anyway            return;        }        // cross-domain requests not supported        if (url.host !== location.hostname) {            return;        }        // no fragment check is to ensure section links/collapsible trigger links on diff pages are ignored        var hasDiffParam = url.query.diff !== undefined && url.fragment === undefined; var isSpecialDiffLink = url.path.indexOf(special.diff) === 0 || url.path.indexOf(special.diffDefault) === 0; var isSpecialCompareLink = url.path.indexOf(special.compare) === 0 || url.path.indexOf(special.compareDefault) === 0; if (hasDiffParam || isSpecialDiffLink || isSpecialCompareLink) { event.preventDefault; loadDiff(url); }   }    function init { modal = new mw.libs.QDmodal("quickdiff-modal"); // full screen modal mw.util.addCSS("#quickdiff-modal { height: 100%; width: 100% }"); // diff styles module was renamed in MW 1.28 if (mw.loader.getState("mediawiki.diff.styles")) { diffStylesModule = "mediawiki.diff.styles"; }       // attach to body for compatibility with ajax-loaded content // also, one attached event handler is better than hundreds! $(document.body).on("click.quickdiff", "a[href]", linkClickHandler); initSpecialPageStrings; }   function initDependencies { var loadURL = "/index.php?title="; var i18nMsgs = new $.Deferred; var waitFor = [ i18nMsgs, mw.loader.using(["mediawiki.Uri", "mediawiki.util"]) ];       if (!(mw.libs.QDmodal && mw.libs.QDmodal.version >= 20180212)) { waitFor.push($.ajax({ cache: true, dataType: "script", url: loadURL + "MediaWiki:QDmodal.js&action=raw&ctype=text/javascript" }));       }        if (!(window.dev && dev.i18n && dev.i18n.loadMessages)) { mw.loader.load(loadURL + "MediaWiki:I18n-js/code.js&action=raw&ctype=text/javascript"); }       mw.hook("dev.i18n").add(function (i18njs) {            i18njs.loadMessages("QuickDiff").done(function (i18nData) { i18n = i18nData.msg; i18nMsgs.resolve; });       });        $.when.apply($, waitFor).done(init); }   initDependencies; // collect action links (edit, undo, rollback, patrol) and add them to footer mw.hook("quickdiff.ready").add(function (modal) {       // edit/undo links use "mw-rev-head-action" class on Wikia,        // and "mw-diff-edit" or "-undo" class on MW 1.24+        var $buttons = modal.$content.find(".diff-ntitle").find( ".mw-rev-head-action, .mw-diff-edit, .mw-diff-undo, .mw-rollback-link, .patrollink" ).clone;       // remove text nodes (the brackets around each link)        $buttons.contents.filter(function (ignore, element) { return element.nodeType === 3; }).remove;       $buttons.find("a")            .addClass("qdmodal-button")            .attr("target", "_blank");        modal.$footer.append($buttons);    }); }(jQuery, mediaWiki));

window.hotcat_use_category_links = false;

/** * @source https://www.mediawiki.org/wiki/Snippets/Open_specific_links_in_new_window * @version 2018-09-15 */ $( function {	$( '#mw-content-text' ).on( 'click', '.newwin > a', function  { var otherWindow = window.open; otherWindow.opener = null; otherWindow.location = this; return false; } ); } );

// Expand all button var state =1; $(function {  $('#collapse-global').html($('', {       'class': 'mw-ui-button',       text: 'Expand/Collapse All'   })).click(function { if(state ===0){ $('.mw-collapsible-toggle-expanded').click; state = 1; }      else { $('.mw-collapsible-toggle-collapsed').click; state = 0; }     } )});

// Refresh Button $(function {  $('.mw-refreshButton').html($('', {       'class': 'mw-ui-button',       text: 'Refresh'   })).click(function { location.reload; window.scrollTo(0, element.offsetTop); } )});

// English Cast Button $(function {  $('.mw-englishButton').html($('', {       'class': 'mw-ui-button',       text: 'English Cast'   })).click(function { window.location = (""+window.location).replace(/#[A-Za-z0-9_]*$/,'')+"#ENG"; } )});

// Japanese Cast Button $(function {  $('.mw-japaneseButton').html($('', {       'class': 'mw-ui-button',       text: 'Japanese Cast'   })).click(function { window.location = (""+window.location).replace(/#[A-Za-z0-9_]*$/,'')+"#JPN"; } )});

// Spanish Cast Button $(function {  $('.mw-spanishButton').html($('', {       'class': 'mw-ui-button',       text: 'Spanish Cast'   })).click(function { window.location = (""+window.location).replace(/#[A-Za-z0-9_]*$/,'')+"#SPA"; } )});

// French Cast Button $(function {  $('.mw-frenchButton').html($('', {       'class': 'mw-ui-button',       text: 'French Cast'   })).click(function { window.location = (""+window.location).replace(/#[A-Za-z0-9_]*$/,'')+"#FR"; } )});

// Italian Cast Button $(function {  $('.mw-italianButton').html($('', {       'class': 'mw-ui-button',       text: 'Italian Cast'   })).click(function { window.location = (""+window.location).replace(/#[A-Za-z0-9_]*$/,'')+"#ITA"; } )});

// See Reference Tooltips // Source https://en.wikipedia.org/wiki/MediaWiki:Gadget-ReferenceTooltips.js

( function {

// enwiki settings var REF_LINK_SELECTOR = '.reference, a[href^="#CITEREF"]', COMMENTED_TEXT_CLASS = 'rt-commentedText', COMMENTED_TEXT_SELECTOR = ( COMMENTED_TEXT_CLASS ? '.' + COMMENTED_TEXT_CLASS + ', ' : '') + 'abbr[title]';

mw.messages.set( {	'rt-settings': 'Reference Tooltips settings',	'rt-enable-footer': 'Enable Reference Tooltips',	'rt-settings-title': 'Reference Tooltips',	'rt-save': 'Save',	'rt-cancel': 'Cancel',	'rt-enable': 'Enable',	'rt-disable': 'Disable',	'rt-activationMethod': 'Tooltip appears when',	'rt-hovering': 'hovering',	'rt-clicking': 'clicking',	'rt-delay': 'Delay before the tooltip appears (in milliseconds)',	'rt-tooltipsForComments': 'Show tooltips over text with a dotted underline in Reference Tooltips style (allows to see such tooltips on devices with no mouse support)',	'rt-disabledNote': 'You can re-enable Reference Tooltips using a link in the footer of the page.',	'rt-done': 'Done',	'rt-enabled': 'Reference Tooltips are enabled' } );

// "Global" variables var SECONDS_IN_A_DAY = 60 * 60 * 24, CLASSES = { FADE_IN_DOWN: 'rt-fade-in-down', FADE_IN_UP: 'rt-fade-in-up', FADE_OUT_DOWN: 'rt-fade-out-down', FADE_OUT_UP: 'rt-fade-out-up' },	IS_TOUCHSCREEN = 'ontouchstart' in document.documentElement, // Quite a rough check for mobile browsers, a mix of what is advised at	// https://stackoverflow.com/a/24600597 (sends to	// https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent) // and https://stackoverflow.com/a/14301832 IS_MOBILE = /Mobi|Android/i.test( navigator.userAgent ) || typeof window.orientation !== 'undefined', CLIENT_NAME = $.client.profile.name, settingsString, settings, enabled, delay, activatedByClick, tooltipsForComments, cursorWaitCss, windowManager, $body = $( document.body ), $window = $( window );

function rt( $content ) { // Popups gadget if ( window.pg ) { return; }

var teSelector, settingsDialogOpening = false;

function setSettingsCookie { mw.cookie.set(			'RTsettings',			Number( enabled ) + '|' + delay + '|' + Number( activatedByClick ) + '|' +				Number( tooltipsForComments ),			{ path: '/', expires: 90 * SECONDS_IN_A_DAY, prefix: '' }		); }

function enableRt { enabled = true; setSettingsCookie; $( '.rt-enableItem' ).remove; rt( $content ); mw.notify( mw.msg( 'rt-enabled' ) ); }

function disableRt { $content.find( teSelector ).removeClass( 'rt-commentedText' ).off( '.rt' ); $body.off( '.rt' ); $window.off( '.rt' ); }

function addEnableLink { // #footer-places – Vector // #f-list – Timeless, Monobook, Modern // parent of #footer li – Cologne Blue var $footer = $( '#footer-places, #f-list' ); if ( !$footer.length ) { $footer = $( '#footer li' ).parent; }		$footer.append(			$( '' )				.addClass( 'rt-enableItem' )				.append( $( '' ) .text( mw.msg( 'rt-enable-footer' ) ) .attr( 'href', 'javascript:' ) .click( function ( e ) {							e.preventDefault;							enableRt;						} ) )		);	}

function TooltippedElement( $element ) { var tooltip, events, te = this;

function onStartEvent( e ) { var showRefArgs;

if ( activatedByClick && te.type !== 'commentedText' && e.type !== 'contextmenu' ) { e.preventDefault; }			if ( !te.noRef ) { showRefArgs = [ $( this ) ]; if ( te.type !== 'supRef' ) { showRefArgs.push( e.pageX, e.pageY ); }				te.showRef.apply( te, showRefArgs ); }		}

function onEndEvent { if ( !te.noRef ) { te.hideRef; }		}

if ( !$element ) { return; }

// TooltippedElement.$element and TooltippedElement.$originalElement will be different when // the first is changed after its cloned version is hovered in a tooltip this.$element = $element; this.$originalElement = $element; if ( this.$element.is( REF_LINK_SELECTOR ) ) { if ( this.$element.prop( 'tagName' ) === 'SUP' ) { this.type = 'supRef'; } else { this.type = 'harvardRef'; }		} else { this.type = 'commentedText'; this.comment = this.$element.attr( 'title' ); if ( !this.comment ) { return; }			this.$element.addClass('rt-commentedText'); }		if ( activatedByClick ) { events = { 'click.rt': onStartEvent };			// Adds an ability to see tooltips for links if ( this.type === 'commentedText' &&				( this.$element.closest( 'a' ).length || this.$element.has( 'a' ).length )			) {				events[ 'contextmenu.rt' ] = onStartEvent; }		} else { events = { 'mouseenter.rt': onStartEvent, 'mouseleave.rt': onEndEvent };		}

this.$element.on( events );

this.hideRef = function ( immediately ) { clearTimeout( te.showTimer );

if ( this.type === 'commentedText' ) { this.$element.attr( 'title', this.comment ); }

if ( this.tooltip && this.tooltip.isPresent ) { if ( activatedByClick || immediately ) { this.tooltip.hide; } else { this.hideTimer = setTimeout( function {						te.tooltip.hide;					}, 200 ); }			} else if ( this.$ref && this.$ref.hasClass( 'rt-target' ) ) { this.$ref.removeClass( 'rt-target' ); if ( activatedByClick ) { $body.off( 'click.rt touchstart.rt', this.onBodyClick ); }			}		};

this.showRef = function ( $element, ePageX, ePageY ) { // Popups gadget if ( window.pg ) { disableRt; return; }			if ( this.tooltip && !this.tooltip.$content.length ) { return; }

var tooltipInitiallyPresent = this.tooltip && this.tooltip.isPresent;

function reallyShow { var viewportTop, refOffsetTop, teHref;

if ( !te.$ref && !te.comment ) { teHref = te.type === 'supRef' ? te.$element.find( 'a' ).attr( 'href' ) : te.$element.attr( 'href' ); // harvardRef te.$ref = teHref && $( '#' + $.escapeSelector( teHref.slice( 1 ) ) ); if ( !te.$ref || !te.$ref.length || !te.$ref.text ) { te.noRef = true; return; }				}

if ( !tooltipInitiallyPresent && !te.comment ) { viewportTop = $window.scrollTop; refOffsetTop = te.$ref.offset.top; if ( !activatedByClick &&						viewportTop < refOffsetTop &&						viewportTop + $window.height > refOffsetTop + te.$ref.height &&						// There can be gadgets/scripts that make references horizontally scrollable.						$window.width > te.$ref.offset.left + te.$ref.width					) { // Highlight the reference itself te.$ref.addClass( 'rt-target' ); return; }				}

if ( !te.tooltip ) { te.tooltip = new Tooltip( te ); if ( !te.tooltip.$content.length ) { return; }				}

// If this tooltip is called from inside another tooltip. We can't define it				// in the constructor since a ref can be cloned but have the same Tooltip object; // so, Tooltip.parent is a floating value. te.tooltip.parent = te.$element.closest( '.rt-tooltip' ).data( 'tooltip' ); if ( te.tooltip.parent && te.tooltip.parent.disappearing ) { return; }

te.tooltip.show;

if ( tooltipInitiallyPresent ) { if ( te.tooltip.$element.hasClass( 'rt-tooltip-above' ) ) { te.tooltip.$element.addClass( CLASSES.FADE_IN_DOWN ); } else { te.tooltip.$element.addClass( CLASSES.FADE_IN_UP ); }					return; }

te.tooltip.calculatePosition( ePageX, ePageY );

$window.on( 'resize.rt', te.onWindowResize ); }

// We redefine this.$element here because e.target can be a reference link inside // a reference tooltip, not a link that was initially assigned to this.$element this.$element = $element;

if ( this.type === 'commentedText' ) { this.$element.attr( 'title', '' ); }

if ( activatedByClick ) { if ( tooltipInitiallyPresent ||					( this.$ref && this.$ref.hasClass( 'rt-target' ) )				) { return; } else { setTimeout( function {						$body.on( 'click.rt touchstart.rt', te.onBodyClick );					}, 0 ); }			}

if ( activatedByClick || tooltipInitiallyPresent ) { reallyShow; } else { this.showTimer = setTimeout( reallyShow, delay ); }		};

this.onBodyClick = function ( e ) { if ( !te.tooltip && !te.$ref.hasClass( 'rt-target' ) ) { return; }

var $current = $( e.target );

function contextMatchesParameter( parameter ) { return this === parameter; }

// The last condition is used to determine cases when a clicked tooltip is the current // element's tooltip or one of its descendants while ( $current.length &&				( !$current.hasClass( 'rt-tooltip' ) || !$current.data( 'tooltip' ) || !$current.data( 'tooltip' ).upToTopParent(						contextMatchesParameter, [ te.tooltip ],						true					) )			) {				$current = $current.parent; }			if ( !$current.length ) { te.hideRef; }		};

this.onWindowResize = function { te.tooltip.calculatePosition; };	}

function Tooltip( te ) { function openSettingsDialog { var settingsDialog, settingsWindow;

if ( cursorWaitCss ) { cursorWaitCss.disabled = true; }

function SettingsDialog { SettingsDialog.parent.call( this ); }			OO.inheritClass( SettingsDialog, OO.ui.ProcessDialog );

SettingsDialog.static.name = 'settingsDialog'; SettingsDialog.static.title = mw.msg( 'rt-settings-title' ); SettingsDialog.static.actions = [ {					modes: 'basic', action: 'save', label: mw.msg( 'rt-save' ), flags: [ 'primary', 'progressive' ] },				{					modes: 'basic', label: mw.msg( 'rt-cancel' ), flags: 'safe' },				{					modes: 'disabled', action: 'deactivated', label: mw.msg( 'rt-done' ), flags: [ 'primary', 'progressive' ] }			];

SettingsDialog.prototype.initialize = function { var dialog = this;

SettingsDialog.parent.prototype.initialize.apply( this, arguments );

this.enableOption = new OO.ui.RadioOptionWidget( {					label: mw.msg( 'rt-enable' )				} ); this.disableOption = new OO.ui.RadioOptionWidget( {					label: mw.msg( 'rt-disable' )				} ); this.enableSelect = new OO.ui.RadioSelectWidget( {					items: [ this.enableOption, this.disableOption ],					classes: [ 'rt-enableSelect' ]				} ); this.enableSelect.selectItem( this.enableOption ); this.enableSelect.on( 'choose', function ( item ) {					if ( item === dialog.disableOption ) {						dialog.activationMethodSelect.setDisabled( true );						dialog.delayInput.setDisabled( true );						dialog.tooltipsForCommentsCheckbox.setDisabled( true );					} else {						dialog.activationMethodSelect.setDisabled( false );						dialog.delayInput.setDisabled( dialog.clickOption.isSelected );						dialog.tooltipsForCommentsCheckbox.setDisabled( false );					}				} );

this.hoverOption = new OO.ui.RadioOptionWidget( {					label: mw.msg( 'rt-hovering' )				} ); this.clickOption = new OO.ui.RadioOptionWidget( {					label: mw.msg( 'rt-clicking' )				} ); this.activationMethodSelect = new OO.ui.RadioSelectWidget( {					items: [ this.hoverOption, this.clickOption ]				} ); this.activationMethodSelect.selectItem( activatedByClick ?					this.clickOption :					this.hoverOption				); this.activationMethodSelect.on( 'choose', function ( item ) {					if ( item === dialog.clickOption ) {						dialog.delayInput.setDisabled( true );					} else {						dialog.delayInput.setDisabled( dialog.clickOption.isSelected );					}				} ); this.activationMethodField = new OO.ui.FieldLayout( this.activationMethodSelect, {					label: mw.msg( 'rt-activationMethod' ),					align: 'top'				} );

this.delayInput = new OO.ui.NumberInputWidget( {					input: { value: delay },					step: 50,					min: 0,					max: 5000,					disabled: activatedByClick,					classes: [ 'rt-numberInput' ]				} ); this.delayField = new OO.ui.FieldLayout( this.delayInput, {					label: mw.msg( 'rt-delay' ),					align: 'top'				} );

this.tooltipsForCommentsCheckbox = new OO.ui.CheckboxInputWidget( {					selected: tooltipsForComments				} ); this.tooltipsForCommentsField = new OO.ui.FieldLayout(					this.tooltipsForCommentsCheckbox,					{						label: new OO.ui.HtmlSnippet( mw.msg( 'rt-tooltipsForComments' ) ),						align: 'inline',						classes: [ 'rt-tooltipsForCommentsField' ]					}				); new TooltippedElement(					this.tooltipsForCommentsField.$element.find( '.' + ( COMMENTED_TEXT_CLASS || 'rt-commentedText' ) )				);

this.fieldset = new OO.ui.FieldsetLayout; this.fieldset.addItems( [					this.activationMethodField,					this.delayField,					this.tooltipsForCommentsField				] );

this.panelSettings = new OO.ui.PanelLayout( {					padded: true,					expanded: false				} ); this.panelSettings.$element.append(					this.enableSelect.$element,					$( ' ' ).addClass( 'rt-settingsFormSeparator' ),					this.fieldset.$element				);

this.panelDisabled = new OO.ui.PanelLayout( {					padded: true,					expanded: false				} ); this.panelDisabled.$element.append(					$( ' ' )						.addClass( 'rt-disabledHelp' )						.append( $( ' ' ).append(								$( ' ' ).append( $( ' ' ).attr( 'src', 'https://en.wikipedia.org/w/load.php?modules=ext.popups.images&image=footer&format=rasterized&lang=ru&skin=vector&version=0uotisb' ) ),								$( ' ' )									.addClass( 'rt-disabledNote' )									.text( mw.msg( 'rt-disabledNote' ) )							) )				);

this.stackLayout = new OO.ui.StackLayout( {					items: [ this.panelSettings, this.panelDisabled ]				} );

this.$body.append( this.stackLayout.$element ); };

SettingsDialog.prototype.getSetupProcess = function ( data ) { return SettingsDialog.parent.prototype.getSetupProcess.call( this, data ) .next( function {						this.stackLayout.setItem( this.panelSettings );						this.actions.setMode( 'basic' );					}, this ); };

SettingsDialog.prototype.getActionProcess = function ( action ) { var dialog = this;

if ( action === 'save' ) { return new OO.ui.Process( function {						var newDelay = Number( dialog.delayInput.getValue );

enabled = dialog.enableOption.isSelected; if ( newDelay >= 0 && newDelay <= 5000 ) { delay = newDelay; }						activatedByClick = dialog.clickOption.isSelected; tooltipsForComments = dialog.tooltipsForCommentsCheckbox.isSelected;

setSettingsCookie;

if ( enabled ) { dialog.close; disableRt; rt( $content ); } else { dialog.actions.setMode( 'disabled' ); dialog.stackLayout.setItem( dialog.panelDisabled ); disableRt; addEnableLink; }					} );				} else if ( action === 'deactivated' ) {					dialog.close;				}				return SettingsDialog.parent.prototype.getActionProcess.call( this, action );			};

SettingsDialog.prototype.getBodyHeight = function { return this.stackLayout.getCurrentItem.$element.outerHeight( true ); };

tooltip.upToTopParent( function adjustRightAndHide {				if ( this.isPresent ) {					if ( this.$element[ 0 ].style.right ) {						this.$element.css( 'right', '+=' + ( window.innerWidth - $window.width ) );					}					this.te.hideRef( true );				}			} );

if ( !windowManager ) { windowManager = new OO.ui.WindowManager; $body.append( windowManager.$element ); }

settingsDialog = new SettingsDialog; windowManager.addWindows( [ settingsDialog ] ); settingsWindow = windowManager.openWindow( settingsDialog ); settingsWindow.opened.then( function {				settingsDialogOpening = false;			} ); settingsWindow.closed.then( function {				windowManager.clearWindows;			} ); }

var tooltip = this;

// This variable can change: one tooltip can be called from a harvard-style reference link // that is put into different tooltips this.te = te;

switch ( this.te.type ) { case 'supRef': this.id = 'rt-' + this.te.$originalElement.attr( 'id' ); this.$content = this.te.$ref .contents .filter( function ( i ) {						var $this = $( this );						return this.nodeType === Node.TEXT_NODE ||							!( $this.is( '.mw-cite-backlink' ) || ( i === 0 &&									// Template:Cnote, Template:Note									( $this.is( 'b' ) || // Template:Note_label $this.is( 'a' ) && $this.attr( 'href' ).indexOf( '#ref' ) === 0 )								)							);					} )					.clone( true ); break; case 'harvardRef': this.id = 'rt-' + this.te.$originalElement.closest( 'li' ).attr( 'id' ); this.$content = this.te.$ref .clone( true ) .removeAttr( 'id' ); break; case 'commentedText': this.id = 'rt-' + String( Math.random ).slice( 2 ); this.$content = $( document.createTextNode( this.te.comment ) ); break; }		if ( !this.$content.length ) { return; }

this.insideWindow = Boolean( this.te.$element.closest( '.oo-ui-window' ).length );

this.$element = $( ' ' ) .addClass( 'rt-tooltip' ) .attr( 'id', this.id ) .attr( 'role', 'tooltip' ) .data( 'tooltip', this ); if ( this.insideWindow ) { this.$element.addClass( 'rt-tooltip-insideWindow' ); }

// We need the $content interlayer here in order for the settings icon to have correct // margins this.$content = this.$content .wrapAll( ' ' ) .parent .addClass( 'rt-tooltipContent' ) .addClass( 'mw-parser-output' ) .appendTo( this.$element );

if ( !activatedByClick ) { this.$element .mouseenter( function {					if ( !tooltip.disappearing ) {						tooltip.upToTopParent( function  { this.show; } );					}				} )				.mouseleave( function ( e ) {					// https://stackoverflow.com/q/47649442 workaround. Relying on relatedTarget					// alone has pitfalls: when alt-tabbing, relatedTarget is empty too					if ( CLIENT_NAME !== 'chrome' || ( !e.originalEvent ||							e.originalEvent.relatedTarget !== null ||							!tooltip.clickedTime ||							$.now - tooltip.clickedTime > 50						) ) {						tooltip.upToTopParent( function { this.te.hideRef; } );					}				} )				.click( function {					tooltip.clickedTime = $.now;				} ); }

if ( !this.insideWindow ) { $( ' ' )				.addClass( 'rt-settingsLink' ) .attr( 'title', mw.msg( 'rt-settings' ) ) .click( function {					if ( settingsDialogOpening ) {						return;					}					settingsDialogOpening = true;

if ( mw.loader.getState( 'oojs-ui' ) !== 'ready' ) { if ( cursorWaitCss ) { cursorWaitCss.disabled = false; } else { cursorWaitCss = mw.util.addCSS( 'body { cursor: wait; }' ); }					}					mw.loader.using( [ 'oojs', 'oojs-ui' ], openSettingsDialog ); } )				.prependTo( this.$content );		}

// Tooltip tail element is inside tooltip content element in order for the tooltip // not to disappear when the mouse is above the tail this.$tail = $( ' ' ) .addClass( 'rt-tooltipTail' ) .prependTo( this.$element );

this.disappearing = false;

this.show = function { this.disappearing = false; clearTimeout( this.te.hideTimer ); clearTimeout( this.te.removeTimer );

this.$element .removeClass( CLASSES.FADE_OUT_DOWN ) .removeClass( CLASSES.FADE_OUT_UP );

if ( !this.isPresent ) { $body.append( this.$element ); }

this.isPresent = true; };

this.hide = function { var tooltip = this;

tooltip.disappearing = true;

if ( tooltip.$element.hasClass( 'rt-tooltip-above' ) ) { tooltip.$element .removeClass( CLASSES.FADE_IN_DOWN ) .addClass( CLASSES.FADE_OUT_UP ); } else { tooltip.$element .removeClass( CLASSES.FADE_IN_UP ) .addClass( CLASSES.FADE_OUT_DOWN ); }

tooltip.te.removeTimer = setTimeout( function {				if ( tooltip.isPresent ) {					tooltip.$element.detach;					tooltip.$tail.css( 'left', '' );

if ( activatedByClick ) { $body.off( 'click.rt touchstart.rt', tooltip.te.onBodyClick ); }					$window.off( 'resize.rt', tooltip.te.onWindowResize );

tooltip.isPresent = false; }			}, 200 );		};

this.calculatePosition = function ( ePageX, ePageY ) { var teElement, teOffsets, teOffset, tooltipTailOffsetX, tooltipTailLeft, offsetYCorrection = 0;

this.$tail.css( 'left', '' );

teElement = this.te.$element.get( 0 ); if ( ePageX !== undefined ) { tooltipTailOffsetX = ePageX; teOffsets = teElement.getClientRects && teElement.getClientRects || teElement.getBoundingClientRect; if ( teOffsets.length > 1 ) { for (var i = teOffsets.length - 1; i >= 0; i--) { if ( ePageY >= Math.round( $window.scrollTop + teOffsets[i].top ) &&							ePageY <= Math.round( $window.scrollTop + teOffsets[i].top + teOffsets[i].height )						) {							teOffset = teOffsets[i]; }					}				}			}

if ( !teOffset ) { teOffset = teElement.getClientRects && teElement.getClientRects[0] || teElement.getBoundingClientRect; }			teOffset = { top: $window.scrollTop + teOffset.top, left: $window.scrollLeft + teOffset.left, width: teOffset.width, height: teOffset.height };			if ( !tooltipTailOffsetX ) { tooltipTailOffsetX = ( teOffset.left * 2 + teOffset.width ) / 2; }			if ( CLIENT_NAME === 'msie' && this.te.type === 'supRef' ) { offsetYCorrection = -Number(					this.te.$element.parent.css( 'font-size' ).replace( 'px', '' )				) / 2; }			this.$element.css( {				top: teOffset.top - this.$element.outerHeight - 7 + offsetYCorrection,				left: tooltipTailOffsetX - 20,				right: ''			} );

// Is it squished against the right side of the page? if ( this.$element.offset.left + this.$element.outerWidth > $window.width - 1 ) { this.$element.css( {					left: '',					right: 0				} ); tooltipTailLeft = tooltipTailOffsetX - this.$element.offset.left - 5; }

// Is a part of it above the top of the screen? if ( teOffset.top < this.$element.outerHeight + $window.scrollTop + 6 ) { this.$element .removeClass( 'rt-tooltip-above' ) .addClass( 'rt-tooltip-below' ) .addClass( CLASSES.FADE_IN_UP ) .css( {						top: teOffset.top + teOffset.height + 9 + offsetYCorrection					} ); if ( tooltipTailLeft ) { this.$tail.css( 'left', ( tooltipTailLeft + 12 ) + 'px' ); }			} else { this.$element .removeClass( 'rt-tooltip-below' ) .addClass( 'rt-tooltip-above' ) .addClass( CLASSES.FADE_IN_DOWN ) // A fix for cases when a tooltip shown once is then wrongly positioned when it // is shown again after a window resize. We just repeat what is above. .css( {						top: teOffset.top - this.$element.outerHeight - 7 + offsetYCorrection					} ); if ( tooltipTailLeft ) { // 12 is the tail element width/height this.$tail.css( 'left', tooltipTailLeft + 'px' ); }			}		};

// Run some function for all the tooltips up to the top one in a tree. Its context will be		// the tooltip, while its parameters may be passed to Tooltip.upToTopParent as an array // in the second parameter. If the third parameter passed to ToolTip.upToTopParent is true, // the execution stops when the function in question returns true for the first time, // and ToolTip.upToTopParent returns true as well. this.upToTopParent = function ( func, parameters, stopAtTrue ) { var returnValue, currentTooltip = this;

do { returnValue = func.apply( currentTooltip, parameters ); if ( stopAtTrue && returnValue ) { break; }			} while ( currentTooltip = currentTooltip.parent );

if ( stopAtTrue ) { return returnValue; }		};	}

if ( !enabled ) { addEnableLink; return; }

teSelector = REF_LINK_SELECTOR; if ( tooltipsForComments ) { teSelector += ', ' + COMMENTED_TEXT_SELECTOR; }	$content.find( teSelector ).each( function {		new TooltippedElement( $( this ) );	} ); }

settingsString = mw.cookie.get( 'RTsettings', '' ); if ( settingsString ) { settings = settingsString.split( '|' ); enabled = Boolean( Number( settings[ 0 ] ) ); delay = Number( settings[ 1 ] ); activatedByClick = Boolean( Number( settings[ 2 ] ) ); // The forth value was added later, so we provide for a default value. See comments below // for why we use "IS_TOUCHSCREEN && IS_MOBILE". tooltipsForComments = settings[ 3 ] === undefined ? IS_TOUCHSCREEN && IS_MOBILE : Boolean( Number( settings[ 3 ] ) ); } else { enabled = true; delay = 200; // Since the mobile browser check is error-prone, adding IS_MOBILE condition here would probably // leave cases where a user interacting with the browser using touches doesn't know how to call // a tooltip in order to switch to activation by click. Some touch-supporting laptop users // interacting by touch (though probably not the most popular use case) would not be happy too. activatedByClick = IS_TOUCHSCREEN; // Arguably we shouldn't convert native tooltips into gadget tooltips for devices that have // mouse support, even if they have touchscreens (there are laptops with touchscreens). // IS_TOUCHSCREEN check here is for reliability, since the mobile check is prone to false // positives. tooltipsForComments = IS_TOUCHSCREEN && IS_MOBILE; }

mw.hook( 'wikipage.content' ).add( rt );

} );