less-rails-bootstrap 3.2.0 → 3.3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/app/assets/fonts/twitter/bootstrap/glyphicons-halflings-regular.svg +1 -1
  4. data/app/assets/javascripts/twitter/bootstrap/affix.js +43 -23
  5. data/app/assets/javascripts/twitter/bootstrap/alert.js +6 -4
  6. data/app/assets/javascripts/twitter/bootstrap/button.js +16 -10
  7. data/app/assets/javascripts/twitter/bootstrap/carousel.js +26 -10
  8. data/app/assets/javascripts/twitter/bootstrap/collapse.js +69 -28
  9. data/app/assets/javascripts/twitter/bootstrap/dropdown.js +20 -10
  10. data/app/assets/javascripts/twitter/bootstrap/modal.js +22 -21
  11. data/app/assets/javascripts/twitter/bootstrap/popover.js +13 -7
  12. data/app/assets/javascripts/twitter/bootstrap/scrollspy.js +12 -7
  13. data/app/assets/javascripts/twitter/bootstrap/tab.js +41 -16
  14. data/app/assets/javascripts/twitter/bootstrap/tooltip.js +60 -39
  15. data/app/assets/javascripts/twitter/bootstrap/transition.js +1 -1
  16. data/app/frameworks/twitter/bootstrap/button-groups.less +16 -9
  17. data/app/frameworks/twitter/bootstrap/buttons.less +7 -4
  18. data/app/frameworks/twitter/bootstrap/carousel.less +24 -0
  19. data/app/frameworks/twitter/bootstrap/code.less +1 -0
  20. data/app/frameworks/twitter/bootstrap/component-animations.less +5 -2
  21. data/app/frameworks/twitter/bootstrap/dropdowns.less +3 -5
  22. data/app/frameworks/twitter/bootstrap/forms.less +44 -25
  23. data/app/frameworks/twitter/bootstrap/glyphicons.less +2 -1
  24. data/app/frameworks/twitter/bootstrap/jumbotron.less +4 -4
  25. data/app/frameworks/twitter/bootstrap/list-group.less +1 -0
  26. data/app/frameworks/twitter/bootstrap/media.less +27 -36
  27. data/app/frameworks/twitter/bootstrap/mixins/buttons.less +2 -0
  28. data/app/frameworks/twitter/bootstrap/mixins/forms.less +5 -1
  29. data/app/frameworks/twitter/bootstrap/mixins/image.less +0 -1
  30. data/app/frameworks/twitter/bootstrap/mixins/labels.less +1 -1
  31. data/app/frameworks/twitter/bootstrap/mixins/vendor-prefixes.less +6 -3
  32. data/app/frameworks/twitter/bootstrap/modals.less +2 -3
  33. data/app/frameworks/twitter/bootstrap/navbar.less +29 -25
  34. data/app/frameworks/twitter/bootstrap/navs.less +3 -1
  35. data/app/frameworks/twitter/bootstrap/normalize.less +5 -3
  36. data/app/frameworks/twitter/bootstrap/pager.less +1 -2
  37. data/app/frameworks/twitter/bootstrap/pagination.less +1 -1
  38. data/app/frameworks/twitter/bootstrap/panels.less +21 -3
  39. data/app/frameworks/twitter/bootstrap/popovers.less +5 -4
  40. data/app/frameworks/twitter/bootstrap/print.less +102 -96
  41. data/app/frameworks/twitter/bootstrap/progress-bars.less +1 -19
  42. data/app/frameworks/twitter/bootstrap/responsive-embed.less +2 -1
  43. data/app/frameworks/twitter/bootstrap/scaffolding.less +1 -1
  44. data/app/frameworks/twitter/bootstrap/tables.less +11 -10
  45. data/app/frameworks/twitter/bootstrap/theme.less +12 -10
  46. data/app/frameworks/twitter/bootstrap/thumbnails.less +1 -1
  47. data/app/frameworks/twitter/bootstrap/type.less +0 -11
  48. data/app/frameworks/twitter/bootstrap/utilities.less +0 -1
  49. data/app/frameworks/twitter/bootstrap/variables.less +29 -19
  50. data/lib/less/rails/bootstrap/version.rb +1 -1
  51. metadata +19 -19
