good_job 1.9.2 → 1.10.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +98 -1
  3. data/README.md +17 -0
  4. data/engine/app/assets/vendor/bootstrap/bootstrap.bundle.min.js +7 -0
  5. data/engine/app/assets/vendor/bootstrap/bootstrap.min.css +7 -0
  6. data/engine/app/controllers/good_job/assets_controller.rb +29 -0
  7. data/engine/app/controllers/good_job/dashboards_controller.rb +8 -6
  8. data/engine/app/controllers/good_job/jobs_controller.rb +9 -0
  9. data/engine/app/views/good_job/dashboards/index.html.erb +1 -1
  10. data/engine/app/views/layouts/good_job/base.html.erb +21 -12
  11. data/engine/app/views/shared/_chart.erb +3 -2
  12. data/engine/app/views/shared/_jobs_table.erb +13 -1
  13. data/engine/app/views/shared/icons/_check.html.erb +4 -0
  14. data/engine/app/views/shared/icons/_exclamation.html.erb +4 -0
  15. data/engine/app/views/shared/icons/_trash.html.erb +5 -0
  16. data/engine/config/routes.rb +10 -1
  17. data/lib/generators/good_job/install_generator.rb +5 -15
  18. data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +27 -0
  19. data/lib/generators/good_job/templates/{migration.rb.erb → update/migrations/01_create_good_jobs.rb} +3 -3
  20. data/lib/generators/good_job/templates/update/migrations/02_add_active_job_id_concurrency_key_cron_key_to_good_jobs.rb +15 -0
  21. data/lib/generators/good_job/templates/update/migrations/03_add_active_job_id_index_and_concurrency_key_index_to_good_jobs.rb +32 -0
  22. data/lib/generators/good_job/update_generator.rb +29 -0
  23. data/lib/good_job.rb +12 -8
  24. data/lib/good_job/adapter.rb +15 -11
  25. data/lib/good_job/configuration.rb +3 -3
  26. data/lib/good_job/current_execution.rb +0 -1
  27. data/lib/good_job/daemon.rb +6 -0
  28. data/lib/good_job/job.rb +38 -7
  29. data/lib/good_job/job_performer.rb +2 -2
  30. data/lib/good_job/lockable.rb +109 -56
  31. data/lib/good_job/log_subscriber.rb +15 -14
  32. data/lib/good_job/multi_scheduler.rb +9 -0
  33. data/lib/good_job/notifier.rb +4 -2
  34. data/lib/good_job/poller.rb +16 -7
  35. data/lib/good_job/scheduler.rb +12 -6
  36. data/lib/good_job/version.rb +1 -1
  37. metadata +28 -5
  38. data/engine/app/assets/vendor/bootstrap/bootstrap-native.js +0 -1662
  39. data/engine/app/assets/vendor/bootstrap/bootstrap.css +0 -10258
@@ -14,6 +14,7 @@ module GoodJob
14
14
 
15
15
  # @!macro notification_responder
16
16
  # Responds to the +$0.good_job+ notification.
17
+ # @param event [ActiveSupport::Notifications::Event]
17
18
  # @return [void]
18
19
  def create(event)
19
20
  # FIXME: This method does not match any good_job notifications.
@@ -24,7 +25,7 @@ module GoodJob
24
25
  end
25
26
  end
26
27
 
27
- # @macro notification_responder
28
+ # @!macro notification_responder
28
29
  def finished_timer_task(event)
29
30
  exception = event.payload[:error]
30
31
  return unless exception
@@ -34,7 +35,7 @@ module GoodJob
34
35
  end
35
36
  end
36
37
 
37
- # @macro notification_responder
38
+ # @!macro notification_responder
38
39
  def finished_job_task(event)
39
40
  exception = event.payload[:error]
40
41
  return unless exception
@@ -44,7 +45,7 @@ module GoodJob
44
45
  end
45
46
  end
46
47
 
47
- # @macro notification_responder
48
+ # @!macro notification_responder
48
49
  def scheduler_create_pool(event)
49
50
  max_threads = event.payload[:max_threads]
50
51
  performer_name = event.payload[:performer_name]
@@ -55,7 +56,7 @@ module GoodJob
55
56
  end
56
57
  end
57
58
 
58
- # @macro notification_responder
59
+ # @!macro notification_responder
59
60
  def scheduler_shutdown_start(event)
60
61
  process_id = event.payload[:process_id]
61
62
 
@@ -64,7 +65,7 @@ module GoodJob
64
65
  end
65
66
  end
66
67
 
67
- # @macro notification_responder
68
+ # @!macro notification_responder
68
69
  def scheduler_shutdown(event)
69
70
  process_id = event.payload[:process_id]
70
71
 
@@ -73,7 +74,7 @@ module GoodJob
73
74
  end
74
75
  end
75
76
 
76
- # @macro notification_responder
77
+ # @!macro notification_responder
77
78
  def scheduler_restart_pools(event)
78
79
  process_id = event.payload[:process_id]
79
80
 
@@ -82,7 +83,7 @@ module GoodJob
82
83
  end
83
84
  end
84
85
 
85
- # @macro notification_responder
86
+ # @!macro notification_responder
86
87
  def perform_job(event)
87
88
  good_job = event.payload[:good_job]
88
89
  process_id = event.payload[:process_id]
@@ -93,14 +94,14 @@ module GoodJob
93
94
  end
94
95
  end
95
96
 
96
- # @macro notification_responder
97
- def notifier_listen(_event)
97
+ # @!macro notification_responder
98
+ def notifier_listen(event) # rubocop:disable Lint/UnusedMethodArgument
98
99
  info do
99
100
  "Notifier subscribed with LISTEN"
100
101
  end
101
102
  end
102
103
 
103
- # @macro notification_responder
104
+ # @!macro notification_responder
104
105
  def notifier_notified(event)
105
106
  payload = event.payload[:payload]
106
107
 
@@ -109,7 +110,7 @@ module GoodJob
109
110
  end
110
111
  end
111
112
 
112
- # @macro notification_responder
113
+ # @!macro notification_responder
113
114
  def notifier_notify_error(event)
114
115
  error = event.payload[:error]
115
116
 
@@ -118,14 +119,14 @@ module GoodJob
118
119
  end
119
120
  end
120
121
 
121
- # @macro notification_responder
122
- def notifier_unlisten(_event)
122
+ # @!macro notification_responder
123
+ def notifier_unlisten(event) # rubocop:disable Lint/UnusedMethodArgument
123
124
  info do
124
125
  "Notifier unsubscribed with UNLISTEN"
125
126
  end
126
127
  end
127
128
 
128
- # @macro notification_responder
129
+ # @!macro notification_responder
129
130
  def cleanup_preserved_jobs(event)
130
131
  timestamp = event.payload[:timestamp]
131
132
  deleted_records_count = event.payload[:deleted_records_count]
@@ -4,31 +4,40 @@ module GoodJob
4
4
  # @return [Array<Scheduler>] List of the scheduler delegates
5
5
  attr_reader :schedulers
6
6
 
7
+ # @param schedulers [Array<Scheduler>]
7
8
  def initialize(schedulers)
8
9
  @schedulers = schedulers
9
10
  end
10
11
 
11
12
  # Delegates to {Scheduler#running?}.
13
+ # @return [Boolean, nil]
12
14
  def running?
13
15
  schedulers.all?(&:running?)
14
16
  end
15
17
 
16
18
  # Delegates to {Scheduler#shutdown?}.
19
+ # @return [Boolean, nil]
17
20
  def shutdown?
18
21
  schedulers.all?(&:shutdown?)
19
22
  end
20
23
 
21
24
  # Delegates to {Scheduler#shutdown}.
25
+ # @param timeout [Numeric, nil]
26
+ # @return [void]
22
27
  def shutdown(timeout: -1)
23
28
  GoodJob._shutdown_all(schedulers, timeout: timeout)
24
29
  end
25
30
 
26
31
  # Delegates to {Scheduler#restart}.
32
+ # @param timeout [Numeric, nil]
33
+ # @return [void]
27
34
  def restart(timeout: -1)
28
35
  GoodJob._shutdown_all(schedulers, :restart, timeout: timeout)
29
36
  end
30
37
 
31
38
  # Delegates to {Scheduler#create_thread}.
39
+ # @param state [Hash]
40
+ # @return [Boolean, nil]
32
41
  def create_thread(state = nil)
33
42
  results = []
34
43
 
@@ -30,7 +30,7 @@ module GoodJob # :nodoc:
30
30
  # @!attribute [r] instances
31
31
  # @!scope class
32
32
  # List of all instantiated Notifiers in the current process.
33
- # @return [Array<GoodJob::Adapter>]
33
+ # @return [Array<GoodJob::Notifier>, nil]
34
34
  cattr_reader :instances, default: [], instance_reader: false
35
35
 
36
36
  # Send a message via Postgres NOTIFY
@@ -64,17 +64,19 @@ module GoodJob # :nodoc:
64
64
  end
65
65
 
66
66
  # Tests whether the notifier is running.
67
+ # @!method running?
67
68
  # @return [true, false, nil]
68
69
  delegate :running?, to: :executor, allow_nil: true
69
70
 
70
71
  # Tests whether the scheduler is shutdown.
72
+ # @!method shutdown?
71
73
  # @return [true, false, nil]
72
74
  delegate :shutdown?, to: :executor, allow_nil: true
73
75
 
74
76
  # Shut down the notifier.
75
77
  # This stops the background LISTENing thread.
76
78
  # Use {#shutdown?} to determine whether threads have stopped.
77
- # @param timeout [nil, Numeric] Seconds to wait for active threads.
79
+ # @param timeout [Numeric, nil] Seconds to wait for active threads.
78
80
  # * +nil+, the scheduler will trigger a shutdown but not wait for it to complete.
79
81
  # * +-1+, the scheduler will wait until the shutdown is complete.
80
82
  # * +0+, the scheduler will immediately shutdown and stop any threads.
@@ -5,18 +5,20 @@ module GoodJob # :nodoc:
5
5
  # Pollers regularly wake up execution threads to check for new work.
6
6
  #
7
7
  class Poller
8
+ TIMEOUT_INTERVAL = 5
9
+
8
10
  # Defaults for instance of Concurrent::TimerTask.
9
11
  # The timer controls how and when sleeping threads check for new work.
10
12
  DEFAULT_TIMER_OPTIONS = {
11
13
  execution_interval: Configuration::DEFAULT_POLL_INTERVAL,
12
- timeout_interval: 1,
14
+ timeout_interval: TIMEOUT_INTERVAL,
13
15
  run_now: true,
14
16
  }.freeze
15
17
 
16
18
  # @!attribute [r] instances
17
19
  # @!scope class
18
20
  # List of all instantiated Pollers in the current process.
19
- # @return [Array<GoodJob::Poller>]
21
+ # @return [Array<GoodJob::Poller>, nil]
20
22
  cattr_reader :instances, default: [], instance_reader: false
21
23
 
22
24
  # Creates GoodJob::Poller from a GoodJob::Configuration instance.
@@ -30,8 +32,8 @@ module GoodJob # :nodoc:
30
32
  # @return [Array<#call, Array(Object, Symbol)>]
31
33
  attr_reader :recipients
32
34
 
33
- # @param recipients [Array<#call, Array(Object, Symbol)>]
34
- # @param poll_interval [Hash] number of seconds between polls
35
+ # @param recipients [Array<Proc, #call, Array(Object, Symbol)>]
36
+ # @param poll_interval [Integer, nil] number of seconds between polls
35
37
  def initialize(*recipients, poll_interval: nil)
36
38
  @recipients = Concurrent::Array.new(recipients)
37
39
 
@@ -49,9 +51,11 @@ module GoodJob # :nodoc:
49
51
 
50
52
  # Tests whether the timer is shutdown.
51
53
  # @return [true, false, nil]
52
- delegate :shutdown?, to: :timer, allow_nil: true
54
+ def shutdown?
55
+ timer ? timer.shutdown? : true
56
+ end
53
57
 
54
- # Shut down the notifier.
58
+ # Shut down the poller.
55
59
  # Use {#shutdown?} to determine whether threads have stopped.
