Jump to content

User:Iamojo/testwiki.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Iamojo (talk | contribs) at 18:58, 5 February 2025. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// See [[mw:Reference Tooltips]]
// Source https://en.wikipedia.org/wiki/User:Iamojo/testwiki.js

/*eslint space-in-parens: ["error", "always"], array-bracket-spacing: ["error", "always"]*/

( function () {

    mw.loader.load('https://en.wikipedia.org/wiki/User:Iamojo/testwiki.css');

    const REF_LINK_SELECTOR = window.twt_REF_LINK_SELECTOR || '.reference, a[href^="#CITEREF"]',
        COMMENTED_TEXT_CLASS = window.twt_COMMENTED_TEXT_CLASS || 'twt-commentedText',
        COMMENTED_TEXT_SELECTOR = (
            window.twt_COMMENTED_TEXT_SELECTOR ||
            ( COMMENTED_TEXT_CLASS ? '.' + COMMENTED_TEXT_CLASS + ', ' : '' ) +
            'abbr[title]'
        );


    //
    if ( mw.messages.get( 'twt-settings' ) === null ) {
        mw.messages.set( {
            'twt-settings': 'TestWiki Tooltips settings',
            'twt-enable-footer': 'Enable TestWiki Tooltips',
            'twt-settings-title': 'TestWiki Tooltips',
            'twt-save': 'Save',
            'twt-enable': 'Enable TestWiki Tooltips',
            'twt-activationMethod': 'Show a tooltip when I\'m',
            'twt-hovering': 'hovering a reference',
            'twt-clicking': 'clicking a reference',
            'twt-delay': 'Delay before the tooltip appears (in milliseconds)',
            'twt-tooltipsForComments': 'Show the tooltip over <span title="Tooltip example" class="' + ( COMMENTED_TEXT_CLASS || 'twt-commentedText' ) + '" style="border-bottom: 1px dotted; cursor: help;">text with a dotted underline</span> in TestWiki Tooltips style (allows to see such tooltips on devices with no mouse support)',
            'twt-disabledNote': 'You can re-enable TestWiki Tooltips using a link in the footer of the page.',
            'twt-done': 'Done',
            'twt-enabled': 'TestWiki Tooltips are enabled'
        } );
    }

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

        $overlay = $( '<div>' )
            .addClass( 'twt-overlay' )
            .appendTo( $body );

// Can't use before https://phabricator.wikimedia.org/T369880 is resolved
// mw.loader.using( 'mediawiki.page.ready' ).then( function ( require ) {
// 	$teleportTarget = $( require( 'mediawiki.page.ready' ).teleportTarget );
// 	$overlay.appendTo( $teleportTarget );
// } );

    // twt (abbr. for TestWikiTool) is a handler function that gets passed to mw:hook().add
    function twt( $content ) {

        console.log( "twt function START." )
        console.log( "testing window.pg..." )

        // Popups gadget
        if ( window.pg ) {
            console.log( "window.pg exists" )
        } else {
            console.log( "window.pg is null" )
        }

        var elementSelector,
            settingsDialogOpening = false;


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

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

        function disableTwt() {
            $content.find( elementSelector ).removeClass( 'twt-commentedText' ).off( '.twt' );
            $body.off( '.twt' );
            $window.off( '.twt' );
        }

        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();
            }

            // add "Enable" link to footer if not there
            if ( !$footer.find( '.twt-enableItem' ).length ) {
                $footer.append(
                    $( '<li>' )
                        .addClass( 'twt-enableItem' )
                        .append(
                            $( '<a>' )
                                .text( mw.msg( 'twt-enable-footer' ) )
                                .attr( 'href', '#' )
                                .click( function ( e ) {
                                    e.preventDefault();
                                    enableTwt();
                                } )
                        )
                );
            }
        }

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

            function onStartEvent( e ) {
                var showRefArgs;

                if ( activatedByClick && te.type !== 'commentedText' && e.type !== 'contextmenu' ) {
                    e.preventDefault();
                }

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


            function onEndEvent() {
                if ( !te.noRef ) {
                    te.hideRef(); // only hide ref if there was a ref to begin with!
                }
            }

            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;

            // set type of element to supRef (superscript from SUP tag), harvardRef, or commentedText
            if ( this.$element.is( REF_LINK_SELECTOR ) ) {
                if ( this.$element.prop( 'tagName' ) === 'SUP' ) {
                    this.type = 'supRef';
                } else {
                    this.type = 'harvardRef';
                }
            } else {
                this.type = 'commentedText';
                this.comment = this.$element.attr( 'title' );
                if ( !this.comment ) {
                    return;
                }
                this.$element.addClass( 'twt-commentedText' );
            }

            // connect the mouse enter and mouse leave events to the citation element
            // special handling if activated by click
            if ( activatedByClick ) {
                events = {
                    'click.twt': onStartEvent
                };
                // Adds an ability to see tooltips for links
                if (
                    this.type === 'commentedText' &&
                    ( this.$element.closest( 'a' ).length || this.$element.has( 'a' ).length )
                ) {
                    events[ 'contextmenu.twt' ] = onStartEvent;
                }
            } else {
                events = {
                    'mouseenter.twt': onStartEvent,
                    'mouseleave.twt': onEndEvent
                };
            }

            console.log("element events:", events)

            this.$element.on( events );


            // does the work to hide reference info
            this.hideRef = function ( immediately ) {
                clearTimeout( te.showTimer );

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

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

            // does the work to show reference info
            this.showRef = function ( $element, ePageX, ePageY ) {
                // Popups gadget

                // exit if there is already a popup present (i think?)
                if ( window.pg ) {
                    disableTwt();
                    return;
                }

                if ( this.tooltip && !this.tooltip.$content.length ) {
                    return;
                }

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

                function reallyShow() {
                    var viewportTop, refOffsetTop, teHref;

                    // i think this:
                    // sets te.$ref to the reference element that the citation href refers to in the References section
                    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;
                        }
                    }

                    // i think:
                    // if the hovered citation is too close to the reference, just hilite the reference
                    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( 'twt-target' );
                            return;
                        }
                    }

                    // create tooltip with Tooltip Element (te), exit if no content
                    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( '.twt-tooltip' ).data( 'tooltip' );
                    if ( te.tooltip.parent && te.tooltip.parent.disappearing ) {
                        return;
                    }

                    te.tooltip.show();

                    if ( tooltipInitiallyPresent ) {
                        if ( te.tooltip.$element.hasClass( 'twt-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.twt', te.onWindowResize );

                } // end reallyShow function

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

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

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

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

            this.onBodyClick = function ( e ) {
                if ( !te.tooltip && !( te.$ref && te.$ref.hasClass( 'twt-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( 'twt-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();
            };
        } // end TooltippedElement object



        function Tooltip( te ) {

                        // we omit settings for now...

                        // 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( 'twt-settings-title' );
                        //     SettingsDialog.static.actions = [
                        //         {
                        //             modes: 'main',
                        //             action: 'save',
                        //             label: mw.msg( 'twt-save' ),
                        //             flags: [ 'primary', 'progressive' ]
                        //         },
                        //         {
                        //             modes: 'main',
                        //             flags: [ 'safe', 'close' ]
                        //         },
                        //         {
                        //             modes: 'disabled',
                        //             action: 'deactivated',
                        //             label: mw.msg( 'twt-done' ),
                        //             flags: [ 'primary', 'progressive' ]
                        //         }
                        //     ];
                        //
                        //     SettingsDialog.prototype.initialize = function () {
                        //         var dialog = this;
                        //
                        //         SettingsDialog.parent.prototype.initialize.apply( this, arguments );
                        //
                        //         this.enableCheckbox = new OO.ui.CheckboxInputWidget( {
                        //             selected: true
                        //         } );
                        //         this.enableCheckbox.on( 'change', function ( selected ) {
                        //             dialog.activationMethodSelect.setDisabled( !selected );
                        //             dialog.delayInput.setDisabled( !selected || dialog.clickOption.isSelected() );
                        //             dialog.tooltipsForCommentsCheckbox.setDisabled( !selected );
                        //         } );
                        //         this.enableField = new OO.ui.FieldLayout( this.enableCheckbox, {
                        //             label: mw.msg( 'twt-enable' ),
                        //             align: 'inline',
                        //             classes: [ 'twt-enableField' ]
                        //         } );
                        //
                        //         this.hoverOption = new OO.ui.RadioOptionWidget( {
                        //             label: mw.msg( 'twt-hovering' )
                        //         } );
                        //         this.clickOption = new OO.ui.RadioOptionWidget( {
                        //             label: mw.msg( 'twt-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 ) {
                        //             dialog.delayInput.setDisabled( item === dialog.clickOption );
                        //         } );
                        //         this.activationMethodField = new OO.ui.FieldLayout( this.activationMethodSelect, {
                        //             label: mw.msg( 'twt-activationMethod' ),
                        //             align: 'top'
                        //         } );
                        //
                        //         this.delayInput = new OO.ui.NumberInputWidget( {
                        //             input: { value: delay },
                        //             step: 50,
                        //             min: 0,
                        //             max: 5000,
                        //             disabled: activatedByClick,
                        //             classes: [ 'twt-numberInput' ]
                        //         } );
                        //         this.delayField = new OO.ui.FieldLayout( this.delayInput, {
                        //             label: mw.msg( 'twt-delay' ),
                        //             align: 'top'
                        //         } );
                        //
                        //         this.tooltipsForCommentsCheckbox = new OO.ui.CheckboxInputWidget( {
                        //             selected: tooltipsForComments
                        //         } );
                        //         this.tooltipsForCommentsField = new OO.ui.FieldLayout(
                        //             this.tooltipsForCommentsCheckbox,
                        //             {
                        //                 label: new OO.ui.HtmlSnippet( mw.msg( 'twt-tooltipsForComments' ) ),
                        //                 align: 'inline',
                        //                 classes: [ 'twt-tooltipsForCommentsField' ]
                        //             }
                        //         );
                        //         new TooltippedElement(
                        //             this.tooltipsForCommentsField.$element.find(
                        //                 '.' + ( COMMENTED_TEXT_CLASS || 'twt-commentedText' )
                        //             )
                        //         );
                        //
                        //         this.fieldset = new OO.ui.FieldsetLayout();
                        //         this.fieldset.addItems( [
                        //             this.enableField,
                        //             this.activationMethodField,
                        //             this.delayField,
                        //             this.tooltipsForCommentsField
                        //         ] );
                        //
                        //         this.panelSettings = new OO.ui.PanelLayout( {
                        //             padded: true,
                        //             expanded: false
                        //         } );
                        //         this.panelSettings.$element.append( this.fieldset.$element );
                        //
                        //         this.panelDisabled = new OO.ui.PanelLayout( {
                        //             padded: true,
                        //             expanded: false
                        //         } );
                        //         this.panelDisabled.$element.append(
                        //             $( '<table>' )
                        //                 .addClass( 'twt-disabledHelp' )
                        //                 .append(
                        //                     $( '<tr>' ).append(
                        //                         $( '<td>' ).append(
                        //                             $( '<img>' ).attr( 'src', '/media/wikipedia/commons/c/c0/MediaWiki_footer_link_ltr.svg' )
                        //                         ),
                        //                         $( '<td>' )
                        //                             .addClass( 'twt-disabledNote' )
                        //                             .text( mw.msg( 'twt-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( 'main' );
                        //             }, 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.enableCheckbox.isSelected();
                        //                 if ( newDelay >= 0 && newDelay <= 5000 ) {
                        //                     delay = newDelay;
                        //                 }
                        //                 activatedByClick = dialog.clickOption.isSelected();
                        //                 tooltipsForComments = dialog.tooltipsForCommentsCheckbox.isSelected();
                        //
                        //                 setSettingsCookie();
                        //
                        //                 if ( enabled ) {
                        //                     dialog.close();
                        //                     disableTwt();
                        //                     cbt( $content );
                        //                 } else {
                        //                     dialog.actions.setMode( 'disabled' );
                        //                     dialog.stackLayout.setItem( dialog.panelDisabled );
                        //                     disableTwt();
                        //                     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();
                        //     } );
                        //
                        // } // end openSettingsDialog

            var tooltip = this;

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

            switch ( this.te.type ) {
                case 'supRef':
                    this.id = 'twt-' + this.te.$originalElement.attr( 'id' );

                    // this gets the reference content and displays it
                    this.$content = this.te.$ref
                        .contents()
                        .filter( function ( i ) {
                            var $this = $( this );
                            return (
                                this.nodeType === Node.TEXT_NODE ||
                                !(
                                    $this.is( '.mw-cite-backlink' ) ||
                                    (
                                        i === 0 &&
                                        // Template:Cnote, Template:Note
                                        ( $this.is( 'b' ) ||
                                            // Template:Note_label
                                            $this.is( 'a' ) &&
                                            $this.attr( 'href' ).indexOf( '#ref' ) === 0
                                        )
                                    )
                                )
                            );
                        } )
                        .clone( true );

                    $('<div>A Book Preview</div>')
                        .addClass( 'twt-content-header' )
                        .prependTo( this.$content );

                    $('<img>', {
                        id: 'imagePreview',
                        style: 'max-width: 500px; display: none;'
                    }).appendTo(this.$content);
                    
                    break;
                case 'harvardRef':
                    this.id = 'twt-' + this.te.$originalElement.closest( 'li' ).attr( 'id' );
                    this.$content = this.te.$ref
                        .clone( true )
                        .removeAttr( 'id' );
                    break;
                case 'commentedText':
                    this.id = 'twt-' + String( Math.random() ).slice( 2 );
                    this.$content = $( document.createTextNode( this.te.comment ) );
                    break;
            }
            if ( !this.$content.length ) {
                return;
            }

            // isInsideWindow used to determine display of "Show Settings" button
            this.isInsideWindow = Boolean( this.te.$element.closest( '.oo-ui-window' ).length );

            this.$element = $( '<div>' )
                .addClass( 'twt-tooltip' )
                .attr( 'id', this.id )
                .attr( 'role', 'tooltip' )
                .data( 'tooltip', this );

            var $hoverArea = $( '<div>' )
                .addClass( 'twt-hoverArea' )
                .appendTo( this.$element );

            var $scroll = $( '<div>' )
                .addClass( 'twt-scroll' )
                .appendTo( $hoverArea );

            this.$content = this.$content
                .wrapAll( '<div>' )
                .parent()
                .addClass( 'twt-content' )
                .addClass( 'mw-parser-output' )
                .appendTo( $scroll );

            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();
                    } );
            }

            // // deactivate settings for now...
            //
            // if ( !this.isInsideWindow ) {
            //     $( '<a>' )
            //         .addClass( 'twt-settingsLink' )
            //         .attr( 'role', 'button' )
            //         .attr( 'href', '#' )
            //         .attr( 'title', mw.msg( 'twt-settings' ) )
            //         .click( function ( e ) {
            //             e.preventDefault();
            //             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( 'twt-tail' )
                .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 ) {
                    $overlay.append( this.$element );
                }

                this.isPresent = true;
            };

            this.hide = function () {
                var tooltip = this;

                tooltip.disappearing = true;

                if ( tooltip.$element.hasClass( 'twt-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.twt touchstart.twt', tooltip.te.onBodyClick );
                        }
                        $window.off( 'resize.twt', tooltip.te.onWindowResize );

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

            this.calculatePosition = function ( ePageX, ePageY ) {
                var teElement, teOffsets, teOffset, targetTailOffsetX, tailLeft;

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

                teElement = this.te.$element.get( 0 );
                if ( ePageX !== undefined ) {
                    targetTailOffsetX = 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 ( !targetTailOffsetX ) {
                    targetTailOffsetX = teOffset.left + ( teOffset.width / 2 );
                }

                // Value of `left` in `.twt-tooltip-above .twt-tail`
                var defaultTailLeft = 19;

                // Value of `width` in `.twt-tail`
                var tailSideWidth = 13;

                // We tilt the square 45 degrees, so we need square root to calculate the distance.
                var tailWidth = tailSideWidth * Math.SQRT2;
                var tailHeight = tailWidth / 2;
                var tailCenterDelta = tailSideWidth + 1 - ( tailWidth / 2 );

                var tooltip = this;
                var getTop = function ( isBelow ) {
                    var delta = isBelow
                        ? teOffset.height + tailHeight
                        : -tooltip.$element.outerHeight() - tailHeight + 1;
                    return teOffset.top + delta;
                };

                this.$element.css( {
                    top: getTop(),
                    left: targetTailOffsetX - defaultTailLeft - tailCenterDelta,
                    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
                    } );
                    tailLeft = targetTailOffsetX - this.$element.offset().left - tailCenterDelta;
                }

                // Is a part of it above the top of the screen?
                if ( teOffset.top < this.$element.outerHeight() + $window.scrollTop() + tailHeight ) {
                    this.$element
                        .removeClass( 'twt-tooltip-above' )
                        .addClass( 'twt-tooltip-below' )
                        .addClass( CLASSES.FADE_IN_UP )
                        .css( {
                            top: getTop( true )
                        } );
                    if ( tailLeft ) {
                        this.$tail.css( 'left', ( tailLeft + tailSideWidth ) + 'px' );
                    }
                } else {
                    this.$element
                        .removeClass( 'twt-tooltip-below' )
                        .addClass( 'twt-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.
                        .css( {
                            top: getTop()
                        } );
                    if ( tailLeft ) {
                        this.$tail.css( 'left', tailLeft + 'px' );
                    }
                }
            }; // end calculatePosition

            // Run some function for all the tooltips up to the top one in a tree.
            // Its context will be the tooltip.
            // 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, then:
            //  Execution stops when the function in question returns true for the first time
            //  ToolTip.upToTopParent returns true
            //  .
            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;
                }
            };
        } // end Tooltip object

        console.log(`TestWiki 'enabled' is ${enabled ?"TRUE":"FALSE"}`)
        if ( !enabled ) {
            addEnableLink();
            return;
        }

        elementSelector = REF_LINK_SELECTOR;
        if ( tooltipsForComments ) {
            elementSelector += ', ' + COMMENTED_TEXT_SELECTOR;
        }

        $content.find( elementSelector ).each( function () {
            // 'this' is the found link element
            new TooltippedElement( $( this ) );
        } );
    }  // end of twt function

    console.log( "Hello from TestWiki.js!" )

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

    console.log("about to add twt to wikipage.content hook")
    mw.hook( 'wikipage.content' ).add( twt );
    console.log("added twt to wikipage.content hook")

}() );