twitter-bootstrap-rails 3.2.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of twitter-bootstrap-rails might be problematic. Click here for more details.

Files changed (148) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +263 -247
  3. data/app/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  4. data/app/assets/fonts/glyphicons-halflings-regular.svg +273 -214
  5. data/app/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  6. data/app/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  7. data/app/assets/fonts/glyphicons-halflings-regular.woff2 +0 -0
  8. data/app/assets/javascripts/twitter/bootstrap/affix.js +60 -35
  9. data/app/assets/javascripts/twitter/bootstrap/alert.js +15 -9
  10. data/app/assets/javascripts/twitter/bootstrap/button.js +33 -20
  11. data/app/assets/javascripts/twitter/bootstrap/carousel.js +79 -47
  12. data/app/assets/javascripts/twitter/bootstrap/collapse.js +84 -43
  13. data/app/assets/javascripts/twitter/bootstrap/dropdown.js +65 -47
  14. data/app/assets/javascripts/twitter/bootstrap/modal.js +138 -44
  15. data/app/assets/javascripts/twitter/bootstrap/popover.js +12 -14
  16. data/app/assets/javascripts/twitter/bootstrap/scrollspy.js +57 -38
  17. data/app/assets/javascripts/twitter/bootstrap/tab.js +54 -24
  18. data/app/assets/javascripts/twitter/bootstrap/tooltip.js +202 -87
  19. data/app/assets/javascripts/twitter/bootstrap/transition.js +19 -8
  20. data/app/assets/stylesheets/twitter-bootstrap-static/bootstrap.css.erb +1 -1
  21. data/app/assets/stylesheets/twitter-bootstrap-static/sprites.css.erb +1 -1
  22. data/app/helpers/bootstrap_flash_helper.rb +8 -3
  23. data/app/helpers/form_errors_helper.rb +11 -11
  24. data/app/helpers/glyph_helper.rb +7 -3
  25. data/app/helpers/modal_helper.rb +9 -13
  26. data/app/helpers/navbar_helper.rb +5 -2
  27. data/app/helpers/twitter_breadcrumbs_helper.rb +4 -1
  28. data/app/views/twitter-bootstrap/_breadcrumbs.html.erb +5 -5
  29. data/lib/generators/bootstrap/install/templates/bootstrap_and_overrides.css +3 -4
  30. data/lib/generators/bootstrap/install/templates/bootstrap_and_overrides.less +7 -15
  31. data/lib/generators/bootstrap/layout/templates/layout.html.erb +5 -5
  32. data/lib/generators/bootstrap/layout/templates/layout.html.haml +3 -3
  33. data/lib/generators/bootstrap/layout/templates/layout.html.slim +3 -5
  34. data/lib/generators/bootstrap/themed/templates/_form.html.erb +12 -9
  35. data/lib/generators/bootstrap/themed/templates/_form.html.haml +7 -5
  36. data/lib/generators/bootstrap/themed/templates/_form.html.slim +8 -6
  37. data/lib/generators/bootstrap/themed/templates/edit.html.slim +1 -1
  38. data/lib/generators/bootstrap/themed/templates/index.html.erb +2 -0
  39. data/lib/generators/bootstrap/themed/templates/index.html.haml +1 -0
  40. data/lib/generators/bootstrap/themed/templates/index.html.slim +4 -3
  41. data/lib/generators/bootstrap/themed/templates/new.html.slim +1 -1
  42. data/lib/generators/bootstrap/themed/templates/show.html.slim +1 -1
  43. data/lib/generators/bootstrap/themed/templates/simple_form/_form.html.haml +1 -1
  44. data/lib/twitter/bootstrap/rails/breadcrumbs.rb +24 -13
  45. data/lib/twitter/bootstrap/rails/engine.rb +17 -12
  46. data/lib/twitter/bootstrap/rails/version.rb +1 -1
  47. data/spec/lib/breadcrumbs_spec.rb +42 -17
  48. data/spec/lib/twitter_bootstrap_rails/badge_label_helper_spec.rb +10 -4
  49. data/spec/lib/twitter_bootstrap_rails/bootstrap_flash_helper_spec.rb +128 -0
  50. data/spec/lib/twitter_bootstrap_rails/form_errors_helper_spec.rb +148 -0
  51. data/spec/lib/twitter_bootstrap_rails/glyph_helper_spec.rb +24 -0
  52. data/spec/lib/twitter_bootstrap_rails/modal_helper_spec.rb +2 -2
  53. data/spec/lib/twitter_bootstrap_rails/navbar_helper_spec.rb +62 -12
  54. data/spec/spec_helper.rb +5 -1
  55. data/vendor/assets/stylesheets/twitter-bootstrap-static/bootstrap.css.erb +1 -3
  56. data/vendor/toolkit/twitter/bootstrap/alerts.less +5 -0
  57. data/vendor/toolkit/twitter/bootstrap/badges.less +14 -3
  58. data/vendor/toolkit/twitter/bootstrap/bootstrap.less +6 -1
  59. data/vendor/toolkit/twitter/bootstrap/breadcrumbs.less +0 -0
  60. data/vendor/toolkit/twitter/bootstrap/button-groups.less +13 -14
  61. data/vendor/toolkit/twitter/bootstrap/buttons.less +14 -7
  62. data/vendor/toolkit/twitter/bootstrap/carousel.less +34 -7
  63. data/vendor/toolkit/twitter/bootstrap/close.less +1 -0
  64. data/vendor/toolkit/twitter/bootstrap/code.less +0 -0
  65. data/vendor/toolkit/twitter/bootstrap/component-animations.less +3 -1
  66. data/vendor/toolkit/twitter/bootstrap/dropdowns.less +9 -8
  67. data/vendor/toolkit/twitter/bootstrap/forms.less +110 -41
  68. data/vendor/toolkit/twitter/bootstrap/glyphicons.less +75 -3
  69. data/vendor/toolkit/twitter/bootstrap/grid.less +0 -0
  70. data/vendor/toolkit/twitter/bootstrap/input-groups.less +8 -3
  71. data/vendor/toolkit/twitter/bootstrap/jumbotron.less +10 -4
  72. data/vendor/toolkit/twitter/bootstrap/labels.less +0 -0
  73. data/vendor/toolkit/twitter/bootstrap/list-group.less +11 -13
  74. data/vendor/toolkit/twitter/bootstrap/media.less +40 -30
  75. data/vendor/toolkit/twitter/bootstrap/mixins/alerts.less +0 -0
  76. data/vendor/toolkit/twitter/bootstrap/mixins/background-variant.less +2 -1
  77. data/vendor/toolkit/twitter/bootstrap/mixins/border-radius.less +0 -0
  78. data/vendor/toolkit/twitter/bootstrap/mixins/buttons.less +19 -6
  79. data/vendor/toolkit/twitter/bootstrap/mixins/center-block.less +0 -0
  80. data/vendor/toolkit/twitter/bootstrap/mixins/clearfix.less +0 -0
  81. data/vendor/toolkit/twitter/bootstrap/mixins/forms.less +0 -0
  82. data/vendor/toolkit/twitter/bootstrap/mixins/gradients.less +0 -0
  83. data/vendor/toolkit/twitter/bootstrap/mixins/grid-framework.less +4 -4
  84. data/vendor/toolkit/twitter/bootstrap/mixins/grid.less +4 -4
  85. data/vendor/toolkit/twitter/bootstrap/mixins/hide-text.less +2 -2
  86. data/vendor/toolkit/twitter/bootstrap/mixins/image.less +0 -0
  87. data/vendor/toolkit/twitter/bootstrap/mixins/labels.less +1 -1
  88. data/vendor/toolkit/twitter/bootstrap/mixins/list-group.less +2 -1
  89. data/vendor/toolkit/twitter/bootstrap/mixins/nav-divider.less +0 -0
  90. data/vendor/toolkit/twitter/bootstrap/mixins/nav-vertical-align.less +0 -0
  91. data/vendor/toolkit/twitter/bootstrap/mixins/opacity.less +0 -0
  92. data/vendor/toolkit/twitter/bootstrap/mixins/pagination.less +2 -1
  93. data/vendor/toolkit/twitter/bootstrap/mixins/panels.less +0 -0
  94. data/vendor/toolkit/twitter/bootstrap/mixins/progress-bar.less +0 -0
  95. data/vendor/toolkit/twitter/bootstrap/mixins/reset-filter.less +0 -0
  96. data/vendor/toolkit/twitter/bootstrap/mixins/reset-text.less +18 -0
  97. data/vendor/toolkit/twitter/bootstrap/mixins/resize.less +0 -0
  98. data/vendor/toolkit/twitter/bootstrap/mixins/responsive-visibility.less +1 -1
  99. data/vendor/toolkit/twitter/bootstrap/mixins/size.less +0 -0
  100. data/vendor/toolkit/twitter/bootstrap/mixins/tab-focus.less +0 -0
  101. data/vendor/toolkit/twitter/bootstrap/mixins/table-row.less +0 -0
  102. data/vendor/toolkit/twitter/bootstrap/mixins/text-emphasis.less +2 -1
  103. data/vendor/toolkit/twitter/bootstrap/mixins/text-overflow.less +0 -0
  104. data/vendor/toolkit/twitter/bootstrap/mixins/vendor-prefixes.less +8 -5
  105. data/vendor/toolkit/twitter/bootstrap/mixins.less +1 -0
  106. data/vendor/toolkit/twitter/bootstrap/modals.less +3 -3
  107. data/vendor/toolkit/twitter/bootstrap/navbar.less +6 -1
  108. data/vendor/toolkit/twitter/bootstrap/navs.less +1 -1
  109. data/vendor/toolkit/twitter/bootstrap/normalize.less +12 -13
  110. data/vendor/toolkit/twitter/bootstrap/pager.less +1 -2
  111. data/vendor/toolkit/twitter/bootstrap/pagination.less +5 -4
  112. data/vendor/toolkit/twitter/bootstrap/panels.less +27 -4
  113. data/vendor/toolkit/twitter/bootstrap/popovers.less +5 -7
  114. data/vendor/toolkit/twitter/bootstrap/print.less +96 -96
  115. data/vendor/toolkit/twitter/bootstrap/progress-bars.less +1 -14
  116. data/vendor/toolkit/twitter/bootstrap/responsive-embed.less +10 -9
  117. data/vendor/toolkit/twitter/bootstrap/responsive-utilities.less +0 -0
  118. data/vendor/toolkit/twitter/bootstrap/scaffolding.less +12 -1
  119. data/vendor/toolkit/twitter/bootstrap/tables.less +6 -5
  120. data/vendor/toolkit/twitter/bootstrap/theme.less +47 -16
  121. data/vendor/toolkit/twitter/bootstrap/thumbnails.less +1 -1
  122. data/vendor/toolkit/twitter/bootstrap/tooltip.less +13 -7
  123. data/vendor/toolkit/twitter/bootstrap/type.less +2 -8
  124. data/vendor/toolkit/twitter/bootstrap/utilities.less +0 -1
  125. data/vendor/toolkit/twitter/bootstrap/variables.less +36 -14
  126. data/vendor/toolkit/twitter/bootstrap/wells.less +0 -0
  127. metadata +76 -52
  128. data/app/assets/fonts/fontawesome-webfont.eot +0 -0
  129. data/app/assets/fonts/fontawesome-webfont.svg +0 -504
  130. data/app/assets/fonts/fontawesome-webfont.ttf +0 -0
  131. data/app/assets/fonts/fontawesome-webfont.woff +0 -0
  132. data/app/assets/images/twitter/bootstrap/glyphicons-halflings-white.png +0 -0
  133. data/app/assets/images/twitter/bootstrap/glyphicons-halflings.png +0 -0
  134. data/app/assets/stylesheets/twitter-bootstrap-static/fontawesome.css.erb +0 -4
  135. data/vendor/static-source/fontawesome.less +0 -8
  136. data/vendor/toolkit/fontawesome/bordered-pulled.less +0 -16
  137. data/vendor/toolkit/fontawesome/core.less +0 -12
  138. data/vendor/toolkit/fontawesome/fixed-width.less +0 -6
  139. data/vendor/toolkit/fontawesome/font-awesome.less +0 -17
  140. data/vendor/toolkit/fontawesome/icons.less +0 -506
  141. data/vendor/toolkit/fontawesome/larger.less +0 -13
  142. data/vendor/toolkit/fontawesome/list.less +0 -19
  143. data/vendor/toolkit/fontawesome/mixins.less +0 -20
  144. data/vendor/toolkit/fontawesome/path.less +0 -14
  145. data/vendor/toolkit/fontawesome/rotated-flipped.less +0 -9
  146. data/vendor/toolkit/fontawesome/spinning.less +0 -32
  147. data/vendor/toolkit/fontawesome/stacked.less +0 -20
  148. data/vendor/toolkit/fontawesome/variables.less +0 -515