56
60
  # @param timeout [nil, Numeric] Seconds to wait for active threads.
57
61
  # * +nil+, the scheduler will trigger a shutdown but not wait for it to complete.
@@ -72,7 +76,7 @@ module GoodJob # :nodoc:
72
76
 
73
77
  # Restart the poller.
74
78
  # When shutdown, start; or shutdown and start.
75
- # @param timeout [nil, Numeric] Seconds to wait; shares same values as {#shutdown}.
79
+ # @param timeout [Numeric, nil] Seconds to wait; shares same values as {#shutdown}.
76
80
  # @return [void]
77
81
  def restart(timeout: -1)
78
82
  shutdown(timeout: timeout) if running?
@@ -81,6 +85,9 @@ module GoodJob # :nodoc:
81
85
 
82
86
  # Invoked on completion of TimerTask task.
83
87
  # @!visibility private
88
+ # @param time [Integer]
89
+ # @param executed_task [Object, nil]
90
+ # @param thread_error [Exception, nil]
84
91
  # @return [void]
85
92
  def timer_observer(time, executed_task, thread_error)
86
93
  GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
@@ -89,8 +96,10 @@ module GoodJob # :nodoc:
89
96
 
90
97
  private
91
98
 
99
+ # @return [Concurrent::TimerTask]
92
100
  attr_reader :timer
93
101
 
102
+ # @return [void]
94
103
  def create_timer
95
104
  return if @timer_options[:execution_interval] <= 0
96
105
 
@@ -30,7 +30,7 @@ module GoodJob # :nodoc:
30
30
  # @!attribute [r] instances
31
31
  # @!scope class
32
32
  # List of all instantiated Schedulers in the current process.
33
- # @return [Array<GoodJob::Scheduler>]
33
+ # @return [Array<GoodJob::Scheduler>, nil]
34
34
  cattr_reader :instances, default: [], instance_reader: false
35
35
 
36
36
  # Creates GoodJob::Scheduler(s) and Performers from a GoodJob::Configuration instance.
@@ -82,17 +82,17 @@ module GoodJob # :nodoc:
82
82
  end
83
83
 
84
84
  # Tests whether the scheduler is running.
85
- # @return [true, false, nil]
85
+ # @return [Boolean, nil]
86
86
  delegate :running?, to: :executor, allow_nil: true
87
87
 
88
88
  # Tests whether the scheduler is shutdown.
89
- # @return [true, false, nil]
89
+ # @return [Boolean, nil]
90
90
  delegate :shutdown?, to: :executor, allow_nil: true
91
91
 
92
92
  # Shut down the scheduler.
93
93
  # This stops all threads in the thread pool.
94
94
  # Use {#shutdown?} to determine whether threads have stopped.
95
- # @param timeout [nil, Numeric] Seconds to wait for actively executing jobs to finish
95
+ # @param timeout [Numeric, nil] Seconds to wait for actively executing jobs to finish
96
96
  # * +nil+, the scheduler will trigger a shutdown but not wait for it to complete.
97
97
  # * +-1+, the scheduler will wait until the shutdown is complete.
98
98
  # * +0+, the scheduler will immediately shutdown and stop any active tasks.
@@ -128,8 +128,8 @@ module GoodJob # :nodoc:
128
128
  end
129
129
 
130
130
  # Wakes a thread to allow the performer to execute a task.
131
- # @param state [nil, Object] Contextual information for the performer. See {JobPerformer#next?}.
132
- # @return [nil, Boolean] Whether work was started.
131
+ # @param state [Hash, nil] Contextual information for the performer. See {JobPerformer#next?}.
132
+ # @return [Boolean, nil] Whether work was started.
133
133
  #
134
134
  # * +nil+ if the scheduler is unable to take new work, for example if the thread pool is shut down or at capacity.
135
135
  # * +true+ if the performer started executing work.
@@ -215,6 +215,7 @@ module GoodJob # :nodoc:
215
215
 
216
216
  attr_reader :performer, :executor, :timer_set
217
217
 
218
+ # @return [void]
218
219
  def create_executor
219
220
  instrument("scheduler_create_pool", { performer_name: performer.name, max_threads: @executor_options[:max_threads] }) do
220
221
  @timer_set = TimerSet.new
@@ -222,6 +223,8 @@ module GoodJob # :nodoc:
222
223
  end
223
224
  end
224
225
 
226
+ # @param delay [Integer]
227
+ # @return [void]
225
228
  def create_task(delay = 0)
226
229
  future = Concurrent::ScheduledTask.new(delay, args: [performer], executor: executor, timer_set: timer_set) do |thr_performer|
227
230
  Rails.application.executor.wrap do
@@ -232,6 +235,9 @@ module GoodJob # :nodoc:
232
235
  future.execute
233
236
  end
234
237
 
238
+ # @param name [String]
239
+ # @param payload [Hash]
240
+ # @return [void]
235
241
  def instrument(name, payload = {}, &block)
