jquery-modaal-rails 0.4.4 → 0.4.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +15 -0
  5. data/Gemfile.lock +144 -0
  6. data/jquery-modaal-rails.gemspec +22 -0
  7. data/lib/jquery/modaal/rails/version.rb +1 -1
  8. data/spec/dummy/Rakefile +6 -0
  9. data/spec/dummy/app/assets/config/manifest.js +4 -0
  10. data/spec/dummy/app/assets/images/.keep +0 -0
  11. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  12. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  13. data/spec/dummy/app/assets/javascripts/channels/.keep +0 -0
  14. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  15. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  16. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  17. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  18. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  19. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  20. data/spec/dummy/app/jobs/application_job.rb +2 -0
  21. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  22. data/spec/dummy/app/models/application_record.rb +3 -0
  23. data/spec/dummy/app/models/concerns/.keep +0 -0
  24. data/spec/dummy/app/views/layouts/application.html.erb +15 -0
  25. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  26. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  27. data/spec/dummy/bin/bundle +3 -0
  28. data/spec/dummy/bin/rails +4 -0
  29. data/spec/dummy/bin/rake +4 -0
  30. data/spec/dummy/bin/setup +36 -0
  31. data/spec/dummy/bin/update +31 -0
  32. data/spec/dummy/bin/yarn +11 -0
  33. data/spec/dummy/config.ru +5 -0
  34. data/spec/dummy/config/application.rb +26 -0
  35. data/spec/dummy/config/boot.rb +5 -0
  36. data/spec/dummy/config/cable.yml +10 -0
  37. data/spec/dummy/config/environment.rb +5 -0
  38. data/spec/dummy/config/environments/development.rb +55 -0
  39. data/spec/dummy/config/environments/production.rb +94 -0
  40. data/spec/dummy/config/environments/test.rb +46 -0
  41. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  42. data/spec/dummy/config/initializers/assets.rb +14 -0
  43. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  44. data/spec/dummy/config/initializers/content_security_policy.rb +25 -0
  45. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  46. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  47. data/spec/dummy/config/initializers/inflections.rb +16 -0
  48. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  49. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  50. data/spec/dummy/config/locales/en.yml +33 -0
  51. data/spec/dummy/config/puma.rb +34 -0
  52. data/spec/dummy/config/routes.rb +3 -0
  53. data/spec/dummy/config/spring.rb +6 -0
  54. data/spec/dummy/config/storage.yml +34 -0
  55. data/spec/dummy/lib/assets/.keep +0 -0
  56. data/spec/dummy/log/.keep +0 -0
  57. data/spec/dummy/package.json +5 -0
  58. data/spec/dummy/public/404.html +67 -0
  59. data/spec/dummy/public/422.html +67 -0
  60. data/spec/dummy/public/500.html +66 -0
  61. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  62. data/spec/dummy/public/apple-touch-icon.png +0 -0
  63. data/spec/dummy/public/favicon.ico +0 -0
  64. data/spec/jquery-modaal-rails_spec.rb +13 -0
  65. data/spec/rails_helper.rb +53 -0
  66. data/spec/spec_helper.rb +96 -0
  67. data/vendor/assets/javascripts/modaal.js +1390 -0
  68. data/vendor/assets/stylesheets/modaal.css +782 -0
  69. metadata +67 -1
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ mount Jquery::Modaal::Rails::Engine => "/jquery-modaal-rails"
3
+ end
@@ -0,0 +1,6 @@
1
+ %w[
2
+ .ruby-version
3
+ .rbenv-vars
4
+ tmp/restart.txt
5
+ tmp/caching-dev.txt
6
+ ].each { |path| Spring.watch(path) }
@@ -0,0 +1,34 @@
1
+ test:
2
+ service: Disk
3
+ root: <%= Rails.root.join("tmp/storage") %>
4
+
5
+ local:
6
+ service: Disk
7
+ root: <%= Rails.root.join("storage") %>
8
+
9
+ # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10
+ # amazon:
11
+ # service: S3
12
+ # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13
+ # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14
+ # region: us-east-1
15
+ # bucket: your_own_bucket
16
+
17
+ # Remember not to checkin your GCS keyfile to a repository
18
+ # google:
19
+ # service: GCS
20
+ # project: your_project
21
+ # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22
+ # bucket: your_own_bucket
23
+
24
+ # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25
+ # microsoft:
26
+ # service: AzureStorage
27
+ # storage_account_name: your_account_name
28
+ # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29
+ # container: your_container_name
30
+
31
+ # mirror:
32
+ # service: Mirror
33
+ # primary: local
34
+ # mirrors: [ amazon, google, microsoft ]
File without changes
File without changes
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "dummy",
3
+ "private": true,
4
+ "dependencies": {}
5
+ }
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ .rails-default-error-page {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ .rails-default-error-page div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ .rails-default-error-page div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ .rails-default-error-page h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ .rails-default-error-page div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body class="rails-default-error-page">
58
+ <!-- This file lives in public/404.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The page you were looking for doesn't exist.</h1>
62
+ <p>You may have mistyped the address or the page may have moved.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ .rails-default-error-page {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ .rails-default-error-page div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ .rails-default-error-page div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ .rails-default-error-page h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ .rails-default-error-page div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body class="rails-default-error-page">
58
+ <!-- This file lives in public/422.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The change you wanted was rejected.</h1>
62
+ <p>Maybe you tried to change something you didn't have access to.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ .rails-default-error-page {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ .rails-default-error-page div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ .rails-default-error-page div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ .rails-default-error-page h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ .rails-default-error-page div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body class="rails-default-error-page">
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
File without changes
@@ -0,0 +1,13 @@
1
+ require 'rails_helper'
2
+
3
+ describe Dummy::Application do
4
+ let(:app) { Dummy::Application }
5
+
6
+ it "modaal.js should be found as an asset" do
7
+ expect(app.assets['modaal.js']).to_not be_nil
8
+ end
9
+
10
+ it "modaal.css should be found as an asset" do
11
+ expect(app.assets['modaal.css']).to_not be_nil
12
+ end
13
+ end
@@ -0,0 +1,53 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ require 'spec_helper'
3
+ ENV['RAILS_ENV'] ||= 'test'
4
+ require File.expand_path('../dummy/config/environment', __FILE__)
5
+ # Prevent database truncation if the environment is production
6
+ abort("The Rails environment is running in production mode!") if Rails.env.production?
7
+ require 'rspec/rails'
8
+ # Add additional requires below this line. Rails is not loaded until this point!
9
+
10
+ # Requires supporting ruby files with custom matchers and macros, etc, in
11
+ # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
12
+ # run as spec files by default. This means that files in spec/support that end
13
+ # in _spec.rb will both be required and run as specs, causing the specs to be
14
+ # run twice. It is recommended that you do not name files matching this glob to
15
+ # end with _spec.rb. You can configure this pattern with the --pattern
16
+ # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
17
+ #
18
+ # The following line is provided for convenience purposes. It has the downside
19
+ # of increasing the boot-up time by auto-requiring all files in the support
20
+ # directory. Alternatively, in the individual `*_spec.rb` files, manually
21
+ # require only the support files necessary.
22
+ #
23
+ # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
24
+
25
+ RSpec.configure do |config|
26
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
27
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
28
+
29
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
30
+ # examples within a transaction, remove the following line or assign false
31
+ # instead of true.
32
+ config.use_transactional_fixtures = true
33
+
34
+ # RSpec Rails can automatically mix in different behaviours to your tests
35
+ # based on their file location, for example enabling you to call `get` and
36
+ # `post` in specs under `spec/controllers`.
37
+ #
38
+ # You can disable this behaviour by removing the line below, and instead
39
+ # explicitly tag your specs with their type, e.g.:
40
+ #
41
+ # RSpec.describe UsersController, :type => :controller do
42
+ # # ...
43
+ # end
44
+ #
45
+ # The different available types are documented in the features, such as in
46
+ # https://relishapp.com/rspec/rspec-rails/docs
47
+ config.infer_spec_type_from_file_location!
48
+
49
+ # Filter lines from Rails gems in backtraces.
50
+ config.filter_rails_from_backtrace!
51
+ # arbitrary gems may also be filtered via:
52
+ # config.filter_gems_from_backtrace("gem name")
53
+ end
@@ -0,0 +1,96 @@
1
+ # This file was generated by the `rails generate rspec:install` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
+ RSpec.configure do |config|
17
+ # rspec-expectations config goes here. You can use an alternate
18
+ # assertion/expectation library such as wrong or the stdlib/minitest
19
+ # assertions if you prefer.
20
+ config.expect_with :rspec do |expectations|
21
+ # This option will default to `true` in RSpec 4. It makes the `description`
22
+ # and `failure_message` of custom matchers include text for helper methods
23
+ # defined using `chain`, e.g.:
24
+ # be_bigger_than(2).and_smaller_than(4).description
25
+ # # => "be bigger than 2 and smaller than 4"
26
+ # ...rather than:
27
+ # # => "be bigger than 2"
28
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29
+ end
30
+
31
+ # rspec-mocks config goes here. You can use an alternate test double
32
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
33
+ config.mock_with :rspec do |mocks|
34
+ # Prevents you from mocking or stubbing a method that does not exist on
35
+ # a real object. This is generally recommended, and will default to
36
+ # `true` in RSpec 4.
37
+ mocks.verify_partial_doubles = true
38
+ end
39
+
40
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41
+ # have no way to turn it off -- the option exists only for backwards
42
+ # compatibility in RSpec 3). It causes shared context metadata to be
43
+ # inherited by the metadata hash of host groups and examples, rather than
44
+ # triggering implicit auto-inclusion in groups with matching metadata.
45
+ config.shared_context_metadata_behavior = :apply_to_host_groups
46
+
47
+ # The settings below are suggested to provide a good initial experience
48
+ # with RSpec, but feel free to customize to your heart's content.
49
+ =begin
50
+ # This allows you to limit a spec run to individual examples or groups
51
+ # you care about by tagging them with `:focus` metadata. When nothing
52
+ # is tagged with `:focus`, all examples get run. RSpec also provides
53
+ # aliases for `it`, `describe`, and `context` that include `:focus`
54
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55
+ config.filter_run_when_matching :focus
56
+
57
+ # Allows RSpec to persist some state between runs in order to support
58
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
59
+ # you configure your source control system to ignore this file.
60
+ config.example_status_persistence_file_path = "spec/examples.txt"
61
+
62
+ # Limits the available syntax to the non-monkey patched syntax that is
63
+ # recommended. For more details, see:
64
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
65
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
66
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
67
+ config.disable_monkey_patching!
68
+
69
+ # Many RSpec users commonly either run the entire suite or an individual
70
+ # file, and it's useful to allow more verbose output when running an
71
+ # individual spec file.
72
+ if config.files_to_run.one?
73
+ # Use the documentation formatter for detailed output,
74
+ # unless a formatter has already been configured
75
+ # (e.g. via a command-line flag).
76
+ config.default_formatter = "doc"
77
+ end
78
+
79
+ # Print the 10 slowest examples and example groups at the
80
+ # end of the spec run, to help surface which specs are running
81
+ # particularly slow.
82
+ config.profile_examples = 10
83
+
84
+ # Run specs in random order to surface order dependencies. If you find an
85
+ # order dependency and want to debug it, you can fix the order by providing
86
+ # the seed, which is printed after each run.
87
+ # --seed 1234
88
+ config.order = :random
89
+
90
+ # Seed global randomization in this process using the `--seed` CLI option.
91
+ # Setting this allows you to use `--seed` to deterministically reproduce
92
+ # test failures related to randomization by passing the same `--seed` value
93
+ # as the one that triggered the failure.
94
+ Kernel.srand config.seed
95
+ =end
96
+ end
@@ -0,0 +1,1390 @@
1
+ /*!
2
+ Modaal - accessible modals - v0.4.4
3
+ by Humaan, for all humans.
4
+ http://humaan.com
5
+ */
6
+ /**
7
+ Modaal jQuery Plugin : Accessible Modals
8
+
9
+ ==== General Options ===
10
+ type (string) : ajax, inline, image, iframe, confirm. Defaults to 'inline'
11
+ content_source (stribg) : Accepts a string value for your target element, such as '#my-content'. This allows for when trigger element is
12
+ an `<a href="#">` link. Not to be confused with the already existing `source` event.
13
+ animation (string) : Fade, expand, down, up. Defaults to 'fade'
14
+ after_callback_delay (integer) : Specify a delay value for the after open callbacks. This is necessary because with the bundled animations
15
+ have a set duration in the bundled CSS. Specify a delay of the same amount as the animation duration in so
16
+ more accurately fire the after open/close callbacks. Defaults 350, does not apply if animation is 'none',
17
+ after open callbacks are dispatched immediately
18
+
19
+ is_locked (boolean) : Set this to true to disable closing the modal via keypress or clicking the background. Beware that if
20
+ type != 'confirm' there will be no interface to dismiss the modal if is_locked = true, you'd have to
21
+ programmatically arrange to dismiss the modal. Confirm modals are always locked regardless of this option
22
+ Defaults to false
23
+
24
+ hide_close (boolean) : Set this to true to hide the close modal button. Key press and overlay click will still close the modal.
25
+ This method is best used when you want to put a custom close button inside the modal container space.
26
+
27
+ background (string) : Background overlay style. Defaults to '#000'
28
+ overlay_opacity (float) : Background overlay transparency. Defaults to 0.8
29
+ overlay_close (boolean) : Set this to false if you want to disable click to close on overlay background.
30
+
31
+ accessible_title (string) : Accessible title. Default 'Dialog Window'
32
+ start_open (boolean) : Set this to true to launch the Modaal window immediately on page open
33
+ fullscreen (boolean) : Set this to true to make the modaal fill the entire screen, false will default to own width/height attributes.
34
+ custom_class (string) : Fill in this string with a custom class that will be applied to the outer most modal wrapper.
35
+
36
+ width (integer) : Desired width of the modal. Required for iframe type. Defaults to undefined //TODO
37
+ height (integer) : Desired height of the modal. Required for iframe type. Defaults to undefined //TODO
38
+
39
+ background_scroll (boolean) : Set this to true to enable the page to scroll behind the open modal.
40
+
41
+ should_open (boolean|function) : Boolean or closure that returns a boolean to determine whether to open the modal or not.
42
+
43
+ close_text : String for close button text. Available for localisation and alternative languages to be used.
44
+ close_aria_label : String for close button aria-label attribute (value that screen readers will read out). Available for localisation and alternative languages to be used.
45
+
46
+ === Events ===
47
+ before_open (function) : Callback function executed before modal is opened
48
+ after_open (function) : Callback function executed after modal is opened
49
+ before_close (function) : Callback function executed before modal is closed
50
+ after_close (function) : Callback function executed after modal is closed
51
+ source (function(element, src)) : Callback function executed on the default source, it is intended to transform the
52
+ source (href in an AJAX modal or iframe). The function passes in the triggering element
53
+ as well as the default source depending of the modal type. The default output of the
54
+ function is an untransformed default source.
55
+
56
+
57
+ === Confirm Options & Events ===
58
+ confirm_button_text (string) : Text on the confirm button. Defaults to 'Confirm'
59
+ confirm_cancel_button_text (string) : Text on the confirm modal cancel button. Defaults to 'Cancel'
60
+ confirm_title (string) : Title for confirm modal. Default 'Confirm Title'
61
+ confirm_content (string) : HTML content for confirm message
62
+ confirm_callback (function) : Callback function for when the confirm button is pressed as opposed to cancel
63
+ confirm_cancel_callback (function) : Callback function for when the cancel button is pressed
64
+
65
+
66
+ === Gallery Options & Events ===
67
+ gallery_active_class (string) : Active class applied to the currently active image or image slide in a gallery 'gallery_active_item'
68
+ outer_controls (boolean) : Set to true to put the next/prev controls outside the Modaal wrapper, at the edges of the browser window.
69
+ before_image_change (function) : Callback function executed before the image slide changes in a gallery modal. Default function( current_item, incoming_item )
70
+ after_image_change (function) : Callback function executed after the image slide changes in a gallery modal. Default function ( current_item )
71
+
72
+
73
+ === AJAX Options & Events ===
74
+ loading_content (string) : HTML content for loading message. Default 'Loading &hellip;'
75
+ loading_class (string) : Class name to be applied while content is loaded via AJAX. Default 'is_loading'
76
+ ajax_error_class (string) : Class name to be applied when content has failed to load. Default is 'modaal-error'
77
+ ajax_success (function) : Callback for when AJAX content is loaded in
78
+
79
+
80
+ === SOCIAL CONTENT ===
81
+ instagram_id (string) : Unique photo ID for an Instagram photo.
82
+
83
+ */
84
+ ( function( $ ) {
85
+
86
+ var modaal_loading_spinner = '<div class="modaal-loading-spinner"><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div></div>'
87
+
88
+ var Modaal = {
89
+ init : function(options, elem) {
90
+ var self = this;
91
+
92
+ self.dom = $('body');
93
+
94
+ self.$elem = $(elem);
95
+ self.options = $.extend({}, $.fn.modaal.options, self.$elem.data(), options);
96
+ self.xhr = null;
97
+
98
+ // set up the scope
99
+ self.scope = {
100
+ is_open: false,
101
+ id: 'modaal_' + ( new Date().getTime() ) + ( Math.random().toString(16).substring(2) ),
102
+ source: self.options.content_source ? self.options.content_source : self.$elem.attr('href')
103
+ };
104
+
105
+ // add scope attribute to trigger element
106
+ self.$elem.attr('data-modaal-scope', self.scope.id);
107
+
108
+ // private options
109
+ self.private_options = {
110
+ active_class: 'is_active'
111
+ };
112
+
113
+ self.lastFocus = null;
114
+
115
+ // if is_locked
116
+ if ( self.options.is_locked || self.options.type == 'confirm' || self.options.hide_close ) {
117
+ self.scope.close_btn = '';
118
+ } else {
119
+ self.scope.close_btn = '<button type="button" class="modaal-close" id="modaal-close" aria-label="' + self.options.close_aria_label + '"><span>' + self.options.close_text + '</span></button>';
120
+ }
121
+
122
+ // reset animation_speed
123
+ if (self.options.animation === 'none' ){
124
+ self.options.animation_speed = 0;
125
+ self.options.after_callback_delay = 0;
126
+ }
127
+
128
+ // On click to open modal
129
+ $(elem).on('click.Modaal', function(e) {
130
+ e.preventDefault();
131
+ self.create_modaal(self, e);
132
+ });
133
+
134
+ // Define next/prev buttons
135
+ if (self.options.outer_controls === true) {
136
+ var mod_class = 'outer';
137
+ } else {
138
+ var mod_class = 'inner';
139
+ }
140
+ self.scope.prev_btn = '<button type="button" class="modaal-gallery-control modaal-gallery-prev modaal-gallery-prev-' + mod_class + '" id="modaal-gallery-prev" aria-label="Previous image (use left arrow to change)"><span>Previous Image</span></button>';
141
+ self.scope.next_btn = '<button type="button" class="modaal-gallery-control modaal-gallery-next modaal-gallery-next-' + mod_class + '" id="modaal-gallery-next" aria-label="Next image (use right arrow to change)"><span>Next Image</span></button>';
142
+
143
+ // Check for start_open
144
+ if (self.options.start_open === true ){
145
+ self.create_modaal( self );
146
+ }
147
+ },
148
+
149
+ // Initial create to determine which content type it requires
150
+ // ----------------------------------------------------------------
151
+ create_modaal : function(self, e) {
152
+ var self = this;
153
+ var source;
154
+
155
+ // Save last active state before modal
156
+ self.lastFocus = self.$elem;
157
+
158
+ if ( self.options.should_open === false || ( typeof self.options.should_open === 'function' && self.options.should_open() === false ) ) {
159
+ return;
160
+ }
161
+
162
+ // CB: before_open
163
+ self.options.before_open.call(self, e);
164
+
165
+ switch (self.options.type) {
166
+ case 'inline':
167
+ self.create_basic();
168
+ break;
169
+
170
+ case 'ajax':
171
+ source = self.options.source( self.$elem, self.scope.source );
172
+ self.fetch_ajax( source );
173
+ break;
174
+
175
+ case 'confirm':
176
+ self.options.is_locked = true;
177
+ self.create_confirm();
178
+ break;
179
+
180
+ case 'image':
181
+ self.create_image();
182
+ break;
183
+
184
+ case 'iframe':
185
+ source = self.options.source( self.$elem, self.scope.source );
186
+ self.create_iframe( source );
187
+ break;
188
+
189
+ case 'video':
190
+ self.create_video(self.scope.source);
191
+ break;
192
+
193
+ case 'instagram':
194
+ self.create_instagram();
195
+ break;
196
+ }
197
+
198
+ // call events to be watched (click, tab, keyup, keydown etc.)
199
+ self.watch_events();
200
+ },
201
+
202
+ // Watching Modal
203
+ // ----------------------------------------------------------------
204
+ watch_events : function() {
205
+ var self = this;
206
+
207
+ self.dom.off('click.Modaal keyup.Modaal keydown.Modaal');
208
+
209
+ // Body keydown
210
+ self.dom.on('keydown.Modaal', function(e) {
211
+ var key = e.keyCode;
212
+ var target = e.target;
213
+
214
+ // look for tab change and reset focus to modal window
215
+ // done in keydown so the check fires repeatedly when you hold the tab key down
216
+ if (key == 9 && self.scope.is_open) {
217
+ if (!$.contains(document.getElementById(self.scope.id), target) ) {
218
+ $('#' + self.scope.id).find('*[tabindex="0"]').focus();
219
+ }
220
+ }
221
+ });
222
+
223
+ // Body keyup
224
+ self.dom.on('keyup.Modaal', function(e) {
225
+ var key = e.keyCode;
226
+ var target = e.target;
227
+
228
+ if ( (e.shiftKey && e.keyCode == 9) && self.scope.is_open) {
229
+ // Watch for shift + tab key press. if open shift focus to close button.
230
+ if (!$.contains(document.getElementById(self.scope.id), target) ) {
231
+ $('#' + self.scope.id).find('.modaal-close').focus();
232
+ }
233
+ }
234
+
235
+ if ( !self.options.is_locked ){
236
+ // On escape key press close modal
237
+ if (key == 27 && self.scope.is_open ) {
238
+ if ( $(document.activeElement).is('input:not(:checkbox):not(:radio)') ) {
239
+ return false;
240
+ }
241
+
242
+ self.modaal_close();
243
+ return;
244
+ }
245
+ }
246
+
247
+ // is gallery open and images length is > 1
248
+ if ( self.options.type == 'image' ) {
249
+ // arrow left for back
250
+ if (key == 37 && self.scope.is_open && (!$('#' + self.scope.id + ' .modaal-gallery-prev').hasClass('is_hidden')) ) {
251
+ self.gallery_update('prev');
252
+ }
253
+ // arrow right for next
254
+ if (key == 39 && self.scope.is_open && (!$('#' + self.scope.id + ' .modaal-gallery-next').hasClass('is_hidden')) ) {
255
+ self.gallery_update('next');
256
+ }
257
+ return;
258
+ }
259
+ });
260
+
261
+ // Body click/touch
262
+ self.dom.on('click.Modaal', function(e) {
263
+ var trigger = $(e.target);
264
+
265
+ // General Controls: If it's not locked allow greedy close
266
+ if ( !self.options.is_locked ){
267
+ if ( (self.options.overlay_close && trigger.is('.modaal-inner-wrapper')) || trigger.is('.modaal-close') || trigger.closest('.modaal-close').length ) {
268
+ self.modaal_close();
269
+ return;
270
+ }
271
+ }
272
+
273
+ //Confirm Controls
274
+ if ( trigger.is('.modaal-confirm-btn' ) ){
275
+ // if 'OK' button is clicked, run confirm_callback()
276
+ if ( trigger.is('.modaal-ok') ) {
277
+ self.options.confirm_callback.call(self, self.lastFocus);
278
+ }
279
+
280
+ if ( trigger.is('.modaal-cancel') ) {
281
+ self.options.confirm_cancel_callback.call(self, self.lastFocus);
282
+ }
283
+ self.modaal_close();
284
+ return;
285
+ }
286
+
287
+ // Gallery Controls
288
+ if ( trigger.is( '.modaal-gallery-control' ) ){
289
+ // it not active, don't do nuthin!
290
+ if ( trigger.hasClass('is_hidden') ) {
291
+ return;
292
+ }
293
+
294
+ // trigger previous
295
+ if ( trigger.is('.modaal-gallery-prev') ) {
296
+ self.gallery_update('prev');
297
+ }
298
+ // trigger next
299
+ if ( trigger.is('.modaal-gallery-next') ) {
300
+ self.gallery_update('next');
301
+ }
302
+ return;
303
+ }
304
+ });
305
+ },
306
+
307
+ // Append markup into DOM
308
+ build_modal : function(content) {
309
+ var self = this;
310
+
311
+ // if is instagram
312
+ var igClass = '';
313
+ if ( self.options.type == 'instagram' ) {
314
+ igClass = ' modaal-instagram';
315
+ }
316
+
317
+ var wrap_class = (self.options.type == 'video') ? 'modaal-video-wrap' : 'modaal-content';
318
+
319
+ /*
320
+ modaal-start_none : fully hidden via display:none;
321
+ modaal-start_fade : hidden via opacity:0
322
+ modaal-start_slidedown : ...
323
+
324
+ */
325
+ var animation_class;
326
+ switch ( self.options.animation ) {
327
+ case 'fade' :
328
+ animation_class = ' modaal-start_fade';
329
+ break;
330
+ case 'slide-down' :
331
+ animation_class = ' modaal-start_slidedown';
332
+ break;
333
+ default :
334
+ animation_class = ' modaal-start_none'
335
+ }
336
+
337
+ // fullscreen check
338
+ var fullscreen_class = '';
339
+ if ( self.options.fullscreen ) {
340
+ fullscreen_class = ' modaal-fullscreen';
341
+ }
342
+
343
+ // custom class check
344
+ if ( self.options.custom_class !== '' || typeof(self.options.custom_class) !== 'undefined' ) {
345
+ self.options.custom_class = ' ' + self.options.custom_class;
346
+ }
347
+
348
+ // if width and heights exists and is typeof number
349
+ var dimensionsStyle = '';
350
+ if ( self.options.width && self.options.height && typeof self.options.width == 'number' && typeof self.options.height == 'number' ) {
351
+ // if width and height exist, and they are both numbers
352
+ dimensionsStyle = ' style="max-width:' + self.options.width + 'px;height:' + self.options.height + 'px;overflow:auto;"';
353
+ } else if ( self.options.width && typeof self.options.width == 'number' ) {
354
+ // if only width
355
+ dimensionsStyle = ' style="max-width:' + self.options.width + 'px;"';
356
+ } else if ( self.options.height && typeof self.options.height == 'number' ) {
357
+ // if only height
358
+ dimensionsStyle = ' style="height:' + self.options.height + 'px;overflow:auto;"';
359
+ }
360
+
361
+ // Reset dimensions style (width and height) for certain types
362
+ if ( self.options.type == 'image' || self.options.type == 'video' || self.options.type == 'instagram' || self.options.fullscreen ) {
363
+ dimensionsStyle = '';
364
+ }
365
+
366
+ // if is touch
367
+ // this is a bug fix for iOS to allow regular click events on div elements.
368
+ var touchTrigger = '';
369
+ if ( self.is_touch() ) {
370
+ touchTrigger = ' style="cursor:pointer;"'
371
+ }
372
+
373
+ var build_markup = '<div class="modaal-wrapper modaal-' + self.options.type + animation_class + igClass + fullscreen_class + self.options.custom_class + '" id="' + self.scope.id + '"><div class="modaal-outer-wrapper"><div class="modaal-inner-wrapper"' + touchTrigger + '>';
374
+
375
+ // hide if video
376
+ if (self.options.type != 'video') {
377
+ build_markup += '<div class="modaal-container"' + dimensionsStyle + '>';
378
+ }
379
+
380
+ // add the guts of the content
381
+ build_markup += '<div class="' + wrap_class + ' modaal-focus" aria-hidden="false" aria-label="' + self.options.accessible_title + ' - ' + self.options.close_aria_label + '" role="dialog">';
382
+
383
+ // If it's inline type, we want to clone content instead of dropping it straight in
384
+ if (self.options.type == 'inline') {
385
+ build_markup += '<div class="modaal-content-container" role="document"></div>';
386
+ } else {
387
+ // Drop in the content if it's not inline
388
+ build_markup += content;
389
+ }
390
+
391
+ // close wrap_class
392
+ build_markup += '</div>' + self.scope.close_btn;
393
+
394
+ // hide if video
395
+ if (self.options.type != 'video') {
396
+ build_markup += '</div>';
397
+ }
398
+
399
+ // close off modaal-inner-wrapper
400
+ build_markup += '</div>';
401
+
402
+ // If type is image AND outer_controls is true: add gallery next and previous controls.
403
+ if (self.options.type == 'image' && self.options.outer_controls === true) {
404
+ build_markup += self.scope.prev_btn + self.scope.next_btn;
405
+ }
406
+
407
+ // close off modaal-wrapper
408
+ build_markup += '</div></div>';
409
+
410
+ // append ajax modal markup to dom
411
+ if ($('#' + self.scope.id + '_overlay').length < 1) {
412
+ self.dom.append(build_markup);
413
+ }
414
+
415
+ // if inline, clone content into space
416
+ if (self.options.type == 'inline') {
417
+ content.appendTo('#' + self.scope.id + ' .modaal-content-container');
418
+ }
419
+
420
+ // Trigger overlay show (which triggers modal show)
421
+ self.modaal_overlay('show');
422
+ },
423
+
424
+ // Create Basic Inline Modal
425
+ // ----------------------------------------------------------------
426
+ create_basic : function() {
427
+ var self = this;
428
+ var target = $(self.scope.source);
429
+ var content = '';
430
+
431
+ if (target.length) {
432
+ content = target.contents().detach();
433
+ target.empty();
434
+ } else {
435
+ content = 'Content could not be loaded. Please check the source and try again.';
436
+ }
437
+
438
+ // now push content into markup
439
+ self.build_modal(content);
440
+ },
441
+
442
+ // Create Instagram Modal
443
+ // ----------------------------------------------------------------
444
+ create_instagram : function() {
445
+ var self = this;
446
+ var id = self.options.instagram_id;
447
+ var content = '';
448
+
449
+ var error_msg = 'Instagram photo couldn\'t be loaded, please check the embed code and try again.';
450
+
451
+ self.build_modal('<div class="modaal-content-container' + ( self.options.loading_class != '' ? ' ' + self.options.loading_class : '' ) + '">' + self.options.loading_content + '</div>' );
452
+
453
+ // ID exists, is not empty null or undefined.
454
+ if ( id != '' && id !== null && id !== undefined ) {
455
+ // set up oembed url
456
+ var ig_url = 'https://api.instagram.com/oembed?url=http://instagr.am/p/' + id + '/';
457
+
458
+ $.ajax({
459
+ url: ig_url,
460
+ dataType: "jsonp",
461
+ cache: false,
462
+ success: function (data) {
463
+
464
+ // Create temp dom element from which we'll clone into the modaal instance. This is required to bypass the unusual small thumb issue instagram oembed was serving up
465
+ self.dom.append('<div id="temp-ig" style="width:0;height:0;overflow:hidden;">' + data.html + '</div>');
466
+
467
+ // Check if it has loaded once before.
468
+ // This is to stop the Embeds.process from throwing and error the first time it's being loaded.
469
+ // private_options are individual to a modaal_scope so will not work across multiple scopes when checking if true, only that one item.
470
+ if ( self.dom.attr('data-igloaded') ) {
471
+ window.instgrm.Embeds.process();
472
+ } else {
473
+ // first time it's loaded, let's set a new private option to use next time it's opened.
474
+ self.dom.attr('data-igloaded', 'true');
475
+ }
476
+
477
+ // now set location for new content
478
+ // timeout is required as well to bypass the unusual small thumb issue instagram oembed was serving up
479
+ var target = '#' + self.scope.id + ' .modaal-content-container';
480
+ if ( $(target).length > 0) {
481
+ setTimeout(function() {
482
+ $('#temp-ig').contents().clone().appendTo( target );
483
+ $('#temp-ig').remove();
484
+ }, 1000);
485
+ }
486
+
487
+ },
488
+ error: function() {
489
+ content = error_msg;
490
+
491
+ // now set location for new content
492
+ var target = $('#' + self.scope.id + ' .modaal-content-container');
493
+ if ( target.length > 0) {
494
+ target.removeClass( self.options.loading_class ).addClass( self.options.ajax_error_class );
495
+ target.html(content);
496
+ }
497
+ }
498
+ });
499
+
500
+ } else {
501
+ content = error_msg;
502
+ }
503
+
504
+ return false;
505
+ },
506
+
507
+ // Fetch Ajax Data
508
+ // ----------------------------------------------------------------
509
+ fetch_ajax : function(url) {
510
+ var self = this;
511
+ var content = '';
512
+
513
+ // If no accessible title, set it to 'Dialog Window'
514
+ if ( self.options.accessible_title == null ) {
515
+ self.options.accessible_title = 'Dialog Window'
516
+ }
517
+
518
+ if ( self.xhr !== null ){
519
+ self.xhr.abort();
520
+ self.xhr = null;
521
+ }
522
+
523
+ self.build_modal('<div class="modaal-content-container' + ( self.options.loading_class != '' ? ' ' + self.options.loading_class : '' ) + '">' + self.options.loading_content + '</div>' );
524
+
525
+ self.xhr = $.ajax(url, {
526
+ success: function(data) {
527
+ // content fetch is successful so push it into markup
528
+ var target = $('#' + self.scope.id).find('.modaal-content-container');
529
+ if ( target.length > 0){
530
+ target.removeClass( self.options.loading_class );
531
+ target.html( data );
532
+
533
+ self.options.ajax_success.call(self, target);
534
+ }
535
+ },
536
+ error: function( xhr ) {
537
+ // There were some errors so return an error message
538
+ if ( xhr.statusText == 'abort' ){
539
+ return;
540
+ }
541
+
542
+ var target = $('#' + self.scope.id + ' .modaal-content-container');
543
+ if ( target.length > 0){
544
+ target.removeClass( self.options.loading_class ).addClass( self.options.ajax_error_class );
545
+ target.html( 'Content could not be loaded. Please check the source and try again.' );
546
+ }
547
+ }
548
+ });
549
+ },
550
+
551
+ // Create Confirm Modal
552
+ // ----------------------------------------------------------------
553
+ create_confirm : function() {
554
+ var self = this;
555
+ var content;
556
+
557
+ content = '<div class="modaal-content-container">' +
558
+ '<h1 id="modaal-title">' + self.options.confirm_title + '</h1>' +
559
+ '<div class="modaal-confirm-content">' + self.options.confirm_content + '</div>' +
560
+ '<div class="modaal-confirm-wrap">' +
561
+ '<button type="button" class="modaal-confirm-btn modaal-ok" aria-label="Confirm">' + self.options.confirm_button_text + '</button>' +
562
+ '<button type="button" class="modaal-confirm-btn modaal-cancel" aria-label="Cancel">' + self.options.confirm_cancel_button_text + '</button>' +
563
+ '</div>' +
564
+ '</div>' +
565
+ '</div>';
566
+
567
+ // now push content into markup
568
+ self.build_modal(content);
569
+ },
570
+
571
+ // Create Image/Gallery Modal
572
+ // ----------------------------------------------------------------
573
+ create_image : function() {
574
+ var self = this;
575
+ var content;
576
+
577
+ var modaal_image_markup = '';
578
+ var gallery_total;
579
+
580
+ // If has group attribute
581
+ if ( self.$elem.is('[data-group]') || self.$elem.is('[rel]') ) {
582
+
583
+ // find gallery groups
584
+ var use_group = self.$elem.is('[data-group]');
585
+ var gallery_group = use_group ? self.$elem.attr('data-group') : self.$elem.attr('rel');
586
+ var gallery_group_items = use_group ? $('[data-group="' + gallery_group + '"]') : $('[rel="' + gallery_group + '"]');
587
+
588
+ // remove any previous active attribute to any in the group
589
+ gallery_group_items.removeAttr('data-gallery-active', 'is_active');
590
+ // add active attribute to the item clicked
591
+ self.$elem.attr('data-gallery-active', 'is_active');
592
+
593
+ // how many in the grouping are there (-1 to connect with each function starting with 0)
594
+ gallery_total = gallery_group_items.length - 1;
595
+
596
+ // prepare array for gallery data
597
+ var gallery = [];
598
+
599
+ // start preparing markup
600
+ modaal_image_markup = '<div class="modaal-gallery-item-wrap">';
601
+
602
+ // loop each grouping item and push it into our gallery array
603
+ gallery_group_items.each(function(i, item) {
604
+ // setup default content
605
+ var img_src = '';
606
+ var img_alt = '';
607
+ var img_description = '';
608
+ var img_active = false;
609
+ var img_src_error = false;
610
+
611
+ var data_modaal_desc = item.getAttribute('data-modaal-desc');
612
+ var data_item_active = item.getAttribute('data-gallery-active');
613
+
614
+ // if item has inline custom source, use that instead of href. Fall back to href if available.
615
+ if ( $(item).attr('data-modaal-content-source') ) {
616
+ img_src = $(item).attr('data-modaal-content-source');
617
+ } else if ( $(item).attr('href') ) {
618
+ img_src = $(item).attr('href');
619
+ } else if ( $(item).attr('src') ) {
620
+ img_src = $(item).attr('src');
621
+ } else {
622
+ img_src = 'trigger requires href or data-modaal-content-source attribute';
623
+ img_src_error = true;
624
+ }
625
+
626
+ // Does it have a modaal description
627
+ if ( data_modaal_desc != '' && data_modaal_desc !== null && data_modaal_desc !== undefined ) {
628
+ img_alt = data_modaal_desc;
629
+ img_description = '<div class="modaal-gallery-label"><span class="modaal-accessible-hide">Image ' + (i+1) + ' - </span>' + data_modaal_desc.replace(/</g, "&lt;").replace(/>/g, "&gt;") + '</div>'
630
+ } else {
631
+ img_description = '<div class="modaal-gallery-label"><span class="modaal-accessible-hide">Image ' + (i+1) + '</span></div>';
632
+ }
633
+
634
+ // is it the active item
635
+ if ( data_item_active ) {
636
+ img_active = true
637
+ }
638
+
639
+ // set new object for values we want
640
+ var gallery_item = {
641
+ 'url': img_src,
642
+ 'alt': img_alt,
643
+ 'rawdesc': data_modaal_desc,
644
+ 'desc': img_description,
645
+ 'active': img_active,
646
+ 'src_error': img_src_error
647
+ };
648
+
649
+ // push object into gallery array
650
+ gallery.push( gallery_item );
651
+ });
652
+
653
+ // now loop through all items in the gallery and build up the markup
654
+ for (var i = 0; i < gallery.length; i++) {
655
+ // Set default active class, then check if array item active is true and update string for class
656
+ var is_active = '';
657
+ var aria_label = gallery[i].rawdesc ? 'Image: ' + gallery[i].rawdesc : 'Image ' + i + ' no description';
658
+
659
+ if ( gallery[i].active ) {
660
+ is_active = ' ' + self.private_options.active_class;
661
+ }
662
+
663
+ // if gallery item has source error, output message rather than undefined image
664
+ var image_output = gallery[i].src_error ? gallery[i].url : '<img src="' + gallery[i].url + '" alt=" " style="width:100%">';
665
+
666
+ // for each item build up the markup
667
+ modaal_image_markup += '<div class="modaal-gallery-item gallery-item-' + i + is_active + '" aria-label="' + aria_label + '">' +
668
+ image_output + gallery[i].desc +
669
+ '</div>';
670
+ }
671
+
672
+ // Close off the markup for the gallery
673
+ modaal_image_markup += '</div>';
674
+
675
+ // Add next and previous buttons if outside
676
+ if (self.options.outer_controls != true) {
677
+ modaal_image_markup += self.scope.prev_btn + self.scope.next_btn;
678
+ }
679
+ } else {
680
+ // This is only a single gallery item so let's grab the necessary values
681
+
682
+ // define the source, check if content_source option exists, and use that or fall back to href.
683
+ var this_img_src;
684
+ var img_src_error = false;
685
+ if ( self.$elem.attr('data-modaal-content-source') ) {
686
+ this_img_src = self.$elem.attr('data-modaal-content-source');
687
+ } else if ( self.$elem.attr('href') ) {
688
+ this_img_src = self.$elem.attr('href');
689
+ } else if ( self.$elem.attr('src') ) {
690
+ this_img_src = self.$elem.attr('src');
691
+ } else {
692
+ this_img_src = 'trigger requires href or data-modaal-content-source attribute';
693
+ img_src_error = true;
694
+ }
695
+
696
+ var this_img_alt_txt = '';
697
+ var this_img_alt = '';
698
+ var aria_label = '';
699
+
700
+ if ( self.$elem.attr('data-modaal-desc') ) {
701
+ aria_label = self.$elem.attr('data-modaal-desc');
702
+ this_img_alt_txt = self.$elem.attr('data-modaal-desc');
703
+ this_img_alt = '<div class="modaal-gallery-label"><span class="modaal-accessible-hide">Image - </span>' + this_img_alt_txt.replace(/</g, "&lt;").replace(/>/g, "&gt;") + '</div>';
704
+ } else {
705
+ aria_label = "Image with no description";
706
+ }
707
+
708
+ // if image item has source error, output message rather than undefined image
709
+ var image_output = img_src_error ? this_img_src : '<img src="' + this_img_src + '" alt=" " style="width:100%">';
710
+
711
+ // build up the html
712
+ modaal_image_markup = '<div class="modaal-gallery-item is_active" aria-label="' + aria_label + '">' +
713
+ image_output + this_img_alt +
714
+ '</div>';
715
+ }
716
+
717
+ // Update content variable
718
+ content = modaal_image_markup;
719
+
720
+ // now push content into markup
721
+ self.build_modal(content);
722
+
723
+ // setup next & prev buttons
724
+ if ( $('.modaal-gallery-item.is_active').is('.gallery-item-0') ) {
725
+ $('.modaal-gallery-prev').hide();
726
+ }
727
+ if ( $('.modaal-gallery-item.is_active').is('.gallery-item-' + gallery_total) ) {
728
+ $('.modaal-gallery-next').hide();
729
+ }
730
+ },
731
+
732
+ // Gallery Change Image
733
+ // ----------------------------------------------------------------
734
+ gallery_update : function(direction) {
735
+ var self = this;
736
+ var this_gallery = $('#' + self.scope.id);
737
+ var this_gallery_item = this_gallery.find('.modaal-gallery-item');
738
+ var this_gallery_total = this_gallery_item.length - 1;
739
+
740
+ // if single item, don't proceed
741
+ if ( this_gallery_total == 0 ) {
742
+ return false;
743
+ }
744
+
745
+ var prev_btn = this_gallery.find('.modaal-gallery-prev'),
746
+ next_btn = this_gallery.find('.modaal-gallery-next');
747
+
748
+ var duration = 250;
749
+
750
+ var new_img_w = 0,
751
+ new_img_h = 0;
752
+
753
+ // CB: Before image change
754
+ var current_item = this_gallery.find( '.modaal-gallery-item.' + self.private_options.active_class ),
755
+ incoming_item = ( direction == 'next' ? current_item.next( '.modaal-gallery-item' ) : current_item.prev( '.modaal-gallery-item' ) );
756
+ self.options.before_image_change.call(self, current_item, incoming_item);
757
+
758
+ // stop change if at start of end
759
+ if ( direction == 'prev' && this_gallery.find('.gallery-item-0').hasClass('is_active') ) {
760
+ return false;
761
+ } else if ( direction == 'next' && this_gallery.find('.gallery-item-' + this_gallery_total).hasClass('is_active') ) {
762
+ return false;
763
+ }
764
+
765
+
766
+ // lock dimensions
767
+ current_item.stop().animate({
768
+ opacity: 0
769
+ }, duration, function(){
770
+ // Move to appropriate image
771
+ incoming_item.addClass('is_next').css({
772
+ 'position': 'absolute',
773
+ 'display': 'block',
774
+ 'opacity': 0
775
+ });
776
+
777
+ // Collect doc width
778
+ var doc_width = $(document).width();
779
+ var width_threshold = doc_width > 1140 ? 280 : 50;
780
+
781
+ // start toggle to 'is_next'
782
+ new_img_w = this_gallery.find('.modaal-gallery-item.is_next').width();
783
+ new_img_h = this_gallery.find('.modaal-gallery-item.is_next').height();
784
+
785
+ var new_natural_w = this_gallery.find('.modaal-gallery-item.is_next img').prop('naturalWidth');
786
+ var new_natural_h = this_gallery.find('.modaal-gallery-item.is_next img').prop('naturalHeight');
787
+
788
+ // if new image is wider than doc width
789
+ if ( new_natural_w > (doc_width - width_threshold) ) {
790
+ // set new width just below doc width
791
+ new_img_w = doc_width - width_threshold;
792
+
793
+ // Set temp widths so we can calulate the correct height;
794
+ this_gallery.find('.modaal-gallery-item.is_next').css({ 'width': new_img_w });
795
+ this_gallery.find('.modaal-gallery-item.is_next img').css({ 'width': new_img_w });
796
+
797
+ // Set new height variable
798
+ new_img_h = this_gallery.find('.modaal-gallery-item.is_next').find('img').height();
799
+ } else {
800
+ // new img is not wider than screen, so let's set the new dimensions
801
+ new_img_w = new_natural_w;
802
+ new_img_h = new_natural_h;
803
+ }
804
+
805
+ // resize gallery region
806
+ this_gallery.find('.modaal-gallery-item-wrap').stop().animate({
807
+ 'width': new_img_w,
808
+ 'height': new_img_h
809
+ }, duration, function() {
810
+ // hide old active image
811
+ current_item.removeClass(self.private_options.active_class + ' ' + self.options.gallery_active_class).removeAttr('style');
812
+ current_item.find('img').removeAttr('style');
813
+
814
+ // show new image
815
+ incoming_item.addClass(self.private_options.active_class + ' ' + self.options.gallery_active_class).removeClass('is_next').css('position','');
816
+
817
+ // animate in new image (now has the normal is_active class
818
+ incoming_item.stop().animate({
819
+ opacity: 1
820
+ }, duration, function(){
821
+ $(this).removeAttr('style').css({
822
+ 'width': '100%'
823
+ });
824
+ $(this).find('img').css('width', '100%');
825
+
826
+ // remove dimension lock
827
+ this_gallery.find('.modaal-gallery-item-wrap').removeAttr('style');
828
+
829
+ // CB: After image change
830
+ self.options.after_image_change.call( self, incoming_item );
831
+ });
832
+
833
+ // Focus on the new gallery item
834
+ this_gallery.find('.modaal-gallery-item').removeAttr('tabindex');
835
+ this_gallery.find('.modaal-gallery-item.' + self.private_options.active_class + '').attr('tabindex', '0').focus();
836
+
837
+ // hide/show next/prev
838
+ if ( this_gallery.find('.modaal-gallery-item.' + self.private_options.active_class).is('.gallery-item-0') ) {
839
+ prev_btn.stop().animate({
840
+ opacity: 0
841
+ }, 150, function(){
842
+ $(this).hide();
843
+ });
844
+ } else {
845
+ prev_btn.stop().css({
846
+ 'display': 'block',
847
+ 'opacity': prev_btn.css('opacity')
848
+ }).animate({
849
+ opacity: 1
850
+ }, 150);
851
+ }
852
+ if ( this_gallery.find('.modaal-gallery-item.' + self.private_options.active_class).is('.gallery-item-' + this_gallery_total) ) {
853
+ next_btn.stop().animate({
854
+ opacity: 0
855
+ }, 150, function(){
856
+ $(this).hide();
857
+ });
858
+ } else {
859
+ next_btn.stop().css({
860
+ 'display': 'block',
861
+ 'opacity': prev_btn.css('opacity')
862
+ }).animate({
863
+ opacity: 1
864
+ }, 150);
865
+ }
866
+ });
867
+ });
868
+ },
869
+
870
+ // Create Video Modal
871
+ // ----------------------------------------------------------------
872
+ create_video : function(url) {
873
+ var self = this;
874
+ var content;
875
+
876
+ // video markup
877
+ content = '<iframe src="' + url + '" class="modaal-video-frame" frameborder="0" allowfullscreen></iframe>';
878
+
879
+ // now push content into markup
880
+ self.build_modal('<div class="modaal-video-container">' + content + '</div>');
881
+ },
882
+
883
+ // Create iFrame Modal
884
+ // ----------------------------------------------------------------
885
+ create_iframe : function(url) {
886
+ var self = this;
887
+ var content;
888
+
889
+ if ( self.options.width !== null || self.options.width !== undefined || self.options.height !== null || self.options.height !== undefined ) {
890
+ // video markup
891
+ content = '<iframe src="' + url + '" class="modaal-iframe-elem" frameborder="0" allowfullscreen></iframe>';
892
+ } else {
893
+ content = '<div class="modaal-content-container">Please specify a width and height for your iframe</div>';
894
+ }
895
+
896
+ // now push content into markup
897
+ self.build_modal(content);
898
+ },
899
+
900
+ // Open Modaal
901
+ // ----------------------------------------------------------------
902
+ modaal_open : function() {
903
+ var self = this;
904
+ var modal_wrapper = $( '#' + self.scope.id );
905
+ var animation_type = self.options.animation;
906
+
907
+ if (animation_type === 'none' ){
908
+ modal_wrapper.removeClass('modaal-start_none');
909
+ self.options.after_open.call(self, modal_wrapper);
910
+ }
911
+
912
+ // Open with fade
913
+ if (animation_type === 'fade') {
914
+ modal_wrapper.removeClass('modaal-start_fade');
915
+ }
916
+
917
+ // Open with slide down
918
+ if (animation_type === 'slide-down') {
919
+ modal_wrapper.removeClass('modaal-start_slide_down');
920
+ }
921
+
922
+ var focusTarget = modal_wrapper;
923
+
924
+ // Switch focusTarget tabindex (switch from other modal if exists)
925
+ $('.modaal-wrapper *[tabindex=0]').removeAttr('tabindex');
926
+
927
+ if ( self.options.type == 'image' ) {
928
+ focusTarget = $('#' + self.scope.id).find('.modaal-gallery-item.' + self.private_options.active_class);
929
+
930
+ } else if ( modal_wrapper.find('.modaal-iframe-elem').length ) {
931
+ focusTarget = modal_wrapper.find('.modaal-iframe-elem');
932
+
933
+ } else if ( modal_wrapper.find('.modaal-video-wrap').length ) {
934
+ focusTarget = modal_wrapper.find('.modaal-video-wrap');
935
+
936
+ } else {
937
+ focusTarget = modal_wrapper.find('.modaal-focus');
938
+
939
+ }
940
+
941
+ // now set the focus
942
+ focusTarget.attr('tabindex', '0').focus();
943
+
944
+ // Run after_open
945
+ if (animation_type !== 'none') {
946
+ // CB: after_open
947
+ setTimeout(function() {
948
+ self.options.after_open.call(self, modal_wrapper)
949
+ }, self.options.after_callback_delay);
950
+ }
951
+ },
952
+
953
+ // Close Modal
954
+ // ----------------------------------------------------------------
955
+ modaal_close : function() {
956
+ var self = this;
957
+ var modal_wrapper = $( '#' + self.scope.id );
958
+
959
+ // CB: before_close
960
+ self.options.before_close.call(self, modal_wrapper);
961
+
962
+ if (self.xhr !== null){
963
+ self.xhr.abort();
964
+ self.xhr = null;
965
+ }
966
+
967
+ // Now we close the modal
968
+ if (self.options.animation === 'none' ){
969
+ modal_wrapper.addClass('modaal-start_none');
970
+ }
971
+
972
+ // Close with fade
973
+ if (self.options.animation === 'fade') {
974
+ modal_wrapper.addClass('modaal-start_fade');
975
+ }
976
+
977
+ // Close with slide up (using initial slide down)
978
+ if (self.options.animation === 'slide-down') {
979
+ modal_wrapper.addClass('modaal-start_slide_down');
980
+ }
981
+
982
+ // CB: after_close and remove
983
+ setTimeout(function() {
984
+ // clone inline content back to origin place
985
+ if (self.options.type == 'inline') {
986
+ $('#' + self.scope.id + ' .modaal-content-container').contents().detach().appendTo( self.scope.source )
987
+ }
988
+ // remove markup from dom
989
+ modal_wrapper.remove();
990
+ // CB: after_close
991
+ self.options.after_close.call(self);
992
+ // scope is now closed
993
+ self.scope.is_open = false;
994
+
995
+ }, self.options.after_callback_delay);
996
+
997
+ // Call overlay hide
998
+ self.modaal_overlay('hide');
999
+
1000
+ // Roll back to last focus state before modal open. If was closed programmatically, this might not be set
1001
+ if (self.lastFocus != null) {
1002
+ self.lastFocus.focus();
1003
+ }
1004
+ },
1005
+
1006
+ // Overlay control (accepts action for show or hide)
1007
+ // ----------------------------------------------------------------
1008
+ modaal_overlay : function(action) {
1009
+ var self = this;
1010
+
1011
+ if (action == 'show') {
1012
+ // Modal is open so update scope
1013
+ self.scope.is_open = true;
1014
+
1015
+ // set body to overflow hidden if background_scroll is false
1016
+ if (! self.options.background_scroll) {
1017
+ self.dom.addClass('modaal-noscroll');
1018
+ }
1019
+
1020
+ // append modaal overlay
1021
+ if ($('#' + self.scope.id + '_overlay').length < 1) {
1022
+ self.dom.append('<div class="modaal-overlay" id="' + self.scope.id + '_overlay"></div>');
1023
+ }
1024
+
1025
+ // now show
1026
+ $('#' + self.scope.id + '_overlay').css('background', self.options.background).stop().animate({
1027
+ opacity: self.options.overlay_opacity
1028
+ }, self.options.animation_speed, function(){
1029
+ // now open the modal
1030
+ self.modaal_open();
1031
+ });
1032
+
1033
+ } else if (action == 'hide') {
1034
+
1035
+ // now hide the overlay
1036
+ $('#' + self.scope.id + '_overlay').stop().animate({
1037
+ opacity: 0
1038
+ }, self.options.animation_speed, function(){
1039
+ // remove overlay from dom
1040
+ $(this).remove();
1041
+
1042
+ // remove body overflow lock
1043
+ self.dom.removeClass('modaal-noscroll');
1044
+ });
1045
+ }
1046
+ },
1047
+
1048
+ // Check if is touch
1049
+ // ----------------------------------------------------------------
1050
+ is_touch : function() {
1051
+ return 'ontouchstart' in window || navigator.maxTouchPoints;
1052
+ }
1053
+ };
1054
+
1055
+ // Define default object to store
1056
+ var modaal_existing_selectors = [];
1057
+
1058
+ // Declare the modaal jQuery method
1059
+ // ------------------------------------------------------------
1060
+ $.fn.modaal = function(options) {
1061
+ return this.each(function (i) {
1062
+ var existing_modaal = $(this).data('modaal');
1063
+
1064
+ if ( existing_modaal ){
1065
+ // Checking for string value, used for methods
1066
+ if (typeof(options) == 'string'){
1067
+ switch (options) {
1068
+ case 'open':
1069
+ // create the modal
1070
+ existing_modaal.create_modaal(existing_modaal);
1071
+ break;
1072
+ case 'close':
1073
+ existing_modaal.modaal_close();
1074
+ break;
1075
+ }
1076
+ }
1077
+ } else {
1078
+ // Not a string, so let's setup the modal ready to use
1079
+ var modaal = Object.create(Modaal);
1080
+ modaal.init(options, this);
1081
+ $.data(this, "modaal", modaal);
1082
+
1083
+ // push this select into existing selectors array which is referenced during modaal_dom_observer
1084
+ modaal_existing_selectors.push({
1085
+ 'element': $(this).attr('class'),
1086
+ 'options': options
1087
+ });
1088
+ }
1089
+ });
1090
+ };
1091
+
1092
+ // Default options
1093
+ // ------------------------------------------------------------
1094
+ $.fn.modaal.options = {
1095
+
1096
+ //General
1097
+ type: 'inline',
1098
+ content_source: null,
1099
+ animation: 'fade',
1100
+ animation_speed: 300,
1101
+ after_callback_delay: 350,
1102
+ is_locked: false,
1103
+ hide_close: false,
1104
+ background: '#000',
1105
+ overlay_opacity: '0.8',
1106
+ overlay_close: true,
1107
+ accessible_title: 'Dialog Window',
1108
+ start_open: false,
1109
+ fullscreen: false,
1110
+ custom_class: '',
1111
+ background_scroll: false,
1112
+ should_open: true,
1113
+ close_text: 'Close',
1114
+ close_aria_label: 'Close (Press escape to close)',
1115
+ width: null,
1116
+ height: null,
1117
+
1118
+ //Events
1119
+ before_open: function(){},
1120
+ after_open: function(){},
1121
+ before_close: function(){},
1122
+ after_close: function(){},
1123
+ source: function( element, src ){
1124
+ return src;
1125
+ },
1126
+
1127
+ //Confirm Modal
1128
+ confirm_button_text: 'Confirm', // text on confirm button
1129
+ confirm_cancel_button_text: 'Cancel',
1130
+ confirm_title: 'Confirm Title', // title for confirm modal
1131
+ confirm_content: '<p>This is the default confirm dialog content. Replace me through the options</p>', // html for confirm message
1132
+ confirm_callback: function() {},
1133
+ confirm_cancel_callback: function() {},
1134
+
1135
+
1136
+ //Gallery Modal
1137
+ gallery_active_class: 'gallery_active_item',
1138
+ outer_controls: false,
1139
+ before_image_change: function( current_item, incoming_item ) {},
1140
+ after_image_change: function( current_item ) {},
1141
+
1142
+ //Ajax Modal
1143
+ loading_content: modaal_loading_spinner,
1144
+ loading_class: 'is_loading',
1145
+ ajax_error_class: 'modaal-error',
1146
+ ajax_success: function(){},
1147
+
1148
+ //Instagram
1149
+ instagram_id: null
1150
+ };
1151
+
1152
+ // Check and Set Inline Options
1153
+ // ------------------------------------------------------------
1154
+ function modaal_inline_options(self) {
1155
+
1156
+ // new empty options
1157
+ var options = {};
1158
+ var inline_options = false;
1159
+
1160
+ // option: type
1161
+ if ( self.attr('data-modaal-type') ) {
1162
+ inline_options = true;
1163
+ options.type = self.attr('data-modaal-type');
1164
+ }
1165
+
1166
+ // option: type
1167
+ if ( self.attr('data-modaal-content-source') ) {
1168
+ inline_options = true;
1169
+ options.content_source = self.attr('data-modaal-content-source');
1170
+ }
1171
+
1172
+ // option: animation
1173
+ if ( self.attr('data-modaal-animation') ) {
1174
+ inline_options = true;
1175
+ options.animation = self.attr('data-modaal-animation');
1176
+ }
1177
+
1178
+ // option: animation_speed
1179
+ if ( self.attr('data-modaal-animation-speed') ) {
1180
+ inline_options = true;
1181
+ options.animation_speed = self.attr('data-modaal-animation-speed');
1182
+ }
1183
+
1184
+ // option: after_callback_delay
1185
+ if ( self.attr('data-modaal-after-callback-delay') ) {
1186
+ inline_options = true;
1187
+ options.after_callback_delay = self.attr('data-modaal-after-callback-delay');
1188
+ }
1189
+
1190
+ // option: is_locked
1191
+ if ( self.attr('data-modaal-is-locked') ) {
1192
+ inline_options = true;
1193
+ options.is_locked = (self.attr('data-modaal-is-locked') === 'true' ? true : false);
1194
+ }
1195
+
1196
+ // option: hide_close
1197
+ if ( self.attr('data-modaal-hide-close') ) {
1198
+ inline_options = true;
1199
+ options.hide_close = (self.attr('data-modaal-hide-close') === 'true' ? true : false);
1200
+ }
1201
+
1202
+ // option: background
1203
+ if ( self.attr('data-modaal-background') ) {
1204
+ inline_options = true;
1205
+ options.background = self.attr('data-modaal-background');
1206
+ }
1207
+
1208
+ // option: overlay_opacity
1209
+ if ( self.attr('data-modaal-overlay-opacity') ) {
1210
+ inline_options = true;
1211
+ options.overlay_opacity = self.attr('data-modaal-overlay-opacity');
1212
+ }
1213
+
1214
+ // option: overlay_close
1215
+ if ( self.attr('data-modaal-overlay-close') ) {
1216
+ inline_options = true;
1217
+ options.overlay_close = (self.attr('data-modaal-overlay-close') === 'false' ? false : true);
1218
+ }
1219
+
1220
+ // option: accessible_title
1221
+ if ( self.attr('data-modaal-accessible-title') ) {
1222
+ inline_options = true;
1223
+ options.accessible_title = self.attr('data-modaal-accessible-title');
1224
+ }
1225
+
1226
+ // option: start_open
1227
+ if ( self.attr('data-modaal-start-open') ) {
1228
+ inline_options = true;
1229
+ options.start_open = (self.attr('data-modaal-start-open') === 'true' ? true : false);
1230
+ }
1231
+
1232
+ // option: fullscreen
1233
+ if ( self.attr('data-modaal-fullscreen') ) {
1234
+ inline_options = true;
1235
+ options.fullscreen = (self.attr('data-modaal-fullscreen') === 'true' ? true : false);
1236
+ }
1237
+
1238
+ // option: custom_class
1239
+ if ( self.attr('data-modaal-custom-class') ) {
1240
+ inline_options = true;
1241
+ options.custom_class = self.attr('data-modaal-custom-class');
1242
+ }
1243
+
1244
+ // option: close_text
1245
+ if ( self.attr('data-modaal-close-text') ) {
1246
+ inline_options = true;
1247
+ options.close_text = self.attr('data-modaal-close-text');
1248
+ }
1249
+
1250
+ // option: close_aria_label
1251
+ if ( self.attr('data-modaal-close-aria-label') ) {
1252
+ inline_options = true;
1253
+ options.close_aria_label = self.attr('data-modaal-close-aria-label');
1254
+ }
1255
+
1256
+ // option: background_scroll
1257
+ if ( self.attr('data-modaal-background-scroll') ) {
1258
+ inline_options = true;
1259
+ options.background_scroll = (self.attr('data-modaal-background-scroll') === 'true' ? true : false);
1260
+ }
1261
+
1262
+ // option: width
1263
+ if ( self.attr('data-modaal-width') ) {
1264
+ inline_options = true;
1265
+ options.width = parseInt( self.attr('data-modaal-width') );
1266
+ }
1267
+
1268
+ // option: height
1269
+ if ( self.attr('data-modaal-height') ) {
1270
+ inline_options = true;
1271
+ options.height = parseInt( self.attr('data-modaal-height') );
1272
+ }
1273
+
1274
+ // option: confirm_button_text
1275
+ if ( self.attr('data-modaal-confirm-button-text') ) {
1276
+ inline_options = true;
1277
+ options.confirm_button_text = self.attr('data-modaal-confirm-button-text');
1278
+ }
1279
+
1280
+ // option: confirm_cancel_button_text
1281
+ if ( self.attr('data-modaal-confirm-cancel-button-text') ) {
1282
+ inline_options = true;
1283
+ options.confirm_cancel_button_text = self.attr('data-modaal-confirm-cancel-button-text');
1284
+ }
1285
+
1286
+ // option: confirm_title
1287
+ if ( self.attr('data-modaal-confirm-title') ) {
1288
+ inline_options = true;
1289
+ options.confirm_title = self.attr('data-modaal-confirm-title');
1290
+ }
1291
+
1292
+ // option: confirm_content
1293
+ if ( self.attr('data-modaal-confirm-content') ) {
1294
+ inline_options = true;
1295
+ options.confirm_content = self.attr('data-modaal-confirm-content');
1296
+ }
1297
+
1298
+ // option: gallery_active_class
1299
+ if ( self.attr('data-modaal-gallery-active-class') ) {
1300
+ inline_options = true;
1301
+ options.gallery_active_class = self.attr('data-modaal-gallery-active-class');
1302
+ }
1303
+
1304
+ // option: loading_content
1305
+ if ( self.attr('data-modaal-loading-content') ) {
1306
+ inline_options = true;
1307
+ options.loading_content = self.attr('data-modaal-loading-content');
1308
+ }
1309
+
1310
+ // option: loading_class
1311
+ if ( self.attr('data-modaal-loading-class') ) {
1312
+ inline_options = true;
1313
+ options.loading_class = self.attr('data-modaal-loading-class');
1314
+ }
1315
+
1316
+ // option: ajax_error_class
1317
+ if ( self.attr('data-modaal-ajax-error-class') ) {
1318
+ inline_options = true;
1319
+ options.ajax_error_class = self.attr('data-modaal-ajax-error-class');
1320
+ }
1321
+
1322
+ // option: start_open
1323
+ if ( self.attr('data-modaal-instagram-id') ) {
1324
+ inline_options = true;
1325
+ options.instagram_id = self.attr('data-modaal-instagram-id');
1326
+ }
1327
+
1328
+ // now set it up for the trigger, but only if inline_options is true
1329
+ if ( inline_options ) {
1330
+ self.modaal(options);
1331
+ }
1332
+ };
1333
+
1334
+ // On body load (or now, if already loaded), init any modaals defined inline
1335
+ // Ensure this is done after $.fn.modaal and default options are declared
1336
+ // ----------------------------------------------------------------
1337
+ $(function(){
1338
+
1339
+ var single_modaal = $('.modaal');
1340
+
1341
+ // Check for existing modaal elements
1342
+ if ( single_modaal.length ) {
1343
+ single_modaal.each(function() {
1344
+ var self = $(this);
1345
+ modaal_inline_options(self);
1346
+ });
1347
+ }
1348
+
1349
+ // Obvserve DOM mutations for newly added triggers
1350
+ var modaal_dom_observer = new MutationObserver(function(mutations) {
1351
+ mutations.forEach(function(mutation) {
1352
+ if (mutation.addedNodes && mutation.addedNodes.length > 0) {
1353
+ // element added to DOM
1354
+ var findElement = [].some.call(mutation.addedNodes, function(el) {
1355
+ var elm = $(el);
1356
+ if ( elm.is('a') || elm.is('button') ) {
1357
+
1358
+ if ( elm.hasClass('modaal') ) {
1359
+ // is inline Modaal, initialise options
1360
+ modaal_inline_options(elm);
1361
+ } else {
1362
+ // is not inline modaal. Check for existing selector
1363
+ modaal_existing_selectors.forEach(function(modaalSelector) {
1364
+ if ( modaalSelector.element == elm.attr('class') ) {
1365
+ $(elm).modaal( modaalSelector.options );
1366
+ return false;
1367
+ }
1368
+ });
1369
+ }
1370
+
1371
+ }
1372
+ });
1373
+ }
1374
+ });
1375
+ });
1376
+ var observer_config = {
1377
+ subtree: true,
1378
+ attributes: true,
1379
+ childList: true,
1380
+ characterData: true
1381
+ };
1382
+
1383
+ // pass in the target node, as well as the observer options
1384
+ setTimeout(function() {
1385
+ modaal_dom_observer.observe(document.body, observer_config);
1386
+ }, 500);
1387
+
1388
+ });
1389
+
1390
+ } ( jQuery, window, document ) );