bootstrap-sass-backport 3.2.0.2 → 3.3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +8 -2
  4. data/CHANGELOG.md +14 -0
  5. data/README.md +35 -22
  6. data/Rakefile +3 -4
  7. data/assets/fonts/bootstrap/glyphicons-halflings-regular.svg +1 -1
  8. data/assets/javascripts/bootstrap.js +1232 -1019
  9. data/assets/javascripts/bootstrap/affix.js +43 -23
  10. data/assets/javascripts/bootstrap/alert.js +6 -4
  11. data/assets/javascripts/bootstrap/button.js +16 -10
  12. data/assets/javascripts/bootstrap/carousel.js +27 -10
  13. data/assets/javascripts/bootstrap/collapse.js +69 -28
  14. data/assets/javascripts/bootstrap/dropdown.js +20 -10
  15. data/assets/javascripts/bootstrap/modal.js +67 -23
  16. data/assets/javascripts/bootstrap/popover.js +13 -7
  17. data/assets/javascripts/bootstrap/scrollspy.js +12 -7
  18. data/assets/javascripts/bootstrap/tab.js +41 -16
  19. data/assets/javascripts/bootstrap/tooltip.js +60 -39
  20. data/assets/javascripts/bootstrap/transition.js +1 -1
  21. data/assets/stylesheets/bootstrap/_badges.scss +7 -1
  22. data/assets/stylesheets/bootstrap/_button-groups.scss +18 -15
  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 +55 -43
  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 +6 -5
  32. data/assets/stylesheets/bootstrap/_list-group.scss +1 -8
  33. data/assets/stylesheets/bootstrap/_media.scss +27 -36
  34. data/assets/stylesheets/bootstrap/_modals.scss +4 -6
  35. data/assets/stylesheets/bootstrap/_navbar.scss +36 -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 +6 -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/_responsive-utilities.scss +4 -1
  46. data/assets/stylesheets/bootstrap/_scaffolding.scss +3 -3
  47. data/assets/stylesheets/bootstrap/_tables.scss +11 -10
  48. data/assets/stylesheets/bootstrap/_theme.scss +27 -13
  49. data/assets/stylesheets/bootstrap/_thumbnails.scss +2 -2
  50. data/assets/stylesheets/bootstrap/_tooltip.scss +12 -4
  51. data/assets/stylesheets/bootstrap/_type.scss +10 -16
  52. data/assets/stylesheets/bootstrap/_utilities.scss +3 -4
  53. data/assets/stylesheets/bootstrap/_variables.scss +31 -21
  54. data/assets/stylesheets/bootstrap/mixins/_buttons.scss +2 -0
  55. data/assets/stylesheets/bootstrap/mixins/_forms.scss +5 -1
  56. data/assets/stylesheets/bootstrap/mixins/_grid.scss +2 -2
  57. data/assets/stylesheets/bootstrap/mixins/_hide-text.scss +1 -1
  58. data/assets/stylesheets/bootstrap/mixins/_image.scss +0 -1
  59. data/assets/stylesheets/bootstrap/mixins/_labels.scss +1 -1
  60. data/assets/stylesheets/bootstrap/mixins/_progress-bar.scss +1 -1
  61. data/assets/stylesheets/bootstrap/mixins/_vendor-prefixes.scss +6 -3
  62. data/bower.json +1 -1
  63. data/composer.json +1 -1
  64. data/lib/bootstrap-sass-backport.rb +2 -1
  65. data/lib/bootstrap-sass/engine.rb +1 -1
  66. data/lib/bootstrap-sass/version.rb +2 -2
  67. data/package.json +4 -4
  68. data/tasks/converter/js_conversion.rb +2 -3
  69. data/tasks/converter/less_conversion.rb +64 -30
  70. data/tasks/converter/network.rb +12 -6
  71. data/templates/project/_bootstrap-variables.sass +866 -0
  72. data/templates/project/manifest.rb +5 -11
  73. data/test/node_sass_compile_test.sh +8 -0
  74. metadata +6 -27
  75. data/templates/project/_bootstrap-variables.sass.erb +0 -4
  76. data/test/dummy_rails/bin/bundle +0 -3
  77. data/test/dummy_rails/bin/rails +0 -4
  78. data/test/dummy_rails/bin/rake +0 -4
  79. data/test/dummy_rails/db/test.sqlite3 +0 -0
  80. data/test/dummy_rails/lib/assets/.keep +0 -0
  81. data/test/dummy_rails/public/404.html +0 -58
  82. data/test/dummy_rails/public/422.html +0 -58
  83. data/test/dummy_rails/public/500.html +0 -57
  84. data/test/dummy_rails/public/favicon.ico +0 -0
  85. data/test/node_sass_test.rb +0 -16
  86. data/test/support/integration_test.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90ea1065ae6d2fd33aeeb9819e3038dc2a57b0f3
4
- data.tar.gz: 7faa8f11248446aa1c0a68df588bd714b98a8aef
3
+ metadata.gz: 38534a478ab1186e87a931cf7d34d4c448dc6483
4
+ data.tar.gz: 8ac2011c6a42aad21fb87202d59dfbf9ba948ad7
5
5
  SHA512:
6
- metadata.gz: 959828544b9274510086cb3f74ef3c4fd711df71cd5fe7aace4735042518eb62f7b79fec76122e26aaac014bcf72b3850bd146cc28571d0cdc38477c4ddcef17
7
- data.tar.gz: 5c202cad05dec02ae455f1a0cf70a9dac4e02b74171ae1e1410135d27ad772601885490ff19417d14cde495c936e1881fe935456605c1a664776ceeb688ce8b2
6
+ metadata.gz: 482bfbde6f6aa2c6e0089e472900da0136ca4c59ef8f5d86a13db9c4542891eec6170e5c554ff90d201154f4bbbbfa18ffbb6db9d6cd7e6154f831b8a15d34a5
7
+ data.tar.gz: f7451edee2ca57a10f55ba37978cb893c1894c3ce03a68dec797bb22e258d609a5c0b3c407f076cf624e78a3cb140a9dbf1e76ab4abb6ebc4a6801b6ad9d647e
data/.gitignore CHANGED
@@ -14,6 +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
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,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.3.1.0
4
+
5
+ * Variables override template at templates/project/_bootstrap-variables.sass
6
+ * Readme: Bower + Rails configuration
7
+
8
+ ## 3.3.0.1
9
+
10
+ * Fix loading issue with the ruby gem version
11
+
12
+ ## 3.3.0
13
+
14
+ * Improve libsass compatibility
15
+ * Support using Bower package with Rails
16
+
3
17
  ## 3.2.0.2
4
18
 
5
19
  Main bootstrap file is now a partial (_bootstrap.scss), for compatibility with Compass 1+.
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.1'
21
21
  gem 'sass-rails', '>= 3.2'
22
22
  ```
23
23
 
@@ -33,6 +33,7 @@ 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
  ```
@@ -57,27 +58,42 @@ Require Bootstrap Javascripts in `app/assets/javascripts/application.js`:
57
58
 
58
59
  #### Bower with Rails
59
60
 
60
- 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, configure assets in `config/application.rb`:
61
62
 
62
63
  ```ruby
63
- # e.g. config/initializers/sass.rb
64
+ # Bower asset paths
65
+ root.join('vendor', 'assets', 'bower_components').to_s.tap do |bower_path|
66
+ config.sass.load_paths << bower_path
67
+ config.assets.paths << bower_path
68
+ end
69
+ # Precompile Bootstrap fonts
70
+ config.assets.precompile << %r(bootstrap-sass/assets/fonts/bootstrap/[\w-]+\.(?:eot|svg|ttf|woff)$)
71
+ # Minimum Sass number precision required by bootstrap-sass
64
72
  ::Sass::Script::Number.precision = [10, ::Sass::Script::Number.precision].max
65
73
  ```
66
74
 
67
- #### Rails 3.2.x
75
+ Replace Bootstrap `@import` statements in `application.css.scss` with:
68
76
 
