bootstrap-on-rails 0.0.2 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +16 -7
  4. data/app/assets/fonts/bootstrap/glyphicons-halflings-regular.eot +0 -0
  5. data/app/assets/fonts/bootstrap/glyphicons-halflings-regular.svg +47 -47
  6. data/app/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf +0 -0
  7. data/app/assets/fonts/bootstrap/glyphicons-halflings-regular.woff +0 -0
  8. data/app/assets/javascripts/bootstrap/affix.js +48 -32
  9. data/app/assets/javascripts/bootstrap/alert.js +14 -20
  10. data/app/assets/javascripts/bootstrap/button.js +34 -33
  11. data/app/assets/javascripts/bootstrap/carousel.js +60 -54
  12. data/app/assets/javascripts/bootstrap/collapse.js +27 -36
  13. data/app/assets/javascripts/bootstrap/dropdown.js +36 -39
  14. data/app/assets/javascripts/bootstrap/modal.js +90 -56
  15. data/app/assets/javascripts/bootstrap/popover.js +21 -25
  16. data/app/assets/javascripts/bootstrap/scrollspy.js +56 -44
  17. data/app/assets/javascripts/bootstrap/tab.js +17 -24
  18. data/app/assets/javascripts/bootstrap/tooltip.js +158 -87
  19. data/app/assets/javascripts/bootstrap/transition.js +24 -21
  20. data/app/assets/stylesheets/bootstrap/alerts.less +4 -3
  21. data/app/assets/stylesheets/bootstrap/badges.less +27 -23
  22. data/app/assets/stylesheets/bootstrap/bootstrap.less +3 -2
  23. data/app/assets/stylesheets/bootstrap/breadcrumbs.less +4 -1
  24. data/app/assets/stylesheets/bootstrap/button-groups.less +36 -49
  25. data/app/assets/stylesheets/bootstrap/buttons.less +16 -17
  26. data/app/assets/stylesheets/bootstrap/carousel.less +18 -6
  27. data/app/assets/stylesheets/bootstrap/close.less +0 -0
  28. data/app/assets/stylesheets/bootstrap/code.less +16 -1
  29. data/app/assets/stylesheets/bootstrap/component-animations.less +6 -4
  30. data/app/assets/stylesheets/bootstrap/dropdowns.less +33 -10
  31. data/app/assets/stylesheets/bootstrap/forms.less +236 -60
  32. data/app/assets/stylesheets/bootstrap/glyphicons.less +5 -9
  33. data/app/assets/stylesheets/bootstrap/grid.less +40 -49
  34. data/app/assets/stylesheets/bootstrap/input-groups.less +51 -21
  35. data/app/assets/stylesheets/bootstrap/jumbotron.less +15 -7
  36. data/app/assets/stylesheets/bootstrap/labels.less +7 -1
  37. data/app/assets/stylesheets/bootstrap/list-group.less +47 -4
  38. data/app/assets/stylesheets/bootstrap/media.less +0 -0
  39. data/app/assets/stylesheets/bootstrap/mixins.less +35 -854
  40. data/app/assets/stylesheets/bootstrap/mixins/alerts.less +14 -0
  41. data/app/assets/stylesheets/bootstrap/mixins/background-variant.less +8 -0
  42. data/app/assets/stylesheets/bootstrap/mixins/border-radius.less +18 -0
  43. data/app/assets/stylesheets/bootstrap/mixins/buttons.less +50 -0
  44. data/app/assets/stylesheets/bootstrap/mixins/center-block.less +7 -0
  45. data/app/assets/stylesheets/bootstrap/mixins/clearfix.less +22 -0
  46. data/app/assets/stylesheets/bootstrap/mixins/forms.less +81 -0
  47. data/app/assets/stylesheets/bootstrap/mixins/gradients.less +59 -0
  48. data/app/assets/stylesheets/bootstrap/mixins/grid-framework.less +91 -0
  49. data/app/assets/stylesheets/bootstrap/mixins/grid.less +122 -0
  50. data/app/assets/stylesheets/bootstrap/mixins/hide-text.less +21 -0
  51. data/app/assets/stylesheets/bootstrap/mixins/image.less +34 -0
  52. data/app/assets/stylesheets/bootstrap/mixins/labels.less +12 -0
  53. data/app/assets/stylesheets/bootstrap/mixins/list-group.less +29 -0
  54. data/app/assets/stylesheets/bootstrap/mixins/nav-divider.less +10 -0
  55. data/app/assets/stylesheets/bootstrap/mixins/nav-vertical-align.less +9 -0
  56. data/app/assets/stylesheets/bootstrap/mixins/opacity.less +8 -0
  57. data/app/assets/stylesheets/bootstrap/mixins/pagination.less +23 -0
  58. data/app/assets/stylesheets/bootstrap/mixins/panels.less +24 -0
  59. data/app/assets/stylesheets/bootstrap/mixins/progress-bar.less +10 -0
  60. data/app/assets/stylesheets/bootstrap/mixins/reset-filter.less +8 -0
  61. data/app/assets/stylesheets/bootstrap/mixins/resize.less +6 -0
  62. data/app/assets/stylesheets/bootstrap/mixins/responsive-visibility.less +15 -0
  63. data/app/assets/stylesheets/bootstrap/mixins/size.less +10 -0
  64. data/app/assets/stylesheets/bootstrap/mixins/tab-focus.less +9 -0
  65. data/app/assets/stylesheets/bootstrap/mixins/table-row.less +28 -0
  66. data/app/assets/stylesheets/bootstrap/mixins/text-emphasis.less +8 -0
  67. data/app/assets/stylesheets/bootstrap/mixins/text-overflow.less +8 -0
  68. data/app/assets/stylesheets/bootstrap/mixins/vendor-prefixes.less +224 -0
  69. data/app/assets/stylesheets/bootstrap/modals.less +38 -20
  70. data/app/assets/stylesheets/bootstrap/navbar.less +103 -72
  71. data/app/assets/stylesheets/bootstrap/navs.less +3 -23
  72. data/app/assets/stylesheets/bootstrap/normalize.less +153 -134
  73. data/app/assets/stylesheets/bootstrap/pager.less +5 -5
  74. data/app/assets/stylesheets/bootstrap/pagination.less +6 -3
  75. data/app/assets/stylesheets/bootstrap/panels.less +117 -46
  76. data/app/assets/stylesheets/bootstrap/popovers.less +12 -12
  77. data/app/assets/stylesheets/bootstrap/print.less +0 -4
  78. data/app/assets/stylesheets/bootstrap/progress-bars.less +27 -14
  79. data/app/assets/stylesheets/bootstrap/responsive-embed.less +34 -0
  80. data/app/assets/stylesheets/bootstrap/responsive-utilities.less +109 -124
  81. data/app/assets/stylesheets/bootstrap/scaffolding.less +34 -3
  82. data/app/assets/stylesheets/bootstrap/tables.less +23 -26
  83. data/app/assets/stylesheets/bootstrap/theme.less +12 -1
  84. data/app/assets/stylesheets/bootstrap/thumbnails.less +10 -4
  85. data/app/assets/stylesheets/bootstrap/tooltip.less +1 -1
  86. data/app/assets/stylesheets/bootstrap/type.less +151 -117
  87. data/app/assets/stylesheets/bootstrap/utilities.less +1 -0
  88. data/app/assets/stylesheets/bootstrap/variables.less +399 -190
  89. data/app/assets/stylesheets/bootstrap/wells.less +1 -1
  90. data/bootstrap-on-rails.gemspec +1 -1
  91. data/lib/bootstrap-on-rails/version.rb +1 -1
  92. metadata +33 -3
