comparison static/bootstrap/js/bootstrap-tooltip.js @ 88:a5a3d454eac9

Updated Bootstrap.
author Ludovic Chabant <ludovic@chabant.com>
date Fri, 05 Apr 2013 08:08:12 -0700
parents 130eccd396d8
children
comparison
equal deleted inserted replaced
87:c0cf67362fb1 88:a5a3d454eac9
1 /* =========================================================== 1 /* ===========================================================
2 * bootstrap-tooltip.js v2.2.2 2 * bootstrap-tooltip.js v2.3.1
3 * http://twitter.github.com/bootstrap/javascript.html#tooltips 3 * http://twitter.github.com/bootstrap/javascript.html#tooltips
4 * Inspired by the original jQuery.tipsy by Jason Frame 4 * Inspired by the original jQuery.tipsy by Jason Frame
5 * =========================================================== 5 * ===========================================================
6 * Copyright 2012 Twitter, Inc. 6 * Copyright 2012 Twitter, Inc.
7 * 7 *
36 constructor: Tooltip 36 constructor: Tooltip
37 37
38 , init: function (type, element, options) { 38 , init: function (type, element, options) {
39 var eventIn 39 var eventIn
40 , eventOut 40 , eventOut
41 , triggers
42 , trigger
43 , i
41 44
42 this.type = type 45 this.type = type
43 this.$element = $(element) 46 this.$element = $(element)
44 this.options = this.getOptions(options) 47 this.options = this.getOptions(options)
45 this.enabled = true 48 this.enabled = true
46 49
47 if (this.options.trigger == 'click') { 50 triggers = this.options.trigger.split(' ')
48 this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) 51
49 } else if (this.options.trigger != 'manual') { 52 for (i = triggers.length; i--;) {
50 eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus' 53 trigger = triggers[i]
51 eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur' 54 if (trigger == 'click') {
52 this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) 55 this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
53 this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, 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 this.options.selector ? 64 this.options.selector ?
57 (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : 65 (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
58 this.fixTitle() 66 this.fixTitle()
59 } 67 }
60 68
61 , getOptions: function (options) { 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 if (options.delay && typeof options.delay == 'number') { 72 if (options.delay && typeof options.delay == 'number') {
65 options.delay = { 73 options.delay = {
66 show: options.delay 74 show: options.delay
67 , hide: options.delay 75 , hide: options.delay
70 78
71 return options 79 return options
72 } 80 }
73 81
74 , enter: function (e) { 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 if (!self.options.delay || !self.options.delay.show) return self.show() 93 if (!self.options.delay || !self.options.delay.show) return self.show()
78 94
79 clearTimeout(this.timeout) 95 clearTimeout(this.timeout)
80 self.hoverState = 'in' 96 self.hoverState = 'in'
95 }, self.options.delay.hide) 111 }, self.options.delay.hide)
96 } 112 }
97 113
98 , show: function () { 114 , show: function () {
99 var $tip 115 var $tip
100 , inside
101 , pos 116 , pos
102 , actualWidth 117 , actualWidth
103 , actualHeight 118 , actualHeight
104 , placement 119 , placement
105 , tp 120 , tp
121 , e = $.Event('show')
106 122
107 if (this.hasContent() && this.enabled) { 123 if (this.hasContent() && this.enabled) {
124 this.$element.trigger(e)
125 if (e.isDefaultPrevented()) return
108 $tip = this.tip() 126 $tip = this.tip()
109 this.setContent() 127 this.setContent()
110 128
111 if (this.options.animation) { 129 if (this.options.animation) {
112 $tip.addClass('fade') 130 $tip.addClass('fade')
114 132
115 placement = typeof this.options.placement == 'function' ? 133 placement = typeof this.options.placement == 'function' ?
116 this.options.placement.call(this, $tip[0], this.$element[0]) : 134 this.options.placement.call(this, $tip[0], this.$element[0]) :
117 this.options.placement 135 this.options.placement
118 136
119 inside = /in/.test(placement)
120
121 $tip 137 $tip
122 .detach() 138 .detach()
123 .css({ top: 0, left: 0, display: 'block' }) 139 .css({ top: 0, left: 0, display: 'block' })
124 .insertAfter(this.$element) 140
125 141 this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
126 pos = this.getPosition(inside) 142
143 pos = this.getPosition()
127 144
128 actualWidth = $tip[0].offsetWidth 145 actualWidth = $tip[0].offsetWidth
129 actualHeight = $tip[0].offsetHeight 146 actualHeight = $tip[0].offsetHeight
130 147
131 switch (inside ? placement.split(' ')[1] : placement) { 148 switch (placement) {
132 case 'bottom': 149 case 'bottom':
133 tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2} 150 tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
134 break 151 break
135 case 'top': 152 case 'top':
136 tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2} 153 tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
141 case 'right': 158 case 'right':
142 tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width} 159 tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
143 break 160 break
144 } 161 }
145 162
146 $tip 163 this.applyPlacement(tp, placement)
147 .offset(tp) 164 this.$element.trigger('shown')
148 .addClass(placement) 165 }
149 .addClass('in') 166 }
150 } 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')
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 , setContent: function () { 215 , setContent: function () {
154 var $tip = this.tip() 216 var $tip = this.tip()
155 , title = this.getTitle() 217 , title = this.getTitle()
159 } 221 }
160 222
161 , hide: function () { 223 , hide: function () {
162 var that = this 224 var that = this
163 , $tip = this.tip() 225 , $tip = this.tip()
226 , e = $.Event('hide')
227
228 this.$element.trigger(e)
229 if (e.isDefaultPrevented()) return
164 230
165 $tip.removeClass('in') 231 $tip.removeClass('in')
166 232
167 function removeWithAnimation() { 233 function removeWithAnimation() {
168 var timeout = setTimeout(function () { 234 var timeout = setTimeout(function () {
177 243
178 $.support.transition && this.$tip.hasClass('fade') ? 244 $.support.transition && this.$tip.hasClass('fade') ?
179 removeWithAnimation() : 245 removeWithAnimation() :
180 $tip.detach() 246 $tip.detach()
181 247
248 this.$element.trigger('hidden')
249
182 return this 250 return this
183 } 251 }
184 252
185 , fixTitle: function () { 253 , fixTitle: function () {
186 var $e = this.$element 254 var $e = this.$element
191 259
192 , hasContent: function () { 260 , hasContent: function () {
193 return this.getTitle() 261 return this.getTitle()
194 } 262 }
195 263
196 , getPosition: function (inside) { 264 , getPosition: function () {
197 return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), { 265 var el = this.$element[0]
198 width: this.$element[0].offsetWidth 266 return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
199 , height: this.$element[0].offsetHeight 267 width: el.offsetWidth
200 }) 268 , height: el.offsetHeight
269 }, this.$element.offset())
201 } 270 }
202 271
203 , getTitle: function () { 272 , getTitle: function () {
204 var title 273 var title
205 , $e = this.$element 274 , $e = this.$element
211 return title 280 return title
212 } 281 }
213 282
214 , tip: function () { 283 , tip: function () {
215 return this.$tip = this.$tip || $(this.options.template) 284 return this.$tip = this.$tip || $(this.options.template)
285 }
286
287 , arrow: function(){
288 return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
216 } 289 }
217 290
218 , validate: function () { 291 , validate: function () {
219 if (!this.$element[0].parentNode) { 292 if (!this.$element[0].parentNode) {
220 this.hide() 293 this.hide()
234 , toggleEnabled: function () { 307 , toggleEnabled: function () {
235 this.enabled = !this.enabled 308 this.enabled = !this.enabled
236 } 309 }
237 310
238 , toggle: function (e) { 311 , toggle: function (e) {
239 var self = $(e.currentTarget)[this.type](this._options).data(this.type) 312 var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this
240 self[self.tip().hasClass('in') ? 'hide' : 'show']() 313 self.tip().hasClass('in') ? self.hide() : self.show()
241 } 314 }
242 315
243 , destroy: function () { 316 , destroy: function () {
244 this.hide().$element.off('.' + this.type).removeData(this.type) 317 this.hide().$element.off('.' + this.type).removeData(this.type)
245 } 318 }
267 $.fn.tooltip.defaults = { 340 $.fn.tooltip.defaults = {
268 animation: true 341 animation: true
269 , placement: 'top' 342 , placement: 'top'
270 , selector: false 343 , selector: false
271 , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>' 344 , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
272 , trigger: 'hover' 345 , trigger: 'hover focus'
273 , title: '' 346 , title: ''
274 , delay: 0 347 , delay: 0
275 , html: false 348 , html: false
349 , container: false
276 } 350 }
277 351
278 352
279 /* TOOLTIP NO CONFLICT 353 /* TOOLTIP NO CONFLICT
280 * =================== */ 354 * =================== */