@@ -1,8 +1,8 @@
1
1
  /* ========================================================================
2
- * Bootstrap: tab.js v3.1.1
2
+ * Bootstrap: tab.js v3.3.6
3
3
  * http://getbootstrap.com/javascript/#tabs
4
4
  * ========================================================================
5
- * Copyright 2011-2014 Twitter, Inc.
5
+ * Copyright 2011-2015 Twitter, Inc.
6
6
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
7
7
  * ======================================================================== */
8
8
 
@@ -14,9 +14,15 @@
14
14
  // ====================
15
15
 
16
16
  var Tab = function (element) {
17
+ // jscs:disable requireDollarBeforejQueryAssignment
17
18
  this.element = $(element)
19
+ // jscs:enable requireDollarBeforejQueryAssignment
18
20
  }
19
21
 
22
+ Tab.VERSION = '3.3.6'
23
+
24
+ Tab.TRANSITION_DURATION = 150
25
+
20
26
  Tab.prototype.show = function () {
21
27
  var $this = this.element
22
28
  var $ul = $this.closest('ul:not(.dropdown-menu)')
@@ -24,27 +30,35 @@
24
30
 
25
31
  if (!selector) {
26
32
  selector = $this.attr('href')
27
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
33
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
28
34
  }
29
35
 
30
36
  if ($this.parent('li').hasClass('active')) return
31
37
 
32
- var previous = $ul.find('.active:last a')[0]
33
- var e = $.Event('show.bs.tab', {
34
- relatedTarget: previous
38
+ var $previous = $ul.find('.active:last a')
39
+ var hideEvent = $.Event('hide.bs.tab', {
40
+ relatedTarget: $this[0]
41
+ })
42
+ var showEvent = $.Event('show.bs.tab', {
43
+ relatedTarget: $previous[0]
35
44
  })
36
45
 
37
- $this.trigger(e)
46
+ $previous.trigger(hideEvent)
47
+ $this.trigger(showEvent)
38
48
 
39
- if (e.isDefaultPrevented()) return
49
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
40
50
 
41
51
  var $target = $(selector)
42
52
 
43
- this.activate($this.parent('li'), $ul)
53
+ this.activate($this.closest('li'), $ul)
44
54
  this.activate($target, $target.parent(), function () {
55
+ $previous.trigger({
56
+ type: 'hidden.bs.tab',
57
+ relatedTarget: $this[0]
58
+ })
45
59
  $this.trigger({
46
60
  type: 'shown.bs.tab',
47
- relatedTarget: previous
61
+ relatedTarget: $previous[0]
48
62
  })
49
63
  })
50
64
  }
@@ -53,15 +67,21 @@
53
67
  var $active = container.find('> .active')
54
68
  var transition = callback
55
69
  && $.support.transition
56
- && $active.hasClass('fade')
70
+ && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)
57
71
 
58
72
  function next() {
59
73
  $active
60
74
  .removeClass('active')
61
75
  .find('> .dropdown-menu > .active')
62
- .removeClass('active')
76
+ .removeClass('active')
77
+ .end()
78
+ .find('[data-toggle="tab"]')
79
+ .attr('aria-expanded', false)
63
80
 
64
- element.addClass('active')
81
+ element
82
+ .addClass('active')
83
+ .find('[data-toggle="tab"]')
84
+ .attr('aria-expanded', true)
65
85
 
66
86
  if (transition) {
67
87
  element[0].offsetWidth // reflow for transition
@@ -70,17 +90,22 @@
70
90
  element.removeClass('fade')
71
91
  }
72
92
 
73
- if (element.parent('.dropdown-menu')) {
74
- element.closest('li.dropdown').addClass('active')
93
+ if (element.parent('.dropdown-menu').length) {
94
+ element
95
+ .closest('li.dropdown')
96
+ .addClass('active')
97
+ .end()
98
+ .find('[data-toggle="tab"]')
99
+ .attr('aria-expanded', true)
75
100
  }
76
101
 
77
102
  callback && callback()
78
103
  }
79
104
 
80
- transition ?
105
+ $active.length && transition ?
81
106
  $active
82
- .one($.support.transition.end, next)
83
- .emulateTransitionEnd(150) :
107
+ .one('bsTransitionEnd', next)
108
+ .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
84
109
  next()
85
110
 
86
111
  $active.removeClass('in')
@@ -90,9 +115,7 @@
90
115
  // TAB PLUGIN DEFINITION
91
116
  // =====================
92
117
 
93
- var old = $.fn.tab
94
-
95
- $.fn.tab = function ( option ) {
118
+ function Plugin(option) {
96
119
  return this.each(function () {
97
120
  var $this = $(this)
98
121
  var data = $this.data('bs.tab')
@@ -102,6 +125,9 @@
102
125
  })
103
126
  }
104
127
 
128
+ var old = $.fn.tab
129
+
130
+ $.fn.tab = Plugin
105
131
  $.fn.tab.Constructor = Tab
106
132
 
107
133
 
@@ -117,9 +143,13 @@
117
143
  // TAB DATA-API
118
144
  // ============
119
145
 
120
- $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
146
+ var clickHandler = function (e) {
121
147
  e.preventDefault()
122
- $(this).tab('show')
123
- })
148
+ Plugin.call($(this), 'show')
149
+ }
150
+
151
+ $(document)
152
+ .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
153
+ .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
124
154
 
125
155
  }(jQuery);
