PageSourceSearch

https://www.steamvr.com/en/public/shared/javascript/tooltip.js?v=LZHsOVauqTrm&_cdn=akamai

js steamvr.com collected 2026-10-02 11:13:45 UTC 17,318 bytes, 562 lines download raw bytes

1/* Requires jQuery
2 *
3 * This plugin will create div.jsTooltip elements (or configure your own!) in body for every tooltip on the page. Some
4 * basic CSS is applied automagically, but you'll want to style it on your own from there. This code will be applied to
5 * every element in your .v_tooltip() selector, so giving it a common selector like '.tooltip' is ideal.
6 *
7 * Options:
8 * - location: Where the tooltip should spawn in relation to it's parent
9 * - offsetN: How many pixels to add
10 * - trackMouse: Should we track the mouse cursor instead of the parent?
11 * - suppressOnClick: Should we hide if a user clicks the target?
12 * - suppressWhileToggled: Should we ignore events if the target has the 'toggled' class?
13 * - tooltipClass: css class to apply to tooltip elements
14 * - fadeSpeed:	Time (in milliseconds) to spend fading in/out. Set to 0 to disable.
15 * - allowHover: Should we keep the tooltip open if we mouse directly on to the tooltip? (Your tooltip will need to spawn inside it's owner's box for this to work)
16 * - tooltipParent: More generally useless properties for supernav: Lets us specify which element to parent the tooltips to. YOU PROBABLY DON'T NEED THIS.
17 * - correctForScreenSize: Adjust tooltip position to ensure it doesn't render outside of the viewport
18 * - sizeCorrectionXPadding: How far we should keep the tooltip from the window edge
19 * - useClickEvent: Should we use the mouse click event instead of hover?
20 * - inheritParentMinWidth: Should we set min-width based on our parent's width?
21 * - parentActiveCSSClass: What CSS class should we add to our parent while we're visible?
22 * - childActiveCSSClass: What CSS class should we add to the tooltip when active (Mostly useful for triggering CSS transitions
23 * - funcName: Global name of a function to call on hover (It searches the global window object; not safe inside "use strict"
24 * - func: Actual JS code to run.
25 * - responsiveMode: enable when in responsive mode/mobile/tablet, requires tooltip elements to be decorated with data-tooltip-responsive to enable in responsive mode
26 */
27/* <script> */
28(function( $ ){
29	let g_nextTooltipId = 0;
30	var methods = {
31
32		init : function( options ) {
33
34			var settings = $.extend( {
35				'location'			: 'top',
36				'offsetX'			: 0,
37				'offsetY'			: -10,
38				'trackMouse'		: false,
39				'trackMouseCentered': true,
40				'suppressOnClick'	: true,
41				'suppressWhileToggled': true,
42				'tooltipClass'		: 'jsTooltip',
43				'fadeSpeed'			: 150,
44				'allowHover'		: true,
45				'tooltipParent'		: 'body',
46				'correctForScreenSize': true,
47				'sizeCorrectionXPadding': 15,
48				'sizeCorrectionYPadding': 10,
49				'useClickEvent'		: false,
50				'useContextMenuEvent'	: false,
51				'useMouseEnterEvent'	: true,
52				'useMouseLeaveEvent'	: true,
53				'preventDefault'	: true,
54				'stopPropagation'	: false,
55				'inheritParentMinWidth'	: false,
56				'parentActiveCSSClass'	: false,
57				'dataName'			: 'tooltipContent',
58				'funcName'			: 'tooltipFunc',
59				'func'				: false,
60				'disableOnTouchDevice'	: false,
61				'childActiveCSSClass' : false,
62				'destroyWhenDone': true,
63				'createOnLoad': false,
64				'replaceExisting': true, // should we stomp on existing tooltips?
65				'defaultType': 'html', // default tooltip type if not specified.
66				'responsiveMode': false,
67			}, options);
68
69
70
71
72			return this.each(function(){
73
74				var $target = $(this);
75
76				if ( settings.responsiveMode && !this.hasAttribute('data-tooltip-responsive' ) )
77					return;
78
79				if( $target.data('tooltip.settings') && !settings.replaceExisting)
80					return;
81
82				if( settings.useClickEvent )
83				{
84					$target.bind('click.tooltip', methods.show);
85				}
86				else
87				{
88					$target.bind('vgp_onfocus.tooltip', methods.show );
89					$target.bind('vgp_onblur.tooltip', methods.hide );
90				}
91
92				if( settings.useContextMenuEvent )
93				{
94					$target.bind('contextmenu.tooltip', methods.show);
95				}
96				if( settings.useMouseEnterEvent )
97				{
98					$target.bind('mouseenter.tooltip', methods.show);
99				}
100				if( settings.useMouseLeaveEvent )
101				{
102					$target.bind('mouseleave.tooltip', methods.hide);
103				}
104
105				if ( settings.disableOnTouchDevice )
106				{
107					// if we get a touch event, disable tooltip events on this object.  This fires before mouse events, so we
108					//	can prevent the hover from showing.
109					//	(we could preventDefault(), but that will prevent the actual click event that's coming too)
110					$target.bind('touchstart.tooltip', function(e) { $target.data('inTouchEvent', true); });
111					$target.bind('mouseup.tooltip', function(e) { $target.data('inTouchEvent', false); });
112				}
113
114				$target.data('tooltip.settings', settings);
115				$target.data('tooltip.id', g_nextTooltipId++);
116
117				if( settings.createOnLoad )
118					methods.gettooltip( this, settings );
119			});
120
121		},
122		destroy : function() {
123
124			return this.each(function(){
125				$(window).unbind('.tooltip');
126			})
127
128		},
129		gettooltip : function( element, settings ) {
130			var $element= $(element);
131			var toolDiv = $element.data("tooltip.element");
132			var toolId = $element.data("tooltip.id");
133			if ( !toolDiv )
134			{
135				toolDiv = $('<div />');
136
137				if( settings.suppressOnClick )
138				{
139					toolDiv.bind('click.tooltip', jQuery.proxy(methods.hide, element));
140				}
141				toolDiv.hide();
142				toolDiv.addClass(settings.tooltipClass);
143				if ( $element.data( 'tooltip-class' ) )
144				{
145					toolDiv.addClass( $element.data( 'tooltip-class' ) );
146				}
147				toolDiv.css({
148					position: 'absolute',
149					'z-index': 1500
150				});
151				toolDiv.attr('id', 'tooltip-' + toolId);
152				var type = $element.data('tooltip-type');
153				if( !type )
154					type = settings.defaultType;
155
156				var content = '';
157				if ( settings.dataAttr )
158					content = $element.attr( settings.dataAttr );
159				else if ( settings.dataName )
160					content = $element.data(settings.dataName);
161
162				if( type == 'text')
163				{
164					toolDiv.text( content );
165				}
166				else if ( type == 'selector' )
167				{
168					var toolElement = $( content, $element.parent() );
169					toolDiv.data( 'originalParent', toolElement.parent() );
170					toolDiv.append( toolElement.show() );
171					toolDiv.data( 'preserveContent', true );
172				}
173				else
174				{
175					toolDiv.html( content );
176				}
177
178				if ( typeof( g_TextFilter ) !== 'undefined' && g_TextFilter != null )
179				{
180					g_TextFilter.FilterElement( toolDiv );
181				}
182
183				$( settings.tooltipParent || $element.parent() ).append(toolDiv);
184				$element.data("tooltip.element", toolDiv);
185
186			}
187
188			var funcName = settings.funcName && $(element).data( settings.funcName );
189			if( funcName )
190			{
191				if( type == 'text')
192					toolDiv.text( window[funcName](element) );
193				else
194					toolDiv.html( window[funcName](element) );
195			}
196
197			if( settings.func )
198				settings.func.bind(toolDiv)(element);
199
200			return toolDiv;
201		},
202		updateposition : function() {
203		    var newPosition = {};
204		    var settings = $(this).data('tooltip.settings');
205		    var toolDiv = methods.gettooltip( this, settings );
206
207		    var parentPosition = $(this).offset();
208		    if( settings.tooltipParent != 'body' )
209		        parentPosition = $(this).position();
210
211		    const bRTL = toolDiv[0].matches( ':dir(rtl)' );
212		    switch( settings.location )
213		    {
214				case 'top':
215					if ( bRTL )
216					{
217						newPosition = {
218							left: parentPosition.left + $(this).outerWidth() - toolDiv.outerWidth() + settings.offsetX,
219							top: parentPosition.top - toolDiv.outerHeight() + settings.offsetY
220						};
221					}
222					else
223					{
224						newPosition = {
225							left: parentPosition.left + settings.offsetX,
226							top: parentPosition.top - toolDiv.outerHeight() + settings.offsetY
227						};
228					}
229					break;
230
231				case 'bottom':
232					if ( bRTL )
233					{
234						newPosition = {
235							left: parentPosition.left + $(this).outerWidth() - toolDiv.outerWidth() + settings.offsetX,
236							top: parentPosition.top + $(this).outerHeight() + settings.offsetY
237						};
238					}
239					else
240					{
241						newPosition = {
242							left: parentPosition.left + settings.offsetX,
243							top: parentPosition.top + $(this).outerHeight() + settings.offsetY
244						};
245					}
246					break;
247
248				case 'bottom left':
249					if ( bRTL )
250					{
251						newPosition = {
252							left: parentPosition.left + settings.offsetX,
253							top: parentPosition.top + $(this).outerHeight() + settings.offsetY
254						};
255					}
256					else
257					{
258						newPosition = {
259							left: parentPosition.left + settings.offsetX - toolDiv.outerWidth() + $J(this).outerWidth(),
260							top: parentPosition.top + $(this).outerHeight() + settings.offsetY
261						};
262					}
263					break;
264
265				default:
266					console.log("Invalid location passed to v_tooltip: %s", settings.location);
267		    }
268		    // Correct for window size
269		    if( settings.correctForScreenSize )
270		    {
271		        var rightEdge = newPosition.left + toolDiv.width();
272		        var windowRightEdge = $(window).width() - settings.sizeCorrectionXPadding + $(window).scrollLeft();
273		        var windowLeftEdge =  $(window).scrollLeft() + settings.sizeCorrectionXPadding;
274
275		        if( rightEdge > windowRightEdge )
276		            newPosition.left = windowRightEdge - toolDiv.width() - settings.sizeCorrectionXPadding;
277
278		        if( newPosition.left < windowLeftEdge )
279		            newPosition.left = windowLeftEdge;
280
281				if ( newPosition.top < 0 )
282					newPosition.top = parentPosition.top + $(this).height() + settings.sizeCorrectionYPadding - settings.offsetY;
283		    }
284
285		    toolDiv.css(newPosition);
286		},
287		reposition : function(event) {
288			var newPosition = {};
289			var settings = $(this).data('tooltip.settings');
290			var toolDiv = methods.gettooltip( this, settings );
291
292			var parentPosition = $(this).offset();
293			if( settings.tooltipParent != 'body' )
294				parentPosition = $(this).position();
295
296			if( settings.trackMouse )
297			{
298				if ( settings.trackMouseCentered )
299					newPosition.left = event.pageX - toolDiv.outerWidth() / 2;
300				else
301					newPosition.left = event.pageX + settings.offsetY;
302
303				if ( settings.location == 'top' )
304					newPosition.top = event.pageY - toolDiv.outerHeight() + settings.offsetY;
305				else
306					newPosition.top = event.pageY + settings.offsetY;
307
308			} else {
309				const bRTL = toolDiv[0].matches( ':dir(rtl)' );
310				switch( settings.location )
311				{
312					case 'top':
313						if ( bRTL )
314						{
315							newPosition = {
316								left: parentPosition.left + $(this).outerWidth() - toolDiv.outerWidth() + settings.offsetX,
317								top: parentPosition.top - toolDiv.outerHeight() + settings.offsetY
318							};
319						}
320						else
321						{
322							newPosition = {
323								left: parentPosition.left + settings.offsetX,
324								top: parentPosition.top - toolDiv.outerHeight() + settings.offsetY
325							};
326						}
327						break;
328
329					case 'bottom':
330						if ( bRTL )
331						{
332							newPosition = {
333								left: parentPosition.left + $(this).outerWidth() - toolDiv.outerWidth() + settings.offsetX,
334								top: parentPosition.top + $(this).outerHeight() + settings.offsetY
335							};
336						}
337						else
338						{
339							newPosition = {
340								left: parentPosition.left + settings.offsetX,
341								top: parentPosition.top + $(this).outerHeight() + settings.offsetY
342							};
343						}
344						break;
345
346					case 'bottom left':
347						if ( bRTL )
348						{
349							newPosition = {
350								left: parentPosition.left + settings.offsetX,
351								top: parentPosition.top + $(this).outerHeight() + settings.offsetY
352							};
353						}
354						else
355						{
356							newPosition = {
357								left: parentPosition.left + settings.offsetX - toolDiv.outerWidth() + $J(this).outerWidth(),
358								top: parentPosition.top + $(this).outerHeight() + settings.offsetY
359							};
360						}
361						break;
362
363					case 'mouse':
364						newPosition = {
365							left: event.pageX + settings.offsetX,
366							top: event.pageY + settings.offsetY
367						}
368
369					default:
370						console.log("Invalid location passed to v_tooltip: %s", settings.location);
371				}
372			}
373			// Correct for window size
374			if( settings.correctForScreenSize )
375			{
376				var rightEdge = newPosition.left + toolDiv.width();
377				var windowRightEdge = $(window).width() - settings.sizeCorrectionXPadding + $(window).scrollLeft();
378				var windowLeftEdge =  $(window).scrollLeft() + settings.sizeCorrectionXPadding;
379
380				if( rightEdge > windowRightEdge )
381					newPosition.left = windowRightEdge - toolDiv.width() - settings.sizeCorrectionXPadding;
382
383				if( newPosition.left < windowLeftEdge )
384					newPosition.left = windowLeftEdge;
385
386				if ( newPosition.top < 0 )
387					newPosition.top = parentPosition.top + $(this).height() + settings.sizeCorrectionYPadding - settings.offsetY;
388			}
389
390			toolDiv.css(newPosition);
391		},
392		show : function(event) {
393			var settings = $(this).data('tooltip.settings') || {};
394
395			if ( settings.disableOnTouchDevice && $(this ).data('inTouchEvent') )
396			{
397				return;
398			}
399
400			var toolDiv = methods.gettooltip( this, settings );
401
402			if( toolDiv.is(':empty') )
403				return;
404
405			if( event.type == "click" && event.currentTarget != this )
406				return;
407
408			if( settings.suppressWhileToggled && $(this).hasClass('toggled') )
409				return false;
410
411			var tipElem = this;
412			toolDiv.find( 'img' ).on( 'load', function() { jQuery.proxy(methods.updateposition, tipElem)() } );
413
414			if( settings.preventDefault )
415				event.preventDefault();
416
417			if( settings.stopPropagation )
418				event.stopPropagation();
419
420			if( settings.parentActiveCSSClass )
421				$(this).addClass(settings.parentActiveCSSClass);
422
423			if( settings.inheritParentMinWidth )
424			{
425				var parentWidth = $(this).outerWidth();
426				var localPadding = toolDiv.outerWidth() - toolDiv.width();
427				toolDiv.css({'min-width': + (parentWidth - localPadding) + "px"});
428			}
429
430			if( settings.fadeSpeed > 0 )
431			{
432				toolDiv.stop(true, true);
433				toolDiv.fadeTo( settings.fadeSpeed, 1 );
434			}
435			else
436				toolDiv.show();
437
438			if( settings.allowHover )
439			{
440				if( settings.useClickEvent )
441					toolDiv.bind('click.tooltip', jQuery.proxy(methods.show, this));
442				else if( settings.useContextMenuEvent )
443					toolDiv.bind('contextmenu.tooltip', jQuery.proxy(methods.show, this));
444				else
445					toolDiv.bind('mouseenter.tooltip', jQuery.proxy(methods.show, this));
446				toolDiv.bind('mouseleave.tooltip', jQuery.proxy(methods.hide, this));
447			}
448
449			if( settings.trackMouse )
450				$(this).bind('mousemove.tooltip', methods.reposition);
451			else
452				jQuery.proxy(methods.reposition, this)(event);
453
454			if( settings.childActiveCSSClass )
455			{
456				toolDiv.css('opacity');
457				toolDiv.addClass(settings.childActiveCSSClass);
458
459			}
460
461			toolDiv.css('pointer-events','auto');
462
463			$(this).trigger('v_tooltip_shown', [ toolDiv ] );
464
465			$(this).attr('aria-describedby', 'tooltip-' + $(this).data("tooltip.id"));
466
467		},
468		hide : function(event) {
469			var toolDiv = $(this).data('tooltip.element');
470			var settings = $(this).data('tooltip.settings') || {};
471
472			// the element may not have been created yet - in which case there is nothing to hide
473			if ( !toolDiv || !toolDiv.length )
474				return;
475
476
477			if( event && event.type != 'click' )
478			{
479				// Moving between the click target and it's children
480				if( event.relatedTarget && ( this == event.relatedTarget || this.contains(event.relatedTarget) ) )
481					return;
482				// Moving to the tooltip or one of it's children
483				else if( event.relatedTarget && ( event.relatedTarget == toolDiv[0] || toolDiv[0].contains(event.relatedTarget ) ) )
484					return;
485
486
487
488			}
489
490			if( settings.trackMouse )
491				$(this).unbind('mousemove.tooltip');
492
493			toolDiv.unbind('mouseenter.tooltip');
494			toolDiv.unbind('mouseleave.tooltip');
495
496			if( settings.parentActiveCSSClass )
497				$(this).removeClass(settings.parentActiveCSSClass);
498
499
500			if( settings.fadeSpeed > 0 && toolDiv.is(':visible') )
501			{
502				toolDiv.stop();
503				toolDiv.fadeTo( settings.fadeSpeed, 0, function() { if( settings.destroyWhenDone ) methods.destroytooltip( toolDiv ) } );
504			} else if( settings.childActiveCSSClass )
505			{
506				toolDiv.css('opacity');
507				toolDiv.removeClass(settings.childActiveCSSClass);
508				// Note: This callback isn't reliable, so we may end up junking up the dom with a few spare copies
509				// Not a huge deal in most cases, but keep in mind if you're using this on pages which may exist for
510				// long periods of time without reloading.
511				if( settings.destroyWhenDone )
512				{
513					toolDiv.on ( "transitionend", function () {
514						methods.destroytooltip ( toolDiv )
515					} );
516
517					// Transitionend may not fire under various conditions, so JUST IN CASE....
518					setTimeout( function(){ methods.destroytooltip ( toolDiv ); }, 1000);
519				}
520			}
521			else
522			{
523				if( settings.destroyWhenDone )
524					methods.destroytooltip( toolDiv );
525			}
526			if( settings.destroyWhenDone )
527				$(this).removeData('tooltip.element');
528			else
529				toolDiv.css('pointer-events','none');
530
531			$(this).trigger( 'v_tooltip_hidden' );
532		},
533		destroytooltip: function( toolDiv )
534		{
535			if ( toolDiv )
536			{
537				if ( $(toolDiv ).data('preserveContent') )
538				{
539					$( toolDiv.data( 'originalParent') ).append( $(toolDiv ).children().hide());
540				}
541
542				$(toolDiv).remove();
543			}
544		}
545	};
546
547	$.fn.v_tooltip = function( method ) {
548
549		if ( methods[method] ) {
550			return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
551		} else if ( typeof method === 'object' || ! method ) {
552			return methods.init.apply( this, arguments );
553		} else {
554			$.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
555		}
556
557	};
558
559
560})( jQuery );
561
562

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.