aboutsummaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorMark Otto <[email protected]>2014-10-26 22:31:59 -0700
committerMark Otto <[email protected]>2014-10-26 22:31:59 -0700
commitd6b0f45fb711989cb8ac32f6717d6920ef5c68e0 (patch)
tree48f08aacd9aadf5cf6fe9aaeeebfa6eb7fab26d0 /js
parentd1660ad0788fa4e9b0d072323c1d70f9c6f5dbf2 (diff)
parent66bb0b4fc963fec42e7168f40b18703d3f31bfa8 (diff)
downloadbootstrap-d6b0f45fb711989cb8ac32f6717d6920ef5c68e0.tar.xz
bootstrap-d6b0f45fb711989cb8ac32f6717d6920ef5c68e0.zip
Merge branch 'master' into derp
Conflicts: Gruntfile.js dist/css/bootstrap-theme.css dist/css/bootstrap-theme.css.map dist/css/bootstrap-theme.min.css dist/css/bootstrap.css dist/css/bootstrap.css.map dist/css/bootstrap.min.css docs/_includes/components/dropdowns.html docs/_includes/components/media.html docs/_includes/components/navs.html docs/_includes/components/progress-bars.html docs/_includes/components/responsive-embed.html docs/_includes/css/buttons.html docs/_includes/css/forms.html docs/_includes/css/less.html docs/_includes/css/overview.html docs/_includes/css/responsive-utilities.html docs/_includes/customizer-variables.html docs/_includes/getting-started/browser-device-support.html docs/_includes/getting-started/grunt.html docs/_includes/getting-started/template.html docs/_includes/header.html docs/_includes/js/alerts.html docs/_includes/js/buttons.html docs/_includes/js/carousel.html docs/_includes/js/collapse.html docs/_includes/js/dropdowns.html docs/_includes/js/modal.html docs/_includes/js/popovers.html docs/_includes/js/scrollspy.html docs/_includes/js/tabs.html docs/_includes/js/tooltips.html docs/_includes/nav/components.html docs/_includes/nav/getting-started.html docs/_layouts/default.html docs/about.html docs/assets/css/docs.min.css docs/assets/css/src/docs.css docs/assets/js/customize.min.js docs/assets/js/docs.min.js docs/assets/js/raw-files.min.js docs/browser-bugs.html docs/components.html docs/components/navbar.md docs/css.html docs/dist/css/bootstrap-theme.css docs/dist/css/bootstrap-theme.css.map docs/dist/css/bootstrap-theme.min.css docs/dist/css/bootstrap.css docs/dist/css/bootstrap.css.map docs/dist/css/bootstrap.min.css docs/examples/blog/index.html docs/examples/carousel/index.html docs/examples/cover/index.html docs/examples/dashboard/index.html docs/examples/grid/index.html docs/examples/jumbotron-narrow/index.html docs/examples/jumbotron/index.html docs/examples/justified-nav/index.html docs/examples/navbar-fixed-top/index.html docs/examples/navbar-static-top/index.html docs/examples/navbar/index.html docs/examples/non-responsive/index.html docs/examples/offcanvas/index.html docs/examples/signin/index.html docs/examples/starter-template/index.html docs/examples/sticky-footer-navbar/index.html docs/examples/sticky-footer/index.html docs/examples/theme/index.html docs/examples/tooltip-viewport/index.html docs/getting-started.html docs/javascript.html docs/migration.html less/_animation.less less/_modal.less less/_navbar.less less/_variables.less less/glyphicons.less less/navs.less less/panels.less less/progress-bars.less
Diffstat (limited to 'js')
-rw-r--r--js/button.js2
-rw-r--r--js/carousel.js9
-rw-r--r--js/collapse.js14
-rw-r--r--js/dropdown.js10
-rw-r--r--js/modal.js20
-rw-r--r--js/popover.js14
-rw-r--r--js/scrollspy.js16
-rw-r--r--js/tab.js37
-rw-r--r--js/tests/index.html5
-rw-r--r--js/tests/unit/button.js20
-rw-r--r--js/tests/unit/carousel.js101
-rw-r--r--js/tests/unit/collapse.js35
-rw-r--r--js/tests/unit/modal.js4
-rw-r--r--js/tests/unit/popover.js48
-rw-r--r--js/tests/unit/scrollspy.js69
-rw-r--r--js/tests/unit/tab.js101
-rw-r--r--js/tests/unit/tooltip.js271
-rw-r--r--js/tests/visual/dropdown.html41
-rw-r--r--js/tooltip.js35
19 files changed, 746 insertions, 106 deletions
diff --git a/js/button.js b/js/button.js
index b3e944c59..901e47a6e 100644
--- a/js/button.js
+++ b/js/button.js
@@ -60,6 +60,8 @@
else $parent.find('.active').removeClass('active')
}
if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
+ } else {
+ this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
}
if (changed) this.$element.toggleClass('active')
diff --git a/js/carousel.js b/js/carousel.js
index 65cc7b912..b68899009 100644
--- a/js/carousel.js
+++ b/js/carousel.js
@@ -14,7 +14,7 @@
// =========================
var Carousel = function (element, options) {
- this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this))
+ this.$element = $(element)
this.$indicators = this.$element.find('.carousel-indicators')
this.options = options
this.paused =
@@ -23,7 +23,9 @@
this.$active =
this.$items = null
- this.options.pause == 'hover' && this.$element
+ this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
+
+ this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
.on('mouseenter.bs.carousel', $.proxy(this.pause, this))
.on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
}
@@ -35,7 +37,8 @@
Carousel.DEFAULTS = {
interval: 5000,
pause: 'hover',
- wrap: true
+ wrap: true,
+ keyboard: true
}
Carousel.prototype.keydown = function (e) {
diff --git a/js/collapse.js b/js/collapse.js
index d7d56bdd5..abbf25f2a 100644
--- a/js/collapse.js
+++ b/js/collapse.js
@@ -38,17 +38,21 @@
Collapse.prototype.show = function () {
if (this.transitioning || this.$element.hasClass('in')) return
+ var activesData
+ var actives = this.$parent && this.$parent.find('> .panel').children('.in, .collapsing')
+
+ if (actives && actives.length) {
+ activesData = actives.data('bs.collapse')
+ if (activesData && activesData.transitioning) return
+ }
+
var startEvent = $.Event('show.bs.collapse')
this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return
- var actives = this.$parent && this.$parent.find('> .panel').children('.in, .collapsing')
-
if (actives && actives.length) {
- var hasData = actives.data('bs.collapse')
- if (hasData && hasData.transitioning) return
Plugin.call(actives, 'hide')
- hasData || actives.data('bs.collapse', null)
+ activesData || actives.data('bs.collapse', null)
}
var dimension = this.dimension()
diff --git a/js/dropdown.js b/js/dropdown.js
index 756fc21f5..7388f8124 100644
--- a/js/dropdown.js
+++ b/js/dropdown.js
@@ -55,7 +55,7 @@
}
Dropdown.prototype.keydown = function (e) {
- if (!/(38|40|27)/.test(e.keyCode)) return
+ if (!/(38|40|27|32)/.test(e.which)) return
var $this = $(this)
@@ -67,7 +67,7 @@
var $parent = getParent($this)
var isActive = $parent.hasClass('open')
- if (!isActive || (isActive && e.keyCode == 27)) {
+ if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
if (e.which == 27) $parent.find(toggle).trigger('focus')
return $this.trigger('click')
}
@@ -77,10 +77,10 @@
if (!$items.length) return
- var index = $items.index($items.filter(':focus'))
+ var index = $items.index(e.target)
- if (e.keyCode == 38 && index > 0) index-- // up
- if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (e.which == 38 && index > 0) index-- // up
+ if (e.which == 40 && index < $items.length - 1) index++ // down
if (!~index) index = 0
$items.eq(index).trigger('focus')
diff --git a/js/modal.js b/js/modal.js
index d0426b01b..6e2ac7e74 100644
--- a/js/modal.js
+++ b/js/modal.js
@@ -107,9 +107,6 @@
this.isShown = false
- this.$body.removeClass('modal-open')
-
- this.resetScrollbar()
this.escape()
$(document).off('focusin.bs.modal')
@@ -150,6 +147,8 @@
var that = this
this.$element.hide()
this.backdrop(function () {
+ that.$body.removeClass('modal-open')
+ that.resetScrollbar()
that.$element.trigger('hidden.bs.modal')
})
}
@@ -167,14 +166,13 @@
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
- .appendTo(this.$body)
-
- this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
- if (e.target !== e.currentTarget) return
- this.options.backdrop == 'static'
- ? this.$element[0].focus.call(this.$element[0])
- : this.hide.call(this)
- }, this))
+ .prependTo(this.$element)
+ .on('click.dismiss.bs.modal', $.proxy(function (e) {
+ if (e.target !== e.currentTarget) return
+ this.options.backdrop == 'static'
+ ? this.$element[0].focus.call(this.$element[0])
+ : this.hide.call(this)
+ }, this))
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
diff --git a/js/popover.js b/js/popover.js
index 87b8d12de..c69be715f 100644
--- a/js/popover.js
+++ b/js/popover.js
@@ -86,12 +86,18 @@
function Plugin(option) {
return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.popover')
- var options = typeof option == 'object' && option
+ var $this = $(this)
+ var data = $this.data('bs.popover')
+ var options = typeof option == 'object' && option
+ var selector = options && options.selector
if (!data && option == 'destroy') return
- if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+ if (selector) {
+ if (!data) $this.data('bs.popover', (data = {}))
+ if (!data[selector]) data[selector] = new Popover(this, options)
+ } else {
+ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+ }
if (typeof option == 'string') data[option]()
})
}
diff --git a/js/scrollspy.js b/js/scrollspy.js
index 841df1878..430b5d6aa 100644
--- a/js/scrollspy.js
+++ b/js/scrollspy.js
@@ -65,7 +65,6 @@
return ($href
&& $href.length
&& $href.is(':visible')
- && $el.is(':visible')
&& [[$href[offsetMethod]().top + offsetBase, href]]) || null
})
.sort(function (a, b) { return a[0] - b[0] })
@@ -92,8 +91,9 @@
return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
}
- if (activeTarget && scrollTop <= offsets[0]) {
- return activeTarget != (i = targets[0]) && this.activate(i)
+ if (activeTarget && scrollTop < offsets[0]) {
+ this.activeTarget = null
+ return this.clear()
}
for (i = offsets.length; i--;) {
@@ -107,9 +107,7 @@
ScrollSpy.prototype.activate = function (target) {
this.activeTarget = target
- $(this.selector)
- .parentsUntil(this.options.target, '.active')
- .removeClass('active')
+ this.clear()
var selector = this.selector +
'[data-target="' + target + '"],' +
@@ -128,6 +126,12 @@
active.trigger('activate.bs.scrollspy')
}
+ ScrollSpy.prototype.clear = function () {
+ $(this.selector)
+ .parentsUntil(this.options.target, '.active')
+ .removeClass('active')
+ }
+
// SCROLLSPY PLUGIN DEFINITION
// ===========================
diff --git a/js/tab.js b/js/tab.js
index d7023c817..dd307a424 100644
--- a/js/tab.js
+++ b/js/tab.js
@@ -33,22 +33,30 @@
if ($this.parent('li').hasClass('active')) return
- var previous = $ul.find('.active:last a')[0]
- var e = $.Event('show.bs.tab', {
- relatedTarget: previous
+ var $previous = $ul.find('.active:last a')
+ var hideEvent = $.Event('hide.bs.tab', {
+ relatedTarget: $this[0]
+ })
+ var showEvent = $.Event('show.bs.tab', {
+ relatedTarget: $previous[0]
})
- $this.trigger(e)
+ $previous.trigger(hideEvent)
+ $this.trigger(showEvent)
- if (e.isDefaultPrevented()) return
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
var $target = $(selector)
this.activate($this.closest('li'), $ul)
this.activate($target, $target.parent(), function () {
+ $previous.trigger({
+ type: 'hidden.bs.tab',
+ relatedTarget: $this[0]
+ })
$this.trigger({
type: 'shown.bs.tab',
- relatedTarget: previous
+ relatedTarget: $previous[0]
})
})
}
@@ -63,9 +71,15 @@
$active
.removeClass('active')
.find('> .dropdown-menu > .active')
- .removeClass('active')
+ .removeClass('active')
+ .end()
+ .find('[data-toggle="tab"]')
+ .attr('aria-expanded', false)
- element.addClass('active')
+ element
+ .addClass('active')
+ .find('[data-toggle="tab"]')
+ .attr('aria-expanded', true)
if (transition) {
element[0].offsetWidth // reflow for transition
@@ -75,7 +89,12 @@
}
if (element.parent('.dropdown-menu')) {
- element.closest('li.dropdown').addClass('active')
+ element
+ .closest('li.dropdown')
+ .addClass('active')
+ .end()
+ .find('[data-toggle="tab"]')
+ .attr('aria-expanded', true)
}
callback && callback()
diff --git a/js/tests/index.html b/js/tests/index.html
index 1c025cf76..194f531aa 100644
--- a/js/tests/index.html
+++ b/js/tests/index.html
@@ -10,6 +10,11 @@
<!-- QUnit -->
<link rel="stylesheet" href="vendor/qunit.css" media="screen">
<script src="vendor/qunit.js"></script>
+ <style>
+ #qunit-tests > li.pass {
+ display: none;/* Make it easier to see failing tests is Sauce screencasts */
+ }
+ </style>
<script>
// See https://github.com/axemclion/grunt-saucelabs#test-result-details-with-qunit
var log = []
diff --git a/js/tests/unit/button.js b/js/tests/unit/button.js
index bd431d546..73747cdd4 100644
--- a/js/tests/unit/button.js
+++ b/js/tests/unit/button.js
@@ -85,7 +85,7 @@ $(function () {
})
test('should toggle active', function () {
- var $btn = $('<button class="btn">mdo</button>')
+ var $btn = $('<button class="btn" data-toggle="button">mdo</button>')
ok(!$btn.hasClass('active'), 'btn does not have active class')
$btn.bootstrapButton('toggle')
ok($btn.hasClass('active'), 'btn has class active')
@@ -102,6 +102,24 @@ $(function () {
ok($btn.hasClass('active'), 'btn has class active')
})
+ test('should toggle aria-pressed', function () {
+ var $btn = $('<button class="btn" data-toggle="button" aria-pressed="false">redux</button>')
+ equal($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false')
+ $btn.bootstrapButton('toggle')
+ equal($btn.attr('aria-pressed'), 'true', 'btn aria-pressed state is true')
+ })
+
+ test('should toggle aria-pressed when btn children are clicked', function () {
+ var $btn = $('<button class="btn" data-toggle="button" aria-pressed="false">redux</button>')
+ var $inner = $('<i/>')
+ $btn
+ .append($inner)
+ .appendTo('#qunit-fixture')
+ equal($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false')
+ $inner.click()
+ equal($btn.attr('aria-pressed'), 'true', 'btn aria-pressed state is true')
+ })
+
test('should toggle active when btn children are clicked within btn-group', function () {
var $btngroup = $('<div class="btn-group" data-toggle="buttons"/>')
var $btn = $('<button class="btn">fat</button>')
diff --git a/js/tests/unit/carousel.js b/js/tests/unit/carousel.js
index 3f9e61a34..6f0b9642f 100644
--- a/js/tests/unit/carousel.js
+++ b/js/tests/unit/carousel.js
@@ -398,4 +398,105 @@ $(function () {
strictEqual($template.find('.item')[1], $template.find('.active')[0], 'second item active')
})
+
+ test('should go to previous item if left arrow key is pressed', function () {
+ var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">'
+ + '<div class="carousel-inner">'
+ + '<div id="first" class="item">'
+ + '<img alt="">'
+ + '</div>'
+ + '<div id="second" class="item active">'
+ + '<img alt="">'
+ + '</div>'
+ + '<div id="third" class="item">'
+ + '<img alt="">'
+ + '</div>'
+ + '</div>'
+ + '</div>'
+ var $template = $(templateHTML)
+
+ $template.bootstrapCarousel()
+
+ strictEqual($template.find('.item')[1], $template.find('.active')[0], 'second item active')
+
+ $template.trigger($.Event('keydown', { which: 37 }))
+
+ strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item active')
+ })
+
+ test('should go to next item if right arrow key is pressed', function () {
+ var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">'
+ + '<div class="carousel-inner">'
+ + '<div id="first" class="item active">'
+ + '<img alt="">'
+ + '</div>'
+ + '<div id="second" class="item">'
+ + '<img alt="">'
+ + '</div>'
+ + '<div id="third" class="item">'
+ + '<img alt="">'
+ + '</div>'
+ + '</div>'
+ + '</div>'
+ var $template = $(templateHTML)
+
+ $template.bootstrapCarousel()
+
+ strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item active')
+
+ $template.trigger($.Event('keydown', { which: 39 }))
+
+ strictEqual($template.find('.item')[1], $template.find('.active')[0], 'second item active')
+ })
+
+ test('should support disabling the keyboard navigation', function () {
+ var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false" data-keyboard="false">'
+ + '<div class="carousel-inner">'
+ + '<div id="first" class="item active">'
+ + '<img alt="">'
+ + '</div>'
+ + '<div id="second" class="item">'
+ + '<img alt="">'
+ + '</div>'
+ + '<div id="third" class="item">'
+ + '<img alt="">'
+ + '</div>'
+ + '</div>'
+ + '</div>'
+ var $template = $(templateHTML)
+
+ $template.bootstrapCarousel()
+
+ strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item active')
+
+ $template.trigger($.Event('keydown', { which: 39 }))
+
+ strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item still active after right arrow press')
+
+ $template.trigger($.Event('keydown', { which: 37 }))
+
+ strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item still active after left arrow press')
+ })
+
+ test('should only add mouseenter and mouseleave listeners when not on mobile', function () {
+ var isMobile = 'ontouchstart' in document.documentElement
+ var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false" data-pause="hover">'
+ + '<div class="carousel-inner">'
+ + '<div id="first" class="item active">'
+ + '<img alt="">'
+ + '</div>'
+ + '<div id="second" class="item">'
+ + '<img alt="">'
+ + '</div>'
+ + '<div id="third" class="item">'
+ + '<img alt="">'
+ + '</div>'
+ + '</div>'
+ + '</div>'
+ var $template = $(templateHTML).bootstrapCarousel()
+
+ $.each(['mouseover', 'mouseout'], function (i, type) {
+ strictEqual(type in $._data($template[0], 'events'), !isMobile, 'does' + (isMobile ? ' not' : '') + ' listen for ' + type + ' events')
+ })
+ })
})
diff --git a/js/tests/unit/collapse.js b/js/tests/unit/collapse.js
index 825f79360..c1d78575c 100644
--- a/js/tests/unit/collapse.js
+++ b/js/tests/unit/collapse.js
@@ -263,4 +263,39 @@ $(function () {
$target3.click()
})
+ test('should not fire show event if show is prevented because other element is still transitioning', function () {
+ stop()
+
+ var accordionHTML = '<div id="accordion">'
+ + '<div class="panel"/>'
+ + '<div class="panel"/>'
+ + '</div>'
+ var showFired = false
+ var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.panel')
+
+ var $target1 = $('<a data-toggle="collapse" href="#body1" data-parent="#accordion"/>').appendTo($groups.eq(0))
+
+ $('<div id="body1" class="collapse"/>')
+ .appendTo($groups.eq(0))
+ .on('show.bs.collapse', function () {
+ showFired = true
+ })
+
+ var $target2 = $('<a data-toggle="collapse" href="#body2" data-parent="#accordion"/>').appendTo($groups.eq(1))
+ var $body2 = $('<div id="body2" class="collapse"/>').appendTo($groups.eq(1))
+
+ $target2.click()
+
+ $body2
+ .toggleClass('in collapsing')
+ .data('bs.collapse').transitioning = 1
+
+ $target1.click()
+
+ setTimeout(function () {
+ ok(!showFired, 'show event didn\'t fire')
+ start()
+ }, 1)
+ })
+
})
diff --git a/js/tests/unit/modal.js b/js/tests/unit/modal.js
index 2262465a4..efd478b20 100644
--- a/js/tests/unit/modal.js
+++ b/js/tests/unit/modal.js
@@ -141,7 +141,7 @@ $(function () {
notEqual($('#modal-test').length, 0, 'modal insterted into dom')
$('.contents').click()
ok($('#modal-test').is(':visible'), 'modal visible')
- $('#modal-test').click()
+ $('#modal-test .modal-backdrop').click()
})
.on('hidden.bs.modal', function () {
ok(!$('#modal-test').is(':visible'), 'modal hidden')
@@ -196,7 +196,7 @@ $(function () {
$('<div id="modal-test"><div class="contents"/></div>')
.on('shown.bs.modal', function () {
triggered = 0
- $('#modal-test').click()
+ $('#modal-test .modal-backdrop').click()
})
.on('hide.bs.modal', function () {
triggered += 1
diff --git a/js/tests/unit/popover.js b/js/tests/unit/popover.js
index 5cb4cafdd..466ebace2 100644
--- a/js/tests/unit/popover.js
+++ b/js/tests/unit/popover.js
@@ -30,7 +30,7 @@ $(function () {
})
test('should render popover element', function () {
- var $popover = $('<a href="#" title="mdo" data-content="http://twitter.com/mdo">@mdo</a>')
+ var $popover = $('<a href="#" title="mdo" data-content="https://twitter.com/mdo">@mdo</a>')
.appendTo('#qunit-fixture')
.bootstrapPopover('show')
@@ -40,7 +40,7 @@ $(function () {
})
test('should store popover instance in popover data object', function () {
- var $popover = $('<a href="#" title="mdo" data-content="http://twitter.com/mdo">@mdo</a>').bootstrapPopover()
+ var $popover = $('<a href="#" title="mdo" data-content="https://twitter.com/mdo">@mdo</a>').bootstrapPopover()
ok($popover.data('bs.popover'), 'popover instance exists')
})
@@ -173,4 +173,48 @@ $(function () {
ok(!$._data($popover[0], 'events').mouseover && !$._data($popover[0], 'events').mouseout, 'popover does not have any events')
})
+ test('should render popover element using delegated selector', function () {
+ var $div = $('<div><a href="#" title="mdo" data-content="http://twitter.com/mdo">@mdo</a></div>')
+ .appendTo('#qunit-fixture')
+ .bootstrapPopover({
+ selector: 'a',
+ trigger: 'click'
+ })
+
+ $div.find('a').click()
+ notEqual($('.popover').length, 0, 'popover was inserted')
+
+ $div.find('a').click()
+ equal($('.popover').length, 0, 'popover was removed')
+ })
+
+ test('should render popover elements using different delegated selectors on the same node', function () {
+ var popoverHTML = '<div>'
+ + '<a href="#" class="first" title="mdo" data-content="http://twitter.com/mdo">@mdo</a>'
+ + '<a href="#" class="second" title="mdo" data-content="http://twitter.com/mdo">@mdo</a>'
+ + '</div>'
+
+ var $div = $(popoverHTML)
+ .appendTo('#qunit-fixture')
+ .bootstrapPopover({
+ selector: 'a.first',
+ trigger: 'click'
+ })
+ .bootstrapPopover({
+ selector: 'a.second',
+ trigger: 'click'
+ })
+
+ $div.find('a.first').click()
+ notEqual($('.popover').length, 0, 'first popover was inserted')
+
+ $div.find('a.first').click()
+ equal($('.popover').length, 0, 'first popover removed')
+
+ $div.find('a.second').click()
+ notEqual($('.popover').length, 0, 'second popover was inserted')
+
+ $div.find('a.second').click()
+ equal($('.popover').length, 0, 'second popover removed')
+ })
})
diff --git a/js/tests/unit/scrollspy.js b/js/tests/unit/scrollspy.js
index c071d0f65..0c9081491 100644
--- a/js/tests/unit/scrollspy.js
+++ b/js/tests/unit/scrollspy.js
@@ -29,22 +29,6 @@ $(function () {
strictEqual($scrollspy[0], $el[0], 'collection contains element')
})
- // Does not work properly ATM, #13500 will fix this
- test('should switch "active" class on scroll', function () {
- var topbarHTML = '<div class="topbar">'
- + '<div class="topbar-inner">'
- + '<div class="container">'
- + '<h3><a href="#">Bootstrap</a></h3>'
- + '<li><a href="#masthead">Overview</a></li>'
- + '</ul>'
- + '</div>'
- + '</div>'
- + '</div>'
- var $topbar = $(topbarHTML).bootstrapScrollspy()
-
- ok($topbar.find('.active', true))
- })
-
test('should only switch "active" class on current target', function () {
stop()
@@ -77,9 +61,9 @@ $(function () {
var $section = $(sectionHTML).appendTo('#qunit-fixture')
var $scrollspy = $section
- .show()
- .find('#scrollspy-example')
- .bootstrapScrollspy({ target: '#ss-target' })
+ .show()
+ .find('#scrollspy-example')
+ .bootstrapScrollspy({ target: '#ss-target' })
$scrollspy.on('scroll.bs.scrollspy', function () {
ok($section.hasClass('active'), '"active" class still on root node')
@@ -89,7 +73,7 @@ $(function () {
$scrollspy.scrollTop(350)
})
- test('middle navigation option correctly selected when large offset is used', function () {
+ test('should correctly select middle navigation option when large offset is used', function () {
stop()
var sectionHTML = '<div id="header" style="height: 500px;"></div>'
@@ -107,8 +91,8 @@ $(function () {
+ '</div>'
var $section = $(sectionHTML).appendTo('#qunit-fixture')
var $scrollspy = $section
- .show()
- .filter('#content')
+ .show()
+ .filter('#content')
$scrollspy.bootstrapScrollspy({ target: '#navigation', offset: $scrollspy.position().top })
@@ -158,4 +142,45 @@ $(function () {
.then(function () { return testElementIsActiveAfterScroll('#li-2', '#div-2') })
})
+ test('should clear selection if above the first section', function () {
+ stop()
+
+ var sectionHTML = '<div id="header" style="height: 500px;"></div>'
+ + '<nav id="navigation" class="navbar">'
+ + '<ul class="nav navbar-nav">'
+ + '<li class="active"><a id="one-link" href="#one">One</a></li>'
+ + '<li><a id="two-link" href="#two">Two</a></li>'
+ + '<li><a id="three-link" href="#three">Three</a></li>'
+ + '</ul>'
+ + '</nav>'
+ $(sectionHTML).appendTo('#qunit-fixture')
+
+ var scrollspyHTML = '<div id="content" style="height: 200px; overflow-y: auto;">'
+ + '<div id="spacer" style="height: 100px;"/>'
+ + '<div id="one" style="height: 100px;"/>'
+ + '<div id="two" style="height: 100px;"/>'
+ + '<div id="three" style="height: 100px;"/>'
+ + '<div id="spacer" style="height: 100px;"/>'
+ + '</div>'
+ var $scrollspy = $(scrollspyHTML).appendTo('#qunit-fixture')
+
+ $scrollspy
+ .bootstrapScrollspy({
+ target: '#navigation',
+ offset: $scrollspy.position().top
+ })
+ .one('scroll.bs.scrollspy', function () {
+ strictEqual($('.active').length, 1, '"active" class on only one element present')
+ strictEqual($('.active').has('#two-link').length, 1, '"active" class on second section')
+
+ $scrollspy
+ .one('scroll.bs.scrollspy', function () {
+ strictEqual($('.active').length, 0, 'selection cleared')
+ start()
+ })
+ .scrollTop(0)
+ })
+ .scrollTop(201)
+ })
+
})
diff --git a/js/tests/unit/tab.js b/js/tests/unit/tab.js
index 8e50614ec..9b2a18d57 100644
--- a/js/tests/unit/tab.js
+++ b/js/tests/unit/tab.js
@@ -101,4 +101,105 @@ $(function () {
.bootstrapTab('show')
})
+ test('should fire hide and hidden events', function () {
+ stop()
+
+ var tabsHTML = '<ul class="tabs">'
+ + '<li><a href="#home">Home</a></li>'
+ + '<li><a href="#profile">Profile</a></li>'
+ + '</ul>'
+
+ $(tabsHTML)
+ .find('li:first a')
+ .on('hide.bs.tab', function () {
+ ok(true, 'hide event fired')
+ })
+ .bootstrapTab('show')
+ .end()
+ .find('li:last a')
+ .bootstrapTab('show')
+
+ $(tabsHTML)
+ .find('li:first a')
+ .on('hidden.bs.tab', function () {
+ ok(true, 'hidden event fired')
+ start()
+ })
+ .bootstrapTab('show')
+ .end()
+ .find('li:last a')
+ .bootstrapTab('show')
+ })
+
+ test('should not fire hidden when hide is prevented', function () {
+ stop()
+
+ var tabsHTML = '<ul class="tabs">'
+ + '<li><a href="#home">Home</a></li>'
+ + '<li><a href="#profile">Profile</a></li>'
+ + '</ul>'
+
+ $(tabsHTML)
+ .find('li:first a')
+ .on('hide.bs.tab', function (e) {
+ e.preventDefault()
+ ok(true, 'hide event fired')
+ start()
+ })
+ .on('hidden.bs.tab', function () {
+ ok(false, 'hidden event fired')
+ })
+ .bootstrapTab('show')
+ .end()
+ .find('li:last a')
+ .bootstrapTab('show')
+ })
+
+ test('hide and hidden events contain correct relatedTarget', function () {
+ stop()
+
+ var tabsHTML = '<ul class="tabs">'
+ + '<li><a href="#home">Home</a></li>'
+ + '<li><a href="#profile">Profile</a></li>'
+ + '</ul>'
+
+ $(tabsHTML)
+ .find('li:first a')
+ .on('hide.bs.tab', function (e) {
+ equal(e.relatedTarget.hash, '#profile', 'references correct element as relatedTarget')
+ })
+ .on('hidden.bs.tab', function (e) {
+ equal(e.relatedTarget.hash, '#profile', 'references correct element as relatedTarget')
+ start()
+ })
+ .bootstrapTab('show')
+ .end()
+ .find('li:last a')
+ .bootstrapTab('show')
+ })
+
+ test('selected tab should have aria-expanded', function () {
+ var tabsHTML = '<ul class="nav nav-tabs">'
+ + '<li class="active"><a href="#home" toggle="tab" aria-expanded="true">Home</a></li>'
+ + '<li><a href="#profile" toggle="tab" aria-expanded="false">Profile</a></li>'
+ + '</ul>'
+ var $tabs = $(tabsHTML).appendTo('#qunit-fixture')
+
+ $tabs.find('li:first a').bootstrapTab('show')
+ equal($tabs.find('.active a').attr('aria-expanded'), 'true', 'shown tab has aria-expanded = true')
+ equal($tabs.find('li:not(.active) a').attr('aria-expanded'), 'false', 'hidden tab has aria-expanded = false')
+
+ $tabs.find('li:last a').click()
+ equal($tabs.find('.active a').attr('aria-expanded'), 'true', 'after click, shown tab has aria-expanded = true')
+ equal($tabs.find('li:not(.active) a').attr('aria-expanded'), 'false', 'after click, hidden tab has aria-expanded = false')
+
+ $tabs.find('li:first a').bootstrapTab('show')
+ equal($tabs.find('.active a').attr('aria-expanded'), 'true', 'shown tab has aria-expanded = true')
+ equal($tabs.find('li:not(.active) a').attr('aria-expanded'), 'false', 'hidden tab has aria-expanded = false')
+
+ $tabs.find('li:first a').click()
+ equal($tabs.find('.active a').attr('aria-expanded'), 'true', 'after second show event, shown tab still has aria-expanded = true')
+ equal($tabs.find('li:not(.active) a').attr('aria-expanded'), 'false', 'after second show event, hidden tab has aria-expanded = false')
+ })
+
})
diff --git a/js/tests/unit/tooltip.js b/js/tests/unit/tooltip.js
index c75924e9f..351dd61cb 100644
--- a/js/tests/unit/tooltip.js
+++ b/js/tests/unit/tooltip.js
@@ -235,6 +235,37 @@ $(function () {
equal($('.tooltip').length, 0, 'tooltip was removed from dom')
})
+ test('should show tooltips with different delegate selectors on the same node on click', function () {
+ var tooltipHTML = '<div>'
+ + '<a href="#" class="first" rel="tooltip" title="First delegated tooltip"/>'
+ + '<a href="#" class="second" rel="tooltip" title="Second delegated tooltip"/>'
+ + '</div>'
+
+ var $div = $(tooltipHTML)
+ .append()
+ .appendTo('#qunit-fixture')
+ .bootstrapTooltip({
+ selector: 'a.first[rel="tooltip"]',
+ trigger: 'click'
+ })
+ .bootstrapTooltip({
+ selector: 'a.second[rel="tooltip"]',
+ trigger: 'click'
+ })
+
+ $div.find('a.first').click()
+ ok($('.tooltip').is('.fade.in'), 'first tooltip is faded in')
+
+ $div.find('a.first').click()
+ equal($('.tooltip').length, 0, 'first tooltip was removed from dom')
+
+ $div.find('a.second').click()
+ ok($('.tooltip').is('.fade.in'), 'second tooltip is faded in')
+
+ $div.find('a.second').click()
+ equal($('.tooltip').length, 0, 'second tooltip was removed from dom')
+ })
+
test('should show tooltip when toggle is called', function () {
$('<a href="#" rel="tooltip" title="tooltip on toggle"/>')
.appendTo('#qunit-fixture')
@@ -382,6 +413,164 @@ $(function () {
$style.remove()
})
+ test('should position tip on top if viewport has enough space and placement is "auto top"', function () {
+ var styles = '<style>'
+ + 'body { padding-top: 100px; }'
+ + '#section { height: 300px; border: 1px solid red; padding-top: 50px }'
+ + 'div[rel="tooltip"] { width: 150px; border: 1px solid blue; }'
+ + '</style>'
+ var $styles = $(styles).appendTo('head')
+
+ var $container = $('<div id="section"/>').appendTo('#qunit-fixture')
+ var $target = $('<div rel="tooltip" title="tip"/>')
+ .appendTo($container)
+ .bootstrapTooltip({
+ placement: 'auto top',
+ viewport: '#section'
+ })
+
+ $target.bootstrapTooltip('show')
+ ok($('.tooltip').is('.top'), 'top positioned tooltip is dynamically positioned to top')
+
+ $target.bootstrapTooltip('hide')
+ equal($('.tooltip').length, 0, 'tooltip removed from dom')
+
+ $styles.remove()
+ })
+
+ test('should position tip on bottom if the tip\'s dimension exceeds the viewport area and placement is "auto top"', function () {
+ var styles = '<style>'
+ + 'body { padding-top: 100px; }'
+ + '#section { height: 300px; border: 1px solid red; }'
+ + 'div[rel="tooltip"] { width: 150px; border: 1px solid blue; }'
+ + '</style>'
+ var $styles = $(styles).appendTo('head')
+
+ var $container = $('<div id="section"/>').appendTo('#qunit-fixture')
+ var $target = $('<div rel="tooltip" title="tip"/>')
+ .appendTo($container)
+ .bootstrapTooltip({
+ placement: 'auto top',
+ viewport: '#section'
+ })
+
+ $target.bootstrapTooltip('show')
+ ok($('.tooltip').is('.bottom'), 'top positioned tooltip is dynamically positioned to bottom')
+
+ $target.bootstrapTooltip('hide')
+ equal($('.tooltip').length, 0, 'tooltip removed from dom')
+
+ $styles.remove()
+ })
+
+ test('should display the tip on top whenever scrollable viewport has enough room if the given placement is "auto top"', function () {
+ var styles = '<style>'
+ + '#scrollable-div { height: 200px; overflow: auto; }'
+ + '.tooltip-item { margin: 200px 0 400px; width: 150px; }'
+ + '</style>'
+ var $styles = $(styles).appendTo('head')
+
+ var $container = $('<div id="scrollable-div"/>').appendTo('#qunit-fixture')
+ var $target = $('<div rel="tooltip" title="tip" class="tooltip-item">Tooltip Item</div>')
+ .appendTo($container)
+ .bootstrapTooltip({
+ placement: 'top auto',
+ viewport: '#scrollable-div'
+ })
+
+ $('#scrollable-div').scrollTop(100)
+
+ $target.bootstrapTooltip('show')
+ ok($('.tooltip').is('.fade.top.in'), 'has correct classes applied')
+
+ $target.bootstrapTooltip('hide')
+ equal($('.tooltip').length, 0, 'tooltip removed from dom')
+
+ $styles.remove()
+ })
+
+ test('should display the tip on bottom whenever scrollable viewport doesn\'t have enough room if the given placement is "auto top"', function () {
+ var styles = '<style>'
+ + '#scrollable-div { height: 200px; overflow: auto; }'
+ + '.tooltip-item { padding: 200px 0 400px; width: 150px; }'
+ + '</style>'
+ var $styles = $(styles).appendTo('head')
+
+ var $container = $('<div id="scrollable-div"/>').appendTo('#qunit-fixture')
+ var $target = $('<div rel="tooltip" title="tip" class="tooltip-item">Tooltip Item</div>')
+ .appendTo($container)
+ .bootstrapTooltip({
+ placement: 'top auto',
+ viewport: '#scrollable-div'
+ })
+
+ $('#scrollable-div').scrollTop(200)
+
+ $target.bootstrapTooltip('show')
+ ok($('.tooltip').is('.fade.bottom.in'), 'has correct classes applied')
+
+ $target.bootstrapTooltip('hide')
+ equal($('.tooltip').length, 0, 'tooltip removed from dom')
+
+ $styles.remove()
+ })
+
+ test('should display the tip on bottom whenever scrollable viewport has enough room if the given placement is "auto bottom"', function () {
+ var styles = '<style>'
+ + '#scrollable-div { height: 200px; overflow: auto; }'
+ + '.spacer { height: 400px; }'
+ + '.spacer:first-child { height: 200px; }'
+ + '.tooltip-item { width: 150px; }'
+ + '</style>'
+ var $styles = $(styles).appendTo('head')
+
+ var $container = $('<div id="scrollable-div"/>').appendTo('#qunit-fixture')
+ var $target = $('<div rel="tooltip" title="tip" class="tooltip-item">Tooltip Item</div>')
+ .appendTo($container)
+ .before('<div class="spacer"/>')
+ .after('<div class="spacer"/>')
+ .bootstrapTooltip({
+ placement: 'bottom auto',
+ viewport: '#scrollable-div'
+ })
+
+ $('#scrollable-div').scrollTop(200)
+
+ $target.bootstrapTooltip('show')
+ ok($('.tooltip').is('.fade.bottom.in'), 'has correct classes applied')
+
+ $target.bootstrapTooltip('hide')
+ equal($('.tooltip').length, 0, 'tooltip removed from dom')
+
+ $styles.remove()
+ })
+
+ test('should display the tip on top whenever scrollable viewport doesn\'t have enough room if the given placement is "auto bottom"', function () {
+ var styles = '<style>'
+ + '#scrollable-div { height: 200px; overflow: auto; }'
+ + '.tooltip-item { margin-top: 400px; width: 150px; }'
+ + '</style>'
+ var $styles = $(styles).appendTo('head')
+
+ var $container = $('<div id="scrollable-div"/>').appendTo('#qunit-fixture')
+ var $target = $('<div rel="tooltip" title="tip" class="tooltip-item">Tooltip Item</div>')
+ .appendTo($container)
+ .bootstrapTooltip({
+ placement: 'bottom auto',
+ viewport: '#scrollable-div'
+ })
+
+ $('#scrollable-div').scrollTop(400)
+
+ $target.bootstrapTooltip('show')
+ ok($('.tooltip').is('.fade.top.in'), 'has correct classes applied')
+
+ $target.bootstrapTooltip('hide')
+ equal($('.tooltip').length, 0, 'tooltip removed from dom')
+
+ $styles.remove()
+ })
+
test('should adjust the tip\'s top position when up against the top of the viewport', function () {
var styles = '<style>'
+ '.tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
@@ -609,7 +798,7 @@ $(function () {
}, 0)
})
- test('should show tooltip if leave event hasn\'t occured before delay expires', function () {
+ test('should show tooltip if leave event hasn\'t occurred before delay expires', function () {
stop()
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
@@ -777,6 +966,47 @@ $(function () {
$circle.bootstrapTooltip('show')
})
+ test('should correctly determine auto placement based on container rather than parent', function () {
+ stop()
+
+ var styles = '<style>'
+ + '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
+ + '.tooltip { position: absolute; display: block; font-size: 12px; line-height: 1.4; }'
+ + '.tooltip .tooltip-inner { max-width: 200px; padding: 3px 8px; font-family: Helvetica; text-align: center; }'
+ + '#trigger-parent {'
+ + ' position: fixed;'
+ + ' top: 100px;'
+ + ' right: 17px;'
+ + '}'
+ + '</style>'
+ var $styles = $(styles).appendTo('head')
+
+ $('#qunit-fixture').append('<span id="trigger-parent"><a id="tt-trigger" title="If a_larger_text is written here, it won\'t fit using older broken version of BS">HOVER OVER ME</a></span>')
+ var $trigger = $('#tt-trigger')
+
+ $trigger
+ .on('shown.bs.tooltip', function () {
+ var $tip = $('.tooltip-inner')
+ var tipXrightEdge = $tip.offset().left + $tip.width()
+ var triggerXleftEdge = $trigger.offset().left
+ ok(tipXrightEdge < triggerXleftEdge, 'tooltip with auto left placement, when near the right edge of the viewport, gets left placement')
+ $trigger.bootstrapTooltip('hide')
+ })
+ .on('hidden.bs.tooltip', function () {
+ $styles.remove()
+ $(this).remove()
+ equal($('.tooltip').length, 0, 'tooltip removed from dom')
+ start()
+ })
+ .bootstrapTooltip({
+ container: 'body',
+ placement: 'auto left',
+ trigger: 'manual'
+ })
+
+ $trigger.bootstrapTooltip('show')
+ })
+
test('should not reload the tooltip on subsequent mouseenter events', function () {
var titleHtml = function () {
var uid = $.fn.bootstrapTooltip.Constructor.prototype.getUID('tooltip')
@@ -859,6 +1089,7 @@ $(function () {
.on('hidden.bs.tooltip', function () {
$styles.remove()
$(this).remove()
+ equal($('.tooltip').length, 0, 'tooltip removed from dom')
start()
})
.bootstrapTooltip({
@@ -869,4 +1100,42 @@ $(function () {
.bootstrapTooltip('show')
})
+ test('should correctly position tooltips on transformed elements', function () {
+ var styleProps = document.documentElement.style
+ if (!('transform' in styleProps) && !('webkitTransform' in styleProps) && !('msTransform' in styleProps)) {
+ expect(0)
+ return
+ }
+
+ stop()
+
+ var styles = '<style>'
+ + '#qunit-fixture { top: 0; left: 0; }'
+ + '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
+ + '.tooltip { position: absolute; }'
+ + '.tooltip .tooltip-inner { width: 24px; height: 24px; font-family: Helvetica; }'
+ + '#target { position: absolute; top: 100px; left: 50px; width: 100px; height: 200px; -webkit-transform: rotate(270deg); -ms-transform: rotate(270deg); transform: rotate(270deg); }'
+ + '</style>'
+ var $styles = $(styles).appendTo('head')
+
+ var $element = $('<div id="target" title="1"/>').appendTo('#qunit-fixture')
+
+ $element
+ .on('shown.bs.tooltip', function () {
+ var offset = $('.tooltip').offset()
+ $styles.remove()
+ ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location')
+ ok(Math.abs(offset.top - 126) <= 1, 'tooltip has correct vertical location')
+ $element.bootstrapTooltip('hide')
+ start()
+ })
+ .bootstrapTooltip({
+ container: 'body',
+ placement: 'top',
+ trigger: 'manual'
+ })
+
+ $element.bootstrapTooltip('show')
+ })
+
})
diff --git a/js/tests/visual/dropdown.html b/js/tests/visual/dropdown.html
index 455a4f571..6c7f52b2f 100644
--- a/js/tests/visual/dropdown.html
+++ b/js/tests/visual/dropdown.html
@@ -28,21 +28,21 @@
<li class="dropdown">
<a id="drop1" href="#" role="button" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="drop1">
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Action</a></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Another action</a></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Something else here</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Another action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Something else here</a></li>
<li role="presentation" class="divider"></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Separated link</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Separated link</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" id="drop2" role="button" class="dropdown-toggle" data-toggle="dropdown">Dropdown 2 <b class="caret"></b></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="drop2">
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Action</a></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Another action</a></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Something else here</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Another action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Something else here</a></li>
<li role="presentation" class="divider"></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Separated link</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Separated link</a></li>
</ul>
</li>
</ul>
@@ -50,11 +50,11 @@
<li id="fat-menu" class="dropdown">
<a href="#" id="drop3" role="button" class="dropdown-toggle" data-toggle="dropdown">Dropdown 3 <b class="caret"></b></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="drop3">
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Action</a></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Another action</a></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Something else here</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Another action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Something else here</a></li>
<li role="presentation" class="divider"></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Separated link</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Separated link</a></li>
</ul>
</li>
</ul>
@@ -67,21 +67,21 @@
<li class="dropdown">
<a id="drop4" role="button" data-toggle="dropdown" href="#">Dropdown <b class="caret"></b></a>
<ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Action</a></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Another action</a></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Something else here</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Another action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Something else here</a></li>
<li role="presentation" class="divider"></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Separated link</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Separated link</a></li>
</ul>
</li>
<li class="dropdown">
<a id="drop5" role="button" data-toggle="dropdown" href="#">Dropdown 2 <b class="caret"></b></a>
<ul id="menu2" class="dropdown-menu" role="menu" aria-labelledby="drop5">
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Action</a></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Another action</a></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Something else here</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Another action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Something else here</a></li>
<li role="presentation" class="divider"></li>
- <li role="presentation"><a role="menuitem" tabindex="-1" href="http://twitter.com/fat">Separated link</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="https://twitter.com/fat">Separated link</a></li>
</ul>
</li>
</ul>
@@ -92,6 +92,7 @@
<script src="../vendor/jquery.min.js"></script>
<script src="../../transition.js"></script>
<script src="../../dropdown.js"></script>
+<script src="../../collapse.js"></script>
</body>
</html>
diff --git a/js/tooltip.js b/js/tooltip.js
index 7194b5d01..6af0e7d72 100644
--- a/js/tooltip.js
+++ b/js/tooltip.js
@@ -188,13 +188,13 @@
if (autoPlace) {
var orgPlacement = placement
- var $parent = this.$element.parent()
- var parentDim = this.getPosition($parent)
+ var $container = this.options.container ? $(this.options.container) : this.$element.parent()
+ var containerDim = this.getPosition($container)
- placement = placement == 'bottom' && pos.top + pos.height + actualHeight - parentDim.scroll > parentDim.height ? 'top' :
- placement == 'top' && pos.top - parentDim.scroll - actualHeight < 0 ? 'bottom' :
- placement == 'right' && pos.right + actualWidth > parentDim.width ? 'left' :
- placement == 'left' && pos.left - actualWidth < parentDim.left ? 'right' :
+ placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
+ placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
+ placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
+ placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
placement
$tip
@@ -207,8 +207,11 @@
this.applyPlacement(calculatedOffset, placement)
var complete = function () {
+ var prevHoverState = that.hoverState
that.$element.trigger('shown.bs.' + that.type)
that.hoverState = null
+
+ if (prevHoverState == 'out') that.leave(that)
}
$.support.transition && this.$tip.hasClass('fade') ?
@@ -329,7 +332,6 @@
var el = $element[0]
var isBody = el.tagName == 'BODY'
- var isSvg = window.SVGElement && el instanceof window.SVGElement
var elRect = el.getBoundingClientRect()
if (elRect.width == null) {
@@ -338,10 +340,7 @@
}
var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
- var outerDims = isSvg ? {} : {
- width: isBody ? $(window).width() : $element.outerWidth(),
- height: isBody ? $(window).height() : $element.outerHeight()
- }
+ var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
return $.extend({}, elRect, scroll, outerDims, elOffset)
}
@@ -446,12 +445,18 @@
function Plugin(option) {
return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.tooltip')
- var options = typeof option == 'object' && option
+ var $this = $(this)
+ var data = $this.data('bs.tooltip')
+ var options = typeof option == 'object' && option
+ var selector = options && options.selector
if (!data && option == 'destroy') return
- if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+ if (selector) {
+ if (!data) $this.data('bs.tooltip', (data = {}))
+ if (!data[selector]) data[selector] = new Tooltip(this, options)
+ } else {
+ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+ }
if (typeof option == 'string') data[option]()
})
}