|
1 | 1 | /* ===========================================================
|
2 |
| - * bootstrap-tooltip.js v2.2.2 |
3 |
| - * http://twitter.github.com/bootstrap/javascript.html#tooltips |
| 2 | + * bootstrap-tooltip.js v2.3.2 |
| 3 | + * http://getbootstrap.com/2.3.2/javascript.html#tooltips |
4 | 4 | * Inspired by the original jQuery.tipsy by Jason Frame
|
5 | 5 | * ===========================================================
|
6 |
| - * Copyright 2012 Twitter, Inc. |
| 6 | + * Copyright 2013 Twitter, Inc. |
7 | 7 | *
|
8 | 8 | * Licensed under the Apache License, Version 2.0 (the "License");
|
9 | 9 | * you may not use this file except in compliance with the License.
|
|
38 | 38 | , init: function (type, element, options) {
|
39 | 39 | var eventIn
|
40 | 40 | , eventOut
|
| 41 | + , triggers |
| 42 | + , trigger |
| 43 | + , i |
41 | 44 |
|
42 | 45 | this.type = type
|
43 | 46 | this.$element = $(element)
|
44 | 47 | this.options = this.getOptions(options)
|
45 | 48 | this.enabled = true
|
46 | 49 |
|
47 |
| - if (this.options.trigger == 'click') { |
48 |
| - this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) |
49 |
| - } else if (this.options.trigger != 'manual') { |
50 |
| - eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus' |
51 |
| - eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur' |
52 |
| - this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) |
53 |
| - this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) |
| 50 | + triggers = this.options.trigger.split(' ') |
| 51 | + |
| 52 | + for (i = triggers.length; i--;) { |
| 53 | + trigger = triggers[i] |
| 54 | + if (trigger == 'click') { |
| 55 | + this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) |
| 56 | + } else if (trigger != 'manual') { |
| 57 | + eventIn = trigger == 'hover' ? 'mouseenter' : 'focus' |
| 58 | + eventOut = trigger == 'hover' ? 'mouseleave' : 'blur' |
| 59 | + this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) |
| 60 | + this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) |
| 61 | + } |
54 | 62 | }
|
55 | 63 |
|
56 | 64 | this.options.selector ?
|
|
59 | 67 | }
|
60 | 68 |
|
61 | 69 | , getOptions: function (options) {
|
62 |
| - options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data()) |
| 70 | + options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options) |
63 | 71 |
|
64 | 72 | if (options.delay && typeof options.delay == 'number') {
|
65 | 73 | options.delay = {
|
|
72 | 80 | }
|
73 | 81 |
|
74 | 82 | , enter: function (e) {
|
75 |
| - var self = $(e.currentTarget)[this.type](this._options).data(this.type) |
| 83 | + var defaults = $.fn[this.type].defaults |
| 84 | + , options = {} |
| 85 | + , self |
| 86 | + |
| 87 | + this._options && $.each(this._options, function (key, value) { |
| 88 | + if (defaults[key] != value) options[key] = value |
| 89 | + }, this) |
| 90 | + |
| 91 | + self = $(e.currentTarget)[this.type](options).data(this.type) |
76 | 92 |
|
77 | 93 | if (!self.options.delay || !self.options.delay.show) return self.show()
|
78 | 94 |
|
|
97 | 113 |
|
98 | 114 | , show: function () {
|
99 | 115 | var $tip
|
100 |
| - , inside |
101 | 116 | , pos
|
102 | 117 | , actualWidth
|
103 | 118 | , actualHeight
|
104 | 119 | , placement
|
105 | 120 | , tp
|
| 121 | + , e = $.Event('show') |
106 | 122 |
|
107 | 123 | if (this.hasContent() && this.enabled) {
|
| 124 | + this.$element.trigger(e) |
| 125 | + if (e.isDefaultPrevented()) return |
108 | 126 | $tip = this.tip()
|
109 | 127 | this.setContent()
|
110 | 128 |
|
|
116 | 134 | this.options.placement.call(this, $tip[0], this.$element[0]) :
|
117 | 135 | this.options.placement
|
118 | 136 |
|
119 |
| - inside = /in/.test(placement) |
120 |
| - |
121 | 137 | $tip
|
122 | 138 | .detach()
|
123 | 139 | .css({ top: 0, left: 0, display: 'block' })
|
124 |
| - .insertAfter(this.$element) |
125 | 140 |
|
126 |
| - pos = this.getPosition(inside) |
| 141 | + this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) |
| 142 | + |
| 143 | + pos = this.getPosition() |
127 | 144 |
|
128 | 145 | actualWidth = $tip[0].offsetWidth
|
129 | 146 | actualHeight = $tip[0].offsetHeight
|
130 | 147 |
|
131 |
| - switch (inside ? placement.split(' ')[1] : placement) { |
| 148 | + switch (placement) { |
132 | 149 | case 'bottom':
|
133 | 150 | tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
|
134 | 151 | break
|
|
143 | 160 | break
|
144 | 161 | }
|
145 | 162 |
|
146 |
| - $tip |
147 |
| - .offset(tp) |
148 |
| - .addClass(placement) |
149 |
| - .addClass('in') |
| 163 | + this.applyPlacement(tp, placement) |
| 164 | + this.$element.trigger('shown') |
| 165 | + } |
| 166 | + } |
| 167 | + |
| 168 | + , applyPlacement: function(offset, placement){ |
| 169 | + var $tip = this.tip() |
| 170 | + , width = $tip[0].offsetWidth |
| 171 | + , height = $tip[0].offsetHeight |
| 172 | + , actualWidth |
| 173 | + , actualHeight |
| 174 | + , delta |
| 175 | + , replace |
| 176 | + |
| 177 | + $tip |
| 178 | + .offset(offset) |
| 179 | + .addClass(placement) |
| 180 | + .addClass('in') |
| 181 | + |
| 182 | + actualWidth = $tip[0].offsetWidth |
| 183 | + actualHeight = $tip[0].offsetHeight |
| 184 | + |
| 185 | + if (placement == 'top' && actualHeight != height) { |
| 186 | + offset.top = offset.top + height - actualHeight |
| 187 | + replace = true |
| 188 | + } |
| 189 | + |
| 190 | + if (placement == 'bottom' || placement == 'top') { |
| 191 | + delta = 0 |
| 192 | + |
| 193 | + if (offset.left < 0){ |
| 194 | + delta = offset.left * -2 |
| 195 | + offset.left = 0 |
| 196 | + $tip.offset(offset) |
| 197 | + actualWidth = $tip[0].offsetWidth |
| 198 | + actualHeight = $tip[0].offsetHeight |
| 199 | + } |
| 200 | + |
| 201 | + this.replaceArrow(delta - width + actualWidth, actualWidth, 'left') |
| 202 | + } else { |
| 203 | + this.replaceArrow(actualHeight - height, actualHeight, 'top') |
150 | 204 | }
|
| 205 | + |
| 206 | + if (replace) $tip.offset(offset) |
| 207 | + } |
| 208 | + |
| 209 | + , replaceArrow: function(delta, dimension, position){ |
| 210 | + this |
| 211 | + .arrow() |
| 212 | + .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '') |
151 | 213 | }
|
152 | 214 |
|
153 | 215 | , setContent: function () {
|
|
161 | 223 | , hide: function () {
|
162 | 224 | var that = this
|
163 | 225 | , $tip = this.tip()
|
| 226 | + , e = $.Event('hide') |
| 227 | + |
| 228 | + this.$element.trigger(e) |
| 229 | + if (e.isDefaultPrevented()) return |
164 | 230 |
|
165 | 231 | $tip.removeClass('in')
|
166 | 232 |
|
|
179 | 245 | removeWithAnimation() :
|
180 | 246 | $tip.detach()
|
181 | 247 |
|
| 248 | + this.$element.trigger('hidden') |
| 249 | + |
182 | 250 | return this
|
183 | 251 | }
|
184 | 252 |
|
|
193 | 261 | return this.getTitle()
|
194 | 262 | }
|
195 | 263 |
|
196 |
| - , getPosition: function (inside) { |
197 |
| - return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), { |
198 |
| - width: this.$element[0].offsetWidth |
199 |
| - , height: this.$element[0].offsetHeight |
200 |
| - }) |
| 264 | + , getPosition: function () { |
| 265 | + var el = this.$element[0] |
| 266 | + return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : { |
| 267 | + width: el.offsetWidth |
| 268 | + , height: el.offsetHeight |
| 269 | + }, this.$element.offset()) |
201 | 270 | }
|
202 | 271 |
|
203 | 272 | , getTitle: function () {
|
|
215 | 284 | return this.$tip = this.$tip || $(this.options.template)
|
216 | 285 | }
|
217 | 286 |
|
| 287 | + , arrow: function(){ |
| 288 | + return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow") |
| 289 | + } |
| 290 | + |
218 | 291 | , validate: function () {
|
219 | 292 | if (!this.$element[0].parentNode) {
|
220 | 293 | this.hide()
|
|
236 | 309 | }
|
237 | 310 |
|
238 | 311 | , toggle: function (e) {
|
239 |
| - var self = $(e.currentTarget)[this.type](this._options).data(this.type) |
240 |
| - self[self.tip().hasClass('in') ? 'hide' : 'show']() |
| 312 | + var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this |
| 313 | + self.tip().hasClass('in') ? self.hide() : self.show() |
241 | 314 | }
|
242 | 315 |
|
243 | 316 | , destroy: function () {
|
|
269 | 342 | , placement: 'top'
|
270 | 343 | , selector: false
|
271 | 344 | , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
|
272 |
| - , trigger: 'hover' |
| 345 | + , trigger: 'hover focus' |
273 | 346 | , title: ''
|
274 | 347 | , delay: 0
|
275 | 348 | , html: false
|
| 349 | + , container: false |
276 | 350 | }
|
277 | 351 |
|
278 | 352 |
|
|
285 | 359 | }
|
286 | 360 |
|
287 | 361 | }(window.jQuery);
|
288 |
| - |
|
0 commit comments