bootstrap-sass 3.2.0.4 → 3.3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bootstrap-sass might be problematic. Click here for more details.

Files changed (81) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -1
  3. data/.travis.yml +8 -2
  4. data/CHANGELOG.md +7 -0
  5. data/CONTRIBUTING.md +1 -1
  6. data/README.md +29 -6
  7. data/Rakefile +3 -4
  8. data/assets/fonts/bootstrap/glyphicons-halflings-regular.svg +1 -1
  9. data/assets/javascripts/bootstrap.js +329 -176
  10. data/assets/javascripts/bootstrap/affix.js +43 -23
  11. data/assets/javascripts/bootstrap/alert.js +6 -4
  12. data/assets/javascripts/bootstrap/button.js +16 -10
  13. data/assets/javascripts/bootstrap/carousel.js +26 -10
  14. data/assets/javascripts/bootstrap/collapse.js +69 -28
  15. data/assets/javascripts/bootstrap/dropdown.js +20 -10
  16. data/assets/javascripts/bootstrap/modal.js +22 -21
  17. data/assets/javascripts/bootstrap/popover.js +13 -7
  18. data/assets/javascripts/bootstrap/scrollspy.js +12 -7
  19. data/assets/javascripts/bootstrap/tab.js +41 -16
  20. data/assets/javascripts/bootstrap/tooltip.js +60 -39
  21. data/assets/javascripts/bootstrap/transition.js +1 -1
  22. data/assets/stylesheets/bootstrap/_button-groups.scss +18 -11
  23. data/assets/stylesheets/bootstrap/_buttons.scss +8 -5
  24. data/assets/stylesheets/bootstrap/_carousel.scss +25 -1
  25. data/assets/stylesheets/bootstrap/_code.scss +1 -0
  26. data/assets/stylesheets/bootstrap/_component-animations.scss +5 -2
  27. data/assets/stylesheets/bootstrap/_dropdowns.scss +4 -6
  28. data/assets/stylesheets/bootstrap/_forms.scss +51 -28
  29. data/assets/stylesheets/bootstrap/_glyphicons.scss +2 -5
  30. data/assets/stylesheets/bootstrap/_grid.scss +4 -4
  31. data/assets/stylesheets/bootstrap/_jumbotron.scss +4 -4
  32. data/assets/stylesheets/bootstrap/_list-group.scss +1 -0
  33. data/assets/stylesheets/bootstrap/_media.scss +27 -36
  34. data/assets/stylesheets/bootstrap/_modals.scss +3 -4
  35. data/assets/stylesheets/bootstrap/_navbar.scss +35 -33
  36. data/assets/stylesheets/bootstrap/_navs.scss +5 -3
  37. data/assets/stylesheets/bootstrap/_normalize.scss +5 -3
  38. data/assets/stylesheets/bootstrap/_pager.scss +2 -3
  39. data/assets/stylesheets/bootstrap/_pagination.scss +1 -1
  40. data/assets/stylesheets/bootstrap/_panels.scss +22 -4
  41. data/assets/stylesheets/bootstrap/_popovers.scss +5 -4
  42. data/assets/stylesheets/bootstrap/_print.scss +102 -96
  43. data/assets/stylesheets/bootstrap/_progress-bars.scss +2 -20
  44. data/assets/stylesheets/bootstrap/_responsive-embed.scss +2 -1
  45. data/assets/stylesheets/bootstrap/_scaffolding.scss +3 -3
  46. data/assets/stylesheets/bootstrap/_tables.scss +11 -10
  47. data/assets/stylesheets/bootstrap/_theme.scss +16 -14
  48. data/assets/stylesheets/bootstrap/_thumbnails.scss +2 -2
  49. data/assets/stylesheets/bootstrap/_type.scss +10 -16
  50. data/assets/stylesheets/bootstrap/_utilities.scss +3 -4
  51. data/assets/stylesheets/bootstrap/_variables.scss +29 -19
  52. data/assets/stylesheets/bootstrap/mixins/_buttons.scss +2 -0
  53. data/assets/stylesheets/bootstrap/mixins/_forms.scss +5 -1
  54. data/assets/stylesheets/bootstrap/mixins/_grid.scss +2 -2
  55. data/assets/stylesheets/bootstrap/mixins/_hide-text.scss +1 -1
  56. data/assets/stylesheets/bootstrap/mixins/_image.scss +0 -1
  57. data/assets/stylesheets/bootstrap/mixins/_labels.scss +1 -1
  58. data/assets/stylesheets/bootstrap/mixins/_progress-bar.scss +1 -1
  59. data/assets/stylesheets/bootstrap/mixins/_vendor-prefixes.scss +6 -3
  60. data/lib/bootstrap-sass.rb +2 -5
  61. data/lib/bootstrap-sass/engine.rb +1 -1
  62. data/lib/bootstrap-sass/version.rb +2 -2
  63. data/package.json +3 -3
  64. data/tasks/converter/less_conversion.rb +60 -28
  65. data/tasks/converter/network.rb +12 -6
  66. data/templates/project/_bootstrap-variables.sass +866 -0
  67. data/templates/project/manifest.rb +5 -11
  68. data/test/node_sass_compile_test.sh +8 -0
  69. metadata +7 -28
  70. data/templates/project/_bootstrap-variables.sass.erb +0 -4
  71. data/test/dummy_rails/bin/bundle +0 -3
  72. data/test/dummy_rails/bin/rails +0 -4
  73. data/test/dummy_rails/bin/rake +0 -4
  74. data/test/dummy_rails/db/test.sqlite3 +0 -0
  75. data/test/dummy_rails/lib/assets/.keep +0 -0
  76. data/test/dummy_rails/public/404.html +0 -58
  77. data/test/dummy_rails/public/422.html +0 -58
  78. data/test/dummy_rails/public/500.html +0 -57
  79. data/test/dummy_rails/public/favicon.ico +0 -0
  80. data/test/node_sass_test.rb +0 -16
  81. data/test/support/integration_test.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 6050eccd4daf64b5e5d72bca8ac1292f271cf035c73b3e5eff62f385816fa2f3
4
- data.tar.gz: 387b67ca35856109dc25957a2eb2da470dc65a6cd53d5facdeb10d12c9f943d6
2
+ SHA1:
3
+ metadata.gz: d99caaebc96473fc43200f9761918c7d434a606a
4
+ data.tar.gz: e7d8b2f1bd54b8a8023d99edf4d8b869035ccf16
5
5
  SHA512:
6
- metadata.gz: 41456d7bbd25cc93328e1ae7038636f54200fbc021b841fb52882226a527b5aa8da37a2311df2c189eb8c5e25c9c78f75fa475c664b4a7147f21f332081191aa
7
- data.tar.gz: '0732993db77d14da653cf64d5d1cca86cb948c4337bda6ee9f256fbedcaf03f55fce53b5eedb0abcbc042ff443c9efccfb2fc88a0e0dad504f78f2fa366ce1d9'
6
+ metadata.gz: 3ad30c63d7281dabc739b5968595db9dd52dfa1553f173cc6b1de72893bfcc299bc59a2c8990717c962f4716d4479764e1f25f9118a65985082b95eac7921e28
7
+ data.tar.gz: 54abaa619e7ead56462ed5f811661b4b279d0bfe8d94c844896841913b0414229834fbd3f8a36daeb046a07315b8eae37ed16de342898e2c50b2b9616aa96d43
data/.gitignore CHANGED
@@ -14,5 +14,6 @@ Gemfile.lock
14
14
  tmp/