@@ -1,9 +1,9 @@
1
1
  /* ========================================================================
2
- * Bootstrap: tooltip.js v3.1.1
2
+ * Bootstrap: tooltip.js v3.3.6
3
3
  * http://getbootstrap.com/javascript/#tooltip
4
4
  * Inspired by the original jQuery.tipsy by Jason Frame
5
5
  * ========================================================================
6
- * Copyright 2011-2014 Twitter, Inc.
6
+ * Copyright 2011-2015 Twitter, Inc.
7
7
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
8
8
  * ======================================================================== */
9
9
 
@@ -15,33 +15,48 @@
15
15
  // ===============================
16
16
 
17
17
  var Tooltip = function (element, options) {
18
- this.type =
19
- this.options =
20
- this.enabled =
21
- this.timeout =
22
- this.hoverState =
18
+ this.type = null
19
+ this.options = null
20
+ this.enabled = null
21
+ this.timeout = null
22
+ this.hoverState = null
23
23
  this.$element = null
24
+ this.inState = null
24
25
 
25
26
  this.init('tooltip', element, options)
26
27
  }
27
28
 
29
+ Tooltip.VERSION = '3.3.6'
30
+
31
+ Tooltip.TRANSITION_DURATION = 150
32
+
28
33
  Tooltip.DEFAULTS = {
29
34
  animation: true,
30
35
  placement: 'top',
31
36
  selector: false,
32
- template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
37
+ template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
33
38
  trigger: 'hover focus',
34
39
  title: '',
35
40
  delay: 0,
36
41
  html: false,
37
- container: false
42
+ container: false,
43
+ viewport: {
44
+ selector: 'body',
45
+ padding: 0
46
+ }
38
47
  }