@@ -1,5 +1,5 @@
1
1
  /* ========================================================================
2
- * Bootstrap: dropdown.js v3.2.0
2
+ * Bootstrap: dropdown.js v3.3.0
3
3
  * http://getbootstrap.com/javascript/#dropdowns
4
4
  * ========================================================================
5
5
  * Copyright 2011-2014 Twitter, Inc.
@@ -19,7 +19,7 @@
19
19
  $(element).on('click.bs.dropdown', this.toggle)
20
20
  }
21
21
 
22
- Dropdown.VERSION = '3.2.0'
22
+ Dropdown.VERSION = '3.3.0'
23
23
 
24
24
  Dropdown.prototype.toggle = function (e) {
25
25
  var $this = $(this)
@@ -42,7 +42,9 @@
42
42
 
43
43
  if (e.isDefaultPrevented()) return
44
44
 
45
- $this.trigger('focus')
45
+ $this
46
+ .trigger('focus')
47
+ .attr('aria-expanded', 'true')
46
48
 
47
49
  $parent
48
50
  .toggleClass('open')
@@ -53,7 +55,7 @@
53
55
  }
54
56
 
55
57
  Dropdown.prototype.keydown = function (e) {
56
- if (!/(38|40|27)/.test(e.keyCode)) return
58
+ if (!/(38|40|27|32)/.test(e.which)) return
57
59
 
58
60
  var $this = $(this)
59
61
 
@@ -65,7 +67,7 @@
65
67
  var $parent = getParent($this)
66
68
  var isActive = $parent.hasClass('open')
67
69
 
68
- if (!isActive || (isActive && e.keyCode == 27)) {
70
+ if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
69
71
  if (e.which == 27) $parent.find(toggle).trigger('focus')
70
72
  return $this.trigger('click')
71
73
  }
@@ -75,10 +77,10 @@
75
77
 
76
78
  if (!$items.length) return
77
79
 
78
- var index = $items.index($items.filter(':focus'))
80
+ var index = $items.index(e.target)
79
81
 
80
- if (e.keyCode == 38 && index > 0) index-- // up
81
- if (e.keyCode == 40 && index < $items.length - 1) index++ // down
82
+ if (e.which == 38 && index > 0) index-- // up
83
+ if (e.which == 40 && index < $items.length - 1) index++ // down
82
84
  if (!~index) index = 0
83
85
 
84
86
  $items.eq(index).trigger('focus')
@@ -88,11 +90,17 @@
88
90
  if (e && e.which === 3) return
89
91
  $(backdrop).remove()
90
92
  $(toggle).each(function () {
91
- var $parent = getParent($(this))
93
+ var $this = $(this)
94
+ var $parent = getParent($this)
92
95
  var relatedTarget = { relatedTarget: this }
96
+
93
97
  if (!$parent.hasClass('open')) return
98
+
94
99
  $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
100
+
95
101
  if (e.isDefaultPrevented()) return
102
+
103
+ $this.attr('aria-expanded', 'false')
96
104
  $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
97
105
  })
98
106
  }
@@ -146,6 +154,8 @@
146
154
  .on('click.bs.dropdown.data-api', clearMenus)
147
155
  .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
148
156
  .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
149
- .on('keydown.bs.dropdown.data-api', toggle + ', [role="menu"], [role="listbox"]', Dropdown.prototype.keydown)
157
+ .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
158
+ .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)
159
+ .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)
150
160
 
151
161
  }(jQuery);
@@ -1,5 +1,5 @@
1
1
  /* ========================================================================
2
- * Bootstrap: modal.js v3.2.0
2
+ * Bootstrap: modal.js v3.3.0
3
3
  * http://getbootstrap.com/javascript/#modals
4
4
  * ========================================================================
5
5
  * Copyright 2011-2014 Twitter, Inc.
@@ -30,7 +30,10 @@
30
30
  }
31
31
  }
32
32
 
33
- Modal.VERSION = '3.2.0'
33
+ Modal.VERSION = '3.3.0'
34
+
35
+ Modal.TRANSITION_DURATION = 300
36
+ Modal.BACKDROP_TRANSITION_DURATION = 150
34
37
 
35
38
  Modal.DEFAULTS = {
36
39
  backdrop: true,
@@ -88,7 +91,7 @@
88
91
  .one('bsTransitionEnd', function () {
89
92
  that.$element.trigger('focus').trigger(e)
90
93
  })
91
- .emulateTransitionEnd(300) :
94
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
92
95
  that.$element.trigger('focus').trigger(e)
93
96
  })
94
97
  }
@@ -104,9 +107,6 @@
104
107
 
105
108
  this.isShown = false
106
109
 
107
- this.$body.removeClass('modal-open')
108
-
109
- this.resetScrollbar()
110
110
  this.escape()
111
111
 
112
112
  $(document).off('focusin.bs.modal')
@@ -119,7 +119,7 @@
119
119
  $.support.transition && this.$element.hasClass('fade') ?
120
120
  this.$element
121
121
  .one('bsTransitionEnd', $.proxy(this.hideModal, this))
122
- .emulateTransitionEnd(300) :
122
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
123
123
  this.hideModal()
124
124
  }
125
125
 
@@ -135,11 +135,11 @@
135
135
 
136
136
  Modal.prototype.escape = function () {
137
137
  if (this.isShown && this.options.keyboard) {
138
- this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
138
+ this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
139
139
  e.which == 27 && this.hide()
140
140
  }, this))
141
141
  } else if (!this.isShown) {
142
- this.$element.off('keyup.dismiss.bs.modal')
142
+ this.$element.off('keydown.dismiss.bs.modal')
143
143
  }
144
144
  }
145
145
 
@@ -147,6 +147,8 @@
147
147
  var that = this
148
148
  this.$element.hide()
149
149
  this.backdrop(function () {
150
+ that.$body.removeClass('modal-open')
151
+ that.resetScrollbar()
150
152
  that.$element.trigger('hidden.bs.modal')
151
153
  })
152
154
  }
@@ -164,14 +166,13 @@
164
166
  var doAnimate = $.support.transition && animate
165
167
 
166
168
  this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
167
- .appendTo(this.$body)
168
-
169
- this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
170
- if (e.target !== e.currentTarget) return
171
- this.options.backdrop == 'static'
172
- ? this.$element[0].focus.call(this.$element[0])
173
- : this.hide.call(this)
174
- }, this))
169
+ .prependTo(this.$element)
170
+ .on('click.dismiss.bs.modal', $.proxy(function (e) {
171
+ if (e.target !== e.currentTarget) return
172
+ this.options.backdrop == 'static'
173
+ ? this.$element[0].focus.call(this.$element[0])
174
+ : this.hide.call(this)
175
+ }, this))
175
176
 
176
177
  if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
177
178
 
@@ -182,7 +183,7 @@
182
183
  doAnimate ?
183
184
  this.$backdrop
184
185
  .one('bsTransitionEnd', callback)
185
- .emulateTransitionEnd(150) :
186
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
186
187
  callback()
187
188
 
188
189
  } else if (!this.isShown && this.$backdrop) {
@@ -195,7 +196,7 @@
195
196
  $.support.transition && this.$element.hasClass('fade') ?
196
197
  this.$backdrop
197
198
  .one('bsTransitionEnd', callbackRemove)
198
- .emulateTransitionEnd(150) :
199
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
199
200
  callbackRemove()
200
201
 
201
202
  } else if (callback) {
@@ -204,8 +205,7 @@
204
205
  }
205
206
 
206
207
  Modal.prototype.checkScrollbar = function () {
207
- if (document.body.clientWidth >= window.innerWidth) return
208
- this.scrollbarWidth = this.scrollbarWidth || this.measureScrollbar()
208
+ this.scrollbarWidth = this.measureScrollbar()
209
209
  }
210
210
 
211
211
  Modal.prototype.setScrollbar = function () {
@@ -218,6 +218,7 @@
218
218
  }
219
219
 
220
220
  Modal.prototype.measureScrollbar = function () { // thx walsh
221
+ if (document.body.clientWidth >= window.innerWidth) return 0
221
222
  var scrollDiv = document.createElement('div')
222
223
  scrollDiv.className = 'modal-scrollbar-measure'
223
224
  this.$body.append(scrollDiv)
@@ -1,5 +1,5 @@
1
1
  /* ========================================================================
2
- * Bootstrap: popover.js v3.2.0
2
+ * Bootstrap: popover.js v3.3.0
3
3
  * http://getbootstrap.com/javascript/#popovers
4
4
  * ========================================================================
5
5
  * Copyright 2011-2014 Twitter, Inc.
@@ -19,7 +19,7 @@
19
19
 
20
20
  if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
21
21
 
22
- Popover.VERSION = '3.2.0'
22
+ Popover.VERSION = '3.3.0'
23
23
 
24
24
  Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
25
25
  placement: 'right',
@@ -46,7 +46,7 @@
46
46
  var content = this.getContent()
47
47
 
48
48
  $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
49
- $tip.find('.popover-content').empty()[ // we use append for html objects to maintain js events
49
+ $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
50
50
  this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
51
51
  ](content)
52
52
 
@@ -86,12 +86,18 @@
86
86
 
87
87
  function Plugin(option) {
88
88
  return this.each(function () {
89
- var $this = $(this)
90
- var data = $this.data('bs.popover')
91
- var options = typeof option == 'object' && option
89
+ var $this = $(this)
90
+ var data = $this.data('bs.popover')
91
+ var options = typeof option == 'object' && option
92
+ var selector = options && options.selector
92
93
 
93
94
  if (!data && option == 'destroy') return
94
- if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
95
+ if (selector) {
96
+ if (!data) $this.data('bs.popover', (data = {}))
97
+ if (!data[selector]) data[selector] = new Popover(this, options)
98
+ } else {
99
+ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
100
+ }
95
101
  if (typeof option == 'string') data[option]()
96
102
  })
97
103
  }
@@ -1,5 +1,5 @@
1
1
  /* ========================================================================
2
- * Bootstrap: scrollspy.js v3.2.0
2
+ * Bootstrap: scrollspy.js v3.3.0
3
3
  * http://getbootstrap.com/javascript/#scrollspy
4
4
  * ========================================================================
5
5
  * Copyright 2011-2014 Twitter, Inc.
@@ -30,7 +30,7 @@
30
30
  this.process()
31
31
  }
32
32
 
33
- ScrollSpy.VERSION = '3.2.0'
33
+ ScrollSpy.VERSION = '3.3.0'
34
34
 
35
35
  ScrollSpy.DEFAULTS = {
36
36
  offset: 10
@@ -91,8 +91,9 @@
91
91
  return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
92
92
  }
93
93
 
94
- if (activeTarget && scrollTop <= offsets[0]) {
95
- return activeTarget != (i = targets[0]) && this.activate(i)
94
+ if (activeTarget && scrollTop < offsets[0]) {
95
+ this.activeTarget = null
96
+ return this.clear()
96
97
  }
97
98
 
98
99
  for (i = offsets.length; i--;) {
@@ -106,9 +107,7 @@
106
107
  ScrollSpy.prototype.activate = function (target) {
107
108
  this.activeTarget = target
108
109
 
109
- $(this.selector)
110
- .parentsUntil(this.options.target, '.active')
111
- .removeClass('active')
110
+ this.clear()
112
111
 
113
112
  var selector = this.selector +
114
113
  '[data-target="' + target + '"],' +
@@ -127,6 +126,12 @@
127
126
  active.trigger('activate.bs.scrollspy')
128
127
  }
129
128
 
129
+ ScrollSpy.prototype.clear = function () {
130
+ $(this.selector)
131
+ .parentsUntil(this.options.target, '.active')
132
+ .removeClass('active')
133
+ }
134
+
130
135
 
131
136
  // SCROLLSPY PLUGIN DEFINITION
132
137
  // ===========================
@@ -1,5 +1,5 @@
1
1
  /* ========================================================================
2
- * Bootstrap: tab.js v3.2.0
2
+ * Bootstrap: tab.js v3.3.0
3
3
  * http://getbootstrap.com/javascript/#tabs
4
4
  * ========================================================================
5
5
  * Copyright 2011-2014 Twitter, Inc.
@@ -17,7 +17,9 @@
17
17
  this.element = $(element)
18
18
  }
19
19
 
20
- Tab.VERSION = '3.2.0'
20
+ Tab.VERSION = '3.3.0'
21
+
22
+ Tab.TRANSITION_DURATION = 150
21
23
 
22
24
  Tab.prototype.show = function () {
23
25
  var $this = this.element
@@ -31,22 +33,30 @@
31
33
 
32
34
  if ($this.parent('li').hasClass('active')) return
33
35
 
34
- var previous = $ul.find('.active:last a')[0]
35
- var e = $.Event('show.bs.tab', {
36
- relatedTarget: previous
36
+ var $previous = $ul.find('.active:last a')
37
+ var hideEvent = $.Event('hide.bs.tab', {
38
+ relatedTarget: $this[0]
39
+ })
40
+ var showEvent = $.Event('show.bs.tab', {
41
+ relatedTarget: $previous[0]
37
42
  })
38
43
 
39
- $this.trigger(e)
44
+ $previous.trigger(hideEvent)
45
+ $this.trigger(showEvent)
40
46
 
41
- if (e.isDefaultPrevented()) return
47
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
42
48
 
43
49
  var $target = $(selector)
44
50
 
45
51
  this.activate($this.closest('li'), $ul)
46
52
  this.activate($target, $target.parent(), function () {
53
+ $previous.trigger({
54
+ type: 'hidden.bs.tab',
55
+ relatedTarget: $this[0]
56
+ })
47
57
  $this.trigger({
48
58
  type: 'shown.bs.tab',
49
- relatedTarget: previous
59
+ relatedTarget: $previous[0]
50
60
  })
51
61
  })
52
62
  }
@@ -55,15 +65,21 @@
55
65
  var $active = container.find('> .active')
56
66
  var transition = callback
57
67
  && $.support.transition
58
- && $active.hasClass('fade')
68
+ && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length)
59
69
 
60
70
  function next() {
61
71
  $active
62
72
  .removeClass('active')
63
73
  .find('> .dropdown-menu > .active')
64
- .removeClass('active')
74
+ .removeClass('active')
75
+ .end()
76
+ .find('[data-toggle="tab"]')
77
+ .attr('aria-expanded', false)
65
78
 
66
- element.addClass('active')
79
+ element
80
+ .addClass('active')
81
+ .find('[data-toggle="tab"]')
82
+ .attr('aria-expanded', true)
67
83
 
68
84
  if (transition) {
69
85
  element[0].offsetWidth // reflow for transition
@@ -73,16 +89,21 @@
73
89
  }
74
90
 
75
91
  if (element.parent('.dropdown-menu')) {
76
- element.closest('li.dropdown').addClass('active')
92
+ element
93
+ .closest('li.dropdown')
94
+ .addClass('active')
95
+ .end()
96
+ .find('[data-toggle="tab"]')
97
+ .attr('aria-expanded', true)
77
98
  }
78
99
 
79
100
  callback && callback()
80
101
  }
81
102
 
82
- transition ?
103
+ $active.length && transition ?
83
104
  $active
84
105
  .one('bsTransitionEnd', next)
85
- .emulateTransitionEnd(150) :
106
+ .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
86
107
  next()
87
108
 
88
109
  $active.removeClass('in')
@@ -120,9 +141,13 @@
120
141
  // TAB DATA-API
121
142
  // ============
122
143
 
123
- $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
144
+ var clickHandler = function (e) {
124
145
  e.preventDefault()
125
146
  Plugin.call($(this), 'show')
126
- })
147
+ }
148
+
149
+ $(document)
150
+ .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
151
+ .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
127
152
 
128
153
  }(jQuery);
@@ -1,5 +1,5 @@
1
1
  /* ========================================================================
2
- * Bootstrap: tooltip.js v3.2.0
2
+ * Bootstrap: tooltip.js v3.3.0
3
3
  * http://getbootstrap.com/javascript/#tooltip
4
4
  * Inspired by the original jQuery.tipsy by Jason Frame
5
5
  * ========================================================================
@@ -25,7 +25,9 @@
25
25
  this.init('tooltip', element, options)
26
26
  }
27
27
 
28
- Tooltip.VERSION = '3.2.0'
28
+ Tooltip.VERSION = '3.3.0'
29
+
30
+ Tooltip.TRANSITION_DURATION = 150
29
31
 
30
32
  Tooltip.DEFAULTS = {
31
33
  animation: true,
@@ -103,6 +105,11 @@
103
105
  var self = obj instanceof this.constructor ?
104
106
  obj : $(obj.currentTarget).data('bs.' + this.type)
105
107
 
108
+ if (self && self.$tip && self.$tip.is(':visible')) {
109
+ self.hoverState = 'in'
110
+ return
111
+ }
112
+
106
113
  if (!self) {
107
114
  self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
108
115
  $(obj.currentTarget).data('bs.' + this.type, self)
@@ -145,7 +152,7 @@
145
152
  if (this.hasContent() && this.enabled) {
146
153
  this.$element.trigger(e)
147
154
 
148
- var inDom = $.contains(document.documentElement, this.$element[0])
155
+ var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
149
156
  if (e.isDefaultPrevented() || !inDom) return
150
157
  var that = this
151
158
 
@@ -181,13 +188,13 @@
181
188
 
182
189
  if (autoPlace) {
183
190
  var orgPlacement = placement
184
- var $parent = this.$element.parent()
185
- var parentDim = this.getPosition($parent)
191
+ var $container = this.options.container ? $(this.options.container) : this.$element.parent()
192
+ var containerDim = this.getPosition($container)
186
193
 
187
- placement = placement == 'bottom' && pos.top + pos.height + actualHeight - parentDim.scroll > parentDim.height ? 'top' :
188
- placement == 'top' && pos.top - parentDim.scroll - actualHeight < 0 ? 'bottom' :
189
- placement == 'right' && pos.right + actualWidth > parentDim.width ? 'left' :
190
- placement == 'left' && pos.left - actualWidth < parentDim.left ? 'right' :
194
+ placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
195
+ placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
196
+ placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
197
+ placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
191
198
  placement
192
199
 
193
200
  $tip
@@ -200,14 +207,17 @@
200
207
  this.applyPlacement(calculatedOffset, placement)
201
208
 
202
209
  var complete = function () {
210
+ var prevHoverState = that.hoverState
203
211
  that.$element.trigger('shown.bs.' + that.type)
204
212
  that.hoverState = null
213
+
214
+ if (prevHoverState == 'out') that.leave(that)
205
215
  }
206
216
 
207
217
  $.support.transition && this.$tip.hasClass('fade') ?
208
218
  $tip
209
219
  .one('bsTransitionEnd', complete)
210
- .emulateTransitionEnd(150) :
220
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
211
221
  complete()
212
222
  }
213
223
  }
@@ -254,16 +264,18 @@
254
264
  if (delta.left) offset.left += delta.left
255
265
  else offset.top += delta.top
256
266
 
257
- var arrowDelta = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
258
- var arrowPosition = delta.left ? 'left' : 'top'
259
- var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight'
267
+ var isVertical = /top|bottom/.test(placement)
268
+ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
269
+ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
260
270
 
261
271
  $tip.offset(offset)
262
- this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
272
+ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
263
273
  }
264
274
 
265
- Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
266
- this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
275
+ Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) {
276
+ this.arrow()
277
+ .css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
278
+ .css(isHorizontal ? 'top' : 'left', '')
267
279
  }
268
280
 
269
281
  Tooltip.prototype.setContent = function () {
@@ -274,16 +286,17 @@
274
286
  $tip.removeClass('fade in top bottom left right')
275
287
  }
276
288
 
277
- Tooltip.prototype.hide = function () {
289
+ Tooltip.prototype.hide = function (callback) {
278
290
  var that = this
279
291
  var $tip = this.tip()
280
292
  var e = $.Event('hide.bs.' + this.type)
281
293
 
282
- this.$element.removeAttr('aria-describedby')
283
-
284
294
  function complete() {
285
295
  if (that.hoverState != 'in') $tip.detach()
286
- that.$element.trigger('hidden.bs.' + that.type)
296
+ that.$element
297
+ .removeAttr('aria-describedby')
298
+ .trigger('hidden.bs.' + that.type)
299
+ callback && callback()
287
300
  }
288
301
 
289
302
  this.$element.trigger(e)
@@ -295,7 +308,7 @@
295
308
  $.support.transition && this.$tip.hasClass('fade') ?
296
309
  $tip
297
310
  .one('bsTransitionEnd', complete)
298
- .emulateTransitionEnd(150) :
311
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
299
312
  complete()
300
313
 
301
314
  this.hoverState = null
@@ -316,13 +329,20 @@
316
329
 
317
330
  Tooltip.prototype.getPosition = function ($element) {
318
331
  $element = $element || this.$element
332
+
319
333
  var el = $element[0]
320
334
  var isBody = el.tagName == 'BODY'
321
- return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, {
322
- scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(),
323
- width: isBody ? $(window).width() : $element.outerWidth(),
324
- height: isBody ? $(window).height() : $element.outerHeight()
325
- }, isBody ? { top: 0, left: 0 } : $element.offset())
335
+
336
+ var elRect = el.getBoundingClientRect()
337
+ if (elRect.width == null) {
338
+ // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
339
+ elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
340
+ }
341
+ var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
342
+ var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
343
+ var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
344
+
345
+ return $.extend({}, elRect, scroll, outerDims, elOffset)
326
346
  }
327
347
 
328
348
  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
@@ -386,14 +406,6 @@
386
406
  return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
387
407
  }
388
408
 
389
- Tooltip.prototype.validate = function () {
390
- if (!this.$element[0].parentNode) {
391
- this.hide()
392
- this.$element = null
393
- this.options = null
394
- }
395
- }
396
-
397
409
  Tooltip.prototype.enable = function () {
398
410
  this.enabled = true
399
411
  }
@@ -420,8 +432,11 @@
420
432
  }
421
433
 
422
434
  Tooltip.prototype.destroy = function () {
435
+ var that = this
423
436
  clearTimeout(this.timeout)
424
- this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
437
+ this.hide(function () {
438
+ that.$element.off('.' + that.type).removeData('bs.' + that.type)
439
+ })
425
440
  }
426
441
 
427
442
 
@@ -430,12 +445,18 @@
430
445
 
431
446
  function Plugin(option) {
432
447
  return this.each(function () {
433
- var $this = $(this)
434
- var data = $this.data('bs.tooltip')
435
- var options = typeof option == 'object' && option
448
+ var $this = $(this)
449
+ var data = $this.data('bs.tooltip')
450
+ var options = typeof option == 'object' && option
451
+ var selector = options && options.selector
436
452
 
437
453
  if (!data && option == 'destroy') return
438
- if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
454
+ if (selector) {
455
+ if (!data) $this.data('bs.tooltip', (data = {}))
456
+ if (!data[selector]) data[selector] = new Tooltip(this, options)
457
+ } else {
458
+ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
459
+ }
439
460
  if (typeof option == 'string') data[option]()
440
461
  })
441
462
  }