15
15
  test/screenshots/
16
16
  test/dummy_rails/log/*.log
17
-
17
+ test/dummy_rails/public/assets/
18
18
  .DS_Store
19
+ node_modules
@@ -1,12 +1,12 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.1
4
- - 2.0.0
3
+ - 2.1.2
5
4
  - jruby
6
5
  - rbx-2
7
6
  gemfile:
8
7
  - test/gemfiles/sass_3_2.gemfile
9
8
  - test/gemfiles/sass_3_3.gemfile
9
+ - test/gemfiles/sass_3_4.gemfile
10
10
  - test/gemfiles/sass_head.gemfile
11
11
  before_install:
12
12
  - "npm install"
@@ -17,3 +17,9 @@ matrix:
17
17
  - gemfile: test/gemfiles/sass_head.gemfile
18
18
  notifications:
19
19
  slack: heybb:3n88HHilXn76ji9vV4gL819Y
20
+ env:
21
+ global:
22
+ - VERBOSE=1
23
+ script:
24
+ - bundle exec rake
25
+ - sh test/*.sh
@@ -1,7 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.3.0
4
+
5
+ * Improve libsass compatibility
6
+ * Support using Bower package with Rails
7
+
3
8
  ## 3.2.0.2
4
9
 
10
+ Main bootstrap file is now a partial (_bootstrap.scss), for compatibility with Compass 1+.
11
+
5
12
  Fixed a number of bugs. [Issues closed in v3.2.0.2](https://github.com/twbs/bootstrap-sass/issues?q=is%3Aissue+is%3Aclosed+milestone%3Av3.2.0.2).
6
13
 
7
14
  ## 3.2.0.1
@@ -3,7 +3,7 @@
3
3
  ## Asset Changes
4
4
 
5
5
  Any changes to `bootstrap-sass` assets (scss, javascripts, fonts) should be checked against the `convert` rake task.
6
- For usage instructions, see the [README](https://github.com/twbs/bootstrap-sass/blob/3/README.md).
6
+ For usage instructions, see the [README](/README.md).
7
7
 
8
8
  If something is broken in the converter, it's preferable to update the converter along with the asset itself.
9
9
 
data/README.md CHANGED
@@ -17,7 +17,7 @@ Please see the appropriate guide for your environment of choice:
17
17
  In your Gemfile you need to add the `bootstrap-sass` gem, and ensure that the `sass-rails` gem is present - it is added to new Rails applications by default.
18
18
 
19
19
  ```ruby
20
- gem 'bootstrap-sass', '~> 3.2.0'
20
+ gem 'bootstrap-sass', '~> 3.3.0'
21
21
  gem 'sass-rails', '>= 3.2'
22
22
  ```
23
23
 
@@ -33,10 +33,13 @@ gem 'autoprefixer-rails'
33
33
  Import Bootstrap styles in `app/assets/stylesheets/application.css.scss`:
34
34
 
35
35
  ```scss
36
+ // "bootstrap-sprockets" must be imported before "bootstrap" and "bootstrap/variables"
36
37
  @import "bootstrap-sprockets";
37
38
  @import "bootstrap";
38
39
  ```
39
40
 
41
+ `bootstrap-sprockets` must be imported before `bootstrap` for the icon fonts to work.
42
+
40
43
  Make sure the file has `.css.scss` extension (or `.css.sass` for Sass syntax). If you have just generated a new Rails app,
41
44
  it may come with a `.css` file instead. If this file exists, it will be served instead of Sass, so remove it:
42
45
 
@@ -55,14 +58,28 @@ Require Bootstrap Javascripts in `app/assets/javascripts/application.js`:
55
58
 
56
59
  #### Bower with Rails
57
60
 
58
- When using [bootstrap-sass Bower package](#c-bower) in Rails, ensure [minimum Sass number precision](#sass-number-precision):
61
+ When using [bootstrap-sass Bower package](#c-bower) instead of the gem in Rails, add Bootstrap asset paths:
62
+
63
+ ```ruby
64
+ # config/application.rb
65
+ # bootstrap-sass asset paths
66
+ root.join('vendor/assets/bower_components/bootstrap-sass/assets').tap do |path|
67
+ config.sass.load_paths << path.join('stylesheets')
68
+ config.assets.paths += %w(javascripts fonts images).map(&path.method(:join))
69
+ end
70
+ ```
71
+
72
+ Then, ensure [minimum Sass number precision](#sass-number-precision):
59
73
 
60
74
  ```ruby
61
- # e.g. config/initializers/sass.rb
75
+ # config/initializers/sass.rb
76
+ # Minimum precision required by bootstrap-sass
62
77
  ::Sass::Script::Number.precision = [10, ::Sass::Script::Number.precision].max
63
78
  ```
64
79
 
65
- `bootstrap-sprockets` must be imported before `bootstrap` for the icon fonts to work.
80
+ #### Rails 4.x
81
+
82
+ Please make sure `sprockets-rails` is at least v2.1.4.
66
83
 
67
84
  #### Rails 3.2.x
68
85
 
@@ -159,8 +176,8 @@ See also this [example manifest.js](/test/dummy_node_mincer/manifest.js) for min
159
176
  By default all of Bootstrap is imported.
160
177
 
161
178
  You can also import components explicitly. To start with a full list of modules copy
162
- [`bootstrap.scss`](assets/stylesheets/bootstrap.scss) file into your assets as `bootstrap-custom.scss`.
163
- Then comment out components you do not want from `bootstrap-custom`.
179
+ [`_bootstrap.scss`](assets/stylesheets/_bootstrap.scss) file into your assets as `_bootstrap-custom.scss`.
180
+ Then comment out components you do not want from `_bootstrap-custom`.
164
181
  In the application Sass file, replace `@import 'bootstrap'` with:
165
182
 
166
183
  ```scss
@@ -253,6 +270,12 @@ $navbar-default-color: $light-orange;
253
270
  @import "bootstrap";
254
271
  ```
255
272
 
273
+ ## Version
274
+
275
+ `bootstrap-sass` version reflects the upstream version, with an additional number for Sass-specific changes.
276
+
277
+ Always refer to [CHANGELOG.md](/CHANGELOG.md) when upgrading.
278
+
256
279
  ---
257
280
 
258
281
  ## Development and Contributing
data/Rakefile CHANGED
@@ -4,10 +4,9 @@ $:.unshift(lib_path) unless $:.include?(lib_path)
4
4
  load './tasks/bower.rake'
5
5
 
6
6
  require 'rake/testtask'
7
- Rake::TestTask.new do |t|
8
- t.libs << "test"
9
- t.test_files = FileList['test/*_test.rb']
10
- t.verbose = true
7
+ task :test do |t|
8
+ $: << File.expand_path('test/')
9
+ Dir.glob('./test/**/*_test.rb').each { |file| require file }
11
10
  end
12
11
 
13
12
  desc 'Dumps output to a CSS file for testing'
@@ -226,4 +226,4 @@
226
226
  <glyph unicode="&#xe199;" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" />
227
227
  <glyph unicode="&#xe200;" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" />
228
228
  </font>
