diff options
| author | Mark Otto <[email protected]> | 2014-08-02 18:30:59 -0700 |
|---|---|---|
| committer | Mark Otto <[email protected]> | 2014-08-02 18:30:59 -0700 |
| commit | 089d434895fbe901d838e4dfb29cdb5941c47ab6 (patch) | |
| tree | dac25fa32c4420bfb3b1199c61898f04b71eaebe /js | |
| parent | f200c39657c5011e12bace716c59b2242d48c077 (diff) | |
| parent | 033f1654b07f7764a54e5b290e5cf36f688b95a3 (diff) | |
| download | bootstrap-089d434895fbe901d838e4dfb29cdb5941c47ab6.tar.xz bootstrap-089d434895fbe901d838e4dfb29cdb5941c47ab6.zip | |
Merge branch 'master' into derp
Conflicts:
_config.yml
dist/css/bootstrap-theme.css.map
dist/css/bootstrap.css
dist/css/bootstrap.css.map
dist/css/bootstrap.min.css
docs/_includes/components/glyphicons.html
docs/_includes/css/forms.html
docs/_includes/css/tables.html
docs/_includes/getting-started/browser-device-support.html
docs/_includes/header.html
docs/_includes/js/affix.html
docs/_includes/js/alerts.html
docs/_includes/js/buttons.html
docs/_includes/js/dropdowns.html
docs/_includes/js/overview.html
docs/_includes/js/popovers.html
docs/_includes/js/tooltips.html
docs/_includes/nav/javascript.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/dist/css/bootstrap-theme.css.map
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
less/code.less
less/panels.less
less/variables.less
Diffstat (limited to 'js')
| -rw-r--r-- | js/.jscsrc | 3 | ||||
| -rw-r--r-- | js/affix.js | 58 | ||||
| -rw-r--r-- | js/alert.js | 4 | ||||
| -rw-r--r-- | js/button.js | 16 | ||||
| -rw-r--r-- | js/carousel.js | 15 | ||||
| -rw-r--r-- | js/collapse.js | 6 | ||||
| -rw-r--r-- | js/dropdown.js | 12 | ||||
| -rw-r--r-- | js/modal.js | 11 | ||||
| -rw-r--r-- | js/tab.js | 4 | ||||
| -rw-r--r-- | js/tests/unit/carousel.js | 27 | ||||
| -rw-r--r-- | js/tests/unit/tooltip.js | 442 | ||||
| -rw-r--r-- | js/tests/visual/affix.html | 68 | ||||
| -rw-r--r-- | js/tooltip.js | 6 |
13 files changed, 413 insertions, 259 deletions
diff --git a/js/.jscsrc b/js/.jscsrc index 619690a68..9612c1683 100644 --- a/js/.jscsrc +++ b/js/.jscsrc @@ -5,9 +5,8 @@ "disallowMultipleLineStrings": true, "disallowMultipleVarDecl": true, "disallowQuotedKeysInObjects": "allButReserved", - "disallowSpaceAfterPrefixUnaryOperators": ["!"], - "disallowSpaceBeforeBinaryOperators": [","], "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], + "disallowSpaceBeforeBinaryOperators": [","], "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], "disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true }, "disallowSpacesInsideArrayBrackets": true, diff --git a/js/affix.js b/js/affix.js index 26b09db56..bd48fc2ca 100644 --- a/js/affix.js +++ b/js/affix.js @@ -37,6 +37,28 @@ target: window } + Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + var targetHeight = this.$target.height() + + if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false + + if (this.affixed == 'bottom') { + if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' + return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' + } + + var initializing = this.affixed == null + var colliderTop = initializing ? scrollTop : position.top + var colliderHeight = initializing ? targetHeight : height + + if (offsetTop != null && colliderTop <= offsetTop) return 'top' + if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' + + return false + } + Affix.prototype.getPinnedOffset = function () { if (this.pinnedOffset) return this.pinnedOffset this.$element.removeClass(Affix.RESET).addClass('affix') @@ -52,42 +74,40 @@ Affix.prototype.checkPosition = function () { if (!this.$element.is(':visible')) return - var scrollHeight = $(document).height() - var scrollTop = this.$target.scrollTop() - var position = this.$element.offset() + var height = this.$element.height() var offset = this.options.offset var offsetTop = offset.top var offsetBottom = offset.bottom + var scrollHeight = $('body').height() if (typeof offset != 'object') offsetBottom = offsetTop = offset if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) - var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false : - offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' : - offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false + var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) - if (this.affixed === affix) return - if (this.unpin != null) this.$element.css('top', '') + if (this.affixed != affix) { + if (this.unpin != null) this.$element.css('top', '') - var affixType = 'affix' + (affix ? '-' + affix : '') - var e = $.Event(affixType + '.bs.affix') + var affixType = 'affix' + (affix ? '-' + affix : '') + var e = $.Event(affixType + '.bs.affix') - this.$element.trigger(e) + this.$element.trigger(e) - if (e.isDefaultPrevented()) return + if (e.isDefaultPrevented()) return - this.affixed = affix - this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null + this.affixed = affix + this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null - this.$element - .removeClass(Affix.RESET) - .addClass(affixType) - .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') + this.$element + .removeClass(Affix.RESET) + .addClass(affixType) + .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') + } if (affix == 'bottom') { this.$element.offset({ - top: scrollHeight - this.$element.height() - offsetBottom + top: scrollHeight - height - offsetBottom }) } } diff --git a/js/alert.js b/js/alert.js index 0efd92cba..5389c32e9 100644 --- a/js/alert.js +++ b/js/alert.js @@ -20,6 +20,8 @@ Alert.VERSION = '3.2.0' + Alert.TRANSITION_DURATION = 150 + Alert.prototype.close = function (e) { var $this = $(this) var selector = $this.attr('data-target') @@ -51,7 +53,7 @@ $.support.transition && $parent.hasClass('fade') ? $parent .one('bsTransitionEnd', removeElement) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Alert.TRANSITION_DURATION) : removeElement() } diff --git a/js/button.js b/js/button.js index 7e2a6e4e8..b3e944c59 100644 --- a/js/button.js +++ b/js/button.js @@ -97,15 +97,6 @@ } - // FOCUS SHIM (FOR BUTTON GROUPS) - // ============================== - - function getBtnTarget(target) { - var $target = $(target) - return $target.hasClass('btn') ? $target : $target.parent('.btn') - } - - // BUTTON DATA-API // =============== @@ -116,11 +107,8 @@ Plugin.call($btn, 'toggle') e.preventDefault() }) - .on('focus.bs.button.data-api', '[data-toggle^="button"]', function (e) { - getBtnTarget(e.target).addClass('focus') - }) - .on('blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { - getBtnTarget(e.target).removeClass('focus') + .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { + $(e.target).closest('.btn').toggleClass('focus', e.type == 'focus') }) }(jQuery); diff --git a/js/carousel.js b/js/carousel.js index b7da1ba53..65cc7b912 100644 --- a/js/carousel.js +++ b/js/carousel.js @@ -30,6 +30,8 @@ Carousel.VERSION = '3.2.0' + Carousel.TRANSITION_DURATION = 600 + Carousel.DEFAULTS = { interval: 5000, pause: 'hover', @@ -63,6 +65,13 @@ return this.$items.index(item || this.$active) } + Carousel.prototype.getItemForDirection = function (direction, active) { + var delta = direction == 'prev' ? -1 : 1 + var activeIndex = this.getItemIndex(active) + var itemIndex = (activeIndex + delta) % this.$items.length + return this.$items.eq(itemIndex) + } + Carousel.prototype.to = function (pos) { var that = this var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) @@ -72,7 +81,7 @@ if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" if (activeIndex == pos) return this.pause().cycle() - return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) } Carousel.prototype.pause = function (e) { @@ -100,7 +109,7 @@ Carousel.prototype.slide = function (type, next) { var $active = this.$element.find('.item.active') - var $next = next || $active[type]() + var $next = next || this.getItemForDirection(type, $active) var isCycling = this.interval var direction = type == 'next' ? 'left' : 'right' var fallback = type == 'next' ? 'first' : 'last' @@ -146,7 +155,7 @@ that.$element.trigger(slidEvent) }, 0) }) - .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000) + .emulateTransitionEnd(Carousel.TRANSITION_DURATION) } else { $active.removeClass('active') $next.addClass('active') diff --git a/js/collapse.js b/js/collapse.js index 5a524241d..a265344f4 100644 --- a/js/collapse.js +++ b/js/collapse.js @@ -24,6 +24,8 @@ Collapse.VERSION = '3.2.0' + Collapse.TRANSITION_DURATION = 350 + Collapse.DEFAULTS = { toggle: true } @@ -72,7 +74,7 @@ this.$element .one('bsTransitionEnd', $.proxy(complete, this)) - .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize]) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) } Collapse.prototype.hide = function () { @@ -105,7 +107,7 @@ this.$element [dimension](0) .one('bsTransitionEnd', $.proxy(complete, this)) - .emulateTransitionEnd(350) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION) } Collapse.prototype.toggle = function () { diff --git a/js/dropdown.js b/js/dropdown.js index 88f118c2d..756fc21f5 100644 --- a/js/dropdown.js +++ b/js/dropdown.js @@ -42,7 +42,9 @@ if (e.isDefaultPrevented()) return - $this.trigger('focus') + $this + .trigger('focus') + .attr('aria-expanded', 'true') $parent .toggleClass('open') @@ -88,11 +90,17 @@ if (e && e.which === 3) return $(backdrop).remove() $(toggle).each(function () { - var $parent = getParent($(this)) + var $this = $(this) + var $parent = getParent($this) var relatedTarget = { relatedTarget: this } + if (!$parent.hasClass('open')) return + $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) + if (e.isDefaultPrevented()) return + + $this.attr('aria-expanded', 'false') $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget) }) } diff --git a/js/modal.js b/js/modal.js index fdefdd2c5..f8d53e251 100644 --- a/js/modal.js +++ b/js/modal.js @@ -32,6 +32,9 @@ Modal.VERSION = '3.2.0' + Modal.TRANSITION_DURATION = 300 + Modal.BACKDROP_TRANSITION_DURATION = 150 + Modal.DEFAULTS = { backdrop: true, keyboard: true, @@ -88,7 +91,7 @@ .one('bsTransitionEnd', function () { that.$element.trigger('focus').trigger(e) }) - .emulateTransitionEnd(300) : + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : that.$element.trigger('focus').trigger(e) }) } @@ -119,7 +122,7 @@ $.support.transition && this.$element.hasClass('fade') ? this.$element .one('bsTransitionEnd', $.proxy(this.hideModal, this)) - .emulateTransitionEnd(300) : + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : this.hideModal() } @@ -182,7 +185,7 @@ doAnimate ? this.$backdrop .one('bsTransitionEnd', callback) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callback() } else if (!this.isShown && this.$backdrop) { @@ -195,7 +198,7 @@ $.support.transition && this.$element.hasClass('fade') ? this.$backdrop .one('bsTransitionEnd', callbackRemove) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callbackRemove() } else if (callback) { @@ -19,6 +19,8 @@ Tab.VERSION = '3.2.0' + Tab.TRANSITION_DURATION = 150 + Tab.prototype.show = function () { var $this = this.element var $ul = $this.closest('ul:not(.dropdown-menu)') @@ -82,7 +84,7 @@ $active.length && transition ? $active .one('bsTransitionEnd', next) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Tab.TRANSITION_DURATION) : next() $active.removeClass('in') diff --git a/js/tests/unit/carousel.js b/js/tests/unit/carousel.js index 51872c57b..3f9e61a34 100644 --- a/js/tests/unit/carousel.js +++ b/js/tests/unit/carousel.js @@ -349,7 +349,7 @@ $(function () { $carousel.remove() }) - test('should skip over non-items', function () { + test('should skip over non-items when using item indices', function () { var templateHTML = '<div id="myCarousel" class="carousel" data-interval="1814">' + '<div class="carousel-inner">' + '<div class="item active">' @@ -373,4 +373,29 @@ $(function () { strictEqual($template.find('.item')[1], $template.find('.active')[0], 'second item active') }) + + test('should skip over non-items when using next/prev methods', function () { + var templateHTML = '<div id="myCarousel" class="carousel" data-interval="1814">' + + '<div class="carousel-inner">' + + '<div class="item active">' + + '<img alt="">' + + '</div>' + + '<script type="text/x-metamorph" id="thingy"/>' + + '<div class="item">' + + '<img alt="">' + + '</div>' + + '<div class="item">' + + '</div>' + + '</div>' + + '</div>' + var $template = $(templateHTML) + + $template.bootstrapCarousel() + + strictEqual($template.find('.item')[0], $template.find('.active')[0], 'first item active') + + $template.bootstrapCarousel('next') + + strictEqual($template.find('.item')[1], $template.find('.active')[0], 'second item active') + }) }) diff --git a/js/tests/unit/tooltip.js b/js/tests/unit/tooltip.js index 6871f538e..7896c2c96 100644 --- a/js/tests/unit/tooltip.js +++ b/js/tests/unit/tooltip.js @@ -202,134 +202,6 @@ $(function () { .bootstrapTooltip('show') }) - test('should not show tooltip if leave event occurs before delay expires', function () { - stop() - - var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') - .appendTo('#qunit-fixture') - .bootstrapTooltip({ delay: 200 }) - - setTimeout(function () { - ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in') - $tooltip.trigger('mouseout') - }, 100) - - setTimeout(function () { - ok(!$('.tooltip').is('.fade.in'), '300ms: tooltip not faded in') - start() - }, 300) - - $tooltip.trigger('mouseenter') - }) - - test('should not show tooltip if leave event occurs before delay expires, even if hide delay is 0', function () { - stop() - - var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') - .appendTo('#qunit-fixture') - .bootstrapTooltip({ delay: { show: 200, hide: 0 }}) - - setTimeout(function () { - ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in') - $tooltip.trigger('mouseout') - }, 100) - - setTimeout(function () { - ok(!$('.tooltip').is('.fade.in'), '300ms: tooltip not faded in') - start() - }, 300) - - $tooltip.trigger('mouseenter') - }) - - test('should wait 200 ms before hiding the tooltip', function () { - stop() - - var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') - .appendTo('#qunit-fixture') - .bootstrapTooltip({ delay: { show: 0, hide: 200 }}) - - setTimeout(function () { - ok($('.tooltip').is('.fade.in'), '1ms: tooltip faded in') - $tooltip.trigger('mouseout') - }, 1) - - setTimeout(function () { - ok($('.tooltip').is('.fade.in'), '100ms: tooltip still faded in') - }, 100) - - setTimeout(function () { - ok(!$('.tooltip').is('.in'), '250ms: tooltip removed') - start() - }, 250) - - $tooltip.trigger('mouseenter') - }) - - test('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', function () { - stop() - - var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') - .appendTo('#qunit-fixture') - .bootstrapTooltip({ delay: { show: 0, hide: 200 }}) - - setTimeout(function () { - ok($('.tooltip').is('.fade.in'), '1ms: tooltip faded in') - $tooltip.trigger('mouseout') - }, 1) - - setTimeout(function () { - ok($('.tooltip').is('.fade.in'), '100ms: tooltip still faded in') - $tooltip.trigger('mouseenter') - }, 100) - - setTimeout(function () { - ok($('.tooltip').is('.fade.in'), '250ms: tooltip still faded in') - start() - }, 250) - - $tooltip.trigger('mouseenter') - }) - - test('should not show tooltip if leave event occurs before delay expires', function () { - stop() - - var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') - .appendTo('#qunit-fixture') - .bootstrapTooltip({ delay: 100 }) - - setTimeout(function () { - ok(!$('.tooltip').is('.fade.in'), '50ms: tooltip not faded in') - $tooltip.trigger('mouseout') - }, 50) - - setTimeout(function () { - ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in') - start() - }, 100) - - $tooltip.trigger('mouseenter') - }) - - test('should show tooltip if leave event hasn\'t occured before delay expires', function () { - stop() - - var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') - .appendTo('#qunit-fixture') - .bootstrapTooltip({ delay: 150 }) - - setTimeout(function () { - ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip is not faded in') - }, 100) - - setTimeout(function () { - ok($('.tooltip').is('.fade.in'), '200ms: tooltip is faded in') - start() - }, 200) - - $tooltip.trigger('mouseenter') - }) - test('should destroy tooltip', function () { var $tooltip = $('<div/>') .bootstrapTooltip() @@ -395,76 +267,6 @@ $(function () { equal($('body > .tooltip').length, 0, 'tooltip was removed from dom') }) - test('should place tooltip inside viewport', function () { - stop() - - var $container = $('<div/>') - .css({ - position: 'absolute', - width: 200, - height: 200, - bottom: 0, - left: 0 - }) - .appendTo(document.body) - - $('<a href="#" title="Very very very very very very very very long tooltip">Hover me</a>') - .css({ - position: 'absolute', - top: 0, - left: 0 - }) - .appendTo($container) - .bootstrapTooltip({ - placement: 'top', - animate: false - }) - .bootstrapTooltip('show') - - setTimeout(function () { - ok($('.tooltip').offset().left >= 0) - $container.remove() - start() - }, 100) - }) - - test('should place tooltip on top of element', function () { - stop() - - var containerHTML = '<div>' - + '<p style="margin-top: 200px">' - + '<a href="#" title="very very very very very very very long tooltip">Hover me</a>' - + '</p>' - + '</div>' - var $container = $(containerHTML) - .css({ - position: 'absolute', - bottom: 0, - left: 0, - textAlign: 'right', - width: 300, - height: 300 - }) - .appendTo(document.body) - - var $trigger = $container - .find('a') - .css('margin-top', 200) - .bootstrapTooltip({ - placement: 'top', - animate: false - }) - .bootstrapTooltip('show') - - var $tooltip = $container.find('.tooltip') - - setTimeout(function () { - ok(Math.round($tooltip.offset().top + $tooltip.outerHeight()) <= Math.round($trigger.offset().top)) - $container.remove() - start() - }, 100) - }) - test('should add position class before positioning so that position-specific styles are taken into account', function () { var styles = '<style>' + '.tooltip.right { white-space: nowrap; }' @@ -472,7 +274,7 @@ $(function () { + '</style>' var $styles = $(styles).appendTo('head') - var $container = $('<div/>').appendTo(document.body) + var $container = $('<div/>').appendTo('#qunit-fixture') var $target = $('<a href="#" rel="tooltip" title="very very very very very very very very long tooltip in one line"/>') .appendTo($container) .bootstrapTooltip({ @@ -587,7 +389,7 @@ $(function () { + '</style>' var $styles = $(styles).appendTo('head') - var $container = $('<div/>').appendTo(document.body) + var $container = $('<div/>').appendTo('#qunit-fixture') var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 0px; left: 0px;"/>') .appendTo($container) .bootstrapTooltip({ @@ -604,7 +406,6 @@ $(function () { $target.bootstrapTooltip('hide') equal($('.tooltip').length, 0, 'tooltip removed from dom') - $container.remove() $styles.remove() }) @@ -615,7 +416,7 @@ $(function () { + '</style>' var $styles = $(styles).appendTo('head') - var $container = $('<div/>').appendTo(document.body) + var $container = $('<div/>').appendTo('#qunit-fixture') var $target = $('<a href="#" rel="tooltip" title="tip" style="bottom: 0px; left: 0px;"/>') .appendTo($container) .bootstrapTooltip({ @@ -644,7 +445,7 @@ $(function () { + '</style>' var $styles = $(styles).appendTo('head') - var $container = $('<div/>').appendTo(document.body) + var $container = $('<div/>').appendTo('#qunit-fixture') var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 0px; left: 0px;"/>') .appendTo($container) .bootstrapTooltip({ @@ -739,4 +540,239 @@ $(function () { ok(passed, '.tooltip(\'show\') should not throw an error if element no longer is in dom') }) + + test('should place tooltip on top of element', function () { + stop() + + var containerHTML = '<div>' + + '<p style="margin-top: 200px">' + + '<a href="#" title="very very very very very very very long tooltip">Hover me</a>' + + '</p>' + + '</div>' + + var $container = $(containerHTML) + .css({ + position: 'absolute', + bottom: 0, + left: 0, + textAlign: 'right', + width: 300, + height: 300 + }) + .appendTo('#qunit-fixture') + + var $trigger = $container + .find('a') + .css('margin-top', 200) + .bootstrapTooltip({ + placement: 'top', + animate: false + }) + .bootstrapTooltip('show') + + var $tooltip = $container.find('.tooltip') + + setTimeout(function () { + ok(Math.round($tooltip.offset().top + $tooltip.outerHeight()) <= Math.round($trigger.offset().top)) + start() + }, 0) + }) + + test('should place tooltip inside viewport', function () { + stop() + + var $container = $('<div/>') + .css({ + position: 'absolute', + width: 200, + height: 200, + bottom: 0, + left: 0 + }) + .appendTo('#qunit-fixture') + + $('<a href="#" title="Very very very very very very very very long tooltip">Hover me</a>') + .css({ + position: 'absolute', + top: 0, + left: 0 + }) + .appendTo($container) + .bootstrapTooltip({ + placement: 'top' + }) + .bootstrapTooltip('show') + + setTimeout(function () { + ok($('.tooltip').offset().left >= 0) + start() + }, 0) + }) + + test('should show tooltip if leave event hasn\'t occured before delay expires', function () { + stop() + + var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') + .appendTo('#qunit-fixture') + .bootstrapTooltip({ delay: 15 }) + + setTimeout(function () { + ok(!$('.tooltip').is('.fade.in'), '10ms: tooltip is not faded in') + }, 10) + + setTimeout(function () { + ok($('.tooltip').is('.fade.in'), '20ms: tooltip is faded in') + start() + }, 20) + + $tooltip.trigger('mouseenter') + }) + + test('should not show tooltip if leave event occurs before delay expires', function () { + stop() + + var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') + .appendTo('#qunit-fixture') + .bootstrapTooltip({ delay: 15 }) + + setTimeout(function () { + ok(!$('.tooltip').is('.fade.in'), '10ms: tooltip not faded in') + $tooltip.trigger('mouseout') + }, 10) + + setTimeout(function () { + ok(!$('.tooltip').is('.fade.in'), '20ms: tooltip not faded in') + start() + }, 20) + + $tooltip.trigger('mouseenter') + }) + + test('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', function () { + stop() + + var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') + .appendTo('#qunit-fixture') + .bootstrapTooltip({ delay: { show: 0, hide: 15 }}) + + setTimeout(function () { + ok($('.tooltip').is('.fade.in'), '1ms: tooltip faded in') + $tooltip.trigger('mouseout') + + setTimeout(function () { + ok($('.tooltip').is('.fade.in'), '10ms: tooltip still faded in') + $tooltip.trigger('mouseenter') + }, 10) + + setTimeout(function () { + ok($('.tooltip').is('.fade.in'), '20ms: tooltip still faded in') + start() + }, 20) + }, 0) + + $tooltip.trigger('mouseenter') + }) + + test('should not show tooltip if leave event occurs before delay expires', function () { + stop() + + var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') + .appendTo('#qunit-fixture') + .bootstrapTooltip({ delay: 15 }) + + setTimeout(function () { + ok(!$('.tooltip').is('.fade.in'), '10ms: tooltip not faded in') + $tooltip.trigger('mouseout') + }, 10) + + setTimeout(function () { + ok(!$('.tooltip').is('.fade.in'), '20ms: tooltip not faded in') + start() + }, 20) + + $tooltip.trigger('mouseenter') + }) + + test('should not show tooltip if leave event occurs before delay expires, even if hide delay is 0', function () { + stop() + + var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') + .appendTo('#qunit-fixture') + .bootstrapTooltip({ delay: { show: 15, hide: 0 }}) + + setTimeout(function () { + ok(!$('.tooltip').is('.fade.in'), '10ms: tooltip not faded in') + $tooltip.trigger('mouseout') + }, 10) + + setTimeout(function () { + ok(!$('.tooltip').is('.fade.in'), '25ms: tooltip not faded in') + start() + }, 25) + + $tooltip.trigger('mouseenter') + }) + + test('should wait 20ms before hiding the tooltip', function () { + stop() + + var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') + .appendTo('#qunit-fixture') + .bootstrapTooltip({ delay: { show: 0, hide: 15 }}) + + setTimeout(function () { + ok($tooltip.data('bs.tooltip').$tip.is('.fade.in'), '1ms: tooltip faded in') + + $tooltip.trigger('mouseout') + + setTimeout(function () { + ok($tooltip.data('bs.tooltip').$tip.is('.fade.in'), '10ms: tooltip still faded in') + }, 10) + + setTimeout(function () { + ok(!$tooltip.data('bs.tooltip').$tip.is('.in'), '20ms: tooltip removed') + start() + }, 20) + + }, 0) + + $tooltip.trigger('mouseenter') + }) + + test('should correctly position tooltips on SVG elements', function () { + if (!window.SVGElement) { + // Skip IE8 since it doesn't support SVG + expect(0) + return + } + + stop() + + var styles = '<style>' + + '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }' + + '.tooltip { position: absolute; }' + + '.tooltip .tooltip-inner { width: 24px; height: 24px; font-family: Helvetica; }' + + '</style>' + var $styles = $(styles).appendTo('head') + + $('#qunit-fixture').append( + '<div style="position: fixed; top: 0; left: 0;">' + + ' <svg width="200" height="200">' + + ' <circle cx="100" cy="100" r="10" title="m" id="theCircle" />' + + ' </svg>' + + '</div>') + var $circle = $('#theCircle') + + $circle + .on('shown.bs.tooltip', function () { + var offset = $('.tooltip').offset() + $styles.remove() + ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location') + start() + }) + .bootstrapTooltip({ container: 'body', placement: 'top', trigger: 'manual' }) + + $circle.bootstrapTooltip('show') + }) + }) diff --git a/js/tests/visual/affix.html b/js/tests/visual/affix.html index 5e677eb57..40a55ab51 100644 --- a/js/tests/visual/affix.html +++ b/js/tests/visual/affix.html @@ -6,12 +6,36 @@ <style> /* Test Styles */ - .affix { + .affixed-element-top.affix { top: 10px; } - .affix-bottom { + .affixed-element-top.affix-bottom { position: absolute; } + .affixed-element-bottom { + margin-bottom: 0; + } + .affixed-element-bottom.affix { + bottom: 10px; + } + .affixed-element-bottom.affix-bottom { + position: relative; + } + .grow-btn, .shrink-btn { + color: #FFF; + } + .grow-btn { + background-color: #2ECC40; + } + .grow-btn:hover { + background-color: #3D9970; + } + .shrink-btn { + background-color: #FF4136; + } + .shrink-btn:hover { + background-color: #85144B; + } </style> </head> <body> @@ -23,7 +47,7 @@ </div> <div class="col-md-3"> - <ul class="list-group js-affixed-element"> + <ul class="list-group affixed-element-top js-affixed-element-top"> <li class="list-group-item">Cras justo odio</li> <li class="list-group-item">Dapibus ac facilisis in</li> <li class="list-group-item">Morbi leo risus</li> @@ -43,7 +67,7 @@ </ul> </div> - <div class="col-md-9"> + <div class="col-md-6 js-content"> <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p> @@ -199,6 +223,27 @@ </div> + <div class="col-md-3"> + <ul class="list-group affixed-element-bottom js-affixed-element-bottom"> + <li class="list-group-item">Sit necessitatibus aspernatur.</li> + <li class="list-group-item">Adipisicing alias dolor!</li> + <li class="list-group-item">Ipsum molestiae impedit.</li> + <li class="list-group-item">Amet quis iste?</li> + <li class="list-group-item">Ipsum quaerat porro.</li> + <li class="list-group-item">Elit lorem libero.</li> + <li class="list-group-item">Ipsum dolore facilis.</li> + <li class="list-group-item">Elit ad atque.</li> + <li class="list-group-item">Dolor amet sequi!</li> + <li class="list-group-item">Consectetur voluptatum facilis!</li> + <li class="list-group-item">Sit neque eligendi?</li> + <li class="list-group-item">Amet fuga consectetur!</li> + <li class="list-group-item">Amet molestias repellat!</li> + <li class="list-group-item">Consectetur minima repellendus.</li> + <li class="list-group-item grow-btn js-grow-btn">Grow content</li> + <li class="list-group-item shrink-btn js-shrink-btn">Shrink content</li> + </ul> + </div> + <div class="col-md-12 js-footer"> <hr> @@ -222,7 +267,7 @@ <!-- JavaScript Test --> <script> $(function () { - $('.js-affixed-element').affix({ + $('.js-affixed-element-top').affix({ offset: { top: $('.js-page-header').outerHeight(true) - 10 , bottom: $('.js-footer').outerHeight(true) + 10 @@ -232,6 +277,19 @@ $(function () { .on('affix.bs.affix', function (e) { $(e.target).width(e.target.offsetWidth) }) + + $('.js-affixed-element-bottom').affix({ + offset: { + bottom: $('.js-footer').outerHeight(true) + 10 + } + }) + + $('.js-grow-btn').on('click', function() { + $('.js-content').append('<p>Ipsum corrupti ipsam est temporibus.</p>') + }) + $('.js-shrink-btn').on('click', function() { + $('.js-content p').last().remove() + }) }) </script> </body> diff --git a/js/tooltip.js b/js/tooltip.js index 0c3a79d0b..0758b07ee 100644 --- a/js/tooltip.js +++ b/js/tooltip.js @@ -27,6 +27,8 @@ Tooltip.VERSION = '3.2.0' + Tooltip.TRANSITION_DURATION = 150 + Tooltip.DEFAULTS = { animation: true, placement: 'top', @@ -207,7 +209,7 @@ $.support.transition && this.$tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() } } @@ -295,7 +297,7 @@ $.support.transition && this.$tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() this.hoverState = null |
