good_job 1.9.0 → 1.9.5

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.
@@ -30,13 +30,13 @@ 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
37
37
  # @param message [#to_json]
38
38
  def self.notify(message)
39
- connection = ActiveRecord::Base.connection
39
+ connection = Job.connection
40
40
  connection.exec_query <<~SQL.squish
41
41
  NOTIFY #{CHANNEL}, #{connection.quote(message.to_json)}
42
42
  SQL
@@ -64,18 +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.
78
- #
79
+ # @param timeout [Numeric, nil] Seconds to wait for active threads.
79
80
  # * +nil+, the scheduler will trigger a shutdown but not wait for it to complete.
80
81
  # * +-1+, the scheduler will wait until the shutdown is complete.
81
82
  # * +0+, the scheduler will immediately shutdown and stop any threads.
@@ -159,8 +160,8 @@ module GoodJob # :nodoc:
159
160
  end
160
161
 
161
162
  def with_listen_connection
162
- ar_conn = ActiveRecord::Base.connection_pool.checkout.tap do |conn|
163
- ActiveRecord::Base.connection_pool.remove(conn)
163
+ ar_conn = Job.connection_pool.checkout.tap do |conn|
164
+ Job.connection_pool.remove(conn)
164
165
  end
165
166
  pg_conn = ar_conn.raw_connection
166
167
  raise AdapterCannotListenError unless pg_conn.respond_to? :wait_for_notify
@@ -16,7 +16,7 @@ module GoodJob # :nodoc:
16
16
  # @!attribute [r] instances
17
17
  # @!scope class
18
18
  # List of all instantiated Pollers in the current process.
19
- # @return [Array<GoodJob::Poller>]
19
+ # @return [Array<GoodJob::Poller>, nil]
20
20
  cattr_reader :instances, default: [], instance_reader: false
21
21
 
22
22
  # Creates GoodJob::Poller from a GoodJob::Configuration instance.
@@ -30,8 +30,8 @@ module GoodJob # :nodoc:
30
30
  # @return [Array<#call, Array(Object, Symbol)>]
31
31
  attr_reader :recipients
32
32
 
33
- # @param recipients [Array<#call, Array(Object, Symbol)>]
34
- # @param poll_interval [Hash] number of seconds between polls
33
+ # @param recipients [Array<Proc, #call, Array(Object, Symbol)>]
34
+ # @param poll_interval [Integer, nil] number of seconds between polls
35
35
  def initialize(*recipients, poll_interval: nil)
36
36
  @recipients = Concurrent::Array.new(recipients)
37
37
 
@@ -54,7 +54,6 @@ module GoodJob # :nodoc:
54
54
  # Shut down the notifier.
55
55
  # Use {#shutdown?} to determine whether threads have stopped.
56
56
  # @param timeout [nil, Numeric] Seconds to wait for active threads.
57
- #
58
57
  # * +nil+, the scheduler will trigger a shutdown but not wait for it to complete.
59
58
  # * +-1+, the scheduler will wait until the shutdown is complete.
60
59
  # * +0+, the scheduler will immediately shutdown and stop any threads.
@@ -73,7 +72,7 @@ module GoodJob # :nodoc:
73
72
 
74
73
  # Restart the poller.
75
74
  # When shutdown, start; or shutdown and start.
76
- # @param timeout [nil, Numeric] Seconds to wait; shares same values as {#shutdown}.
75
+ # @param timeout [Numeric, nil] Seconds to wait; shares same values as {#shutdown}.
77
76
  # @return [void]
78
77
  def restart(timeout: -1)
79
78
  shutdown(timeout: timeout) if running?
@@ -82,6 +81,9 @@ module GoodJob # :nodoc:
82
81
 
83
82
  # Invoked on completion of TimerTask task.
84
83
  # @!visibility private
84
+ # @param time [Integer]
85
+ # @param executed_task [Object, nil]
86
+ # @param thread_error [Exception, nil]
85
87
  # @return [void]
86
88
  def timer_observer(time, executed_task, thread_error)
87
89
  GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
@@ -90,8 +92,10 @@ module GoodJob # :nodoc:
90
92
 
91
93
  private
92
94
 
95
+ # @return [Concurrent::TimerTask]
93
96
  attr_reader :timer
94
97
 
98
+ # @return [void]
95
99
  def create_timer
96
100
  return if @timer_options[:execution_interval] <= 0
97
101
 
@@ -30,14 +30,14 @@ 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.
37
37
  # @param configuration [GoodJob::Configuration]
38
38
  # @param warm_cache_on_initialize [Boolean]
39
39
  # @return [GoodJob::Scheduler, GoodJob::MultiScheduler]
40
- def self.from_configuration(configuration, warm_cache_on_initialize: true)
40
+ def self.from_configuration(configuration, warm_cache_on_initialize: false)
41
41
  schedulers = configuration.queue_string.split(';').map do |queue_string_and_max_threads|
42
42
  queue_string, max_threads = queue_string_and_max_threads.split(':')
43
43
  max_threads = (max_threads || configuration.max_threads).to_i
@@ -61,8 +61,8 @@ module GoodJob # :nodoc:
61
61
  # @param performer [GoodJob::JobPerformer]
62
62
  # @param max_threads [Numeric, nil] number of seconds between polls for jobs
63
63
  # @param max_cache [Numeric, nil] maximum number of scheduled jobs to cache in memory
