diff --git a/docs/assets/bootstrap.zip b/docs/assets/bootstrap.zip
index a07fb5664d..cd93364e47 100644
Binary files a/docs/assets/bootstrap.zip and b/docs/assets/bootstrap.zip differ
diff --git a/docs/assets/js/bootstrap-carousel.js b/docs/assets/js/bootstrap-carousel.js
index 8c0723d280..7db5bea333 100644
--- a/docs/assets/js/bootstrap-carousel.js
+++ b/docs/assets/js/bootstrap-carousel.js
@@ -27,7 +27,7 @@
var Carousel = function (element, options) {
this.$element = $(element)
- this.options = $.extend({}, $.fn.carousel.defaults, options)
+ this.options = options
this.options.slide && this.slide(this.options.slide)
this.options.pause == 'hover' && this.$element
.on('mouseenter', $.proxy(this.pause, this))
@@ -37,7 +37,8 @@
Carousel.prototype = {
cycle: function () {
- this.interval = setInterval($.proxy(this.next, this), this.options.interval)
+ this.options.interval
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
return this
}
@@ -94,13 +95,7 @@
if ($next.hasClass('active')) return
- if (!$.support.transition && this.$element.hasClass('slide')) {
- this.$element.trigger('slide')
- $active.removeClass('active')
- $next.addClass('active')
- this.sliding = false
- this.$element.trigger('slid')
- } else {
+ if ($.support.transition && this.$element.hasClass('slide')) {
$next.addClass(type)
$next[0].offsetWidth // force reflow
$active.addClass(direction)
@@ -112,6 +107,12 @@
that.sliding = false
setTimeout(function () { that.$element.trigger('slid') }, 0)
})
+ } else {
+ this.$element.trigger('slide')
+ $active.removeClass('active')
+ $next.addClass('active')
+ this.sliding = false
+ this.$element.trigger('slid')
}
isCycling && this.cycle()
@@ -129,11 +130,11 @@
return this.each(function () {
var $this = $(this)
, data = $this.data('carousel')
- , options = typeof option == 'object' && option
+ , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
if (!data) $this.data('carousel', (data = new Carousel(this, options)))
if (typeof option == 'number') data.to(option)
else if (typeof option == 'string' || (option = options.slide)) data[option]()
- else data.cycle()
+ else if (options.interval) data.cycle()
})
}
diff --git a/docs/assets/js/bootstrap-collapse.js b/docs/assets/js/bootstrap-collapse.js
index 9a364468b7..16b91eb244 100644
--- a/docs/assets/js/bootstrap-collapse.js
+++ b/docs/assets/js/bootstrap-collapse.js
@@ -42,11 +42,18 @@
}
, show: function () {
- var dimension = this.dimension()
- , scroll = $.camelCase(['scroll', dimension].join('-'))
- , actives = this.$parent && this.$parent.find('.in')
+ var dimension
+ , scroll
+ , actives
, hasData
+ if (this.transitioning) return
+
+ dimension = this.dimension()
+ scroll = $.camelCase(['scroll', dimension].join('-'))
+ actives = this.$parent && this.$parent.find('> .accordion-group > .in')
+ hasData
+
if (actives && actives.length) {
hasData = actives.data('collapse')
actives.collapse('hide')
@@ -56,11 +63,12 @@
this.$element[dimension](0)
this.transition('addClass', 'show', 'shown')
this.$element[dimension](this.$element[0][scroll])
-
}
, hide: function () {
- var dimension = this.dimension()
+ var dimension
+ if (this.transitioning) return
+ dimension = this.dimension()
this.reset(this.$element[dimension]())
this.transition('removeClass', 'hide', 'hidden')
this.$element[dimension](0)
@@ -74,7 +82,7 @@
[dimension](size || 'auto')
[0].offsetWidth
- this.$element[size ? 'addClass' : 'removeClass']('collapse')
+ this.$element[size != null ? 'addClass' : 'removeClass']('collapse')
return this
}
@@ -83,9 +91,12 @@
var that = this
, complete = function () {
if (startEvent == 'show') that.reset()
+ that.transitioning = 0
that.$element.trigger(completeEvent)
}
+ this.transitioning = 1
+
this.$element
.trigger(startEvent)
[method]('in')
diff --git a/docs/assets/js/bootstrap-dropdown.js b/docs/assets/js/bootstrap-dropdown.js
index 54b61c5e9d..f26b83f41d 100644
--- a/docs/assets/js/bootstrap-dropdown.js
+++ b/docs/assets/js/bootstrap-dropdown.js
@@ -86,7 +86,9 @@
$(function () {
$('html').on('click.dropdown.data-api', clearMenus)
- $('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+ $('body')
+ .on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
})
}( window.jQuery );
\ No newline at end of file
diff --git a/docs/assets/js/bootstrap-popover.js b/docs/assets/js/bootstrap-popover.js
index e1aa5ac391..6f389785cc 100644
--- a/docs/assets/js/bootstrap-popover.js
+++ b/docs/assets/js/bootstrap-popover.js
@@ -38,8 +38,8 @@
, title = this.getTitle()
, content = this.getContent()
- $tip.find('.popover-title')[ $.type(title) == 'object' ? 'append' : 'html' ](title)
- $tip.find('.popover-content > *')[ $.type(content) == 'object' ? 'append' : 'html' ](content)
+ $tip.find('.popover-title').html(title)
+ $tip.find('.popover-content > *').html(content)
$tip.removeClass('fade top bottom left right in')
}
@@ -56,8 +56,6 @@
content = $e.attr('data-content')
|| (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
- content = content.toString().replace(/(^\s*|\s*$)/, "")
-
return content
}
diff --git a/docs/assets/js/bootstrap-scrollspy.js b/docs/assets/js/bootstrap-scrollspy.js
index ea29f2f866..bd3f106044 100644
--- a/docs/assets/js/bootstrap-scrollspy.js
+++ b/docs/assets/js/bootstrap-scrollspy.js
@@ -43,25 +43,42 @@
constructor: ScrollSpy
, refresh: function () {
- this.targets = this.$body
+ var self = this
+ , $targets
+
+ this.offsets = []
+ this.targets = []
+
+ $targets = this.$body
.find(this.selector)
.map(function () {
var href = $(this).attr('href')
- return /^#\w/.test(href) && $(href).length ? href : null
+ , $href = /^#\w/.test(href) && $(href)
+ return ( $href
+ && href.length
+ && [[ $href.position().top, href ]] ) || null
+ })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .each(function () {
+ self.offsets.push(this[0])
+ self.targets.push(this[1])
})
-
- this.offsets = $.map(this.targets, function (id) {
- return $(id).position().top
- })
}
, process: function () {
var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
+ , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
+ , maxScroll = scrollHeight - this.$scrollElement.height()
, offsets = this.offsets
, targets = this.targets
, activeTarget = this.activeTarget
, i
+ if (scrollTop >= maxScroll) {
+ return activeTarget != (i = targets.last()[0])
+ && this.activate ( i )
+ }
+
for (i = offsets.length; i--;) {
activeTarget != targets[i]
&& scrollTop >= offsets[i]
@@ -85,8 +102,10 @@
.addClass('active')
if ( active.parent('.dropdown-menu') ) {
- active.closest('li.dropdown').addClass('active')
+ active = active.closest('li.dropdown').addClass('active')
}
+
+ active.trigger('activate')
}
}
diff --git a/docs/assets/js/bootstrap-tooltip.js b/docs/assets/js/bootstrap-tooltip.js
index 49b5f7286a..7d58a6c8d2 100644
--- a/docs/assets/js/bootstrap-tooltip.js
+++ b/docs/assets/js/bootstrap-tooltip.js
@@ -73,8 +73,9 @@
if (!self.options.delay || !self.options.delay.show) {
self.show()
} else {
+ clearTimeout(this.timeout)
self.hoverState = 'in'
- setTimeout(function() {
+ this.timeout = setTimeout(function() {
if (self.hoverState == 'in') {
self.show()
}
@@ -88,8 +89,9 @@
if (!self.options.delay || !self.options.delay.hide) {
self.hide()
} else {
+ clearTimeout(this.timeout)
self.hoverState = 'out'
- setTimeout(function() {
+ this.timeout = setTimeout(function() {
if (self.hoverState == 'out') {
self.hide()
}
@@ -206,8 +208,6 @@
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
- title = (title || '').toString().replace(/(^\s*|\s*$)/, "")
-
return title
}
diff --git a/docs/assets/js/bootstrap-typeahead.js b/docs/assets/js/bootstrap-typeahead.js
index dc2f882218..f4d26186c7 100644
--- a/docs/assets/js/bootstrap-typeahead.js
+++ b/docs/assets/js/bootstrap-typeahead.js
@@ -39,8 +39,9 @@
, select: function () {
var val = this.$menu.find('.active').attr('data-value')
- this.$element.val(val)
- this.$element.change();
+ this.$element
+ .val(val)
+ .change()
return this.hide()
}
@@ -109,7 +110,8 @@
}
, highlighter: function (item) {
- return item.replace(new RegExp('(' + this.query + ')', 'ig'), function ($1, match) {
+ var query = this.query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
+ return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
return '' + match + ''
})
}
diff --git a/docs/javascript.html b/docs/javascript.html
index 4f6059cb71..8728eb66df 100644
--- a/docs/javascript.html
+++ b/docs/javascript.html
@@ -589,6 +589,21 @@ $('#myModal').on('hidden', function () {
+
Events
+
+
+
+ Event |
+ Description |
+
+
+
+
+ activate |
+ This event fires whenever a new item becomes activated by the scrollspy. |
+
+
+
@@ -1320,7 +1335,7 @@ $('#myCollapsible').on('hidden', function () {
interval |
number |
5000 |
- The amount of time to delay between automatically cycling an item. |
+ The amount of time to delay between automatically cycling an item. If false, carousel will not automatically cycle. |
pause |
diff --git a/docs/templates/pages/javascript.mustache b/docs/templates/pages/javascript.mustache
index 06816c7ad3..1c5b96d2e6 100644
--- a/docs/templates/pages/javascript.mustache
+++ b/docs/templates/pages/javascript.mustache
@@ -513,6 +513,21 @@ $('#myModal').on('hidden', function () {
+ {{_i}}Events{{/i}}
+
+
+
+ {{_i}}Event{{/i}} |
+ {{_i}}Description{{/i}} |
+
+
+
+
+ {{_i}}activate{{/i}} |
+ {{_i}}This event fires whenever a new item becomes activated by the scrollspy.{{/i}} |
+
+
+
@@ -1244,7 +1259,7 @@ $('#myCollapsible').on('hidden', function () {
{{_i}}interval{{/i}} |
{{_i}}number{{/i}} |
5000 |
- {{_i}}The amount of time to delay between automatically cycling an item.{{/i}} |
+ {{_i}}The amount of time to delay between automatically cycling an item. If false, carousel will not automatically cycle.{{/i}} |
{{_i}}pause{{/i}} |
diff --git a/js/bootstrap-carousel.js b/js/bootstrap-carousel.js
index 8c0723d280..7db5bea333 100644
--- a/js/bootstrap-carousel.js
+++ b/js/bootstrap-carousel.js
@@ -27,7 +27,7 @@
var Carousel = function (element, options) {
this.$element = $(element)
- this.options = $.extend({}, $.fn.carousel.defaults, options)
+ this.options = options
this.options.slide && this.slide(this.options.slide)
this.options.pause == 'hover' && this.$element
.on('mouseenter', $.proxy(this.pause, this))
@@ -37,7 +37,8 @@
Carousel.prototype = {
cycle: function () {
- this.interval = setInterval($.proxy(this.next, this), this.options.interval)
+ this.options.interval
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
return this
}
@@ -94,13 +95,7 @@
if ($next.hasClass('active')) return
- if (!$.support.transition && this.$element.hasClass('slide')) {
- this.$element.trigger('slide')
- $active.removeClass('active')
- $next.addClass('active')
- this.sliding = false
- this.$element.trigger('slid')
- } else {
+ if ($.support.transition && this.$element.hasClass('slide')) {
$next.addClass(type)
$next[0].offsetWidth // force reflow
$active.addClass(direction)
@@ -112,6 +107,12 @@
that.sliding = false
setTimeout(function () { that.$element.trigger('slid') }, 0)
})
+ } else {
+ this.$element.trigger('slide')
+ $active.removeClass('active')
+ $next.addClass('active')
+ this.sliding = false
+ this.$element.trigger('slid')
}
isCycling && this.cycle()
@@ -129,11 +130,11 @@
return this.each(function () {
var $this = $(this)
, data = $this.data('carousel')
- , options = typeof option == 'object' && option
+ , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
if (!data) $this.data('carousel', (data = new Carousel(this, options)))
if (typeof option == 'number') data.to(option)
else if (typeof option == 'string' || (option = options.slide)) data[option]()
- else data.cycle()
+ else if (options.interval) data.cycle()
})
}
diff --git a/js/bootstrap-collapse.js b/js/bootstrap-collapse.js
index 9a364468b7..16b91eb244 100644
--- a/js/bootstrap-collapse.js
+++ b/js/bootstrap-collapse.js
@@ -42,11 +42,18 @@
}
, show: function () {
- var dimension = this.dimension()
- , scroll = $.camelCase(['scroll', dimension].join('-'))
- , actives = this.$parent && this.$parent.find('.in')
+ var dimension
+ , scroll
+ , actives
, hasData
+ if (this.transitioning) return
+
+ dimension = this.dimension()
+ scroll = $.camelCase(['scroll', dimension].join('-'))
+ actives = this.$parent && this.$parent.find('> .accordion-group > .in')
+ hasData
+
if (actives && actives.length) {
hasData = actives.data('collapse')
actives.collapse('hide')
@@ -56,11 +63,12 @@
this.$element[dimension](0)
this.transition('addClass', 'show', 'shown')
this.$element[dimension](this.$element[0][scroll])
-
}
, hide: function () {
- var dimension = this.dimension()
+ var dimension
+ if (this.transitioning) return
+ dimension = this.dimension()
this.reset(this.$element[dimension]())
this.transition('removeClass', 'hide', 'hidden')
this.$element[dimension](0)
@@ -74,7 +82,7 @@
[dimension](size || 'auto')
[0].offsetWidth
- this.$element[size ? 'addClass' : 'removeClass']('collapse')
+ this.$element[size != null ? 'addClass' : 'removeClass']('collapse')
return this
}
@@ -83,9 +91,12 @@
var that = this
, complete = function () {
if (startEvent == 'show') that.reset()
+ that.transitioning = 0
that.$element.trigger(completeEvent)
}
+ this.transitioning = 1
+
this.$element
.trigger(startEvent)
[method]('in')
diff --git a/js/bootstrap-dropdown.js b/js/bootstrap-dropdown.js
index 54b61c5e9d..f26b83f41d 100644
--- a/js/bootstrap-dropdown.js
+++ b/js/bootstrap-dropdown.js
@@ -86,7 +86,9 @@
$(function () {
$('html').on('click.dropdown.data-api', clearMenus)
- $('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+ $('body')
+ .on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
})
}( window.jQuery );
\ No newline at end of file
diff --git a/js/bootstrap-popover.js b/js/bootstrap-popover.js
index e1aa5ac391..6f389785cc 100644
--- a/js/bootstrap-popover.js
+++ b/js/bootstrap-popover.js
@@ -38,8 +38,8 @@
, title = this.getTitle()
, content = this.getContent()
- $tip.find('.popover-title')[ $.type(title) == 'object' ? 'append' : 'html' ](title)
- $tip.find('.popover-content > *')[ $.type(content) == 'object' ? 'append' : 'html' ](content)
+ $tip.find('.popover-title').html(title)
+ $tip.find('.popover-content > *').html(content)
$tip.removeClass('fade top bottom left right in')
}
@@ -56,8 +56,6 @@
content = $e.attr('data-content')
|| (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
- content = content.toString().replace(/(^\s*|\s*$)/, "")
-
return content
}
diff --git a/js/bootstrap-scrollspy.js b/js/bootstrap-scrollspy.js
index ea29f2f866..bd3f106044 100644
--- a/js/bootstrap-scrollspy.js
+++ b/js/bootstrap-scrollspy.js
@@ -43,25 +43,42 @@
constructor: ScrollSpy
, refresh: function () {
- this.targets = this.$body
+ var self = this
+ , $targets
+
+ this.offsets = []
+ this.targets = []
+
+ $targets = this.$body
.find(this.selector)
.map(function () {
var href = $(this).attr('href')
- return /^#\w/.test(href) && $(href).length ? href : null
+ , $href = /^#\w/.test(href) && $(href)
+ return ( $href
+ && href.length
+ && [[ $href.position().top, href ]] ) || null
+ })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .each(function () {
+ self.offsets.push(this[0])
+ self.targets.push(this[1])
})
-
- this.offsets = $.map(this.targets, function (id) {
- return $(id).position().top
- })
}
, process: function () {
var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
+ , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
+ , maxScroll = scrollHeight - this.$scrollElement.height()
, offsets = this.offsets
, targets = this.targets
, activeTarget = this.activeTarget
, i
+ if (scrollTop >= maxScroll) {
+ return activeTarget != (i = targets.last()[0])
+ && this.activate ( i )
+ }
+
for (i = offsets.length; i--;) {
activeTarget != targets[i]
&& scrollTop >= offsets[i]
@@ -85,8 +102,10 @@
.addClass('active')
if ( active.parent('.dropdown-menu') ) {
- active.closest('li.dropdown').addClass('active')
+ active = active.closest('li.dropdown').addClass('active')
}
+
+ active.trigger('activate')
}
}
diff --git a/js/bootstrap-tooltip.js b/js/bootstrap-tooltip.js
index 49b5f7286a..7d58a6c8d2 100644
--- a/js/bootstrap-tooltip.js
+++ b/js/bootstrap-tooltip.js
@@ -73,8 +73,9 @@
if (!self.options.delay || !self.options.delay.show) {
self.show()
} else {
+ clearTimeout(this.timeout)
self.hoverState = 'in'
- setTimeout(function() {
+ this.timeout = setTimeout(function() {
if (self.hoverState == 'in') {
self.show()
}
@@ -88,8 +89,9 @@
if (!self.options.delay || !self.options.delay.hide) {
self.hide()
} else {
+ clearTimeout(this.timeout)
self.hoverState = 'out'
- setTimeout(function() {
+ this.timeout = setTimeout(function() {
if (self.hoverState == 'out') {
self.hide()
}
@@ -206,8 +208,6 @@
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
- title = (title || '').toString().replace(/(^\s*|\s*$)/, "")
-
return title
}
diff --git a/js/bootstrap-typeahead.js b/js/bootstrap-typeahead.js
index dc2f882218..f4d26186c7 100644
--- a/js/bootstrap-typeahead.js
+++ b/js/bootstrap-typeahead.js
@@ -39,8 +39,9 @@
, select: function () {
var val = this.$menu.find('.active').attr('data-value')
- this.$element.val(val)
- this.$element.change();
+ this.$element
+ .val(val)
+ .change()
return this.hide()
}
@@ -109,7 +110,8 @@
}
, highlighter: function (item) {
- return item.replace(new RegExp('(' + this.query + ')', 'ig'), function ($1, match) {
+ var query = this.query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
+ return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
return '' + match + ''
})
}
diff --git a/js/tests/index.html b/js/tests/index.html
index 8c710de366..2d59ab0321 100644
--- a/js/tests/index.html
+++ b/js/tests/index.html
@@ -36,7 +36,7 @@
-
+
diff --git a/js/tests/unit/bootstrap-typeahead.js b/js/tests/unit/bootstrap-typeahead.js
index 96ea7c45fc..25d313c85c 100644
--- a/js/tests/unit/bootstrap-typeahead.js
+++ b/js/tests/unit/bootstrap-typeahead.js
@@ -52,6 +52,22 @@ $(function () {
typeahead.$menu.remove()
})
+ test("should not explode when regex chars are entered", function () {
+ var $input = $('').typeahead({
+ source: ['aa', 'ab', 'ac', 'mdo*', 'fat+']
+ })
+ , typeahead = $input.data('typeahead')
+
+ $input.val('+')
+ typeahead.lookup()
+
+ ok(typeahead.$menu.is(":visible"), 'typeahead is visible')
+ equals(typeahead.$menu.find('li').length, 1, 'has 1 item in menu')
+ equals(typeahead.$menu.find('.active').length, 1, 'one item is active')
+
+ typeahead.$menu.remove()
+ })
+
test("should hide menu when query entered", function () {
stop()
var $input = $('').typeahead({