@@ -1,68 +1,71 @@
1
1
  /* ========================================================================
2
- * Bootstrap: scrollspy.js v3.0.2
2
+ * Bootstrap: scrollspy.js v3.2.0
3
3
  * http://getbootstrap.com/javascript/#scrollspy
4
4
  * ========================================================================
5
- * Copyright 2013 Twitter, Inc.
6
- *
7
- * Licensed under the Apache License, Version 2.0 (the "License");
8
- * you may not use this file except in compliance with the License.
9
- * You may obtain a copy of the License at
10
- *
11
- * http://www.apache.org/licenses/LICENSE-2.0
12
- *
13
- * Unless required by applicable law or agreed to in writing, software
14
- * distributed under the License is distributed on an "AS IS" BASIS,
15
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- * See the License for the specific language governing permissions and
17
- * limitations under the License.
5
+ * Copyright 2011-2014 Twitter, Inc.
6
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
18
7
  * ======================================================================== */
19
8
 
20
9
 
21
- +function ($) { "use strict";
10
+ +function ($) {
11
+ 'use strict';
22
12
 
23
13
  // SCROLLSPY CLASS DEFINITION
24
14
  // ==========================
25
15
 
26
16
  function ScrollSpy(element, options) {
27
- var href
28
17
  var process = $.proxy(this.process, this)
29
18
 
30
- this.$element = $(element).is('body') ? $(window) : $(element)
31
19
  this.$body = $('body')
32
- this.$scrollElement = this.$element.on('scroll.bs.scroll-spy.data-api', process)
20
+ this.$scrollElement = $(element).is('body') ? $(window) : $(element)
33
21
  this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
34
- this.selector = (this.options.target
35
- || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
36
- || '') + ' .nav li > a'
37
- this.offsets = $([])
38
- this.targets = $([])
22
+ this.selector = (this.options.target || '') + ' .nav li > a'
23
+ this.offsets = []
24
+ this.targets = []
39
25
  this.activeTarget = null
26
+ this.scrollHeight = 0
40
27
 
28
+ this.$scrollElement.on('scroll.bs.scrollspy', process)
41
29
  this.refresh()
42
30
  this.process()
43
31
  }
44
32
 
33
+ ScrollSpy.VERSION = '3.2.0'
34
+
45
35
  ScrollSpy.DEFAULTS = {
46
36
  offset: 10
47
37
  }
48
38
 
39
+ ScrollSpy.prototype.getScrollHeight = function () {
40
+ return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
41
+ }
42
+
49
43
  ScrollSpy.prototype.refresh = function () {
50
- var offsetMethod = this.$element[0] == window ? 'offset' : 'position'
44
+ var offsetMethod = 'offset'
45
+ var offsetBase = 0
51
46
 
52
- this.offsets = $([])
53
- this.targets = $([])
47
+ if (!$.isWindow(this.$scrollElement[0])) {
48
+ offsetMethod = 'position'
49
+ offsetBase = this.$scrollElement.scrollTop()
50
+ }
51
+
52
+ this.offsets = []
53
+ this.targets = []
54
+ this.scrollHeight = this.getScrollHeight()
54
55
 
55
56
  var self = this
56
- var $targets = this.$body
57
+
58
+ this.$body
57
59
  .find(this.selector)
58
60
  .map(function () {
59
61
  var $el = $(this)
60
62
  var href = $el.data('target') || $el.attr('href')
61
- var $href = /^#\w/.test(href) && $(href)
63
+ var $href = /^#./.test(href) && $(href)
62
64
 
63
65
  return ($href
64
66
  && $href.length
65
- && [[ $href[offsetMethod]().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]]) || null
67
+ && $href.is(':visible')
68
+ && [[$href[offsetMethod]().top + offsetBase, href]]) || null
66
69
  })
67
70
  .sort(function (a, b) { return a[0] - b[0] })
68
71
  .each(function () {
@@ -73,22 +76,30 @@
73
76
 
74
77
  ScrollSpy.prototype.process = function () {
75
78
  var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
76
- var scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
77
- var maxScroll = scrollHeight - this.$scrollElement.height()
79
+ var scrollHeight = this.getScrollHeight()
80
+ var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
78
81
  var offsets = this.offsets
79
82
  var targets = this.targets
80
83
  var activeTarget = this.activeTarget
81
84
  var i
82
85
 
86
+ if (this.scrollHeight != scrollHeight) {
87
+ this.refresh()
88
+ }
89
+
83
90
  if (scrollTop >= maxScroll) {
84
- return activeTarget != (i = targets.last()[0]) && this.activate(i)
91
+ return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
92
+ }
93
+
94
+ if (activeTarget && scrollTop <= offsets[0]) {
95
+ return activeTarget != (i = targets[0]) && this.activate(i)
85
96
  }
86
97
 
87
98
  for (i = offsets.length; i--;) {
88
99
  activeTarget != targets[i]
89
100
  && scrollTop >= offsets[i]
90
101
  && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
91
- && this.activate( targets[i] )
102
+ && this.activate(targets[i])
92
103
  }
93
104
  }
94
105
 
@@ -96,33 +107,31 @@
96
107
  this.activeTarget = target
97
108
 
98
109
  $(this.selector)
99
- .parents('.active')
110
+ .parentsUntil(this.options.target, '.active')
100
111
  .removeClass('active')
101
112
 
102
- var selector = this.selector
103
- + '[data-target="' + target + '"],'
104
- + this.selector + '[href="' + target + '"]'
113
+ var selector = this.selector +
114
+ '[data-target="' + target + '"],' +
115
+ this.selector + '[href="' + target + '"]'
105
116
 
106
117
  var active = $(selector)
107
118
  .parents('li')
108
119
  .addClass('active')
109
120
 
110
- if (active.parent('.dropdown-menu').length) {
121
+ if (active.parent('.dropdown-menu').length) {
111
122
  active = active
112
123
  .closest('li.dropdown')
113
124
  .addClass('active')
114
125
  }
115
126
 
116
- active.trigger('activate')
127
+ active.trigger('activate.bs.scrollspy')
117
128
  }
118
129
 
119
130
 
120
131
  // SCROLLSPY PLUGIN DEFINITION
121
132
  // ===========================
122
133
 
123
- var old = $.fn.scrollspy
124
-
125
- $.fn.scrollspy = function (option) {
134
+ function Plugin(option) {
126
135
  return this.each(function () {
127
136
  var $this = $(this)
128
137
  var data = $this.data('bs.scrollspy')
@@ -133,6 +142,9 @@
133
142
  })
134
143
  }
135
144
 
145
+ var old = $.fn.scrollspy
146
+
147
+ $.fn.scrollspy = Plugin
136
148
  $.fn.scrollspy.Constructor = ScrollSpy
137
149
 
138
150
 
@@ -148,10 +160,10 @@
148
160
  // SCROLLSPY DATA-API
149
161
  // ==================
150
162
 
151
- $(window).on('load', function () {
163
+ $(window).on('load.bs.scrollspy.data-api', function () {
152
164
  $('[data-spy="scroll"]').each(function () {
153
165
  var $spy = $(this)
154
- $spy.scrollspy($spy.data())
166
+ Plugin.call($spy, $spy.data())
155
167
  })
156
168
  })
157
169
 
@@ -1,24 +1,14 @@
1
1
  /* ========================================================================
2
- * Bootstrap: tab.js v3.0.2
2
+ * Bootstrap: tab.js v3.2.0
3
3
  * http://getbootstrap.com/javascript/#tabs
4
4
  * ========================================================================
5
- * Copyright 2013 Twitter, Inc.
6
- *
7
- * Licensed under the Apache License, Version 2.0 (the "License");
8
- * you may not use this file except in compliance with the License.
9
- * You may obtain a copy of the License at
10
- *
11
- * http://www.apache.org/licenses/LICENSE-2.0
12
- *
13
- * Unless required by applicable law or agreed to in writing, software
14
- * distributed under the License is distributed on an "AS IS" BASIS,
15
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- * See the License for the specific language governing permissions and
17
- * limitations under the License.
5
+ * Copyright 2011-2014 Twitter, Inc.
6
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
18
7
  * ======================================================================== */
19
8
 
20
9
 
21
- +function ($) { "use strict";
10
+ +function ($) {
11
+ 'use strict';
22
12
 
23
13
  // TAB CLASS DEFINITION
24
14
  // ====================
@@ -27,6 +17,8 @@
27
17
  this.element = $(element)
28
18
  }
29
19
 
20
+ Tab.VERSION = '3.2.0'
21
+
30
22
  Tab.prototype.show = function () {
31
23
  var $this = this.element
32
24
  var $ul = $this.closest('ul:not(.dropdown-menu)')
@@ -34,7 +26,7 @@
34
26
 
35
27
  if (!selector) {
36
28
  selector = $this.attr('href')
37
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
29
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
38
30
  }
39
31
 
40
32
  if ($this.parent('li').hasClass('active')) return
@@ -50,11 +42,11 @@
50
42
 
51
43
  var $target = $(selector)
52
44
 
53
- this.activate($this.parent('li'), $ul)
45
+ this.activate($this.closest('li'), $ul)
54
46
  this.activate($target, $target.parent(), function () {
55
47
  $this.trigger({
56
- type: 'shown.bs.tab'
57
- , relatedTarget: previous
48
+ type: 'shown.bs.tab',
49
+ relatedTarget: previous
58
50
  })
59
51
  })
60
52
  }
@@ -89,7 +81,7 @@
89
81
 
90
82
  transition ?
91
83
  $active
92
- .one($.support.transition.end, next)
84
+ .one('bsTransitionEnd', next)
93
85
  .emulateTransitionEnd(150) :
94
86
  next()
95
87
 
@@ -100,9 +92,7 @@
100
92
  // TAB PLUGIN DEFINITION
101
93
  // =====================
102
94
 
103
- var old = $.fn.tab
104
-
105
- $.fn.tab = function ( option ) {
95
+ function Plugin(option) {
106
96
  return this.each(function () {
107
97
  var $this = $(this)
108
98
  var data = $this.data('bs.tab')
@@ -112,6 +102,9 @@
112
102
  })
113
103
  }
114
104
 
105
+ var old = $.fn.tab
106
+
107
+ $.fn.tab = Plugin
115
108
  $.fn.tab.Constructor = Tab
116
109
 
117
110
 
@@ -129,7 +122,7 @@
129
122
 
130
123
  $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
131
124
  e.preventDefault()
132
- $(this).tab('show')
125
+ Plugin.call($(this), 'show')
133
126
  })
134
127
 
135
128
  }(jQuery);
@@ -1,25 +1,15 @@
1
1
  /* ========================================================================
2
- * Bootstrap: tooltip.js v3.0.2
2
+ * Bootstrap: tooltip.js v3.2.0
3
3
  * http://getbootstrap.com/javascript/#tooltip
4
4
  * Inspired by the original jQuery.tipsy by Jason Frame
5
5
  * ========================================================================
6
- * Copyright 2013 Twitter, Inc.
7
- *
8
- * Licensed under the Apache License, Version 2.0 (the "License");
9
- * you may not use this file except in compliance with the License.
10
- * You may obtain a copy of the License at
11
- *
12
- * http://www.apache.org/licenses/LICENSE-2.0
13
- *
14
- * Unless required by applicable law or agreed to in writing, software
15
- * distributed under the License is distributed on an "AS IS" BASIS,
16
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
- * See the License for the specific language governing permissions and
18
- * limitations under the License.
6
+ * Copyright 2011-2014 Twitter, Inc.
7
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
19
8
  * ======================================================================== */
20
9
 
21
10
 
22
- +function ($) { "use strict";
11
+ +function ($) {
12
+ 'use strict';
23
13
 
24
14
  // TOOLTIP PUBLIC CLASS DEFINITION
25
15
  // ===============================
@@ -35,23 +25,30 @@
35
25
  this.init('tooltip', element, options)
36
26
  }
37
27
 
28
+ Tooltip.VERSION = '3.2.0'
29
+
38
30
  Tooltip.DEFAULTS = {
39
- animation: true
40
- , placement: 'top'
41
- , selector: false
42
- , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
43
- , trigger: 'hover focus'
44
- , title: ''
45
- , delay: 0
46
- , html: false
47
- , container: false
31
+ animation: true,
32
+ placement: 'top',
33
+ selector: false,
34
+ template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
35
+ trigger: 'hover focus',
36
+ title: '',
37
+ delay: 0,
38
+ html: false,
39
+ container: false,
40
+ viewport: {
41
+ selector: 'body',
42
+ padding: 0
43
+ }
48
44
  }
49
45
 
50
46
  Tooltip.prototype.init = function (type, element, options) {
51
- this.enabled = true
52
- this.type = type
53
- this.$element = $(element)
54
- this.options = this.getOptions(options)
47
+ this.enabled = true
48
+ this.type = type
49
+ this.$element = $(element)
50
+ this.options = this.getOptions(options)
51
+ this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
55
52
 
56
53
  var triggers = this.options.trigger.split(' ')
57
54
 
@@ -61,8 +58,8 @@
61
58
  if (trigger == 'click') {
62
59
  this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
63
60
  } else if (trigger != 'manual') {
64
- var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
65
- var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
61
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
62
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
66
63
 
67
64
  this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
68
65
  this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
@@ -83,8 +80,8 @@
83
80
 
84
81
  if (options.delay && typeof options.delay == 'number') {
85
82
  options.delay = {
86
- show: options.delay
87
- , hide: options.delay
83
+ show: options.delay,
84
+ hide: options.delay
88
85
  }
89
86
  }
90
87
 
@@ -104,7 +101,12 @@
104
101
 
105
102
  Tooltip.prototype.enter = function (obj) {
106
103
  var self = obj instanceof this.constructor ?
107
- obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
104
+ obj : $(obj.currentTarget).data('bs.' + this.type)
105
+
106
+ if (!self) {
107
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
108
+ $(obj.currentTarget).data('bs.' + this.type, self)
109
+ }
108
110
 
109
111
  clearTimeout(self.timeout)
110
112
 
@@ -119,7 +121,12 @@
119
121
 
120
122
  Tooltip.prototype.leave = function (obj) {
121
123
  var self = obj instanceof this.constructor ?
122
- obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
124
+ obj : $(obj.currentTarget).data('bs.' + this.type)
125
+
126
+ if (!self) {
127
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
128
+ $(obj.currentTarget).data('bs.' + this.type, self)
129
+ }
123
130
 
124
131
  clearTimeout(self.timeout)
125
132
 
@@ -133,16 +140,22 @@
133
140
  }
134
141
 
135
142
  Tooltip.prototype.show = function () {
136
- var e = $.Event('show.bs.'+ this.type)
143
+ var e = $.Event('show.bs.' + this.type)
137
144
 
138
145
  if (this.hasContent() && this.enabled) {
139
146
  this.$element.trigger(e)
140
147
 
141
- if (e.isDefaultPrevented()) return
148
+ var inDom = $.contains(document.documentElement, this.$element[0])
149
+ if (e.isDefaultPrevented() || !inDom) return
150
+ var that = this
142
151
 
143
152
  var $tip = this.tip()
144
153
 
154
+ var tipId = this.getUID(this.type)
155
+
145
156
  this.setContent()
157
+ $tip.attr('id', tipId)
158
+ this.$element.attr('aria-describedby', tipId)
146
159
 
147
160
  if (this.options.animation) $tip.addClass('fade')
148
161
 
@@ -158,6 +171,7 @@
158
171
  .detach()
159
172
  .css({ top: 0, left: 0, display: 'block' })
160
173
  .addClass(placement)
174
+ .data('bs.' + this.type, this)
161
175
 
162
176
  this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
163
177
 
@@ -166,18 +180,14 @@
166
180
  var actualHeight = $tip[0].offsetHeight
167
181
 
168
182
  if (autoPlace) {
169
- var $parent = this.$element.parent()
170
-
171
183
  var orgPlacement = placement
172
- var docScroll = document.documentElement.scrollTop || document.body.scrollTop
173
- var parentWidth = this.options.container == 'body' ? window.innerWidth : $parent.outerWidth()
174
- var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()
175
- var parentLeft = this.options.container == 'body' ? 0 : $parent.offset().left
176
-
177
- placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' :
178
- placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' :
179
- placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' :
180
- placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' :
184
+ var $parent = this.$element.parent()
185
+ var parentDim = this.getPosition($parent)
186
+
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' :
181
191
  placement
182
192
 
183
193
  $tip
@@ -188,12 +198,21 @@
188
198
  var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
189
199
 
190
200
  this.applyPlacement(calculatedOffset, placement)
191
- this.$element.trigger('shown.bs.' + this.type)
201
+
202
+ var complete = function () {
203
+ that.$element.trigger('shown.bs.' + that.type)
204
+ that.hoverState = null
205
+ }
206
+
207
+ $.support.transition && this.$tip.hasClass('fade') ?
208
+ $tip
209
+ .one('bsTransitionEnd', complete)
210
+ .emulateTransitionEnd(150) :
211
+ complete()
192
212
  }
193
213
  }
194
214
 
195
- Tooltip.prototype.applyPlacement = function(offset, placement) {
196
- var replace
215
+ Tooltip.prototype.applyPlacement = function (offset, placement) {
197
216
  var $tip = this.tip()
198
217
  var width = $tip[0].offsetWidth
199
218
  var height = $tip[0].offsetHeight
@@ -209,42 +228,42 @@
209
228
  offset.top = offset.top + marginTop
210
229
  offset.left = offset.left + marginLeft
211
230
 
212
- $tip
213
- .offset(offset)
214
- .addClass('in')
231
+ // $.fn.offset doesn't round pixel values
232
+ // so we use setOffset directly with our own function B-0
233
+ $.offset.setOffset($tip[0], $.extend({
234
+ using: function (props) {
235
+ $tip.css({
236
+ top: Math.round(props.top),
237
+ left: Math.round(props.left)
238
+ })
239
+ }
240
+ }, offset), 0)
241
+
242
+ $tip.addClass('in')
215
243
 
216
244
  // check to see if placing tip in new offset caused the tip to resize itself
217
245
  var actualWidth = $tip[0].offsetWidth
218
246
  var actualHeight = $tip[0].offsetHeight
219
247
 
220
248
  if (placement == 'top' && actualHeight != height) {
221
- replace = true
222
249
  offset.top = offset.top + height - actualHeight
223
250
  }
224
251
 
225
- if (/bottom|top/.test(placement)) {
226
- var delta = 0
252
+ var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
227
253
 
228
- if (offset.left < 0) {
229
- delta = offset.left * -2
230
- offset.left = 0
254
+ if (delta.left) offset.left += delta.left
255
+ else offset.top += delta.top
231
256
 
232
- $tip.offset(offset)
233
-
234
- actualWidth = $tip[0].offsetWidth
235
- actualHeight = $tip[0].offsetHeight
236
- }
237
-
238
- this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
239
- } else {
240
- this.replaceArrow(actualHeight - height, actualHeight, 'top')
241
- }
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'
242
260
 
243
- if (replace) $tip.offset(offset)
261
+ $tip.offset(offset)
262
+ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
244
263
  }
245
264
 
246
- Tooltip.prototype.replaceArrow = function(delta, dimension, position) {
247
- this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
265
+ Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
266
+ this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
248
267
  }
249
268
 
250
269
  Tooltip.prototype.setContent = function () {
@@ -260,8 +279,11 @@
260
279
  var $tip = this.tip()
261
280
  var e = $.Event('hide.bs.' + this.type)
262
281
 
282
+ this.$element.removeAttr('aria-describedby')
283
+
263
284
  function complete() {
264
285
  if (that.hoverState != 'in') $tip.detach()
286
+ that.$element.trigger('hidden.bs.' + that.type)
265
287
  }
266
288
 
267
289
  this.$element.trigger(e)
@@ -272,18 +294,18 @@
272
294
 
273
295
  $.support.transition && this.$tip.hasClass('fade') ?
274
296
  $tip
275
- .one($.support.transition.end, complete)
297
+ .one('bsTransitionEnd', complete)
276
298
  .emulateTransitionEnd(150) :
277
299
  complete()
278
300
 
279
- this.$element.trigger('hidden.bs.' + this.type)
301
+ this.hoverState = null
280
302
 
281
303
  return this
282
304
  }
283
305
 
284
306
  Tooltip.prototype.fixTitle = function () {
285
307
  var $e = this.$element
286
- if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
308
+ if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
287
309
  $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
288
310
  }
289
311
  }
@@ -292,12 +314,15 @@
292
314
  return this.getTitle()
293
315
  }
294
316
 
295
- Tooltip.prototype.getPosition = function () {
296
- var el = this.$element[0]
297
- return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
298
- width: el.offsetWidth
299
- , height: el.offsetHeight
300
- }, this.$element.offset())
317
+ Tooltip.prototype.getPosition = function ($element) {
318
+ $element = $element || this.$element
319
+ var el = $element[0]
320
+ 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())
301
326
  }
302
327
 
303
328
  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
@@ -305,6 +330,35 @@
305
330
  placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
306
331
  placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
307
332
  /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
333
+
334
+ }
335
+
336
+ Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
337
+ var delta = { top: 0, left: 0 }
338
+ if (!this.$viewport) return delta
339
+
340
+ var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
341
+ var viewportDimensions = this.getPosition(this.$viewport)
342
+
343
+ if (/right|left/.test(placement)) {
344
+ var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
345
+ var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
346
+ if (topEdgeOffset < viewportDimensions.top) { // top overflow
347
+ delta.top = viewportDimensions.top - topEdgeOffset
348
+ } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
349
+ delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
350
+ }
351
+ } else {
352
+ var leftEdgeOffset = pos.left - viewportPadding
353
+ var rightEdgeOffset = pos.left + viewportPadding + actualWidth
354
+ if (leftEdgeOffset < viewportDimensions.left) { // left overflow
355
+ delta.left = viewportDimensions.left - leftEdgeOffset
356
+ } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
357
+ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
358
+ }
359
+ }
360
+
361
+ return delta
308
362
  }
309
363
 
310
364
  Tooltip.prototype.getTitle = function () {
@@ -318,12 +372,18 @@
318
372
  return title
319
373
  }
320
374
 
375
+ Tooltip.prototype.getUID = function (prefix) {
376
+ do prefix += ~~(Math.random() * 1000000)
377
+ while (document.getElementById(prefix))
378
+ return prefix
379
+ }
380
+
321
381
  Tooltip.prototype.tip = function () {
322
- return this.$tip = this.$tip || $(this.options.template)
382
+ return (this.$tip = this.$tip || $(this.options.template))
323
383
  }
324
384
 
325
385
  Tooltip.prototype.arrow = function () {
326
- return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
386
+ return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
327
387
  }
328
388
 
329
389
  Tooltip.prototype.validate = function () {
@@ -347,11 +407,20 @@
347
407
  }
348
408
 
349
409
  Tooltip.prototype.toggle = function (e) {
350
- var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
410
+ var self = this
411
+ if (e) {
412
+ self = $(e.currentTarget).data('bs.' + this.type)
413
+ if (!self) {
414
+ self = new this.constructor(e.currentTarget, this.getDelegateOptions())
415
+ $(e.currentTarget).data('bs.' + this.type, self)
416
+ }
417
+ }
418
+
351
419
  self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
352
420
  }
353
421
 
354
422
  Tooltip.prototype.destroy = function () {
423
+ clearTimeout(this.timeout)
355
424
  this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
356
425
  }
357
426
 
@@ -359,19 +428,21 @@
359
428
  // TOOLTIP PLUGIN DEFINITION
360
429
  // =========================
361
430
 
362
- var old = $.fn.tooltip
363
-
364
- $.fn.tooltip = function (option) {
431
+ function Plugin(option) {
365
432
  return this.each(function () {
366
433
  var $this = $(this)
367
434
  var data = $this.data('bs.tooltip')
368
435
  var options = typeof option == 'object' && option
369
436
 
437
+ if (!data && option == 'destroy') return
370
438
  if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
371
439
  if (typeof option == 'string') data[option]()
372
440
  })
373
441
  }
374
442
 
443
+ var old = $.fn.tooltip
444
+
445
+ $.fn.tooltip = Plugin
375
446
  $.fn.tooltip.Constructor = Tooltip
376
447
 
377
448