229
- </defs></svg>
229
+ </defs></svg>
@@ -1,5 +1,5 @@
1
1
  /* ========================================================================
2
- * Bootstrap: affix.js v3.2.0
2
+ * Bootstrap: affix.js v3.3.0
3
3
  * http://getbootstrap.com/javascript/#affix
4
4
  * ========================================================================
5
5
  * Copyright 2011-2014 Twitter, Inc.
@@ -28,7 +28,7 @@
28
28
  this.checkPosition()
29
29
  }
30
30
 
31
- Affix.VERSION = '3.2.0'
31
+ Affix.VERSION = '3.3.0'
32
32
 
33
33
  Affix.RESET = 'affix affix-top affix-bottom'
34
34
 
@@ -37,6 +37,28 @@
37
37
  target: window
38
38
  }
39
39
 
40
+ Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
41
+ var scrollTop = this.$target.scrollTop()
42
+ var position = this.$element.offset()
43
+ var targetHeight = this.$target.height()
44
+
45
+ if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
46
+
47
+ if (this.affixed == 'bottom') {
48
+ if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
49
+ return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
50
+ }
51
+
52
+ var initializing = this.affixed == null
53
+ var colliderTop = initializing ? scrollTop : position.top
54
+ var colliderHeight = initializing ? targetHeight : height
55
+
56
+ if (offsetTop != null && colliderTop <= offsetTop) return 'top'
57
+ if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
58
+
59
+ return false
60
+ }
61
+
40
62
  Affix.prototype.getPinnedOffset = function () {
41
63
  if (this.pinnedOffset) return this.pinnedOffset
42
64
  this.$element.removeClass(Affix.RESET).addClass('affix')
@@ -52,42 +74,40 @@
52
74
  Affix.prototype.checkPosition = function () {
53
75
  if (!this.$element.is(':visible')) return
54
76
 
55
- var scrollHeight = $(document).height()
56
- var scrollTop = this.$target.scrollTop()
57
- var position = this.$element.offset()
77
+ var height = this.$element.height()
58
78
  var offset = this.options.offset
59
79
  var offsetTop = offset.top
60
80
  var offsetBottom = offset.bottom
81
+ var scrollHeight = $('body').height()
61
82
 
62
83
  if (typeof offset != 'object') offsetBottom = offsetTop = offset
63
84
  if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
64
85
  if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
65
86
 
66
- var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false :
67
- offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
68
- offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false
87
+ var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
69
88
 
70
- if (this.affixed === affix) return
71
- if (this.unpin != null) this.$element.css('top', '')
89
+ if (this.affixed != affix) {
90
+ if (this.unpin != null) this.$element.css('top', '')
72
91
 
73
- var affixType = 'affix' + (affix ? '-' + affix : '')
74
- var e = $.Event(affixType + '.bs.affix')
92
+ var affixType = 'affix' + (affix ? '-' + affix : '')
93
+ var e = $.Event(affixType + '.bs.affix')
75
94
 
76
- this.$element.trigger(e)
95
+ this.$element.trigger(e)
77
96
 
78
- if (e.isDefaultPrevented()) return
97
+ if (e.isDefaultPrevented()) return
79
98
 
80
- this.affixed = affix
81
- this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
99
+ this.affixed = affix
100
+ this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
82
101
 
83
- this.$element
84
- .removeClass(Affix.RESET)
85
- .addClass(affixType)
86
- .trigger($.Event(affixType.replace('affix', 'affixed')))
102
+ this.$element
103
+ .removeClass(Affix.RESET)
104
+ .addClass(affixType)
105
+ .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
106
+ }
87
107
 
88
108
  if (affix == 'bottom') {
89
109
  this.$element.offset({
90
- top: scrollHeight - this.$element.height() - offsetBottom
110
+ top: scrollHeight - height - offsetBottom
91
111
  })
92
112
  }
93
113
  }
@@ -132,8 +152,8 @@
132
152
 
133
153
  data.offset = data.offset || {}
134
154
 
135
- if (data.offsetBottom) data.offset.bottom = data.offsetBottom
136
- if (data.offsetTop) data.offset.top = data.offsetTop
155
+ if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
156
+ if (data.offsetTop != null) data.offset.top = data.offsetTop
137
157
 
138
158
  Plugin.call($spy, data)
139
159
  })
@@ -142,7 +162,7 @@
142
162
  }(jQuery);
143
163
 
144
164
  /* ========================================================================
145
- * Bootstrap: alert.js v3.2.0
165
+ * Bootstrap: alert.js v3.3.0
146
166
  * http://getbootstrap.com/javascript/#alerts
147
167
  * ========================================================================
148
168
  * Copyright 2011-2014 Twitter, Inc.
@@ -161,7 +181,9 @@
161
181
  $(el).on('click', dismiss, this.close)
162
182
  }
163
183
 
164
- Alert.VERSION = '3.2.0'
184
+ Alert.VERSION = '3.3.0'
185
+
186
+ Alert.TRANSITION_DURATION = 150
165
187
 
166
188
  Alert.prototype.close = function (e) {
167
189
  var $this = $(this)
@@ -177,7 +199,7 @@
177
199
  if (e) e.preventDefault()
178
200
 
179
201
  if (!$parent.length) {
180
- $parent = $this.hasClass('alert') ? $this : $this.parent()
202
+ $parent = $this.closest('.alert')
181
203
  }
182
204
 
183
205
  $parent.trigger(e = $.Event('close.bs.alert'))
@@ -194,7 +216,7 @@
194
216
  $.support.transition && $parent.hasClass('fade') ?
195
217
  $parent
196
218
  .one('bsTransitionEnd', removeElement)
197
- .emulateTransitionEnd(150) :
219
+ .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
198
220
  removeElement()
199
221
  }
200
222
 
@@ -235,7 +257,7 @@
235
257
  }(jQuery);
236
258
 
237
259
  /* ========================================================================
238
- * Bootstrap: button.js v3.2.0
260
+ * Bootstrap: button.js v3.3.0
239
261
  * http://getbootstrap.com/javascript/#buttons
240
262
  * ========================================================================
241
263
  * Copyright 2011-2014 Twitter, Inc.
@@ -255,7 +277,7 @@
255
277
  this.isLoading = false
256
278
  }
257
279
 
258
- Button.VERSION = '3.2.0'
280
+ Button.VERSION = '3.3.0'
259
281
 
260
282
  Button.DEFAULTS = {
261
283
  loadingText: 'loading...'
@@ -271,10 +293,10 @@
271
293
 
272
294
  if (data.resetText == null) $el.data('resetText', $el[val]())
273
295
 
274
- $el[val](data[state] == null ? this.options[state] : data[state])
275
-
276
296
  // push to event loop to allow forms to submit
277
297
  setTimeout($.proxy(function () {
298
+ $el[val](data[state] == null ? this.options[state] : data[state])
299
+
278
300
  if (state == 'loadingText') {
279
301
  this.isLoading = true
280
302
  $el.addClass(d).attr(d, d)
@@ -296,6 +318,8 @@
296
318
  else $parent.find('.active').removeClass('active')
297
319
  }
298
320
  if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
321
+ } else {
322
+ this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
299
323
  }
300
324
 
301
325
  if (changed) this.$element.toggleClass('active')
@@ -336,17 +360,21 @@
336
360
  // BUTTON DATA-API
337
361
  // ===============
338
362
 
339
- $(document).on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
340
- var $btn = $(e.target)
341
- if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
342
- Plugin.call($btn, 'toggle')
343
- e.preventDefault()
344
- })
363
+ $(document)
364
+ .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
365
+ var $btn = $(e.target)
366
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
367
+ Plugin.call($btn, 'toggle')
368
+ e.preventDefault()
369
+ })
370
+ .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
371
+ $(e.target).closest('.btn').toggleClass('focus', e.type == 'focus')
372
+ })
345
373
 
346
374
  }(jQuery);
347
375
 
348
376
  /* ========================================================================
349
- * Bootstrap: carousel.js v3.2.0
377
+ * Bootstrap: carousel.js v3.3.0
350
378
  * http://getbootstrap.com/javascript/#carousel
351
379
  * ========================================================================
352
380
  * Copyright 2011-2014 Twitter, Inc.
@@ -361,7 +389,7 @@
361
389
  // =========================
362
390
 
363
391
  var Carousel = function (element, options) {
364
- this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this))
392
+ this.$element = $(element)
365
393
  this.$indicators = this.$element.find('.carousel-indicators')
366
394
  this.options = options
367
395
  this.paused =
@@ -370,17 +398,22 @@
370
398
  this.$active =
371
399
  this.$items = null
372
400
 
373
- this.options.pause == 'hover' && this.$element
401
+ this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
402
+
403
+ this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
374
404
  .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
375
405
  .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
376
406
  }
377
407
 
378
- Carousel.VERSION = '3.2.0'
408
+ Carousel.VERSION = '3.3.0'
409
+
410
+ Carousel.TRANSITION_DURATION = 600
379
411
 
380
412
  Carousel.DEFAULTS = {
381
413
  interval: 5000,
382
414
  pause: 'hover',
383
- wrap: true
415
+ wrap: true,
416
+ keyboard: true
384
417
  }
385
418
 
386
419
  Carousel.prototype.keydown = function (e) {
@@ -410,6 +443,13 @@
410
443
  return this.$items.index(item || this.$active)
411
444
  }
412
445
 
446
+ Carousel.prototype.getItemForDirection = function (direction, active) {
447
+ var delta = direction == 'prev' ? -1 : 1
448
+ var activeIndex = this.getItemIndex(active)
449
+ var itemIndex = (activeIndex + delta) % this.$items.length
450
+ return this.$items.eq(itemIndex)
451
+ }
452
+
413
453
  Carousel.prototype.to = function (pos) {
414
454
  var that = this
415
455
  var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
@@ -419,7 +459,7 @@
419
459
  if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
420
460
  if (activeIndex == pos) return this.pause().cycle()
421
461
 
422
- return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
462
+ return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
423
463
  }
424
464
 
425
465
  Carousel.prototype.pause = function (e) {
@@ -447,7 +487,7 @@
447
487
 
448
488
  Carousel.prototype.slide = function (type, next) {
449
489
  var $active = this.$element.find('.item.active')
450
- var $next = next || $active[type]()
490
+ var $next = next || this.getItemForDirection(type, $active)
451
491
  var isCycling = this.interval
452
492
  var direction = type == 'next' ? 'left' : 'right'
453
493
  var fallback = type == 'next' ? 'first' : 'last'
@@ -493,7 +533,7 @@
493
533
  that.$element.trigger(slidEvent)
494
534
  }, 0)
495
535
  })
496
- .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000)
536
+ .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
497
537
  } else {
498
538
  $active.removeClass('active')
499
539
  $next.addClass('active')
@@ -542,7 +582,7 @@
542
582
  // CAROUSEL DATA-API
543
583
  // =================
544
584
 
545
- $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
585
+ var clickHandler = function (e) {
546
586
  var href
547
587
  var $this = $(this)
548
588
  var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
@@ -558,7 +598,11 @@
558
598
  }
559
599
 
560
600
  e.preventDefault()
561
- })
601
+ }
602
+
603
+ $(document)
604
+ .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
605
+ .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
562
606
 
563
607
  $(window).on('load', function () {
564
608
  $('[data-ride="carousel"]').each(function () {
@@ -570,7 +614,7 @@
570
614
  }(jQuery);
571
615
 
572
616
  /* ========================================================================
573
- * Bootstrap: collapse.js v3.2.0
617
+ * Bootstrap: collapse.js v3.3.0
574
618
  * http://getbootstrap.com/javascript/#collapse
575
619
  * ========================================================================
576
620
  * Copyright 2011-2014 Twitter, Inc.
@@ -587,16 +631,25 @@
587
631
  var Collapse = function (element, options) {
588
632
  this.$element = $(element)
589
633
  this.options = $.extend({}, Collapse.DEFAULTS, options)
634
+ this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]')
590
635
  this.transitioning = null
591
636
 
592
- if (this.options.parent) this.$parent = $(this.options.parent)
637
+ if (this.options.parent) {
638
+ this.$parent = this.getParent()
639
+ } else {
640
+ this.addAriaAndCollapsedClass(this.$element, this.$trigger)
641
+ }
642
+
593
643
  if (this.options.toggle) this.toggle()
594
644
  }
595
645
 
596
- Collapse.VERSION = '3.2.0'
646
+ Collapse.VERSION = '3.3.0'
647
+
648
+ Collapse.TRANSITION_DURATION = 350
597
649
 
598
650
  Collapse.DEFAULTS = {
599
- toggle: true
651
+ toggle: true,
652
+ trigger: '[data-toggle="collapse"]'
600
653
  }
601
654
 
602
655
  Collapse.prototype.dimension = function () {
@@ -607,17 +660,21 @@
607
660
  Collapse.prototype.show = function () {
608
661
  if (this.transitioning || this.$element.hasClass('in')) return
609
662
 
663
+ var activesData
664
+ var actives = this.$parent && this.$parent.find('> .panel').children('.in, .collapsing')
665
+
666
+ if (actives && actives.length) {
667
+ activesData = actives.data('bs.collapse')
668
+ if (activesData && activesData.transitioning) return
669
+ }
670
+
610
671
  var startEvent = $.Event('show.bs.collapse')
611
672
  this.$element.trigger(startEvent)
612
673
  if (startEvent.isDefaultPrevented()) return
613
674
 
614
- var actives = this.$parent && this.$parent.find('> .panel > .in')
615
-
616
675
  if (actives && actives.length) {
617
- var hasData = actives.data('bs.collapse')
618
- if (hasData && hasData.transitioning) return
619
676
  Plugin.call(actives, 'hide')
620
- hasData || actives.data('bs.collapse', null)
677
+ activesData || actives.data('bs.collapse', null)
621
678
  }
622
679
 
623
680
  var dimension = this.dimension()
@@ -625,6 +682,11 @@
625
682
  this.$element
626
683
  .removeClass('collapse')
627
684
  .addClass('collapsing')[dimension](0)
685
+ .attr('aria-expanded', true)
686
+
687
+ this.$trigger
688
+ .removeClass('collapsed')
689
+ .attr('aria-expanded', true)
628
690
 
629
691
  this.transitioning = 1
630
692
 
@@ -643,7 +705,7 @@
643
705
 
644
706
  this.$element
645
707
  .one('bsTransitionEnd', $.proxy(complete, this))
646
- .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize])
708
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
647
709
  }
648
710
 
649
711
  Collapse.prototype.hide = function () {
@@ -659,17 +721,21 @@
659
721
 
660
722
  this.$element
661
723
  .addClass('collapsing')
662
- .removeClass('collapse')
663
- .removeClass('in')
724
+ .removeClass('collapse in')
725
+ .attr('aria-expanded', false)
726
+
727
+ this.$trigger
728
+ .addClass('collapsed')
729
+ .attr('aria-expanded', false)
664
730
 
665
731
  this.transitioning = 1
666
732
 
667
733
  var complete = function () {
668
734
  this.transitioning = 0
669
735
  this.$element
670
- .trigger('hidden.bs.collapse')
671
736
  .removeClass('collapsing')
672
737
  .addClass('collapse')
738
+ .trigger('hidden.bs.collapse')
673
739
  }
674
740
 
675
741
  if (!$.support.transition) return complete.call(this)
@@ -677,13 +743,40 @@
677
743
  this.$element
678
744
  [dimension](0)
679
745
  .one('bsTransitionEnd', $.proxy(complete, this))
680
- .emulateTransitionEnd(350)
746
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
681
747
  }
682
748
 
683
749
  Collapse.prototype.toggle = function () {
684
750
  this[this.$element.hasClass('in') ? 'hide' : 'show']()
685
751
  }
686
752
 
753
+ Collapse.prototype.getParent = function () {
754
+ return $(this.options.parent)
755
+ .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
756
+ .each($.proxy(function (i, element) {
757
+ var $element = $(element)
758
+ this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
759
+ }, this))
760
+ .end()
761
+ }
762
+
763
+ Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
764
+ var isOpen = $element.hasClass('in')
765
+
766
+ $element.attr('aria-expanded', isOpen)
767
+ $trigger
768
+ .toggleClass('collapsed', !isOpen)
769
+ .attr('aria-expanded', isOpen)
770
+ }
771
+
772
+ function getTargetFromTrigger($trigger) {
773
+ var href
774
+ var target = $trigger.attr('data-target')
775
+ || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
776
+
777
+ return $(target)
778
+ }
779
+
687
780
 
688
781
  // COLLAPSE PLUGIN DEFINITION
689
782
  // ==========================
@@ -694,7 +787,7 @@
694
787
  var data = $this.data('bs.collapse')
695
788
  var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
696
789
 
697
- if (!data && options.toggle && option == 'show') option = !option
790
+ if (!data && options.toggle && option == 'show') options.toggle = false
698
791
  if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
699
792
  if (typeof option == 'string') data[option]()
700
793
  })
@@ -719,21 +812,13 @@
719
812
  // =================
720
813
 
721
814
  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
722
- var href
723
815
  var $this = $(this)
724
- var target = $this.attr('data-target')
725
- || e.preventDefault()
726
- || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
727
- var $target = $(target)
728
- var data = $target.data('bs.collapse')
729
- var option = data ? 'toggle' : $this.data()
730
- var parent = $this.attr('data-parent')
731
- var $parent = parent && $(parent)
732
816
 
733
- if (!data || !data.transitioning) {
734
- if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed')
735
- $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
736
- }
817
+ if (!$this.attr('data-target')) e.preventDefault()
818
+
819
+ var $target = getTargetFromTrigger($this)
820
+ var data = $target.data('bs.collapse')
821
+ var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this })
737
822
 
738
823
  Plugin.call($target, option)
739
824
  })
@@ -741,7 +826,7 @@
741
826
  }(jQuery);
742
827
 
743
828
  /* ========================================================================
744
- * Bootstrap: dropdown.js v3.2.0
829
+ * Bootstrap: dropdown.js v3.3.0
745
830
  * http://getbootstrap.com/javascript/#dropdowns
746
831
  * ========================================================================
747
832
  * Copyright 2011-2014 Twitter, Inc.
@@ -761,7 +846,7 @@
761
846
  $(element).on('click.bs.dropdown', this.toggle)
762
847
  }
763
848
 
764
- Dropdown.VERSION = '3.2.0'
849
+ Dropdown.VERSION = '3.3.0'
765
850
 
766
851
  Dropdown.prototype.toggle = function (e) {
767
852
  var $this = $(this)
@@ -784,7 +869,9 @@
784
869
 
785
870
  if (e.isDefaultPrevented()) return
786
871
 
787
- $this.trigger('focus')
872
+ $this
873
+ .trigger('focus')
874
+ .attr('aria-expanded', 'true')
788
875
 
789
876
  $parent
790
877
  .toggleClass('open')
@@ -795,7 +882,7 @@
795
882
  }
796
883
 
797
884
  Dropdown.prototype.keydown = function (e) {
798
- if (!/(38|40|27)/.test(e.keyCode)) return
885
+ if (!/(38|40|27|32)/.test(e.which)) return
799
886
 
800
887
  var $this = $(this)
801
888
 
@@ -807,7 +894,7 @@
807
894
  var $parent = getParent($this)
808
895
  var isActive = $parent.hasClass('open')
809
896
 
810
- if (!isActive || (isActive && e.keyCode == 27)) {
897
+ if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
811
898
  if (e.which == 27) $parent.find(toggle).trigger('focus')
812
899
  return $this.trigger('click')
813
900
  }
@@ -817,10 +904,10 @@
817
904
 
818
905
  if (!$items.length) return
819
906
 
820
- var index = $items.index($items.filter(':focus'))
907
+ var index = $items.index(e.target)
821
908
 
822
- if (e.keyCode == 38 && index > 0) index-- // up
823
- if (e.keyCode == 40 && index < $items.length - 1) index++ // down
909
+ if (e.which == 38 && index > 0) index-- // up
910
+ if (e.which == 40 && index < $items.length - 1) index++ // down
824
911
  if (!~index) index = 0
825
912
 
826
913
  $items.eq(index).trigger('focus')
@@ -830,11 +917,17 @@
830
917
  if (e && e.which === 3) return
831
918
  $(backdrop).remove()
832
919
  $(toggle).each(function () {
833
- var $parent = getParent($(this))
920
+ var $this = $(this)
921
+ var $parent = getParent($this)
834
922
  var relatedTarget = { relatedTarget: this }
923
+
835
924
  if (!$parent.hasClass('open')) return
925
+
836
926
  $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
927
+
837
928
  if (e.isDefaultPrevented()) return
929
+
930
+ $this.attr('aria-expanded', 'false')
838
931
  $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
839
932
  })
840
933
  }
@@ -888,12 +981,14 @@
888
981
  .on('click.bs.dropdown.data-api', clearMenus)
889
982
  .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
890
983
  .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
891
- .on('keydown.bs.dropdown.data-api', toggle + ', [role="menu"], [role="listbox"]', Dropdown.prototype.keydown)
984
+ .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
985
+ .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)
986
+ .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)
892
987
 
893
988
  }(jQuery);
894
989
 
895
990
  /* ========================================================================
896
- * Bootstrap: tab.js v3.2.0
991
+ * Bootstrap: tab.js v3.3.0
897
992
  * http://getbootstrap.com/javascript/#tabs
898
993
  * ========================================================================
899
994
  * Copyright 2011-2014 Twitter, Inc.
@@ -911,7 +1006,9 @@
911
1006
  this.element = $(element)
912
1007
  }
913
1008
 
914
- Tab.VERSION = '3.2.0'
1009
+ Tab.VERSION = '3.3.0'
1010
+
1011
+ Tab.TRANSITION_DURATION = 150
915
1012
 
916
1013
  Tab.prototype.show = function () {
917
1014
  var $this = this.element
@@ -925,22 +1022,30 @@
925
1022
 
926
1023
  if ($this.parent('li').hasClass('active')) return
927
1024
 
928
- var previous = $ul.find('.active:last a')[0]
929
- var e = $.Event('show.bs.tab', {
930
- relatedTarget: previous
1025
+ var $previous = $ul.find('.active:last a')
1026
+ var hideEvent = $.Event('hide.bs.tab', {
1027
+ relatedTarget: $this[0]
1028
+ })
1029
+ var showEvent = $.Event('show.bs.tab', {
1030
+ relatedTarget: $previous[0]
931
1031
  })
932
1032
 
933
- $this.trigger(e)
1033
+ $previous.trigger(hideEvent)
1034
+ $this.trigger(showEvent)
934
1035
 
935
- if (e.isDefaultPrevented()) return
1036
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
936
1037
 
937
1038
  var $target = $(selector)
938
1039
 
939
1040
  this.activate($this.closest('li'), $ul)
940
1041
  this.activate($target, $target.parent(), function () {
1042
+ $previous.trigger({
1043
+ type: 'hidden.bs.tab',
1044
+ relatedTarget: $this[0]
1045
+ })
941
1046
  $this.trigger({
942
1047
  type: 'shown.bs.tab',
943
- relatedTarget: previous
1048
+ relatedTarget: $previous[0]
944
1049
  })
945
1050
  })
946
1051
  }
@@ -949,15 +1054,21 @@
949
1054
  var $active = container.find('> .active')
950
1055
  var transition = callback
951
1056
  && $.support.transition
952
- && $active.hasClass('fade')
1057
+ && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length)
953
1058
 
954
1059
  function next() {
955
1060
  $active
956
1061
  .removeClass('active')
957
1062
  .find('> .dropdown-menu > .active')
958
- .removeClass('active')
1063
+ .removeClass('active')
1064
+ .end()
1065
+ .find('[data-toggle="tab"]')
1066
+ .attr('aria-expanded', false)
959
1067
 
960
- element.addClass('active')
1068
+ element
1069
+ .addClass('active')
1070
+ .find('[data-toggle="tab"]')
1071
+ .attr('aria-expanded', true)
961
1072
 
962
1073
  if (transition) {
963
1074
  element[0].offsetWidth // reflow for transition
@@ -967,16 +1078,21 @@
967
1078
  }
968
1079
 
969
1080
  if (element.parent('.dropdown-menu')) {
970
- element.closest('li.dropdown').addClass('active')
1081
+ element
1082
+ .closest('li.dropdown')
1083
+ .addClass('active')
1084
+ .end()
1085
+ .find('[data-toggle="tab"]')
1086
+ .attr('aria-expanded', true)
971
1087
  }
972
1088
 
973
1089
  callback && callback()
974
1090
  }
975
1091
 
976
- transition ?
1092
+ $active.length && transition ?
977
1093
  $active
978
1094
  .one('bsTransitionEnd', next)
979
- .emulateTransitionEnd(150) :
1095
+ .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
980
1096
  next()
981
1097
 
982
1098
  $active.removeClass('in')
@@ -1014,15 +1130,19 @@
1014
1130
  // TAB DATA-API
1015
1131
  // ============
1016
1132
 
1017
- $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
1133
+ var clickHandler = function (e) {
1018
1134
  e.preventDefault()
1019
1135
  Plugin.call($(this), 'show')
1020
- })
1136
+ }
1137
+
1138
+ $(document)
1139
+ .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
1140
+ .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
1021
1141
 
1022
1142
  }(jQuery);
1023
1143
 
1024
1144
  /* ========================================================================
1025
- * Bootstrap: transition.js v3.2.0
1145
+ * Bootstrap: transition.js v3.3.0
1026
1146
  * http://getbootstrap.com/javascript/#transitions
1027
1147
  * ========================================================================
1028
1148
  * Copyright 2011-2014 Twitter, Inc.
@@ -1082,7 +1202,7 @@
1082
1202
  }(jQuery);
1083
1203
 
1084
1204
  /* ========================================================================
1085
- * Bootstrap: scrollspy.js v3.2.0
1205
+ * Bootstrap: scrollspy.js v3.3.0
1086
1206
  * http://getbootstrap.com/javascript/#scrollspy
1087
1207
  * ========================================================================
1088
1208
  * Copyright 2011-2014 Twitter, Inc.
@@ -1113,7 +1233,7 @@
1113
1233
  this.process()
1114
1234
  }
1115
1235
 
1116
- ScrollSpy.VERSION = '3.2.0'
1236
+ ScrollSpy.VERSION = '3.3.0'
1117
1237
 
1118
1238
  ScrollSpy.DEFAULTS = {
1119
1239
  offset: 10
@@ -1174,8 +1294,9 @@
1174
1294
  return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
1175
1295
  }
1176
1296
 
1177
- if (activeTarget && scrollTop <= offsets[0]) {
1178
- return activeTarget != (i = targets[0]) && this.activate(i)
1297
+ if (activeTarget && scrollTop < offsets[0]) {
1298
+ this.activeTarget = null
1299
+ return this.clear()
1179
1300
  }
1180
1301
 
1181
1302
  for (i = offsets.length; i--;) {
@@ -1189,9 +1310,7 @@
1189
1310
  ScrollSpy.prototype.activate = function (target) {
1190
1311
  this.activeTarget = target
1191
1312
 
1192
- $(this.selector)
1193
- .parentsUntil(this.options.target, '.active')
1194
- .removeClass('active')
1313
+ this.clear()
1195
1314
 
1196
1315
  var selector = this.selector +
1197
1316
  '[data-target="' + target + '"],' +
@@ -1210,6 +1329,12 @@
1210
1329
  active.trigger('activate.bs.scrollspy')
1211
1330
  }
1212
1331
 
1332
+ ScrollSpy.prototype.clear = function () {
1333
+ $(this.selector)
1334
+ .parentsUntil(this.options.target, '.active')
1335
+ .removeClass('active')
1336
+ }
1337
+
1213
1338
 
1214
1339
  // SCROLLSPY PLUGIN DEFINITION
1215
1340
  // ===========================
@@ -1253,7 +1378,7 @@
1253
1378
  }(jQuery);
1254
1379
 
1255
1380
  /* ========================================================================
1256
- * Bootstrap: modal.js v3.2.0
1381
+ * Bootstrap: modal.js v3.3.0
1257
1382
  * http://getbootstrap.com/javascript/#modals
1258
1383
  * ========================================================================
1259
1384
  * Copyright 2011-2014 Twitter, Inc.
@@ -1284,7 +1409,10 @@
1284
1409
  }
1285
1410
  }
1286
1411
 
1287
- Modal.VERSION = '3.2.0'
1412
+ Modal.VERSION = '3.3.0'
1413
+
1414
+ Modal.TRANSITION_DURATION = 300
1415
+ Modal.BACKDROP_TRANSITION_DURATION = 150
1288
1416
 
1289
1417
  Modal.DEFAULTS = {
1290
1418
  backdrop: true,
@@ -1342,7 +1470,7 @@
1342
1470
  .one('bsTransitionEnd', function () {
1343
1471
  that.$element.trigger('focus').trigger(e)
1344
1472
  })
1345
- .emulateTransitionEnd(300) :
1473
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
1346
1474
  that.$element.trigger('focus').trigger(e)
1347
1475
  })
1348
1476
  }
@@ -1358,9 +1486,6 @@
1358
1486
 
1359
1487
  this.isShown = false
1360
1488
 
1361
- this.$body.removeClass('modal-open')
1362
-
1363
- this.resetScrollbar()
1364
1489
  this.escape()
1365
1490
 
1366
1491
  $(document).off('focusin.bs.modal')
@@ -1373,7 +1498,7 @@
1373
1498
  $.support.transition && this.$element.hasClass('fade') ?
1374
1499
  this.$element
1375
1500
  .one('bsTransitionEnd', $.proxy(this.hideModal, this))
1376
- .emulateTransitionEnd(300) :
1501
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
1377
1502
  this.hideModal()
1378
1503
  }
1379
1504
 
@@ -1389,11 +1514,11 @@
1389
1514
 
1390
1515
  Modal.prototype.escape = function () {
1391
1516
  if (this.isShown && this.options.keyboard) {
1392
- this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
1517
+ this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
1393
1518
  e.which == 27 && this.hide()
1394
1519
  }, this))
1395
1520
  } else if (!this.isShown) {
1396
- this.$element.off('keyup.dismiss.bs.modal')
1521
+ this.$element.off('keydown.dismiss.bs.modal')
1397
1522
  }
1398
1523
  }
1399
1524
 
@@ -1401,6 +1526,8 @@
1401
1526
  var that = this
1402
1527
  this.$element.hide()
1403
1528
  this.backdrop(function () {
1529
+ that.$body.removeClass('modal-open')
1530
+ that.resetScrollbar()
1404
1531
  that.$element.trigger('hidden.bs.modal')
1405
1532
  })
1406
1533
  }
@@ -1418,14 +1545,13 @@
1418
1545
  var doAnimate = $.support.transition && animate
1419
1546
 
1420
1547
  this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
1421
- .appendTo(this.$body)
1422
-
1423
- this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
1424
- if (e.target !== e.currentTarget) return
1425
- this.options.backdrop == 'static'
1426
- ? this.$element[0].focus.call(this.$element[0])
1427
- : this.hide.call(this)
1428
- }, this))
1548
+ .prependTo(this.$element)
1549
+ .on('click.dismiss.bs.modal', $.proxy(function (e) {
1550
+ if (e.target !== e.currentTarget) return
1551
+ this.options.backdrop == 'static'
1552
+ ? this.$element[0].focus.call(this.$element[0])
1553
+ : this.hide.call(this)
1554
+ }, this))
1429
1555
 
1430
1556
  if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
1431
1557
 
@@ -1436,7 +1562,7 @@
1436
1562
  doAnimate ?
1437
1563
  this.$backdrop
1438
1564
  .one('bsTransitionEnd', callback)
1439
- .emulateTransitionEnd(150) :
1565
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
1440
1566
  callback()
1441
1567
 
1442
1568
  } else if (!this.isShown && this.$backdrop) {
@@ -1449,7 +1575,7 @@
1449
1575
  $.support.transition && this.$element.hasClass('fade') ?
1450
1576
  this.$backdrop
1451
1577
  .one('bsTransitionEnd', callbackRemove)
1452
- .emulateTransitionEnd(150) :
1578
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
1453
1579
  callbackRemove()
1454
1580
 
1455
1581
  } else if (callback) {
@@ -1458,8 +1584,7 @@
1458
1584
  }
1459
1585
 
1460
1586
  Modal.prototype.checkScrollbar = function () {
1461
- if (document.body.clientWidth >= window.innerWidth) return
1462
- this.scrollbarWidth = this.scrollbarWidth || this.measureScrollbar()
1587
+ this.scrollbarWidth = this.measureScrollbar()
1463
1588
  }
1464
1589
 
1465
1590
  Modal.prototype.setScrollbar = function () {
@@ -1472,6 +1597,7 @@
1472
1597
  }
1473
1598
 
1474
1599
  Modal.prototype.measureScrollbar = function () { // thx walsh
1600
+ if (document.body.clientWidth >= window.innerWidth) return 0
1475
1601
  var scrollDiv = document.createElement('div')
1476
1602
  scrollDiv.className = 'modal-scrollbar-measure'
1477
1603
  this.$body.append(scrollDiv)
@@ -1534,7 +1660,7 @@
1534
1660
  }(jQuery);
1535
1661
 
1536
1662
  /* ========================================================================
1537
- * Bootstrap: tooltip.js v3.2.0
1663
+ * Bootstrap: tooltip.js v3.3.0
1538
1664
  * http://getbootstrap.com/javascript/#tooltip
1539
1665
  * Inspired by the original jQuery.tipsy by Jason Frame
1540
1666
  * ========================================================================
@@ -1560,7 +1686,9 @@
1560
1686
  this.init('tooltip', element, options)
1561
1687
  }
1562
1688
 
1563
- Tooltip.VERSION = '3.2.0'
1689
+ Tooltip.VERSION = '3.3.0'
1690
+
1691
+ Tooltip.TRANSITION_DURATION = 150
1564
1692
 
1565
1693
  Tooltip.DEFAULTS = {
1566
1694
  animation: true,
@@ -1638,6 +1766,11 @@
1638
1766
  var self = obj instanceof this.constructor ?
1639
1767
  obj : $(obj.currentTarget).data('bs.' + this.type)
1640
1768
 
1769
+ if (self && self.$tip && self.$tip.is(':visible')) {
1770
+ self.hoverState = 'in'
1771
+ return
1772
+ }
1773
+
1641
1774
  if (!self) {
1642
1775
  self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
1643
1776
  $(obj.currentTarget).data('bs.' + this.type, self)
@@ -1680,7 +1813,7 @@
1680
1813
  if (this.hasContent() && this.enabled) {
1681
1814
  this.$element.trigger(e)
1682
1815
 
1683
- var inDom = $.contains(document.documentElement, this.$element[0])
1816
+ var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
1684
1817
  if (e.isDefaultPrevented() || !inDom) return
1685
1818
  var that = this
1686
1819
 
@@ -1716,13 +1849,13 @@
1716
1849
 
1717
1850
  if (autoPlace) {
1718
1851
  var orgPlacement = placement
1719
- var $parent = this.$element.parent()
1720
- var parentDim = this.getPosition($parent)
1852
+ var $container = this.options.container ? $(this.options.container) : this.$element.parent()
1853
+ var containerDim = this.getPosition($container)
1721
1854
 
1722
- placement = placement == 'bottom' && pos.top + pos.height + actualHeight - parentDim.scroll > parentDim.height ? 'top' :
1723
- placement == 'top' && pos.top - parentDim.scroll - actualHeight < 0 ? 'bottom' :
1724
- placement == 'right' && pos.right + actualWidth > parentDim.width ? 'left' :
1725
- placement == 'left' && pos.left - actualWidth < parentDim.left ? 'right' :
1855
+ placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
1856
+ placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
1857
+ placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
1858
+ placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
1726
1859
  placement
1727
1860
 
1728
1861
  $tip
@@ -1735,14 +1868,17 @@
1735
1868
  this.applyPlacement(calculatedOffset, placement)
1736
1869
 
1737
1870
  var complete = function () {
1871
+ var prevHoverState = that.hoverState
1738
1872
  that.$element.trigger('shown.bs.' + that.type)
1739
1873
  that.hoverState = null
1874
+
1875
+ if (prevHoverState == 'out') that.leave(that)
1740
1876
  }
1741
1877
 
1742
1878
  $.support.transition && this.$tip.hasClass('fade') ?
1743
1879
  $tip
1744
1880
  .one('bsTransitionEnd', complete)
1745
- .emulateTransitionEnd(150) :
1881
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
1746
1882
  complete()
1747
1883
  }
1748
1884
  }
@@ -1789,16 +1925,18 @@
1789
1925
  if (delta.left) offset.left += delta.left
1790
1926
  else offset.top += delta.top
1791
1927
 
1792
- var arrowDelta = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
1793
- var arrowPosition = delta.left ? 'left' : 'top'
1794
- var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight'
1928
+ var isVertical = /top|bottom/.test(placement)
1929
+ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
1930
+ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
1795
1931
 
1796
1932
  $tip.offset(offset)
1797
- this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
1933
+ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
1798
1934
  }
1799
1935
 
1800
- Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
1801
- this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
1936
+ Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) {
1937
+ this.arrow()
1938
+ .css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
1939
+ .css(isHorizontal ? 'top' : 'left', '')
1802
1940
  }
1803
1941
 
1804
1942
  Tooltip.prototype.setContent = function () {
@@ -1809,16 +1947,17 @@
1809
1947
  $tip.removeClass('fade in top bottom left right')
1810
1948
  }
1811
1949
 
1812
- Tooltip.prototype.hide = function () {
1950
+ Tooltip.prototype.hide = function (callback) {
1813
1951
  var that = this
1814
1952
  var $tip = this.tip()
1815
1953
  var e = $.Event('hide.bs.' + this.type)
1816
1954
 
1817
- this.$element.removeAttr('aria-describedby')
1818
-
1819
1955
  function complete() {
1820
1956
  if (that.hoverState != 'in') $tip.detach()
1821
- that.$element.trigger('hidden.bs.' + that.type)
1957
+ that.$element
1958
+ .removeAttr('aria-describedby')
1959
+ .trigger('hidden.bs.' + that.type)
1960
+ callback && callback()
1822
1961
  }
1823
1962
 
1824
1963
  this.$element.trigger(e)
@@ -1830,7 +1969,7 @@
1830
1969
  $.support.transition && this.$tip.hasClass('fade') ?
1831
1970
  $tip
1832
1971
  .one('bsTransitionEnd', complete)
1833
- .emulateTransitionEnd(150) :
1972
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
1834
1973
  complete()
1835
1974
 
1836
1975
  this.hoverState = null
@@ -1851,13 +1990,20 @@
1851
1990
 
1852
1991
  Tooltip.prototype.getPosition = function ($element) {
1853
1992
  $element = $element || this.$element
1993
+
1854
1994
  var el = $element[0]
1855
1995
  var isBody = el.tagName == 'BODY'
1856
- return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, {
1857
- scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(),
1858
- width: isBody ? $(window).width() : $element.outerWidth(),
1859
- height: isBody ? $(window).height() : $element.outerHeight()
1860
- }, isBody ? { top: 0, left: 0 } : $element.offset())
1996
+
1997
+ var elRect = el.getBoundingClientRect()
1998
+ if (elRect.width == null) {
1999
+ // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
2000
+ elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
2001
+ }
2002
+ var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
2003
+ var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
2004
+ var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
2005
+
2006
+ return $.extend({}, elRect, scroll, outerDims, elOffset)
1861
2007
  }
1862
2008
 
1863
2009
  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
@@ -1921,14 +2067,6 @@
1921
2067
  return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
1922
2068
  }
1923
2069
 
1924
- Tooltip.prototype.validate = function () {
1925
- if (!this.$element[0].parentNode) {
1926
- this.hide()
1927
- this.$element = null
1928
- this.options = null
1929
- }
1930
- }
1931
-
1932
2070
  Tooltip.prototype.enable = function () {
1933
2071
  this.enabled = true
1934
2072
  }
@@ -1955,8 +2093,11 @@
1955
2093
  }
1956
2094
 
1957
2095
  Tooltip.prototype.destroy = function () {
2096
+ var that = this
1958
2097
  clearTimeout(this.timeout)
1959
- this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
2098
+ this.hide(function () {
2099
+ that.$element.off('.' + that.type).removeData('bs.' + that.type)
2100
+ })
1960
2101
  }
1961
2102
 
1962
2103
 
@@ -1965,12 +2106,18 @@
1965
2106
 
1966
2107
  function Plugin(option) {
1967
2108
  return this.each(function () {
1968
- var $this = $(this)
1969
- var data = $this.data('bs.tooltip')
1970
- var options = typeof option == 'object' && option
2109
+ var $this = $(this)
2110
+ var data = $this.data('bs.tooltip')
2111
+ var options = typeof option == 'object' && option
2112
+ var selector = options && options.selector
1971
2113
 
1972
2114
  if (!data && option == 'destroy') return
1973
- if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
2115
+ if (selector) {
2116
+ if (!data) $this.data('bs.tooltip', (data = {}))
2117
+ if (!data[selector]) data[selector] = new Tooltip(this, options)
2118
+ } else {
2119
+ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
2120
+ }
1974
2121
  if (typeof option == 'string') data[option]()
1975
2122
  })
1976
2123
  }
@@ -1992,7 +2139,7 @@
1992
2139
  }(jQuery);
1993
2140
 
1994
2141
  /* ========================================================================
1995
- * Bootstrap: popover.js v3.2.0
2142
+ * Bootstrap: popover.js v3.3.0
1996
2143
  * http://getbootstrap.com/javascript/#popovers
1997
2144
  * ========================================================================
1998
2145
  * Copyright 2011-2014 Twitter, Inc.
@@ -2012,7 +2159,7 @@
2012
2159
 
2013
2160
  if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
2014
2161
 
2015
- Popover.VERSION = '3.2.0'
2162
+ Popover.VERSION = '3.3.0'
2016
2163
 
2017
2164
  Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
2018
2165
  placement: 'right',
@@ -2039,7 +2186,7 @@
2039
2186
  var content = this.getContent()
2040
2187
 
2041
2188
  $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
2042
- $tip.find('.popover-content').empty()[ // we use append for html objects to maintain js events
2189
+ $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
2043
2190
  this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
2044
2191
  ](content)
2045
2192
 
@@ -2079,12 +2226,18 @@
2079
2226
 
2080
2227
  function Plugin(option) {
2081
2228
  return this.each(function () {
2082
- var $this = $(this)
2083
- var data = $this.data('bs.popover')
2084
- var options = typeof option == 'object' && option
2229
+ var $this = $(this)
2230
+ var data = $this.data('bs.popover')
2231
+ var options = typeof option == 'object' && option
2232
+ var selector = options && options.selector
2085
2233
 
2086
2234
  if (!data && option == 'destroy') return
2087
- if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
2235
+ if (selector) {
2236
+ if (!data) $this.data('bs.popover', (data = {}))
2237
+ if (!data[selector]) data[selector] = new Popover(this, options)
2238
+ } else {
2239
+ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
2240
+ }
2088
2241
  if (typeof option == 'string') data[option]()
2089
2242
  })
2090
2243
  }