User:Iamojo/testwiki.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
![]() | Documentation for this user script can be added at User:Iamojo/testwiki. This user script seems to have an accompanying .css page at User:Iamojo/testwiki.css. |
// 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")
}() );