69
- Rails 3.2 is [no longer maintained for bugfixes](http://guides.rubyonrails.org/maintenance_policy.html), and you should upgrade as soon as possible.
77
+ ```scss
78
+ $icon-font-path: "bootstrap-sass/assets/fonts/bootstrap/";
79
+ @import "bootstrap-sass/assets/stylesheets/bootstrap-sprockets";
80
+ @import "bootstrap-sass/assets/stylesheets/bootstrap";
81
+ ```
70
82
 
71
- Starting with bootstrap-sass v3.1.1.1, due to the structural changes from upstream you will need these
72
- backported asset pipeline gems on Rails 3.2. There is more on why this is necessary in
73
- https://github.com/twbs/bootstrap-sass/issues/523 and https://github.com/twbs/bootstrap-sass/issues/578.
83
+ Replace Bootstrap `require` directive in `application.js` with:
74
84
 
75
- ```ruby
76
- gem 'sprockets-rails', '=2.0.0.backport1'
77
- gem 'sprockets', '=2.2.2.backport2'
78
- gem 'sass-rails', github: 'guilleiguaran/sass-rails', branch: 'backport'
85
+ ```js
86
+ //= require bootstrap-sass/assets/javascripts/bootstrap-sprockets
79
87
  ```
80
88
 
89
+ #### Rails 4.x
90
+
91
+ Please make sure `sprockets-rails` is at least v2.1.4.
92
+
93
+ #### Rails 3.2.x
94
+
95
+ bootstrap-sass is no longer compatible with Rails 3. The latest version of bootstrap-sass compatible with Rails 3.2 is v3.1.1.0.
96
+
81
97
  ### b. Compass without Rails
82
98
 
83
99
  Install the gem
@@ -111,14 +127,14 @@ $ compass create my-new-project -r bootstrap-sass --using bootstrap
111
127
  This will create a new Compass project with the following files in it:
112
128
 
113
129
  * [styles.sass](/templates/project/styles.sass) - main project Sass file, imports Bootstrap and variables.
114
- * [_bootstrap-variables.sass](/templates/project/_bootstrap-variables.sass.erb) - all of Bootstrap variables, override them here.
130
+ * [_bootstrap-variables.sass](/templates/project/_bootstrap-variables.sass) - all of Bootstrap variables, override them here.
115
131
 
116
132
  Some bootstrap-sass mixins may conflict with the Compass ones.
117
133
  If this happens, change the import order so that Compass mixins are loaded later.
118
134
 
119
135
  ### c. Bower
120
136
 
121
- Using bootstrap-sass as a Bower package is still being tested. It is compatible with node-sass 0.8.3+. You can install it with:
137
+ bootstrap-sass Bower package is compatible with node-sass 1.2.3+. You can install it with:
122
138
 
123
139
  ```console
124
140
  $ bower install bootstrap-sass-official
@@ -128,14 +144,14 @@ $ bower install bootstrap-sass-official
128
144
 
129
145
  Sass, JS, and all other assets are located at [assets](/assets).
130
146
 
131
- By default, `bower.json` main field list only the main `bootstrap.scss` and all the static assets (fonts and JS).
147
+ By default, `bower.json` main field list only the main `_bootstrap.scss` and all the static assets (fonts and JS).
132
148
  This is compatible by default with asset managers such as [wiredep](https://github.com/taptapship/wiredep).
133
149
 
134
150
  #### Node.js Mincer
135
151
 
136
- If you use [mincer][mincer] with node-sass, import bootstrap into like so:
152
+ If you use [mincer][mincer] with node-sass, import bootstrap like so:
137
153
 
138
- In `application.css.ejs.scss` (NB **.css.ejs.css**):
154
+ In `application.css.ejs.scss` (NB **.css.ejs.scss**):
139
155
 
140
156
  ```scss
141
157
  // Import mincer asset paths helper integration
@@ -159,8 +175,8 @@ See also this [example manifest.js](/test/dummy_node_mincer/manifest.js) for min
159
175
  By default all of Bootstrap is imported.
160
176
 
161
177
  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`.
178
+ [`_bootstrap.scss`](assets/stylesheets/_bootstrap.scss) file into your assets as `_bootstrap-custom.scss`.
179
+ Then comment out components you do not want from `_bootstrap-custom`.
164
180
  In the application Sass file, replace `@import 'bootstrap'` with:
165
181
 
166
182
  ```scss
@@ -178,9 +194,6 @@ When using ruby Sass compiler standalone or with the Bower version you can set i
178
194
  ::Sass::Script::Number.precision = [10, ::Sass::Script::Number.precision].max
179
195
  ```
180
196
 
181
- Note that libsass and node-sass do not currently support the precision option, due to an open bug ([bug #364](https://github.com/sass/libsass/issues/364)) in libsass.
182
-
183
-
184
197
  #### Sass: Autoprefixer
185
198
 
186
199
  Using [Autoprefixer][autoprefixer] with Bootstrap is recommended.
data/Rakefile CHANGED
@@ -5,10 +5,9 @@ require 'bundler/gem_tasks'
5
5
  load './tasks/bower.rake'
6
6
 
7
7
  require 'rake/testtask'
8
- Rake::TestTask.new do |t|
9
- t.libs << "test"
10
- t.test_files = FileList['test/*_test.rb']
11
- t.verbose = true
8
+ task :test do |t|
9
+ $: << File.expand_path('test/')
10
+ Dir.glob('./test/**/*_test.rb').each { |file| require file }
12
11
  end
13
12
 
14
13
  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,148 +1,82 @@
1
- /* ========================================================================
2
- * Bootstrap: affix.js v3.2.0
3
- * http://getbootstrap.com/javascript/#affix
4
- * ========================================================================
1
+ /*!
2
+ * Bootstrap v3.3.1 (http://getbootstrap.com)
5
3
  * Copyright 2011-2014 Twitter, Inc.
6
4
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
7
- * ======================================================================== */
5
+ */
8
6
 
7
+ if (typeof jQuery === 'undefined') {
8
+ throw new Error('Bootstrap\'s JavaScript requires jQuery')
9
+ }
9
10
 
10
11
  +function ($) {
11
- 'use strict';
12
-
13
- // AFFIX CLASS DEFINITION
14
- // ======================
15
-
16
- var Affix = function (element, options) {
17
- this.options = $.extend({}, Affix.DEFAULTS, options)
18
-
19
- this.$target = $(this.options.target)
20
- .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
21
- .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
22
-
23
- this.$element = $(element)
24
- this.affixed =
25
- this.unpin =
26
- this.pinnedOffset = null
27
-
28
- this.checkPosition()
29
- }
30
-
31
- Affix.VERSION = '3.2.0'
32
-
33
- Affix.RESET = 'affix affix-top affix-bottom'
34
-
35
- Affix.DEFAULTS = {
36
- offset: 0,
37
- target: window
38
- }
39
-
40
- Affix.prototype.getPinnedOffset = function () {
41
- if (this.pinnedOffset) return this.pinnedOffset
42
- this.$element.removeClass(Affix.RESET).addClass('affix')
43
- var scrollTop = this.$target.scrollTop()
44
- var position = this.$element.offset()
45
- return (this.pinnedOffset = position.top - scrollTop)
46
- }
47
-
48
- Affix.prototype.checkPositionWithEventLoop = function () {
49
- setTimeout($.proxy(this.checkPosition, this), 1)
12
+ var version = $.fn.jquery.split(' ')[0].split('.')
13
+ if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) {
14
+ throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher')
50
15
  }
16
+ }(jQuery);
51
17
 
52
- Affix.prototype.checkPosition = function () {
53
- if (!this.$element.is(':visible')) return
54
-
55
- var scrollHeight = $(document).height()
56
- var scrollTop = this.$target.scrollTop()
57
- var position = this.$element.offset()
58
- var offset = this.options.offset
59
- var offsetTop = offset.top
60
- var offsetBottom = offset.bottom
61
-
62
- if (typeof offset != 'object') offsetBottom = offsetTop = offset
63
- if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
64
- if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
65
-
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
69
-
70
- if (this.affixed === affix) return
71
- if (this.unpin != null) this.$element.css('top', '')
72
-
73
- var affixType = 'affix' + (affix ? '-' + affix : '')
74
- var e = $.Event(affixType + '.bs.affix')
18
+ /* ========================================================================
19
+ * Bootstrap: transition.js v3.3.1
20
+ * http://getbootstrap.com/javascript/#transitions
21
+ * ========================================================================
22
+ * Copyright 2011-2014 Twitter, Inc.
23
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
24
+ * ======================================================================== */
75
25
 
76
- this.$element.trigger(e)
77
26
 
78
- if (e.isDefaultPrevented()) return
27
+ +function ($) {
28
+ 'use strict';
79
29
 
80
- this.affixed = affix
81
- this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
30
+ // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
31
+ // ============================================================
82
32
 
83
- this.$element
84
- .removeClass(Affix.RESET)
85
- .addClass(affixType)
86
- .trigger($.Event(affixType.replace('affix', 'affixed')))
33
+ function transitionEnd() {
34
+ var el = document.createElement('bootstrap')
87
35
 
88
- if (affix == 'bottom') {
89
- this.$element.offset({
90
- top: scrollHeight - this.$element.height() - offsetBottom
91
- })
36
+ var transEndEventNames = {
37
+ WebkitTransition : 'webkitTransitionEnd',
38
+ MozTransition : 'transitionend',
39
+ OTransition : 'oTransitionEnd otransitionend',
40
+ transition : 'transitionend'
92
41
  }
93
- }
94
-
95
-
96
- // AFFIX PLUGIN DEFINITION
97
- // =======================
98
42
 
99
- function Plugin(option) {
100
- return this.each(function () {
101
- var $this = $(this)
102
- var data = $this.data('bs.affix')
103
- var options = typeof option == 'object' && option
43
+ for (var name in transEndEventNames) {
44
+ if (el.style[name] !== undefined) {
45
+ return { end: transEndEventNames[name] }
46
+ }
47
+ }
104
48
 
105
- if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
106
- if (typeof option == 'string') data[option]()
107
- })
49
+ return false // explicit for ie8 ( ._.)
108
50
  }
109
51
 
110
- var old = $.fn.affix
111
-
112
- $.fn.affix = Plugin
113
- $.fn.affix.Constructor = Affix
114
-
115
-
116
- // AFFIX NO CONFLICT
117
- // =================
118
-
119
- $.fn.affix.noConflict = function () {
120
- $.fn.affix = old
52
+ // http://blog.alexmaccaw.com/css-transitions
53
+ $.fn.emulateTransitionEnd = function (duration) {
54
+ var called = false
55
+ var $el = this
56
+ $(this).one('bsTransitionEnd', function () { called = true })
57
+ var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
58
+ setTimeout(callback, duration)
121
59
  return this
122
60
  }
123
61
 
62
+ $(function () {
63
+ $.support.transition = transitionEnd()
124
64
 
125
- // AFFIX DATA-API
126
- // ==============
127
-
128
- $(window).on('load', function () {
129
- $('[data-spy="affix"]').each(function () {
130
- var $spy = $(this)
131
- var data = $spy.data()
132
-
133
- data.offset = data.offset || {}
134
-
135
- if (data.offsetBottom) data.offset.bottom = data.offsetBottom
136
- if (data.offsetTop) data.offset.top = data.offsetTop
65
+ if (!$.support.transition) return
137
66
 
138
- Plugin.call($spy, data)
139
- })
67
+ $.event.special.bsTransitionEnd = {
68
+ bindType: $.support.transition.end,
69
+ delegateType: $.support.transition.end,
70
+ handle: function (e) {
71
+ if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
72
+ }
73
+ }
140
74
  })
141
75
 
142
76
  }(jQuery);
143
77
 
144
78
  /* ========================================================================
145
- * Bootstrap: alert.js v3.2.0
79
+ * Bootstrap: alert.js v3.3.1
146
80
  * http://getbootstrap.com/javascript/#alerts
147
81
  * ========================================================================
148
82
  * Copyright 2011-2014 Twitter, Inc.
@@ -161,7 +95,9 @@
161
95
  $(el).on('click', dismiss, this.close)
162
96
  }
163
97
 
164
- Alert.VERSION = '3.2.0'
98
+ Alert.VERSION = '3.3.1'
99
+
100
+ Alert.TRANSITION_DURATION = 150
165
101
 
166
102
  Alert.prototype.close = function (e) {
167
103
  var $this = $(this)
@@ -177,7 +113,7 @@
177
113
  if (e) e.preventDefault()
178
114
 
179
115
  if (!$parent.length) {
180
- $parent = $this.hasClass('alert') ? $this : $this.parent()
116
+ $parent = $this.closest('.alert')
181
117
  }
182
118
 
183
119
  $parent.trigger(e = $.Event('close.bs.alert'))
@@ -194,7 +130,7 @@
194
130
  $.support.transition && $parent.hasClass('fade') ?
195
131
  $parent
196
132
  .one('bsTransitionEnd', removeElement)
197
- .emulateTransitionEnd(150) :
133
+ .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
198
134
  removeElement()
199
135
  }
200
136
 
@@ -235,7 +171,7 @@
235
171
  }(jQuery);
236
172
 
237
173
  /* ========================================================================
238
- * Bootstrap: button.js v3.2.0
174
+ * Bootstrap: button.js v3.3.1
239
175
  * http://getbootstrap.com/javascript/#buttons
240
176
  * ========================================================================
241
177
  * Copyright 2011-2014 Twitter, Inc.
@@ -255,7 +191,7 @@
255
191
  this.isLoading = false
256
192
  }
257
193
 
258
- Button.VERSION = '3.2.0'
194
+ Button.VERSION = '3.3.1'
259
195
 
260
196
  Button.DEFAULTS = {
261
197
  loadingText: 'loading...'
@@ -271,10 +207,10 @@
271
207
 
272
208
  if (data.resetText == null) $el.data('resetText', $el[val]())
273
209
 
274
- $el[val](data[state] == null ? this.options[state] : data[state])
275
-
276
210
  // push to event loop to allow forms to submit
277
211
  setTimeout($.proxy(function () {
212
+ $el[val](data[state] == null ? this.options[state] : data[state])
213
+
278
214
  if (state == 'loadingText') {
279
215
  this.isLoading = true
280
216
  $el.addClass(d).attr(d, d)
@@ -296,6 +232,8 @@
296
232
  else $parent.find('.active').removeClass('active')
297
233
  }
298
234
  if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
235
+ } else {
236
+ this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
299
237
  }
300
238
 
301
239
  if (changed) this.$element.toggleClass('active')
@@ -336,17 +274,21 @@
336
274
  // BUTTON DATA-API
337
275
  // ===============
338
276
 
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
- })
277
+ $(document)
278
+ .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
279
+ var $btn = $(e.target)
280
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
281
+ Plugin.call($btn, 'toggle')
282
+ e.preventDefault()
283
+ })
284
+ .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
285
+ $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
286
+ })
345
287
 
346
288
  }(jQuery);
347
289
 
348
290
  /* ========================================================================
349
- * Bootstrap: carousel.js v3.2.0
291
+ * Bootstrap: carousel.js v3.3.1
350
292
  * http://getbootstrap.com/javascript/#carousel
351
293
  * ========================================================================
352
294
  * Copyright 2011-2014 Twitter, Inc.
@@ -361,7 +303,7 @@
361
303
  // =========================
362
304
 
363
305
  var Carousel = function (element, options) {
364
- this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this))
306
+ this.$element = $(element)
365
307
  this.$indicators = this.$element.find('.carousel-indicators')
366
308
  this.options = options
367
309
  this.paused =
@@ -370,20 +312,26 @@
370
312
  this.$active =
371
313
  this.$items = null
372
314
 
373
- this.options.pause == 'hover' && this.$element
315
+ this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
316
+
317
+ this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
374
318
  .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
375
319
  .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
376
320
  }
377
321
 
378
- Carousel.VERSION = '3.2.0'
322
+ Carousel.VERSION = '3.3.1'
323
+
324
+ Carousel.TRANSITION_DURATION = 600
379
325
 
380
326
  Carousel.DEFAULTS = {
381
327
  interval: 5000,
382
328
  pause: 'hover',
383
- wrap: true
329
+ wrap: true,
330
+ keyboard: true
384
331
  }
385
332
 
386
333
  Carousel.prototype.keydown = function (e) {
334
+ if (/input|textarea/i.test(e.target.tagName)) return
387
335
  switch (e.which) {
388
336
  case 37: this.prev(); break
389
337
  case 39: this.next(); break
@@ -410,6 +358,13 @@
410
358
  return this.$items.index(item || this.$active)
411
359
  }
412
360
 
361
+ Carousel.prototype.getItemForDirection = function (direction, active) {
362
+ var delta = direction == 'prev' ? -1 : 1
363
+ var activeIndex = this.getItemIndex(active)
364
+ var itemIndex = (activeIndex + delta) % this.$items.length
365
+ return this.$items.eq(itemIndex)
366
+ }
367
+
413
368
  Carousel.prototype.to = function (pos) {
414
369
  var that = this
415
370
  var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
@@ -419,7 +374,7 @@
419
374
  if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
420
375
  if (activeIndex == pos) return this.pause().cycle()
421
376
 
422
- return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
377
+ return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
423
378
  }
424
379
 
425
380
  Carousel.prototype.pause = function (e) {
@@ -447,7 +402,7 @@
447
402
 
448
403
  Carousel.prototype.slide = function (type, next) {
449
404
  var $active = this.$element.find('.item.active')
450
- var $next = next || $active[type]()
405
+ var $next = next || this.getItemForDirection(type, $active)
451
406
  var isCycling = this.interval
452
407
  var direction = type == 'next' ? 'left' : 'right'
453
408
  var fallback = type == 'next' ? 'first' : 'last'
@@ -493,7 +448,7 @@
493
448
  that.$element.trigger(slidEvent)
494
449
  }, 0)
495
450
  })
496
- .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000)
451
+ .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
497
452
  } else {
498
453
  $active.removeClass('active')
499
454
  $next.addClass('active')
@@ -542,7 +497,7 @@
542
497
  // CAROUSEL DATA-API
543
498
  // =================
544
499
 
545
- $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
500
+ var clickHandler = function (e) {
546
501
  var href
547
502
  var $this = $(this)
548
503
  var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
@@ -558,7 +513,11 @@
558
513
  }
559
514
 
560
515
  e.preventDefault()
561
- })
516
+ }
517
+
518
+ $(document)
519
+ .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
520
+ .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
562
521
 
563
522
  $(window).on('load', function () {
564
523
  $('[data-ride="carousel"]').each(function () {
@@ -570,7 +529,7 @@
570
529
  }(jQuery);
571
530
 
572
531
  /* ========================================================================
573
- * Bootstrap: collapse.js v3.2.0
532
+ * Bootstrap: collapse.js v3.3.1
574
533
  * http://getbootstrap.com/javascript/#collapse
575
534
  * ========================================================================
576
535
  * Copyright 2011-2014 Twitter, Inc.
@@ -587,16 +546,25 @@
587
546
  var Collapse = function (element, options) {
588
547
  this.$element = $(element)
589
548
  this.options = $.extend({}, Collapse.DEFAULTS, options)
549
+ this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]')
590
550
  this.transitioning = null
591
551
 
592
- if (this.options.parent) this.$parent = $(this.options.parent)
552
+ if (this.options.parent) {
553
+ this.$parent = this.getParent()
554
+ } else {
555
+ this.addAriaAndCollapsedClass(this.$element, this.$trigger)
556
+ }
557
+
593
558
  if (this.options.toggle) this.toggle()
594
559
  }
595
560
 
596
- Collapse.VERSION = '3.2.0'
561
+ Collapse.VERSION = '3.3.1'
562
+
563
+ Collapse.TRANSITION_DURATION = 350
597
564
 
598
565
  Collapse.DEFAULTS = {
599
- toggle: true
566
+ toggle: true,
567
+ trigger: '[data-toggle="collapse"]'
600
568
  }
601
569
 
602
570
  Collapse.prototype.dimension = function () {
@@ -607,17 +575,21 @@
607
575
  Collapse.prototype.show = function () {
608
576
  if (this.transitioning || this.$element.hasClass('in')) return
609
577
 
578
+ var activesData
579
+ var actives = this.$parent && this.$parent.find('> .panel').children('.in, .collapsing')
580
+
581
+ if (actives && actives.length) {
582
+ activesData = actives.data('bs.collapse')
583
+ if (activesData && activesData.transitioning) return
584
+ }
585
+
610
586
  var startEvent = $.Event('show.bs.collapse')
611
587
  this.$element.trigger(startEvent)
612
588
  if (startEvent.isDefaultPrevented()) return
613
589
 
614
- var actives = this.$parent && this.$parent.find('> .panel > .in')
615
-
616
590
  if (actives && actives.length) {
617
- var hasData = actives.data('bs.collapse')
618
- if (hasData && hasData.transitioning) return
619
591
  Plugin.call(actives, 'hide')
620
- hasData || actives.data('bs.collapse', null)
592
+ activesData || actives.data('bs.collapse', null)
621
593
  }
622
594
 
623
595
  var dimension = this.dimension()
@@ -625,6 +597,11 @@
625
597
  this.$element
626
598
  .removeClass('collapse')
627
599
  .addClass('collapsing')[dimension](0)
600
+ .attr('aria-expanded', true)
601
+
602
+ this.$trigger
603
+ .removeClass('collapsed')
604
+ .attr('aria-expanded', true)
628
605
 
629
606
  this.transitioning = 1
630
607
 
@@ -643,7 +620,7 @@
643
620
 
644
621
  this.$element
645
622
  .one('bsTransitionEnd', $.proxy(complete, this))
646
- .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize])
623
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
647
624
  }
648
625
 
649
626
  Collapse.prototype.hide = function () {
@@ -659,17 +636,21 @@
659
636
 
660
637
  this.$element
661
638
  .addClass('collapsing')
662
- .removeClass('collapse')
663
- .removeClass('in')
639
+ .removeClass('collapse in')
640
+ .attr('aria-expanded', false)
641
+
642
+ this.$trigger
643
+ .addClass('collapsed')
644
+ .attr('aria-expanded', false)
664
645
 
665
646
  this.transitioning = 1
666
647
 
667
648
  var complete = function () {
668
649
  this.transitioning = 0
669
650
  this.$element
670
- .trigger('hidden.bs.collapse')
671
651
  .removeClass('collapsing')
672
652
  .addClass('collapse')
653
+ .trigger('hidden.bs.collapse')
673
654
  }
674
655
 
675
656
  if (!$.support.transition) return complete.call(this)
@@ -677,13 +658,40 @@
677
658
  this.$element
678
659
  [dimension](0)
679
660
  .one('bsTransitionEnd', $.proxy(complete, this))
680
- .emulateTransitionEnd(350)
661
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
681
662
  }
682
663
 
683
664
  Collapse.prototype.toggle = function () {
684
665
  this[this.$element.hasClass('in') ? 'hide' : 'show']()
685
666
  }
686
667
 
668
+ Collapse.prototype.getParent = function () {
669
+ return $(this.options.parent)
670
+ .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
671
+ .each($.proxy(function (i, element) {
672
+ var $element = $(element)
673
+ this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
674
+ }, this))
675
+ .end()
676
+ }
677
+
678
+ Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
679
+ var isOpen = $element.hasClass('in')
680
+
681
+ $element.attr('aria-expanded', isOpen)
682
+ $trigger
683
+ .toggleClass('collapsed', !isOpen)
684
+ .attr('aria-expanded', isOpen)
685
+ }
686
+
687
+ function getTargetFromTrigger($trigger) {
688
+ var href
689
+ var target = $trigger.attr('data-target')
690
+ || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
691
+
692
+ return $(target)
693
+ }
694
+
687
695
 
688
696
  // COLLAPSE PLUGIN DEFINITION
689
697
  // ==========================
@@ -694,7 +702,7 @@
694
702
  var data = $this.data('bs.collapse')
695
703
  var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
696
704
 
697
- if (!data && options.toggle && option == 'show') option = !option
705
+ if (!data && options.toggle && option == 'show') options.toggle = false
698
706
  if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
699
707
  if (typeof option == 'string') data[option]()
700
708
  })
@@ -719,21 +727,13 @@
719
727
  // =================
720
728
 
721
729
  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
722
- var href
723
730
  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
731
 
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
- }
732
+ if (!$this.attr('data-target')) e.preventDefault()
733
+
734
+ var $target = getTargetFromTrigger($this)
735
+ var data = $target.data('bs.collapse')
736
+ var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this })
737
737
 
738
738
  Plugin.call($target, option)
739
739
  })
@@ -741,7 +741,7 @@
741
741
  }(jQuery);
742
742
 
743
743
  /* ========================================================================
744
- * Bootstrap: dropdown.js v3.2.0
744
+ * Bootstrap: dropdown.js v3.3.1
745
745
  * http://getbootstrap.com/javascript/#dropdowns
746
746
  * ========================================================================
747
747
  * Copyright 2011-2014 Twitter, Inc.
@@ -761,7 +761,7 @@
761
761
  $(element).on('click.bs.dropdown', this.toggle)
762
762
  }
763
763
 
764
- Dropdown.VERSION = '3.2.0'
764
+ Dropdown.VERSION = '3.3.1'
765
765
 
766
766
  Dropdown.prototype.toggle = function (e) {
767
767
  var $this = $(this)
@@ -784,7 +784,9 @@
784
784
 
785
785
  if (e.isDefaultPrevented()) return
786
786
 
787
- $this.trigger('focus')
787
+ $this
788
+ .trigger('focus')
789
+ .attr('aria-expanded', 'true')
788
790
 
789
791
  $parent
790
792
  .toggleClass('open')
@@ -795,7 +797,7 @@
795
797
  }
796
798
 
797
799
  Dropdown.prototype.keydown = function (e) {
798
- if (!/(38|40|27)/.test(e.keyCode)) return
800
+ if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
799
801
 
800
802
  var $this = $(this)
801
803
 
@@ -807,7 +809,7 @@
807
809
  var $parent = getParent($this)
808
810
  var isActive = $parent.hasClass('open')
809
811
 
810
- if (!isActive || (isActive && e.keyCode == 27)) {
812
+ if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
811
813
  if (e.which == 27) $parent.find(toggle).trigger('focus')
812
814
  return $this.trigger('click')
813
815
  }
@@ -817,10 +819,10 @@
817
819
 
818
820
  if (!$items.length) return
819
821
 
820
- var index = $items.index($items.filter(':focus'))
822
+ var index = $items.index(e.target)
821
823
 
822
- if (e.keyCode == 38 && index > 0) index-- // up
823
- if (e.keyCode == 40 && index < $items.length - 1) index++ // down
824
+ if (e.which == 38 && index > 0) index-- // up
825
+ if (e.which == 40 && index < $items.length - 1) index++ // down
824
826
  if (!~index) index = 0
825
827
 
826
828
  $items.eq(index).trigger('focus')
@@ -830,11 +832,17 @@
830
832
  if (e && e.which === 3) return
831
833
  $(backdrop).remove()
832
834
  $(toggle).each(function () {
833
- var $parent = getParent($(this))
835
+ var $this = $(this)
836
+ var $parent = getParent($this)
834
837
  var relatedTarget = { relatedTarget: this }
838
+
835
839
  if (!$parent.hasClass('open')) return
840
+
836
841
  $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
842
+
837
843
  if (e.isDefaultPrevented()) return
844
+
845
+ $this.attr('aria-expanded', 'false')
838
846
  $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
839
847
  })
840
848
  }
@@ -888,13 +896,15 @@
888
896
  .on('click.bs.dropdown.data-api', clearMenus)
889
897
  .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
890
898
  .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)
899
+ .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
900
+ .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)
901
+ .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)
892
902
 
893
903
  }(jQuery);
894
904
 
895
905
  /* ========================================================================
896
- * Bootstrap: tab.js v3.2.0
897
- * http://getbootstrap.com/javascript/#tabs
906
+ * Bootstrap: modal.js v3.3.1
907
+ * http://getbootstrap.com/javascript/#modals
898
908
  * ========================================================================
899
909
  * Copyright 2011-2014 Twitter, Inc.
900
910
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
@@ -904,357 +914,323 @@
904
914
  +function ($) {
905
915
  'use strict';
906
916
 
907
- // TAB CLASS DEFINITION
908
- // ====================
917
+ // MODAL CLASS DEFINITION
918
+ // ======================
909
919
 
910
- var Tab = function (element) {
911
- this.element = $(element)
920
+ var Modal = function (element, options) {
921
+ this.options = options
922
+ this.$body = $(document.body)
923
+ this.$element = $(element)
924
+ this.$backdrop =
925
+ this.isShown = null
926
+ this.scrollbarWidth = 0
927
+
928
+ if (this.options.remote) {
929
+ this.$element
930
+ .find('.modal-content')
931
+ .load(this.options.remote, $.proxy(function () {
932
+ this.$element.trigger('loaded.bs.modal')
933
+ }, this))
934
+ }
912
935
  }
913
936
 
914
- Tab.VERSION = '3.2.0'
937
+ Modal.VERSION = '3.3.1'
915
938
 
916
- Tab.prototype.show = function () {
917
- var $this = this.element
918
- var $ul = $this.closest('ul:not(.dropdown-menu)')
919
- var selector = $this.data('target')
939
+ Modal.TRANSITION_DURATION = 300
940
+ Modal.BACKDROP_TRANSITION_DURATION = 150
920
941
 
921
- if (!selector) {
922
- selector = $this.attr('href')
923
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
924
- }
942
+ Modal.DEFAULTS = {
943
+ backdrop: true,
944
+ keyboard: true,
945
+ show: true
946
+ }
925
947
 
926
- if ($this.parent('li').hasClass('active')) return
948
+ Modal.prototype.toggle = function (_relatedTarget) {
949
+ return this.isShown ? this.hide() : this.show(_relatedTarget)
950
+ }
927
951
 
928
- var previous = $ul.find('.active:last a')[0]
929
- var e = $.Event('show.bs.tab', {
930
- relatedTarget: previous
931
- })
952
+ Modal.prototype.show = function (_relatedTarget) {
953
+ var that = this
954
+ var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
932
955
 
933
- $this.trigger(e)
956
+ this.$element.trigger(e)
934
957
 
935
- if (e.isDefaultPrevented()) return
958
+ if (this.isShown || e.isDefaultPrevented()) return
936
959
 
937
- var $target = $(selector)
960
+ this.isShown = true
938
961
 
939
- this.activate($this.closest('li'), $ul)
940
- this.activate($target, $target.parent(), function () {
941
- $this.trigger({
942
- type: 'shown.bs.tab',
943
- relatedTarget: previous
944
- })
945
- })
946
- }
962
+ this.checkScrollbar()
963
+ this.setScrollbar()
964
+ this.$body.addClass('modal-open')
947
965
 
948
- Tab.prototype.activate = function (element, container, callback) {
949
- var $active = container.find('> .active')
950
- var transition = callback
951
- && $.support.transition
952
- && $active.hasClass('fade')
966
+ this.escape()
967
+ this.resize()
953
968
 
954
- function next() {
955
- $active
956
- .removeClass('active')
957
- .find('> .dropdown-menu > .active')
958
- .removeClass('active')
959
-
960
- element.addClass('active')
969
+ this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
961
970
 
962
- if (transition) {
963
- element[0].offsetWidth // reflow for transition
964
- element.addClass('in')
965
- } else {
966
- element.removeClass('fade')
967
- }
971
+ this.backdrop(function () {
972
+ var transition = $.support.transition && that.$element.hasClass('fade')
968
973
 
969
- if (element.parent('.dropdown-menu')) {
970
- element.closest('li.dropdown').addClass('active')
974
+ if (!that.$element.parent().length) {
975
+ that.$element.appendTo(that.$body) // don't move modals dom position
971
976
  }
972
977
 
973
- callback && callback()
974
- }
978
+ that.$element
979
+ .show()
980
+ .scrollTop(0)
975
981
 
976
- transition ?
977
- $active
978
- .one('bsTransitionEnd', next)
979
- .emulateTransitionEnd(150) :
980
- next()
982
+ if (that.options.backdrop) that.adjustBackdrop()
983
+ that.adjustDialog()
981
984
 
982
- $active.removeClass('in')
983
- }
985
+ if (transition) {
986
+ that.$element[0].offsetWidth // force reflow
987
+ }
984
988
 
989
+ that.$element
990
+ .addClass('in')
991
+ .attr('aria-hidden', false)
985
992
 
986
- // TAB PLUGIN DEFINITION
987
- // =====================
993
+ that.enforceFocus()
988
994
 
989
- function Plugin(option) {
990
- return this.each(function () {
991
- var $this = $(this)
992
- var data = $this.data('bs.tab')
995
+ var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
993
996
 
994
- if (!data) $this.data('bs.tab', (data = new Tab(this)))
995
- if (typeof option == 'string') data[option]()
997
+ transition ?
998
+ that.$element.find('.modal-dialog') // wait for modal to slide in
999
+ .one('bsTransitionEnd', function () {
1000
+ that.$element.trigger('focus').trigger(e)
1001
+ })
1002
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
1003
+ that.$element.trigger('focus').trigger(e)
996
1004
  })
997
1005
  }
998
1006
 
999
- var old = $.fn.tab
1000
-
1001
- $.fn.tab = Plugin
1002
- $.fn.tab.Constructor = Tab
1003
-
1004
-
1005
- // TAB NO CONFLICT
1006
- // ===============
1007
-
1008
- $.fn.tab.noConflict = function () {
1009
- $.fn.tab = old
1010
- return this
1011
- }
1007
+ Modal.prototype.hide = function (e) {
1008
+ if (e) e.preventDefault()
1012
1009
 
1010
+ e = $.Event('hide.bs.modal')
1013
1011
 
1014
- // TAB DATA-API
1015
- // ============
1012
+ this.$element.trigger(e)
1016
1013
 
1017
- $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
1018
- e.preventDefault()
1019
- Plugin.call($(this), 'show')
1020
- })
1014
+ if (!this.isShown || e.isDefaultPrevented()) return
1021
1015
 
1022
- }(jQuery);
1016
+ this.isShown = false
1023
1017
 
1024
- /* ========================================================================
1025
- * Bootstrap: transition.js v3.2.0
1026
- * http://getbootstrap.com/javascript/#transitions
1027
- * ========================================================================
1028
- * Copyright 2011-2014 Twitter, Inc.
1029
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1030
- * ======================================================================== */
1018
+ this.escape()
1019
+ this.resize()
1031
1020
 
1021
+ $(document).off('focusin.bs.modal')
1032
1022
 
1033
- +function ($) {
1034
- 'use strict';
1023
+ this.$element
1024
+ .removeClass('in')
1025
+ .attr('aria-hidden', true)
1026
+ .off('click.dismiss.bs.modal')
1035
1027
 
1036
- // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
1037
- // ============================================================
1028
+ $.support.transition && this.$element.hasClass('fade') ?
1029
+ this.$element
1030
+ .one('bsTransitionEnd', $.proxy(this.hideModal, this))
1031
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
1032
+ this.hideModal()
1033
+ }
1038
1034
 
1039
- function transitionEnd() {
1040
- var el = document.createElement('bootstrap')
1035
+ Modal.prototype.enforceFocus = function () {
1036
+ $(document)
1037
+ .off('focusin.bs.modal') // guard against infinite focus loop
1038
+ .on('focusin.bs.modal', $.proxy(function (e) {
1039
+ if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
1040
+ this.$element.trigger('focus')
1041
+ }
1042
+ }, this))
1043
+ }
1041
1044
 
1042
- var transEndEventNames = {
1043
- WebkitTransition : 'webkitTransitionEnd',
1044
- MozTransition : 'transitionend',
1045
- OTransition : 'oTransitionEnd otransitionend',
1046
- transition : 'transitionend'
1045
+ Modal.prototype.escape = function () {
1046
+ if (this.isShown && this.options.keyboard) {
1047
+ this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
1048
+ e.which == 27 && this.hide()
1049
+ }, this))
1050
+ } else if (!this.isShown) {
1051
+ this.$element.off('keydown.dismiss.bs.modal')
1047
1052
  }
1053
+ }
1048
1054
 
1049
- for (var name in transEndEventNames) {
1050
- if (el.style[name] !== undefined) {
1051
- return { end: transEndEventNames[name] }
1052
- }
1055
+ Modal.prototype.resize = function () {
1056
+ if (this.isShown) {
1057
+ $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
1058
+ } else {
1059
+ $(window).off('resize.bs.modal')
1053
1060
  }
1054
-
1055
- return false // explicit for ie8 ( ._.)
1056
1061
  }
1057
1062
 
1058
- // http://blog.alexmaccaw.com/css-transitions
1059
- $.fn.emulateTransitionEnd = function (duration) {
1060
- var called = false
1061
- var $el = this
1062
- $(this).one('bsTransitionEnd', function () { called = true })
1063
- var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
1064
- setTimeout(callback, duration)
1065
- return this
1063
+ Modal.prototype.hideModal = function () {
1064
+ var that = this
1065
+ this.$element.hide()
1066
+ this.backdrop(function () {
1067
+ that.$body.removeClass('modal-open')
1068
+ that.resetAdjustments()
1069
+ that.resetScrollbar()
1070
+ that.$element.trigger('hidden.bs.modal')
1071
+ })
1066
1072
  }
1067
1073
 
1068
- $(function () {
1069
- $.support.transition = transitionEnd()
1074
+ Modal.prototype.removeBackdrop = function () {
1075
+ this.$backdrop && this.$backdrop.remove()
1076
+ this.$backdrop = null
1077
+ }
1070
1078
 
1071
- if (!$.support.transition) return
1079
+ Modal.prototype.backdrop = function (callback) {
1080
+ var that = this
1081
+ var animate = this.$element.hasClass('fade') ? 'fade' : ''
1072
1082
 
1073
- $.event.special.bsTransitionEnd = {
1074
- bindType: $.support.transition.end,
1075
- delegateType: $.support.transition.end,
1076
- handle: function (e) {
1077
- if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
1078
- }
1079
- }
1080
- })
1083
+ if (this.isShown && this.options.backdrop) {
1084
+ var doAnimate = $.support.transition && animate
1081
1085
 
1082
- }(jQuery);
1086
+ this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
1087
+ .prependTo(this.$element)
1088
+ .on('click.dismiss.bs.modal', $.proxy(function (e) {
1089
+ if (e.target !== e.currentTarget) return
1090
+ this.options.backdrop == 'static'
1091
+ ? this.$element[0].focus.call(this.$element[0])
1092
+ : this.hide.call(this)
1093
+ }, this))
1083
1094
 
1084
- /* ========================================================================
1085
- * Bootstrap: scrollspy.js v3.2.0
1086
- * http://getbootstrap.com/javascript/#scrollspy
1087
- * ========================================================================
1088
- * Copyright 2011-2014 Twitter, Inc.
1089
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1090
- * ======================================================================== */
1095
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
1091
1096
 
1097
+ this.$backdrop.addClass('in')
1092
1098
 
1093
- +function ($) {
1094
- 'use strict';
1099
+ if (!callback) return
1095
1100
 
1096
- // SCROLLSPY CLASS DEFINITION
1097
- // ==========================
1101
+ doAnimate ?
1102
+ this.$backdrop
1103
+ .one('bsTransitionEnd', callback)
1104
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
1105
+ callback()
1098
1106
 
1099
- function ScrollSpy(element, options) {
1100
- var process = $.proxy(this.process, this)
1107
+ } else if (!this.isShown && this.$backdrop) {
1108
+ this.$backdrop.removeClass('in')
1101
1109
 
1102
- this.$body = $('body')
1103
- this.$scrollElement = $(element).is('body') ? $(window) : $(element)
1104
- this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
1105
- this.selector = (this.options.target || '') + ' .nav li > a'
1106
- this.offsets = []
1107
- this.targets = []
1108
- this.activeTarget = null
1109
- this.scrollHeight = 0
1110
+ var callbackRemove = function () {
1111
+ that.removeBackdrop()
1112
+ callback && callback()
1113
+ }
1114
+ $.support.transition && this.$element.hasClass('fade') ?
1115
+ this.$backdrop
1116
+ .one('bsTransitionEnd', callbackRemove)
1117
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
1118
+ callbackRemove()
1110
1119
 
1111
- this.$scrollElement.on('scroll.bs.scrollspy', process)
1112
- this.refresh()
1113
- this.process()
1120
+ } else if (callback) {
1121
+ callback()
1122
+ }
1114
1123
  }
1115
1124
 
1116
- ScrollSpy.VERSION = '3.2.0'
1125
+ // these following methods are used to handle overflowing modals
1117
1126
 
1118
- ScrollSpy.DEFAULTS = {
1119
- offset: 10
1127
+ Modal.prototype.handleUpdate = function () {
1128
+ if (this.options.backdrop) this.adjustBackdrop()
1129
+ this.adjustDialog()
1120
1130
  }
1121
1131
 
1122
- ScrollSpy.prototype.getScrollHeight = function () {
1123
- return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
1132
+ Modal.prototype.adjustBackdrop = function () {
1133
+ this.$backdrop
1134
+ .css('height', 0)
1135
+ .css('height', this.$element[0].scrollHeight)
1124
1136
  }
1125
1137
 
1126
- ScrollSpy.prototype.refresh = function () {
1127
- var offsetMethod = 'offset'
1128
- var offsetBase = 0
1129
-
1130
- if (!$.isWindow(this.$scrollElement[0])) {
1131
- offsetMethod = 'position'
1132
- offsetBase = this.$scrollElement.scrollTop()
1133
- }
1138
+ Modal.prototype.adjustDialog = function () {
1139
+ var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
1134
1140
 
1135
- this.offsets = []
1136
- this.targets = []
1137
- this.scrollHeight = this.getScrollHeight()
1141
+ this.$element.css({
1142
+ paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
1143
+ paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
1144
+ })
1145
+ }
1138
1146
 
1139
- var self = this
1147
+ Modal.prototype.resetAdjustments = function () {
1148
+ this.$element.css({
1149
+ paddingLeft: '',
1150
+ paddingRight: ''
1151
+ })
1152
+ }
1140
1153
 
1141
- this.$body
1142
- .find(this.selector)
1143
- .map(function () {
1144
- var $el = $(this)
1145
- var href = $el.data('target') || $el.attr('href')
1146
- var $href = /^#./.test(href) && $(href)
1154
+ Modal.prototype.checkScrollbar = function () {
1155
+ this.bodyIsOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
1156
+ this.scrollbarWidth = this.measureScrollbar()
1157
+ }
1147
1158
 
1148
- return ($href
1149
- && $href.length
1150
- && $href.is(':visible')
1151
- && [[$href[offsetMethod]().top + offsetBase, href]]) || null
1152
- })
1153
- .sort(function (a, b) { return a[0] - b[0] })
1154
- .each(function () {
1155
- self.offsets.push(this[0])
1156
- self.targets.push(this[1])
1157
- })
1159
+ Modal.prototype.setScrollbar = function () {
1160
+ var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
1161
+ if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
1158
1162
  }
1159
1163
 
1160
- ScrollSpy.prototype.process = function () {
1161
- var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
1162
- var scrollHeight = this.getScrollHeight()
1163
- var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
1164
- var offsets = this.offsets
1165
- var targets = this.targets
1166
- var activeTarget = this.activeTarget
1167
- var i
1164
+ Modal.prototype.resetScrollbar = function () {
1165
+ this.$body.css('padding-right', '')
1166
+ }
1168
1167
 
1169
- if (this.scrollHeight != scrollHeight) {
1170
- this.refresh()
1171
- }
1172
-
1173
- if (scrollTop >= maxScroll) {
1174
- return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
1175
- }
1176
-
1177
- if (activeTarget && scrollTop <= offsets[0]) {
1178
- return activeTarget != (i = targets[0]) && this.activate(i)
1179
- }
1180
-
1181
- for (i = offsets.length; i--;) {
1182
- activeTarget != targets[i]
1183
- && scrollTop >= offsets[i]
1184
- && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
1185
- && this.activate(targets[i])
1186
- }
1187
- }
1188
-
1189
- ScrollSpy.prototype.activate = function (target) {
1190
- this.activeTarget = target
1191
-
1192
- $(this.selector)
1193
- .parentsUntil(this.options.target, '.active')
1194
- .removeClass('active')
1195
-
1196
- var selector = this.selector +
1197
- '[data-target="' + target + '"],' +
1198
- this.selector + '[href="' + target + '"]'
1199
-
1200
- var active = $(selector)
1201
- .parents('li')
1202
- .addClass('active')
1203
-
1204
- if (active.parent('.dropdown-menu').length) {
1205
- active = active
1206
- .closest('li.dropdown')
1207
- .addClass('active')
1208
- }
1209
-
1210
- active.trigger('activate.bs.scrollspy')
1168
+ Modal.prototype.measureScrollbar = function () { // thx walsh
1169
+ var scrollDiv = document.createElement('div')
1170
+ scrollDiv.className = 'modal-scrollbar-measure'
1171
+ this.$body.append(scrollDiv)
1172
+ var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
1173
+ this.$body[0].removeChild(scrollDiv)
1174
+ return scrollbarWidth
1211
1175
  }
1212
1176
 
1213
1177
 
1214
- // SCROLLSPY PLUGIN DEFINITION
1215
- // ===========================
1178
+ // MODAL PLUGIN DEFINITION
1179
+ // =======================
1216
1180
 
1217
- function Plugin(option) {
1181
+ function Plugin(option, _relatedTarget) {
1218
1182
  return this.each(function () {
1219
1183
  var $this = $(this)
1220
- var data = $this.data('bs.scrollspy')
1221
- var options = typeof option == 'object' && option
1184
+ var data = $this.data('bs.modal')
1185
+ var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
1222
1186
 
1223
- if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
1224
- if (typeof option == 'string') data[option]()
1187
+ if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
1188
+ if (typeof option == 'string') data[option](_relatedTarget)
1189
+ else if (options.show) data.show(_relatedTarget)
1225
1190
  })
1226
1191
  }
1227
1192
 
1228
- var old = $.fn.scrollspy
1193
+ var old = $.fn.modal
1229
1194
 
1230
- $.fn.scrollspy = Plugin
1231
- $.fn.scrollspy.Constructor = ScrollSpy
1195
+ $.fn.modal = Plugin
1196
+ $.fn.modal.Constructor = Modal
1232
1197
 
1233
1198
 
1234
- // SCROLLSPY NO CONFLICT
1235
- // =====================
1199
+ // MODAL NO CONFLICT
1200
+ // =================
1236
1201
 
1237
- $.fn.scrollspy.noConflict = function () {
1238
- $.fn.scrollspy = old
1202
+ $.fn.modal.noConflict = function () {
1203
+ $.fn.modal = old
1239
1204
  return this
1240
1205
  }
1241
1206
 
1242
1207
 
1243
- // SCROLLSPY DATA-API
1244
- // ==================
1208
+ // MODAL DATA-API
1209
+ // ==============
1245
1210
 
1246
- $(window).on('load.bs.scrollspy.data-api', function () {
1247
- $('[data-spy="scroll"]').each(function () {
1248
- var $spy = $(this)
1249
- Plugin.call($spy, $spy.data())
1211
+ $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
1212
+ var $this = $(this)
1213
+ var href = $this.attr('href')
1214
+ var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
1215
+ var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
1216
+
1217
+ if ($this.is('a')) e.preventDefault()
1218
+
1219
+ $target.one('show.bs.modal', function (showEvent) {
1220
+ if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
1221
+ $target.one('hidden.bs.modal', function () {
1222
+ $this.is(':visible') && $this.trigger('focus')
1223
+ })
1250
1224
  })
1225
+ Plugin.call($target, option, this)
1251
1226
  })
1252
1227
 
1253
1228
  }(jQuery);
1254
1229
 
1255
1230
  /* ========================================================================
1256
- * Bootstrap: modal.js v3.2.0
1257
- * http://getbootstrap.com/javascript/#modals
1231
+ * Bootstrap: tooltip.js v3.3.1
1232
+ * http://getbootstrap.com/javascript/#tooltip
1233
+ * Inspired by the original jQuery.tipsy by Jason Frame
1258
1234
  * ========================================================================
1259
1235
  * Copyright 2011-2014 Twitter, Inc.
1260
1236
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
@@ -1264,736 +1240,925 @@
1264
1240
  +function ($) {
1265
1241
  'use strict';
1266
1242
 
1267
- // MODAL CLASS DEFINITION
1268
- // ======================
1243
+ // TOOLTIP PUBLIC CLASS DEFINITION
1244
+ // ===============================
1269
1245
 
1270
- var Modal = function (element, options) {
1271
- this.options = options
1272
- this.$body = $(document.body)
1273
- this.$element = $(element)
1274
- this.$backdrop =
1275
- this.isShown = null
1276
- this.scrollbarWidth = 0
1246
+ var Tooltip = function (element, options) {
1247
+ this.type =
1248
+ this.options =
1249
+ this.enabled =
1250
+ this.timeout =
1251
+ this.hoverState =
1252
+ this.$element = null
1277
1253
 
1278
- if (this.options.remote) {
1279
- this.$element
1280
- .find('.modal-content')
1281
- .load(this.options.remote, $.proxy(function () {
1282
- this.$element.trigger('loaded.bs.modal')
1283
- }, this))
1284
- }
1254
+ this.init('tooltip', element, options)
1285
1255
  }
1286
1256
 
1287
- Modal.VERSION = '3.2.0'
1257
+ Tooltip.VERSION = '3.3.1'
1288
1258
 
1289
- Modal.DEFAULTS = {
1290
- backdrop: true,
1291
- keyboard: true,
1292
- show: true
1293
- }
1259
+ Tooltip.TRANSITION_DURATION = 150
1294
1260
 
1295
- Modal.prototype.toggle = function (_relatedTarget) {
1296
- return this.isShown ? this.hide() : this.show(_relatedTarget)
1261
+ Tooltip.DEFAULTS = {
1262
+ animation: true,
1263
+ placement: 'top',
1264
+ selector: false,
1265
+ template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
1266
+ trigger: 'hover focus',
1267
+ title: '',
1268
+ delay: 0,
1269
+ html: false,
1270
+ container: false,
1271
+ viewport: {
1272
+ selector: 'body',
1273
+ padding: 0
1274
+ }
1297
1275
  }
1298
1276
 
1299
- Modal.prototype.show = function (_relatedTarget) {
1300
- var that = this
1301
- var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
1277
+ Tooltip.prototype.init = function (type, element, options) {
1278
+ this.enabled = true
1279
+ this.type = type
1280
+ this.$element = $(element)
1281
+ this.options = this.getOptions(options)
1282
+ this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
1302
1283
 
1303
- this.$element.trigger(e)
1284
+ var triggers = this.options.trigger.split(' ')
1304
1285
 
1305
- if (this.isShown || e.isDefaultPrevented()) return
1286
+ for (var i = triggers.length; i--;) {
1287
+ var trigger = triggers[i]
1306
1288
 
1307
- this.isShown = true
1289
+ if (trigger == 'click') {
1290
+ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
1291
+ } else if (trigger != 'manual') {
1292
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
1293
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
1308
1294
 
1309
- this.checkScrollbar()
1310
- this.$body.addClass('modal-open')
1295
+ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
1296
+ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
1297
+ }
1298
+ }
1311
1299
 
1312
- this.setScrollbar()
1313
- this.escape()
1300
+ this.options.selector ?
1301
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
1302
+ this.fixTitle()
1303
+ }
1314
1304
 
1315
- this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
1305
+ Tooltip.prototype.getDefaults = function () {
1306
+ return Tooltip.DEFAULTS
1307
+ }
1316
1308
 
1317
- this.backdrop(function () {
1318
- var transition = $.support.transition && that.$element.hasClass('fade')
1309
+ Tooltip.prototype.getOptions = function (options) {
1310
+ options = $.extend({}, this.getDefaults(), this.$element.data(), options)
1319
1311
 
1320
- if (!that.$element.parent().length) {
1321
- that.$element.appendTo(that.$body) // don't move modals dom position
1312
+ if (options.delay && typeof options.delay == 'number') {
1313
+ options.delay = {
1314
+ show: options.delay,
1315
+ hide: options.delay
1322
1316
  }
1317
+ }
1323
1318
 
1324
- that.$element
1325
- .show()
1326
- .scrollTop(0)
1319
+ return options
1320
+ }
1327
1321
 
1328
- if (transition) {
1329
- that.$element[0].offsetWidth // force reflow
1330
- }
1322
+ Tooltip.prototype.getDelegateOptions = function () {
1323
+ var options = {}
1324
+ var defaults = this.getDefaults()
1331
1325
 
1332
- that.$element
1333
- .addClass('in')
1334
- .attr('aria-hidden', false)
1326
+ this._options && $.each(this._options, function (key, value) {
1327
+ if (defaults[key] != value) options[key] = value
1328
+ })
1335
1329
 
1336
- that.enforceFocus()
1330
+ return options
1331
+ }
1337
1332
 
1338
- var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
1333
+ Tooltip.prototype.enter = function (obj) {
1334
+ var self = obj instanceof this.constructor ?
1335
+ obj : $(obj.currentTarget).data('bs.' + this.type)
1339
1336
 
1340
- transition ?
1341
- that.$element.find('.modal-dialog') // wait for modal to slide in
1342
- .one('bsTransitionEnd', function () {
1343
- that.$element.trigger('focus').trigger(e)
1344
- })
1345
- .emulateTransitionEnd(300) :
1346
- that.$element.trigger('focus').trigger(e)
1347
- })
1348
- }
1337
+ if (self && self.$tip && self.$tip.is(':visible')) {
1338
+ self.hoverState = 'in'
1339
+ return
1340
+ }
1349
1341
 
1350
- Modal.prototype.hide = function (e) {
1351
- if (e) e.preventDefault()
1342
+ if (!self) {
1343
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
1344
+ $(obj.currentTarget).data('bs.' + this.type, self)
1345
+ }
1352
1346
 
1353
- e = $.Event('hide.bs.modal')
1347
+ clearTimeout(self.timeout)
1354
1348
 
1355
- this.$element.trigger(e)
1349
+ self.hoverState = 'in'
1356
1350
 
1357
- if (!this.isShown || e.isDefaultPrevented()) return
1351
+ if (!self.options.delay || !self.options.delay.show) return self.show()
1358
1352
 
1359
- this.isShown = false
1353
+ self.timeout = setTimeout(function () {
1354
+ if (self.hoverState == 'in') self.show()
1355
+ }, self.options.delay.show)
1356
+ }
1360
1357
 
1361
- this.$body.removeClass('modal-open')
1358
+ Tooltip.prototype.leave = function (obj) {
1359
+ var self = obj instanceof this.constructor ?
1360
+ obj : $(obj.currentTarget).data('bs.' + this.type)
1362
1361
 
1363
- this.resetScrollbar()
1364
- this.escape()
1362
+ if (!self) {
1363
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
1364
+ $(obj.currentTarget).data('bs.' + this.type, self)
1365
+ }
1365
1366
 
1366
- $(document).off('focusin.bs.modal')
1367
+ clearTimeout(self.timeout)
1367
1368
 
1368
- this.$element
1369
- .removeClass('in')
1370
- .attr('aria-hidden', true)
1371
- .off('click.dismiss.bs.modal')
1369
+ self.hoverState = 'out'
1372
1370
 
1373
- $.support.transition && this.$element.hasClass('fade') ?
1374
- this.$element
1375
- .one('bsTransitionEnd', $.proxy(this.hideModal, this))
1376
- .emulateTransitionEnd(300) :
1377
- this.hideModal()
1378
- }
1371
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
1379
1372
 
1380
- Modal.prototype.enforceFocus = function () {
1381
- $(document)
1382
- .off('focusin.bs.modal') // guard against infinite focus loop
1383
- .on('focusin.bs.modal', $.proxy(function (e) {
1384
- if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
1385
- this.$element.trigger('focus')
1386
- }
1387
- }, this))
1373
+ self.timeout = setTimeout(function () {
1374
+ if (self.hoverState == 'out') self.hide()
1375
+ }, self.options.delay.hide)
1388
1376
  }
1389
1377
 
1390
- Modal.prototype.escape = function () {
1391
- if (this.isShown && this.options.keyboard) {
1392
- this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
1393
- e.which == 27 && this.hide()
1394
- }, this))
1395
- } else if (!this.isShown) {
1396
- this.$element.off('keyup.dismiss.bs.modal')
1397
- }
1398
- }
1378
+ Tooltip.prototype.show = function () {
1379
+ var e = $.Event('show.bs.' + this.type)
1399
1380
 
1400
- Modal.prototype.hideModal = function () {
1401
- var that = this
1402
- this.$element.hide()
1403
- this.backdrop(function () {
1404
- that.$element.trigger('hidden.bs.modal')
1405
- })
1406
- }
1381
+ if (this.hasContent() && this.enabled) {
1382
+ this.$element.trigger(e)
1407
1383
 
1408
- Modal.prototype.removeBackdrop = function () {
1409
- this.$backdrop && this.$backdrop.remove()
1410
- this.$backdrop = null
1411
- }
1384
+ var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
1385
+ if (e.isDefaultPrevented() || !inDom) return
1386
+ var that = this
1412
1387
 
1413
- Modal.prototype.backdrop = function (callback) {
1414
- var that = this
1415
- var animate = this.$element.hasClass('fade') ? 'fade' : ''
1388
+ var $tip = this.tip()
1416
1389
 
1417
- if (this.isShown && this.options.backdrop) {
1418
- var doAnimate = $.support.transition && animate
1390
+ var tipId = this.getUID(this.type)
1419
1391
 
1420
- this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
1421
- .appendTo(this.$body)
1392
+ this.setContent()
1393
+ $tip.attr('id', tipId)
1394
+ this.$element.attr('aria-describedby', tipId)
1422
1395
 
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))
1396
+ if (this.options.animation) $tip.addClass('fade')
1429
1397
 
1430
- if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
1398
+ var placement = typeof this.options.placement == 'function' ?
1399
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
1400
+ this.options.placement
1431
1401
 
1432
- this.$backdrop.addClass('in')
1402
+ var autoToken = /\s?auto?\s?/i
1403
+ var autoPlace = autoToken.test(placement)
1404
+ if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
1433
1405
 
1434
- if (!callback) return
1406
+ $tip
1407
+ .detach()
1408
+ .css({ top: 0, left: 0, display: 'block' })
1409
+ .addClass(placement)
1410
+ .data('bs.' + this.type, this)
1435
1411
 
1436
- doAnimate ?
1437
- this.$backdrop
1438
- .one('bsTransitionEnd', callback)
1439
- .emulateTransitionEnd(150) :
1440
- callback()
1412
+ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
1441
1413
 
1442
- } else if (!this.isShown && this.$backdrop) {
1443
- this.$backdrop.removeClass('in')
1414
+ var pos = this.getPosition()
1415
+ var actualWidth = $tip[0].offsetWidth
1416
+ var actualHeight = $tip[0].offsetHeight
1444
1417
 
1445
- var callbackRemove = function () {
1446
- that.removeBackdrop()
1447
- callback && callback()
1418
+ if (autoPlace) {
1419
+ var orgPlacement = placement
1420
+ var $container = this.options.container ? $(this.options.container) : this.$element.parent()
1421
+ var containerDim = this.getPosition($container)
1422
+
1423
+ placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
1424
+ placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
1425
+ placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
1426
+ placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
1427
+ placement
1428
+
1429
+ $tip
1430
+ .removeClass(orgPlacement)
1431
+ .addClass(placement)
1448
1432
  }
1449
- $.support.transition && this.$element.hasClass('fade') ?
1450
- this.$backdrop
1451
- .one('bsTransitionEnd', callbackRemove)
1452
- .emulateTransitionEnd(150) :
1453
- callbackRemove()
1454
1433
 
1455
- } else if (callback) {
1456
- callback()
1434
+ var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
1435
+
1436
+ this.applyPlacement(calculatedOffset, placement)
1437
+
1438
+ var complete = function () {
1439
+ var prevHoverState = that.hoverState
1440
+ that.$element.trigger('shown.bs.' + that.type)
1441
+ that.hoverState = null
1442
+
1443
+ if (prevHoverState == 'out') that.leave(that)
1444
+ }
1445
+
1446
+ $.support.transition && this.$tip.hasClass('fade') ?
1447
+ $tip
1448
+ .one('bsTransitionEnd', complete)
1449
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
1450
+ complete()
1457
1451
  }
1458
1452
  }
1459
1453
 
1460
- Modal.prototype.checkScrollbar = function () {
1461
- if (document.body.clientWidth >= window.innerWidth) return
1462
- this.scrollbarWidth = this.scrollbarWidth || this.measureScrollbar()
1463
- }
1454
+ Tooltip.prototype.applyPlacement = function (offset, placement) {
1455
+ var $tip = this.tip()
1456
+ var width = $tip[0].offsetWidth
1457
+ var height = $tip[0].offsetHeight
1464
1458
 
1465
- Modal.prototype.setScrollbar = function () {
1466
- var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
1467
- if (this.scrollbarWidth) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
1468
- }
1459
+ // manually read margins because getBoundingClientRect includes difference
1460
+ var marginTop = parseInt($tip.css('margin-top'), 10)
1461
+ var marginLeft = parseInt($tip.css('margin-left'), 10)
1469
1462
 
1470
- Modal.prototype.resetScrollbar = function () {
1471
- this.$body.css('padding-right', '')
1472
- }
1463
+ // we must check for NaN for ie 8/9
1464
+ if (isNaN(marginTop)) marginTop = 0
1465
+ if (isNaN(marginLeft)) marginLeft = 0
1473
1466
 
1474
- Modal.prototype.measureScrollbar = function () { // thx walsh
1475
- var scrollDiv = document.createElement('div')
1476
- scrollDiv.className = 'modal-scrollbar-measure'
1477
- this.$body.append(scrollDiv)
1478
- var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
1479
- this.$body[0].removeChild(scrollDiv)
1480
- return scrollbarWidth
1481
- }
1467
+ offset.top = offset.top + marginTop
1468
+ offset.left = offset.left + marginLeft
1482
1469
 
1470
+ // $.fn.offset doesn't round pixel values
1471
+ // so we use setOffset directly with our own function B-0
1472
+ $.offset.setOffset($tip[0], $.extend({
1473
+ using: function (props) {
1474
+ $tip.css({
1475
+ top: Math.round(props.top),
1476
+ left: Math.round(props.left)
1477
+ })
1478
+ }
1479
+ }, offset), 0)
1483
1480
 
1484
- // MODAL PLUGIN DEFINITION
1485
- // =======================
1481
+ $tip.addClass('in')
1486
1482
 
1487
- function Plugin(option, _relatedTarget) {
1488
- return this.each(function () {
1489
- var $this = $(this)
1490
- var data = $this.data('bs.modal')
1491
- var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
1483
+ // check to see if placing tip in new offset caused the tip to resize itself
1484
+ var actualWidth = $tip[0].offsetWidth
1485
+ var actualHeight = $tip[0].offsetHeight
1492
1486
 
1493
- if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
1494
- if (typeof option == 'string') data[option](_relatedTarget)
1495
- else if (options.show) data.show(_relatedTarget)
1496
- })
1497
- }
1487
+ if (placement == 'top' && actualHeight != height) {
1488
+ offset.top = offset.top + height - actualHeight
1489
+ }
1498
1490
 
1499
- var old = $.fn.modal
1491
+ var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
1500
1492
 
1501
- $.fn.modal = Plugin
1502
- $.fn.modal.Constructor = Modal
1493
+ if (delta.left) offset.left += delta.left
1494
+ else offset.top += delta.top
1503
1495
 
1496
+ var isVertical = /top|bottom/.test(placement)
1497
+ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
1498
+ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
1504
1499
 
1505
- // MODAL NO CONFLICT
1506
- // =================
1500
+ $tip.offset(offset)
1501
+ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
1502
+ }
1507
1503
 
1508
- $.fn.modal.noConflict = function () {
1509
- $.fn.modal = old
1510
- return this
1504
+ Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) {
1505
+ this.arrow()
1506
+ .css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
1507
+ .css(isHorizontal ? 'top' : 'left', '')
1511
1508
  }
1512
1509
 
1510
+ Tooltip.prototype.setContent = function () {
1511
+ var $tip = this.tip()
1512
+ var title = this.getTitle()
1513
1513
 
1514
- // MODAL DATA-API
1515
- // ==============
1514
+ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
1515
+ $tip.removeClass('fade in top bottom left right')
1516
+ }
1516
1517
 
1517
- $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
1518
- var $this = $(this)
1519
- var href = $this.attr('href')
1520
- var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
1521
- var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
1518
+ Tooltip.prototype.hide = function (callback) {
1519
+ var that = this
1520
+ var $tip = this.tip()
1521
+ var e = $.Event('hide.bs.' + this.type)
1522
1522
 
1523
- if ($this.is('a')) e.preventDefault()
1523
+ function complete() {
1524
+ if (that.hoverState != 'in') $tip.detach()
1525
+ that.$element
1526
+ .removeAttr('aria-describedby')
1527
+ .trigger('hidden.bs.' + that.type)
1528
+ callback && callback()
1529
+ }
1524
1530
 
1525
- $target.one('show.bs.modal', function (showEvent) {
1526
- if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
1527
- $target.one('hidden.bs.modal', function () {
1528
- $this.is(':visible') && $this.trigger('focus')
1529
- })
1530
- })
1531
- Plugin.call($target, option, this)
1532
- })
1531
+ this.$element.trigger(e)
1533
1532
 
1534
- }(jQuery);
1533
+ if (e.isDefaultPrevented()) return
1535
1534
 
1536
- /* ========================================================================
1537
- * Bootstrap: tooltip.js v3.2.0
1538
- * http://getbootstrap.com/javascript/#tooltip
1539
- * Inspired by the original jQuery.tipsy by Jason Frame
1540
- * ========================================================================
1541
- * Copyright 2011-2014 Twitter, Inc.
1542
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1543
- * ======================================================================== */
1535
+ $tip.removeClass('in')
1544
1536
 
1537
+ $.support.transition && this.$tip.hasClass('fade') ?
1538
+ $tip
1539
+ .one('bsTransitionEnd', complete)
1540
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
1541
+ complete()
1545
1542
 
1546
- +function ($) {
1547
- 'use strict';
1543
+ this.hoverState = null
1548
1544
 
1549
- // TOOLTIP PUBLIC CLASS DEFINITION
1550
- // ===============================
1545
+ return this
1546
+ }
1551
1547
 
1552
- var Tooltip = function (element, options) {
1553
- this.type =
1554
- this.options =
1555
- this.enabled =
1556
- this.timeout =
1557
- this.hoverState =
1558
- this.$element = null
1548
+ Tooltip.prototype.fixTitle = function () {
1549
+ var $e = this.$element
1550
+ if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
1551
+ $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
1552
+ }
1553
+ }
1559
1554
 
1560
- this.init('tooltip', element, options)
1555
+ Tooltip.prototype.hasContent = function () {
1556
+ return this.getTitle()
1561
1557
  }
1562
1558
 
1563
- Tooltip.VERSION = '3.2.0'
1559
+ Tooltip.prototype.getPosition = function ($element) {
1560
+ $element = $element || this.$element
1564
1561
 
1565
- Tooltip.DEFAULTS = {
1566
- animation: true,
1567
- placement: 'top',
1568
- selector: false,
1569
- template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
1570
- trigger: 'hover focus',
1571
- title: '',
1572
- delay: 0,
1573
- html: false,
1574
- container: false,
1575
- viewport: {
1576
- selector: 'body',
1577
- padding: 0
1562
+ var el = $element[0]
1563
+ var isBody = el.tagName == 'BODY'
1564
+
1565
+ var elRect = el.getBoundingClientRect()
1566
+ if (elRect.width == null) {
1567
+ // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
1568
+ elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
1578
1569
  }
1570
+ var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
1571
+ var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
1572
+ var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
1573
+
1574
+ return $.extend({}, elRect, scroll, outerDims, elOffset)
1579
1575
  }
1580
1576
 
1581
- Tooltip.prototype.init = function (type, element, options) {
1582
- this.enabled = true
1583
- this.type = type
1584
- this.$element = $(element)
1585
- this.options = this.getOptions(options)
1586
- this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
1577
+ Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
1578
+ return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
1579
+ placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
1580
+ placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
1581
+ /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
1587
1582
 
1588
- var triggers = this.options.trigger.split(' ')
1583
+ }
1589
1584
 
1590
- for (var i = triggers.length; i--;) {
1591
- var trigger = triggers[i]
1585
+ Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
1586
+ var delta = { top: 0, left: 0 }
1587
+ if (!this.$viewport) return delta
1592
1588
 
1593
- if (trigger == 'click') {
1594
- this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
1595
- } else if (trigger != 'manual') {
1596
- var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
1597
- var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
1589
+ var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
1590
+ var viewportDimensions = this.getPosition(this.$viewport)
1598
1591
 
1599
- this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
1600
- this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
1592
+ if (/right|left/.test(placement)) {
1593
+ var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
1594
+ var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
1595
+ if (topEdgeOffset < viewportDimensions.top) { // top overflow
1596
+ delta.top = viewportDimensions.top - topEdgeOffset
1597
+ } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
1598
+ delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
1599
+ }
1600
+ } else {
1601
+ var leftEdgeOffset = pos.left - viewportPadding
1602
+ var rightEdgeOffset = pos.left + viewportPadding + actualWidth
1603
+ if (leftEdgeOffset < viewportDimensions.left) { // left overflow
1604
+ delta.left = viewportDimensions.left - leftEdgeOffset
1605
+ } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
1606
+ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
1601
1607
  }
1602
1608
  }
1603
1609
 
1604
- this.options.selector ?
1605
- (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
1606
- this.fixTitle()
1607
- }
1608
-
1609
- Tooltip.prototype.getDefaults = function () {
1610
- return Tooltip.DEFAULTS
1610
+ return delta
1611
1611
  }
1612
1612
 
1613
- Tooltip.prototype.getOptions = function (options) {
1614
- options = $.extend({}, this.getDefaults(), this.$element.data(), options)
1613
+ Tooltip.prototype.getTitle = function () {
1614
+ var title
1615
+ var $e = this.$element
1616
+ var o = this.options
1615
1617
 
1616
- if (options.delay && typeof options.delay == 'number') {
1617
- options.delay = {
1618
- show: options.delay,
1619
- hide: options.delay
1620
- }
1621
- }
1618
+ title = $e.attr('data-original-title')
1619
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
1622
1620
 
1623
- return options
1621
+ return title
1624
1622
  }
1625
1623
 
1626
- Tooltip.prototype.getDelegateOptions = function () {
1627
- var options = {}
1628
- var defaults = this.getDefaults()
1629
-
1630
- this._options && $.each(this._options, function (key, value) {
1631
- if (defaults[key] != value) options[key] = value
1632
- })
1624
+ Tooltip.prototype.getUID = function (prefix) {
1625
+ do prefix += ~~(Math.random() * 1000000)
1626
+ while (document.getElementById(prefix))
1627
+ return prefix
1628
+ }
1633
1629
 
1634
- return options
1630
+ Tooltip.prototype.tip = function () {
1631
+ return (this.$tip = this.$tip || $(this.options.template))
1635
1632
  }
1636
1633
 
1637
- Tooltip.prototype.enter = function (obj) {
1638
- var self = obj instanceof this.constructor ?
1639
- obj : $(obj.currentTarget).data('bs.' + this.type)
1634
+ Tooltip.prototype.arrow = function () {
1635
+ return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
1636
+ }
1640
1637
 
1641
- if (!self) {
1642
- self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
1643
- $(obj.currentTarget).data('bs.' + this.type, self)
1644
- }
1638
+ Tooltip.prototype.enable = function () {
1639
+ this.enabled = true
1640
+ }
1645
1641
 
1646
- clearTimeout(self.timeout)
1642
+ Tooltip.prototype.disable = function () {
1643
+ this.enabled = false
1644
+ }
1647
1645
 
1648
- self.hoverState = 'in'
1646
+ Tooltip.prototype.toggleEnabled = function () {
1647
+ this.enabled = !this.enabled
1648
+ }
1649
1649
 
1650
- if (!self.options.delay || !self.options.delay.show) return self.show()
1650
+ Tooltip.prototype.toggle = function (e) {
1651
+ var self = this
1652
+ if (e) {
1653
+ self = $(e.currentTarget).data('bs.' + this.type)
1654
+ if (!self) {
1655
+ self = new this.constructor(e.currentTarget, this.getDelegateOptions())
1656
+ $(e.currentTarget).data('bs.' + this.type, self)
1657
+ }
1658
+ }
1651
1659
 
1652
- self.timeout = setTimeout(function () {
1653
- if (self.hoverState == 'in') self.show()
1654
- }, self.options.delay.show)
1660
+ self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
1655
1661
  }
1656
1662
 
1657
- Tooltip.prototype.leave = function (obj) {
1658
- var self = obj instanceof this.constructor ?
1659
- obj : $(obj.currentTarget).data('bs.' + this.type)
1660
-
1661
- if (!self) {
1662
- self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
1663
- $(obj.currentTarget).data('bs.' + this.type, self)
1664
- }
1663
+ Tooltip.prototype.destroy = function () {
1664
+ var that = this
1665
+ clearTimeout(this.timeout)
1666
+ this.hide(function () {
1667
+ that.$element.off('.' + that.type).removeData('bs.' + that.type)
1668
+ })
1669
+ }
1665
1670
 
1666
- clearTimeout(self.timeout)
1667
1671
 
1668
- self.hoverState = 'out'
1672
+ // TOOLTIP PLUGIN DEFINITION
1673
+ // =========================
1669
1674
 
1670
- if (!self.options.delay || !self.options.delay.hide) return self.hide()
1675
+ function Plugin(option) {
1676
+ return this.each(function () {
1677
+ var $this = $(this)
1678
+ var data = $this.data('bs.tooltip')
1679
+ var options = typeof option == 'object' && option
1680
+ var selector = options && options.selector
1671
1681
 
1672
- self.timeout = setTimeout(function () {
1673
- if (self.hoverState == 'out') self.hide()
1674
- }, self.options.delay.hide)
1682
+ if (!data && option == 'destroy') return
1683
+ if (selector) {
1684
+ if (!data) $this.data('bs.tooltip', (data = {}))
1685
+ if (!data[selector]) data[selector] = new Tooltip(this, options)
1686
+ } else {
1687
+ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
1688
+ }
1689
+ if (typeof option == 'string') data[option]()
1690
+ })
1675
1691
  }
1676
1692
 
1677
- Tooltip.prototype.show = function () {
1678
- var e = $.Event('show.bs.' + this.type)
1693
+ var old = $.fn.tooltip
1679
1694
 
1680
- if (this.hasContent() && this.enabled) {
1681
- this.$element.trigger(e)
1695
+ $.fn.tooltip = Plugin
1696
+ $.fn.tooltip.Constructor = Tooltip
1682
1697
 
1683
- var inDom = $.contains(document.documentElement, this.$element[0])
1684
- if (e.isDefaultPrevented() || !inDom) return
1685
- var that = this
1686
1698
 
1687
- var $tip = this.tip()
1699
+ // TOOLTIP NO CONFLICT
1700
+ // ===================
1688
1701
 
1689
- var tipId = this.getUID(this.type)
1702
+ $.fn.tooltip.noConflict = function () {
1703
+ $.fn.tooltip = old
1704
+ return this
1705
+ }
1690
1706
 
1691
- this.setContent()
1692
- $tip.attr('id', tipId)
1693
- this.$element.attr('aria-describedby', tipId)
1707
+ }(jQuery);
1694
1708
 
1695
- if (this.options.animation) $tip.addClass('fade')
1709
+ /* ========================================================================
1710
+ * Bootstrap: popover.js v3.3.1
1711
+ * http://getbootstrap.com/javascript/#popovers
1712
+ * ========================================================================
1713
+ * Copyright 2011-2014 Twitter, Inc.
1714
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1715
+ * ======================================================================== */
1696
1716
 
1697
- var placement = typeof this.options.placement == 'function' ?
1698
- this.options.placement.call(this, $tip[0], this.$element[0]) :
1699
- this.options.placement
1700
1717
 
1701
- var autoToken = /\s?auto?\s?/i
1702
- var autoPlace = autoToken.test(placement)
1703
- if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
1718
+ +function ($) {
1719
+ 'use strict';
1704
1720
 
1705
- $tip
1706
- .detach()
1707
- .css({ top: 0, left: 0, display: 'block' })
1708
- .addClass(placement)
1709
- .data('bs.' + this.type, this)
1721
+ // POPOVER PUBLIC CLASS DEFINITION
1722
+ // ===============================
1710
1723
 
1711
- this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
1724
+ var Popover = function (element, options) {
1725
+ this.init('popover', element, options)
1726
+ }
1712
1727
 
1713
- var pos = this.getPosition()
1714
- var actualWidth = $tip[0].offsetWidth
1715
- var actualHeight = $tip[0].offsetHeight
1728
+ if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
1716
1729
 
1717
- if (autoPlace) {
1718
- var orgPlacement = placement
1719
- var $parent = this.$element.parent()
1720
- var parentDim = this.getPosition($parent)
1730
+ Popover.VERSION = '3.3.1'
1721
1731
 
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' :
1726
- placement
1732
+ Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
1733
+ placement: 'right',
1734
+ trigger: 'click',
1735
+ content: '',
1736
+ template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
1737
+ })
1727
1738
 
1728
- $tip
1729
- .removeClass(orgPlacement)
1730
- .addClass(placement)
1731
- }
1732
1739
 
1733
- var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
1740
+ // NOTE: POPOVER EXTENDS tooltip.js
1741
+ // ================================
1734
1742
 
1735
- this.applyPlacement(calculatedOffset, placement)
1743
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
1736
1744
 
1737
- var complete = function () {
1738
- that.$element.trigger('shown.bs.' + that.type)
1739
- that.hoverState = null
1740
- }
1745
+ Popover.prototype.constructor = Popover
1741
1746
 
1742
- $.support.transition && this.$tip.hasClass('fade') ?
1743
- $tip
1744
- .one('bsTransitionEnd', complete)
1745
- .emulateTransitionEnd(150) :
1746
- complete()
1747
- }
1747
+ Popover.prototype.getDefaults = function () {
1748
+ return Popover.DEFAULTS
1748
1749
  }
1749
1750
 
1750
- Tooltip.prototype.applyPlacement = function (offset, placement) {
1751
- var $tip = this.tip()
1752
- var width = $tip[0].offsetWidth
1753
- var height = $tip[0].offsetHeight
1751
+ Popover.prototype.setContent = function () {
1752
+ var $tip = this.tip()
1753
+ var title = this.getTitle()
1754
+ var content = this.getContent()
1754
1755
 
1755
- // manually read margins because getBoundingClientRect includes difference
1756
- var marginTop = parseInt($tip.css('margin-top'), 10)
1757
- var marginLeft = parseInt($tip.css('margin-left'), 10)
1756
+ $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
1757
+ $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
1758
+ this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
1759
+ ](content)
1758
1760
 
1759
- // we must check for NaN for ie 8/9
1760
- if (isNaN(marginTop)) marginTop = 0
1761
- if (isNaN(marginLeft)) marginLeft = 0
1761
+ $tip.removeClass('fade top bottom left right in')
1762
1762
 
1763
- offset.top = offset.top + marginTop
1764
- offset.left = offset.left + marginLeft
1763
+ // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
1764
+ // this manually by checking the contents.
1765
+ if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
1766
+ }
1765
1767
 
1766
- // $.fn.offset doesn't round pixel values
1767
- // so we use setOffset directly with our own function B-0
1768
- $.offset.setOffset($tip[0], $.extend({
1769
- using: function (props) {
1770
- $tip.css({
1771
- top: Math.round(props.top),
1772
- left: Math.round(props.left)
1773
- })
1774
- }
1775
- }, offset), 0)
1768
+ Popover.prototype.hasContent = function () {
1769
+ return this.getTitle() || this.getContent()
1770
+ }
1776
1771
 
1777
- $tip.addClass('in')
1772
+ Popover.prototype.getContent = function () {
1773
+ var $e = this.$element
1774
+ var o = this.options
1778
1775
 
1779
- // check to see if placing tip in new offset caused the tip to resize itself
1780
- var actualWidth = $tip[0].offsetWidth
1781
- var actualHeight = $tip[0].offsetHeight
1776
+ return $e.attr('data-content')
1777
+ || (typeof o.content == 'function' ?
1778
+ o.content.call($e[0]) :
1779
+ o.content)
1780
+ }
1782
1781
 
1783
- if (placement == 'top' && actualHeight != height) {
1784
- offset.top = offset.top + height - actualHeight
1785
- }
1782
+ Popover.prototype.arrow = function () {
1783
+ return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
1784
+ }
1786
1785
 
1787
- var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
1786
+ Popover.prototype.tip = function () {
1787
+ if (!this.$tip) this.$tip = $(this.options.template)
1788
+ return this.$tip
1789
+ }
1788
1790
 
1789
- if (delta.left) offset.left += delta.left
1790
- else offset.top += delta.top
1791
1791
 
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'
1792
+ // POPOVER PLUGIN DEFINITION
1793
+ // =========================
1794
+
1795
+ function Plugin(option) {
1796
+ return this.each(function () {
1797
+ var $this = $(this)
1798
+ var data = $this.data('bs.popover')
1799
+ var options = typeof option == 'object' && option
1800
+ var selector = options && options.selector
1795
1801
 
1796
- $tip.offset(offset)
1797
- this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
1802
+ if (!data && option == 'destroy') return
1803
+ if (selector) {
1804
+ if (!data) $this.data('bs.popover', (data = {}))
1805
+ if (!data[selector]) data[selector] = new Popover(this, options)
1806
+ } else {
1807
+ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
1808
+ }
1809
+ if (typeof option == 'string') data[option]()
1810
+ })
1798
1811
  }
1799
1812
 
1800
- Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
1801
- this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
1813
+ var old = $.fn.popover
1814
+
1815
+ $.fn.popover = Plugin
1816
+ $.fn.popover.Constructor = Popover
1817
+
1818
+
1819
+ // POPOVER NO CONFLICT
1820
+ // ===================
1821
+
1822
+ $.fn.popover.noConflict = function () {
1823
+ $.fn.popover = old
1824
+ return this
1802
1825
  }
1803
1826
 
1804
- Tooltip.prototype.setContent = function () {
1805
- var $tip = this.tip()
1806
- var title = this.getTitle()
1827
+ }(jQuery);
1807
1828
 
1808
- $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
1809
- $tip.removeClass('fade in top bottom left right')
1829
+ /* ========================================================================
1830
+ * Bootstrap: scrollspy.js v3.3.1
1831
+ * http://getbootstrap.com/javascript/#scrollspy
1832
+ * ========================================================================
1833
+ * Copyright 2011-2014 Twitter, Inc.
1834
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1835
+ * ======================================================================== */
1836
+
1837
+
1838
+ +function ($) {
1839
+ 'use strict';
1840
+
1841
+ // SCROLLSPY CLASS DEFINITION
1842
+ // ==========================
1843
+
1844
+ function ScrollSpy(element, options) {
1845
+ var process = $.proxy(this.process, this)
1846
+
1847
+ this.$body = $('body')
1848
+ this.$scrollElement = $(element).is('body') ? $(window) : $(element)
1849
+ this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
1850
+ this.selector = (this.options.target || '') + ' .nav li > a'
1851
+ this.offsets = []
1852
+ this.targets = []
1853
+ this.activeTarget = null
1854
+ this.scrollHeight = 0
1855
+
1856
+ this.$scrollElement.on('scroll.bs.scrollspy', process)
1857
+ this.refresh()
1858
+ this.process()
1810
1859
  }
1811
1860
 
1812
- Tooltip.prototype.hide = function () {
1813
- var that = this
1814
- var $tip = this.tip()
1815
- var e = $.Event('hide.bs.' + this.type)
1861
+ ScrollSpy.VERSION = '3.3.1'
1816
1862
 
1817
- this.$element.removeAttr('aria-describedby')
1863
+ ScrollSpy.DEFAULTS = {
1864
+ offset: 10
1865
+ }
1818
1866
 
1819
- function complete() {
1820
- if (that.hoverState != 'in') $tip.detach()
1821
- that.$element.trigger('hidden.bs.' + that.type)
1867
+ ScrollSpy.prototype.getScrollHeight = function () {
1868
+ return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
1869
+ }
1870
+
1871
+ ScrollSpy.prototype.refresh = function () {
1872
+ var offsetMethod = 'offset'
1873
+ var offsetBase = 0
1874
+
1875
+ if (!$.isWindow(this.$scrollElement[0])) {
1876
+ offsetMethod = 'position'
1877
+ offsetBase = this.$scrollElement.scrollTop()
1822
1878
  }
1823
1879
 
1824
- this.$element.trigger(e)
1880
+ this.offsets = []
1881
+ this.targets = []
1882
+ this.scrollHeight = this.getScrollHeight()
1825
1883
 
1826
- if (e.isDefaultPrevented()) return
1884
+ var self = this
1827
1885
 
1828
- $tip.removeClass('in')
1886
+ this.$body
1887
+ .find(this.selector)
1888
+ .map(function () {
1889
+ var $el = $(this)
1890
+ var href = $el.data('target') || $el.attr('href')
1891
+ var $href = /^#./.test(href) && $(href)
1829
1892
 
1830
- $.support.transition && this.$tip.hasClass('fade') ?
1831
- $tip
1832
- .one('bsTransitionEnd', complete)
1833
- .emulateTransitionEnd(150) :
1834
- complete()
1893
+ return ($href
1894
+ && $href.length
1895
+ && $href.is(':visible')
1896
+ && [[$href[offsetMethod]().top + offsetBase, href]]) || null
1897
+ })
1898
+ .sort(function (a, b) { return a[0] - b[0] })
1899
+ .each(function () {
1900
+ self.offsets.push(this[0])
1901
+ self.targets.push(this[1])
1902
+ })
1903
+ }
1835
1904
 
1836
- this.hoverState = null
1905
+ ScrollSpy.prototype.process = function () {
1906
+ var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
1907
+ var scrollHeight = this.getScrollHeight()
1908
+ var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
1909
+ var offsets = this.offsets
1910
+ var targets = this.targets
1911
+ var activeTarget = this.activeTarget
1912
+ var i
1837
1913
 
1838
- return this
1914
+ if (this.scrollHeight != scrollHeight) {
1915
+ this.refresh()
1916
+ }
1917
+
1918
+ if (scrollTop >= maxScroll) {
1919
+ return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
1920
+ }
1921
+
1922
+ if (activeTarget && scrollTop < offsets[0]) {
1923
+ this.activeTarget = null
1924
+ return this.clear()
1925
+ }
1926
+
1927
+ for (i = offsets.length; i--;) {
1928
+ activeTarget != targets[i]
1929
+ && scrollTop >= offsets[i]
1930
+ && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
1931
+ && this.activate(targets[i])
1932
+ }
1839
1933
  }
1840
1934
 
1841
- Tooltip.prototype.fixTitle = function () {
1842
- var $e = this.$element
1843
- if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
1844
- $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
1935
+ ScrollSpy.prototype.activate = function (target) {
1936
+ this.activeTarget = target
1937
+
1938
+ this.clear()
1939
+
1940
+ var selector = this.selector +
1941
+ '[data-target="' + target + '"],' +
1942
+ this.selector + '[href="' + target + '"]'
1943
+
1944
+ var active = $(selector)
1945
+ .parents('li')
1946
+ .addClass('active')
1947
+
1948
+ if (active.parent('.dropdown-menu').length) {
1949
+ active = active
1950
+ .closest('li.dropdown')
1951
+ .addClass('active')
1845
1952
  }
1953
+
1954
+ active.trigger('activate.bs.scrollspy')
1846
1955
  }
1847
1956
 
1848
- Tooltip.prototype.hasContent = function () {
1849
- return this.getTitle()
1957
+ ScrollSpy.prototype.clear = function () {
1958
+ $(this.selector)
1959
+ .parentsUntil(this.options.target, '.active')
1960
+ .removeClass('active')
1850
1961
  }
1851
1962
 
1852
- Tooltip.prototype.getPosition = function ($element) {
1853
- $element = $element || this.$element
1854
- var el = $element[0]
1855
- 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())
1963
+
1964
+ // SCROLLSPY PLUGIN DEFINITION
1965
+ // ===========================
1966
+
1967
+ function Plugin(option) {
1968
+ return this.each(function () {
1969
+ var $this = $(this)
1970
+ var data = $this.data('bs.scrollspy')
1971
+ var options = typeof option == 'object' && option
1972
+
1973
+ if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
1974
+ if (typeof option == 'string') data[option]()
1975
+ })
1861
1976
  }
1862
1977
 
1863
- Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
1864
- return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
1865
- placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
1866
- placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
1867
- /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
1978
+ var old = $.fn.scrollspy
1979
+
1980
+ $.fn.scrollspy = Plugin
1981
+ $.fn.scrollspy.Constructor = ScrollSpy
1982
+
1983
+
1984
+ // SCROLLSPY NO CONFLICT
1985
+ // =====================
1868
1986
 
1987
+ $.fn.scrollspy.noConflict = function () {
1988
+ $.fn.scrollspy = old
1989
+ return this
1869
1990
  }
1870
1991
 
1871
- Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
1872
- var delta = { top: 0, left: 0 }
1873
- if (!this.$viewport) return delta
1874
1992
 
1875
- var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
1876
- var viewportDimensions = this.getPosition(this.$viewport)
1993
+ // SCROLLSPY DATA-API
1994
+ // ==================
1877
1995
 
1878
- if (/right|left/.test(placement)) {
1879
- var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
1880
- var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
1881
- if (topEdgeOffset < viewportDimensions.top) { // top overflow
1882
- delta.top = viewportDimensions.top - topEdgeOffset
1883
- } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
1884
- delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
1885
- }
1886
- } else {
1887
- var leftEdgeOffset = pos.left - viewportPadding
1888
- var rightEdgeOffset = pos.left + viewportPadding + actualWidth
1889
- if (leftEdgeOffset < viewportDimensions.left) { // left overflow
1890
- delta.left = viewportDimensions.left - leftEdgeOffset
1891
- } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
1892
- delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
1893
- }
1894
- }
1996
+ $(window).on('load.bs.scrollspy.data-api', function () {
1997
+ $('[data-spy="scroll"]').each(function () {
1998
+ var $spy = $(this)
1999
+ Plugin.call($spy, $spy.data())
2000
+ })
2001
+ })
2002
+
2003
+ }(jQuery);
2004
+
2005
+ /* ========================================================================
2006
+ * Bootstrap: tab.js v3.3.1
2007
+ * http://getbootstrap.com/javascript/#tabs
2008
+ * ========================================================================
2009
+ * Copyright 2011-2014 Twitter, Inc.
2010
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
2011
+ * ======================================================================== */
2012
+
2013
+
2014
+ +function ($) {
2015
+ 'use strict';
2016
+
2017
+ // TAB CLASS DEFINITION
2018
+ // ====================
2019
+
2020
+ var Tab = function (element) {
2021
+ this.element = $(element)
2022
+ }
2023
+
2024
+ Tab.VERSION = '3.3.1'
2025
+
2026
+ Tab.TRANSITION_DURATION = 150
2027
+
2028
+ Tab.prototype.show = function () {
2029
+ var $this = this.element
2030
+ var $ul = $this.closest('ul:not(.dropdown-menu)')
2031
+ var selector = $this.data('target')
1895
2032
 
1896
- return delta
1897
- }
2033
+ if (!selector) {
2034
+ selector = $this.attr('href')
2035
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
2036
+ }
1898
2037
 
1899
- Tooltip.prototype.getTitle = function () {
1900
- var title
1901
- var $e = this.$element
1902
- var o = this.options
2038
+ if ($this.parent('li').hasClass('active')) return
1903
2039
 
1904
- title = $e.attr('data-original-title')
1905
- || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
2040
+ var $previous = $ul.find('.active:last a')
2041
+ var hideEvent = $.Event('hide.bs.tab', {
2042
+ relatedTarget: $this[0]
2043
+ })
2044
+ var showEvent = $.Event('show.bs.tab', {
2045
+ relatedTarget: $previous[0]
2046
+ })
1906
2047
 
1907
- return title
1908
- }
2048
+ $previous.trigger(hideEvent)
2049
+ $this.trigger(showEvent)
1909
2050
 
1910
- Tooltip.prototype.getUID = function (prefix) {
1911
- do prefix += ~~(Math.random() * 1000000)
1912
- while (document.getElementById(prefix))
1913
- return prefix
1914
- }
2051
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
1915
2052
 
1916
- Tooltip.prototype.tip = function () {
1917
- return (this.$tip = this.$tip || $(this.options.template))
1918
- }
2053
+ var $target = $(selector)
1919
2054
 
1920
- Tooltip.prototype.arrow = function () {
1921
- return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
2055
+ this.activate($this.closest('li'), $ul)
2056
+ this.activate($target, $target.parent(), function () {
2057
+ $previous.trigger({
2058
+ type: 'hidden.bs.tab',
2059
+ relatedTarget: $this[0]
2060
+ })
2061
+ $this.trigger({
2062
+ type: 'shown.bs.tab',
2063
+ relatedTarget: $previous[0]
2064
+ })
2065
+ })
1922
2066
  }
1923
2067
 
1924
- Tooltip.prototype.validate = function () {
1925
- if (!this.$element[0].parentNode) {
1926
- this.hide()
1927
- this.$element = null
1928
- this.options = null
1929
- }
1930
- }
2068
+ Tab.prototype.activate = function (element, container, callback) {
2069
+ var $active = container.find('> .active')
2070
+ var transition = callback
2071
+ && $.support.transition
2072
+ && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length)
1931
2073
 
1932
- Tooltip.prototype.enable = function () {
1933
- this.enabled = true
1934
- }
2074
+ function next() {
2075
+ $active
2076
+ .removeClass('active')
2077
+ .find('> .dropdown-menu > .active')
2078
+ .removeClass('active')
2079
+ .end()
2080
+ .find('[data-toggle="tab"]')
2081
+ .attr('aria-expanded', false)
1935
2082
 
1936
- Tooltip.prototype.disable = function () {
1937
- this.enabled = false
1938
- }
2083
+ element
2084
+ .addClass('active')
2085
+ .find('[data-toggle="tab"]')
2086
+ .attr('aria-expanded', true)
1939
2087
 
1940
- Tooltip.prototype.toggleEnabled = function () {
1941
- this.enabled = !this.enabled
1942
- }
2088
+ if (transition) {
2089
+ element[0].offsetWidth // reflow for transition
2090
+ element.addClass('in')
2091
+ } else {
2092
+ element.removeClass('fade')
2093
+ }
1943
2094
 
1944
- Tooltip.prototype.toggle = function (e) {
1945
- var self = this
1946
- if (e) {
1947
- self = $(e.currentTarget).data('bs.' + this.type)
1948
- if (!self) {
1949
- self = new this.constructor(e.currentTarget, this.getDelegateOptions())
1950
- $(e.currentTarget).data('bs.' + this.type, self)
2095
+ if (element.parent('.dropdown-menu')) {
2096
+ element
2097
+ .closest('li.dropdown')
2098
+ .addClass('active')
2099
+ .end()
2100
+ .find('[data-toggle="tab"]')
2101
+ .attr('aria-expanded', true)
1951
2102
  }
2103
+
2104
+ callback && callback()
1952
2105
  }
1953
2106
 
1954
- self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
1955
- }
2107
+ $active.length && transition ?
2108
+ $active
2109
+ .one('bsTransitionEnd', next)
2110
+ .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
2111
+ next()
1956
2112
 
1957
- Tooltip.prototype.destroy = function () {
1958
- clearTimeout(this.timeout)
1959
- this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
2113
+ $active.removeClass('in')
1960
2114
  }
1961
2115
 
1962
2116
 
1963
- // TOOLTIP PLUGIN DEFINITION
1964
- // =========================
2117
+ // TAB PLUGIN DEFINITION
2118
+ // =====================
1965
2119
 
1966
2120
  function Plugin(option) {
1967
2121
  return this.each(function () {
1968
- var $this = $(this)
1969
- var data = $this.data('bs.tooltip')
1970
- var options = typeof option == 'object' && option
2122
+ var $this = $(this)
2123
+ var data = $this.data('bs.tab')
1971
2124
 
1972
- if (!data && option == 'destroy') return
1973
- if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
2125
+ if (!data) $this.data('bs.tab', (data = new Tab(this)))
1974
2126
  if (typeof option == 'string') data[option]()
1975
2127
  })
1976
2128
  }
1977
2129
 
1978
- var old = $.fn.tooltip
2130
+ var old = $.fn.tab
1979
2131
 
1980
- $.fn.tooltip = Plugin
1981
- $.fn.tooltip.Constructor = Tooltip
2132
+ $.fn.tab = Plugin
2133
+ $.fn.tab.Constructor = Tab
1982
2134
 
1983
2135
 
1984
- // TOOLTIP NO CONFLICT
1985
- // ===================
2136
+ // TAB NO CONFLICT
2137
+ // ===============
1986
2138
 
1987
- $.fn.tooltip.noConflict = function () {
1988
- $.fn.tooltip = old
2139
+ $.fn.tab.noConflict = function () {
2140
+ $.fn.tab = old
1989
2141
  return this
1990
2142
  }
1991
2143
 
2144
+
2145
+ // TAB DATA-API
2146
+ // ============
2147
+
2148
+ var clickHandler = function (e) {
2149
+ e.preventDefault()
2150
+ Plugin.call($(this), 'show')
2151
+ }
2152
+
2153
+ $(document)
2154
+ .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
2155
+ .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
2156
+
1992
2157
  }(jQuery);
1993
2158
 
1994
2159
  /* ========================================================================
1995
- * Bootstrap: popover.js v3.2.0
1996
- * http://getbootstrap.com/javascript/#popovers
2160
+ * Bootstrap: affix.js v3.3.1
2161
+ * http://getbootstrap.com/javascript/#affix
1997
2162
  * ========================================================================
1998
2163
  * Copyright 2011-2014 Twitter, Inc.
1999
2164
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
@@ -2003,105 +2168,153 @@
2003
2168
  +function ($) {
2004
2169
  'use strict';
2005
2170
 
2006
- // POPOVER PUBLIC CLASS DEFINITION
2007
- // ===============================
2171
+ // AFFIX CLASS DEFINITION
2172
+ // ======================
2008
2173
 
2009
- var Popover = function (element, options) {
2010
- this.init('popover', element, options)
2011
- }
2174
+ var Affix = function (element, options) {
2175
+ this.options = $.extend({}, Affix.DEFAULTS, options)
2012
2176
 
2013
- if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
2177
+ this.$target = $(this.options.target)
2178
+ .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
2179
+ .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
2014
2180
 
2015
- Popover.VERSION = '3.2.0'
2181
+ this.$element = $(element)
2182
+ this.affixed =
2183
+ this.unpin =
2184
+ this.pinnedOffset = null
2016
2185
 
2017
- Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
2018
- placement: 'right',
2019
- trigger: 'click',
2020
- content: '',
2021
- template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
2022
- })
2186
+ this.checkPosition()
2187
+ }
2023
2188
 
2189
+ Affix.VERSION = '3.3.1'
2024
2190
 
2025
- // NOTE: POPOVER EXTENDS tooltip.js
2026
- // ================================
2191
+ Affix.RESET = 'affix affix-top affix-bottom'
2027
2192
 
2028
- Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
2193
+ Affix.DEFAULTS = {
2194
+ offset: 0,
2195
+ target: window
2196
+ }
2029
2197
 
2030
- Popover.prototype.constructor = Popover
2198
+ Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
2199
+ var scrollTop = this.$target.scrollTop()
2200
+ var position = this.$element.offset()
2201
+ var targetHeight = this.$target.height()
2031
2202
 
2032
- Popover.prototype.getDefaults = function () {
2033
- return Popover.DEFAULTS
2034
- }
2203
+ if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
2035
2204
 
2036
- Popover.prototype.setContent = function () {
2037
- var $tip = this.tip()
2038
- var title = this.getTitle()
2039
- var content = this.getContent()
2205
+ if (this.affixed == 'bottom') {
2206
+ if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
2207
+ return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
2208
+ }
2040
2209
 
2041
- $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
2043
- this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
2044
- ](content)
2210
+ var initializing = this.affixed == null
2211
+ var colliderTop = initializing ? scrollTop : position.top
2212
+ var colliderHeight = initializing ? targetHeight : height
2045
2213
 
2046
- $tip.removeClass('fade top bottom left right in')
2214
+ if (offsetTop != null && colliderTop <= offsetTop) return 'top'
2215
+ if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
2047
2216
 
2048
- // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
2049
- // this manually by checking the contents.
2050
- if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
2217
+ return false
2051
2218
  }
2052
2219
 
2053
- Popover.prototype.hasContent = function () {
2054
- return this.getTitle() || this.getContent()
2220
+ Affix.prototype.getPinnedOffset = function () {
2221
+ if (this.pinnedOffset) return this.pinnedOffset
2222
+ this.$element.removeClass(Affix.RESET).addClass('affix')
2223
+ var scrollTop = this.$target.scrollTop()
2224
+ var position = this.$element.offset()
2225
+ return (this.pinnedOffset = position.top - scrollTop)
2055
2226
  }
2056
2227
 
2057
- Popover.prototype.getContent = function () {
2058
- var $e = this.$element
2059
- var o = this.options
2060
-
2061
- return $e.attr('data-content')
2062
- || (typeof o.content == 'function' ?
2063
- o.content.call($e[0]) :
2064
- o.content)
2228
+ Affix.prototype.checkPositionWithEventLoop = function () {
2229
+ setTimeout($.proxy(this.checkPosition, this), 1)
2065
2230
  }
2066
2231
 
2067
- Popover.prototype.arrow = function () {
2068
- return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
2069
- }
2232
+ Affix.prototype.checkPosition = function () {
2233
+ if (!this.$element.is(':visible')) return
2070
2234
 
2071
- Popover.prototype.tip = function () {
2072
- if (!this.$tip) this.$tip = $(this.options.template)
2073
- return this.$tip
2235
+ var height = this.$element.height()
2236
+ var offset = this.options.offset
2237
+ var offsetTop = offset.top
2238
+ var offsetBottom = offset.bottom
2239
+ var scrollHeight = $('body').height()
2240
+
2241
+ if (typeof offset != 'object') offsetBottom = offsetTop = offset
2242
+ if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
2243
+ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
2244
+
2245
+ var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
2246
+
2247
+ if (this.affixed != affix) {
2248
+ if (this.unpin != null) this.$element.css('top', '')
2249
+
2250
+ var affixType = 'affix' + (affix ? '-' + affix : '')
2251
+ var e = $.Event(affixType + '.bs.affix')
2252
+
2253
+ this.$element.trigger(e)
2254
+
2255
+ if (e.isDefaultPrevented()) return
2256
+
2257
+ this.affixed = affix
2258
+ this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
2259
+
2260
+ this.$element
2261
+ .removeClass(Affix.RESET)
2262
+ .addClass(affixType)
2263
+ .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
2264
+ }
2265
+
2266
+ if (affix == 'bottom') {
2267
+ this.$element.offset({
2268
+ top: scrollHeight - height - offsetBottom
2269
+ })
2270
+ }
2074
2271
  }
2075
2272
 
2076
2273
 
2077
- // POPOVER PLUGIN DEFINITION
2078
- // =========================
2274
+ // AFFIX PLUGIN DEFINITION
2275
+ // =======================
2079
2276
 
2080
2277
  function Plugin(option) {
2081
2278
  return this.each(function () {
2082
2279
  var $this = $(this)
2083
- var data = $this.data('bs.popover')
2280
+ var data = $this.data('bs.affix')
2084
2281
  var options = typeof option == 'object' && option
2085
2282
 
2086
- if (!data && option == 'destroy') return
2087
- if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
2283
+ if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
2088
2284
  if (typeof option == 'string') data[option]()
2089
2285
  })
2090
2286
  }
2091
2287
 
2092
- var old = $.fn.popover
2288
+ var old = $.fn.affix
2093
2289
 
2094
- $.fn.popover = Plugin
2095
- $.fn.popover.Constructor = Popover
2290
+ $.fn.affix = Plugin
2291
+ $.fn.affix.Constructor = Affix
2096
2292
 
2097
2293
 
2098
- // POPOVER NO CONFLICT
2099
- // ===================
2294
+ // AFFIX NO CONFLICT
2295
+ // =================
2100
2296
 
2101
- $.fn.popover.noConflict = function () {
2102
- $.fn.popover = old
2297
+ $.fn.affix.noConflict = function () {
2298
+ $.fn.affix = old
2103
2299
  return this
2104
2300
  }
2105
2301
 
2106
- }(jQuery);
2107
2302
 
2303
+ // AFFIX DATA-API
2304
+ // ==============
2305
+
2306
+ $(window).on('load', function () {
2307
+ $('[data-spy="affix"]').each(function () {
2308
+ var $spy = $(this)
2309
+ var data = $spy.data()
2310
+
2311
+ data.offset = data.offset || {}
2312
+
2313
+ if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
2314
+ if (data.offsetTop != null) data.offset.top = data.offsetTop
2315
+
2316
+ Plugin.call($spy, data)
2317
+ })
2318
+ })
2319
+
2320
+ }(jQuery);