Aller au contenu

MediaWiki:Gadget-ReferenceTooltips.js

Une page de Wikipédia, l'encyclopédie libre.
Ceci est une version archivée de cette page, en date du 3 septembre 2019 à 22:34 et modifiée en dernier par Od1n (discuter | contributions) (extraction du code de filtrage dans une fonction distincte, et remplacement des ternaries par des classiques if/elseif ; cela pour faciliter la compréhension de ce morceau de code, et faciliter sa maintenance). Elle peut contenir des erreurs, des inexactitudes ou des contenus vandalisés non présents dans la version actuelle.
Note : après avoir enregistré la page, vous devrez forcer le rechargement complet du cache de votre navigateur pour voir les changements.

Mozilla / Firefox / Konqueror / Safari : maintenez la touche Majuscule (Shift) en cliquant sur le bouton Actualiser (Reload) ou pressez Maj-Ctrl-R (Cmd-R sur Apple Mac) ;

Firefox (sur GNU/Linux) / Chrome / Internet Explorer / Opera : maintenez la touche Ctrl en cliquant sur le bouton Actualiser ou pressez Ctrl-F5.
// 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, a[href^="#CITEREF"]',
            REF_JUMP_UP_SELECTOR = '.mw-cite-up-arrow, .renvois_vers_le_texte';

        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 n’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(
                                            $( '<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 ) {
                    var $this = $( this );
                    if ( this.nodeType === Node.TEXT_NODE ) {
                        return true;
                    } else if ( $this.is( REF_JUMP_UP_SELECTOR ) ) {
                        return false;
                    } else if (
                        // Template:Note_label
                        i === 0 &&
                        $this.is( 'a' ) &&
                        $this.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 );
        
    }() );
}