1 | /* ======================================================================== |
---|
2 | * Bootstrap: tooltip.js v3.1.0 |
---|
3 | * http://getbootstrap.com/javascript/#tooltip |
---|
4 | * Inspired by the original jQuery.tipsy by Jason Frame |
---|
5 | * ======================================================================== |
---|
6 | * Copyright 2011-2014 Twitter, Inc. |
---|
7 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) |
---|
8 | * ======================================================================== */ |
---|
9 | |
---|
10 | |
---|
11 | +function ($) { |
---|
12 | 'use strict'; |
---|
13 | |
---|
14 | // TOOLTIP PUBLIC CLASS DEFINITION |
---|
15 | // =============================== |
---|
16 | |
---|
17 | var Tooltip = function (element, options) { |
---|
18 | this.type = |
---|
19 | this.options = |
---|
20 | this.enabled = |
---|
21 | this.timeout = |
---|
22 | this.hoverState = |
---|
23 | this.$element = null |
---|
24 | |
---|
25 | this.init('tooltip', element, options) |
---|
26 | } |
---|
27 | |
---|
28 | Tooltip.DEFAULTS = { |
---|
29 | animation: true, |
---|
30 | placement: 'top', |
---|
31 | selector: false, |
---|
32 | template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>', |
---|
33 | trigger: 'hover focus', |
---|
34 | title: '', |
---|
35 | delay: 0, |
---|
36 | html: false, |
---|
37 | container: false |
---|
38 | } |
---|
39 | |
---|
40 | Tooltip.prototype.init = function (type, element, options) { |
---|
41 | this.enabled = true |
---|
42 | this.type = type |
---|
43 | this.$element = $(element) |
---|
44 | this.options = this.getOptions(options) |
---|
45 | |
---|
46 | var triggers = this.options.trigger.split(' ') |
---|
47 | |
---|
48 | for (var i = triggers.length; i--;) { |
---|
49 | var trigger = triggers[i] |
---|
50 | |
---|
51 | if (trigger == 'click') { |
---|
52 | this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) |
---|
53 | } else if (trigger != 'manual') { |
---|
54 | var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' |
---|
55 | var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' |
---|
56 | |
---|
57 | this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) |
---|
58 | this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) |
---|
59 | } |
---|
60 | } |
---|
61 | |
---|
62 | this.options.selector ? |
---|
63 | (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : |
---|
64 | this.fixTitle() |
---|
65 | } |
---|
66 | |
---|
67 | Tooltip.prototype.getDefaults = function () { |
---|
68 | return Tooltip.DEFAULTS |
---|
69 | } |
---|
70 | |
---|
71 | Tooltip.prototype.getOptions = function (options) { |
---|
72 | options = $.extend({}, this.getDefaults(), this.$element.data(), options) |
---|
73 | |
---|
74 | if (options.delay && typeof options.delay == 'number') { |
---|
75 | options.delay = { |
---|
76 | show: options.delay, |
---|
77 | hide: options.delay |
---|
78 | } |
---|
79 | } |
---|
80 | |
---|
81 | return options |
---|
82 | } |
---|
83 | |
---|
84 | Tooltip.prototype.getDelegateOptions = function () { |
---|
85 | var options = {} |
---|
86 | var defaults = this.getDefaults() |
---|
87 | |
---|
88 | this._options && $.each(this._options, function (key, value) { |
---|
89 | if (defaults[key] != value) options[key] = value |
---|
90 | }) |
---|
91 | |
---|
92 | return options |
---|
93 | } |
---|
94 | |
---|
95 | Tooltip.prototype.enter = function (obj) { |
---|
96 | var self = obj instanceof this.constructor ? |
---|
97 | obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) |
---|
98 | |
---|
99 | clearTimeout(self.timeout) |
---|
100 | |
---|
101 | self.hoverState = 'in' |
---|
102 | |
---|
103 | if (!self.options.delay || !self.options.delay.show) return self.show() |
---|
104 | |
---|
105 | self.timeout = setTimeout(function () { |
---|
106 | if (self.hoverState == 'in') self.show() |
---|
107 | }, self.options.delay.show) |
---|
108 | } |
---|
109 | |
---|
110 | Tooltip.prototype.leave = function (obj) { |
---|
111 | var self = obj instanceof this.constructor ? |
---|
112 | obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) |
---|
113 | |
---|
114 | clearTimeout(self.timeout) |
---|
115 | |
---|
116 | self.hoverState = 'out' |
---|
117 | |
---|
118 | if (!self.options.delay || !self.options.delay.hide) return self.hide() |
---|
119 | |
---|
120 | self.timeout = setTimeout(function () { |
---|
121 | if (self.hoverState == 'out') self.hide() |
---|
122 | }, self.options.delay.hide) |
---|
123 | } |
---|
124 | |
---|
125 | Tooltip.prototype.show = function () { |
---|
126 | var e = $.Event('show.bs.' + this.type) |
---|
127 | |
---|
128 | if (this.hasContent() && this.enabled) { |
---|
129 | this.$element.trigger(e) |
---|
130 | |
---|
131 | if (e.isDefaultPrevented()) return |
---|
132 | var that = this; |
---|
133 | |
---|
134 | var $tip = this.tip() |
---|
135 | |
---|
136 | this.setContent() |
---|
137 | |
---|
138 | if (this.options.animation) $tip.addClass('fade') |
---|
139 | |
---|
140 | var placement = typeof this.options.placement == 'function' ? |
---|
141 | this.options.placement.call(this, $tip[0], this.$element[0]) : |
---|
142 | this.options.placement |
---|
143 | |
---|
144 | var autoToken = /\s?auto?\s?/i |
---|
145 | var autoPlace = autoToken.test(placement) |
---|
146 | if (autoPlace) placement = placement.replace(autoToken, '') || 'top' |
---|
147 | |
---|
148 | $tip |
---|
149 | .detach() |
---|
150 | .css({ top: 0, left: 0, display: 'block' }) |
---|
151 | .addClass(placement) |
---|
152 | |
---|
153 | this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) |
---|
154 | |
---|
155 | var pos = this.getPosition() |
---|
156 | var actualWidth = $tip[0].offsetWidth |
---|
157 | var actualHeight = $tip[0].offsetHeight |
---|
158 | |
---|
159 | if (autoPlace) { |
---|
160 | var $parent = this.$element.parent() |
---|
161 | |
---|
162 | var orgPlacement = placement |
---|
163 | var docScroll = document.documentElement.scrollTop || document.body.scrollTop |
---|
164 | var parentWidth = this.options.container == 'body' ? window.innerWidth : $parent.outerWidth() |
---|
165 | var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight() |
---|
166 | var parentLeft = this.options.container == 'body' ? 0 : $parent.offset().left |
---|
167 | |
---|
168 | placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' : |
---|
169 | placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' : |
---|
170 | placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' : |
---|
171 | placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' : |
---|
172 | placement |
---|
173 | |
---|
174 | $tip |
---|
175 | .removeClass(orgPlacement) |
---|
176 | .addClass(placement) |
---|
177 | } |
---|
178 | |
---|
179 | var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) |
---|
180 | |
---|
181 | this.applyPlacement(calculatedOffset, placement) |
---|
182 | this.hoverState = null |
---|
183 | |
---|
184 | var complete = function() { |
---|
185 | that.$element.trigger('shown.bs.' + that.type) |
---|
186 | } |
---|
187 | |
---|
188 | $.support.transition && this.$tip.hasClass('fade') ? |
---|
189 | $tip |
---|
190 | .one($.support.transition.end, complete) |
---|
191 | .emulateTransitionEnd(150) : |
---|
192 | complete() |
---|
193 | } |
---|
194 | } |
---|
195 | |
---|
196 | Tooltip.prototype.applyPlacement = function (offset, placement) { |
---|
197 | var replace |
---|
198 | var $tip = this.tip() |
---|
199 | var width = $tip[0].offsetWidth |
---|
200 | var height = $tip[0].offsetHeight |
---|
201 | |
---|
202 | // manually read margins because getBoundingClientRect includes difference |
---|
203 | var marginTop = parseInt($tip.css('margin-top'), 10) |
---|
204 | var marginLeft = parseInt($tip.css('margin-left'), 10) |
---|
205 | |
---|
206 | // we must check for NaN for ie 8/9 |
---|
207 | if (isNaN(marginTop)) marginTop = 0 |
---|
208 | if (isNaN(marginLeft)) marginLeft = 0 |
---|
209 | |
---|
210 | offset.top = offset.top + marginTop |
---|
211 | offset.left = offset.left + marginLeft |
---|
212 | |
---|
213 | // $.fn.offset doesn't round pixel values |
---|
214 | // so we use setOffset directly with our own function B-0 |
---|
215 | $.offset.setOffset($tip[0], $.extend({ |
---|
216 | using: function (props) { |
---|
217 | $tip.css({ |
---|
218 | top: Math.round(props.top), |
---|
219 | left: Math.round(props.left) |
---|
220 | }) |
---|
221 | } |
---|
222 | }, offset), 0) |
---|
223 | |
---|
224 | $tip.addClass('in') |
---|
225 | |
---|
226 | // check to see if placing tip in new offset caused the tip to resize itself |
---|
227 | var actualWidth = $tip[0].offsetWidth |
---|
228 | var actualHeight = $tip[0].offsetHeight |
---|
229 | |
---|
230 | if (placement == 'top' && actualHeight != height) { |
---|
231 | replace = true |
---|
232 | offset.top = offset.top + height - actualHeight |
---|
233 | } |
---|
234 | |
---|
235 | if (/bottom|top/.test(placement)) { |
---|
236 | var delta = 0 |
---|
237 | |
---|
238 | if (offset.left < 0) { |
---|
239 | delta = offset.left * -2 |
---|
240 | offset.left = 0 |
---|
241 | |
---|
242 | $tip.offset(offset) |
---|
243 | |
---|
244 | actualWidth = $tip[0].offsetWidth |
---|
245 | actualHeight = $tip[0].offsetHeight |
---|
246 | } |
---|
247 | |
---|
248 | this.replaceArrow(delta - width + actualWidth, actualWidth, 'left') |
---|
249 | } else { |
---|
250 | this.replaceArrow(actualHeight - height, actualHeight, 'top') |
---|
251 | } |
---|
252 | |
---|
253 | if (replace) $tip.offset(offset) |
---|
254 | } |
---|
255 | |
---|
256 | Tooltip.prototype.replaceArrow = function (delta, dimension, position) { |
---|
257 | this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '') |
---|
258 | } |
---|
259 | |
---|
260 | Tooltip.prototype.setContent = function () { |
---|
261 | var $tip = this.tip() |
---|
262 | var title = this.getTitle() |
---|
263 | |
---|
264 | $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) |
---|
265 | $tip.removeClass('fade in top bottom left right') |
---|
266 | } |
---|
267 | |
---|
268 | Tooltip.prototype.hide = function () { |
---|
269 | var that = this |
---|
270 | var $tip = this.tip() |
---|
271 | var e = $.Event('hide.bs.' + this.type) |
---|
272 | |
---|
273 | function complete() { |
---|
274 | if (that.hoverState != 'in') $tip.detach() |
---|
275 | that.$element.trigger('hidden.bs.' + that.type) |
---|
276 | } |
---|
277 | |
---|
278 | this.$element.trigger(e) |
---|
279 | |
---|
280 | if (e.isDefaultPrevented()) return |
---|
281 | |
---|
282 | $tip.removeClass('in') |
---|
283 | |
---|
284 | $.support.transition && this.$tip.hasClass('fade') ? |
---|
285 | $tip |
---|
286 | .one($.support.transition.end, complete) |
---|
287 | .emulateTransitionEnd(150) : |
---|
288 | complete() |
---|
289 | |
---|
290 | this.hoverState = null |
---|
291 | |
---|
292 | return this |
---|
293 | } |
---|
294 | |
---|
295 | Tooltip.prototype.fixTitle = function () { |
---|
296 | var $e = this.$element |
---|
297 | if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') { |
---|
298 | $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') |
---|
299 | } |
---|
300 | } |
---|
301 | |
---|
302 | Tooltip.prototype.hasContent = function () { |
---|
303 | return this.getTitle() |
---|
304 | } |
---|
305 | |
---|
306 | Tooltip.prototype.getPosition = function () { |
---|
307 | var el = this.$element[0] |
---|
308 | return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : { |
---|
309 | width: el.offsetWidth, |
---|
310 | height: el.offsetHeight |
---|
311 | }, this.$element.offset()) |
---|
312 | } |
---|
313 | |
---|
314 | Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { |
---|
315 | return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : |
---|
316 | placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : |
---|
317 | placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : |
---|
318 | /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } |
---|
319 | } |
---|
320 | |
---|
321 | Tooltip.prototype.getTitle = function () { |
---|
322 | var title |
---|
323 | var $e = this.$element |
---|
324 | var o = this.options |
---|
325 | |
---|
326 | title = $e.attr('data-original-title') |
---|
327 | || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) |
---|
328 | |
---|
329 | return title |
---|
330 | } |
---|
331 | |
---|
332 | Tooltip.prototype.tip = function () { |
---|
333 | return this.$tip = this.$tip || $(this.options.template) |
---|
334 | } |
---|
335 | |
---|
336 | Tooltip.prototype.arrow = function () { |
---|
337 | return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow') |
---|
338 | } |
---|
339 | |
---|
340 | Tooltip.prototype.validate = function () { |
---|
341 | if (!this.$element[0].parentNode) { |
---|
342 | this.hide() |
---|
343 | this.$element = null |
---|
344 | this.options = null |
---|
345 | } |
---|
346 | } |
---|
347 | |
---|
348 | Tooltip.prototype.enable = function () { |
---|
349 | this.enabled = true |
---|
350 | } |
---|
351 | |
---|
352 | Tooltip.prototype.disable = function () { |
---|
353 | this.enabled = false |
---|
354 | } |
---|
355 | |
---|
356 | Tooltip.prototype.toggleEnabled = function () { |
---|
357 | this.enabled = !this.enabled |
---|
358 | } |
---|
359 | |
---|
360 | Tooltip.prototype.toggle = function (e) { |
---|
361 | var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this |
---|
362 | self.tip().hasClass('in') ? self.leave(self) : self.enter(self) |
---|
363 | } |
---|
364 | |
---|
365 | Tooltip.prototype.destroy = function () { |
---|
366 | clearTimeout(this.timeout) |
---|
367 | this.hide().$element.off('.' + this.type).removeData('bs.' + this.type) |
---|
368 | } |
---|
369 | |
---|
370 | |
---|
371 | // TOOLTIP PLUGIN DEFINITION |
---|
372 | // ========================= |
---|
373 | |
---|
374 | var old = $.fn.tooltip |
---|
375 | |
---|
376 | $.fn.tooltip = function (option) { |
---|
377 | return this.each(function () { |
---|
378 | var $this = $(this) |
---|
379 | var data = $this.data('bs.tooltip') |
---|
380 | var options = typeof option == 'object' && option |
---|
381 | |
---|
382 | if (!data && option == 'destroy') return |
---|
383 | if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) |
---|
384 | if (typeof option == 'string') data[option]() |
---|
385 | }) |
---|
386 | } |
---|
387 | |
---|
388 | $.fn.tooltip.Constructor = Tooltip |
---|
389 | |
---|
390 | |
---|
391 | // TOOLTIP NO CONFLICT |
---|
392 | // =================== |
---|
393 | |
---|
394 | $.fn.tooltip.noConflict = function () { |
---|
395 | $.fn.tooltip = old |
---|
396 | return this |
---|
397 | } |
---|
398 | |
---|
399 | }(jQuery); |
---|