balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

tooltip.js (14282B)


      1 /*!
      2  * jQuery UI Tooltip 1.12.1
      3  * http://jqueryui.com
      4  *
      5  * Copyright jQuery Foundation and other contributors
      6  * Released under the MIT license.
      7  * http://jquery.org/license
      8  */
      9 
     10 //>>label: Tooltip
     11 //>>group: Widgets
     12 //>>description: Shows additional information for any element on hover or focus.
     13 //>>docs: http://api.jqueryui.com/tooltip/
     14 //>>demos: http://jqueryui.com/tooltip/
     15 //>>css.structure: ../../themes/base/core.css
     16 //>>css.structure: ../../themes/base/tooltip.css
     17 //>>css.theme: ../../themes/base/theme.css
     18 
     19 ( function( factory ) {
     20 	if ( typeof define === "function" && define.amd ) {
     21 
     22 		// AMD. Register as an anonymous module.
     23 		define( [
     24 			"jquery",
     25 			"./core"
     26 		], factory );
     27 	} else {
     28 
     29 		// Browser globals
     30 		factory( jQuery );
     31 	}
     32 }( function( $ ) {
     33 
     34 $.widget( "ui.tooltip", {
     35 	version: "1.12.1",
     36 	options: {
     37 		classes: {
     38 			"ui-tooltip": "ui-corner-all ui-widget-shadow"
     39 		},
     40 		content: function() {
     41 
     42 			// support: IE<9, Opera in jQuery <1.7
     43 			// .text() can't accept undefined, so coerce to a string
     44 			var title = $( this ).attr( "title" ) || "";
     45 
     46 			// Escape title, since we're going from an attribute to raw HTML
     47 			return $( "<a>" ).text( title ).html();
     48 		},
     49 		hide: true,
     50 
     51 		// Disabled elements have inconsistent behavior across browsers (#8661)
     52 		items: "[title]:not([disabled])",
     53 		position: {
     54 			my: "left top+15",
     55 			at: "left bottom",
     56 			collision: "flipfit flip"
     57 		},
     58 		show: true,
     59 		track: false,
     60 
     61 		// Callbacks
     62 		close: null,
     63 		open: null
     64 	},
     65 
     66 	_addDescribedBy: function( elem, id ) {
     67 		var describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ );
     68 		describedby.push( id );
     69 		elem
     70 			.data( "ui-tooltip-id", id )
     71 			.attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
     72 	},
     73 
     74 	_removeDescribedBy: function( elem ) {
     75 		var id = elem.data( "ui-tooltip-id" ),
     76 			describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ ),
     77 			index = $.inArray( id, describedby );
     78 
     79 		if ( index !== -1 ) {
     80 			describedby.splice( index, 1 );
     81 		}
     82 
     83 		elem.removeData( "ui-tooltip-id" );
     84 		describedby = $.trim( describedby.join( " " ) );
     85 		if ( describedby ) {
     86 			elem.attr( "aria-describedby", describedby );
     87 		} else {
     88 			elem.removeAttr( "aria-describedby" );
     89 		}
     90 	},
     91 
     92 	_create: function() {
     93 		this._on( {
     94 			mouseover: "open",
     95 			focusin: "open"
     96 		} );
     97 
     98 		// IDs of generated tooltips, needed for destroy
     99 		this.tooltips = {};
    100 
    101 		// IDs of parent tooltips where we removed the title attribute
    102 		this.parents = {};
    103 
    104 		// Append the aria-live region so tooltips announce correctly
    105 		this.liveRegion = $( "<div>" )
    106 			.attr( {
    107 				role: "log",
    108 				"aria-live": "assertive",
    109 				"aria-relevant": "additions"
    110 			} )
    111 			.appendTo( this.document[ 0 ].body );
    112 		this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
    113 
    114 		this.disabledTitles = $( [] );
    115 	},
    116 
    117 	_setOption: function( key, value ) {
    118 		var that = this;
    119 
    120 		this._super( key, value );
    121 
    122 		if ( key === "content" ) {
    123 			$.each( this.tooltips, function( id, tooltipData ) {
    124 				that._updateContent( tooltipData.element );
    125 			} );
    126 		}
    127 	},
    128 
    129 	_setOptionDisabled: function( value ) {
    130 		this[ value ? "_disable" : "_enable" ]();
    131 	},
    132 
    133 	_disable: function() {
    134 		var that = this;
    135 
    136 		// Close open tooltips
    137 		$.each( this.tooltips, function( id, tooltipData ) {
    138 			var event = $.Event( "blur" );
    139 			event.target = event.currentTarget = tooltipData.element[ 0 ];
    140 			that.close( event, true );
    141 		} );
    142 
    143 		// Remove title attributes to prevent native tooltips
    144 		this.disabledTitles = this.disabledTitles.add(
    145 			this.element.find( this.options.items ).addBack()
    146 				.filter( function() {
    147 					var element = $( this );
    148 					if ( element.is( "[title]" ) ) {
    149 						return element
    150 							.data( "ui-tooltip-title", element.attr( "title" ) )
    151 							.removeAttr( "title" );
    152 					}
    153 				} )
    154 		);
    155 	},
    156 
    157 	_enable: function() {
    158 
    159 		// restore title attributes
    160 		this.disabledTitles.each( function() {
    161 			var element = $( this );
    162 			if ( element.data( "ui-tooltip-title" ) ) {
    163 				element.attr( "title", element.data( "ui-tooltip-title" ) );
    164 			}
    165 		} );
    166 		this.disabledTitles = $( [] );
    167 	},
    168 
    169 	open: function( event ) {
    170 		var that = this,
    171 			target = $( event ? event.target : this.element )
    172 
    173 				// we need closest here due to mouseover bubbling,
    174 				// but always pointing at the same event target
    175 				.closest( this.options.items );
    176 
    177 		// No element to show a tooltip for or the tooltip is already open
    178 		if ( !target.length || target.data( "ui-tooltip-id" ) ) {
    179 			return;
    180 		}
    181 
    182 		if ( target.attr( "title" ) ) {
    183 			target.data( "ui-tooltip-title", target.attr( "title" ) );
    184 		}
    185 
    186 		target.data( "ui-tooltip-open", true );
    187 
    188 		// Kill parent tooltips, custom or native, for hover
    189 		if ( event && event.type === "mouseover" ) {
    190 			target.parents().each( function() {
    191 				var parent = $( this ),
    192 					blurEvent;
    193 				if ( parent.data( "ui-tooltip-open" ) ) {
    194 					blurEvent = $.Event( "blur" );
    195 					blurEvent.target = blurEvent.currentTarget = this;
    196 					that.close( blurEvent, true );
    197 				}
    198 				if ( parent.attr( "title" ) ) {
    199 					parent.uniqueId();
    200 					that.parents[ this.id ] = {
    201 						element: this,
    202 						title: parent.attr( "title" )
    203 					};
    204 					parent.attr( "title", "" );
    205 				}
    206 			} );
    207 		}
    208 
    209 		this._registerCloseHandlers( event, target );
    210 		this._updateContent( target, event );
    211 	},
    212 
    213 	_updateContent: function( target, event ) {
    214 		var content,
    215 			contentOption = this.options.content,
    216 			that = this,
    217 			eventType = event ? event.type : null;
    218 
    219 		if ( typeof contentOption === "string" || contentOption.nodeType ||
    220 				contentOption.jquery ) {
    221 			return this._open( event, target, contentOption );
    222 		}
    223 
    224 		content = contentOption.call( target[ 0 ], function( response ) {
    225 
    226 			// IE may instantly serve a cached response for ajax requests
    227 			// delay this call to _open so the other call to _open runs first
    228 			that._delay( function() {
    229 
    230 				// Ignore async response if tooltip was closed already
    231 				if ( !target.data( "ui-tooltip-open" ) ) {
    232 					return;
    233 				}
    234 
    235 				// JQuery creates a special event for focusin when it doesn't
    236 				// exist natively. To improve performance, the native event
    237 				// object is reused and the type is changed. Therefore, we can't
    238 				// rely on the type being correct after the event finished
    239 				// bubbling, so we set it back to the previous value. (#8740)
    240 				if ( event ) {
    241 					event.type = eventType;
    242 				}
    243 				this._open( event, target, response );
    244 			} );
    245 		} );
    246 		if ( content ) {
    247 			this._open( event, target, content );
    248 		}
    249 	},
    250 
    251 	_open: function( event, target, content ) {
    252 		var tooltipData, tooltip, delayedShow, a11yContent,
    253 			positionOption = $.extend( {}, this.options.position );
    254 
    255 		if ( !content ) {
    256 			return;
    257 		}
    258 
    259 		// Content can be updated multiple times. If the tooltip already
    260 		// exists, then just update the content and bail.
    261 		tooltipData = this._find( target );
    262 		if ( tooltipData ) {
    263 			tooltipData.tooltip.find( ".ui-tooltip-content" ).html( content );
    264 			return;
    265 		}
    266 
    267 		// If we have a title, clear it to prevent the native tooltip
    268 		// we have to check first to avoid defining a title if none exists
    269 		// (we don't want to cause an element to start matching [title])
    270 		//
    271 		// We use removeAttr only for key events, to allow IE to export the correct
    272 		// accessible attributes. For mouse events, set to empty string to avoid
    273 		// native tooltip showing up (happens only when removing inside mouseover).
    274 		if ( target.is( "[title]" ) ) {
    275 			if ( event && event.type === "mouseover" ) {
    276 				target.attr( "title", "" );
    277 			} else {
    278 				target.removeAttr( "title" );
    279 			}
    280 		}
    281 
    282 		tooltipData = this._tooltip( target );
    283 		tooltip = tooltipData.tooltip;
    284 		this._addDescribedBy( target, tooltip.attr( "id" ) );
    285 		tooltip.find( ".ui-tooltip-content" ).html( content );
    286 
    287 		// Support: Voiceover on OS X, JAWS on IE <= 9
    288 		// JAWS announces deletions even when aria-relevant="additions"
    289 		// Voiceover will sometimes re-read the entire log region's contents from the beginning
    290 		this.liveRegion.children().hide();
    291 		a11yContent = $( "<div>" ).html( tooltip.find( ".ui-tooltip-content" ).html() );
    292 		a11yContent.removeAttr( "name" ).find( "[name]" ).removeAttr( "name" );
    293 		a11yContent.removeAttr( "id" ).find( "[id]" ).removeAttr( "id" );
    294 		a11yContent.appendTo( this.liveRegion );
    295 
    296 		function position( event ) {
    297 			positionOption.of = event;
    298 			if ( tooltip.is( ":hidden" ) ) {
    299 				return;
    300 			}
    301 			tooltip.position( positionOption );
    302 		}
    303 		if ( this.options.track && event && /^mouse/.test( event.type ) ) {
    304 			this._on( this.document, {
    305 				mousemove: position
    306 			} );
    307 
    308 			// trigger once to override element-relative positioning
    309 			position( event );
    310 		} else {
    311 			tooltip.position( $.extend( {
    312 				of: target
    313 			}, this.options.position ) );
    314 		}
    315 
    316 		tooltip.hide();
    317 
    318 		this._show( tooltip, this.options.show );
    319 
    320 		// Handle tracking tooltips that are shown with a delay (#8644). As soon
    321 		// as the tooltip is visible, position the tooltip using the most recent
    322 		// event.
    323 		// Adds the check to add the timers only when both delay and track options are set (#14682)
    324 		if ( this.options.track && this.options.show && this.options.show.delay ) {
    325 			delayedShow = this.delayedShow = setInterval( function() {
    326 				if ( tooltip.is( ":visible" ) ) {
    327 					position( positionOption.of );
    328 					clearInterval( delayedShow );
    329 				}
    330 			}, $.fx.interval );
    331 		}
    332 
    333 		this._trigger( "open", event, { tooltip: tooltip } );
    334 	},
    335 
    336 	_registerCloseHandlers: function( event, target ) {
    337 		var events = {
    338 			keyup: function( event ) {
    339 				if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
    340 					var fakeEvent = $.Event( event );
    341 					fakeEvent.currentTarget = target[ 0 ];
    342 					this.close( fakeEvent, true );
    343 				}
    344 			}
    345 		};
    346 
    347 		// Only bind remove handler for delegated targets. Non-delegated
    348 		// tooltips will handle this in destroy.
    349 		if ( target[ 0 ] !== this.element[ 0 ] ) {
    350 			events.remove = function() {
    351 				this._removeTooltip( this._find( target ).tooltip );
    352 			};
    353 		}
    354 
    355 		if ( !event || event.type === "mouseover" ) {
    356 			events.mouseleave = "close";
    357 		}
    358 		if ( !event || event.type === "focusin" ) {
    359 			events.focusout = "close";
    360 		}
    361 		this._on( true, target, events );
    362 	},
    363 
    364 	close: function( event ) {
    365 		var tooltip,
    366 			that = this,
    367 			target = $( event ? event.currentTarget : this.element ),
    368 			tooltipData = this._find( target );
    369 
    370 		// The tooltip may already be closed
    371 		if ( !tooltipData ) {
    372 
    373 			// We set ui-tooltip-open immediately upon open (in open()), but only set the
    374 			// additional data once there's actually content to show (in _open()). So even if the
    375 			// tooltip doesn't have full data, we always remove ui-tooltip-open in case we're in
    376 			// the period between open() and _open().
    377 			target.removeData( "ui-tooltip-open" );
    378 			return;
    379 		}
    380 
    381 		tooltip = tooltipData.tooltip;
    382 
    383 		// Disabling closes the tooltip, so we need to track when we're closing
    384 		// to avoid an infinite loop in case the tooltip becomes disabled on close
    385 		if ( tooltipData.closing ) {
    386 			return;
    387 		}
    388 
    389 		// Clear the interval for delayed tracking tooltips
    390 		clearInterval( this.delayedShow );
    391 
    392 		// Only set title if we had one before (see comment in _open())
    393 		// If the title attribute has changed since open(), don't restore
    394 		if ( target.data( "ui-tooltip-title" ) && !target.attr( "title" ) ) {
    395 			target.attr( "title", target.data( "ui-tooltip-title" ) );
    396 		}
    397 
    398 		this._removeDescribedBy( target );
    399 
    400 		tooltipData.hiding = true;
    401 		tooltip.stop( true );
    402 		this._hide( tooltip, this.options.hide, function() {
    403 			that._removeTooltip( $( this ) );
    404 		} );
    405 
    406 		target.removeData( "ui-tooltip-open" );
    407 		this._off( target, "mouseleave focusout keyup" );
    408 
    409 		// Remove 'remove' binding only on delegated targets
    410 		if ( target[ 0 ] !== this.element[ 0 ] ) {
    411 			this._off( target, "remove" );
    412 		}
    413 		this._off( this.document, "mousemove" );
    414 
    415 		if ( event && event.type === "mouseleave" ) {
    416 			$.each( this.parents, function( id, parent ) {
    417 				$( parent.element ).attr( "title", parent.title );
    418 				delete that.parents[ id ];
    419 			} );
    420 		}
    421 
    422 		tooltipData.closing = true;
    423 		this._trigger( "close", event, { tooltip: tooltip } );
    424 		if ( !tooltipData.hiding ) {
    425 			tooltipData.closing = false;
    426 		}
    427 	},
    428 
    429 	_tooltip: function( element ) {
    430 		var tooltip = $( "<div>" ).attr( "role", "tooltip" ),
    431 			content = $( "<div>" ).appendTo( tooltip ),
    432 			id = tooltip.uniqueId().attr( "id" );
    433 
    434 		this._addClass( content, "ui-tooltip-content" );
    435 		this._addClass( tooltip, "ui-tooltip", "ui-widget ui-widget-content" );
    436 
    437 		tooltip.appendTo( this._appendTo( element ) );
    438 
    439 		return this.tooltips[ id ] = {
    440 			element: element,
    441 			tooltip: tooltip
    442 		};
    443 	},
    444 
    445 	_find: function( target ) {
    446 		var id = target.data( "ui-tooltip-id" );
    447 		return id ? this.tooltips[ id ] : null;
    448 	},
    449 
    450 	_removeTooltip: function( tooltip ) {
    451 		tooltip.remove();
    452 		delete this.tooltips[ tooltip.attr( "id" ) ];
    453 	},
    454 
    455 	_appendTo: function( target ) {
    456 		var element = target.closest( ".ui-front, dialog" );
    457 
    458 		if ( !element.length ) {
    459 			element = this.document[ 0 ].body;
    460 		}
    461 
    462 		return element;
    463 	},
    464 
    465 	_destroy: function() {
    466 		var that = this;
    467 
    468 		// Close open tooltips
    469 		$.each( this.tooltips, function( id, tooltipData ) {
    470 
    471 			// Delegate to close method to handle common cleanup
    472 			var event = $.Event( "blur" ),
    473 				element = tooltipData.element;
    474 			event.target = event.currentTarget = element[ 0 ];
    475 			that.close( event, true );
    476 
    477 			// Remove immediately; destroying an open tooltip doesn't use the
    478 			// hide animation
    479 			$( "#" + id ).remove();
    480 
    481 			// Restore the title
    482 			if ( element.data( "ui-tooltip-title" ) ) {
    483 
    484 				// If the title attribute has changed since open(), don't restore
    485 				if ( !element.attr( "title" ) ) {
    486 					element.attr( "title", element.data( "ui-tooltip-title" ) );
    487 				}
    488 				element.removeData( "ui-tooltip-title" );
    489 			}
    490 		} );
    491 		this.liveRegion.remove();
    492 	}
    493 } );
    494 
    495 // DEPRECATED
    496 // TODO: Switch return back to widget declaration at top of file when this is removed
    497 if ( $.uiBackCompat !== false ) {
    498 
    499 	// Backcompat for tooltipClass option
    500 	$.widget( "ui.tooltip", $.ui.tooltip, {
    501 		options: {
    502 			tooltipClass: null
    503 		},
    504 		_tooltip: function() {
    505 			var tooltipData = this._superApply( arguments );
    506 			if ( this.options.tooltipClass ) {
    507 				tooltipData.tooltip.addClass( this.options.tooltipClass );
    508 			}
    509 			return tooltipData;
    510 		}
    511 	} );
    512 }
    513 
    514 return $.ui.tooltip;
    515 
    516 } ) );