39
48
 
40
49
  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)
50
+ this.enabled = true
51
+ this.type = type
52
+ this.$element = $(element)
53
+ this.options = this.getOptions(options)
54
+ this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
55
+ this.inState = { click: false, hover: false, focus: false }
56
+
57
+ if (this.$element[0] instanceof document.constructor && !this.options.selector) {
58
+ throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
59
+ }
45
60
 
46
61
  var triggers = this.options.trigger.split(' ')
47
62
 
@@ -94,7 +109,21 @@
94
109
 
95
110
  Tooltip.prototype.enter = function (obj) {
96
111
  var self = obj instanceof this.constructor ?
97
- obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
112
+ obj : $(obj.currentTarget).data('bs.' + this.type)
113
+
114
+ if (!self) {
115
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
116
+ $(obj.currentTarget).data('bs.' + this.type, self)
117
+ }
118
+
119
+ if (obj instanceof $.Event) {
120
+ self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
121
+ }
122
+
123
+ if (self.tip().hasClass('in') || self.hoverState == 'in') {
124
+ self.hoverState = 'in'
125
+ return
126
+ }
98
127
 
99
128
  clearTimeout(self.timeout)
100
129
 
@@ -107,9 +136,28 @@
107
136
  }, self.options.delay.show)
108
137
  }
