Différences entre versions de « MediaWiki:Gadget-ReferenceTooltips.js »

De Témoins de Jéhovah: TJ-Encyclopedie, l'encyclopédie libre sur les Témoins de Jéhovah
Sauter à la navigation Sauter à la recherche
(Page créée avec « window.pg || $(document).ready( function($) { // Make sure we are in article, project, or help namespace if ( wgCanonicalNamespace === '' || wgCanonicalNamespace... »)
 
 
Ligne 1 : Ligne 1 :
 +
// See [[mw:Reference Tooltips]]
 +
// Source https://en.wikipedia.org/wiki/MediaWiki:Gadget-ReferenceTooltips.js
  
window.pg || $(document).ready( function($) {
+
window.referenceTooltipsEnabled = true;
  
     // Make sure we are in article, project, or help namespace
+
if( typeof tooltipRefEnabled === 'undefined' ) {
    if ( wgCanonicalNamespace === '' || wgCanonicalNamespace === 'Project' || wgCanonicalNamespace === 'Help' ) {
+
     ( function () {
         function toggleRT(o){
+
        // Settings
             mw.loader.using("jquery.cookie",function(){
+
        var REF_LINK_SELECTOR = '.reference:not(.cite_virgule), a[href^="#CITEREF"]',
                    $.cookie("RTsettings",o+"|"+ settings[1] + "|" + settings[2], {path:"/",expires:90});
+
            REF_JUMP_UP_SELECTOR = '.mw-cite-backlink';
                    location.reload();
+
 
             })
+
        mw.messages.set({
         }
+
            'rt-settings': 'Paramètres des infobulles des références',
        var settings = document.cookie.split("RTsettings=")[1];
+
            'rt-enable-footer': 'Activer les infobulles des références',
        settings = settings ? settings.split(";")[0].split("%7C") : [1, 200, +("ontouchstart" in document.documentElement)];
+
            'rt-settings-title': 'Gadget ReferenceTooltips',
         if( settings[0] == 0 ) {
+
            'rt-save': 'Enregistrer',
            var footer = $("#footer-places, #f-list");
+
            'rt-cancel': 'Annuler',
            if( footer.length === 0 ) {
+
            'rt-enable': 'Activer',
                    footer = $("#footer li").parent();
+
            'rt-disable': 'Désactiver',
 +
            'rt-activationMethod': 'L’infobulle doit apparaître au :',
 +
            'rt-hovering': 'survol',
 +
            'rt-clicking': 'clic',
 +
            'rt-delay': 'Délai avant que l’infobulle apparaisse (en millisecondes) :',
 +
            'rt-disabledNote': 'Vous pouvez réactiver les infobulles en cliquant sur le lien «\u00a0Activer les infobulles des références\u00a0» en bas de la page.',
 +
            'rt-done': 'Terminer',
 +
            'rt-enabled': 'Le gadget ReferenceTooltips est activé.'
 +
        });
 +
       
 +
        // "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,
 +
            CLIENT_NAME = $.client.profile().name,
 +
            settingsString, settings, enabled, delay, activatedByClick, cursorWaitCss, windowManager,
 +
            $body = $( document.body ),
 +
            $window = $( window );
 +
       
 +
         function rt( $content ) {
 +
             // Popups gadget
 +
            if ( window.pg ) {
 +
                return;
 +
            }
 +
       
 +
            var settingsDialogOpening = false;
 +
       
 +
            function setSettingsCookie() {
 +
                mw.cookie.set(
 +
                    'RTsettings',
 +
                    Number( enabled ) + '|' + delay + '|' + Number( activatedByClick ),
 +
                    { 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( REF_LINK_SELECTOR ).off( '.rt' );
 +
                $body.off( '.rt' );
 +
                $window.off( '.rt' );
 
             }
 
             }
             footer.append($("<li>").append($("<a>").text("Enable Reference Tooltips").attr("href","javascript:(function(){})()").click(function(){toggleRT(1)})));
+
       
            return;
+
             function addEnableLink() {
        }
+
                // #footer-places – Vector
        var isTouchscreen = +settings[2],
+
                // #f-list – Timeless, Monobook, Modern
            timerLength = isTouchscreen ? 0 : +settings[1],
+
                // parent of #footer li – Cologne Blue
            settingsMenu;
+
                var $footer = $( '#footer-places, #f-list' );
        $(".reference").each( function() {
+
                if ( !$footer.length ) {
            var tooltipNode, hideTimer, showTimer, checkFlip = false;
+
                    $footer = $( '#footer li' ).parent();
            function findRef( h ){
+
                }
                     h = h.firstChild.getAttribute("href"); h = h && h.split("#"); h = h && h[1];
+
                $footer.append(
                    h = h && document.getElementById( h );
+
                    $( '<li>' )
                    h = h && h.nodeName == "LI" && h;
+
                        .addClass( 'rt-enableItem' )
                    return h;
+
                        .append(
 +
                            $( '<a>' )
 +
                                .text( mw.msg( 'rt-enable-footer' ) )
 +
                                .attr( 'href', 'javascript:' )
 +
                                .click( function ( e ) {
 +
                                    e.preventDefault();
 +
                                    enableRt();
 +
                                } )
 +
                     )
 +
                );
 
             }
 
             }
             function hide( refLink ){
+
       
                     if( tooltipNode && tooltipNode.parentNode == document.body ) {
+
             function TooltippedElement( $element ) {
                             hideTimer = setTimeout( function() {
+
                var events,
                                    $(tooltipNode).animate({opacity: 0}, 100, function(){ document.body.removeChild( tooltipNode ) })
+
                    te = this;
                             }, isTouchscreen ? 16 : 100)
+
       
 +
                function onStartEvent( e ) {
 +
                    var showRefArgs;
 +
       
 +
                     if ( activatedByClick && 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.prop( 'tagName' ) === 'SUP' ) {
 +
                    this.type = 'supRef';
 +
                } else {
 +
                    this.type = 'harvardRef';
 +
                }
 +
               
 +
                if ( activatedByClick ) {
 +
                    events = {
 +
                        'click.rt': onStartEvent
 +
                    };
 +
                } else {
 +
                    events = {
 +
                        'mouseenter.rt': onStartEvent,
 +
                        'mouseleave.rt': onEndEvent
 +
                    };
 +
                }
 +
       
 +
                this.$element.on( events );
 +
       
 +
                this.hideRef = function ( immediately ) {
 +
                    clearTimeout( te.showTimer );
 +
       
 +
                    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 ( 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 {
 
                     } else {
                            var h = findRef( refLink );
+
                        this.showTimer = setTimeout( reallyShow, delay );
                            h && (h.style.border = "");
 
 
                     }
 
                     }
 +
                };
 +
       
 +
                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 show(){
+
       
                     if( !tooltipNode.parentNode || tooltipNode.parentNode.nodeType === 11 ){
+
             function Tooltip( te ) {
                            document.body.appendChild( tooltipNode );
+
                function openSettingsDialog() {
                            checkFlip = true;
+
                    var settingsDialog, settingsWindow;
 +
       
 +
                     if ( cursorWaitCss ) {
 +
                        cursorWaitCss.disabled = true;
 
                     }
 
                     }
                     $(tooltipNode).stop().animate({opacity: 1}, 100)
+
       
                    clearTimeout( hideTimer );
+
                     function SettingsDialog() {
            }
+
                        SettingsDialog.parent.call( this );
            function openSettingsMenu(){
+
                    }
                    if( settingsMenu ) {
+
                    OO.inheritClass( SettingsDialog, OO.ui.ProcessDialog );
                             settingsMenu.dialog( "open" );
+
       
                    } else {
+
                    SettingsDialog.static.name = 'settingsDialog';
                             settingsMenu = $("<form>").append(
+
                    SettingsDialog.static.title = mw.msg( 'rt-settings-title' );
                                    $("<button>").css("width","100%").text("Disable Reference Tooltips").button().click(function(){toggleRT(0)}),
+
                    SettingsDialog.static.actions = [
                                    $("<br>"),
+
                        {
                                    $("<small>").text("Once disabled, Reference Tooltips can be re-enabled using a link in the footer of the page."),
+
                            modes: 'basic',
                                    $("<hr>"),
+
                            action: 'save',
                                    $("<label>").text("Delay before the tooltip appears (in milliseconds): ").append($("<input>").attr({"type":"number","value":settings[1],step:50,min:0,max:5000})),
+
                            label: mw.msg( 'rt-save' ),
                                    $("<br>"),
+
                            flags: [ 'primary', 'progressive' ]
                                    $("<span>").text("Tooltip is activated by:"),
+
                        },
                                    $("<label>").append(
+
                        {
                                            $("<input>").attr({"type":"radio", "name":"RTActivate", "checked":settings[2]==0&&"checked", "disabled":"ontouchstart" in document.documentElement&&"disabled"}),
+
                            modes: 'basic',
                                            "hovering"
+
                            label: mw.msg( 'rt-cancel' ),
                                    ),
+
                            flags: 'safe'
                                     $("<label>").append(
+
                        },
                                            $("<input>").attr({"type":"radio", "name":"RTActivate", "checked":settings[2]==1&&"checked"}),
+
                        {
                                             "clicking"
+
                            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 );
 +
                            } else {
 +
                                dialog.activationMethodSelect.setDisabled( false );
 +
                                dialog.delayInput.setDisabled( dialog.clickOption.isSelected() );
 +
                            }
 +
                        } );
 +
       
 +
                        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.fieldset = new OO.ui.FieldsetLayout();
 +
                        this.fieldset.addItems( [
 +
                            this.activationMethodField,
 +
                            this.delayField
 +
                        ] );
 +
       
 +
                        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(
 +
                                            // Don't remove the "lang" and "skin" parameters, they may be mandatory in the future: https://github.com/wikimedia/mediawiki/commit/fa05976
 +
                                            $( '<img>' ).attr( 'src', mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/load.php?modules=ext.popups.images&image=footer&lang=fr&skin=fallback' )
 +
                                        ),
 +
                                        $( '<td>' )
 +
                                            .addClass( 'rt-disabledNote' )
 +
                                             .text( mw.msg( 'rt-disabledNote' ) )
 
                                     )
 
                                     )
                             ).submit(function(e){e.preventDefault()}).dialog({modal:true,width:500,title:"Reference Tooltips options",buttons:{"Save settings":function(){
+
                                )
                                    var a = this.getElementsByTagName("input"),
+
                        );
                                            b = +a[0].value;
+
       
                                     $.cookie("RTsettings","1|"+ (b > -1 && b < 5001 ? b : settings[1]) + (a[1].checked ? "|0" : "|1"), {path:"/",expires:90});
+
                        this.stackLayout = new OO.ui.StackLayout( {
                                     location.reload();
+
                             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();
 +
       
 +
                                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 );
 
                     }
 
                     }
            }
+
       
            $(this)[ isTouchscreen ? 'click' : 'hover' ](function( e ){
+
                    settingsDialog = new SettingsDialog();
                     var _this = this;
+
                    windowManager.addWindows( [ settingsDialog ] );
                     if( isTouchscreen ) {
+
                     settingsWindow = windowManager.openWindow( settingsDialog );
                            e.preventDefault();
+
                     settingsWindow.opened.then( function () {
                            (tooltipNode && tooltipNode.parentNode == document.body) || setTimeout( function(){
+
                        settingsDialogOpening = false;
                                    $( document.body ).on("click touchstart", function( e ) {
+
                    } );
                                            e = e || event;
+
                    settingsWindow.closed.then( function () {
                                            e = e.target || e.srcElement;
+
                        windowManager.clearWindows();
                                            for( ; e && !$( e ).hasClass( "referencetooltip" ) ; )
+
                    } );
                                                    e = e.parentNode;
+
                }
                                            if( !e ){
+
       
                                                    clearTimeout( showTimer );
+
                var tooltip = this;
                                                    hide( _this );
+
       
                                                    $(document.body).off("click touchstart", arguments.callee)
+
                // This variable can change: one tooltip can be called from a harvard-style reference link
                                            }
+
                // that is put into different tooltips
                                    })
+
                this.te = te;
                            }, 0);
+
       
 +
                function filterNoteContents( i, elm ) {
 +
                    var $elm = $( elm );
 +
                    if ( elm.nodeType === Node.TEXT_NODE ) {
 +
                        return true;
 +
                    } else if ( $elm.is( REF_JUMP_UP_SELECTOR ) ) {
 +
                        return false;
 +
                    } else if (
 +
                        // Template:Note_label
 +
                        i === 0 &&
 +
                        $elm.is( 'a' ) &&
 +
                        $elm.attr( 'href' ).indexOf( '#ref' ) === 0
 +
                    ) {
 +
                        return false;
 
                     }
 
                     }
                     showTimer && clearTimeout( showTimer );
+
                     return true;
                    showTimer = setTimeout( function() {
+
                }
                             var h = findRef( _this );
+
       
                             if( !h ){return};
+
                switch ( this.te.type ) {
                            if( !isTouchscreen && ( window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0 ) + $(window).height() > $( h ).offset().top + h.offsetHeight ) {
+
                    case 'supRef':
                                     h.style.border = "#080086 2px solid";
+
                        this.id = 'rt-' + this.te.$originalElement.attr( 'id' );
                                    return;
+
                        this.$content = this.te.$ref
 +
                            .contents()
 +
                            .filter( filterNoteContents )
 +
                             .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;
 +
                }
 +
                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 correct
 +
                // margins
 +
                this.$content = this.$content
 +
                    .wrapAll( '<div>' )
 +
                    .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();
 +
                                } );
 
                             }
 
                             }
                            if(!tooltipNode){
+
                        } )
                                    tooltipNode = document.createElement("ul");
+
                        .mouseleave( function ( e ) {
                                    tooltipNode.className = "referencetooltip";
+
                            // https://stackoverflow.com/q/47649442 workaround. Relying on relatedTarget
                                    var c = tooltipNode.appendChild( h.cloneNode( true ) );
+
                            // alone has pitfalls: when alt-tabbing, relatedTarget is empty too
                                    try {
+
                            if ( CLIENT_NAME !== 'chrome' ||
                                            if( c.firstChild.nodeName != "A" ) {
+
                                ( !e.originalEvent ||
                                                    while( c.childNodes[1].nodeName == "A" && c.childNodes[1].getAttribute( "href" ).indexOf("#cite_ref-") !== -1 ) {
+
                                    e.originalEvent.relatedTarget !== null ||
                                                            do { c.removeChild( c.childNodes[1] ) } while ( c.childNodes[1].nodeValue == " " );
+
                                     !tooltip.clickedTime ||
                                                    }
+
                                     $.now() - tooltip.clickedTime > 50
                                            }
+
                                )
                                     } catch (e) { mw.log(e) }
+
                            ) {
                                    c.removeChild( c.firstChild );
+
                                tooltip.upToTopParent( function () {
                                     $( tooltipNode.firstChild.insertBefore( document.createElement( "span" ), tooltipNode.firstChild.firstChild ) ).addClass("RTsettings").attr("title", "Tooltip settings").click(function(){
+
                                    this.te.hideRef();
                                            mw.loader.using(["jquery.cookie","jquery.ui.dialog"], openSettingsMenu);
+
                                } );
                                    })
 
                                    tooltipNode.appendChild( document.createElement( "li" ) );
 
                                    isTouchscreen || $(tooltipNode).hover(show, hide);
 
 
                             }
 
                             }
                            show();
+
                        } )
                             var o = $(_this).offset(), oH = tooltipNode.offsetHeight;
+
                        .click( function () {
                            $(tooltipNode).css({top: o.top - oH, left: o.left - 7 });
+
                             tooltip.clickedTime = $.now();
                            if( tooltipNode.offsetHeight > oH ) { // is it squished against the right side of the page?
+
                        } );
                                    $(tooltipNode).css({left:'auto',right:0});
+
                }
                                    tooltipNode.lastChild.style.marginLeft = (o.left - tooltipNode.offsetLeft) + "px";
+
       
 +
                if ( !this.insideWindow ) {
 +
                    $( '<div>' )
 +
                        .addClass( 'rt-settingsLink' )
 +
                        .attr( 'title', mw.msg( 'rt-settings' ) )
 +
                        .click( function () {
 +
                            if ( settingsDialogOpening ) {
 +
                                return;
 
                             }
 
                             }
                             if( checkFlip ) {
+
                             settingsDialogOpening = true;
                                    if( o.top < tooltipNode.offsetHeight + ( window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0 ) ) { // is part of it above the top of the screen?
+
       
                                            $(tooltipNode).addClass("RTflipped").css({top: o.top + 12});
+
                            if ( mw.loader.getState( 'oojs-ui' ) !== 'ready' ) {
                                    } else if( tooltipNode.className === "referencetooltip RTflipped" ) { // cancel previous
+
                                if ( cursorWaitCss ) {
                                            $(tooltipNode).removeClass("RTflipped");
+
                                    cursorWaitCss.disabled = false;
                                    }
+
                                } else {
                                    checkFlip = false;
+
                                    cursorWaitCss = mw.util.addCSS( 'body { cursor: wait; }' );
 +
                                }
 
                             }
 
                             }
                    }, timerLength);
+
                            mw.loader.using( [ 'oojs', 'oojs-ui' ], openSettingsDialog );
            }, isTouchscreen ? undefined : function(){clearTimeout(showTimer); hide(this); } )
+
                        } )
+
                        .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 = $( '<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();
 +
                           
 +
                            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;
 +
            }
 +
       
 +
            $content.find( REF_LINK_SELECTOR ).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 ] ) );
 +
        } else {
 +
            enabled = true;
 +
            delay = 200;
 +
            // Since mobile browser checks are error-prone, adding an 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;
 +
        }
 +
       
 +
        mw.hook( 'wikipage.content' ).add( rt );
 +
       
 +
    }() );
 +
}

Version actuelle datée du 6 octobre 2020 à 17:56

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

window.referenceTooltipsEnabled = true;

if( typeof tooltipRefEnabled === 'undefined' ) {
    ( function () {
        // Settings
        var REF_LINK_SELECTOR = '.reference:not(.cite_virgule), a[href^="#CITEREF"]',
            REF_JUMP_UP_SELECTOR = '.mw-cite-backlink';

        mw.messages.set({
            'rt-settings': 'Paramètres des infobulles des références',
            'rt-enable-footer': 'Activer les infobulles des références',
            'rt-settings-title': 'Gadget ReferenceTooltips',
            'rt-save': 'Enregistrer',
            'rt-cancel': 'Annuler',
            'rt-enable': 'Activer',
            'rt-disable': 'Désactiver',
            'rt-activationMethod': 'L’infobulle doit apparaître au :',
            'rt-hovering': 'survol',
            'rt-clicking': 'clic',
            'rt-delay': 'Délai avant que l’infobulle apparaisse (en millisecondes) :',
            'rt-disabledNote': 'Vous pouvez réactiver les infobulles en cliquant sur le lien «\u00a0Activer les infobulles des références\u00a0» en bas de la page.',
            'rt-done': 'Terminer',
            'rt-enabled': 'Le gadget ReferenceTooltips est activé.'
        });
        
        // "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,
            CLIENT_NAME = $.client.profile().name,
            settingsString, settings, enabled, delay, activatedByClick, cursorWaitCss, windowManager,
            $body = $( document.body ),
            $window = $( window );
        
        function rt( $content ) {
            // Popups gadget
            if ( window.pg ) {
                return;
            }
        
            var settingsDialogOpening = false;
        
            function setSettingsCookie() {
                mw.cookie.set(
                    'RTsettings',
                    Number( enabled ) + '|' + delay + '|' + Number( activatedByClick ),
                    { 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( REF_LINK_SELECTOR ).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 events,
                    te = this;
        
                function onStartEvent( e ) {
                    var showRefArgs;
        
                    if ( activatedByClick && 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.prop( 'tagName' ) === 'SUP' ) {
                    this.type = 'supRef';
                } else {
                    this.type = 'harvardRef';
                }
                
                if ( activatedByClick ) {
                    events = {
                        'click.rt': onStartEvent
                    };
                } else {
                    events = {
                        'mouseenter.rt': onStartEvent,
                        'mouseleave.rt': onEndEvent
                    };
                }
        
                this.$element.on( events );
        
                this.hideRef = function ( immediately ) {
                    clearTimeout( te.showTimer );
        
                    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 ( 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 );
                            } else {
                                dialog.activationMethodSelect.setDisabled( false );
                                dialog.delayInput.setDisabled( dialog.clickOption.isSelected() );
                            }
                        } );
        
                        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.fieldset = new OO.ui.FieldsetLayout();
                        this.fieldset.addItems( [
                            this.activationMethodField,
                            this.delayField
                        ] );
        
                        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(
                                            // Don't remove the "lang" and "skin" parameters, they may be mandatory in the future: https://github.com/wikimedia/mediawiki/commit/fa05976
                                            $( '<img>' ).attr( 'src', mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/load.php?modules=ext.popups.images&image=footer&lang=fr&skin=fallback' )
                                        ),
                                        $( '<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();
        
                                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;
        
                function filterNoteContents( i, elm ) {
                    var $elm = $( elm );
                    if ( elm.nodeType === Node.TEXT_NODE ) {
                        return true;
                    } else if ( $elm.is( REF_JUMP_UP_SELECTOR ) ) {
                        return false;
                    } else if (
                        // Template:Note_label
                        i === 0 &&
                        $elm.is( 'a' ) &&
                        $elm.attr( 'href' ).indexOf( '#ref' ) === 0
                    ) {
                        return false;
                    }
                    return true;
                }
        
                switch ( this.te.type ) {
                    case 'supRef':
                        this.id = 'rt-' + this.te.$originalElement.attr( 'id' );
                        this.$content = this.te.$ref
                            .contents()
                            .filter( filterNoteContents )
                            .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;
                }
                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 correct
                // margins
                this.$content = this.$content
                    .wrapAll( '<div>' )
                    .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 ) {
                    $( '<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
                this.$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();
                            
                            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;
            }
        
            $content.find( REF_LINK_SELECTOR ).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 ] ) );
        } else {
            enabled = true;
            delay = 200;
            // Since mobile browser checks are error-prone, adding an 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;
        }
        
        mw.hook( 'wikipage.content' ).add( rt );
        
    }() );
}