/*! * jQuery UI Tooltip @VERSION * http://jqueryui.com * * Copyright 2012 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * Depends: * jquery.ui.core.js * jquery.ui.widget.js * jquery.ui.position.js */ (function( $ ) { var increments = 0; function addDescribedBy( elem, id ) { var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ); describedby.push( id ); elem .data( "ui-tooltip-id", id ) .attr( "aria-describedby", $.trim( describedby.join( " " ) ) ); } function removeDescribedBy( elem ) { var id = elem.data( "ui-tooltip-id" ), describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ), index = $.inArray( id, describedby ); if ( index !== -1 ) { describedby.splice( index, 1 ); } elem.removeData( "ui-tooltip-id" ); describedby = $.trim( describedby.join( " " ) ); if ( describedby ) { elem.attr( "aria-describedby", describedby ); } else { elem.removeAttr( "aria-describedby" ); } } $.widget( "ui.tooltip", { version: "@VERSION", options: { content: function() { return $( this ).attr( "title" ); }, hide: true, items: "[title]", position: { my: "left+15 center", at: "right center", collision: "flipfit flipfit" }, show: true, tooltipClass: null, track: false, // callbacks close: null, open: null }, _create: function() { this._on({ mouseover: "open", focusin: "open" }); // IDs of generated tooltips, needed for destroy this.tooltips = {}; }, _setOption: function( key, value ) { var that = this; if ( key === "disabled" ) { this[ value ? "_disable" : "_enable" ](); this.options[ key ] = value; // disable element style changes return; } this._super( key, value ); if ( key === "content" ) { $.each( this.tooltips, function( id, element ) { that._updateContent( element ); }); } }, _disable: function() { var that = this; // close open tooltips $.each( this.tooltips, function( id, element ) { var event = $.Event( "blur" ); event.target = event.currentTarget = element[0]; that.close( event, true ); }); // remove title attributes to prevent native tooltips this.element.find( this.options.items ).andSelf().each(function() { var element = $( this ); if ( element.is( "[title]" ) ) { element .data( "ui-tooltip-title", element.attr( "title" ) ) .attr( "title", "" ); } }); }, _enable: function() { // restore title attributes this.element.find( this.options.items ).andSelf().each(function() { var element = $( this ); if ( element.data( "ui-tooltip-title" ) ) { element.attr( "title", element.data( "ui-tooltip-title" ) ); } }); }, open: function( event ) { var target = $( event ? event.target : this.element ) .closest( this.options.items ); // No element to show a tooltip for if ( !target.length ) { return; } // If the tooltip is open and we're tracking then reposition the tooltip. // This makes sure that a tracking tooltip doesn't obscure a focused element // if the user was hovering when the element gained focused. if ( this.options.track && target.data( "ui-tooltip-id" ) ) { this._find( target ).position( $.extend({ of: target }, this.options.position ) ); return; } if ( target.attr( "title" ) ) { target.data( "ui-tooltip-title", target.attr( "title" ) ); } target.data( "tooltip-open", true ); this._updateContent( target, event ); }, _updateContent: function( target, event ) { var content, contentOption = this.options.content, that = this; if ( typeof contentOption === "string" ) { return this._open( event, target, contentOption ); } content = contentOption.call( target[0], function( response ) { // ignore async response if tooltip was closed already if ( !target.data( "tooltip-open" ) ) { return; } // IE may instantly serve a cached response for ajax requests // delay this call to _open so the other call to _open runs first that._delay(function() { this._open( event, target, response ); }); }); if ( content ) { this._open( event, target, content ); } }, _open: function( event, target, content ) { var tooltip, positionOption; if ( !content ) { return; } // Content can be updated multiple times. If the tooltip already // exists, then just update the content and bail. tooltip = this._find( target ); if ( tooltip.length ) { tooltip.find( ".ui-tooltip-content" ).html( content ); return; } // if we have a title, clear it to prevent the native tooltip // we have to check first to avoid defining a title if none exists // (we don't want to cause an element to start matching [title]) // // We use removeAttr only for key events, to allow IE to export the correct // accessible attributes. For mouse events, set to empty string to avoid // native tooltip showing up (happens only when removing inside mouseover). if ( target.is( "[title]" ) ) { if ( event && event.type === "mouseover" ) { target.attr( "title", "" ); } else { target.removeAttr( "title" ); } } tooltip = this._tooltip( target ); addDescribedBy( target, tooltip.attr( "id" ) ); tooltip.find( ".ui-tooltip-content" ).html( content ); function position( event ) { positionOption.of = event; tooltip.position( positionOption ); } if ( this.options.track && /^mouse/.test( event.originalEvent.type ) ) { positionOption = $.extend( {}, this.options.position ); this._on( this.document, { mousemove: position }); // trigger once to override element-relative positioning position( event ); } else { tooltip.position( $.extend({ of: target }, this.options.position ) ); } tooltip.hide(); this._show( tooltip, this.options.show ); this._trigger( "open", event, { tooltip: tooltip } ); this._on( target, { mouseleave: "close", focusout: "close", keyup: function( event ) { if ( event.keyCode === $.ui.keyCode.ESCAPE ) { var fakeEvent = $.Event(event); fakeEvent.currentTarget = target[0]; this.close( fakeEvent, true ); } } }); }, close: function( event, force ) { var that = this, target = $( event ? event.currentTarget : this.element ), tooltip = this._find( target ); // disabling closes the tooltip, so we need to track when we're closing // to avoid an infinite loop in case the tooltip becomes disabled on close if ( this.closing ) { return; } // don't close if the element has focus // this prevents the tooltip from closing if you hover while focused // // we have to check the event type because tabbing out of the document // may leave the element as the activeElement if ( !force && event && event.type !== "focusout" && this.document[0].activeElement === target[0] ) { return; } // only set title if we had one before (see comment in _open()) if ( target.data( "ui-tooltip-title" ) ) { target.attr( "title", target.data( "ui-tooltip-title" ) ); } removeDescribedBy( target ); tooltip.stop( true ); this._hide( tooltip, this.options.hide, function() { $( this ).remove(); delete that.tooltips[ this.id ]; }); target.removeData( "tooltip-open" ); this._off( target, "mouseleave focusout keyup" ); this._off( this.document, "mousemove" ); this.closing = true; this._trigger( "close", event, { tooltip: tooltip } ); this.closing = false; }, _tooltip: function( element ) { var id = "ui-tooltip-" + increments++, tooltip = $( "