wp/wp-includes/js/jquery/ui/tooltip.js
changeset 18 be944660c56a
child 19 3d72ae0968f4
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
       
     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 } ) );