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

+ + + + + + + + + + + + + +
EventDescription
activateThis 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 @@ - +

Bootstrap Plugin Test Suite

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({