64
- # @param warm_cache_on_initialize [Boolean] whether to warm the cache immediately
65
- def initialize(performer, max_threads: nil, max_cache: nil, warm_cache_on_initialize: true)
64
+ # @param warm_cache_on_initialize [Boolean] whether to warm the cache immediately, or manually by calling +warm_cache+
65
+ def initialize(performer, max_threads: nil, max_cache: nil, warm_cache_on_initialize: false)
66
66
  raise ArgumentError, "Performer argument must implement #next" unless performer.respond_to?(:next)
67
67
 
68
68
  self.class.instances << self
@@ -82,18 +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
96
- #
95
+ # @param timeout [Numeric, nil] Seconds to wait for actively executing jobs to finish
97
96
  # * +nil+, the scheduler will trigger a shutdown but not wait for it to complete.
98
97
  # * +-1+, the scheduler will wait until the shutdown is complete.
99
98
  # * +0+, the scheduler will immediately shutdown and stop any active tasks.
@@ -129,8 +128,8 @@ module GoodJob # :nodoc:
129
128
  end
130
129
 
131
130
  # Wakes a thread to allow the performer to execute a task.
132
- # @param state [nil, Object] Contextual information for the performer. See {JobPerformer#next?}.
133
- # @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.
134
133
  #
135
134
  # * +nil+ if the scheduler is unable to take new work, for example if the thread pool is shut down or at capacity.
136
135
  # * +true+ if the performer started executing work.
@@ -192,18 +191,31 @@ module GoodJob # :nodoc:
192
191
  def warm_cache
193
192
  return if @max_cache.zero?
194
193
 
195
- performer.next_at(
196
- limit: @max_cache,
197
- now_limit: @executor_options[:max_threads]
198
- ).each do |scheduled_at|
199
- create_thread({ scheduled_at: scheduled_at })
194
+ future = Concurrent::Future.new(args: [self, @performer], executor: executor) do |thr_scheduler, thr_performer|
195
+ Rails.application.executor.wrap do
196
+ thr_performer.next_at(
197
+ limit: @max_cache,
198
+ now_limit: @executor_options[:max_threads]
199
+ ).each do |scheduled_at|
200
+ thr_scheduler.create_thread({ scheduled_at: scheduled_at })
201
+ end
202
+ end
203
+ end
204
+
205
+ observer = lambda do |_time, _output, thread_error|
206
+ GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
207
+ create_task # If cache-warming exhausts the threads, ensure there isn't an executable task remaining
200
208
  end
209
+ future.add_observer(observer, :call)
210
+
211
+ future.execute
201
212
  end
202
213
 
203
214
  private
204
215
 
205
216
  attr_reader :performer, :executor, :timer_set
206
217
 
218
+ # @return [void]
207
219
  def create_executor
208
220
  instrument("scheduler_create_pool", { performer_name: performer.name, max_threads: @executor_options[:max_threads] }) do
209
221
  @timer_set = TimerSet.new
@@ -211,16 +223,21 @@ module GoodJob # :nodoc:
211
223
  end
212
224
  end
213
225
 
226
+ # @param delay [Integer]
227
+ # @return [void]
214
228
  def create_task(delay = 0)
215
229
  future = Concurrent::ScheduledTask.new(delay, args: [performer], executor: executor, timer_set: timer_set) do |thr_performer|
216
- output = nil
217
- Rails.application.executor.wrap { output = thr_performer.next }
218
- output
230
+ Rails.application.executor.wrap do
231
+ thr_performer.next
232
+ end
219
233
  end
220
234
  future.add_observer(self, :task_observer)
221
235
  future.execute
222
236
  end
223
237
 
238
+ # @param name [String]
239
+ # @param payload [Hash]
240
+ # @return [void]
224
241
  def instrument(name, payload = {}, &block)
225
242
  payload = payload.reverse_merge({
226
243
  scheduler: self,
@@ -1,4 +1,4 @@
1
1
  module GoodJob
2
2
  # GoodJob gem version.
3
- VERSION = '1.9.0'.freeze
3
+ VERSION = '1.9.5'.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.0
4
+ version: 1.9.5
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-04-16 00:00:00.000000000 Z
11
+ date: 2021-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -319,11 +319,12 @@ files:
319
319
  - LICENSE.txt
320
320
  - README.md
321
321
  - engine/app/assets/style.css
322
- - engine/app/assets/vendor/bootstrap/bootstrap-native.js
323
- - engine/app/assets/vendor/bootstrap/bootstrap.css
322
+ - engine/app/assets/vendor/bootstrap/bootstrap.bundle.min.js
323
+ - engine/app/assets/vendor/bootstrap/bootstrap.min.css
324
324
  - engine/app/assets/vendor/chartist/chartist.css
325
325
  - engine/app/assets/vendor/chartist/chartist.js
326
326
  - engine/app/controllers/good_job/active_jobs_controller.rb
327
+ - engine/app/controllers/good_job/assets_controller.rb
327
328
  - engine/app/controllers/good_job/base_controller.rb
328
329
  - engine/app/controllers/good_job/dashboards_controller.rb
329
330
  - engine/app/helpers/good_job/application_helper.rb
@@ -344,6 +345,7 @@ files:
344
345
  - lib/good_job/configuration.rb
345
346
  - lib/good_job/current_execution.rb
346
347
  - lib/good_job/daemon.rb
348
+ - lib/good_job/execution_result.rb
347
349
  - lib/good_job/job.rb
348
350
  - lib/good_job/job_performer.rb
349
351
  - lib/good_job/lockable.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
- })));