MediaWiki:Gadget-refTooltip.js

$.when(	$.ready,	mw.loader.using( [ 'mediawiki.ui.button', 'mediawiki.ui.checkbox' ] ) ).done( function {	'use strict';	var i18n = {		cancelButton: 'Cancel',		doneButton: 'Done',		enableLabel: 'Enable reference tooltips',		optionsButtonTitle: 'Change reference tooltip options',		saveFailedStorageFull: "Is your browser's localStorage full?",		saveFailedTitle: 'Saving options failed'	};	var $win = $( window );	var $body = $( '.mw-body' );	var $content = $( '#mw-content-text' );	var $tooltip = $;	var $tooltipText = $;	var tooltipRect, tooltipInnerWidth, tooltipOffset, $anchor, anchorRect;	var showTimer, hideTimer;	var options;	try {		options = JSON.parse( localStorage.refTooltip );	} catch ( e ) {		options = {			enabled: true		};	}	importStylesheet( 'User:Majr/refTooltip.css' );	var createTooltip = function( $anchor, content, showOptions, closeCallback ) {		// Get rid of any existing tooltip $tooltip.remove; // Create the tooltip $tooltip = $( ' ' ).addClass( 'ref-tooltip' ).data( {			anchor: $anchor,			fresh: true,			closeCallback: closeCallback		} ).hover( function {		// Callback to the ref's hover functions when the tooltip is hovered over			$anchor.mouseenter;		}, function {			$anchor.mouseleave;		} ); $tooltipText = $( ' ' ).addClass( 'ref-tooltip-text' ) .append( content ).appendTo( $tooltip ); if ( showOptions ) { $tooltipText.prepend(				$( ' ' ).addClass( 'ref-tooltip-options-button' )					.attr( 'title', i18n.optionsButtonTitle )			); }		$( ' ' ).addClass( 'ref-tooltip-arrow' ).appendTo( $tooltip ); $tooltip.appendTo( 'body' ); // Set width to content size $tooltipText.width( $tooltipText.width + 1 ); setPos( true ); // Data prevents tooltips being immediately closed if opened via a click setTimeout( function {			$tooltip.removeData( 'fresh' );		}, 0 ); };	var removeTooltip = function { var callback = $tooltip.data( 'closeCallback' ); if ( callback ) { callback; }		$tooltip.remove; $tooltip = $; };	var getRefText = function( $ref ) { var refId = $ref.find( 'a' ).attr( 'href' ).split( '#' )[1]; var $refText = $( document.getElementById( refId ) ); return $refText.find( '.cite-reference-text' ).html; };	var setPos = function( initial ) { if ( !$tooltip.length ) { return; }		if ( initial ) { tooltipRect = $tooltipText[0].getBoundingClientRect; tooltipInnerWidth = $tooltipText.width; tooltipOffset = { top: parseFloat( $tooltipText.css( 'margin-top' ) ), left: parseFloat( $tooltipText.css( 'margin-left' ) ) };			$anchor = $tooltip.data( 'anchor' ); anchorRect = $anchor[0].getBoundingClientRect; } else { $tooltip.removeClass( 'ref-tooltip-flipped' ); $tooltipText.css( 'margin-left', '' ); }		// Position the tooltip var tooltipPos = { top: $win.scrollTop, left: $win.scrollLeft };		if ( anchorRect.top + tooltipOffset.top < tooltipRect.height ) { $tooltip.addClass( 'ref-tooltip-flipped' ); tooltipPos.top += anchorRect.bottom; } else { tooltipPos.top += anchorRect.top - tooltipRect.height; }		tooltipPos.left += anchorRect.left + anchorRect.width / 2; // Stop it going off the side of the page var contentPadding = parseFloat( $body.css( 'padding-right' ) ); var contentBoundary = $body[0].getBoundingClientRect.right - contentPadding / 2; var overlap = anchorRect.left + tooltipOffset.left + tooltipRect.width - contentBoundary; if ( overlap > 0 ) { $tooltipText.css(				'margin-left',				Math.max( tooltipOffset.left - overlap, -tooltipInnerWidth )			); }		$tooltip.css( tooltipPos ); };	var bindRefHandlers = function { $content.on( {			'mouseenter.refTooltip': function {				var $this = $( this );				clearTimeout( hideTimer );				// Current tooltip, do nothing				if ( $tooltip.length && (					$this.is( $tooltip.data( 'anchor' ) ) || $.contains( $tooltip[0], this )				) ) {					return;				}				// Create the tooltip if timeout succeeds				showTimer = setTimeout( function { createTooltip( $this, getRefText( $this ), true ); }, 200 );			},			'mouseleave.refTooltip': function {				clearTimeout( showTimer );				// Remove the tooltip if timeout succeeds				hideTimer = setTimeout( function { removeTooltip; }, 300 );			}		}, '.reference' ); };	// When anywhere but the tooltip or anchor is clicked, remove it immediately $( window ).on( 'click.refTooltip', function( e ) {		if ( $tooltip.length && !$tooltip.data( 'fresh' ) && !$.contains( $tooltip[0], e.target ) ) {			clearTimeout( showTimer );			removeTooltip;		}	} ); $( '#References' ).before(		$( ' ' ).addClass( 'ref-tooltip-options-button' )			.attr( 'title', i18n.optionsButtonTitle )	); $( 'body' ).on( 'click.refTooltip', '.ref-tooltip-options-button', function( e ) {		// Just close the tooltip if it is already open		if ( $tooltip.length && $tooltip.data( 'anchor' ).is( e.target ) ) {			return;		}		// Disable ref handlers while options are open		$content.off( 'mouseenter.refTooltip mouseleave.refTooltip' );		var $anchor = $( this );		var restoreTooltip;		// Replace current tooltip if clicking the options button within a tooltip		if ( $tooltip.length && $.contains( $tooltip[0], this ) ) {			$anchor = $tooltip.data( 'anchor' );			restoreTooltip = true;			removeTooltip; 		}		var $optionsText = $( ' ' ).addClass( 'ref-tooltip-options' ).append( $( ' ' ).addClass( 'mw-ui-checkbox' ).append(				$( ' ' ).attr( { type: 'checkbox', id: 'ref-tooltip-options-enabled', checked: options.enabled } ),				$( ' ' ).attr( 'for', 'ref-tooltip-options-enabled' ).text( i18n.enableLabel )			), $( ' ' ).addClass( 'ref-tooltip-actions' ).append(				$( ' ' ).addClass( 'mw-ui-button mw-ui-quiet' ).text( i18n.cancelButton )					.on( 'click.refTooltip', function { removeTooltip; if ( options.enabled && restoreTooltip ) { createTooltip( $anchor, getRefText( $anchor ), true ); }					} ),				$( ' ' ).addClass( 'mw-ui-button mw-ui-progressive' ).text( i18n.doneButton )					.on( 'click.refTooltip', function { options.enabled = $( '#ref-tooltip-options-enabled' ).prop( 'checked' ); var success = true; try { localStorage.refTooltip = JSON.stringify( options ); } catch ( e ) { mw.notify( i18n.saveFailedStorageFull, { title: i18n.saveFailedTitle } ); success = false; }						if ( success ) { removeTooltip; if ( options.enabled && restoreTooltip ) { createTooltip( $anchor, getRefText( $anchor ), true ); }						}					} )			)		);		createTooltip( $anchor, $optionsText, false, function { if ( options.enabled ) { bindRefHandlers; }		} );	} );	// Finally, enable tooltips if ( options.enabled ) { bindRefHandlers; } } );

// Add width and height to Element.getBoundingClientRect in IE8 // TODO: Remove on 1.27 if ( window.TextRectangle && !TextRectangle.prototype.width ) { Object.defineProperty( TextRectangle.prototype, 'width', {		get: function { return this.right - this.left; }	} ); Object.defineProperty( TextRectangle.prototype, 'height', {		get: function { return this.bottom - this.top; }	} ); }