|
|
(172 intermediate revisions by the same user not shown) |
Line 1: |
Line 1: |
− | /* https://www.mediawiki.org/wiki/Manual:Interface/JavaScript#User-specific */
| |
− |
| |
| console.log("User:Aemony/common.js executing..."); | | console.log("User:Aemony/common.js executing..."); |
| console.log("-- Link: https://pcgamingwiki.com/wiki/User:Aemony/common.js"); | | console.log("-- Link: https://pcgamingwiki.com/wiki/User:Aemony/common.js"); |
− |
| |
− | console.log("- Site-wide");
| |
− | console.log("-- wgSiteName: " + mw.config.get( 'wgSiteName' ) );
| |
− | console.log("-- wgMainPageTitle: " + mw.config.get( 'wgMainPageTitle' ) );
| |
− |
| |
− | console.log("- Page-specific");
| |
− | console.log("-- wsPageName: " + mw.config.get( 'wgPageName' ) );
| |
− | console.log("-- wgRelevantPageName: " + mw.config.get( 'wgRelevantPageName' ) );
| |
− | console.log("-- wgCategories: " + mw.config.get( 'wgCategories' ) );
| |
− | console.log("-- wgArticleId: " + mw.config.get( 'wgArticleId' ) );
| |
− | console.log("-- wgCurRevisionId: " + mw.config.get( 'wgCurRevisionId' ) );
| |
− | console.log("-- wgTitle: " + mw.config.get( 'wgTitle' ) );
| |
− |
| |
− |
| |
− | if( mw.config.get( 'wgPageName' ) == 'Home' )
| |
− | {
| |
− | var upcomingReleasesRows = document.getElementsByClassName("ur-row-day");
| |
− |
| |
− | }
| |
− |
| |
− |
| |
− | console.log("User:Aemony/common.js finished.");
| |
− |
| |
− |
| |
− |
| |
− |
| |
− | /* ===== FUNCTIONALITY : Adds ReferenceTooltips =========================================================================================================================
| |
− | *
| |
− | * Adds ReferenceTooltips copied directly over from the Russian Wikipedia.
| |
− | *
| |
− | * Information: https://www.mediawiki.org/w/index.php?title=Topic:Ueqlcc482l9yw8gv&topic_showPostId=ueqlcc6fkvg15qzj#flow-post-ueqlcc6fkvg15qzj
| |
− | *
| |
− | * Source Code:
| |
− | * - Javascript : https://ru.wikipedia.org/wiki/MediaWiki:Gadget-referenceTooltips.js
| |
− | * - CSS : https://ru.wikipedia.org/wiki/MediaWiki:Gadget-referenceTooltips.css
| |
− | *
| |
− | * 2018-09-17 - Updated + added english text (Google Translated)
| |
− | *
| |
− | */
| |
− |
| |
− | // See [[mw:Reference Tooltips]]
| |
− |
| |
− | ( function () {
| |
− |
| |
− | // ruwiki settings
| |
− | var REF_LINK_SELECTOR = '.reference, a[href^="#CITEREF"]',
| |
− | COMMENTED_TEXT_CLASS = 'ts-comment-commentedText',
| |
− | COMMENTED_TEXT_SELECTOR = '.' + COMMENTED_TEXT_CLASS + ', abbr[title]';
| |
− |
| |
− | mw.messages.set( {
| |
− | 'rt-settings': ''Tooltip settings',
| |
− | 'rt-enable-footer': 'Enable Reference Tooltips.',
| |
− | 'rt-settings-title': 'Reference Tooltips options',
| |
− | 'rt-save': 'Save settings',
| |
− | 'rt-cancel': 'Cancel',
| |
− | 'rt-enable': 'Enable Reference Tooltips',
| |
− | 'rt-disable': 'Disable Reference Tooltips',
| |
− | 'rt-activationMethod': 'Tooltip is activated by:',
| |
− | 'rt-hovering': 'hovering',
| |
− | 'rt-clicking': 'clicking',
| |
− | 'rt-delay': 'Delay before the tooltip appears (in milliseconds):',
| |
− | 'rt-tooltipsForComments': 'Show tooltips over <span title="Tooltip example" class="'+ COMMENTED_TEXT_CLASS +'" style="border-bottom: 1px dotted; cursor: help;">underlined text</span> in the style of pop-up notes (allows you to see such hints on devices where the mouse is not supported)',
| |
− | 'rt-disabledNote': 'Once disabled, Reference Tooltips can be re-enabled using a link in the footer of the page.',
| |
− | 'rt-ready': 'Ready',
| |
− | 'rt-enabled': 'Tooltips with notes included'
| |
− | } );
| |
− |
| |
− | // "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 }
| |
− | );
| |
− | }
| |
− |
| |
− | 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(
| |
− | $( '<li>' )
| |
− | .addClass( 'rt-enableItem' )
| |
− | .append(
| |
− | $( '<a>' )
| |
− | .text( mw.msg( 'rt-enable-footer' ) )
| |
− | .attr( 'href', 'javascript:' )
| |
− | .click( function ( e ) {
| |
− | e.preventDefault();
| |
− | enableRt();
| |
− | } )
| |
− | )
| |
− | );
| |
− | }
| |
− |
| |
− | function TooltippedElement( $element ) {
| |
− | var tooltip,
| |
− | te = this;
| |
− |
| |
− | function onStartEvent( e ) {
| |
− | if ( activatedByClick ) {
| |
− | e.preventDefault();
| |
− | }
| |
− | if ( !te.noRef ) {
| |
− | te.showRef( $( this ) );
| |
− | }
| |
− | }
| |
− |
| |
− | 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');
| |
− | }
| |
− |
| |
− | this.$element.on( activatedByClick ?
| |
− | { 'click.rt': onStartEvent } :
| |
− | { 'mouseenter.rt': onStartEvent, 'mouseleave.rt': onEndEvent }
| |
− | );
| |
− |
| |
− | 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 ) {
| |
− | // Popups gadget
| |
− | if ( window.pg ) {
| |
− | disableRt();
| |
− | 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();
| |
− |
| |
− | $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-ready' ),
| |
− | 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 )
| |
− | );
| |
− |
| |
− | 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,
| |
− | $( '<hr>' ).addClass( 'rt-settingsFormSeparator' ),
| |
− | this.fieldset.$element
| |
− | );
| |
− |
| |
− | this.panelDisabled = new OO.ui.PanelLayout( {
| |
− | padded: true,
| |
− | expanded: false
| |
− | } );
| |
− | this.panelDisabled.$element.append(
| |
− | $( '<table>' )
| |
− | .addClass( 'rt-disabledHelp' )
| |
− | .append(
| |
− | $( '<tr>' ).append(
| |
− | $( '<td>' ).append(
| |
− | $( '<img>' ).attr( 'src', 'https://ru.wikipedia.org/w/load.php?modules=ext.popups.images&image=footer&format=rasterized&lang=ru&skin=vector&version=0uotisb' )
| |
− | ),
| |
− | $( '<td>' )
| |
− | .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
| |
− | .children()
| |
− | .not( '.mw-cite-backlink' )
| |
− | .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 = $( '<div>' )
| |
− | .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 the correct
| |
− | // margins
| |
− | this.$content = this.$content
| |
− | .wrapAll( '<div>' )
| |
− | .parent()
| |
− | .addClass( 'rt-tooltipContent' )
| |
− | .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 ) {
| |
− | $( '<div>' )
| |
− | .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
| |
− | $( '<div>' )
| |
− | .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();
| |
− |
| |
− | if ( tooltip.tailCss ) {
| |
− | tooltip.tailCss.disabled = true;
| |
− | }
| |
− |
| |
− | 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 () {
| |
− | var teElement, teOffset, teCenter, tooltipTailLeft,
| |
− | offsetYCorrection = 0;
| |
− |
| |
− | if ( this.tailCss ) {
| |
− | this.tailCss.disabled = true;
| |
− | }
| |
− |
| |
− | teElement = this.te.$element.get( 0 );
| |
− | teOffset = teElement.getClientRects &&
| |
− | teElement.getClientRects()[0] ?
| |
− | teElement.getClientRects()[0] :
| |
− | teElement.getBoundingClientRect();
| |
− | teOffset = {
| |
− | top: $window.scrollTop() + teOffset.top,
| |
− | left: $window.scrollLeft() + teOffset.left,
| |
− | width: teOffset.width,
| |
− | height: teOffset.height
| |
− | };
| |
− | teCenter = ( 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: teCenter - 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 = teCenter - 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.tailCss = mw.util.addCSS(
| |
− | '#' + $.escapeSelector( tooltip.id ) + ' .rt-tooltipTail { 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.tailCss = mw.util.addCSS(
| |
− | '#' + $.escapeSelector( tooltip.id ) + ' .rt-tooltipTail { 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 );
| |
− |
| |
− | }() );
| |