109
138
 
139
+ Tooltip.prototype.isInStateTrue = function () {
140
+ for (var key in this.inState) {
141
+ if (this.inState[key]) return true
142
+ }
143
+
144
+ return false
145
+ }
146
+
110
147
  Tooltip.prototype.leave = function (obj) {
111
148
  var self = obj instanceof this.constructor ?
112
- obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
149
+ obj : $(obj.currentTarget).data('bs.' + this.type)
150
+
151
+ if (!self) {
152
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
153
+ $(obj.currentTarget).data('bs.' + this.type, self)
154
+ }
155
+
156
+ if (obj instanceof $.Event) {
157
+ self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
158
+ }
159
+
160
+ if (self.isInStateTrue()) return
113
161
 
114
162
  clearTimeout(self.timeout)
115
163
 
@@ -128,12 +176,17 @@
128
176
  if (this.hasContent() && this.enabled) {
129
177
  this.$element.trigger(e)
130
178
 
131
- if (e.isDefaultPrevented()) return
132
- var that = this;
179
+ var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
180
+ if (e.isDefaultPrevented() || !inDom) return
181
+ var that = this
133
182
 
134
183
  var $tip = this.tip()
135
184
 
185
+ var tipId = this.getUID(this.type)
186
+
136
187
  this.setContent()
188
+ $tip.attr('id', tipId)
189
+ this.$element.attr('aria-describedby', tipId)
137
190
 
138
191
  if (this.options.animation) $tip.addClass('fade')
139
192
 
@@ -149,26 +202,23 @@
149
202
  .detach()
150
203
  .css({ top: 0, left: 0, display: 'block' })
151
204
  .addClass(placement)
205
+ .data('bs.' + this.type, this)
152
206
 
153
207
  this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
208
+ this.$element.trigger('inserted.bs.' + this.type)
154
209
 
155
210
  var pos = this.getPosition()
156
211
  var actualWidth = $tip[0].offsetWidth
157
212
  var actualHeight = $tip[0].offsetHeight
158
213
 
159
214
  if (autoPlace) {
160
- var $parent = this.$element.parent()
161
-
162
215
  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' :
216
+ var viewportDim = this.getPosition(this.$viewport)
217
+
218
+ placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' :
219
+ placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' :
220
+ placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' :
221
+ placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' :
172
222
  placement
173
223
 
174
224
  $tip
@@ -179,22 +229,24 @@
179
229
  var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
180
230
 
181
231
  this.applyPlacement(calculatedOffset, placement)
182
- this.hoverState = null
183
232
 
184
- var complete = function() {
233
+ var complete = function () {
234
+ var prevHoverState = that.hoverState
185
235
  that.$element.trigger('shown.bs.' + that.type)
236
+ that.hoverState = null
237
+
238
+ if (prevHoverState == 'out') that.leave(that)
186
239
  }
187
240
 
188
241
  $.support.transition && this.$tip.hasClass('fade') ?
189
242
  $tip
190
- .one($.support.transition.end, complete)
191
- .emulateTransitionEnd(150) :
243
+ .one('bsTransitionEnd', complete)
244
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
192
245
  complete()
193
246
  }
194
247
  }
195
248
 
196
249
  Tooltip.prototype.applyPlacement = function (offset, placement) {
197
- var replace
198
250
  var $tip = this.tip()
199
251
  var width = $tip[0].offsetWidth
200
252
  var height = $tip[0].offsetHeight
@@ -207,8 +259,8 @@
207
259
  if (isNaN(marginTop)) marginTop = 0
208
260
  if (isNaN(marginLeft)) marginLeft = 0
209
261
 
210
- offset.top = offset.top + marginTop
211
- offset.left = offset.left + marginLeft
262
+ offset.top += marginTop
263
+ offset.left += marginLeft
212
264
 
213
265
  // $.fn.offset doesn't round pixel values
214
266
  // so we use setOffset directly with our own function B-0
@@ -228,33 +280,26 @@
228
280
  var actualHeight = $tip[0].offsetHeight
229
281
 
230
282
  if (placement == 'top' && actualHeight != height) {
231
- replace = true
232
283
  offset.top = offset.top + height - actualHeight
233
284
  }
234
285
 
235
- if (/bottom|top/.test(placement)) {
236
- var delta = 0
286
+ var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
237
287
 
238
- if (offset.left < 0) {
239
- delta = offset.left * -2
240
- offset.left = 0
288
+ if (delta.left) offset.left += delta.left
289
+ else offset.top += delta.top
241
290
 
242
- $tip.offset(offset)
291
+ var isVertical = /top|bottom/.test(placement)
292
+ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
293
+ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
243
294
 
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)
295
+ $tip.offset(offset)
296
+ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
254
297
  }
255
298
 
256
- Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
257
- this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
299
+ Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
300
+ this.arrow()
301
+ .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
302
+ .css(isVertical ? 'top' : 'left', '')
258
303
  }
259
304
 
260
305
  Tooltip.prototype.setContent = function () {
@@ -265,14 +310,17 @@
265
310
  $tip.removeClass('fade in top bottom left right')
266
311
  }
267
312
 
268
- Tooltip.prototype.hide = function () {
313
+ Tooltip.prototype.hide = function (callback) {
269
314
  var that = this
270
- var $tip = this.tip()
315
+ var $tip = $(this.$tip)
271
316
  var e = $.Event('hide.bs.' + this.type)
272
317
 
273
318
  function complete() {
274
319
  if (that.hoverState != 'in') $tip.detach()
275
- that.$element.trigger('hidden.bs.' + that.type)
320
+ that.$element
321
+ .removeAttr('aria-describedby')
322
+ .trigger('hidden.bs.' + that.type)
323
+ callback && callback()
276
324
  }
277
325
 
278
326
  this.$element.trigger(e)
@@ -281,10 +329,10 @@
281
329
 
282
330
  $tip.removeClass('in')
283
331
 
284
- $.support.transition && this.$tip.hasClass('fade') ?
332
+ $.support.transition && $tip.hasClass('fade') ?
285
333
  $tip
286
- .one($.support.transition.end, complete)
287
- .emulateTransitionEnd(150) :
334
+ .one('bsTransitionEnd', complete)
335
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
288
336
  complete()
289
337
 
290
338
  this.hoverState = null
@@ -294,7 +342,7 @@
294
342
 
295
343
  Tooltip.prototype.fixTitle = function () {
296
344
  var $e = this.$element
297
- if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
345
+ if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {
298
346
  $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
299
347
  }
300
348
  }
@@ -303,19 +351,58 @@
303
351
  return this.getTitle()
304
352
  }
305
353
 
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())
354
+ Tooltip.prototype.getPosition = function ($element) {
355
+ $element = $element || this.$element
356
+
357
+ var el = $element[0]
358
+ var isBody = el.tagName == 'BODY'
359
+
360
+ var elRect = el.getBoundingClientRect()
361
+ if (elRect.width == null) {
362
+ // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
363
+ elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
364
+ }
365
+ var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
366
+ var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
367
+ var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
368
+
369
+ return $.extend({}, elRect, scroll, outerDims, elOffset)
312
370
  }
313
371
 
314
372
  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 } :
373
+ return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
374
+ placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
317
375
  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 }