236
242
  payload = payload.reverse_merge({
237
243
  scheduler: self,
@@ -1,4 +1,4 @@
1
1
  module GoodJob
2
2
  # GoodJob gem version.
3
- VERSION = '1.9.2'.freeze
3
+ VERSION = '1.10.0'.freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: good_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.2
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-05-10 00:00:00.000000000 Z
11
+ date: 2021-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '2.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: benchmark-ips
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 2.8.4
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 2.8.4
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: capybara
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -319,25 +333,34 @@ files:
319
333
  - LICENSE.txt
320
334
  - README.md
321
335
  - engine/app/assets/style.css
322
- - engine/app/assets/vendor/bootstrap/bootstrap-native.js
323
- - engine/app/assets/vendor/bootstrap/bootstrap.css
336
+ - engine/app/assets/vendor/bootstrap/bootstrap.bundle.min.js
337
+ - engine/app/assets/vendor/bootstrap/bootstrap.min.css
324
338
  - engine/app/assets/vendor/chartist/chartist.css
325
339
  - engine/app/assets/vendor/chartist/chartist.js
326
340
  - engine/app/controllers/good_job/active_jobs_controller.rb
341
+ - engine/app/controllers/good_job/assets_controller.rb
327
342
  - engine/app/controllers/good_job/base_controller.rb
328
343
  - engine/app/controllers/good_job/dashboards_controller.rb
344
+ - engine/app/controllers/good_job/jobs_controller.rb
329
345
  - engine/app/helpers/good_job/application_helper.rb
330
346
  - engine/app/views/good_job/active_jobs/show.html.erb
331
347
  - engine/app/views/good_job/dashboards/index.html.erb
332
348
  - engine/app/views/layouts/good_job/base.html.erb
333
349
  - engine/app/views/shared/_chart.erb
334
350
  - engine/app/views/shared/_jobs_table.erb
351
+ - engine/app/views/shared/icons/_check.html.erb
352
+ - engine/app/views/shared/icons/_exclamation.html.erb
353
+ - engine/app/views/shared/icons/_trash.html.erb
335
354
  - engine/config/routes.rb
336
355
  - engine/lib/good_job/engine.rb
337
356
  - exe/good_job
338
357
  - lib/active_job/queue_adapters/good_job_adapter.rb
339
358
  - lib/generators/good_job/install_generator.rb
340
- - lib/generators/good_job/templates/migration.rb.erb
359
+ - lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb
360
+ - lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb
361
+ - lib/generators/good_job/templates/update/migrations/02_add_active_job_id_concurrency_key_cron_key_to_good_jobs.rb
362
+ - lib/generators/good_job/templates/update/migrations/03_add_active_job_id_index_and_concurrency_key_index_to_good_jobs.rb
363
+ - lib/generators/good_job/update_generator.rb
341
364
  - lib/good_job.rb
342
365
  - lib/good_job/adapter.rb
343
366
  - lib/good_job/cli.rb
@@ -1,1662 +0,0 @@
1
- /*!
2
- * Native JavaScript for Bootstrap v3.0.10 (https://thednp.github.io/bootstrap.native/)
3
- * Copyright 2015-2020 © dnp_theme
4
- * Licensed under MIT (https://github.com/thednp/bootstrap.native/blob/master/LICENSE)
5
- */
6
- (function (global, factory) {
7
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
8
- typeof define === 'function' && define.amd ? define(factory) :
9
- (global = global || self, global.BSN = factory());
10
- }(this, (function () { 'use strict';
11
-
12
- var transitionEndEvent = 'webkitTransition' in document.head.style ? 'webkitTransitionEnd' : 'transitionend';
13
-
14
- var supportTransition = 'webkitTransition' in document.head.style || 'transition' in document.head.style;
15
-
16
- var transitionDuration = 'webkitTransition' in document.head.style ? 'webkitTransitionDuration' : 'transitionDuration';
17
-
18
- function getElementTransitionDuration(element) {
19
- var duration = supportTransition ? parseFloat(getComputedStyle(element)[transitionDuration]) : 0;
20
- duration = typeof duration === 'number' && !isNaN(duration) ? duration * 1000 : 0;
21
- return duration;
22
- }
23
-
24
- function emulateTransitionEnd(element,handler){
25
- var called = 0, duration = getElementTransitionDuration(element);
26
- duration ? element.addEventListener( transitionEndEvent, function transitionEndWrapper(e){
27
- !called && handler(e), called = 1;
28
- element.removeEventListener( transitionEndEvent, transitionEndWrapper);
29
- })
30
- : setTimeout(function() { !called && handler(), called = 1; }, 17);
31
- }
32
-
33
- function queryElement(selector, parent) {
34
- var lookUp = parent && parent instanceof Element ? parent : document;
35
- return selector instanceof Element ? selector : lookUp.querySelector(selector);
36
- }
37
-
38
- function bootstrapCustomEvent(eventName, componentName, related) {
39
- var OriginalCustomEvent = new CustomEvent( eventName + '.bs.' + componentName, {cancelable: true});
40
- OriginalCustomEvent.relatedTarget = related;
41
- return OriginalCustomEvent;
42
- }
43
-
44
- function dispatchCustomEvent(customEvent){
45
- this && this.dispatchEvent(customEvent);
46
- }
47
-
48
- function Alert(element) {
49
- var self = this,
50
- alert,
51
- closeCustomEvent = bootstrapCustomEvent('close','alert'),
52
- closedCustomEvent = bootstrapCustomEvent('closed','alert');
53
- function triggerHandler() {
54
- alert.classList.contains('fade') ? emulateTransitionEnd(alert,transitionEndHandler) : transitionEndHandler();
55
- }
56
- function toggleEvents(action){
57
- action = action ? 'addEventListener' : 'removeEventListener';
58
- element[action]('click',clickHandler,false);
59
- }
60
- function clickHandler(e) {
61
- alert = e && e.target.closest(".alert");
62
- element = queryElement('[data-dismiss="alert"]',alert);
63
- element && alert && (element === e.target || element.contains(e.target)) && self.close();
64
- }
65
- function transitionEndHandler() {
66
- toggleEvents();
67
- alert.parentNode.removeChild(alert);
68
- dispatchCustomEvent.call(alert,closedCustomEvent);
69
- }
70
- self.close = function () {
71
- if ( alert && element && alert.classList.contains('show') ) {
72
- dispatchCustomEvent.call(alert,closeCustomEvent);
73
- if ( closeCustomEvent.defaultPrevented ) { return; }
74
- self.dispose();
75
- alert.classList.remove('show');
76
- triggerHandler();
77
- }
78
- };
79
- self.dispose = function () {
80
- toggleEvents();
81
- delete element.Alert;
82
- };
83
- element = queryElement(element);
84
- alert = element.closest('.alert');
85
- element.Alert && element.Alert.dispose();
86
- if ( !element.Alert ) {
87
- toggleEvents(1);
88
- }
89
- self.element = element;
90
- element.Alert = self;
91
- }
92
-
93
- function Button(element) {
94
- var self = this, labels,
95
- changeCustomEvent = bootstrapCustomEvent('change', 'button');
96
- function toggle(e) {
97
- var input,
98
- label = e.target.tagName === 'LABEL' ? e.target
99
- : e.target.closest('LABEL') ? e.target.closest('LABEL') : null;
100
- input = label && label.getElementsByTagName('INPUT')[0];
101
- if ( !input ) { return; }
102
- dispatchCustomEvent.call(input, changeCustomEvent);
103
- dispatchCustomEvent.call(element, changeCustomEvent);
104
- if ( input.type === 'checkbox' ) {
105
- if ( changeCustomEvent.defaultPrevented ) { return; }
106
- if ( !input.checked ) {
107
- label.classList.add('active');
108
- input.getAttribute('checked');
109
- input.setAttribute('checked','checked');
110
- input.checked = true;
111
- } else {
112
- label.classList.remove('active');
113
- input.getAttribute('checked');
114
- input.removeAttribute('checked');
115
- input.checked = false;
116
- }
117
- if (!element.toggled) {
118
- element.toggled = true;
119
- }
120
- }
121
- if ( input.type === 'radio' && !element.toggled ) {
122
- if ( changeCustomEvent.defaultPrevented ) { return; }
123
- if ( !input.checked || (e.screenX === 0 && e.screenY == 0) ) {
124
- label.classList.add('active');
125
- label.classList.add('focus');
126
- input.setAttribute('checked','checked');
127
- input.checked = true;
128
- element.toggled = true;
129
- Array.from(labels).map(function (otherLabel){
130
- var otherInput = otherLabel.getElementsByTagName('INPUT')[0];
131
- if ( otherLabel !== label && otherLabel.classList.contains('active') ) {
132
- dispatchCustomEvent.call(otherInput, changeCustomEvent);
133
- otherLabel.classList.remove('active');
134
- otherInput.removeAttribute('checked');
135
- otherInput.checked = false;
136
- }
137
- });
138
- }
139
- }
140
- setTimeout( function () { element.toggled = false; }, 50 );
141
- }
142
- function keyHandler(e) {
143
- var key = e.which || e.keyCode;
144
- key === 32 && e.target === document.activeElement && toggle(e);
145
- }
146
- function preventScroll(e) {
147
- var key = e.which || e.keyCode;
148
- key === 32 && e.preventDefault();
149
- }
150
- function focusToggle(e) {
151
- if (e.target.tagName === 'INPUT' ) {
152
- var action = e.type === 'focusin' ? 'add' : 'remove';
153
- e.target.closest('.btn').classList[action]('focus');
154
- }
155
- }
156
- function toggleEvents(action) {
157
- action = action ? 'addEventListener' : 'removeEventListener';
158
- element[action]('click',toggle,false );
159
- element[action]('keyup',keyHandler,false), element[action]('keydown',preventScroll,false);
160
- element[action]('focusin',focusToggle,false), element[action]('focusout',focusToggle,false);
161
- }
162
- self.dispose = function () {
163
- toggleEvents();
164
- delete element.Button;
165
- };
166
- element = queryElement(element);
167
- element.Button && element.Button.dispose();
168
- labels = element.getElementsByClassName('btn');
169
- if (!labels.length) { return; }
170
- if ( !element.Button ) {
171
- toggleEvents(1);
172
- }
173
- element.toggled = false;
174
- element.Button = self;
175
- Array.from(labels).map(function (btn){
176
- !btn.classList.contains('active')
177
- && queryElement('input:checked',btn)
178
- && btn.classList.add('active');
179
- btn.classList.contains('active')
180
- && !queryElement('input:checked',btn)
181
- && btn.classList.remove('active');
182
- });
183
- }
184
-
185
- var mouseHoverEvents = ('onmouseleave' in document) ? [ 'mouseenter', 'mouseleave'] : [ 'mouseover', 'mouseout' ];
186
-
187
- var supportPassive = (function () {
188
- var result = false;
189
- try {
190
- var opts = Object.defineProperty({}, 'passive', {
191
- get: function() {
192
- result = true;
193
- }
194
- });
195
- document.addEventListener('DOMContentLoaded', function wrap(){
196
- document.removeEventListener('DOMContentLoaded', wrap, opts);
197
- }, opts);
198
- } catch (e) {}
199
- return result;
200
- })();
201
-
202
- var passiveHandler = supportPassive ? { passive: true } : false;
203
-
204
- function isElementInScrollRange(element) {
205
- var bcr = element.getBoundingClientRect(),
206
- viewportHeight = window.innerHeight || document.documentElement.clientHeight;
207
- return bcr.top <= viewportHeight && bcr.bottom >= 0;
208
- }
209
-
210
- function Carousel (element,options) {
211
- options = options || {};
212
- var self = this,
213
- vars, ops,
214
- slideCustomEvent, slidCustomEvent,
215
- slides, leftArrow, rightArrow, indicator, indicators;
216
- function pauseHandler() {
217
- if ( ops.interval !==false && !element.classList.contains('paused') ) {
218
- element.classList.add('paused');
219
- !vars.isSliding && ( clearInterval(vars.timer), vars.timer = null );
220
- }
221
- }
222
- function resumeHandler() {
223
- if ( ops.interval !== false && element.classList.contains('paused') ) {
224
- element.classList.remove('paused');
225
- !vars.isSliding && ( clearInterval(vars.timer), vars.timer = null );
226
- !vars.isSliding && self.cycle();
227
- }
228
- }
229
- function indicatorHandler(e) {
230
- e.preventDefault();
231
- if (vars.isSliding) { return; }
232
- var eventTarget = e.target;
233
- if ( eventTarget && !eventTarget.classList.contains('active') && eventTarget.getAttribute('data-slide-to') ) {
234
- vars.index = parseInt( eventTarget.getAttribute('data-slide-to'));
235
- } else { return false; }
236
- self.slideTo( vars.index );
237
- }
238
- function controlsHandler(e) {
239
- e.preventDefault();
240
- if (vars.isSliding) { return; }
241
- var eventTarget = e.currentTarget || e.srcElement;
242
- if ( eventTarget === rightArrow ) {
243
- vars.index++;
244
- } else if ( eventTarget === leftArrow ) {
245
- vars.index--;
246
- }
247
- self.slideTo( vars.index );
248
- }
249
- function keyHandler(ref) {
250
- var which = ref.which;
251
- if (vars.isSliding) { return; }
252
- switch (which) {
253
- case 39:
254
- vars.index++;
255
- break;
256
- case 37:
257
- vars.index--;
258
- break;
259
- default: return;
260
- }
261
- self.slideTo( vars.index );
262
- }
263
- function toggleEvents(action) {
264
- action = action ? 'addEventListener' : 'removeEventListener';
265
- if ( ops.pause && ops.interval ) {
266
- element[action]( mouseHoverEvents[0], pauseHandler, false );
267
- element[action]( mouseHoverEvents[1], resumeHandler, false );
268
- element[action]( 'touchstart', pauseHandler, passiveHandler );
269
- element[action]( 'touchend', resumeHandler, passiveHandler );
270
- }
271
- ops.touch && slides.length > 1 && element[action]( 'touchstart', touchDownHandler, passiveHandler );
272
- rightArrow && rightArrow[action]( 'click', controlsHandler,false );
273
- leftArrow && leftArrow[action]( 'click', controlsHandler,false );
274
- indicator && indicator[action]( 'click', indicatorHandler,false );
275
- ops.keyboard && window[action]( 'keydown', keyHandler,false );
276
- }
277
- function toggleTouchEvents(action) {
278
- action = action ? 'addEventListener' : 'removeEventListener';
279
- element[action]( 'touchmove', touchMoveHandler, passiveHandler );
280
- element[action]( 'touchend', touchEndHandler, passiveHandler );
281
- }
282
- function touchDownHandler(e) {
283
- if ( vars.isTouch ) { return; }
284
- vars.touchPosition.startX = e.changedTouches[0].pageX;
285
- if ( element.contains(e.target) ) {
286
- vars.isTouch = true;
287
- toggleTouchEvents(1);
288
- }
289
- }
290
- function touchMoveHandler(e) {
291
- if ( !vars.isTouch ) { e.preventDefault(); return; }
292
- vars.touchPosition.currentX = e.changedTouches[0].pageX;
293
- if ( e.type === 'touchmove' && e.changedTouches.length > 1 ) {
294
- e.preventDefault();
295
- return false;
296
- }
297
- }
298
- function touchEndHandler (e) {
299
- if ( !vars.isTouch || vars.isSliding ) { return }
300
- vars.touchPosition.endX = vars.touchPosition.currentX || e.changedTouches[0].pageX;
301
- if ( vars.isTouch ) {
302
- if ( (!element.contains(e.target) || !element.contains(e.relatedTarget) )
303
- && Math.abs(vars.touchPosition.startX - vars.touchPosition.endX) < 75 ) {
304
- return false;
305
- } else {
306
- if ( vars.touchPosition.currentX < vars.touchPosition.startX ) {
307
- vars.index++;
308
- } else if ( vars.touchPosition.currentX > vars.touchPosition.startX ) {
309
- vars.index--;
310
- }
311
- vars.isTouch = false;
312
- self.slideTo(vars.index);
313
- }
314
- toggleTouchEvents();
315
- }
316
- }
317
- function setActivePage(pageIndex) {
318
- Array.from(indicators).map(function (x){x.classList.remove('active');});
319
- indicators[pageIndex] && indicators[pageIndex].classList.add('active');
320
- }
321
- function transitionEndHandler(e){
322
- if (vars.touchPosition){
323
- var next = vars.index,
324
- timeout = e && e.target !== slides[next] ? e.elapsedTime*1000+100 : 20,
325
- activeItem = self.getActiveIndex(),
326
- orientation = vars.direction === 'left' ? 'next' : 'prev';
327
- vars.isSliding && setTimeout(function () {
328
- if (vars.touchPosition){
329
- vars.isSliding = false;
330
- slides[next].classList.add('active');
331
- slides[activeItem].classList.remove('active');
332
- slides[next].classList.remove(("carousel-item-" + orientation));
333
- slides[next].classList.remove(("carousel-item-" + (vars.direction)));
334
- slides[activeItem].classList.remove(("carousel-item-" + (vars.direction)));
335
- dispatchCustomEvent.call(element, slidCustomEvent);
336
- if ( !document.hidden && ops.interval && !element.classList.contains('paused') ) {
337
- self.cycle();
338
- }
339
- }
340
- }, timeout);
341
- }
342
- }
343
- self.cycle = function () {
344
- if (vars.timer) {
345
- clearInterval(vars.timer);
346
- vars.timer = null;
347
- }
348
- vars.timer = setInterval(function () {
349
- var idx = vars.index || self.getActiveIndex();
350
- isElementInScrollRange(element) && (idx++, self.slideTo( idx ) );
351
- }, ops.interval);
352
- };
353
- self.slideTo = function (next) {
354
- if (vars.isSliding) { return; }
355
- var activeItem = self.getActiveIndex(), orientation;
356
- if ( activeItem === next ) {
357
- return;
358
- } else if ( (activeItem < next ) || (activeItem === 0 && next === slides.length -1 ) ) {
359
- vars.direction = 'left';
360
- } else if ( (activeItem > next) || (activeItem === slides.length - 1 && next === 0 ) ) {
361
- vars.direction = 'right';
362
- }
363
- if ( next < 0 ) { next = slides.length - 1; }
364
- else if ( next >= slides.length ){ next = 0; }
365
- orientation = vars.direction === 'left' ? 'next' : 'prev';
366
- slideCustomEvent = bootstrapCustomEvent('slide', 'carousel', slides[next]);
367
- slidCustomEvent = bootstrapCustomEvent('slid', 'carousel', slides[next]);
368
- dispatchCustomEvent.call(element, slideCustomEvent);
369
- if (slideCustomEvent.defaultPrevented) { return; }
370
- vars.index = next;
371
- vars.isSliding = true;
372
- clearInterval(vars.timer);
373
- vars.timer = null;
374
- setActivePage( next );
375
- if ( getElementTransitionDuration(slides[next]) && element.classList.contains('slide') ) {
376
- slides[next].classList.add(("carousel-item-" + orientation));
377
- slides[next].offsetWidth;
378
- slides[next].classList.add(("carousel-item-" + (vars.direction)));
379
- slides[activeItem].classList.add(("carousel-item-" + (vars.direction)));
380
- emulateTransitionEnd(slides[next], transitionEndHandler);
381
- } else {
382
- slides[next].classList.add('active');
383
- slides[next].offsetWidth;
384
- slides[activeItem].classList.remove('active');
385
- setTimeout(function () {
386
- vars.isSliding = false;
387
- if ( ops.interval && element && !element.classList.contains('paused') ) {
388
- self.cycle();
389
- }
390
- dispatchCustomEvent.call(element, slidCustomEvent);
391
- }, 100 );
392
- }
393
- };
394
- self.getActiveIndex = function () { return Array.from(slides).indexOf(element.getElementsByClassName('carousel-item active')[0]) || 0; };
395
- self.dispose = function () {
396
- var itemClasses = ['left','right','prev','next'];
397
- Array.from(slides).map(function (slide,idx) {
398
- slide.classList.contains('active') && setActivePage( idx );
399
- itemClasses.map(function (cls) { return slide.classList.remove(("carousel-item-" + cls)); });
400
- });
401
- clearInterval(vars.timer);
402
- toggleEvents();
403
- vars = {};
404
- ops = {};
405
- delete element.Carousel;
406
- };
407
- element = queryElement( element );
408
- element.Carousel && element.Carousel.dispose();
409
- slides = element.getElementsByClassName('carousel-item');
410
- leftArrow = element.getElementsByClassName('carousel-control-prev')[0];
411
- rightArrow = element.getElementsByClassName('carousel-control-next')[0];
412
- indicator = element.getElementsByClassName('carousel-indicators')[0];
413
- indicators = indicator && indicator.getElementsByTagName( "LI" ) || [];
414
- if (slides.length < 2) { return }
415
- var
416
- intervalAttribute = element.getAttribute('data-interval'),
417
- intervalData = intervalAttribute === 'false' ? 0 : parseInt(intervalAttribute),
418
- touchData = element.getAttribute('data-touch') === 'false' ? 0 : 1,
419
- pauseData = element.getAttribute('data-pause') === 'hover' || false,
420
- keyboardData = element.getAttribute('data-keyboard') === 'true' || false,
421
- intervalOption = options.interval,
422
- touchOption = options.touch;
423
- ops = {};
424
- ops.keyboard = options.keyboard === true || keyboardData;
425
- ops.pause = (options.pause === 'hover' || pauseData) ? 'hover' : false;
426
- ops.touch = touchOption || touchData;
427
- ops.interval = typeof intervalOption === 'number' ? intervalOption
428
- : intervalOption === false || intervalData === 0 || intervalData === false ? 0
429
- : isNaN(intervalData) ? 5000
430
- : intervalData;
431
- if (self.getActiveIndex()<0) {
432
- slides.length && slides[0].classList.add('active');
433
- indicators.length && setActivePage(0);
434
- }
435
- vars = {};
436
- vars.direction = 'left';
437
- vars.index = 0;
438
- vars.timer = null;
439
- vars.isSliding = false;
440
- vars.isTouch = false;
441
- vars.touchPosition = {
442
- startX : 0,
443
- currentX : 0,
444
- endX : 0
445
- };
446
- toggleEvents(1);
447
- if ( ops.interval ){ self.cycle(); }
448
- element.Carousel = self;
449
- }
450
-
451
- function Collapse(element,options) {
452
- options = options || {};
453
- var self = this;
454
- var accordion = null,
455
- collapse = null,
456
- activeCollapse,
457
- activeElement,
458
- showCustomEvent,
459
- shownCustomEvent,
460
- hideCustomEvent,
461
- hiddenCustomEvent;
462
- function openAction(collapseElement, toggle) {
463
- dispatchCustomEvent.call(collapseElement, showCustomEvent);
464
- if ( showCustomEvent.defaultPrevented ) { return; }
465
- collapseElement.isAnimating = true;
466
- collapseElement.classList.add('collapsing');
467
- collapseElement.classList.remove('collapse');
468
- collapseElement.style.height = (collapseElement.scrollHeight) + "px";
469
- emulateTransitionEnd(collapseElement, function () {
470
- collapseElement.isAnimating = false;
471
- collapseElement.setAttribute('aria-expanded','true');
472
- toggle.setAttribute('aria-expanded','true');
473
- collapseElement.classList.remove('collapsing');
474
- collapseElement.classList.add('collapse');
475
- collapseElement.classList.add('show');
476
- collapseElement.style.height = '';
477
- dispatchCustomEvent.call(collapseElement, shownCustomEvent);
478
- });
479
- }
480
- function closeAction(collapseElement, toggle) {
481
- dispatchCustomEvent.call(collapseElement, hideCustomEvent);
482
- if ( hideCustomEvent.defaultPrevented ) { return; }
483
- collapseElement.isAnimating = true;
484
- collapseElement.style.height = (collapseElement.scrollHeight) + "px";
485
- collapseElement.classList.remove('collapse');
486
- collapseElement.classList.remove('show');
487
- collapseElement.classList.add('collapsing');
488
- collapseElement.offsetWidth;
489
- collapseElement.style.height = '0px';
490
- emulateTransitionEnd(collapseElement, function () {
491
- collapseElement.isAnimating = false;
492
- collapseElement.setAttribute('aria-expanded','false');
493
- toggle.setAttribute('aria-expanded','false');
494
- collapseElement.classList.remove('collapsing');
495
- collapseElement.classList.add('collapse');
496
- collapseElement.style.height = '';
497
- dispatchCustomEvent.call(collapseElement, hiddenCustomEvent);
498
- });
499
- }
500
- self.toggle = function (e) {
501
- if (e && e.target.tagName === 'A' || element.tagName === 'A') {e.preventDefault();}
502
- if (element.contains(e.target) || e.target === element) {
503
- if (!collapse.classList.contains('show')) { self.show(); }
504
- else { self.hide(); }
505
- }
506
- };
507
- self.hide = function () {
508
- if ( collapse.isAnimating ) { return; }
509
- closeAction(collapse,element);
510
- element.classList.add('collapsed');
511
- };
512
- self.show = function () {
513
- if ( accordion ) {
514
- activeCollapse = accordion.getElementsByClassName("collapse show")[0];
515
- activeElement = activeCollapse && (queryElement(("[data-target=\"#" + (activeCollapse.id) + "\"]"),accordion)
516
- || queryElement(("[href=\"#" + (activeCollapse.id) + "\"]"),accordion) );
517
- }
518
- if ( !collapse.isAnimating ) {
519
- if ( activeElement && activeCollapse !== collapse ) {
520
- closeAction(activeCollapse,activeElement);
521
- activeElement.classList.add('collapsed');
522
- }
523
- openAction(collapse,element);
524
- element.classList.remove('collapsed');
525
- }
526
- };
527
- self.dispose = function () {
528
- element.removeEventListener('click',self.toggle,false);
529
- delete element.Collapse;
530
- };
531
- element = queryElement(element);
532
- element.Collapse && element.Collapse.dispose();
533
- var accordionData = element.getAttribute('data-parent');
534
- showCustomEvent = bootstrapCustomEvent('show', 'collapse');
535
- shownCustomEvent = bootstrapCustomEvent('shown', 'collapse');
536
- hideCustomEvent = bootstrapCustomEvent('hide', 'collapse');
537
- hiddenCustomEvent = bootstrapCustomEvent('hidden', 'collapse');
538
- collapse = queryElement(options.target || element.getAttribute('data-target') || element.getAttribute('href'));
539
- collapse.isAnimating = false;
540
- accordion = element.closest(options.parent || accordionData);
541
- if ( !element.Collapse ) {
542
- element.addEventListener('click',self.toggle,false);
543
- }
544
- element.Collapse = self;
545
- }
546
-
547
- function setFocus (element){
548
- element.focus ? element.focus() : element.setActive();
549
- }
550
-
551
- function Dropdown(element,option) {
552
- var self = this,
553
- showCustomEvent,
554
- shownCustomEvent,
555
- hideCustomEvent,
556
- hiddenCustomEvent,
557
- relatedTarget = null,
558
- parent, menu, menuItems = [],
559
- persist;
560
- function preventEmptyAnchor(anchor) {
561
- (anchor.href && anchor.href.slice(-1) === '#' || anchor.parentNode && anchor.parentNode.href
562
- && anchor.parentNode.href.slice(-1) === '#') && this.preventDefault();
563
- }
564
- function toggleDismiss() {
565
- var action = element.open ? 'addEventListener' : 'removeEventListener';
566
- document[action]('click',dismissHandler,false);
567
- document[action]('keydown',preventScroll,false);
568
- document[action]('keyup',keyHandler,false);
569
- document[action]('focus',dismissHandler,false);
570
- }
571
- function dismissHandler(e) {
572
- var eventTarget = e.target,
573
- hasData = eventTarget && (eventTarget.getAttribute('data-toggle')
574
- || eventTarget.parentNode && eventTarget.parentNode.getAttribute
575
- && eventTarget.parentNode.getAttribute('data-toggle'));
576
- if ( e.type === 'focus' && (eventTarget === element || eventTarget === menu || menu.contains(eventTarget) ) ) {
577
- return;
578
- }
579
- if ( (eventTarget === menu || menu.contains(eventTarget)) && (persist || hasData) ) { return; }
580
- else {
581
- relatedTarget = eventTarget === element || element.contains(eventTarget) ? element : null;
582
- self.hide();
583
- }
584
- preventEmptyAnchor.call(e,eventTarget);
585
- }
586
- function clickHandler(e) {
587
- relatedTarget = element;
588
- self.show();
589
- preventEmptyAnchor.call(e,e.target);
590
- }
591
- function preventScroll(e) {
592
- var key = e.which || e.keyCode;
593
- if( key === 38 || key === 40 ) { e.preventDefault(); }
594
- }
595
- function keyHandler(e) {
596
- var key = e.which || e.keyCode,
597
- activeItem = document.activeElement,
598
- isSameElement = activeItem === element,
599
- isInsideMenu = menu.contains(activeItem),
600
- isMenuItem = activeItem.parentNode === menu || activeItem.parentNode.parentNode === menu,
601
- idx = menuItems.indexOf(activeItem);
602
- if ( isMenuItem ) {
603
- idx = isSameElement ? 0
604
- : key === 38 ? (idx>1?idx-1:0)
605
- : key === 40 ? (idx<menuItems.length-1?idx+1:idx) : idx;
606
- menuItems[idx] && setFocus(menuItems[idx]);
607
- }
608
- if ( (menuItems.length && isMenuItem
609
- || !menuItems.length && (isInsideMenu || isSameElement)
610
- || !isInsideMenu )
611
- && element.open && key === 27
612
- ) {
613
- self.toggle();
614
- relatedTarget = null;
615
- }
616
- }
617
- self.show = function () {
618
- showCustomEvent = bootstrapCustomEvent('show', 'dropdown', relatedTarget);
619
- dispatchCustomEvent.call(parent, showCustomEvent);
620
- if ( showCustomEvent.defaultPrevented ) { return; }
621
- menu.classList.add('show');
622
- parent.classList.add('show');
623
- element.setAttribute('aria-expanded',true);
624
- element.open = true;
625
- element.removeEventListener('click',clickHandler,false);
626
- setTimeout(function () {
627
- setFocus( menu.getElementsByTagName('INPUT')[0] || element );
628
- toggleDismiss();
629
- shownCustomEvent = bootstrapCustomEvent( 'shown', 'dropdown', relatedTarget);
630
- dispatchCustomEvent.call(parent, shownCustomEvent);
631
- },1);
632
- };
633
- self.hide = function () {
634
- hideCustomEvent = bootstrapCustomEvent('hide', 'dropdown', relatedTarget);
635
- dispatchCustomEvent.call(parent, hideCustomEvent);
636
- if ( hideCustomEvent.defaultPrevented ) { return; }
637
- menu.classList.remove('show');
638
- parent.classList.remove('show');
639
- element.setAttribute('aria-expanded',false);
640
- element.open = false;
641
- toggleDismiss();
642
- setFocus(element);
643
- setTimeout(function () {
644
- element.Dropdown && element.addEventListener('click',clickHandler,false);
645
- },1);
646
- hiddenCustomEvent = bootstrapCustomEvent('hidden', 'dropdown', relatedTarget);
647
- dispatchCustomEvent.call(parent, hiddenCustomEvent);
648
- };
649
- self.toggle = function () {
650
- if (parent.classList.contains('show') && element.open) { self.hide(); }
651
- else { self.show(); }
652
- };
653
- self.dispose = function () {
654
- if (parent.classList.contains('show') && element.open) { self.hide(); }
655
- element.removeEventListener('click',clickHandler,false);
656
- delete element.Dropdown;
657
- };
658
- element = queryElement(element);
659
- element.Dropdown && element.Dropdown.dispose();
660
- parent = element.parentNode;
661
- menu = queryElement('.dropdown-menu', parent);
662
- Array.from(menu.children).map(function (child){
663
- child.children.length && (child.children[0].tagName === 'A' && menuItems.push(child.children[0]));
664
- child.tagName === 'A' && menuItems.push(child);
665
- });
666
- if ( !element.Dropdown ) {
667
- !('tabindex' in menu) && menu.setAttribute('tabindex', '0');
668
- element.addEventListener('click',clickHandler,false);
669
- }
670
- persist = option === true || element.getAttribute('data-persist') === 'true' || false;
671
- element.open = false;
672
- element.Dropdown = self;
673
- }
674
-
675
- function Modal(element,options) {
676
- options = options || {};
677
- var self = this, modal,
678
- showCustomEvent,
679
- shownCustomEvent,
680
- hideCustomEvent,
681
- hiddenCustomEvent,
682
- relatedTarget = null,
683
- scrollBarWidth,
684
- overlay,
685
- overlayDelay,
686
- fixedItems,
687
- ops = {};
688
- function setScrollbar() {
689
- var openModal = document.body.classList.contains('modal-open'),
690
- bodyPad = parseInt(getComputedStyle(document.body).paddingRight),
691
- bodyOverflow = document.documentElement.clientHeight !== document.documentElement.scrollHeight
692
- || document.body.clientHeight !== document.body.scrollHeight,
693
- modalOverflow = modal.clientHeight !== modal.scrollHeight;
694
- scrollBarWidth = measureScrollbar();
695
- modal.style.paddingRight = !modalOverflow && scrollBarWidth ? (scrollBarWidth + "px") : '';
696
- document.body.style.paddingRight = modalOverflow || bodyOverflow ? ((bodyPad + (openModal ? 0:scrollBarWidth)) + "px") : '';
697
- fixedItems.length && fixedItems.map(function (fixed){
698
- var itemPad = getComputedStyle(fixed).paddingRight;
699
- fixed.style.paddingRight = modalOverflow || bodyOverflow ? ((parseInt(itemPad) + (openModal?0:scrollBarWidth)) + "px") : ((parseInt(itemPad)) + "px");
700
- });
701
- }
702
- function resetScrollbar() {
703
- document.body.style.paddingRight = '';
704
- modal.style.paddingRight = '';
705
- fixedItems.length && fixedItems.map(function (fixed){
706
- fixed.style.paddingRight = '';
707
- });
708
- }
709
- function measureScrollbar() {
710
- var scrollDiv = document.createElement('div'), widthValue;
711
- scrollDiv.className = 'modal-scrollbar-measure';
712
- document.body.appendChild(scrollDiv);
713
- widthValue = scrollDiv.offsetWidth - scrollDiv.clientWidth;
714
- document.body.removeChild(scrollDiv);
715
- return widthValue;
716
- }
717
- function createOverlay() {
718
- var newOverlay = document.createElement('div');
719
- overlay = queryElement('.modal-backdrop');
720
- if ( overlay === null ) {
721
- newOverlay.setAttribute('class', 'modal-backdrop' + (ops.animation ? ' fade' : ''));
722
- overlay = newOverlay;
723
- document.body.appendChild(overlay);
724
- }
725
- return overlay;
726
- }
727
- function removeOverlay () {
728
- overlay = queryElement('.modal-backdrop');
729
- if ( overlay && !document.getElementsByClassName('modal show')[0] ) {
730
- document.body.removeChild(overlay); overlay = null;
731
- }
732
- overlay === null && (document.body.classList.remove('modal-open'), resetScrollbar());
733
- }
734
- function toggleEvents(action) {
735
- action = action ? 'addEventListener' : 'removeEventListener';
736
- window[action]( 'resize', self.update, passiveHandler);
737
- modal[action]( 'click',dismissHandler,false);
738
- document[action]( 'keydown',keyHandler,false);
739
- }
740
- function beforeShow() {
741
- modal.style.display = 'block';
742
- setScrollbar();
743
- !document.getElementsByClassName('modal show')[0] && document.body.classList.add('modal-open');
744
- modal.classList.add('show');
745
- modal.setAttribute('aria-hidden', false);
746
- modal.classList.contains('fade') ? emulateTransitionEnd(modal, triggerShow) : triggerShow();
747
- }
748
- function triggerShow() {
749
- setFocus(modal);
750
- modal.isAnimating = false;
751
- toggleEvents(1);
752
- shownCustomEvent = bootstrapCustomEvent('shown', 'modal', relatedTarget);
753
- dispatchCustomEvent.call(modal, shownCustomEvent);
754
- }
755
- function triggerHide(force) {
756
- modal.style.display = '';
757
- element && (setFocus(element));
758
- overlay = queryElement('.modal-backdrop');
759
- if (force !== 1 && overlay && overlay.classList.contains('show') && !document.getElementsByClassName('modal show')[0]) {
760
- overlay.classList.remove('show');
761
- emulateTransitionEnd(overlay,removeOverlay);
762
- } else {
763
- removeOverlay();
764
- }
765
- toggleEvents();
766
- modal.isAnimating = false;
767
- hiddenCustomEvent = bootstrapCustomEvent('hidden', 'modal');
768
- dispatchCustomEvent.call(modal, hiddenCustomEvent);
769
- }
770
- function clickHandler(e) {
771
- if ( modal.isAnimating ) { return; }
772
- var clickTarget = e.target,
773
- modalID = "#" + (modal.getAttribute('id')),
774
- targetAttrValue = clickTarget.getAttribute('data-target') || clickTarget.getAttribute('href'),
775
- elemAttrValue = element.getAttribute('data-target') || element.getAttribute('href');
776
- if ( !modal.classList.contains('show')
777
- && (clickTarget === element && targetAttrValue === modalID
778
- || element.contains(clickTarget) && elemAttrValue === modalID) ) {
779
- modal.modalTrigger = element;
780
- relatedTarget = element;
781
- self.show();
782
- e.preventDefault();
783
- }
784
- }
785
- function keyHandler(ref) {
786
- var which = ref.which;
787
- if (!modal.isAnimating && ops.keyboard && which == 27 && modal.classList.contains('show') ) {
788
- self.hide();
789
- }
790
- }
791
- function dismissHandler(e) {
792
- if ( modal.isAnimating ) { return; }
793
- var clickTarget = e.target,
794
- hasData = clickTarget.getAttribute('data-dismiss') === 'modal',
795
- parentWithData = clickTarget.closest('[data-dismiss="modal"]');
796
- if ( modal.classList.contains('show') && ( parentWithData || hasData
797
- || clickTarget === modal && ops.backdrop !== 'static' ) ) {
798
- self.hide(); relatedTarget = null;
799
- e.preventDefault();
800
- }
801
- }
802
- self.toggle = function () {
803
- if ( modal.classList.contains('show') ) {self.hide();} else {self.show();}
804
- };
805
- self.show = function () {
806
- if (modal.classList.contains('show') && !!modal.isAnimating ) {return}
807
- showCustomEvent = bootstrapCustomEvent('show', 'modal', relatedTarget);
808
- dispatchCustomEvent.call(modal, showCustomEvent);
809
- if ( showCustomEvent.defaultPrevented ) { return; }
810
- modal.isAnimating = true;
811
- var currentOpen = document.getElementsByClassName('modal show')[0];
812
- if (currentOpen && currentOpen !== modal) {
813
- currentOpen.modalTrigger && currentOpen.modalTrigger.Modal.hide();
814
- currentOpen.Modal && currentOpen.Modal.hide();
815
- }
816
- if ( ops.backdrop ) {
817
- overlay = createOverlay();
818
- }
819
- if ( overlay && !currentOpen && !overlay.classList.contains('show') ) {
820
- overlay.offsetWidth;
821
- overlayDelay = getElementTransitionDuration(overlay);
822
- overlay.classList.add('show');
823
- }
824
- !currentOpen ? setTimeout( beforeShow, overlay && overlayDelay ? overlayDelay:0 ) : beforeShow();
825
- };
826
- self.hide = function (force) {
827
- if ( !modal.classList.contains('show') ) {return}
828
- hideCustomEvent = bootstrapCustomEvent( 'hide', 'modal');
829
- dispatchCustomEvent.call(modal, hideCustomEvent);
830
- if ( hideCustomEvent.defaultPrevented ) { return; }
831
- modal.isAnimating = true;
832
- modal.classList.remove('show');
833
- modal.setAttribute('aria-hidden', true);
834
- modal.classList.contains('fade') && force !== 1 ? emulateTransitionEnd(modal, triggerHide) : triggerHide();
835
- };
836
- self.setContent = function (content) {
837
- queryElement('.modal-content',modal).innerHTML = content;
838
- };
839
- self.update = function () {
840
- if (modal.classList.contains('show')) {
841
- setScrollbar();
842
- }
843
- };
844
- self.dispose = function () {
845
- self.hide(1);
846
- if (element) {element.removeEventListener('click',clickHandler,false); delete element.Modal; }
847
- else {delete modal.Modal;}
848
- };
849
- element = queryElement(element);
850
- var checkModal = queryElement( element.getAttribute('data-target') || element.getAttribute('href') );
851
- modal = element.classList.contains('modal') ? element : checkModal;
852
- fixedItems = Array.from(document.getElementsByClassName('fixed-top'))
853
- .concat(Array.from(document.getElementsByClassName('fixed-bottom')));
854
- if ( element.classList.contains('modal') ) { element = null; }
855
- element && element.Modal && element.Modal.dispose();
856
- modal && modal.Modal && modal.Modal.dispose();
857
- ops.keyboard = options.keyboard === false || modal.getAttribute('data-keyboard') === 'false' ? false : true;
858
- ops.backdrop = options.backdrop === 'static' || modal.getAttribute('data-backdrop') === 'static' ? 'static' : true;
859
- ops.backdrop = options.backdrop === false || modal.getAttribute('data-backdrop') === 'false' ? false : ops.backdrop;
860
- ops.animation = modal.classList.contains('fade') ? true : false;
861
- ops.content = options.content;
862
- modal.isAnimating = false;
863
- if ( element && !element.Modal ) {
864
- element.addEventListener('click',clickHandler,false);
865
- }
866
- if ( ops.content ) {
867
- self.setContent( ops.content.trim() );
868
- }
869
- if (element) {
870
- modal.modalTrigger = element;
871
- element.Modal = self;
872
- } else {
873
- modal.Modal = self;
874
- }
875
- }
876
-
877
- var mouseClickEvents = { down: 'mousedown', up: 'mouseup' };
878
-
879
- function getScroll() {
880
- return {
881
- y : window.pageYOffset || document.documentElement.scrollTop,
882
- x : window.pageXOffset || document.documentElement.scrollLeft
883
- }
884
- }
885
-
886
- function styleTip(link,element,position,parent) {
887
- var tipPositions = /\b(top|bottom|left|right)+/,
888
- elementDimensions = { w : element.offsetWidth, h: element.offsetHeight },
889
- windowWidth = (document.documentElement.clientWidth || document.body.clientWidth),
890
- windowHeight = (document.documentElement.clientHeight || document.body.clientHeight),
891
- rect = link.getBoundingClientRect(),
892
- scroll = parent === document.body ? getScroll() : { x: parent.offsetLeft + parent.scrollLeft, y: parent.offsetTop + parent.scrollTop },
893
- linkDimensions = { w: rect.right - rect.left, h: rect.bottom - rect.top },
894
- isPopover = element.classList.contains('popover'),
895
- arrow = element.getElementsByClassName('arrow')[0],
896
- halfTopExceed = rect.top + linkDimensions.h/2 - elementDimensions.h/2 < 0,
897
- halfLeftExceed = rect.left + linkDimensions.w/2 - elementDimensions.w/2 < 0,
898
- halfRightExceed = rect.left + elementDimensions.w/2 + linkDimensions.w/2 >= windowWidth,
899
- halfBottomExceed = rect.top + elementDimensions.h/2 + linkDimensions.h/2 >= windowHeight,
900
- topExceed = rect.top - elementDimensions.h < 0,
901
- leftExceed = rect.left - elementDimensions.w < 0,
902
- bottomExceed = rect.top + elementDimensions.h + linkDimensions.h >= windowHeight,
903
- rightExceed = rect.left + elementDimensions.w + linkDimensions.w >= windowWidth;
904
- position = (position === 'left' || position === 'right') && leftExceed && rightExceed ? 'top' : position;
905
- position = position === 'top' && topExceed ? 'bottom' : position;
906
- position = position === 'bottom' && bottomExceed ? 'top' : position;
907
- position = position === 'left' && leftExceed ? 'right' : position;
908
- position = position === 'right' && rightExceed ? 'left' : position;
909
- var topPosition,
910
- leftPosition,
911
- arrowTop,
912
- arrowLeft,
913
- arrowWidth,
914
- arrowHeight;
915
- element.className.indexOf(position) === -1 && (element.className = element.className.replace(tipPositions,position));
916
- arrowWidth = arrow.offsetWidth; arrowHeight = arrow.offsetHeight;
917
- if ( position === 'left' || position === 'right' ) {
918
- if ( position === 'left' ) {
919
- leftPosition = rect.left + scroll.x - elementDimensions.w - ( isPopover ? arrowWidth : 0 );
920
- } else {
921
- leftPosition = rect.left + scroll.x + linkDimensions.w;
922
- }
923
- if (halfTopExceed) {
924
- topPosition = rect.top + scroll.y;
925
- arrowTop = linkDimensions.h/2 - arrowWidth;
926
- } else if (halfBottomExceed) {
927
- topPosition = rect.top + scroll.y - elementDimensions.h + linkDimensions.h;
928
- arrowTop = elementDimensions.h - linkDimensions.h/2 - arrowWidth;
929
- } else {
930
- topPosition = rect.top + scroll.y - elementDimensions.h/2 + linkDimensions.h/2;
931
- arrowTop = elementDimensions.h/2 - (isPopover ? arrowHeight*0.9 : arrowHeight/2);
932
- }
933
- } else if ( position === 'top' || position === 'bottom' ) {
934
- if ( position === 'top') {
935
- topPosition = rect.top + scroll.y - elementDimensions.h - ( isPopover ? arrowHeight : 0 );
936
- } else {
937
- topPosition = rect.top + scroll.y + linkDimensions.h;
938
- }
939
- if (halfLeftExceed) {
940
- leftPosition = 0;
941
- arrowLeft = rect.left + linkDimensions.w/2 - arrowWidth;
942
- } else if (halfRightExceed) {
943
- leftPosition = windowWidth - elementDimensions.w*1.01;
944
- arrowLeft = elementDimensions.w - ( windowWidth - rect.left ) + linkDimensions.w/2 - arrowWidth/2;
945
- } else {
946
- leftPosition = rect.left + scroll.x - elementDimensions.w/2 + linkDimensions.w/2;
947
- arrowLeft = elementDimensions.w/2 - ( isPopover ? arrowWidth : arrowWidth/2 );
948
- }
949
- }
950
- element.style.top = topPosition + 'px';
951
- element.style.left = leftPosition + 'px';
952
- arrowTop && (arrow.style.top = arrowTop + 'px');
953
- arrowLeft && (arrow.style.left = arrowLeft + 'px');
954
- }
955
-
956
- function Popover(element,options) {
957
- options = options || {};
958
- var self = this;
959
- var popover = null,
960
- timer = 0,
961
- isIphone = /(iPhone|iPod|iPad)/.test(navigator.userAgent),
962
- titleString,
963
- contentString,
964
- ops = {};
965
- var triggerData,
966
- animationData,
967
- placementData,
968
- dismissibleData,
969
- delayData,
970
- containerData,
971
- closeBtn,
972
- showCustomEvent,
973
- shownCustomEvent,
974
- hideCustomEvent,
975
- hiddenCustomEvent,
976
- containerElement,
977
- containerDataElement,
978
- modal,
979
- navbarFixedTop,
980
- navbarFixedBottom,
981
- placementClass;
982
- function dismissibleHandler(e) {
983
- if (popover !== null && e.target === queryElement('.close',popover)) {
984
- self.hide();
985
- }
986
- }
987
- function getContents() {
988
- return {
989
- 0 : options.title || element.getAttribute('data-title') || null,
990
- 1 : options.content || element.getAttribute('data-content') || null
991
- }
992
- }
993
- function removePopover() {
994
- ops.container.removeChild(popover);
995
- timer = null; popover = null;
996
- }
997
- function createPopover() {
998
- titleString = getContents()[0] || null;
999
- contentString = getContents()[1];
1000
- contentString = !!contentString ? contentString.trim() : null;
1001
- popover = document.createElement('div');
1002
- var popoverArrow = document.createElement('div');
1003
- popoverArrow.classList.add('arrow');
1004
- popover.appendChild(popoverArrow);
1005
- if ( contentString !== null && ops.template === null ) {
1006
- popover.setAttribute('role','tooltip');
1007
- if (titleString !== null) {
1008
- var popoverTitle = document.createElement('h3');
1009
- popoverTitle.classList.add('popover-header');
1010
- popoverTitle.innerHTML = ops.dismissible ? titleString + closeBtn : titleString;
1011
- popover.appendChild(popoverTitle);
1012
- }
1013
- var popoverBodyMarkup = document.createElement('div');
1014
- popoverBodyMarkup.classList.add('popover-body');
1015
- popoverBodyMarkup.innerHTML = ops.dismissible && titleString === null ? contentString + closeBtn : contentString;
1016
- popover.appendChild(popoverBodyMarkup);
1017
- } else {
1018
- var popoverTemplate = document.createElement('div');
1019
- popoverTemplate.innerHTML = ops.template.trim();
1020
- popover.className = popoverTemplate.firstChild.className;
1021
- popover.innerHTML = popoverTemplate.firstChild.innerHTML;
1022
- var popoverHeader = queryElement('.popover-header',popover),
1023
- popoverBody = queryElement('.popover-body',popover);
1024
- titleString && popoverHeader && (popoverHeader.innerHTML = titleString.trim());
1025
- contentString && popoverBody && (popoverBody.innerHTML = contentString.trim());
1026
- }
1027
- ops.container.appendChild(popover);
1028
- popover.style.display = 'block';
1029
- !popover.classList.contains( 'popover') && popover.classList.add('popover');
1030
- !popover.classList.contains( ops.animation) && popover.classList.add(ops.animation);
1031
- !popover.classList.contains( placementClass) && popover.classList.add(placementClass);
1032
- }
1033
- function showPopover() {
1034
- !popover.classList.contains('show') && ( popover.classList.add('show') );
1035
- }
1036
- function updatePopover() {
1037
- styleTip(element, popover, ops.placement, ops.container);
1038
- }
1039
- function forceFocus () {
1040
- if (popover === null) { element.focus(); }
1041
- }
1042
- function toggleEvents(action) {
1043
- action = action ? 'addEventListener' : 'removeEventListener';
1044
- if (ops.trigger === 'hover') {
1045
- element[action]( mouseClickEvents.down, self.show );
1046
- element[action]( mouseHoverEvents[0], self.show );
1047
- if (!ops.dismissible) { element[action]( mouseHoverEvents[1], self.hide ); }
1048
- } else if ('click' == ops.trigger) {
1049
- element[action]( ops.trigger, self.toggle );
1050
- } else if ('focus' == ops.trigger) {
1051
- isIphone && element[action]( 'click', forceFocus, false );
1052
- element[action]( ops.trigger, self.toggle );
1053
- }
1054
- }
1055
- function touchHandler(e){
1056
- if ( popover && popover.contains(e.target) || e.target === element || element.contains(e.target)) ; else {
1057
- self.hide();
1058
- }
1059
- }
1060
- function dismissHandlerToggle(action) {
1061
- action = action ? 'addEventListener' : 'removeEventListener';
1062
- if (ops.dismissible) {
1063
- document[action]('click', dismissibleHandler, false );
1064
- } else {
1065
- 'focus' == ops.trigger && element[action]( 'blur', self.hide );
1066
- 'hover' == ops.trigger && document[action]( 'touchstart', touchHandler, passiveHandler );
1067
- }
1068
- window[action]('resize', self.hide, passiveHandler );
1069
- }
1070
- function showTrigger() {
1071
- dismissHandlerToggle(1);
1072
- dispatchCustomEvent.call(element, shownCustomEvent);
1073
- }
1074
- function hideTrigger() {
1075
- dismissHandlerToggle();
1076
- removePopover();
1077
- dispatchCustomEvent.call(element, hiddenCustomEvent);
1078
- }
1079
- self.toggle = function () {
1080
- if (popover === null) { self.show(); }
1081
- else { self.hide(); }
1082
- };
1083
- self.show = function () {
1084
- clearTimeout(timer);
1085
- timer = setTimeout( function () {
1086
- if (popover === null) {
1087
- dispatchCustomEvent.call(element, showCustomEvent);
1088
- if ( showCustomEvent.defaultPrevented ) { return; }
1089
- createPopover();
1090
- updatePopover();
1091
- showPopover();
1092
- !!ops.animation ? emulateTransitionEnd(popover, showTrigger) : showTrigger();
1093
- }
1094
- }, 20 );
1095
- };
1096
- self.hide = function () {
1097
- clearTimeout(timer);
1098
- timer = setTimeout( function () {
1099
- if (popover && popover !== null && popover.classList.contains('show')) {
1100
- dispatchCustomEvent.call(element, hideCustomEvent);
1101
- if ( hideCustomEvent.defaultPrevented ) { return; }
1102
- popover.classList.remove('show');
1103
- !!ops.animation ? emulateTransitionEnd(popover, hideTrigger) : hideTrigger();
1104
- }
1105
- }, ops.delay );
1106
- };
1107
- self.dispose = function () {
1108
- self.hide();
1109
- toggleEvents();
1110
- delete element.Popover;
1111
- };
1112
- element = queryElement(element);
1113
- element.Popover && element.Popover.dispose();
1114
- triggerData = element.getAttribute('data-trigger');
1115
- animationData = element.getAttribute('data-animation');
1116
- placementData = element.getAttribute('data-placement');
1117
- dismissibleData = element.getAttribute('data-dismissible');
1118
- delayData = element.getAttribute('data-delay');
1119
- containerData = element.getAttribute('data-container');
1120
- closeBtn = '<button type="button" class="close">×</button>';
1121
- showCustomEvent = bootstrapCustomEvent('show', 'popover');
1122
- shownCustomEvent = bootstrapCustomEvent('shown', 'popover');
1123
- hideCustomEvent = bootstrapCustomEvent('hide', 'popover');
1124
- hiddenCustomEvent = bootstrapCustomEvent('hidden', 'popover');
1125
- containerElement = queryElement(options.container);
1126
- containerDataElement = queryElement(containerData);
1127
- modal = element.closest('.modal');
1128
- navbarFixedTop = element.closest('.fixed-top');
1129
- navbarFixedBottom = element.closest('.fixed-bottom');
1130
- ops.template = options.template ? options.template : null;
1131
- ops.trigger = options.trigger ? options.trigger : triggerData || 'hover';
1132
- ops.animation = options.animation && options.animation !== 'fade' ? options.animation : animationData || 'fade';
1133
- ops.placement = options.placement ? options.placement : placementData || 'top';
1134
- ops.delay = parseInt(options.delay || delayData) || 200;
1135
- ops.dismissible = options.dismissible || dismissibleData === 'true' ? true : false;
1136
- ops.container = containerElement ? containerElement
1137
- : containerDataElement ? containerDataElement
1138
- : navbarFixedTop ? navbarFixedTop
1139
- : navbarFixedBottom ? navbarFixedBottom
1140
- : modal ? modal : document.body;
1141
- placementClass = "bs-popover-" + (ops.placement);
1142
- var popoverContents = getContents();
1143
- titleString = popoverContents[0];
1144
- contentString = popoverContents[1];
1145
- if ( !contentString && !ops.template ) { return; }
1146
- if ( !element.Popover ) {
1147
- toggleEvents(1);
1148
- }
1149
- element.Popover = self;
1150
- }
1151
-
1152
- function ScrollSpy(element,options) {
1153
- options = options || {};
1154
- var self = this,
1155
- vars,
1156
- targetData,
1157
- offsetData,
1158
- spyTarget,
1159
- scrollTarget,
1160
- ops = {};
1161
- function updateTargets(){
1162
- var links = spyTarget.getElementsByTagName('A');
1163
- if (vars.length !== links.length) {
1164
- vars.items = [];
1165
- vars.targets = [];
1166
- Array.from(links).map(function (link){
1167
- var href = link.getAttribute('href'),
1168
- targetItem = href && href.charAt(0) === '#' && href.slice(-1) !== '#' && queryElement(href);
1169
- if ( targetItem ) {
1170
- vars.items.push(link);
1171
- vars.targets.push(targetItem);
1172
- }
1173
- });
1174
- vars.length = links.length;
1175
- }
1176
- }
1177
- function updateItem(index) {
1178
- var item = vars.items[index],
1179
- targetItem = vars.targets[index],
1180
- dropmenu = item.classList.contains('dropdown-item') && item.closest('.dropdown-menu'),
1181
- dropLink = dropmenu && dropmenu.previousElementSibling,
1182
- nextSibling = item.nextElementSibling,
1183
- activeSibling = nextSibling && nextSibling.getElementsByClassName('active').length,
1184
- targetRect = vars.isWindow && targetItem.getBoundingClientRect(),
1185
- isActive = item.classList.contains('active') || false,
1186
- topEdge = (vars.isWindow ? targetRect.top + vars.scrollOffset : targetItem.offsetTop) - ops.offset,
1187
- bottomEdge = vars.isWindow ? targetRect.bottom + vars.scrollOffset - ops.offset
1188
- : vars.targets[index+1] ? vars.targets[index+1].offsetTop - ops.offset
1189
- : element.scrollHeight,
1190
- inside = activeSibling || vars.scrollOffset >= topEdge && bottomEdge > vars.scrollOffset;
1191
- if ( !isActive && inside ) {
1192
- item.classList.add('active');
1193
- if (dropLink && !dropLink.classList.contains('active') ) {
1194
- dropLink.classList.add('active');
1195
- }
1196
- dispatchCustomEvent.call(element, bootstrapCustomEvent( 'activate', 'scrollspy', vars.items[index]));
1197
- } else if ( isActive && !inside ) {
1198
- item.classList.remove('active');
1199
- if (dropLink && dropLink.classList.contains('active') && !item.parentNode.getElementsByClassName('active').length ) {
1200
- dropLink.classList.remove('active');
1201
- }
1202
- } else if ( isActive && inside || !inside && !isActive ) {
1203
- return;
1204
- }
1205
- }
1206
- function updateItems() {
1207
- updateTargets();
1208
- vars.scrollOffset = vars.isWindow ? getScroll().y : element.scrollTop;
1209
- vars.items.map(function (l,idx){ return updateItem(idx); });
1210
- }
1211
- function toggleEvents(action) {
1212
- action = action ? 'addEventListener' : 'removeEventListener';
1213
- scrollTarget[action]('scroll', self.refresh, passiveHandler );
1214
- window[action]( 'resize', self.refresh, passiveHandler );
1215
- }
1216
- self.refresh = function () {
1217
- updateItems();
1218
- };
1219
- self.dispose = function () {
1220
- toggleEvents();
1221
- delete element.ScrollSpy;
1222
- };
1223
- element = queryElement(element);
1224
- element.ScrollSpy && element.ScrollSpy.dispose();
1225
- targetData = element.getAttribute('data-target');
1226
- offsetData = element.getAttribute('data-offset');
1227
- spyTarget = queryElement(options.target || targetData);
1228
- scrollTarget = element.offsetHeight < element.scrollHeight ? element : window;
1229
- if (!spyTarget) { return }
1230
- ops.target = spyTarget;
1231
- ops.offset = parseInt(options.offset || offsetData) || 10;
1232
- vars = {};
1233
- vars.length = 0;
1234
- vars.items = [];
1235
- vars.targets = [];
1236
- vars.isWindow = scrollTarget === window;
1237
- if ( !element.ScrollSpy ) {
1238
- toggleEvents(1);
1239
- }
1240
- self.refresh();
1241
- element.ScrollSpy = self;
1242
- }
1243
-
1244
- function Tab(element,options) {
1245
- options = options || {};
1246
- var self = this,
1247
- heightData,
1248
- tabs, dropdown,
1249
- showCustomEvent,
1250
- shownCustomEvent,
1251
- hideCustomEvent,
1252
- hiddenCustomEvent,
1253
- next,
1254
- tabsContentContainer = false,
1255
- activeTab,
1256
- activeContent,
1257
- nextContent,
1258
- containerHeight,
1259
- equalContents,
1260
- nextHeight,
1261
- animateHeight;
1262
- function triggerEnd() {
1263
- tabsContentContainer.style.height = '';
1264
- tabsContentContainer.classList.remove('collapsing');
1265
- tabs.isAnimating = false;
1266
- }
1267
- function triggerShow() {
1268
- if (tabsContentContainer) {
1269
- if ( equalContents ) {
1270
- triggerEnd();
1271
- } else {
1272
- setTimeout(function () {
1273
- tabsContentContainer.style.height = nextHeight + "px";
1274
- tabsContentContainer.offsetWidth;
1275
- emulateTransitionEnd(tabsContentContainer, triggerEnd);
1276
- },50);
1277
- }
1278
- } else {
1279
- tabs.isAnimating = false;
1280
- }
1281
- shownCustomEvent = bootstrapCustomEvent('shown', 'tab', activeTab);
1282
- dispatchCustomEvent.call(next, shownCustomEvent);
1283
- }
1284
- function triggerHide() {
1285
- if (tabsContentContainer) {
1286
- activeContent.style.float = 'left';
1287
- nextContent.style.float = 'left';
1288
- containerHeight = activeContent.scrollHeight;
1289
- }
1290
- showCustomEvent = bootstrapCustomEvent('show', 'tab', activeTab);
1291
- hiddenCustomEvent = bootstrapCustomEvent('hidden', 'tab', next);
1292
- dispatchCustomEvent.call(next, showCustomEvent);
1293
- if ( showCustomEvent.defaultPrevented ) { return; }
1294
- nextContent.classList.add('active');
1295
- activeContent.classList.remove('active');
1296
- if (tabsContentContainer) {
1297
- nextHeight = nextContent.scrollHeight;
1298
- equalContents = nextHeight === containerHeight;
1299
- tabsContentContainer.classList.add('collapsing');
1300
- tabsContentContainer.style.height = containerHeight + "px";
1301
- tabsContentContainer.offsetHeight;
1302
- activeContent.style.float = '';
1303
- nextContent.style.float = '';
1304
- }
1305
- if ( nextContent.classList.contains('fade') ) {
1306
- setTimeout(function () {
1307
- nextContent.classList.add('show');
1308
- emulateTransitionEnd(nextContent,triggerShow);
1309
- },20);
1310
- } else { triggerShow(); }
1311
- dispatchCustomEvent.call(activeTab, hiddenCustomEvent);
1312
- }
1313
- function getActiveTab() {
1314
- var activeTabs = tabs.getElementsByClassName('active'), activeTab;
1315
- if ( activeTabs.length === 1 && !activeTabs[0].parentNode.classList.contains('dropdown') ) {
1316
- activeTab = activeTabs[0];
1317
- } else if ( activeTabs.length > 1 ) {
1318
- activeTab = activeTabs[activeTabs.length-1];
1319
- }
1320
- return activeTab;
1321
- }
1322
- function getActiveContent() { return queryElement(getActiveTab().getAttribute('href')) }
1323
- function clickHandler(e) {
1324
- e.preventDefault();
1325
- next = e.currentTarget;
1326
- !tabs.isAnimating && self.show();
1327
- }
1328
- self.show = function () {
1329
- next = next || element;
1330
- if (!next.classList.contains('active')) {
1331
- nextContent = queryElement(next.getAttribute('href'));
1332
- activeTab = getActiveTab();
1333
- activeContent = getActiveContent();
1334
- hideCustomEvent = bootstrapCustomEvent( 'hide', 'tab', next);
1335
- dispatchCustomEvent.call(activeTab, hideCustomEvent);
1336
- if (hideCustomEvent.defaultPrevented) { return; }
1337
- tabs.isAnimating = true;
1338
- activeTab.classList.remove('active');
1339
- activeTab.setAttribute('aria-selected','false');
1340
- next.classList.add('active');
1341
- next.setAttribute('aria-selected','true');
1342
- if ( dropdown ) {
1343
- if ( !element.parentNode.classList.contains('dropdown-menu') ) {
1344
- if (dropdown.classList.contains('active')) { dropdown.classList.remove('active'); }
1345
- } else {
1346
- if (!dropdown.classList.contains('active')) { dropdown.classList.add('active'); }
1347
- }
1348
- }
1349
- if (activeContent.classList.contains('fade')) {
1350
- activeContent.classList.remove('show');
1351
- emulateTransitionEnd(activeContent, triggerHide);
1352
- } else { triggerHide(); }
1353
- }
1354
- };
1355
- self.dispose = function () {
1356
- element.removeEventListener('click',clickHandler,false);
1357
- delete element.Tab;
1358
- };
1359
- element = queryElement(element);
1360
- element.Tab && element.Tab.dispose();
1361
- heightData = element.getAttribute('data-height');
1362
- tabs = element.closest('.nav');
1363
- dropdown = tabs && queryElement('.dropdown-toggle',tabs);
1364
- animateHeight = !supportTransition || (options.height === false || heightData === 'false') ? false : true;
1365
- tabs.isAnimating = false;
1366
- if ( !element.Tab ) {
1367
- element.addEventListener('click',clickHandler,false);
1368
- }
1369
- if (animateHeight) { tabsContentContainer = getActiveContent().parentNode; }
1370
- element.Tab = self;
1371
- }
1372
-
1373
- function Toast(element,options) {
1374
- options = options || {};
1375
- var self = this,
1376
- toast, timer = 0,
1377
- animationData,
1378
- autohideData,
1379
- delayData,
1380
- showCustomEvent,
1381
- hideCustomEvent,
1382
- shownCustomEvent,
1383
- hiddenCustomEvent,
1384
- ops = {};
1385
- function showComplete() {
1386
- toast.classList.remove( 'showing' );
1387
- toast.classList.add( 'show' );
1388
- dispatchCustomEvent.call(toast,shownCustomEvent);
1389
- if (ops.autohide) { self.hide(); }
1390
- }
1391
- function hideComplete() {
1392
- toast.classList.add( 'hide' );
1393
- dispatchCustomEvent.call(toast,hiddenCustomEvent);
1394
- }
1395
- function close () {
1396
- toast.classList.remove('show' );
1397
- ops.animation ? emulateTransitionEnd(toast, hideComplete) : hideComplete();
1398
- }
1399
- function disposeComplete() {
1400
- clearTimeout(timer);
1401
- element.removeEventListener('click',self.hide,false);
1402
- delete element.Toast;
1403
- }
1404
- self.show = function () {
1405
- if (toast && !toast.classList.contains('show')) {
1406
- dispatchCustomEvent.call(toast,showCustomEvent);
1407
- if (showCustomEvent.defaultPrevented) { return; }
1408
- ops.animation && toast.classList.add( 'fade' );
1409
- toast.classList.remove('hide' );
1410
- toast.offsetWidth;
1411
- toast.classList.add('showing' );
1412
- ops.animation ? emulateTransitionEnd(toast, showComplete) : showComplete();
1413
- }
1414
- };
1415
- self.hide = function (noTimer) {
1416
- if (toast && toast.classList.contains('show')) {
1417
- dispatchCustomEvent.call(toast,hideCustomEvent);
1418
- if(hideCustomEvent.defaultPrevented) { return; }
1419
- noTimer ? close() : (timer = setTimeout( close, ops.delay));
1420
- }
1421
- };
1422
- self.dispose = function () {
1423
- ops.animation ? emulateTransitionEnd(toast, disposeComplete) : disposeComplete();
1424
- };
1425
- element = queryElement(element);
1426
- element.Toast && element.Toast.dispose();
1427
- toast = element.closest('.toast');
1428
- animationData = element.getAttribute('data-animation');
1429
- autohideData = element.getAttribute('data-autohide');
1430
- delayData = element.getAttribute('data-delay');
1431
- showCustomEvent = bootstrapCustomEvent('show', 'toast');
1432
- hideCustomEvent = bootstrapCustomEvent('hide', 'toast');
1433
- shownCustomEvent = bootstrapCustomEvent('shown', 'toast');
1434
- hiddenCustomEvent = bootstrapCustomEvent('hidden', 'toast');
1435
- ops.animation = options.animation === false || animationData === 'false' ? 0 : 1;
1436
- ops.autohide = options.autohide === false || autohideData === 'false' ? 0 : 1;
1437
- ops.delay = parseInt(options.delay || delayData) || 500;
1438
- if ( !element.Toast ) {
1439
- element.addEventListener('click',self.hide,false);
1440
- }
1441
- element.Toast = self;
1442
- }
1443
-
1444
- function Tooltip(element,options) {
1445
- options = options || {};
1446
- var self = this,
1447
- tooltip = null, timer = 0, titleString,
1448
- animationData,
1449
- placementData,
1450
- delayData,
1451
- containerData,
1452
- showCustomEvent,
1453
- shownCustomEvent,
1454
- hideCustomEvent,
1455
- hiddenCustomEvent,
1456
- containerElement,
1457
- containerDataElement,
1458
- modal,
1459
- navbarFixedTop,
1460
- navbarFixedBottom,
1461
- placementClass,
1462
- ops = {};
1463
- function getTitle() {
1464
- return element.getAttribute('title')
1465
- || element.getAttribute('data-title')
1466
- || element.getAttribute('data-original-title')
1467
- }
1468
- function removeToolTip() {
1469
- ops.container.removeChild(tooltip);
1470
- tooltip = null; timer = null;
1471
- }
1472
- function createToolTip() {
1473
- titleString = getTitle();
1474
- if ( titleString ) {
1475
- tooltip = document.createElement('div');
1476
- if (ops.template) {
1477
- var tooltipMarkup = document.createElement('div');
1478
- tooltipMarkup.innerHTML = ops.template.trim();
1479
- tooltip.className = tooltipMarkup.firstChild.className;
1480
- tooltip.innerHTML = tooltipMarkup.firstChild.innerHTML;
1481
- queryElement('.tooltip-inner',tooltip).innerHTML = titleString.trim();
1482
- } else {
1483
- var tooltipArrow = document.createElement('div');
1484
- tooltipArrow.classList.add('arrow');
1485
- tooltip.appendChild(tooltipArrow);
1486
- var tooltipInner = document.createElement('div');
1487
- tooltipInner.classList.add('tooltip-inner');
1488
- tooltip.appendChild(tooltipInner);
1489
- tooltipInner.innerHTML = titleString;
1490
- }
1491
- tooltip.style.left = '0';
1492
- tooltip.style.top = '0';
1493
- tooltip.setAttribute('role','tooltip');
1494
- !tooltip.classList.contains('tooltip') && tooltip.classList.add('tooltip');
1495
- !tooltip.classList.contains(ops.animation) && tooltip.classList.add(ops.animation);
1496
- !tooltip.classList.contains(placementClass) && tooltip.classList.add(placementClass);
1497
- ops.container.appendChild(tooltip);
1498
- }
1499
- }
1500
- function updateTooltip() {
1501
- styleTip(element, tooltip, ops.placement, ops.container);
1502
- }
1503
- function showTooltip() {
1504
- !tooltip.classList.contains('show') && ( tooltip.classList.add('show') );
1505
- }
1506
- function touchHandler(e){
1507
- if ( tooltip && tooltip.contains(e.target) || e.target === element || element.contains(e.target)) ; else {
1508
- self.hide();
1509
- }
1510
- }
1511
- function toggleAction(action){
1512
- action = action ? 'addEventListener' : 'removeEventListener';
1513
- document[action]( 'touchstart', touchHandler, passiveHandler );
1514
- window[action]( 'resize', self.hide, passiveHandler );
1515
- }
1516
- function showAction() {
1517
- toggleAction(1);
1518
- dispatchCustomEvent.call(element, shownCustomEvent);
1519
- }
1520
- function hideAction() {
1521
- toggleAction();
1522
- removeToolTip();
1523
- dispatchCustomEvent.call(element, hiddenCustomEvent);
1524
- }
1525
- function toggleEvents(action) {
1526
- action = action ? 'addEventListener' : 'removeEventListener';
1527
- element[action](mouseClickEvents.down, self.show,false);
1528
- element[action](mouseHoverEvents[0], self.show,false);
1529
- element[action](mouseHoverEvents[1], self.hide,false);
1530
- }
1531
- self.show = function () {
1532
- clearTimeout(timer);
1533
- timer = setTimeout( function () {
1534
- if (tooltip === null) {
1535
- dispatchCustomEvent.call(element, showCustomEvent);
1536
- if (showCustomEvent.defaultPrevented) { return; }
1537
- if(createToolTip() !== false) {
1538
- updateTooltip();
1539
- showTooltip();
1540
- !!ops.animation ? emulateTransitionEnd(tooltip, showAction) : showAction();
1541
- }
1542
- }
1543
- }, 20 );
1544
- };
1545
- self.hide = function () {
1546
- clearTimeout(timer);
1547
- timer = setTimeout( function () {
1548
- if (tooltip && tooltip.classList.contains('show')) {
1549
- dispatchCustomEvent.call(element, hideCustomEvent);
1550
- if (hideCustomEvent.defaultPrevented) { return; }
1551
- tooltip.classList.remove('show');
1552
- !!ops.animation ? emulateTransitionEnd(tooltip, hideAction) : hideAction();
1553
- }
1554
- }, ops.delay);
1555
- };
1556
- self.toggle = function () {
1557
- if (!tooltip) { self.show(); }
1558
- else { self.hide(); }
1559
- };
1560
- self.dispose = function () {
1561
- toggleEvents();
1562
- self.hide();
1563
- element.setAttribute('title', element.getAttribute('data-original-title'));
1564
- element.removeAttribute('data-original-title');
1565
- delete element.Tooltip;
1566
- };
1567
- element = queryElement(element);
1568
- element.Tooltip && element.Tooltip.dispose();
1569
- animationData = element.getAttribute('data-animation');
1570
- placementData = element.getAttribute('data-placement');
1571
- delayData = element.getAttribute('data-delay');
1572
- containerData = element.getAttribute('data-container');
1573
- showCustomEvent = bootstrapCustomEvent('show', 'tooltip');
1574
- shownCustomEvent = bootstrapCustomEvent('shown', 'tooltip');
1575
- hideCustomEvent = bootstrapCustomEvent('hide', 'tooltip');
1576
- hiddenCustomEvent = bootstrapCustomEvent('hidden', 'tooltip');
1577
- containerElement = queryElement(options.container);
1578
- containerDataElement = queryElement(containerData);
1579
- modal = element.closest('.modal');
1580
- navbarFixedTop = element.closest('.fixed-top');
1581
- navbarFixedBottom = element.closest('.fixed-bottom');
1582
- ops.animation = options.animation && options.animation !== 'fade' ? options.animation : animationData || 'fade';
1583
- ops.placement = options.placement ? options.placement : placementData || 'top';
1584
- ops.template = options.template ? options.template : null;
1585
- ops.delay = parseInt(options.delay || delayData) || 200;
1586
- ops.container = containerElement ? containerElement
1587
- : containerDataElement ? containerDataElement
1588
- : navbarFixedTop ? navbarFixedTop
1589
- : navbarFixedBottom ? navbarFixedBottom
1590
- : modal ? modal : document.body;
1591
- placementClass = "bs-tooltip-" + (ops.placement);
1592
- titleString = getTitle();
1593
- if ( !titleString ) { return; }
1594
- if (!element.Tooltip) {
1595
- element.setAttribute('data-original-title',titleString);
1596
- element.removeAttribute('title');
1597
- toggleEvents(1);
1598
- }
1599
- element.Tooltip = self;
1600
- }
1601
-
1602
- var componentsInit = {};
1603
-
1604
- function initializeDataAPI( Constructor, collection ){
1605
- Array.from(collection).map(function (x){ return new Constructor(x); });
1606
- }
1607
- function initCallback(lookUp){
1608
- lookUp = lookUp || document;
1609
- for (var component in componentsInit) {
1610
- initializeDataAPI( componentsInit[component][0], lookUp.querySelectorAll (componentsInit[component][1]) );
1611
- }
1612
- }
1613
-
1614
- componentsInit.Alert = [ Alert, '[data-dismiss="alert"]'];
1615
- componentsInit.Button = [ Button, '[data-toggle="buttons"]' ];
1616
- componentsInit.Carousel = [ Carousel, '[data-ride="carousel"]' ];
1617
- componentsInit.Collapse = [ Collapse, '[data-toggle="collapse"]' ];
1618
- componentsInit.Dropdown = [ Dropdown, '[data-toggle="dropdown"]'];
1619
- componentsInit.Modal = [ Modal, '[data-toggle="modal"]' ];
1620
- componentsInit.Popover = [ Popover, '[data-toggle="popover"],[data-tip="popover"]' ];
1621
- componentsInit.ScrollSpy = [ ScrollSpy, '[data-spy="scroll"]' ];
1622
- componentsInit.Tab = [ Tab, '[data-toggle="tab"]' ];
1623
- componentsInit.Toast = [ Toast, '[data-dismiss="toast"]' ];
1624
- componentsInit.Tooltip = [ Tooltip, '[data-toggle="tooltip"],[data-tip="tooltip"]' ];
1625
- document.body ? initCallback() : document.addEventListener( 'DOMContentLoaded', function initWrapper(){
1626
- initCallback();
1627
- document.removeEventListener('DOMContentLoaded',initWrapper,false);
1628
- }, false );
1629
-
1630
- function removeElementDataAPI( ConstructorName, collection ){
1631
- Array.from(collection).map(function (x){ return x[ConstructorName].dispose(); });
1632
- }
1633
- function removeDataAPI(lookUp) {
1634
- lookUp = lookUp || document;
1635
- for (var component in componentsInit) {
1636
- removeElementDataAPI( component, lookUp.querySelectorAll (componentsInit[component][1]) );
1637
- }
1638
- }
1639
-
1640
- var version = "3.0.10";
1641
-
1642
- var index = {
1643
- Alert: Alert,
1644
- Button: Button,
1645
- Carousel: Carousel,
1646
- Collapse: Collapse,
1647
- Dropdown: Dropdown,
1648
- Modal: Modal,
1649
- Popover: Popover,
1650
- ScrollSpy: ScrollSpy,
1651
- Tab: Tab,
1652
- Toast: Toast,
1653
- Tooltip: Tooltip,
1654
- initCallback: initCallback,
1655
- removeDataAPI: removeDataAPI,
1656
- componentsInit: componentsInit,
1657
- Version: version
1658
- };
1659
-
1660
- return index;
1661
-
1662
- })));