|
1 /*! |
|
2 * jQuery UI Tooltip 1.10.3 |
|
3 * http://jqueryui.com |
|
4 * |
|
5 * Copyright 2013 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.3", |
|
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 ) ); |