376
+ /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
377
+
378
+ }
379
+
380
+ Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
381
+ var delta = { top: 0, left: 0 }
382
+ if (!this.$viewport) return delta
383
+
384
+ var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
385
+ var viewportDimensions = this.getPosition(this.$viewport)
386
+
387
+ if (/right|left/.test(placement)) {
388
+ var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
389
+ var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
390
+ if (topEdgeOffset < viewportDimensions.top) { // top overflow
391
+ delta.top = viewportDimensions.top - topEdgeOffset
392
+ } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
393
+ delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
394
+ }
395
+ } else {
396
+ var leftEdgeOffset = pos.left - viewportPadding
397
+ var rightEdgeOffset = pos.left + viewportPadding + actualWidth
398
+ if (leftEdgeOffset < viewportDimensions.left) { // left overflow
399
+ delta.left = viewportDimensions.left - leftEdgeOffset
400
+ } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow
401
+ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
402
+ }
403
+ }
404
+
405
+ return delta
319
406
  }
320
407
 
321
408
  Tooltip.prototype.getTitle = function () {
@@ -329,20 +416,24 @@
329
416
  return title
330
417
  }
331
418
 
332
- Tooltip.prototype.tip = function () {
333
- return this.$tip = this.$tip || $(this.options.template)
419
+ Tooltip.prototype.getUID = function (prefix) {
420
+ do prefix += ~~(Math.random() * 1000000)
421
+ while (document.getElementById(prefix))
422
+ return prefix
334
423
  }
335
424
 
336
- Tooltip.prototype.arrow = function () {
337
- return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
425
+ Tooltip.prototype.tip = function () {
426
+ if (!this.$tip) {
427
+ this.$tip = $(this.options.template)
428
+ if (this.$tip.length != 1) {
429
+ throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
430
+ }
431
+ }
432
+ return this.$tip
338
433
  }
339
434
 
340
- Tooltip.prototype.validate = function () {
341
- if (!this.$element[0].parentNode) {
342
- this.hide()
343
- this.$element = null
344
- this.options = null
345
- }
435
+ Tooltip.prototype.arrow = function () {
436
+ return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
346
437
  }
347
438
 
348
439
  Tooltip.prototype.enable = function () {
@@ -358,33 +449,57 @@
358
449
  }
359
450
 
360
451
  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)
452
+ var self = this
453
+ if (e) {
454
+ self = $(e.currentTarget).data('bs.' + this.type)
455
+ if (!self) {
456
+ self = new this.constructor(e.currentTarget, this.getDelegateOptions())
457
+ $(e.currentTarget).data('bs.' + this.type, self)
458
+ }
459
+ }
460
+
461
+ if (e) {
462
+ self.inState.click = !self.inState.click
463
+ if (self.isInStateTrue()) self.enter(self)
464
+ else self.leave(self)
465
+ } else {
466
+ self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
467
+ }
363
468
  }
364
469
 
365
470
  Tooltip.prototype.destroy = function () {
471
+ var that = this
366
472
  clearTimeout(this.timeout)
367
- this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
473
+ this.hide(function () {
474
+ that.$element.off('.' + that.type).removeData('bs.' + that.type)
475
+ if (that.$tip) {
476
+ that.$tip.detach()
477
+ }
478
+ that.$tip = null
479
+ that.$arrow = null
480
+ that.$viewport = null
481
+ })
368
482
  }
369
483
 
370
484
 
371
485
  // TOOLTIP PLUGIN DEFINITION
372
486
  // =========================
373
487
 
374
- var old = $.fn.tooltip
375
-
376
- $.fn.tooltip = function (option) {
488
+ function Plugin(option) {
377
489
  return this.each(function () {
378
490
  var $this = $(this)
379
491
  var data = $this.data('bs.tooltip')
380
492
  var options = typeof option == 'object' && option
381
493
 
382
- if (!data && option == 'destroy') return
494
+ if (!data && /destroy|hide/.test(option)) return
383
495
  if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
384
496
  if (typeof option == 'string') data[option]()
385
497
  })
386
498
  }
387
499
 
500
+ var old = $.fn.tooltip
501
+
502
+ $.fn.tooltip = Plugin
388
503
  $.fn.tooltip.Constructor = Tooltip
389
504
 
390
505