7
|
1 /*!
|
|
2 * jQuery UI Tooltip 1.10.4
|
|
3 * http://jqueryui.com
|
|
4 *
|
|
5 * Copyright 2014 jQuery Foundation and other contributors
|
|
6 * Released under the MIT license.
|
|
7 * http://jquery.org/license
|
|
8 *
|
|
9 * http://api.jqueryui.com/tooltip/
|
|
10 *
|
|
11 * Depends:
|
|
12 * jquery.ui.core.js
|
|
13 * jquery.ui.widget.js
|
|
14 * jquery.ui.position.js
|
|
15 */
|
|
16 (function( $ ) {
|
|
17
|
|
18 var increments = 0;
|
|
19
|
|
20 function addDescribedBy( elem, id ) {
|
|
21 var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
|
|
22 describedby.push( id );
|
|
23 elem
|
|
24 .data( "ui-tooltip-id", id )
|
|
25 .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
|
|
26 }
|
|
27
|
|
28 function removeDescribedBy( elem ) {
|
|
29 var id = elem.data( "ui-tooltip-id" ),
|
|
30 describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
|
|
31 index = $.inArray( id, describedby );
|
|
32 if ( index !== -1 ) {
|
|
33 describedby.splice( index, 1 );
|
|
34 }
|
|
35
|
|
36 elem.removeData( "ui-tooltip-id" );
|
|
37 describedby = $.trim( describedby.join( " " ) );
|
|
38 if ( describedby ) {
|
|
39 elem.attr( "aria-describedby", describedby );
|
|
40 } else {
|
|
41 elem.removeAttr( "aria-describedby" );
|
|
42 }
|
|
43 }
|
|
44
|
|
45 $.widget( "ui.tooltip", {
|
|
46 version: "1.10.4",
|
|
47 options: {
|
|
48 content: function() {
|
|
49 // support: IE<9, Opera in jQuery <1.7
|
|
50 // .text() can't accept undefined, so coerce to a string
|
|
51 var title = $( this ).attr( "title" ) || "";
|
|
52 // Escape title, since we're going from an attribute to raw HTML
|
|
53 return $( "<a>" ).text( title ).html();
|
|
54 },
|
|
55 hide: true,
|
|
56 // Disabled elements have inconsistent behavior across browsers (#8661)
|
|
57 items: "[title]:not([disabled])",
|
|
58 position: {
|
|
59 my: "left top+15",
|
|
60 at: "left bottom",
|
|
61 collision: "flipfit flip"
|
|
62 },
|
|
63 show: true,
|
|
64 tooltipClass: null,
|
|
65 track: false,
|
|
66
|
|
67 // callbacks
|
|
68 close: null,
|
|
69 open: null
|
|
70 },
|
|
71
|
|
72 _create: function() {
|
|
73 this._on({
|
|
74 mouseover: "open",
|
|
75 focusin: "open"
|
|
76 });
|
|
77
|
|
78 // IDs of generated tooltips, needed for destroy
|
|
79 this.tooltips = {};
|
|
80 // IDs of parent tooltips where we removed the title attribute
|
|
81 this.parents = {};
|
|
82
|
|
83 if ( this.options.disabled ) {
|
|
84 this._disable();
|
|
85 }
|
|
86 },
|
|
87
|
|
88 _setOption: function( key, value ) {
|
|
89 var that = this;
|
|
90
|
|
91 if ( key === "disabled" ) {
|
|
92 this[ value ? "_disable" : "_enable" ]();
|
|
93 this.options[ key ] = value;
|
|
94 // disable element style changes
|
|
95 return;
|
|
96 }
|
|
97
|
|
98 this._super( key, value );
|
|
99
|
|
100 if ( key === "content" ) {
|
|
101 $.each( this.tooltips, function( id, element ) {
|
|
102 that._updateContent( element );
|
|
103 });
|
|
104 }
|
|
105 },
|
|
106
|
|
107 _disable: function() {
|
|
108 var that = this;
|
|
109
|
|
110 // close open tooltips
|
|
111 $.each( this.tooltips, function( id, element ) {
|
|
112 var event = $.Event( "blur" );
|
|
113 event.target = event.currentTarget = element[0];
|
|
114 that.close( event, true );
|
|
115 });
|
|
116
|
|
117 // remove title attributes to prevent native tooltips
|
|
118 this.element.find( this.options.items ).addBack().each(function() {
|
|
119 var element = $( this );
|
|
120 if ( element.is( "[title]" ) ) {
|
|
121 element
|
|
122 .data( "ui-tooltip-title", element.attr( "title" ) )
|
|
123 .attr( "title", "" );
|
|
124 }
|
|
125 });
|
|
126 },
|
|
127
|
|
128 _enable: function() {
|
|
129 // restore title attributes
|
|
130 this.element.find( this.options.items ).addBack().each(function() {
|
|
131 var element = $( this );
|
|
132 if ( element.data( "ui-tooltip-title" ) ) {
|
|
133 element.attr( "title", element.data( "ui-tooltip-title" ) );
|
|
134 }
|
|
135 });
|
|
136 },
|
|
137
|
|
138 open: function( event ) {
|
|
139 var that = this,
|
|
140 target = $( event ? event.target : this.element )
|
|
141 // we need closest here due to mouseover bubbling,
|
|
142 // but always pointing at the same event target
|
|
143 .closest( this.options.items );
|
|
144
|
|
145 // No element to show a tooltip for or the tooltip is already open
|
|
146 if ( !target.length || target.data( "ui-tooltip-id" ) ) {
|
|
147 return;
|
|
148 }
|
|
149
|
|
150 if ( target.attr( "title" ) ) {
|
|
151 target.data( "ui-tooltip-title", target.attr( "title" ) );
|
|
152 }
|
|
153
|
|
154 target.data( "ui-tooltip-open", true );
|
|
155
|
|
156 // kill parent tooltips, custom or native, for hover
|
|
157 if ( event && event.type === "mouseover" ) {
|
|
158 target.parents().each(function() {
|
|
159 var parent = $( this ),
|
|
160 blurEvent;
|
|
161 if ( parent.data( "ui-tooltip-open" ) ) {
|
|
162 blurEvent = $.Event( "blur" );
|
|
163 blurEvent.target = blurEvent.currentTarget = this;
|
|
164 that.close( blurEvent, true );
|
|
165 }
|
|
166 if ( parent.attr( "title" ) ) {
|
|
167 parent.uniqueId();
|
|
168 that.parents[ this.id ] = {
|
|
169 element: this,
|
|
170 title: parent.attr( "title" )
|
|
171 };
|
|
172 parent.attr( "title", "" );
|
|
173 }
|
|
174 });
|
|
175 }
|
|
176
|
|
177 this._updateContent( target, event );
|
|
178 },
|
|
179
|
|
180 _updateContent: function( target, event ) {
|
|
181 var content,
|
|
182 contentOption = this.options.content,
|
|
183 that = this,
|
|
184 eventType = event ? event.type : null;
|
|
185
|
|
186 if ( typeof contentOption === "string" ) {
|
|
187 return this._open( event, target, contentOption );
|
|
188 }
|
|
189
|
|
190 content = contentOption.call( target[0], function( response ) {
|
|
191 // ignore async response if tooltip was closed already
|
|
192 if ( !target.data( "ui-tooltip-open" ) ) {
|
|
193 return;
|
|
194 }
|
|
195 // IE may instantly serve a cached response for ajax requests
|
|
196 // delay this call to _open so the other call to _open runs first
|
|
197 that._delay(function() {
|
|
198 // jQuery creates a special event for focusin when it doesn't
|
|
199 // exist natively. To improve performance, the native event
|
|
200 // object is reused and the type is changed. Therefore, we can't
|
|
201 // rely on the type being correct after the event finished
|
|
202 // bubbling, so we set it back to the previous value. (#8740)
|
|
203 if ( event ) {
|
|
204 event.type = eventType;
|
|
205 }
|
|
206 this._open( event, target, response );
|
|
207 });
|
|
208 });
|
|
209 if ( content ) {
|
|
210 this._open( event, target, content );
|
|
211 }
|
|
212 },
|
|
213
|
|
214 _open: function( event, target, content ) {
|
|
215 var tooltip, events, delayedShow,
|
|
216 positionOption = $.extend( {}, this.options.position );
|
|
217
|
|
218 if ( !content ) {
|
|
219 return;
|
|
220 }
|
|
221
|
|
222 // Content can be updated multiple times. If the tooltip already
|
|
223 // exists, then just update the content and bail.
|
|
224 tooltip = this._find( target );
|
|
225 if ( tooltip.length ) {
|
|
226 tooltip.find( ".ui-tooltip-content" ).html( content );
|
|
227 return;
|
|
228 }
|
|
229
|
|
230 // if we have a title, clear it to prevent the native tooltip
|
|
231 // we have to check first to avoid defining a title if none exists
|
|
232 // (we don't want to cause an element to start matching [title])
|
|
233 //
|
|
234 // We use removeAttr only for key events, to allow IE to export the correct
|
|
235 // accessible attributes. For mouse events, set to empty string to avoid
|
|
236 // native tooltip showing up (happens only when removing inside mouseover).
|
|
237 if ( target.is( "[title]" ) ) {
|
|
238 if ( event && event.type === "mouseover" ) {
|
|
239 target.attr( "title", "" );
|
|
240 } else {
|
|
241 target.removeAttr( "title" );
|
|
242 }
|
|
243 }
|
|
244
|
|
245 tooltip = this._tooltip( target );
|
|
246 addDescribedBy( target, tooltip.attr( "id" ) );
|
|
247 tooltip.find( ".ui-tooltip-content" ).html( content );
|
|
248
|
|
249 function position( event ) {
|
|
250 positionOption.of = event;
|
|
251 if ( tooltip.is( ":hidden" ) ) {
|
|
252 return;
|
|
253 }
|
|
254 tooltip.position( positionOption );
|
|
255 }
|
|
256 if ( this.options.track && event && /^mouse/.test( event.type ) ) {
|
|
257 this._on( this.document, {
|
|
258 mousemove: position
|
|
259 });
|
|
260 // trigger once to override element-relative positioning
|
|
261 position( event );
|
|
262 } else {
|
|
263 tooltip.position( $.extend({
|
|
264 of: target
|
|
265 }, this.options.position ) );
|
|
266 }
|
|
267
|
|
268 tooltip.hide();
|
|
269
|
|
270 this._show( tooltip, this.options.show );
|
|
271 // Handle tracking tooltips that are shown with a delay (#8644). As soon
|
|
272 // as the tooltip is visible, position the tooltip using the most recent
|
|
273 // event.
|
|
274 if ( this.options.show && this.options.show.delay ) {
|
|
275 delayedShow = this.delayedShow = setInterval(function() {
|
|
276 if ( tooltip.is( ":visible" ) ) {
|
|
277 position( positionOption.of );
|
|
278 clearInterval( delayedShow );
|
|
279 }
|
|
280 }, $.fx.interval );
|
|
281 }
|
|
282
|
|
283 this._trigger( "open", event, { tooltip: tooltip } );
|
|
284
|
|
285 events = {
|
|
286 keyup: function( event ) {
|
|
287 if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
|
|
288 var fakeEvent = $.Event(event);
|
|
289 fakeEvent.currentTarget = target[0];
|
|
290 this.close( fakeEvent, true );
|
|
291 }
|
|
292 },
|
|
293 remove: function() {
|
|
294 this._removeTooltip( tooltip );
|
|
295 }
|
|
296 };
|
|
297 if ( !event || event.type === "mouseover" ) {
|
|
298 events.mouseleave = "close";
|
|
299 }
|
|
300 if ( !event || event.type === "focusin" ) {
|
|
301 events.focusout = "close";
|
|
302 }
|
|
303 this._on( true, target, events );
|
|
304 },
|
|
305
|
|
306 close: function( event ) {
|
|
307 var that = this,
|
|
308 target = $( event ? event.currentTarget : this.element ),
|
|
309 tooltip = this._find( target );
|
|
310
|
|
311 // disabling closes the tooltip, so we need to track when we're closing
|
|
312 // to avoid an infinite loop in case the tooltip becomes disabled on close
|
|
313 if ( this.closing ) {
|
|
314 return;
|
|
315 }
|
|
316
|
|
317 // Clear the interval for delayed tracking tooltips
|
|
318 clearInterval( this.delayedShow );
|
|
319
|
|
320 // only set title if we had one before (see comment in _open())
|
|
321 if ( target.data( "ui-tooltip-title" ) ) {
|
|
322 target.attr( "title", target.data( "ui-tooltip-title" ) );
|
|
323 }
|
|
324
|
|
325 removeDescribedBy( target );
|
|
326
|
|
327 tooltip.stop( true );
|
|
328 this._hide( tooltip, this.options.hide, function() {
|
|
329 that._removeTooltip( $( this ) );
|
|
330 });
|
|
331
|
|
332 target.removeData( "ui-tooltip-open" );
|
|
333 this._off( target, "mouseleave focusout keyup" );
|
|
334 // Remove 'remove' binding only on delegated targets
|
|
335 if ( target[0] !== this.element[0] ) {
|
|
336 this._off( target, "remove" );
|
|
337 }
|
|
338 this._off( this.document, "mousemove" );
|
|
339
|
|
340 if ( event && event.type === "mouseleave" ) {
|
|
341 $.each( this.parents, function( id, parent ) {
|
|
342 $( parent.element ).attr( "title", parent.title );
|
|
343 delete that.parents[ id ];
|
|
344 });
|
|
345 }
|
|
346
|
|
347 this.closing = true;
|
|
348 this._trigger( "close", event, { tooltip: tooltip } );
|
|
349 this.closing = false;
|
|
350 },
|
|
351
|
|
352 _tooltip: function( element ) {
|
|
353 var id = "ui-tooltip-" + increments++,
|
|
354 tooltip = $( "<div>" )
|
|
355 .attr({
|
|
356 id: id,
|
|
357 role: "tooltip"
|
|
358 })
|
|
359 .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
|
|
360 ( this.options.tooltipClass || "" ) );
|
|
361 $( "<div>" )
|
|
362 .addClass( "ui-tooltip-content" )
|
|
363 .appendTo( tooltip );
|
|
364 tooltip.appendTo( this.document[0].body );
|
|
365 this.tooltips[ id ] = element;
|
|
366 return tooltip;
|
|
367 },
|
|
368
|
|
369 _find: function( target ) {
|
|
370 var id = target.data( "ui-tooltip-id" );
|
|
371 return id ? $( "#" + id ) : $();
|
|
372 },
|
|
373
|
|
374 _removeTooltip: function( tooltip ) {
|
|
375 tooltip.remove();
|
|
376 delete this.tooltips[ tooltip.attr( "id" ) ];
|
|
377 },
|
|
378
|
|
379 _destroy: function() {
|
|
380 var that = this;
|
|
381
|
|
382 // close open tooltips
|
|
383 $.each( this.tooltips, function( id, element ) {
|
|
384 // Delegate to close method to handle common cleanup
|
|
385 var event = $.Event( "blur" );
|
|
386 event.target = event.currentTarget = element[0];
|
|
387 that.close( event, true );
|
|
388
|
|
389 // Remove immediately; destroying an open tooltip doesn't use the
|
|
390 // hide animation
|
|
391 $( "#" + id ).remove();
|
|
392
|
|
393 // Restore the title
|
|
394 if ( element.data( "ui-tooltip-title" ) ) {
|
|
395 element.attr( "title", element.data( "ui-tooltip-title" ) );
|
|
396 element.removeData( "ui-tooltip-title" );
|
|
397 }
|
|
398 });
|
|
399 }
|
|
400 });
|
|
401
|
|
402 }( jQuery ) );
|