diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba2bfc52d..060a3bd91 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,7 @@ We only accept issues that are bug reports or feature requests. Bugs must be iso - Multiple-line approach (one property and value per line) - Always a space after a property's colon (.e.g, `display: block;` and not `display:block;`) - End all lines with a semi-colon -- For multiple, comma-separated selectors, place each selector on it's own line +- For multiple, comma-separated selectors, place each selector on its own line - Attribute selectors, like `input[type="text"]` should always wrap the attribute's value in double quotes, for consistency and safety (see this [blog post on unquoted attribute values](http://mathiasbynens.be/notes/unquoted-attribute-values) that can lead to XSS attacks). ### JS diff --git a/Gruntfile.js b/Gruntfile.js index 7aad9b339..de6db2f3c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,127 +1,144 @@ /* jshint node: true */ + module.exports = function(grunt) { - "use strict"; + "use strict"; - // Project configuration. - grunt.initConfig({ - // Metadata. - pkg: grunt.file.readJSON('package.json'), - banner: '/**\n' + - '* <%= pkg.name %>.js v<%= pkg.version %> by @fat and @mdo\n' + - '* Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' + - '* <%= _.pluck(pkg.licenses, "url").join(", ") %>\n' + - '*/\n', - jqueryCheck: 'if (!jQuery) { throw new Error(\"Bootstrap requires jQuery\") }\n\n', - // Task configuration. - clean: { - dist: ['dist'] + // Project configuration. + grunt.initConfig({ + + // Metadata. + pkg: grunt.file.readJSON('package.json'), + banner: '/**\n' + + '* <%= pkg.name %>.js v<%= pkg.version %> by @fat and @mdo\n' + + '* Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' + + '* <%= _.pluck(pkg.licenses, "url").join(", ") %>\n' + + '*/\n', + jqueryCheck: 'if (!jQuery) { throw new Error(\"Bootstrap requires jQuery\") }\n\n', + + // Task configuration. + clean: { + dist: ['dist'] + }, + + jshint: { + options: { + jshintrc: 'js/.jshintrc' + }, + gruntfile: { + src: 'Gruntfile.js' + }, + src: { + src: ['js/*.js'] + }, + test: { + src: ['js/tests/unit/*.js'] + } + }, + concat: { + options: { + banner: '<%= banner %><%= jqueryCheck %>', + stripBanners: false + }, + bootstrap: { + src: [ + 'js/transition.js', + 'js/alert.js', + 'js/button.js', + 'js/carousel.js', + 'js/collapse.js', + 'js/dropdown.js', + 'js/modal.js', + 'js/tooltip.js', + 'js/popover.js', + 'js/scrollspy.js', + 'js/tab.js', + 'js/affix.js' + ], + dest: 'dist/js/<%= pkg.name %>.js' + } + }, + uglify: { + options: { + banner: '<%= banner %>' + }, + bootstrap: { + src: ['<%= concat.bootstrap.dest %>'], + dest: 'dist/js/<%= pkg.name %>.min.js' + } + }, + + recess: { + options: { + compile: true + }, + bootstrap: { + src: ['less/bootstrap.less'], + dest: 'dist/css/<%= pkg.name %>.css' + }, + min: { + options: { + compress: true }, - concat: { - options: { - banner: '<%= banner %><%= jqueryCheck %>', - stripBanners: false - }, - bootstrap: { - src: ['js/transition.js', 'js/alert.js', 'js/button.js', 'js/carousel.js', 'js/collapse.js', 'js/dropdown.js', 'js/modal.js', 'js/tooltip.js', 'js/popover.js', 'js/scrollspy.js', 'js/tab.js', 'js/affix.js'], - dest: 'dist/js/<%= pkg.name %>.js' - } - }, - jshint: { - options: { - jshintrc: 'js/.jshintrc' - }, - gruntfile: { - src: 'Gruntfile.js' - }, - src: { - src: ['js/*.js'] - }, - test: { - src: ['js/tests/unit/*.js'] - } - }, - recess: { - options: { - compile: true - }, - bootstrap: { - files: { - 'dist/css/bootstrap.css': ['less/bootstrap.less'] - } - }, - min: { - options: { - compress: true - }, - files: { - 'dist/css/bootstrap.min.css': ['less/bootstrap.less'] - } - } - }, - uglify: { - options: { - banner: '<%= banner %>' - }, - bootstrap: { - files: { - 'dist/js/<%= pkg.name %>.min.js': ['<%= concat.bootstrap.dest %>'] - } - } - }, - qunit: { - options: { - inject: 'js/tests/unit/phantom.js' - }, - files: ['js/tests/*.html'] - }, - connect: { - server: { - options: { - port: 3000, - base: '.' - } - } - }, - watch: { - src: { - files: '<%= jshint.src.src %>', - tasks: ['jshint:src', 'qunit'] - }, - test: { - files: '<%= jshint.test.src %>', - tasks: ['jshint:test', 'qunit'] - }, - recess: { - files: 'less/*.less', - tasks: ['recess'] - } + src: ['less/bootstrap.less'], + dest: 'dist/css/<%= pkg.name %>.min.css' + } + }, + + qunit: { + options: { + inject: 'js/tests/unit/phantom.js' + }, + files: ['js/tests/*.html'] + }, + connect: { + server: { + options: { + port: 3000, + base: '.' } - }); + } + }, + + watch: { + src: { + files: '<%= jshint.src.src %>', + tasks: ['jshint:src', 'qunit'] + }, + test: { + files: '<%= jshint.test.src %>', + tasks: ['jshint:test', 'qunit'] + }, + recess: { + files: 'less/*.less', + tasks: ['recess'] + } + } + }); - // These plugins provide necessary tasks. - grunt.loadNpmTasks('grunt-contrib-connect'); - grunt.loadNpmTasks('grunt-contrib-clean'); - grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-contrib-qunit'); - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-recess'); + // These plugins provide necessary tasks. + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-connect'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-qunit'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-recess'); - // Test task. - grunt.registerTask('test', ['jshint', 'qunit']); + // Test task. + grunt.registerTask('test', ['jshint', 'qunit']); - // JS distribution task. - grunt.registerTask('dist-js', ['concat', 'uglify']); + // JS distribution task. + grunt.registerTask('dist-js', ['concat', 'uglify']); - // CSS distribution task. - grunt.registerTask('dist-css', ['recess']); + // CSS distribution task. + grunt.registerTask('dist-css', ['recess']); - // Full distribution task. - grunt.registerTask('dist', ['clean', 'dist-css', 'dist-js']); + // Full distribution task. + grunt.registerTask('dist', ['clean', 'dist-css', 'dist-js']); - // Default task. - grunt.registerTask('default', ['test', 'dist']); + // Default task. + grunt.registerTask('default', ['test', 'dist']); }; diff --git a/_includes/ads.html b/_includes/ads.html index 495e7ba47..ba4775f5e 100644 --- a/_includes/ads.html +++ b/_includes/ads.html @@ -1 +1 @@ -
\ No newline at end of file +
diff --git a/_includes/footer.html b/_includes/footer.html index 47a671b26..83ff87e26 100644 --- a/_includes/footer.html +++ b/_includes/footer.html @@ -4,7 +4,7 @@ - + @@ -15,7 +15,6 @@ var _gauges = _gauges || []; (function() { var t = document.createElement('script'); - t.type = 'text/javascript'; t.async = true; t.id = 'gauges-tracker'; t.setAttribute('data-site-id', '4f0dc9fef5a1f55508000013'); diff --git a/_includes/header.html b/_includes/header.html index b5eff5f5e..827b1c2c9 100644 --- a/_includes/header.html +++ b/_includes/header.html @@ -31,12 +31,12 @@ - - + diff --git a/index.html b/index.html index 37b634b42..ea5568ec9 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,7 @@ base_url: "./"

Bootstrap 3

Sleek, intuitive, and powerful mobile-first front-end framework for faster and easier web development.

- Download Bootstrap + Download Bootstrap

diff --git a/javascript.html b/javascript.html index 2dbf76fd4..aa65eabe8 100644 --- a/javascript.html +++ b/javascript.html @@ -15,7 +15,7 @@ base_url: "../"

Individual or compiled

-

Plugins can be included individually (using bootstrap's individual *.js files, or all at once (using bootstrap.js or the minified bootstrap.min.js.

+

Plugins can be included individually (using Bootstrap's individual *.js files, or all at once (using bootstrap.js or the minified bootstrap.min.js.

Do not attempt to include both.

@@ -28,7 +28,7 @@ base_url: "../"

Data attributes

-

You can use all Bootstrap plugins purely through the markup API without writing a single line of JavaScript. This is Bootstrap's first class API and should be your first consideration when using a plugin.

+

You can use all Bootstrap plugins purely through the markup API without writing a single line of JavaScript. This is Bootstrap's first-class API and should be your first consideration when using a plugin.

That said, in some situations it may be desirable to turn this functionality off. Therefore, we also provide the ability to disable the data attribute API by unbinding all events on the document namespaced with data-api. This looks like this: {% highlight js %} @@ -59,12 +59,12 @@ $("#myModal").modal('show') // initializes and invokes show immed

Sometimes it is necessary to use Bootstrap plugins with other UI frameworks. In these circumstances, namespace collisions can occasionally occur. If this happens, you may call .noConflict on the plugin you wish to revert the value of.

{% highlight js %} var bootstrapButton = $.fn.button.noConflict() // return $.fn.button to previously assigned value -$.fn.bootstrapBtn = bootstrapButton // give $().bootstrapBtn the bootstrap functionality +$.fn.bootstrapBtn = bootstrapButton // give $().bootstrapBtn the Bootstrap functionality {% endhighlight %}

Events

Bootstrap provides custom events for most plugin's unique actions. Generally, these come in an infinitive and past participle form - where the infinitive (ex. show) is triggered at the start of an event, and its past participle form (ex. shown) is trigger on the completion of an action.

-

As of 3.0.0, all bootstrap events are namespaced.

+

As of 3.0.0, all Bootstrap events are namespaced.

All infinitive events provide preventDefault functionality. This provides the ability to stop the execution of an action before it starts.

{% highlight js %} $('#myModal').on('show.bs.modal', function (e) { @@ -89,7 +89,7 @@ $('#myModal').on('show.bs.modal', function (e) {

About transitions

For simple transition effects, include transition.js once alongside the other JS files. If you're using the compiled (or minified) bootstrap.js, there is no need to include this—it's already there.

What's inside

-

Transition.js is a basic helper for transitionEnd events as well as a css transition emulator. It's used by the other plugins to check for css transition support and to catch hanging transitions.

+

Transition.js is a basic helper for transitionEnd events as well as a CSS transition emulator. It's used by the other plugins to check for CSS transition support and to catch hanging transitions.

@@ -189,11 +189,11 @@ $('#myModal').on('show.bs.modal', function (e) {
- Launch demo modal + Launch demo modal
{% highlight html %} - Launch demo modal + Launch demo modal @@ -465,7 +465,7 @@ $('.dropdown-toggle').dropdown()

Methods

$().dropdown('toggle')

-

A programmatic api for toggling menus for a given navbar or tabbed navigation.

+

Toggles the dropdown menu of a given navbar or tabbed navigation.

@@ -957,7 +957,7 @@ $('#myTooltip').on('hidden.bs.tooltip', function () {

Live demo

- Click to toggle popover + Click to toggle popover

Four directions

@@ -1794,7 +1794,7 @@ $('#myCarousel').on('slide.bs.carousel', function () {

Requires independent styling ;)

Affix toggles between three states/classes: affix, affix-top, and affix-bottom. You must provide the styles for these classes yourself (independent of this plugin). - The affix-top class should be in the regular flow of the document. The affix class should be fixed to the page. And affix-bottom should be positioned absolute. Note, affix-bottom is special in that the plugin will place the element with js relative to the offset: { bottom: number } option you've provided. + The affix-top class should be in the regular flow of the document. The affix class should be fixed to the page. And affix-bottom should be positioned absolute. Note, affix-bottom is special in that the plugin will place the element with JS relative to the offset: { bottom: number } option you've provided.

diff --git a/js/popover.js b/js/popover.js index b5888b6e4..feee3308d 100644 --- a/js/popover.js +++ b/js/popover.js @@ -75,6 +75,10 @@ o.content) } + Popover.prototype.arrow =function () { + return this.$arrow = this.$arrow || this.tip().find('.arrow') + } + Popover.prototype.tip = function () { if (!this.$tip) this.$tip = $(this.options.template) return this.$tip diff --git a/js/tooltip.js b/js/tooltip.js index 30e3ae140..8f2beedce 100644 --- a/js/tooltip.js +++ b/js/tooltip.js @@ -179,12 +179,9 @@ .addClass(placement) } - var tp = placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : - placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : - placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : - /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } + var calculatedOffset = this.getCalcuatedOffset(placement, pos, actualWidth, actualHeight) - this.applyPlacement(tp, placement) + this.applyPlacement(calculatedOffset, placement) this.$element.trigger('shown.bs.' + this.type) } } @@ -196,25 +193,33 @@ var height = $tip[0].offsetHeight // manually read margins because getBoundingClientRect includes difference - offset.top = offset.top + parseInt($tip.css('margin-top'), 10) - offset.left = offset.left + parseInt($tip.css('margin-left'), 10) + var marginTop = parseInt($tip.css('margin-top'), 10) + var marginLeft = parseInt($tip.css('margin-left'), 10) + + // we must check for NaN for ie 8/9 + if (isNaN(marginTop)) marginTop = 0 + if (isNaN(marginLeft)) marginLeft = 0 + + offset.top = offset.top + marginTop + offset.left = offset.left + marginLeft $tip .offset(offset) .addClass('in') + // check to see if placing tip in new offset caused the tip to resize itself var actualWidth = $tip[0].offsetWidth var actualHeight = $tip[0].offsetHeight if (placement == 'top' && actualHeight != height) { replace = true - offset.top = offset.top + height - actualHeight + offset.top = offset.top + height - actualHeight } - if (placement == 'bottom' || placement == 'top') { + if (/bottom|top/.test(placement)) { var delta = 0 - if (offset.left < 0){ + if (offset.left < 0) { delta = offset.left * -2 offset.left = 0 @@ -287,6 +292,13 @@ }, this.$element.offset()) } + Tooltip.prototype.getCalcuatedOffset = function (placement, pos, actualWidth, actualHeight) { + return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : + /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } + } + Tooltip.prototype.getTitle = function () { var title var $e = this.$element @@ -302,8 +314,8 @@ return this.$tip = this.$tip || $(this.options.template) } - Tooltip.prototype.arrow =function(){ - return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow") + Tooltip.prototype.arrow = function () { + return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow') } Tooltip.prototype.validate = function () { diff --git a/less/button-groups.less b/less/button-groups.less index d78815c93..ac58b38ce 100644 --- a/less/button-groups.less +++ b/less/button-groups.less @@ -22,7 +22,8 @@ // Bring the "active" button to the front &:hover, &:focus, - &:active { + &:active, + &.active { z-index: 2; } } @@ -100,7 +101,7 @@ padding-left: 8px; padding-right: 8px; } -.btn-group > .btn-large + .dropdown-toggle { +.btn-group > .btn-lg + .dropdown-toggle { padding-left: 12px; padding-right: 12px; } @@ -117,11 +118,11 @@ margin-left: 0; } // Carets in other button sizes -.btn-large .caret { +.btn-lg .caret { border-width: 5px; } // Upside down carets for .dropup -.dropup .btn-large .caret { +.dropup .btn-lg .caret { border-bottom-width: 5px; } @@ -142,10 +143,10 @@ &:not(:first-child):not(:last-child) { border-radius: 0; } - &:first-child { + &:first-child:not(:last-child) { .border-bottom-radius(0); } - &:last-child { + &:last-child:not(:first-child) { .border-top-radius(0); } } @@ -157,6 +158,7 @@ .btn-group-justified { display: table; width: 100%; + table-layout: fixed; .btn { float: none; display: table-cell; diff --git a/less/buttons.less b/less/buttons.less index 8905ffa4a..8fad99cc8 100644 --- a/less/buttons.less +++ b/less/buttons.less @@ -120,20 +120,20 @@ // Button Sizes // -------------------------------------------------- -.btn-large { +.btn-lg { padding: @padding-large-vertical @padding-large-horizontal; font-size: @font-size-large; - line-height: 1.33; // ensure even-numbered height of button next to large input + line-height: @line-height-large; // ensure even-numbered height of button next to large input border-radius: @border-radius-large; } -.btn-small, -.btn-mini { +.btn-sm, +.btn-xs { padding: @padding-small-vertical @padding-small-horizontal; font-size: @font-size-small; - line-height: 1.5; // ensure proper height of button next to small input + line-height: @line-height-small; // ensure proper height of button next to small input border-radius: @border-radius-small; } -.btn-mini { +.btn-xs { padding: 3px 5px; } diff --git a/less/forms.less b/less/forms.less index 41d50a5e0..027131b3b 100644 --- a/less/forms.less +++ b/less/forms.less @@ -129,11 +129,8 @@ input[type="number"] { .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s"); - &:focus { - border-color: rgba(82,168,236,.8); - outline: 0; - .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6)"); - } + // Customize the `:focus` state to imitate native WebKit styles. + .form-control-focus(); // Disabled and read-only inputs // Note: HTML5 says that inputs under a fieldset > legend:first-child won't be @@ -218,14 +215,14 @@ input[type="number"] { // horizontal sizing, wrap controls in the predefined grid classes. `