64d8f0396a62c8e6eb07d17281a178951a9ded16
[myslice.git] / third-party / jquery-ui-1.10.2 / ui / jquery.ui.tooltip.js
1 /*!
2  * jQuery UI Tooltip 1.10.2
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.2",
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 ) );