good_job 1.2.4 → 1.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -7
- data/README.md +13 -10
- data/engine/app/controllers/good_job/active_jobs_controller.rb +8 -0
- data/engine/app/controllers/good_job/base_controller.rb +5 -0
- data/engine/app/controllers/good_job/dashboards_controller.rb +50 -0
- data/engine/app/helpers/good_job/application_helper.rb +4 -0
- data/engine/app/views/assets/_style.css.erb +16 -0
- data/engine/app/views/good_job/active_jobs/show.html.erb +1 -0
- data/engine/app/views/good_job/dashboards/index.html.erb +19 -0
- data/engine/app/views/layouts/good_job/base.html.erb +50 -0
- data/engine/app/views/shared/_chart.erb +51 -0
- data/engine/app/views/shared/_jobs_table.erb +26 -0
- data/engine/app/views/vendor/bootstrap/_bootstrap-native.js.erb +1662 -0
- data/engine/app/views/vendor/bootstrap/_bootstrap.css.erb +10258 -0
- data/engine/app/views/vendor/chartist/_chartist.css.erb +613 -0
- data/engine/app/views/vendor/chartist/_chartist.js.erb +4516 -0
- data/engine/config/routes.rb +4 -0
- data/engine/lib/good_job/engine.rb +5 -0
- data/lib/active_job/queue_adapters/good_job_adapter.rb +3 -2
- data/lib/generators/good_job/install_generator.rb +8 -0
- data/lib/good_job.rb +40 -24
- data/lib/good_job/adapter.rb +38 -0
- data/lib/good_job/cli.rb +30 -7
- data/lib/good_job/configuration.rb +44 -0
- data/lib/good_job/job.rb +116 -20
- data/lib/good_job/lockable.rb +119 -6
- data/lib/good_job/log_subscriber.rb +70 -4
- data/lib/good_job/multi_scheduler.rb +6 -0
- data/lib/good_job/notifier.rb +55 -29
- data/lib/good_job/performer.rb +38 -0
- data/lib/good_job/railtie.rb +1 -0
- data/lib/good_job/scheduler.rb +33 -20
- data/lib/good_job/version.rb +2 -1
- metadata +96 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca2673887424565881a47ad6eaf9300b6281e43c87066f01ceb899b76716d43e
|
4
|
+
data.tar.gz: 6a0f749171316300ebfc7257d1d62efc330092391f026d6f50e20ccda5fee208
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fcef37d707e0d25f44965949abfeba914a4ee9f743ed32498e28df95751f666bdfb942c12049b23d78ff2b52e14465ca1cda2007bfae3541f9f0c53e6f74964
|
7
|
+
data.tar.gz: db7d224567bde1427210638226d5005dd279a2c73c97ff9c19eb40edc523ae7863973cccf7d263a08dcf56ec66fd112a492afa269dad4b12cc70b79027baf296
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,40 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v1.2.5](https://github.com/bensheldon/good_job/tree/v1.2.5) (2020-09-17)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.2.4...v1.2.5)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Use Zeitwerk for auto-loading [\#87](https://github.com/bensheldon/good_job/issues/87)
|
10
|
+
|
11
|
+
**Fixed bugs:**
|
12
|
+
|
13
|
+
- `poll-interval=-1` does not disable polling as intended [\#133](https://github.com/bensheldon/good_job/issues/133)
|
14
|
+
|
15
|
+
**Closed issues:**
|
16
|
+
|
17
|
+
- Lint - Introduce line character limits [\#122](https://github.com/bensheldon/good_job/issues/122)
|
18
|
+
- Jobs are not processed in multi schema setup. Apartment + GoodJob \( post 1.1.2 \) [\#117](https://github.com/bensheldon/good_job/issues/117)
|
19
|
+
- Host a documentation sprint [\#48](https://github.com/bensheldon/good_job/issues/48)
|
20
|
+
|
21
|
+
**Merged pull requests:**
|
22
|
+
|
23
|
+
- Test GoodJob against Rails HEAD [\#144](https://github.com/bensheldon/good_job/pull/144) ([bensheldon](https://github.com/bensheldon))
|
24
|
+
- Update Gemspec to reflect that GoodJob is not compatible with Rails 5.1 [\#143](https://github.com/bensheldon/good_job/pull/143) ([bensheldon](https://github.com/bensheldon))
|
25
|
+
- Drop Ruby 2.4 support [\#142](https://github.com/bensheldon/good_job/pull/142) ([morgoth](https://github.com/morgoth))
|
26
|
+
- Prevent jobs hanging [\#141](https://github.com/bensheldon/good_job/pull/141) ([morgoth](https://github.com/morgoth))
|
27
|
+
- Remove arguments from perform method [\#140](https://github.com/bensheldon/good_job/pull/140) ([morgoth](https://github.com/morgoth))
|
28
|
+
- Extract "execute" method to reduce "perform" method complexity [\#138](https://github.com/bensheldon/good_job/pull/138) ([morgoth](https://github.com/morgoth))
|
29
|
+
- Correct example on how to configure multiple queues by command line. [\#135](https://github.com/bensheldon/good_job/pull/135) ([morgoth](https://github.com/morgoth))
|
30
|
+
- Add explicit require\_paths to gemspec for engine [\#134](https://github.com/bensheldon/good_job/pull/134) ([bensheldon](https://github.com/bensheldon))
|
31
|
+
- Spike on data dashboard; pull in full Bootstrap CSS and JS [\#131](https://github.com/bensheldon/good_job/pull/131) ([bensheldon](https://github.com/bensheldon))
|
32
|
+
- Update ActionMailer Job class, to match the default [\#130](https://github.com/bensheldon/good_job/pull/130) ([morgoth](https://github.com/morgoth))
|
33
|
+
- Add initial Engine scaffold [\#125](https://github.com/bensheldon/good_job/pull/125) ([bensheldon](https://github.com/bensheldon))
|
34
|
+
- Use `connection.quote\_table\_name` and add spacing for SQL concatenation [\#124](https://github.com/bensheldon/good_job/pull/124) ([bensheldon](https://github.com/bensheldon))
|
35
|
+
- Zeitwerk Loader Implementation [\#123](https://github.com/bensheldon/good_job/pull/123) ([gadimbaylisahil](https://github.com/gadimbaylisahil))
|
36
|
+
- Update code-level documentation [\#111](https://github.com/bensheldon/good_job/pull/111) ([bensheldon](https://github.com/bensheldon))
|
37
|
+
|
3
38
|
## [v1.2.4](https://github.com/bensheldon/good_job/tree/v1.2.4) (2020-09-01)
|
4
39
|
|
5
40
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.2.3...v1.2.4)
|
@@ -88,7 +123,6 @@
|
|
88
123
|
**Merged pull requests:**
|
89
124
|
|
90
125
|
- Document GoodJob module [\#83](https://github.com/bensheldon/good_job/pull/83) ([bensheldon](https://github.com/bensheldon))
|
91
|
-
- Add Postgres LISTEN/NOTIFY support [\#82](https://github.com/bensheldon/good_job/pull/82) ([bensheldon](https://github.com/bensheldon))
|
92
126
|
|
93
127
|
## [v1.1.4](https://github.com/bensheldon/good_job/tree/v1.1.4) (2020-08-19)
|
94
128
|
|
@@ -101,6 +135,7 @@
|
|
101
135
|
|
102
136
|
**Merged pull requests:**
|
103
137
|
|
138
|
+
- Add Postgres LISTEN/NOTIFY support [\#82](https://github.com/bensheldon/good_job/pull/82) ([bensheldon](https://github.com/bensheldon))
|
104
139
|
- Allow Schedulers to filter \#create\_thread to avoid flood of queries when running async with multiple schedulers [\#81](https://github.com/bensheldon/good_job/pull/81) ([bensheldon](https://github.com/bensheldon))
|
105
140
|
- Fully name scheduler threadpools and thread names; refactor CLI STDOUT [\#80](https://github.com/bensheldon/good_job/pull/80) ([bensheldon](https://github.com/bensheldon))
|
106
141
|
|
@@ -182,6 +217,10 @@
|
|
182
217
|
|
183
218
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.0.1...v1.0.2)
|
184
219
|
|
220
|
+
**Fixed bugs:**
|
221
|
+
|
222
|
+
- Fix counting of available execution threads [\#58](https://github.com/bensheldon/good_job/pull/58) ([bensheldon](https://github.com/bensheldon))
|
223
|
+
|
185
224
|
**Merged pull requests:**
|
186
225
|
|
187
226
|
- Add migration generator [\#56](https://github.com/bensheldon/good_job/pull/56) ([thedanbob](https://github.com/thedanbob))
|
@@ -203,10 +242,6 @@
|
|
203
242
|
|
204
243
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v0.8.2...v0.9.0)
|
205
244
|
|
206
|
-
**Fixed bugs:**
|
207
|
-
|
208
|
-
- Fix counting of available execution threads [\#58](https://github.com/bensheldon/good_job/pull/58) ([bensheldon](https://github.com/bensheldon))
|
209
|
-
|
210
245
|
**Merged pull requests:**
|
211
246
|
|
212
247
|
- Allow preservation of finished job records [\#46](https://github.com/bensheldon/good_job/pull/46) ([bensheldon](https://github.com/bensheldon))
|
@@ -287,7 +322,6 @@
|
|
287
322
|
|
288
323
|
- Improve ActiveRecord usage for advisory locking [\#24](https://github.com/bensheldon/good_job/pull/24) ([bensheldon](https://github.com/bensheldon))
|
289
324
|
- Remove support for Rails 5.1 [\#23](https://github.com/bensheldon/good_job/pull/23) ([bensheldon](https://github.com/bensheldon))
|
290
|
-
- Clean up Gemspec [\#15](https://github.com/bensheldon/good_job/pull/15) ([bensheldon](https://github.com/bensheldon))
|
291
325
|
|
292
326
|
## [v0.3.0](https://github.com/bensheldon/good_job/tree/v0.3.0) (2020-03-22)
|
293
327
|
|
@@ -315,10 +349,12 @@
|
|
315
349
|
|
316
350
|
**Merged pull requests:**
|
317
351
|
|
352
|
+
- Clean up Gemspec [\#15](https://github.com/bensheldon/good_job/pull/15) ([bensheldon](https://github.com/bensheldon))
|
318
353
|
- Set up Rubocop [\#14](https://github.com/bensheldon/good_job/pull/14) ([bensheldon](https://github.com/bensheldon))
|
319
354
|
- Add pg gem as explicit dependency [\#13](https://github.com/bensheldon/good_job/pull/13) ([bensheldon](https://github.com/bensheldon))
|
320
355
|
- Bump nokogiri from 1.10.7 to 1.10.9 [\#12](https://github.com/bensheldon/good_job/pull/12) ([dependabot[bot]](https://github.com/apps/dependabot))
|
321
356
|
- Add Appraisal with tests for Rails 5.1, 5.2, 6.0 [\#11](https://github.com/bensheldon/good_job/pull/11) ([bensheldon](https://github.com/bensheldon))
|
357
|
+
- Use Rails.logger and ActiveSupport::Notifications for logging instead of puts [\#10](https://github.com/bensheldon/good_job/pull/10) ([bensheldon](https://github.com/bensheldon))
|
322
358
|
|
323
359
|
## [v0.2.0](https://github.com/bensheldon/good_job/tree/v0.2.0) (2020-03-06)
|
324
360
|
|
@@ -326,7 +362,6 @@
|
|
326
362
|
|
327
363
|
**Merged pull requests:**
|
328
364
|
|
329
|
-
- Use Rails.logger and ActiveSupport::Notifications for logging instead of puts [\#10](https://github.com/bensheldon/good_job/pull/10) ([bensheldon](https://github.com/bensheldon))
|
330
365
|
- Remove minitest files [\#9](https://github.com/bensheldon/good_job/pull/9) ([bensheldon](https://github.com/bensheldon))
|
331
366
|
- Use scheduled\_at and priority for scheduling [\#8](https://github.com/bensheldon/good_job/pull/8) ([bensheldon](https://github.com/bensheldon))
|
332
367
|
- Create Github Action workflow for PRs and Issues [\#7](https://github.com/bensheldon/good_job/pull/7) ([bensheldon](https://github.com/bensheldon))
|
data/README.md
CHANGED
@@ -184,7 +184,7 @@ To use GoodJob, you can set `config.active_job.queue_adapter` to a `:good_job` o
|
|
184
184
|
|
185
185
|
- `execution_mode` (symbol) specifies how and where jobs should be executed. You can also set this with the environment variable `GOOD_JOB_EXECUTION_MODE`. It can be any one of:
|
186
186
|
- `:inline` executes jobs immediately in whatever process queued them (usually the web server process). This should only be used in test and development environments.
|
187
|
-
- `:external` causes the adapter to
|
187
|
+
- `:external` causes the adapter to enqueue jobs, but not execute them. When using this option (the default for production environments), you’ll need to use the command-line tool to actually execute your jobs.
|
188
188
|
- `:async` causes the adapter to execute you jobs in separate threads in whatever process queued them (usually the web process). This is akin to running the command-line tool’s code inside your web server. It can be more economical for small workloads (you don’t need a separate machine or environment for running your jobs), but if your web server is under heavy load or your jobs require a lot of resources, you should choose `:external` instead.
|
189
189
|
- `max_threads` (integer) sets the maximum number of threads to use when `execution_mode` is set to `:async`. You can also set this with the environment variable `GOOD_JOB_MAX_THREADS`.
|
190
190
|
- `queues` (string) determines which queues to execute jobs from when `execution_mode` is set to `:async`. See the description of `good_job start` for more details on the format of this string. You can also set this with the environment variable `GOOD_JOB_QUEUES`.
|
@@ -210,7 +210,7 @@ Good Job’s general behavior can also be configured via several attributes dire
|
|
210
210
|
- **`GoodJob.logger`** ([Rails Logger](https://api.rubyonrails.org/classes/ActiveSupport/Logger.html)) lets you set a custom logger for GoodJob. It should be an instance of a Rails `Logger`.
|
211
211
|
- **`GoodJob.preserve_job_records`** (boolean) keeps job records in your database even after jobs are completed. (Default: `false`)
|
212
212
|
- **`GoodJob.reperform_jobs_on_standard_error`** (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `true`)
|
213
|
-
- **`GoodJob.on_thread_error`** (proc, lambda, or callable) will be called when
|
213
|
+
- **`GoodJob.on_thread_error`** (proc, lambda, or callable) will be called when an Exception. It can be useful for logging errors to bug tracking services, like Sentry or Airbrake.
|
214
214
|
|
215
215
|
You’ll generally want to configure these in `config/initializers/good_job.rb`, like so:
|
216
216
|
|
@@ -291,16 +291,16 @@ end
|
|
291
291
|
|
292
292
|
#### ActionMailer retries
|
293
293
|
|
294
|
-
Any configuration in `ApplicationJob` will have to be duplicated on `ActionMailer::DeliveryJob` because ActionMailer uses a custom class, `ActionMailer::
|
294
|
+
Any configuration in `ApplicationJob` will have to be duplicated on `ActionMailer::MailDeliveryJob` (`ActionMailer::DeliveryJob` in Rails 5.2 or earlier) because ActionMailer uses a custom class, `ActionMailer::MailDeliveryJob`, which inherits from `ActiveJob::Base`, rather than your applications `ApplicationJob`.
|
295
295
|
|
296
|
-
You can use an initializer to configure `ActionMailer::
|
296
|
+
You can use an initializer to configure `ActionMailer::MailDeliveryJob`, for example:
|
297
297
|
|
298
298
|
```ruby
|
299
299
|
# config/initializers/good_job.rb
|
300
|
-
ActionMailer::
|
300
|
+
ActionMailer::MailDeliveryJob.retry_on StandardError, wait: :exponentially_longer, attempts: Float::INFINITY
|
301
301
|
|
302
302
|
# With Sentry (or Bugsnag, Airbrake, Honeybadger, etc.)
|
303
|
-
ActionMailer::
|
303
|
+
ActionMailer::MailDeliveryJob.around_perform do |_job, block|
|
304
304
|
block.call
|
305
305
|
rescue StandardError => e
|
306
306
|
Raven.capture_exception(e)
|
@@ -308,6 +308,9 @@ rescue StandardError => e
|
|
308
308
|
end
|
309
309
|
```
|
310
310
|
|
311
|
+
Note, that `ActionMailer::MailDeliveryJob` is a default since Rails 6.0. Be sure that your app is using that class, as it
|
312
|
+
might also be configured to use (deprecated now) `ActionMailer::DeliveryJob`.
|
313
|
+
|
311
314
|
### Timeouts
|
312
315
|
|
313
316
|
Job timeouts can be configured with an `around_perform`:
|
@@ -332,7 +335,7 @@ By default, GoodJob creates a single thread execution pool that will execute job
|
|
332
335
|
- Multiple execution pools within a single process:
|
333
336
|
|
334
337
|
```bash
|
335
|
-
$ bundle exec good_job --queues=transactional_messages:2;batch_processing:1;-transactional_messages,batch_processing:2;* --max-threads=5
|
338
|
+
$ bundle exec good_job --queues="transactional_messages:2;batch_processing:1;-transactional_messages,batch_processing:2;*" --max-threads=5
|
336
339
|
```
|
337
340
|
|
338
341
|
This configuration will result in a single process with 4 isolated thread execution pools. Isolated execution pools are separated with a semicolon (`;`) and queue names and thread counts with a colon (`:`)
|
@@ -357,11 +360,11 @@ By default, GoodJob creates a single thread execution pool that will execute job
|
|
357
360
|
|
358
361
|
# Separate dyno types
|
359
362
|
worker: bundle exec good_job --max-threads=5
|
360
|
-
transactional_worker: bundle exec good_job --queues=transactional_messages --max-threads=2
|
361
|
-
batch_worker: bundle exec good_job --queues=batch_processing --max-threads=1
|
363
|
+
transactional_worker: bundle exec good_job --queues="transactional_messages" --max-threads=2
|
364
|
+
batch_worker: bundle exec good_job --queues="batch_processing" --max-threads=1
|
362
365
|
|
363
366
|
# Combined multi-process dyno
|
364
|
-
combined_worker: bundle exec good_job --max-threads=5 & bundle exec good_job --queues=transactional_messages --max-threads=2 & bundle exec good_job --queues=batch_processing --max-threads=1 & wait -n
|
367
|
+
combined_worker: bundle exec good_job --max-threads=5 & bundle exec good_job --queues="transactional_messages" --max-threads=2 & bundle exec good_job --queues="batch_processing" --max-threads=1 & wait -n
|
365
368
|
```
|
366
369
|
|
367
370
|
Running multiple processes can optimize for CPU performance at the expense of greater memory and system resource usage.
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module GoodJob
|
2
|
+
class DashboardsController < GoodJob::BaseController
|
3
|
+
def index
|
4
|
+
@jobs = GoodJob::Job.display_all(after_scheduled_at: params[:after_scheduled_at], after_id: params[:after_id])
|
5
|
+
.limit(params.fetch(:limit, 10))
|
6
|
+
|
7
|
+
job_data = GoodJob::Job.connection.exec_query Arel.sql(<<~SQL)
|
8
|
+
SELECT *
|
9
|
+
FROM generate_series(
|
10
|
+
date_trunc('hour', NOW() - '1 day'::interval),
|
11
|
+
date_trunc('hour', NOW()),
|
12
|
+
'1 hour'
|
13
|
+
) timestamp
|
14
|
+
LEFT JOIN (
|
15
|
+
SELECT
|
16
|
+
date_trunc('hour', scheduled_at) AS scheduled_at,
|
17
|
+
queue_name,
|
18
|
+
count(*) AS count
|
19
|
+
FROM (
|
20
|
+
SELECT
|
21
|
+
COALESCE(scheduled_at, created_at)::timestamp AS scheduled_at,
|
22
|
+
queue_name
|
23
|
+
FROM good_jobs
|
24
|
+
) sources
|
25
|
+
GROUP BY date_trunc('hour', scheduled_at), queue_name
|
26
|
+
) sources ON sources.scheduled_at = timestamp
|
27
|
+
ORDER BY timestamp DESC
|
28
|
+
SQL
|
29
|
+
|
30
|
+
queue_names = job_data.map { |d| d['queue_name'] }.uniq
|
31
|
+
labels = []
|
32
|
+
queues_data = job_data.to_a.group_by { |d| d['timestamp'] }.each_with_object({}) do |(timestamp, values), hash|
|
33
|
+
labels << timestamp
|
34
|
+
queue_names.each do |queue_name|
|
35
|
+
(hash[queue_name] ||= []) << values.find { |d| d['queue_name'] == queue_name }&.[]('count')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
@chart = {
|
40
|
+
labels: labels,
|
41
|
+
series: queues_data.map do |queue, data|
|
42
|
+
{
|
43
|
+
name: queue,
|
44
|
+
data: data,
|
45
|
+
}
|
46
|
+
end,
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
.tooltip {
|
2
|
+
position: absolute;
|
3
|
+
z-index: 1;
|
4
|
+
padding: 5px;
|
5
|
+
background: rgba(0, 0, 0, 0.3);
|
6
|
+
opacity: 1;
|
7
|
+
border-radius: 3px;
|
8
|
+
text-align: center;
|
9
|
+
pointer-events: none;
|
10
|
+
color: white;
|
11
|
+
transition: opacity .1s ease-out;
|
12
|
+
}
|
13
|
+
|
14
|
+
.tooltip.tooltip-hidden {
|
15
|
+
opacity: 0;
|
16
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render 'shared/jobs_table', jobs: @jobs %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<div class="card my-3 p-6">
|
2
|
+
<%= render 'shared/chart', chart_data: @chart %>
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<% if @jobs.present? %>
|
6
|
+
<%= render 'shared/jobs_table', jobs: @jobs %>
|
7
|
+
|
8
|
+
<nav aria-label="Job pagination">
|
9
|
+
<ul class="pagination">
|
10
|
+
<li class="page-item">
|
11
|
+
<%= link_to({ after_scheduled_at: (@jobs.last.scheduled_at || @jobs.last.created_at), after_id: @jobs.last.id }, class: "page-link") do %>
|
12
|
+
Next jobs <span aria-hidden="true">»</span>
|
13
|
+
<% end %>
|
14
|
+
</li>
|
15
|
+
</ul>
|
16
|
+
</nav>
|
17
|
+
<% else %>
|
18
|
+
<em>No jobs present.</em>
|
19
|
+
<% end %>
|
@@ -0,0 +1,50 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Good job</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<style>
|
9
|
+
<%= render "vendor/bootstrap/bootstrap.css" %>
|
10
|
+
<%= render "vendor/chartist/chartist.css" %>
|
11
|
+
<%= render "assets/style.css" %>
|
12
|
+
</style>
|
13
|
+
|
14
|
+
<script>
|
15
|
+
<%= render "vendor/bootstrap/bootstrap-native.js" %>
|
16
|
+
<%= render "vendor/chartist/chartist.js" %>
|
17
|
+
</script>
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
21
|
+
<div class="container">
|
22
|
+
<%= link_to "GoodJob 👍", root_path, class: 'navbar-brand mb-0 h1' %>
|
23
|
+
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
24
|
+
<span class="navbar-toggler-icon"></span>
|
25
|
+
</button>
|
26
|
+
|
27
|
+
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
28
|
+
<ul class="navbar-nav mr-auto">
|
29
|
+
<li class="nav-item">
|
30
|
+
<%= link_to "All jobs", root_path, class: ["nav-link", ("active" if current_page?(root_path))] %>
|
31
|
+
</li>
|
32
|
+
<li class="nav-item">
|
33
|
+
<%= link_to "Upcoming Jobs", 'todo', class: ["nav-link", ("active" if current_page?('todo'))] %>
|
34
|
+
</li>
|
35
|
+
<li class="nav-item">
|
36
|
+
<%= link_to "Finished Jobs", 'todo', class: ["nav-link", ("active" if current_page?('todo'))] %>
|
37
|
+
</li>
|
38
|
+
<li class="nav-item">
|
39
|
+
<%= link_to "Errored Jobs", 'todo', class: ["nav-link", ("active" if current_page?('todo'))] %>
|
40
|
+
</li>
|
41
|
+
</ul>
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
</nav>
|
45
|
+
|
46
|
+
<div class="container">
|
47
|
+
<%= yield %>
|
48
|
+
</div>
|
49
|
+
</body>
|
50
|
+
</html>
|
@@ -0,0 +1,51 @@
|
|
1
|
+
<div id="chart"></div>
|
2
|
+
|
3
|
+
<script>
|
4
|
+
new Chartist.Line('#chart', <%= raw chart_data.to_json %>, {
|
5
|
+
height: '300px',
|
6
|
+
fullWidth: true,
|
7
|
+
chartPadding: {
|
8
|
+
right: 40,
|
9
|
+
top: 20
|
10
|
+
},
|
11
|
+
axisX: {
|
12
|
+
labelInterpolationFnc: function(value, index) {
|
13
|
+
return index % 3 === 0 ? value : null;
|
14
|
+
}
|
15
|
+
},
|
16
|
+
axisY: {
|
17
|
+
low: 0,
|
18
|
+
onlyInteger: true
|
19
|
+
}
|
20
|
+
})
|
21
|
+
|
22
|
+
// https://www.smashingmagazine.com/2014/12/chartist-js-open-source-library-responsive-charts/
|
23
|
+
const chartEl = document.getElementById('chart');
|
24
|
+
const tooltipEl = document.createElement('div')
|
25
|
+
|
26
|
+
tooltipEl.classList.add('tooltip', 'tooltip-hidden');
|
27
|
+
chartEl.appendChild(tooltipEl);
|
28
|
+
|
29
|
+
document.body.addEventListener('mouseenter', function (event) {
|
30
|
+
if (!(event.target.matches && event.target.matches('.ct-point'))) return;
|
31
|
+
|
32
|
+
const seriesName = event.target.closest('.ct-series').getAttribute('ct:series-name');
|
33
|
+
const value = event.target.getAttribute('ct:value');
|
34
|
+
|
35
|
+
tooltipEl.innerText = seriesName + ': ' + value;
|
36
|
+
tooltipEl.classList.remove('tooltip-hidden');
|
37
|
+
}, true);
|
38
|
+
|
39
|
+
document.body.addEventListener('mouseleave', function (event) {
|
40
|
+
if (!(event.target.matches && event.target.matches('.ct-point'))) return;
|
41
|
+
|
42
|
+
tooltipEl.classList.add('tooltip-hidden');
|
43
|
+
}, true);
|
44
|
+
|
45
|
+
document.body.addEventListener('mousemove', function(event) {
|
46
|
+
if (!(event.target.matches && event.target.matches('.ct-point'))) return;
|
47
|
+
|
48
|
+
tooltipEl.style.left = (event.offsetX || event.originalEvent.layerX) + tooltipEl.offsetWidth + 10 + 'px';
|
49
|
+
tooltipEl.style.top = (event.offsetY || event.originalEvent.layerY) + tooltipEl.offsetHeight - 20 + 'px';
|
50
|
+
}, true);
|
51
|
+
</script>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<div class="table-responsive">
|
2
|
+
<table class="table table-bordered table-hover">
|
3
|
+
<thead>
|
4
|
+
<th>GoodJob ID</th>
|
5
|
+
<th>ActiveJob ID</th>
|
6
|
+
<th>Job Class</th>
|
7
|
+
<th>Queue</th>
|
8
|
+
<th>Scheduled At</th>
|
9
|
+
<th>ActiveJob Params</th>
|
10
|
+
<th>Error</th>
|
11
|
+
</thead>
|
12
|
+
<tbody>
|
13
|
+
<% jobs.each do |job| %>
|
14
|
+
<tr id="<%= dom_id(job) %>">
|
15
|
+
<td><%= link_to job.id, active_job_path(job.serialized_params['job_id'], anchor: dom_id(job)) %></td>
|
16
|
+
<td><%= link_to job.serialized_params['job_id'], active_job_path(job.serialized_params['job_id']) %></td>
|
17
|
+
<td><%= job.serialized_params['job_class'] %></td>
|
18
|
+
<td><%= job.queue_name %></td>
|
19
|
+
<td><%= job.scheduled_at || job.created_at %></td>
|
20
|
+
<td><%= job.serialized_params %></td>
|
21
|
+
<td><%= job.error %></td>
|
22
|
+
</tr>
|
23
|
+
<% end %>
|
24
|
+
</tbody>
|
25
|
+
</table>
|
26
|
+
</div>
|
@@ -0,0 +1,1662 @@
|
|
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
|
+
})));
|