blacklight-spotlight 3.0.0.rc2 → 3.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/assets/images/blacklight/arrow-alt-circle-left.svg +1 -0
  4. data/app/assets/images/blacklight/arrow-alt-circle-right.svg +1 -0
  5. data/app/assets/javascripts/spotlight/admin/{add_new_page_button.js → add_new_button.js} +7 -0
  6. data/app/assets/javascripts/spotlight/admin/block_mixins/autocompleteable.js +4 -4
  7. data/app/assets/javascripts/spotlight/admin/blocks/browse_group_categories_block.js +88 -0
  8. data/app/assets/javascripts/spotlight/admin/blocks/pages_block.js +1 -1
  9. data/app/assets/javascripts/spotlight/admin/blocks/solr_documents_base_block.js +1 -1
  10. data/app/assets/javascripts/spotlight/admin/croppable.js +1 -1
  11. data/app/assets/javascripts/spotlight/admin/index.js +0 -2
  12. data/app/assets/javascripts/spotlight/admin/search_typeahead.js +2 -2
  13. data/app/assets/javascripts/spotlight/admin/sir-trevor/locales.js +7 -0
  14. data/app/assets/javascripts/spotlight/user/browse_group_categories.js +59 -0
  15. data/app/assets/javascripts/spotlight/user/index.js +1 -0
  16. data/app/assets/stylesheets/spotlight/_breadcrumbs.scss +8 -0
  17. data/app/assets/stylesheets/spotlight/_featured_browse_categories_block.scss +34 -1
  18. data/app/assets/stylesheets/spotlight/_spotlight.scss +2 -0
  19. data/app/assets/stylesheets/spotlight/browse_group_categories_block.scss +69 -0
  20. data/app/builders/spotlight/bootstrap_breadcrumbs_builder.rb +1 -2
  21. data/app/controllers/concerns/spotlight/search_helper.rb +2 -8
  22. data/app/controllers/spotlight/appearances_controller.rb +0 -13
  23. data/app/controllers/spotlight/browse_controller.rb +7 -3
  24. data/app/controllers/spotlight/catalog_controller.rb +1 -1
  25. data/app/controllers/spotlight/concerns/application_controller.rb +13 -2
  26. data/app/controllers/spotlight/groups_controller.rb +80 -0
  27. data/app/controllers/spotlight/pages_controller.rb +5 -8
  28. data/app/controllers/spotlight/searches_controller.rb +4 -17
  29. data/app/helpers/spotlight/application_helper.rb +1 -1
  30. data/app/helpers/spotlight/crud_link_helpers.rb +1 -1
  31. data/app/helpers/spotlight/main_app_helpers.rb +1 -1
  32. data/app/models/sir_trevor_rails/blocks/browse_group_categories_block.rb +25 -0
  33. data/app/models/spotlight/ability.rb +2 -0
  34. data/app/models/spotlight/about_page.rb +1 -1
  35. data/app/models/spotlight/blacklight_configuration.rb +2 -1
  36. data/app/models/spotlight/exhibit.rb +14 -2
  37. data/app/models/spotlight/feature_page.rb +1 -1
  38. data/app/models/spotlight/featured_image.rb +1 -1
  39. data/app/models/spotlight/group.rb +22 -0
  40. data/app/models/spotlight/group_member.rb +11 -0
  41. data/app/models/spotlight/home_page.rb +1 -1
  42. data/app/models/spotlight/page.rb +2 -2
  43. data/app/models/spotlight/page_configurations.rb +5 -0
  44. data/app/models/spotlight/resources/iiif_manifest.rb +6 -6
  45. data/app/models/spotlight/search.rb +5 -0
  46. data/app/services/spotlight/exhibit_import_export_service.rb +50 -22
  47. data/app/views/spotlight/browse/_search.html.erb +2 -1
  48. data/app/views/spotlight/browse/index.html.erb +13 -0
  49. data/app/views/spotlight/catalog/_document.html.erb +2 -4
  50. data/app/views/spotlight/catalog/index.iiif_json.jbuilder +22 -0
  51. data/app/views/spotlight/searches/_form.html.erb +12 -0
  52. data/app/views/spotlight/searches/_group.html.erb +27 -0
  53. data/app/views/spotlight/searches/index.html.erb +58 -17
  54. data/app/views/spotlight/sir_trevor/blocks/_browse_group_categories_block.html.erb +44 -0
  55. data/app/views/spotlight/translations/_groups.html.erb +34 -0
  56. data/app/views/spotlight/translations/edit.html.erb +6 -0
  57. data/config/i18n-tasks.yml +5 -0
  58. data/config/locales/spotlight.en.yml +37 -1
  59. data/config/routes.rb +10 -0
  60. data/db/migrate/20210113092223_create_spotlight_groups.rb +23 -0
  61. data/lib/generators/spotlight/install_generator.rb +3 -3
  62. data/lib/generators/spotlight/templates/config/initializers/spotlight_initializer.rb +1 -1
  63. data/lib/spotlight/engine.rb +4 -3
  64. data/lib/spotlight/version.rb +1 -1
  65. data/spec/controllers/spotlight/browse_controller_spec.rb +23 -0
  66. data/spec/controllers/spotlight/feature_pages_controller_spec.rb +11 -0
  67. data/spec/controllers/spotlight/groups_controller_spec.rb +103 -0
  68. data/spec/controllers/spotlight/searches_controller_spec.rb +10 -3
  69. data/spec/controllers/spotlight/view_configurations_controller_spec.rb +1 -1
  70. data/spec/examples.txt +1437 -136
  71. data/spec/factories/group.rb +17 -0
  72. data/spec/factories/searches.rb +10 -0
  73. data/spec/features/browse_category_admin_spec.rb +36 -4
  74. data/spec/features/browse_category_navigation_spec.rb +44 -0
  75. data/spec/features/browse_category_spec.rb +2 -2
  76. data/spec/features/exhibits/translation_editing_spec.rb +49 -0
  77. data/spec/features/home_page_spec.rb +4 -4
  78. data/spec/features/javascript/about_page_admin_spec.rb +1 -1
  79. data/spec/features/javascript/block_controls_spec.rb +1 -1
  80. data/spec/features/javascript/blocks/browse_group_categories_block_spec.rb +64 -0
  81. data/spec/features/javascript/browse_group_admin_spec.rb +45 -0
  82. data/spec/features/javascript/edit_in_place_spec.rb +2 -2
  83. data/spec/features/javascript/feature_page_admin_spec.rb +1 -1
  84. data/spec/helpers/spotlight/crud_link_helpers_spec.rb +3 -3
  85. data/spec/models/sir_trevor_rails/blocks/browse_group_categories_block_spec.rb +41 -0
  86. data/spec/models/spotlight/exhibit_spec.rb +8 -0
  87. data/spec/models/spotlight/group_spec.rb +19 -0
  88. data/spec/models/spotlight/role_spec.rb +1 -1
  89. data/spec/models/spotlight/search_spec.rb +30 -3
  90. data/spec/services/spotlight/exhibit_import_export_service_spec.rb +48 -1
  91. data/spec/services/spotlight/solr_document_builder_spec.rb +1 -1
  92. data/spec/spec_helper.rb +1 -1
  93. data/spec/support/features/test_features_helpers.rb +15 -0
  94. data/spec/test_app_templates/Gemfile.extra +1 -0
  95. data/spec/test_app_templates/catalog_controller.rb +6 -3
  96. data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
  97. data/spec/views/spotlight/browse/index.html.erb_spec.rb +2 -0
  98. data/spec/views/spotlight/pages/show.html.erb_spec.rb +1 -0
  99. data/vendor/assets/javascripts/tiny-slider.js +3218 -0
  100. data/vendor/assets/stylesheets/tiny-slider.css +1 -0
  101. metadata +339 -263
  102. data/vendor/assets/javascripts/handlebars-v1.3.0.js +0 -2746
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe SirTrevorRails::Blocks::BrowseGroupCategoriesBlock do
4
+ subject { described_class.new({ type: '', data: block_data }, page) }
5
+
6
+ let!(:exhibit) { FactoryBot.create(:exhibit) }
7
+ let!(:page) { FactoryBot.create(:feature_page, exhibit: exhibit) }
8
+ let!(:group1) { FactoryBot.create(:group, exhibit: exhibit, title: 'abc123', published: true) }
9
+ let!(:group2) { FactoryBot.create(:group, exhibit: exhibit, title: 'xyz321', published: true) }
10
+
11
+ let(:block_data) { {} }
12
+
13
+ describe '#groups' do
14
+ it 'is the array of items with display set to true' do
15
+ block_data[:item] = {
16
+ '0': { id: 'abc123', display: 'true', weight: '2' },
17
+ '1': { id: 'xyz321', display: 'true', weight: '1' }
18
+ }
19
+ expect(subject.groups.length).to eq 2
20
+ expect(subject.groups.first.title).to eq 'xyz321'
21
+ end
22
+ end
23
+
24
+ describe '#groups?' do
25
+ it 'is the array of items with display set to true' do
26
+ block_data[:item] = {}
27
+ expect(subject.be_groups).to be_falsy
28
+ end
29
+ end
30
+
31
+ describe '#display_item_counts?' do
32
+ it do
33
+ expect(subject).not_to be_display_item_counts
34
+ end
35
+
36
+ it do
37
+ block_data['display-item-counts'] = 'true'
38
+ expect(subject).to be_display_item_counts
39
+ end
40
+ end
41
+ end
@@ -19,6 +19,14 @@ describe Spotlight::Exhibit, type: :model do
19
19
  expect(subject.description).to eq 'Test description'
20
20
  end
21
21
 
22
+ it 'has reserved slugs' do
23
+ subject.slug = 'site'
24
+
25
+ expect do
26
+ subject.save!
27
+ end.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Slug is reserved')
28
+ end
29
+
22
30
  describe 'validations' do
23
31
  it 'validates the presence of the title' do
24
32
  exhibit.title = ''
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Spotlight::Group, type: :model do
4
+ let(:exhibit) { FactoryBot.create(:exhibit) }
5
+
6
+ describe '#searches' do
7
+ let(:group) { FactoryBot.create(:group_with_searches, exhibit: exhibit, searches_count: 4) }
8
+
9
+ it do
10
+ expect(group.searches.count).to eq 4
11
+ end
12
+
13
+ it do
14
+ group.searches.all do |search|
15
+ expect(search).to be_an Spotlight::Search
16
+ end
17
+ end
18
+ end
19
+ end
@@ -9,7 +9,7 @@ describe Spotlight::Role, type: :model do
9
9
 
10
10
  it 'does not be valid' do
11
11
  expect(subject).not_to be_valid
12
- expect(subject.errors.messages).to include(role: ['is not included in the list'], 'user.email': ["can't be blank"])
12
+ expect(subject.errors.messages.transform_values(&:to_a)).to include(role: ['is not included in the list'], 'user.email': ["can't be blank"])
13
13
  end
14
14
  end
15
15
 
@@ -5,7 +5,7 @@ describe Spotlight::Search, type: :model do
5
5
 
6
6
  let(:exhibit) { FactoryBot.create(:exhibit) }
7
7
 
8
- let(:query_params) { { 'f' => { 'genre_sim' => ['map'] } } }
8
+ let(:query_params) { { 'f' => { 'genre_ssim' => ['map'] } } }
9
9
 
10
10
  let(:blacklight_config) { ::CatalogController.blacklight_config }
11
11
  let(:document) do
@@ -17,7 +17,10 @@ describe Spotlight::Search, type: :model do
17
17
  let(:query_params) { {} }
18
18
 
19
19
  it 'has items' do
20
- expect(subject.documents.size).to eq 55
20
+ # Add some fuzziness to this. Running locally it should be 55 but in some
21
+ # CI environments, spec/features/add_items_spec.rb will end up adding new
22
+ # records and cannot ensure deletion of them.
23
+ expect(subject.documents.size).to be_within(1).of(55)
21
24
  end
22
25
  end
23
26
 
@@ -31,6 +34,16 @@ describe Spotlight::Search, type: :model do
31
34
  end
32
35
  end
33
36
 
37
+ describe '.unpublished' do
38
+ let!(:search1) { FactoryBot.create(:search, published: true) }
39
+ let!(:search2) { FactoryBot.create(:search, published: false) }
40
+ let!(:search3) { FactoryBot.create(:search, published: nil) }
41
+
42
+ it 'accounts for nil and false values' do
43
+ expect(described_class.unpublished.count).to eq 5
44
+ end
45
+ end
46
+
34
47
  describe '#slug' do
35
48
  let(:search) { FactoryBot.create(:search) }
36
49
 
@@ -54,7 +67,7 @@ describe Spotlight::Search, type: :model do
54
67
 
55
68
  describe '#search_params' do
56
69
  it 'maps the search to the appropriate facet values' do
57
- expect(subject.search_params.to_hash).to include 'fq' => array_including('{!term f=genre_sim}map')
70
+ expect(subject.search_params.to_hash).to include 'fq' => array_including('{!term f=genre_ssim}map')
58
71
  end
59
72
 
60
73
  context 'with filter_resources_by_exhibit configured' do
@@ -95,4 +108,18 @@ describe Spotlight::Search, type: :model do
95
108
  expect(search_params).to include user_params
96
109
  end
97
110
  end
111
+
112
+ describe '#groups' do
113
+ let(:search) { FactoryBot.create(:search_with_groups, groups_count: 3) }
114
+
115
+ it do
116
+ expect(search.groups.count).to eq 3
117
+ end
118
+
119
+ it do
120
+ search.groups.all do |group|
121
+ expect(group).to be_an Spotlight::Group
122
+ end
123
+ end
124
+ end
98
125
  end
@@ -24,6 +24,10 @@ describe Spotlight::ExhibitImportExportService do
24
24
  expect(subject['searches']).to have(source_exhibit.searches.count).searches
25
25
  end
26
26
 
27
+ it 'has group attributes' do
28
+ expect(subject['groups']).to have(source_exhibit.groups.count).groups
29
+ end
30
+
27
31
  it 'has home page attributes' do
28
32
  expect(subject).to have_key 'home_page'
29
33
  expect(subject['home_page']).not_to have_key 'id'
@@ -329,10 +333,26 @@ describe Spotlight::ExhibitImportExportService do
329
333
  end
330
334
  end
331
335
 
336
+ context 'remote thumbnail with existing tilesource' do
337
+ it do
338
+ search.thumbnail.iiif_tilesource
339
+ search.thumbnail.save
340
+ source_exhibit.reload
341
+ end
342
+
343
+ it 'unsets the iiif_tilesource' do
344
+ subject
345
+ existing_search.reload
346
+ expect(existing_search.thumbnail.iiif_tilesource).not_to eq search.thumbnail.iiif_tilesource
347
+ end
348
+ end
349
+
332
350
  context 'with a thumbnail from an uploaded resource' do
333
351
  before do
334
352
  search.masthead.document_global_id = SolrDocument.new(id: 'xyz').to_global_id
335
353
  search.masthead.source = 'exhibit'
354
+ search.masthead.iiif_tilesource = 'foo'
355
+ search.masthead.image = nil # Setting to nil to mimick an exhibit source
336
356
  search.masthead.save
337
357
 
338
358
  source_exhibit.reload
@@ -343,6 +363,33 @@ describe Spotlight::ExhibitImportExportService do
343
363
  existing_search.reload
344
364
  expect(existing_search.masthead).not_to be_blank
345
365
  end
366
+
367
+ it 'does not unset the iiif_tilesource' do
368
+ subject
369
+ existing_search.reload
370
+ expect(existing_search.masthead.iiif_tilesource).to eq search.masthead.iiif_tilesource
371
+ end
372
+ end
373
+
374
+ context 'in a group' do
375
+ let!(:group) { FactoryBot.create(:group, title: 'blah', exhibit: source_exhibit) }
376
+
377
+ before do
378
+ search.groups << group
379
+ search.save!
380
+ end
381
+
382
+ it 'copies the group' do
383
+ subject
384
+ expect(destination_exhibit.groups.length).to eq 1
385
+ expect(destination_exhibit.groups.first.as_json).to include(group.as_json.slice('slug', 'title', 'weight', 'published'))
386
+ end
387
+
388
+ it 'copies the group membership' do
389
+ subject
390
+ expect(existing_search.reload.groups.length).to eq 1
391
+ expect(existing_search.groups.pluck(:slug)).to match_array [group.slug]
392
+ end
346
393
  end
347
394
  end
348
395
 
@@ -516,7 +563,7 @@ describe Spotlight::ExhibitImportExportService do
516
563
  end
517
564
 
518
565
  it 'includes only the page-related data' do
519
- expect(subject.keys).to match_array %w[searches about_pages feature_pages home_page contacts]
566
+ expect(subject.keys).to match_array %w[searches groups about_pages feature_pages home_page contacts]
520
567
  end
521
568
  end
522
569
  end
@@ -54,7 +54,7 @@ describe Spotlight::SolrDocumentBuilder do
54
54
  end
55
55
 
56
56
  context 'without a persisted resource' do
57
- let(:resource) { FactoryBot.build(:resource) }
57
+ let(:resource) { FactoryBot.build(:resource, exhibit: exhibit) }
58
58
 
59
59
  it 'does not implicitly persist the resource' do
60
60
  resource.document_builder.documents_to_index.first
data/spec/spec_helper.rb CHANGED
@@ -136,7 +136,7 @@ RSpec.configure do |config|
136
136
  Kernel.srand config.seed
137
137
  end
138
138
 
139
- def add_new_page_via_button(title = 'New Page')
139
+ def add_new_via_button(title = 'New Page')
140
140
  add_link = find('[data-expanded-add-button]')
141
141
  within(add_link) do
142
142
  expect(page).to have_css("input[type='text']", visible: false)
@@ -15,6 +15,21 @@ module Spotlight
15
15
  find('.tt-suggestion', text: opts[:with], match: :first).click
16
16
  end
17
17
 
18
+ ##
19
+ # For typeahead "prefetched" fields, we need to wait for a resolved selector
20
+ # before proceeding.
21
+ def fill_in_prefetched_typeahead_field(opts)
22
+ type = opts[:type] || 'twitter'
23
+ # Poltergeist / Capybara doesn't fire the events typeahead.js
24
+ # is listening for, so we help it out a little:
25
+ find(opts[:wait_for]) if opts[:wait_for]
26
+ page.execute_script <<-EOF
27
+ $("[data-#{type}-typeahead]:visible").val("#{opts[:with]}").trigger("input");
28
+ $("[data-#{type}-typeahead]:visible").typeahead("open");
29
+ $(".tt-suggestion").click();
30
+ EOF
31
+ end
32
+
18
33
  # just like #fill_in_typeahead_field, but wait for the
19
34
  # form fields to show up on the page too
20
35
  def fill_in_solr_document_block_typeahead_field(opts)
@@ -0,0 +1 @@
1
+ gem 'sprockets', '< 4' if ENV['RAILS_VERSION'] && ENV['RAILS_VERSION'] < '6'
@@ -6,6 +6,12 @@ class CatalogController < ApplicationController
6
6
  before_action :set_paper_trail_whodunnit
7
7
 
8
8
  configure_blacklight do |config|
9
+ config.view.gallery.document_component = Blacklight::Gallery::DocumentComponent
10
+ # config.view.gallery.classes = 'row-cols-2 row-cols-md-3'
11
+ config.view.masonry.document_component = Blacklight::Gallery::DocumentComponent
12
+ config.view.slideshow.document_component = Blacklight::Gallery::SlideshowComponent
13
+ config.show.tile_source_field = :content_metadata_image_iiif_info_ssm
14
+ config.show.partials.insert(1, :openseadragon)
9
15
  ## Default parameters to send to solr for all search-like requests. See also SolrHelper#solr_search_params
10
16
  config.default_solr_params = {
11
17
  qt: 'search',
@@ -29,9 +35,6 @@ class CatalogController < ApplicationController
29
35
  config.index.display_type_field = 'content_metadata_type_ssm'
30
36
  config.index.thumbnail_field = Spotlight::Engine.config.thumbnail_field
31
37
 
32
- config.view.gallery.partials = %i[index_header index]
33
- config.view.slideshow.partials = [:index]
34
-
35
38
  config.show.tile_source_field = :content_metadata_image_iiif_info_ssm
36
39
  config.show.partials.insert(1, :openseadragon)
37
40
 
@@ -11,7 +11,7 @@ class TestAppGenerator < Rails::Generators::Base
11
11
 
12
12
  def add_gems
13
13
  gem 'blacklight', '~> 7.0'
14
- gem 'blacklight-gallery', '~> 1.1'
14
+ gem 'blacklight-gallery', '~> 3.0'
15
15
  Bundler.with_clean_env do
16
16
  run 'bundle install'
17
17
  end
@@ -7,11 +7,13 @@ describe 'spotlight/browse/index', type: :view do
7
7
  before { allow(view).to receive(:current_exhibit).and_return(search.exhibit) }
8
8
 
9
9
  it 'has a title' do
10
+ assign :groups, []
10
11
  render
11
12
  expect(response).to have_selector 'h1', text: 'Browse'
12
13
  end
13
14
 
14
15
  it 'renders the collection of searches' do
16
+ assign :groups, []
15
17
  assign :searches, [search, another_search]
16
18
  stub_template 'spotlight/browse/_search.html.erb' => '<%= search.id %> <br/>'
17
19
  render
@@ -11,6 +11,7 @@ describe 'spotlight/pages/show', type: :view do
11
11
 
12
12
  before do
13
13
  allow(view).to receive(:current_exhibit).and_return(exhibit)
14
+ allow(view).to receive(:render_body_class).and_return('')
14
15
  assign(:page, page)
15
16
  stub_template 'spotlight/pages/_sidebar.html.erb' => 'Sidebar'
16
17
  stub_template 'spotlight/pages/_tophat.html.erb' => ''
@@ -0,0 +1,3218 @@
1
+ // Includes an unreleased RTL support pull request: https://github.com/ganlanyuan/tiny-slider/pull/658
2
+ var tns = (function (){
3
+ var win = window;
4
+
5
+ var raf = win.requestAnimationFrame
6
+ || win.webkitRequestAnimationFrame
7
+ || win.mozRequestAnimationFrame
8
+ || win.msRequestAnimationFrame
9
+ || function(cb) { return setTimeout(cb, 16); };
10
+
11
+ var win$1 = window;
12
+
13
+ var caf = win$1.cancelAnimationFrame
14
+ || win$1.mozCancelAnimationFrame
15
+ || function(id){ clearTimeout(id); };
16
+
17
+ function extend() {
18
+ var obj, name, copy,
19
+ target = arguments[0] || {},
20
+ i = 1,
21
+ length = arguments.length;
22
+
23
+ for (; i < length; i++) {
24
+ if ((obj = arguments[i]) !== null) {
25
+ for (name in obj) {
26
+ copy = obj[name];
27
+
28
+ if (target === copy) {
29
+ continue;
30
+ } else if (copy !== undefined) {
31
+ target[name] = copy;
32
+ }
33
+ }
34
+ }
35
+ }
36
+ return target;
37
+ }
38
+
39
+ function checkStorageValue (value) {
40
+ return ['true', 'false'].indexOf(value) >= 0 ? JSON.parse(value) : value;
41
+ }
42
+
43
+ function setLocalStorage(storage, key, value, access) {
44
+ if (access) {
45
+ try { storage.setItem(key, value); } catch (e) {}
46
+ }
47
+ return value;
48
+ }
49
+
50
+ function getSlideId() {
51
+ var id = window.tnsId;
52
+ window.tnsId = !id ? 1 : id + 1;
53
+
54
+ return 'tns' + window.tnsId;
55
+ }
56
+
57
+ function getBody () {
58
+ var doc = document,
59
+ body = doc.body;
60
+
61
+ if (!body) {
62
+ body = doc.createElement('body');
63
+ body.fake = true;
64
+ }
65
+
66
+ return body;
67
+ }
68
+
69
+ var docElement = document.documentElement;
70
+
71
+ function setFakeBody (body) {
72
+ var docOverflow = '';
73
+ if (body.fake) {
74
+ docOverflow = docElement.style.overflow;
75
+ //avoid crashing IE8, if background image is used
76
+ body.style.background = '';
77
+ //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
78
+ body.style.overflow = docElement.style.overflow = 'hidden';
79
+ docElement.appendChild(body);
80
+ }
81
+
82
+ return docOverflow;
83
+ }
84
+
85
+ function resetFakeBody (body, docOverflow) {
86
+ if (body.fake) {
87
+ body.remove();
88
+ docElement.style.overflow = docOverflow;
89
+ // Trigger layout so kinetic scrolling isn't disabled in iOS6+
90
+ // eslint-disable-next-line
91
+ docElement.offsetHeight;
92
+ }
93
+ }
94
+
95
+ // get css-calc
96
+
97
+ function calc() {
98
+ var doc = document,
99
+ body = getBody(),
100
+ docOverflow = setFakeBody(body),
101
+ div = doc.createElement('div'),
102
+ result = false;
103
+
104
+ body.appendChild(div);
105
+ try {
106
+ var str = '(10px * 10)',
107
+ vals = ['calc' + str, '-moz-calc' + str, '-webkit-calc' + str],
108
+ val;
109
+ for (var i = 0; i < 3; i++) {
110
+ val = vals[i];
111
+ div.style.width = val;
112
+ if (div.offsetWidth === 100) {
113
+ result = val.replace(str, '');
114
+ break;
115
+ }
116
+ }
117
+ } catch (e) {}
118
+
119
+ body.fake ? resetFakeBody(body, docOverflow) : div.remove();
120
+
121
+ return result;
122
+ }
123
+
124
+ // get subpixel support value
125
+
126
+ function percentageLayout() {
127
+ // check subpixel layout supporting
128
+ var doc = document,
129
+ body = getBody(),
130
+ docOverflow = setFakeBody(body),
131
+ wrapper = doc.createElement('div'),
132
+ outer = doc.createElement('div'),
133
+ str = '',
134
+ count = 70,
135
+ perPage = 3,
136
+ supported = false;
137
+
138
+ wrapper.className = "tns-t-subp2";
139
+ outer.className = "tns-t-ct";
140
+
141
+ for (var i = 0; i < count; i++) {
142
+ str += '<div></div>';
143
+ }
144
+
145
+ outer.innerHTML = str;
146
+ wrapper.appendChild(outer);
147
+ body.appendChild(wrapper);
148
+
149
+ supported = Math.abs(wrapper.getBoundingClientRect().left - outer.children[count - perPage].getBoundingClientRect().left) < 2;
150
+
151
+ body.fake ? resetFakeBody(body, docOverflow) : wrapper.remove();
152
+
153
+ return supported;
154
+ }
155
+
156
+ function mediaquerySupport () {
157
+ if (window.matchMedia || window.msMatchMedia) {
158
+ return true;
159
+ }
160
+
161
+ var doc = document,
162
+ body = getBody(),
163
+ docOverflow = setFakeBody(body),
164
+ div = doc.createElement('div'),
165
+ style = doc.createElement('style'),
166
+ rule = '@media all and (min-width:1px){.tns-mq-test{position:absolute}}',
167
+ position;
168
+
169
+ style.type = 'text/css';
170
+ div.className = 'tns-mq-test';
171
+
172
+ body.appendChild(style);
173
+ body.appendChild(div);
174
+
175
+ if (style.styleSheet) {
176
+ style.styleSheet.cssText = rule;
177
+ } else {
178
+ style.appendChild(doc.createTextNode(rule));
179
+ }
180
+
181
+ position = window.getComputedStyle ? window.getComputedStyle(div).position : div.currentStyle['position'];
182
+
183
+ body.fake ? resetFakeBody(body, docOverflow) : div.remove();
184
+
185
+ return position === "absolute";
186
+ }
187
+
188
+ // create and append style sheet
189
+ function createStyleSheet (media, nonce) {
190
+ // Create the <style> tag
191
+ var style = document.createElement("style");
192
+ // style.setAttribute("type", "text/css");
193
+
194
+ // Add a media (and/or media query) here if you'd like!
195
+ // style.setAttribute("media", "screen")
196
+ // style.setAttribute("media", "only screen and (max-width : 1024px)")
197
+ if (media) { style.setAttribute("media", media); }
198
+
199
+ // Add nonce attribute for Content Security Policy
200
+ if (nonce) { style.setAttribute("nonce", nonce); }
201
+
202
+ // WebKit hack :(
203
+ // style.appendChild(document.createTextNode(""));
204
+
205
+ // Add the <style> element to the page
206
+ document.querySelector('head').appendChild(style);
207
+
208
+ return style.sheet ? style.sheet : style.styleSheet;
209
+ }
210
+
211
+ // cross browsers addRule method
212
+ function addCSSRule(sheet, selector, rules, index) {
213
+ // return raf(function() {
214
+ 'insertRule' in sheet ?
215
+ sheet.insertRule(selector + '{' + rules + '}', index) :
216
+ sheet.addRule(selector, rules, index);
217
+ // });
218
+ }
219
+
220
+ // cross browsers addRule method
221
+ function removeCSSRule(sheet, index) {
222
+ // return raf(function() {
223
+ 'deleteRule' in sheet ?
224
+ sheet.deleteRule(index) :
225
+ sheet.removeRule(index);
226
+ // });
227
+ }
228
+
229
+ function getCssRulesLength(sheet) {
230
+ var rule = ('insertRule' in sheet) ? sheet.cssRules : sheet.rules;
231
+ return rule.length;
232
+ }
233
+
234
+ function toDegree (y, x) {
235
+ return Math.atan2(y, x) * (180 / Math.PI);
236
+ }
237
+
238
+ function getTouchDirection(angle, range) {
239
+ var direction = false,
240
+ gap = Math.abs(90 - Math.abs(angle));
241
+
242
+ if (gap >= 90 - range) {
243
+ direction = 'horizontal';
244
+ } else if (gap <= range) {
245
+ direction = 'vertical';
246
+ }
247
+
248
+ return direction;
249
+ }
250
+
251
+ // https://toddmotto.com/ditch-the-array-foreach-call-nodelist-hack/
252
+ function forEach (arr, callback, scope) {
253
+ for (var i = 0, l = arr.length; i < l; i++) {
254
+ callback.call(scope, arr[i], i);
255
+ }
256
+ }
257
+
258
+ var classListSupport = 'classList' in document.createElement('_');
259
+
260
+ var hasClass = classListSupport ?
261
+ function (el, str) { return el.classList.contains(str); } :
262
+ function (el, str) { return el.className.indexOf(str) >= 0; };
263
+
264
+ var addClass = classListSupport ?
265
+ function (el, str) {
266
+ if (!hasClass(el, str)) { el.classList.add(str); }
267
+ } :
268
+ function (el, str) {
269
+ if (!hasClass(el, str)) { el.className += ' ' + str; }
270
+ };
271
+
272
+ var removeClass = classListSupport ?
273
+ function (el, str) {
274
+ if (hasClass(el, str)) { el.classList.remove(str); }
275
+ } :
276
+ function (el, str) {
277
+ if (hasClass(el, str)) { el.className = el.className.replace(str, ''); }
278
+ };
279
+
280
+ function hasAttr(el, attr) {
281
+ return el.hasAttribute(attr);
282
+ }
283
+
284
+ function getAttr(el, attr) {
285
+ return el.getAttribute(attr);
286
+ }
287
+
288
+ function isNodeList(el) {
289
+ // Only NodeList has the "item()" function
290
+ return typeof el.item !== "undefined";
291
+ }
292
+
293
+ function setAttrs(els, attrs) {
294
+ els = (isNodeList(els) || els instanceof Array) ? els : [els];
295
+ if (Object.prototype.toString.call(attrs) !== '[object Object]') { return; }
296
+
297
+ for (var i = els.length; i--;) {
298
+ for(var key in attrs) {
299
+ els[i].setAttribute(key, attrs[key]);
300
+ }
301
+ }
302
+ }
303
+
304
+ function removeAttrs(els, attrs) {
305
+ els = (isNodeList(els) || els instanceof Array) ? els : [els];
306
+ attrs = (attrs instanceof Array) ? attrs : [attrs];
307
+
308
+ var attrLength = attrs.length;
309
+ for (var i = els.length; i--;) {
310
+ for (var j = attrLength; j--;) {
311
+ els[i].removeAttribute(attrs[j]);
312
+ }
313
+ }
314
+ }
315
+
316
+ function arrayFromNodeList (nl) {
317
+ var arr = [];
318
+ for (var i = 0, l = nl.length; i < l; i++) {
319
+ arr.push(nl[i]);
320
+ }
321
+ return arr;
322
+ }
323
+
324
+ function hideElement(el, forceHide) {
325
+ if (el.style.display !== 'none') { el.style.display = 'none'; }
326
+ }
327
+
328
+ function showElement(el, forceHide) {
329
+ if (el.style.display === 'none') { el.style.display = ''; }
330
+ }
331
+
332
+ function isVisible(el) {
333
+ return window.getComputedStyle(el).display !== 'none';
334
+ }
335
+
336
+ function whichProperty(props){
337
+ if (typeof props === 'string') {
338
+ var arr = [props],
339
+ Props = props.charAt(0).toUpperCase() + props.substr(1),
340
+ prefixes = ['Webkit', 'Moz', 'ms', 'O'];
341
+
342
+ prefixes.forEach(function(prefix) {
343
+ if (prefix !== 'ms' || props === 'transform') {
344
+ arr.push(prefix + Props);
345
+ }
346
+ });
347
+
348
+ props = arr;
349
+ }
350
+
351
+ var el = document.createElement('fakeelement'),
352
+ len = props.length;
353
+ for(var i = 0; i < props.length; i++){
354
+ var prop = props[i];
355
+ if( el.style[prop] !== undefined ){ return prop; }
356
+ }
357
+
358
+ return false; // explicit for ie9-
359
+ }
360
+
361
+ function has3DTransforms(tf){
362
+ if (!tf) { return false; }
363
+ if (!window.getComputedStyle) { return false; }
364
+
365
+ var doc = document,
366
+ body = getBody(),
367
+ docOverflow = setFakeBody(body),
368
+ el = doc.createElement('p'),
369
+ has3d,
370
+ cssTF = tf.length > 9 ? '-' + tf.slice(0, -9).toLowerCase() + '-' : '';
371
+
372
+ cssTF += 'transform';
373
+
374
+ // Add it to the body to get the computed style
375
+ body.insertBefore(el, null);
376
+
377
+ el.style[tf] = 'translate3d(1px,1px,1px)';
378
+ has3d = window.getComputedStyle(el).getPropertyValue(cssTF);
379
+
380
+ body.fake ? resetFakeBody(body, docOverflow) : el.remove();
381
+
382
+ return (has3d !== undefined && has3d.length > 0 && has3d !== "none");
383
+ }
384
+
385
+ // get transitionend, animationend based on transitionDuration
386
+ // @propin: string
387
+ // @propOut: string, first-letter uppercase
388
+ // Usage: getEndProperty('WebkitTransitionDuration', 'Transition') => webkitTransitionEnd
389
+ function getEndProperty(propIn, propOut) {
390
+ var endProp = false;
391
+ if (/^Webkit/.test(propIn)) {
392
+ endProp = 'webkit' + propOut + 'End';
393
+ } else if (/^O/.test(propIn)) {
394
+ endProp = 'o' + propOut + 'End';
395
+ } else if (propIn) {
396
+ endProp = propOut.toLowerCase() + 'end';
397
+ }
398
+ return endProp;
399
+ }
400
+
401
+ // Test via a getter in the options object to see if the passive property is accessed
402
+ var supportsPassive = false;
403
+ try {
404
+ var opts = Object.defineProperty({}, 'passive', {
405
+ get: function() {
406
+ supportsPassive = true;
407
+ }
408
+ });
409
+ window.addEventListener("test", null, opts);
410
+ } catch (e) {}
411
+ var passiveOption = supportsPassive ? { passive: true } : false;
412
+
413
+ function addEvents(el, obj, preventScrolling) {
414
+ for (var prop in obj) {
415
+ var option = ['touchstart', 'touchmove'].indexOf(prop) >= 0 && !preventScrolling ? passiveOption : false;
416
+ el.addEventListener(prop, obj[prop], option);
417
+ }
418
+ }
419
+
420
+ function removeEvents(el, obj) {
421
+ for (var prop in obj) {
422
+ var option = ['touchstart', 'touchmove'].indexOf(prop) >= 0 ? passiveOption : false;
423
+ el.removeEventListener(prop, obj[prop], option);
424
+ }
425
+ }
426
+
427
+ function Events() {
428
+ return {
429
+ topics: {},
430
+ on: function (eventName, fn) {
431
+ this.topics[eventName] = this.topics[eventName] || [];
432
+ this.topics[eventName].push(fn);
433
+ },
434
+ off: function(eventName, fn) {
435
+ if (this.topics[eventName]) {
436
+ for (var i = 0; i < this.topics[eventName].length; i++) {
437
+ if (this.topics[eventName][i] === fn) {
438
+ this.topics[eventName].splice(i, 1);
439
+ break;
440
+ }
441
+ }
442
+ }
443
+ },
444
+ emit: function (eventName, data) {
445
+ data.type = eventName;
446
+ if (this.topics[eventName]) {
447
+ this.topics[eventName].forEach(function(fn) {
448
+ fn(data, eventName);
449
+ });
450
+ }
451
+ }
452
+ };
453
+ }
454
+
455
+ function jsTransform(element, attr, prefix, postfix, to, duration, callback) {
456
+ var tick = Math.min(duration, 10),
457
+ unit = (to.indexOf('%') >= 0) ? '%' : 'px',
458
+ to = to.replace(unit, ''),
459
+ from = Number(element.style[attr].replace(prefix, '').replace(postfix, '').replace(unit, '')),
460
+ positionTick = (to - from) / duration * tick,
461
+ running;
462
+
463
+ setTimeout(moveElement, tick);
464
+ function moveElement() {
465
+ duration -= tick;
466
+ from += positionTick;
467
+ element.style[attr] = prefix + from + unit + postfix;
468
+ if (duration > 0) {
469
+ setTimeout(moveElement, tick);
470
+ } else {
471
+ callback();
472
+ }
473
+ }
474
+ }
475
+
476
+ // Object.keys
477
+ if (!Object.keys) {
478
+ Object.keys = function(object) {
479
+ var keys = [];
480
+ for (var name in object) {
481
+ if (Object.prototype.hasOwnProperty.call(object, name)) {
482
+ keys.push(name);
483
+ }
484
+ }
485
+ return keys;
486
+ };
487
+ }
488
+
489
+ // ChildNode.remove
490
+ if(!("remove" in Element.prototype)){
491
+ Element.prototype.remove = function(){
492
+ if(this.parentNode) {
493
+ this.parentNode.removeChild(this);
494
+ }
495
+ };
496
+ }
497
+
498
+ var tns = function(options) {
499
+ options = extend({
500
+ container: '.slider',
501
+ mode: 'carousel',
502
+ axis: 'horizontal',
503
+ items: 1,
504
+ gutter: 0,
505
+ edgePadding: 0,
506
+ fixedWidth: false,
507
+ autoWidth: false,
508
+ viewportMax: false,
509
+ slideBy: 1,
510
+ center: false,
511
+ controls: true,
512
+ controlsPosition: 'top',
513
+ controlsText: ['prev', 'next'],
514
+ controlsContainer: false,
515
+ prevButton: false,
516
+ nextButton: false,
517
+ nav: true,
518
+ navPosition: 'top',
519
+ navContainer: false,
520
+ navAsThumbnails: false,
521
+ arrowKeys: false,
522
+ speed: 300,
523
+ autoplay: false,
524
+ autoplayPosition: 'top',
525
+ autoplayTimeout: 5000,
526
+ autoplayDirection: 'forward',
527
+ autoplayText: ['start', 'stop'],
528
+ autoplayHoverPause: false,
529
+ autoplayButton: false,
530
+ autoplayButtonOutput: true,
531
+ autoplayResetOnVisibility: true,
532
+ animateIn: 'tns-fadeIn',
533
+ animateOut: 'tns-fadeOut',
534
+ animateNormal: 'tns-normal',
535
+ animateDelay: false,
536
+ loop: true,
537
+ rewind: false,
538
+ autoHeight: false,
539
+ responsive: false,
540
+ lazyload: false,
541
+ lazyloadSelector: '.tns-lazy-img',
542
+ touch: true,
543
+ mouseDrag: false,
544
+ swipeAngle: 15,
545
+ nested: false,
546
+ preventActionWhenRunning: false,
547
+ preventScrollOnTouch: false,
548
+ freezable: true,
549
+ onInit: false,
550
+ useLocalStorage: true,
551
+ textDirection: 'ltr',
552
+ nonce: false
553
+ }, options || {});
554
+
555
+ var doc = document,
556
+ win = window,
557
+ KEYS = {
558
+ ENTER: 13,
559
+ SPACE: 32,
560
+ LEFT: 37,
561
+ RIGHT: 39
562
+ },
563
+ tnsStorage = {},
564
+ localStorageAccess = options.useLocalStorage;
565
+
566
+ if (localStorageAccess) {
567
+ // check browser version and local storage access
568
+ var browserInfo = navigator.userAgent;
569
+ var uid = new Date;
570
+
571
+ try {
572
+ tnsStorage = win.localStorage;
573
+ if (tnsStorage) {
574
+ tnsStorage.setItem(uid, uid);
575
+ localStorageAccess = tnsStorage.getItem(uid) == uid;
576
+ tnsStorage.removeItem(uid);
577
+ } else {
578
+ localStorageAccess = false;
579
+ }
580
+ if (!localStorageAccess) { tnsStorage = {}; }
581
+ } catch(e) {
582
+ localStorageAccess = false;
583
+ }
584
+
585
+ if (localStorageAccess) {
586
+ // remove storage when browser version changes
587
+ if (tnsStorage['tnsApp'] && tnsStorage['tnsApp'] !== browserInfo) {
588
+ ['tC', 'tPL', 'tMQ', 'tTf', 't3D', 'tTDu', 'tTDe', 'tADu', 'tADe', 'tTE', 'tAE'].forEach(function(item) { tnsStorage.removeItem(item); });
589
+ }
590
+ // update browserInfo
591
+ localStorage['tnsApp'] = browserInfo;
592
+ }
593
+ }
594
+
595
+ var CALC = tnsStorage['tC'] ? checkStorageValue(tnsStorage['tC']) : setLocalStorage(tnsStorage, 'tC', calc(), localStorageAccess),
596
+ PERCENTAGELAYOUT = tnsStorage['tPL'] ? checkStorageValue(tnsStorage['tPL']) : setLocalStorage(tnsStorage, 'tPL', percentageLayout(), localStorageAccess),
597
+ CSSMQ = tnsStorage['tMQ'] ? checkStorageValue(tnsStorage['tMQ']) : setLocalStorage(tnsStorage, 'tMQ', mediaquerySupport(), localStorageAccess),
598
+ TRANSFORM = tnsStorage['tTf'] ? checkStorageValue(tnsStorage['tTf']) : setLocalStorage(tnsStorage, 'tTf', whichProperty('transform'), localStorageAccess),
599
+ HAS3DTRANSFORMS = tnsStorage['t3D'] ? checkStorageValue(tnsStorage['t3D']) : setLocalStorage(tnsStorage, 't3D', has3DTransforms(TRANSFORM), localStorageAccess),
600
+ TRANSITIONDURATION = tnsStorage['tTDu'] ? checkStorageValue(tnsStorage['tTDu']) : setLocalStorage(tnsStorage, 'tTDu', whichProperty('transitionDuration'), localStorageAccess),
601
+ TRANSITIONDELAY = tnsStorage['tTDe'] ? checkStorageValue(tnsStorage['tTDe']) : setLocalStorage(tnsStorage, 'tTDe', whichProperty('transitionDelay'), localStorageAccess),
602
+ ANIMATIONDURATION = tnsStorage['tADu'] ? checkStorageValue(tnsStorage['tADu']) : setLocalStorage(tnsStorage, 'tADu', whichProperty('animationDuration'), localStorageAccess),
603
+ ANIMATIONDELAY = tnsStorage['tADe'] ? checkStorageValue(tnsStorage['tADe']) : setLocalStorage(tnsStorage, 'tADe', whichProperty('animationDelay'), localStorageAccess),
604
+ TRANSITIONEND = tnsStorage['tTE'] ? checkStorageValue(tnsStorage['tTE']) : setLocalStorage(tnsStorage, 'tTE', getEndProperty(TRANSITIONDURATION, 'Transition'), localStorageAccess),
605
+ ANIMATIONEND = tnsStorage['tAE'] ? checkStorageValue(tnsStorage['tAE']) : setLocalStorage(tnsStorage, 'tAE', getEndProperty(ANIMATIONDURATION, 'Animation'), localStorageAccess);
606
+
607
+ // get element nodes from selectors
608
+ var supportConsoleWarn = win.console && typeof win.console.warn === "function",
609
+ tnsList = ['container', 'controlsContainer', 'prevButton', 'nextButton', 'navContainer', 'autoplayButton'],
610
+ optionsElements = {};
611
+
612
+ tnsList.forEach(function(item) {
613
+ if (typeof options[item] === 'string') {
614
+ var str = options[item],
615
+ el = doc.querySelector(str);
616
+ optionsElements[item] = str;
617
+
618
+ if (el && el.nodeName) {
619
+ options[item] = el;
620
+ } else {
621
+ if (supportConsoleWarn) { console.warn('Can\'t find', options[item]); }
622
+ return;
623
+ }
624
+ }
625
+ });
626
+
627
+ // make sure at least 1 slide
628
+ if (options.container.children.length < 1) {
629
+ if (supportConsoleWarn) { console.warn('No slides found in', options.container); }
630
+ return;
631
+ }
632
+
633
+ // update options
634
+ var responsive = options.responsive,
635
+ nested = options.nested,
636
+ carousel = options.mode === 'carousel' ? true : false;
637
+
638
+ if (responsive) {
639
+ // apply responsive[0] to options and remove it
640
+ if (0 in responsive) {
641
+ options = extend(options, responsive[0]);
642
+ delete responsive[0];
643
+ }
644
+
645
+ var responsiveTem = {};
646
+ for (var key in responsive) {
647
+ var val = responsive[key];
648
+ // update responsive
649
+ // from: 300: 2
650
+ // to:
651
+ // 300: {
652
+ // items: 2
653
+ // }
654
+ val = typeof val === 'number' ? {items: val} : val;
655
+ responsiveTem[key] = val;
656
+ }
657
+ responsive = responsiveTem;
658
+ responsiveTem = null;
659
+ }
660
+
661
+ // update options
662
+ function updateOptions (obj) {
663
+ for (var key in obj) {
664
+ if (!carousel) {
665
+ if (key === 'slideBy') { obj[key] = 'page'; }
666
+ if (key === 'edgePadding') { obj[key] = false; }
667
+ if (key === 'autoHeight') { obj[key] = false; }
668
+ }
669
+
670
+ // update responsive options
671
+ if (key === 'responsive') { updateOptions(obj[key]); }
672
+ }
673
+ }
674
+ if (!carousel) { updateOptions(options); }
675
+
676
+
677
+ // === define and set variables ===
678
+ if (!carousel) {
679
+ options.axis = 'horizontal';
680
+ options.slideBy = 'page';
681
+ options.edgePadding = false;
682
+
683
+ var animateIn = options.animateIn,
684
+ animateOut = options.animateOut,
685
+ animateDelay = options.animateDelay,
686
+ animateNormal = options.animateNormal;
687
+ }
688
+
689
+ var horizontal = options.axis === 'horizontal' ? true : false,
690
+ outerWrapper = doc.createElement('div'),
691
+ innerWrapper = doc.createElement('div'),
692
+ middleWrapper,
693
+ container = options.container,
694
+ containerParent = container.parentNode,
695
+ containerHTML = container.outerHTML,
696
+ slideItems = container.children,
697
+ slideCount = slideItems.length,
698
+ breakpointZone,
699
+ windowWidth = getWindowWidth(),
700
+ isOn = false;
701
+ if (responsive) { setBreakpointZone(); }
702
+ if (carousel) { container.className += ' tns-vpfix'; }
703
+
704
+ // fixedWidth: viewport > rightBoundary > indexMax
705
+ var autoWidth = options.autoWidth,
706
+ fixedWidth = getOption('fixedWidth'),
707
+ edgePadding = getOption('edgePadding'),
708
+ gutter = getOption('gutter'),
709
+ viewport = getViewportWidth(),
710
+ center = getOption('center'),
711
+ items = !autoWidth ? Math.floor(getOption('items')) : 1,
712
+ slideBy = getOption('slideBy'),
713
+ viewportMax = options.viewportMax || options.fixedWidthViewportWidth,
714
+ arrowKeys = getOption('arrowKeys'),
715
+ speed = getOption('speed'),
716
+ rewind = options.rewind,
717
+ loop = rewind ? false : options.loop,
718
+ autoHeight = getOption('autoHeight'),
719
+ controls = getOption('controls'),
720
+ controlsText = getOption('controlsText'),
721
+ textDirection = getOption('textDirection'),
722
+ nav = getOption('nav'),
723
+ touch = getOption('touch'),
724
+ mouseDrag = getOption('mouseDrag'),
725
+ autoplay = getOption('autoplay'),
726
+ autoplayTimeout = getOption('autoplayTimeout'),
727
+ autoplayText = getOption('autoplayText'),
728
+ autoplayHoverPause = getOption('autoplayHoverPause'),
729
+ autoplayResetOnVisibility = getOption('autoplayResetOnVisibility'),
730
+ sheet = createStyleSheet(null, getOption('nonce')),
731
+ lazyload = options.lazyload,
732
+ lazyloadSelector = options.lazyloadSelector,
733
+ slidePositions, // collection of slide positions
734
+ slideItemsOut = [],
735
+ cloneCount = loop ? getCloneCountForLoop() : 0,
736
+ slideCountNew = !carousel ? slideCount + cloneCount : slideCount + cloneCount * 2,
737
+ hasRightDeadZone = (fixedWidth || autoWidth) && !loop ? true : false,
738
+ rightBoundary = fixedWidth ? getRightBoundary() : null,
739
+ updateIndexBeforeTransform = (!carousel || !loop) ? true : false,
740
+ // transform
741
+ transformAttr = horizontal ? 'left' : 'top',
742
+ transformPrefix = '',
743
+ transformPostfix = '',
744
+ // index
745
+ getIndexMax = (function () {
746
+ if (fixedWidth) {
747
+ return function() { return center && !loop ? slideCount - 1 : Math.ceil(- rightBoundary / (fixedWidth + gutter)); };
748
+ } else if (autoWidth) {
749
+ return function() {
750
+ for (var i = 0; i < slideCountNew; i++) {
751
+ if (slidePositions[i] >= - rightBoundary) { return i; }
752
+ }
753
+ };
754
+ } else {
755
+ return function() {
756
+ if (center && carousel && !loop) {
757
+ return slideCount - 1;
758
+ } else {
759
+ return loop || carousel ? Math.max(0, slideCountNew - Math.ceil(items)) : slideCountNew - 1;
760
+ }
761
+ };
762
+ }
763
+ })(),
764
+ index = getStartIndex(getOption('startIndex')),
765
+ indexCached = index,
766
+ displayIndex = getCurrentSlide(),
767
+ indexMin = 0,
768
+ indexMax = !autoWidth ? getIndexMax() : null,
769
+ // resize
770
+ resizeTimer,
771
+ preventActionWhenRunning = options.preventActionWhenRunning,
772
+ swipeAngle = options.swipeAngle,
773
+ moveDirectionExpected = swipeAngle ? '?' : true,
774
+ running = false,
775
+ onInit = options.onInit,
776
+ events = new Events(),
777
+ // id, class
778
+ newContainerClasses = ' tns-slider tns-' + options.mode,
779
+ slideId = container.id || getSlideId(),
780
+ disable = getOption('disable'),
781
+ disabled = false,
782
+ freezable = options.freezable,
783
+ freeze = freezable && !autoWidth ? getFreeze() : false,
784
+ frozen = false,
785
+ controlsEvents = {
786
+ 'click': onControlsClick,
787
+ 'keydown': onControlsKeydown
788
+ },
789
+ navEvents = {
790
+ 'click': onNavClick,
791
+ 'keydown': onNavKeydown
792
+ },
793
+ hoverEvents = {
794
+ 'mouseover': mouseoverPause,
795
+ 'mouseout': mouseoutRestart
796
+ },
797
+ visibilityEvent = {'visibilitychange': onVisibilityChange},
798
+ docmentKeydownEvent = {'keydown': onDocumentKeydown},
799
+ touchEvents = {
800
+ 'touchstart': onPanStart,
801
+ 'touchmove': onPanMove,
802
+ 'touchend': onPanEnd,
803
+ 'touchcancel': onPanEnd
804
+ }, dragEvents = {
805
+ 'mousedown': onPanStart,
806
+ 'mousemove': onPanMove,
807
+ 'mouseup': onPanEnd,
808
+ 'mouseleave': onPanEnd
809
+ },
810
+ hasControls = hasOption('controls'),
811
+ hasNav = hasOption('nav'),
812
+ navAsThumbnails = autoWidth ? true : options.navAsThumbnails,
813
+ hasAutoplay = hasOption('autoplay'),
814
+ hasTouch = hasOption('touch'),
815
+ hasMouseDrag = hasOption('mouseDrag'),
816
+ slideActiveClass = 'tns-slide-active',
817
+ slideClonedClass = 'tns-slide-cloned',
818
+ imgCompleteClass = 'tns-complete',
819
+ imgEvents = {
820
+ 'load': onImgLoaded,
821
+ 'error': onImgFailed
822
+ },
823
+ imgsComplete,
824
+ liveregionCurrent,
825
+ preventScroll = options.preventScrollOnTouch === 'force' ? true : false;
826
+
827
+ // controls
828
+ if (hasControls) {
829
+ var controlsContainer = options.controlsContainer,
830
+ controlsContainerHTML = options.controlsContainer ? options.controlsContainer.outerHTML : '',
831
+ prevButton = options.prevButton,
832
+ nextButton = options.nextButton,
833
+ prevButtonHTML = options.prevButton ? options.prevButton.outerHTML : '',
834
+ nextButtonHTML = options.nextButton ? options.nextButton.outerHTML : '',
835
+ prevIsButton,
836
+ nextIsButton;
837
+ }
838
+
839
+ // nav
840
+ if (hasNav) {
841
+ var navContainer = options.navContainer,
842
+ navContainerHTML = options.navContainer ? options.navContainer.outerHTML : '',
843
+ navItems,
844
+ pages = autoWidth ? slideCount : getPages(),
845
+ pagesCached = 0,
846
+ navClicked = -1,
847
+ navCurrentIndex = getCurrentNavIndex(),
848
+ navCurrentIndexCached = navCurrentIndex,
849
+ navActiveClass = 'tns-nav-active',
850
+ navStr = 'Carousel Page ',
851
+ navStrCurrent = ' (Current Slide)';
852
+ }
853
+
854
+ // autoplay
855
+ if (hasAutoplay) {
856
+ var autoplayDirection = options.autoplayDirection === 'forward' ? 1 : -1,
857
+ autoplayButton = options.autoplayButton,
858
+ autoplayButtonHTML = options.autoplayButton ? options.autoplayButton.outerHTML : '',
859
+ autoplayHtmlStrings = ['<span class=\'tns-visually-hidden\'>', ' animation</span>'],
860
+ autoplayTimer,
861
+ animating,
862
+ autoplayHoverPaused,
863
+ autoplayUserPaused,
864
+ autoplayVisibilityPaused;
865
+ }
866
+
867
+ if (hasTouch || hasMouseDrag) {
868
+ var initPosition = {},
869
+ lastPosition = {},
870
+ translateInit,
871
+ disX,
872
+ disY,
873
+ panStart = false,
874
+ rafIndex,
875
+ getDist = horizontal ?
876
+ function(a, b) { return a.x - b.x; } :
877
+ function(a, b) { return a.y - b.y; };
878
+ }
879
+
880
+ // disable slider when slidecount <= items
881
+ if (!autoWidth) { resetVariblesWhenDisable(disable || freeze); }
882
+
883
+ if (TRANSFORM) {
884
+ transformAttr = TRANSFORM;
885
+ transformPrefix = 'translate';
886
+
887
+ if (HAS3DTRANSFORMS) {
888
+ transformPrefix += horizontal ? '3d(' : '3d(0px, ';
889
+ transformPostfix = horizontal ? ', 0px, 0px)' : ', 0px)';
890
+ } else {
891
+ transformPrefix += horizontal ? 'X(' : 'Y(';
892
+ transformPostfix = ')';
893
+ }
894
+
895
+ }
896
+
897
+ if (carousel) { container.className = container.className.replace('tns-vpfix', ''); }
898
+ initStructure();
899
+ initSheet();
900
+ initSliderTransform();
901
+
902
+ // === COMMON FUNCTIONS === //
903
+ function resetVariblesWhenDisable (condition) {
904
+ if (condition) {
905
+ controls = nav = touch = mouseDrag = arrowKeys = autoplay = autoplayHoverPause = autoplayResetOnVisibility = false;
906
+ }
907
+ }
908
+
909
+ function getCurrentSlide () {
910
+ var tem = carousel ? index - cloneCount : index;
911
+ while (tem < 0) { tem += slideCount; }
912
+ return tem%slideCount + 1;
913
+ }
914
+
915
+ function getStartIndex (ind) {
916
+ ind = ind ? Math.max(0, Math.min(loop ? slideCount - 1 : slideCount - items, ind)) : 0;
917
+ return carousel ? ind + cloneCount : ind;
918
+ }
919
+
920
+ function getAbsIndex (i) {
921
+ if (i == null) { i = index; }
922
+
923
+ if (carousel) { i -= cloneCount; }
924
+ while (i < 0) { i += slideCount; }
925
+
926
+ return Math.floor(i%slideCount);
927
+ }
928
+
929
+ function getCurrentNavIndex () {
930
+ var absIndex = getAbsIndex(),
931
+ result;
932
+
933
+ result = navAsThumbnails ? absIndex :
934
+ fixedWidth || autoWidth ? Math.ceil((absIndex + 1) * pages / slideCount - 1) :
935
+ Math.floor(absIndex / items);
936
+
937
+ // set active nav to the last one when reaches the right edge
938
+ if (!loop && carousel && index === indexMax) { result = pages - 1; }
939
+
940
+ return result;
941
+ }
942
+
943
+ function getItemsMax () {
944
+ // fixedWidth or autoWidth while viewportMax is not available
945
+ if (autoWidth || (fixedWidth && !viewportMax)) {
946
+ return slideCount - 1;
947
+ // most cases
948
+ } else {
949
+ var str = fixedWidth ? 'fixedWidth' : 'items',
950
+ arr = [];
951
+
952
+ if (fixedWidth || options[str] < slideCount) { arr.push(options[str]); }
953
+
954
+ if (responsive) {
955
+ for (var bp in responsive) {
956
+ var tem = responsive[bp][str];
957
+ if (tem && (fixedWidth || tem < slideCount)) { arr.push(tem); }
958
+ }
959
+ }
960
+
961
+ if (!arr.length) { arr.push(0); }
962
+
963
+ return Math.ceil(fixedWidth ? viewportMax / Math.min.apply(null, arr) : Math.max.apply(null, arr));
964
+ }
965
+ }
966
+
967
+ function getCloneCountForLoop () {
968
+ var itemsMax = getItemsMax(),
969
+ result = carousel ? Math.ceil((itemsMax * 5 - slideCount)/2) : (itemsMax * 4 - slideCount);
970
+ result = Math.max(itemsMax, result);
971
+
972
+ return hasOption('edgePadding') ? result + 1 : result;
973
+ }
974
+
975
+ function getWindowWidth () {
976
+ return win.innerWidth || doc.documentElement.clientWidth || doc.body.clientWidth;
977
+ }
978
+
979
+ function getInsertPosition (pos) {
980
+ return pos === 'top' ? 'afterbegin' : 'beforeend';
981
+ }
982
+
983
+ function getClientWidth (el) {
984
+ if (el == null) { return; }
985
+ var div = doc.createElement('div'), rect, width;
986
+ el.appendChild(div);
987
+ rect = div.getBoundingClientRect();
988
+ width = rect.right - rect.left;
989
+ div.remove();
990
+ return width || getClientWidth(el.parentNode);
991
+ }
992
+
993
+ function getViewportWidth () {
994
+ var gap = edgePadding ? edgePadding * 2 - gutter : 0;
995
+ return getClientWidth(containerParent) - gap;
996
+ }
997
+
998
+ function hasOption (item) {
999
+ if (options[item]) {
1000
+ return true;
1001
+ } else {
1002
+ if (responsive) {
1003
+ for (var bp in responsive) {
1004
+ if (responsive[bp][item]) { return true; }
1005
+ }
1006
+ }
1007
+ return false;
1008
+ }
1009
+ }
1010
+
1011
+ // get option:
1012
+ // fixed width: viewport, fixedWidth, gutter => items
1013
+ // others: window width => all variables
1014
+ // all: items => slideBy
1015
+ function getOption (item, ww) {
1016
+ if (ww == null) { ww = windowWidth; }
1017
+
1018
+ if (item === 'items' && fixedWidth) {
1019
+ return Math.floor((viewport + gutter) / (fixedWidth + gutter)) || 1;
1020
+
1021
+ } else {
1022
+ var result = options[item];
1023
+
1024
+ if (responsive) {
1025
+ for (var bp in responsive) {
1026
+ // bp: convert string to number
1027
+ if (ww >= parseInt(bp)) {
1028
+ if (item in responsive[bp]) { result = responsive[bp][item]; }
1029
+ }
1030
+ }
1031
+ }
1032
+
1033
+ if (item === 'slideBy' && result === 'page') { result = getOption('items'); }
1034
+ if (!carousel && (item === 'slideBy' || item === 'items')) { result = Math.floor(result); }
1035
+
1036
+ return result;
1037
+ }
1038
+ }
1039
+
1040
+ function getSlideMarginLeft (i) {
1041
+ return CALC ?
1042
+ CALC + '(' + i * 100 + '% / ' + slideCountNew + ')' :
1043
+ i * 100 / slideCountNew + '%';
1044
+ }
1045
+
1046
+ function getInnerWrapperStyles (edgePaddingTem, gutterTem, fixedWidthTem, speedTem, autoHeightBP) {
1047
+ var str = '';
1048
+
1049
+ if (edgePaddingTem !== undefined) {
1050
+ var gap = edgePaddingTem;
1051
+ if (gutterTem) { gap -= gutterTem; }
1052
+ str = horizontal ?
1053
+ 'margin: 0 ' + gap + 'px 0 ' + edgePaddingTem + 'px;' :
1054
+ 'margin: ' + edgePaddingTem + 'px 0 ' + gap + 'px 0;';
1055
+ } else if (gutterTem && !fixedWidthTem) {
1056
+ var gutterTemUnit = '-' + gutterTem + 'px',
1057
+ dir = horizontal ? gutterTemUnit + ' 0 0' : '0 ' + gutterTemUnit + ' 0';
1058
+ str = 'margin: 0 ' + dir + ';';
1059
+ }
1060
+
1061
+ if (!carousel && autoHeightBP && TRANSITIONDURATION && speedTem) { str += getTransitionDurationStyle(speedTem); }
1062
+ return str;
1063
+ }
1064
+
1065
+ function getContainerWidth (fixedWidthTem, gutterTem, itemsTem) {
1066
+ if (fixedWidthTem) {
1067
+ return (fixedWidthTem + gutterTem) * slideCountNew + 'px';
1068
+ } else {
1069
+ return CALC ?
1070
+ CALC + '(' + slideCountNew * 100 + '% / ' + itemsTem + ')' :
1071
+ slideCountNew * 100 / itemsTem + '%';
1072
+ }
1073
+ }
1074
+
1075
+ function getSlideWidthStyle (fixedWidthTem, gutterTem, itemsTem) {
1076
+ var width;
1077
+
1078
+ if (fixedWidthTem) {
1079
+ width = (fixedWidthTem + gutterTem) + 'px';
1080
+ } else {
1081
+ if (!carousel) { itemsTem = Math.floor(itemsTem); }
1082
+ var dividend = carousel ? slideCountNew : itemsTem;
1083
+ width = CALC ?
1084
+ CALC + '(100% / ' + dividend + ')' :
1085
+ 100 / dividend + '%';
1086
+ }
1087
+
1088
+ width = 'width:' + width;
1089
+
1090
+ // inner slider: overwrite outer slider styles
1091
+ return nested !== 'inner' ? width + ';' : width + ' !important;';
1092
+ }
1093
+
1094
+ function getSlideGutterStyle (gutterTem) {
1095
+ var str = '';
1096
+
1097
+ // gutter maybe interger || 0
1098
+ // so can't use 'if (gutter)'
1099
+ if (gutterTem !== false) {
1100
+ var prop = horizontal ? 'padding-' : 'margin-',
1101
+ dir = horizontal ? 'right' : 'bottom';
1102
+ str = prop + dir + ': ' + gutterTem + 'px;';
1103
+ }
1104
+
1105
+ return str;
1106
+ }
1107
+
1108
+ function getCSSPrefix (name, num) {
1109
+ var prefix = name.substring(0, name.length - num).toLowerCase();
1110
+ if (prefix) { prefix = '-' + prefix + '-'; }
1111
+
1112
+ return prefix;
1113
+ }
1114
+
1115
+ function getTransitionDurationStyle (speed) {
1116
+ return getCSSPrefix(TRANSITIONDURATION, 18) + 'transition-duration:' + speed / 1000 + 's;';
1117
+ }
1118
+
1119
+ function getAnimationDurationStyle (speed) {
1120
+ return getCSSPrefix(ANIMATIONDURATION, 17) + 'animation-duration:' + speed / 1000 + 's;';
1121
+ }
1122
+
1123
+ function initStructure () {
1124
+ var classOuter = 'tns-outer',
1125
+ classInner = 'tns-inner',
1126
+ hasGutter = hasOption('gutter');
1127
+
1128
+ outerWrapper.className = classOuter;
1129
+ innerWrapper.className = classInner;
1130
+ outerWrapper.id = slideId + '-ow';
1131
+ innerWrapper.id = slideId + '-iw';
1132
+
1133
+ // set container properties
1134
+ if (container.id === '') { container.id = slideId; }
1135
+ newContainerClasses += PERCENTAGELAYOUT || autoWidth ? ' tns-subpixel' : ' tns-no-subpixel';
1136
+ newContainerClasses += CALC ? ' tns-calc' : ' tns-no-calc';
1137
+ if (autoWidth) { newContainerClasses += ' tns-autowidth'; }
1138
+ newContainerClasses += ' tns-' + options.axis;
1139
+ container.className += newContainerClasses;
1140
+
1141
+ // add constrain layer for carousel
1142
+ if (carousel) {
1143
+ middleWrapper = doc.createElement('div');
1144
+ middleWrapper.id = slideId + '-mw';
1145
+ middleWrapper.className = 'tns-ovh';
1146
+
1147
+ outerWrapper.appendChild(middleWrapper);
1148
+ middleWrapper.appendChild(innerWrapper);
1149
+ } else {
1150
+ outerWrapper.appendChild(innerWrapper);
1151
+ }
1152
+
1153
+ if (autoHeight) {
1154
+ var wp = middleWrapper ? middleWrapper : innerWrapper;
1155
+ wp.className += ' tns-ah';
1156
+ }
1157
+
1158
+ containerParent.insertBefore(outerWrapper, container);
1159
+ innerWrapper.appendChild(container);
1160
+
1161
+ // add id, class, aria attributes
1162
+ // before clone slides
1163
+ forEach(slideItems, function(item, i) {
1164
+ addClass(item, 'tns-item');
1165
+ if (!item.id) { item.id = slideId + '-item' + i; }
1166
+ if (!carousel && animateNormal) { addClass(item, animateNormal); }
1167
+ setAttrs(item, {
1168
+ 'aria-hidden': 'true',
1169
+ 'tabindex': '-1'
1170
+ });
1171
+ });
1172
+
1173
+ // ## clone slides
1174
+ // carousel: n + slides + n
1175
+ // gallery: slides + n
1176
+ if (cloneCount) {
1177
+ var fragmentBefore = doc.createDocumentFragment(),
1178
+ fragmentAfter = doc.createDocumentFragment();
1179
+
1180
+ for (var j = cloneCount; j--;) {
1181
+ var num = j%slideCount,
1182
+ cloneFirst = slideItems[num].cloneNode(true);
1183
+ addClass(cloneFirst, slideClonedClass);
1184
+ removeAttrs(cloneFirst, 'id');
1185
+ fragmentAfter.insertBefore(cloneFirst, fragmentAfter.firstChild);
1186
+
1187
+ if (carousel) {
1188
+ var cloneLast = slideItems[slideCount - 1 - num].cloneNode(true);
1189
+ addClass(cloneLast, slideClonedClass);
1190
+ removeAttrs(cloneLast, 'id');
1191
+ fragmentBefore.appendChild(cloneLast);
1192
+ }
1193
+ }
1194
+
1195
+ container.insertBefore(fragmentBefore, container.firstChild);
1196
+ container.appendChild(fragmentAfter);
1197
+ slideItems = container.children;
1198
+ }
1199
+
1200
+ }
1201
+
1202
+ function initSliderTransform () {
1203
+ // ## images loaded/failed
1204
+ if (hasOption('autoHeight') || autoWidth || !horizontal) {
1205
+ var imgs = container.querySelectorAll('img');
1206
+
1207
+ // add img load event listener
1208
+ forEach(imgs, function(img) {
1209
+ var src = img.src;
1210
+
1211
+ if (!lazyload) {
1212
+ // not data img
1213
+ if (src && src.indexOf('data:image') < 0) {
1214
+ img.src = '';
1215
+ addEvents(img, imgEvents);
1216
+ addClass(img, 'loading');
1217
+
1218
+ img.src = src;
1219
+ // data img
1220
+ } else {
1221
+ imgLoaded(img);
1222
+ }
1223
+ }
1224
+ });
1225
+
1226
+ // set imgsComplete
1227
+ raf(function(){ imgsLoadedCheck(arrayFromNodeList(imgs), function() { imgsComplete = true; }); });
1228
+
1229
+ // reset imgs for auto height: check visible imgs only
1230
+ if (hasOption('autoHeight')) { imgs = getImageArray(index, Math.min(index + items - 1, slideCountNew - 1)); }
1231
+
1232
+ lazyload ? initSliderTransformStyleCheck() : raf(function(){ imgsLoadedCheck(arrayFromNodeList(imgs), initSliderTransformStyleCheck); });
1233
+
1234
+ } else {
1235
+ // set container transform property
1236
+ if (carousel) { doContainerTransformSilent(); }
1237
+
1238
+ // update slider tools and events
1239
+ initTools();
1240
+ initEvents();
1241
+ }
1242
+ }
1243
+
1244
+ function initSliderTransformStyleCheck () {
1245
+ if (autoWidth && slideCount > 1) {
1246
+ // check styles application
1247
+ var num = loop ? index : slideCount - 1;
1248
+
1249
+ (function stylesApplicationCheck() {
1250
+ var left = slideItems[num].getBoundingClientRect().left;
1251
+ var right = slideItems[num - 1].getBoundingClientRect().right;
1252
+
1253
+ (Math.abs(left - right) <= 1) ?
1254
+ initSliderTransformCore() :
1255
+ setTimeout(function(){ stylesApplicationCheck(); }, 16);
1256
+ })();
1257
+
1258
+ } else {
1259
+ initSliderTransformCore();
1260
+ }
1261
+ }
1262
+
1263
+
1264
+ function initSliderTransformCore () {
1265
+ // run Fn()s which are rely on image loading
1266
+ if (!horizontal || autoWidth) {
1267
+ setSlidePositions();
1268
+
1269
+ if (autoWidth) {
1270
+ rightBoundary = getRightBoundary();
1271
+ if (freezable) { freeze = getFreeze(); }
1272
+ indexMax = getIndexMax(); // <= slidePositions, rightBoundary <=
1273
+ resetVariblesWhenDisable(disable || freeze);
1274
+ } else {
1275
+ updateContentWrapperHeight();
1276
+ }
1277
+ }
1278
+
1279
+ // set container transform property
1280
+ if (carousel) { doContainerTransformSilent(); }
1281
+
1282
+ // update slider tools and events
1283
+ initTools();
1284
+ initEvents();
1285
+ }
1286
+
1287
+ function initSheet () {
1288
+ // gallery:
1289
+ // set animation classes and left value for gallery slider
1290
+ if (!carousel) {
1291
+ for (var i = index, l = index + Math.min(slideCount, items); i < l; i++) {
1292
+ var item = slideItems[i];
1293
+ item.style.left = (i - index) * 100 / items + '%';
1294
+ addClass(item, animateIn);
1295
+ removeClass(item, animateNormal);
1296
+ }
1297
+ }
1298
+
1299
+ // #### LAYOUT
1300
+
1301
+ // ## INLINE-BLOCK VS FLOAT
1302
+
1303
+ // ## PercentageLayout:
1304
+ // slides: inline-block
1305
+ // remove blank space between slides by set font-size: 0
1306
+
1307
+ // ## Non PercentageLayout:
1308
+ // slides: float
1309
+ // margin-right: -100%
1310
+ // margin-left: ~
1311
+
1312
+ // Resource: https://docs.google.com/spreadsheets/d/147up245wwTXeQYve3BRSAD4oVcvQmuGsFteJOeA5xNQ/edit?usp=sharing
1313
+ if (horizontal) {
1314
+ if (PERCENTAGELAYOUT || autoWidth) {
1315
+ addCSSRule(sheet, '#' + slideId + ' > .tns-item', 'font-size:' + win.getComputedStyle(slideItems[0]).fontSize + ';', getCssRulesLength(sheet));
1316
+ addCSSRule(sheet, '#' + slideId, 'font-size:0;', getCssRulesLength(sheet));
1317
+ } else if (carousel) {
1318
+ forEach(slideItems, function (slide, i) {
1319
+ slide.style.marginLeft = getSlideMarginLeft(i);
1320
+ });
1321
+ }
1322
+ }
1323
+
1324
+
1325
+ // ## BASIC STYLES
1326
+ if (CSSMQ) {
1327
+ // middle wrapper style
1328
+ if (TRANSITIONDURATION) {
1329
+ var str = middleWrapper && options.autoHeight ? getTransitionDurationStyle(options.speed) : '';
1330
+ addCSSRule(sheet, '#' + slideId + '-mw', str, getCssRulesLength(sheet));
1331
+ }
1332
+
1333
+ // inner wrapper styles
1334
+ str = getInnerWrapperStyles(options.edgePadding, options.gutter, options.fixedWidth, options.speed, options.autoHeight);
1335
+ addCSSRule(sheet, '#' + slideId + '-iw', str, getCssRulesLength(sheet));
1336
+
1337
+ // container styles
1338
+ if (carousel) {
1339
+ str = horizontal && !autoWidth ? 'width:' + getContainerWidth(options.fixedWidth, options.gutter, options.items) + ';' : '';
1340
+ if (TRANSITIONDURATION) { str += getTransitionDurationStyle(speed); }
1341
+ addCSSRule(sheet, '#' + slideId, str, getCssRulesLength(sheet));
1342
+ }
1343
+
1344
+ // slide styles
1345
+ str = horizontal && !autoWidth ? getSlideWidthStyle(options.fixedWidth, options.gutter, options.items) : '';
1346
+ if (options.gutter) { str += getSlideGutterStyle(options.gutter); }
1347
+ // set gallery items transition-duration
1348
+ if (!carousel) {
1349
+ if (TRANSITIONDURATION) { str += getTransitionDurationStyle(speed); }
1350
+ if (ANIMATIONDURATION) { str += getAnimationDurationStyle(speed); }
1351
+ }
1352
+ if (str) { addCSSRule(sheet, '#' + slideId + ' > .tns-item', str, getCssRulesLength(sheet)); }
1353
+
1354
+ // non CSS mediaqueries: IE8
1355
+ // ## update inner wrapper, container, slides if needed
1356
+ // set inline styles for inner wrapper & container
1357
+ // insert stylesheet (one line) for slides only (since slides are many)
1358
+ } else {
1359
+ // middle wrapper styles
1360
+ update_carousel_transition_duration();
1361
+
1362
+ // inner wrapper styles
1363
+ innerWrapper.style.cssText = getInnerWrapperStyles(edgePadding, gutter, fixedWidth, autoHeight);
1364
+
1365
+ // container styles
1366
+ if (carousel && horizontal && !autoWidth) {
1367
+ container.style.width = getContainerWidth(fixedWidth, gutter, items);
1368
+ }
1369
+
1370
+ // slide styles
1371
+ var str = horizontal && !autoWidth ? getSlideWidthStyle(fixedWidth, gutter, items) : '';
1372
+ if (gutter) { str += getSlideGutterStyle(gutter); }
1373
+
1374
+ // append to the last line
1375
+ if (str) { addCSSRule(sheet, '#' + slideId + ' > .tns-item', str, getCssRulesLength(sheet)); }
1376
+ }
1377
+
1378
+ // ## MEDIAQUERIES
1379
+ if (responsive && CSSMQ) {
1380
+ for (var bp in responsive) {
1381
+ // bp: convert string to number
1382
+ bp = parseInt(bp);
1383
+
1384
+ var opts = responsive[bp],
1385
+ str = '',
1386
+ middleWrapperStr = '',
1387
+ innerWrapperStr = '',
1388
+ containerStr = '',
1389
+ slideStr = '',
1390
+ itemsBP = !autoWidth ? getOption('items', bp) : null,
1391
+ fixedWidthBP = getOption('fixedWidth', bp),
1392
+ speedBP = getOption('speed', bp),
1393
+ edgePaddingBP = getOption('edgePadding', bp),
1394
+ autoHeightBP = getOption('autoHeight', bp),
1395
+ gutterBP = getOption('gutter', bp);
1396
+
1397
+ // middle wrapper string
1398
+ if (TRANSITIONDURATION && middleWrapper && getOption('autoHeight', bp) && 'speed' in opts) {
1399
+ middleWrapperStr = '#' + slideId + '-mw{' + getTransitionDurationStyle(speedBP) + '}';
1400
+ }
1401
+
1402
+ // inner wrapper string
1403
+ if ('edgePadding' in opts || 'gutter' in opts) {
1404
+ innerWrapperStr = '#' + slideId + '-iw{' + getInnerWrapperStyles(edgePaddingBP, gutterBP, fixedWidthBP, speedBP, autoHeightBP) + '}';
1405
+ }
1406
+
1407
+ // container string
1408
+ if (carousel && horizontal && !autoWidth && ('fixedWidth' in opts || 'items' in opts || (fixedWidth && 'gutter' in opts))) {
1409
+ containerStr = 'width:' + getContainerWidth(fixedWidthBP, gutterBP, itemsBP) + ';';
1410
+ }
1411
+ if (TRANSITIONDURATION && 'speed' in opts) {
1412
+ containerStr += getTransitionDurationStyle(speedBP);
1413
+ }
1414
+ if (containerStr) {
1415
+ containerStr = '#' + slideId + '{' + containerStr + '}';
1416
+ }
1417
+
1418
+ // slide string
1419
+ if ('fixedWidth' in opts || (fixedWidth && 'gutter' in opts) || !carousel && 'items' in opts) {
1420
+ slideStr += getSlideWidthStyle(fixedWidthBP, gutterBP, itemsBP);
1421
+ }
1422
+ if ('gutter' in opts) {
1423
+ slideStr += getSlideGutterStyle(gutterBP);
1424
+ }
1425
+ // set gallery items transition-duration
1426
+ if (!carousel && 'speed' in opts) {
1427
+ if (TRANSITIONDURATION) { slideStr += getTransitionDurationStyle(speedBP); }
1428
+ if (ANIMATIONDURATION) { slideStr += getAnimationDurationStyle(speedBP); }
1429
+ }
1430
+ if (slideStr) { slideStr = '#' + slideId + ' > .tns-item{' + slideStr + '}'; }
1431
+
1432
+ // add up
1433
+ str = middleWrapperStr + innerWrapperStr + containerStr + slideStr;
1434
+
1435
+ if (str) {
1436
+ sheet.insertRule('@media (min-width: ' + bp / 16 + 'em) {' + str + '}', sheet.cssRules.length);
1437
+ }
1438
+ }
1439
+ }
1440
+ }
1441
+
1442
+ function initTools () {
1443
+ // == slides ==
1444
+ updateSlideStatus();
1445
+
1446
+ // == live region ==
1447
+ outerWrapper.insertAdjacentHTML('afterbegin', '<div class="tns-liveregion tns-visually-hidden" aria-live="polite" aria-atomic="true">slide <span class="current">' + getLiveRegionStr() + '</span> of ' + slideCount + '</div>');
1448
+ liveregionCurrent = outerWrapper.querySelector('.tns-liveregion .current');
1449
+
1450
+ // == autoplayInit ==
1451
+ if (hasAutoplay) {
1452
+ var txt = autoplay ? 'stop' : 'start';
1453
+ if (autoplayButton) {
1454
+ setAttrs(autoplayButton, {'data-action': txt});
1455
+ } else if (options.autoplayButtonOutput) {
1456
+ outerWrapper.insertAdjacentHTML(getInsertPosition(options.autoplayPosition), '<button type="button" data-action="' + txt + '">' + autoplayHtmlStrings[0] + txt + autoplayHtmlStrings[1] + autoplayText[0] + '</button>');
1457
+ autoplayButton = outerWrapper.querySelector('[data-action]');
1458
+ }
1459
+
1460
+ // add event
1461
+ if (autoplayButton) {
1462
+ addEvents(autoplayButton, {'click': toggleAutoplay});
1463
+ }
1464
+
1465
+ if (autoplay) {
1466
+ startAutoplay();
1467
+ if (autoplayHoverPause) { addEvents(container, hoverEvents); }
1468
+ if (autoplayResetOnVisibility) { addEvents(container, visibilityEvent); }
1469
+ }
1470
+ }
1471
+
1472
+ // == navInit ==
1473
+ if (hasNav) {
1474
+ var initIndex = !carousel ? 0 : cloneCount;
1475
+ // customized nav
1476
+ // will not hide the navs in case they're thumbnails
1477
+ if (navContainer) {
1478
+ setAttrs(navContainer, {'aria-label': 'Carousel Pagination'});
1479
+ navItems = navContainer.children;
1480
+ forEach(navItems, function(item, i) {
1481
+ setAttrs(item, {
1482
+ 'data-nav': i,
1483
+ 'tabindex': '-1',
1484
+ 'aria-label': navStr + (i + 1),
1485
+ 'aria-controls': slideId,
1486
+ });
1487
+ });
1488
+
1489
+ // generated nav
1490
+ } else {
1491
+ var navHtml = '',
1492
+ hiddenStr = navAsThumbnails ? '' : 'style="display:none"';
1493
+ for (var i = 0; i < slideCount; i++) {
1494
+ // hide nav items by default
1495
+ navHtml += '<button type="button" data-nav="' + i +'" tabindex="-1" aria-controls="' + slideId + '" ' + hiddenStr + ' aria-label="' + navStr + (i + 1) +'"></button>';
1496
+ }
1497
+ navHtml = '<div class="tns-nav" aria-label="Carousel Pagination">' + navHtml + '</div>';
1498
+ outerWrapper.insertAdjacentHTML(getInsertPosition(options.navPosition), navHtml);
1499
+
1500
+ navContainer = outerWrapper.querySelector('.tns-nav');
1501
+ navItems = navContainer.children;
1502
+ }
1503
+
1504
+ updateNavVisibility();
1505
+
1506
+ // add transition
1507
+ if (TRANSITIONDURATION) {
1508
+ var prefix = TRANSITIONDURATION.substring(0, TRANSITIONDURATION.length - 18).toLowerCase(),
1509
+ str = 'transition: all ' + speed / 1000 + 's';
1510
+
1511
+ if (prefix) {
1512
+ str = '-' + prefix + '-' + str;
1513
+ }
1514
+
1515
+ addCSSRule(sheet, '[aria-controls^=' + slideId + '-item]', str, getCssRulesLength(sheet));
1516
+ }
1517
+
1518
+ setAttrs(navItems[navCurrentIndex], {'aria-label': navStr + (navCurrentIndex + 1) + navStrCurrent});
1519
+ removeAttrs(navItems[navCurrentIndex], 'tabindex');
1520
+ addClass(navItems[navCurrentIndex], navActiveClass);
1521
+
1522
+ // add events
1523
+ addEvents(navContainer, navEvents);
1524
+ }
1525
+
1526
+
1527
+
1528
+ // == controlsInit ==
1529
+ if (hasControls) {
1530
+ if (!controlsContainer && (!prevButton || !nextButton)) {
1531
+ outerWrapper.insertAdjacentHTML(getInsertPosition(options.controlsPosition), '<div class="tns-controls" aria-label="Carousel Navigation" tabindex="0"><button type="button" data-controls="prev" tabindex="-1" aria-controls="' + slideId +'">' + controlsText[0] + '</button><button type="button" data-controls="next" tabindex="-1" aria-controls="' + slideId +'">' + controlsText[1] + '</button></div>');
1532
+
1533
+ controlsContainer = outerWrapper.querySelector('.tns-controls');
1534
+ }
1535
+
1536
+ if (!prevButton || !nextButton) {
1537
+ prevButton = controlsContainer.children[0];
1538
+ nextButton = controlsContainer.children[1];
1539
+ }
1540
+
1541
+ if (options.controlsContainer) {
1542
+ setAttrs(controlsContainer, {
1543
+ 'aria-label': 'Carousel Navigation',
1544
+ 'tabindex': '0'
1545
+ });
1546
+ }
1547
+
1548
+ if (options.controlsContainer || (options.prevButton && options.nextButton)) {
1549
+ setAttrs([prevButton, nextButton], {
1550
+ 'aria-controls': slideId,
1551
+ 'tabindex': '-1',
1552
+ });
1553
+ }
1554
+
1555
+ if (options.controlsContainer || (options.prevButton && options.nextButton)) {
1556
+ setAttrs(prevButton, {'data-controls' : 'prev'});
1557
+ setAttrs(nextButton, {'data-controls' : 'next'});
1558
+ }
1559
+
1560
+ prevIsButton = isButton(prevButton);
1561
+ nextIsButton = isButton(nextButton);
1562
+
1563
+ updateControlsStatus();
1564
+
1565
+ // add events
1566
+ if (controlsContainer) {
1567
+ addEvents(controlsContainer, controlsEvents);
1568
+ } else {
1569
+ addEvents(prevButton, controlsEvents);
1570
+ addEvents(nextButton, controlsEvents);
1571
+ }
1572
+ }
1573
+
1574
+ // hide tools if needed
1575
+ disableUI();
1576
+ }
1577
+
1578
+ function initEvents () {
1579
+ // add events
1580
+ if (carousel && TRANSITIONEND) {
1581
+ var eve = {};
1582
+ eve[TRANSITIONEND] = onTransitionEnd;
1583
+ addEvents(container, eve);
1584
+ }
1585
+
1586
+ if (touch) { addEvents(container, touchEvents, options.preventScrollOnTouch); }
1587
+ if (mouseDrag) { addEvents(container, dragEvents); }
1588
+ if (arrowKeys) { addEvents(doc, docmentKeydownEvent); }
1589
+
1590
+ if (nested === 'inner') {
1591
+ events.on('outerResized', function () {
1592
+ resizeTasks();
1593
+ events.emit('innerLoaded', info());
1594
+ });
1595
+ } else if (responsive || fixedWidth || autoWidth || autoHeight || !horizontal) {
1596
+ addEvents(win, {'resize': onResize});
1597
+ }
1598
+
1599
+ if (autoHeight) {
1600
+ if (nested === 'outer') {
1601
+ events.on('innerLoaded', doAutoHeight);
1602
+ } else if (!disable) { doAutoHeight(); }
1603
+ }
1604
+
1605
+ doLazyLoad();
1606
+ if (disable) { disableSlider(); } else if (freeze) { freezeSlider(); }
1607
+
1608
+ events.on('indexChanged', additionalUpdates);
1609
+ if (nested === 'inner') { events.emit('innerLoaded', info()); }
1610
+ if (typeof onInit === 'function') { onInit(info()); }
1611
+ isOn = true;
1612
+ }
1613
+
1614
+ function destroy () {
1615
+ // sheet
1616
+ sheet.disabled = true;
1617
+ if (sheet.ownerNode) { sheet.ownerNode.remove(); }
1618
+
1619
+ // remove win event listeners
1620
+ removeEvents(win, {'resize': onResize});
1621
+
1622
+ // arrowKeys, controls, nav
1623
+ if (arrowKeys) { removeEvents(doc, docmentKeydownEvent); }
1624
+ if (controlsContainer) { removeEvents(controlsContainer, controlsEvents); }
1625
+ if (navContainer) { removeEvents(navContainer, navEvents); }
1626
+
1627
+ // autoplay
1628
+ removeEvents(container, hoverEvents);
1629
+ removeEvents(container, visibilityEvent);
1630
+ if (autoplayButton) { removeEvents(autoplayButton, {'click': toggleAutoplay}); }
1631
+ if (autoplay) { clearInterval(autoplayTimer); }
1632
+
1633
+ // container
1634
+ if (carousel && TRANSITIONEND) {
1635
+ var eve = {};
1636
+ eve[TRANSITIONEND] = onTransitionEnd;
1637
+ removeEvents(container, eve);
1638
+ }
1639
+ if (touch) { removeEvents(container, touchEvents); }
1640
+ if (mouseDrag) { removeEvents(container, dragEvents); }
1641
+
1642
+ // cache Object values in options && reset HTML
1643
+ var htmlList = [containerHTML, controlsContainerHTML, prevButtonHTML, nextButtonHTML, navContainerHTML, autoplayButtonHTML];
1644
+
1645
+ tnsList.forEach(function(item, i) {
1646
+ var el = item === 'container' ? outerWrapper : options[item];
1647
+
1648
+ if (typeof el === 'object' && el) {
1649
+ var prevEl = el.previousElementSibling ? el.previousElementSibling : false,
1650
+ parentEl = el.parentNode;
1651
+ el.outerHTML = htmlList[i];
1652
+ options[item] = prevEl ? prevEl.nextElementSibling : parentEl.firstElementChild;
1653
+ }
1654
+ });
1655
+
1656
+
1657
+ // reset variables
1658
+ tnsList = animateIn = animateOut = animateDelay = animateNormal = horizontal = outerWrapper = innerWrapper = container = containerParent = containerHTML = slideItems = slideCount = breakpointZone = windowWidth = autoWidth = fixedWidth = edgePadding = gutter = viewport = items = slideBy = viewportMax = arrowKeys = speed = rewind = loop = autoHeight = sheet = lazyload = slidePositions = slideItemsOut = cloneCount = slideCountNew = hasRightDeadZone = rightBoundary = updateIndexBeforeTransform = transformAttr = transformPrefix = transformPostfix = getIndexMax = index = indexCached = indexMin = indexMax = resizeTimer = swipeAngle = moveDirectionExpected = running = onInit = events = newContainerClasses = slideId = disable = disabled = freezable = freeze = frozen = controlsEvents = navEvents = hoverEvents = visibilityEvent = docmentKeydownEvent = touchEvents = dragEvents = hasControls = hasNav = navAsThumbnails = hasAutoplay = hasTouch = hasMouseDrag = slideActiveClass = imgCompleteClass = imgEvents = imgsComplete = controls = controlsText = controlsContainer = controlsContainerHTML = prevButton = nextButton = prevIsButton = nextIsButton = nav = navContainer = navContainerHTML = navItems = pages = pagesCached = navClicked = navCurrentIndex = navCurrentIndexCached = navActiveClass = navStr = navStrCurrent = autoplay = autoplayTimeout = autoplayDirection = autoplayText = autoplayHoverPause = autoplayButton = autoplayButtonHTML = autoplayResetOnVisibility = autoplayHtmlStrings = autoplayTimer = animating = autoplayHoverPaused = autoplayUserPaused = autoplayVisibilityPaused = initPosition = lastPosition = translateInit = disX = disY = panStart = rafIndex = getDist = touch = mouseDrag = null;
1659
+ // check variables
1660
+ // [animateIn, animateOut, animateDelay, animateNormal, horizontal, outerWrapper, innerWrapper, container, containerParent, containerHTML, slideItems, slideCount, breakpointZone, windowWidth, autoWidth, fixedWidth, edgePadding, gutter, viewport, items, slideBy, viewportMax, arrowKeys, speed, rewind, loop, autoHeight, sheet, lazyload, slidePositions, slideItemsOut, cloneCount, slideCountNew, hasRightDeadZone, rightBoundary, updateIndexBeforeTransform, transformAttr, transformPrefix, transformPostfix, getIndexMax, index, indexCached, indexMin, indexMax, resizeTimer, swipeAngle, moveDirectionExpected, running, onInit, events, newContainerClasses, slideId, disable, disabled, freezable, freeze, frozen, controlsEvents, navEvents, hoverEvents, visibilityEvent, docmentKeydownEvent, touchEvents, dragEvents, hasControls, hasNav, navAsThumbnails, hasAutoplay, hasTouch, hasMouseDrag, slideActiveClass, imgCompleteClass, imgEvents, imgsComplete, controls, controlsText, controlsContainer, controlsContainerHTML, prevButton, nextButton, prevIsButton, nextIsButton, nav, navContainer, navContainerHTML, navItems, pages, pagesCached, navClicked, navCurrentIndex, navCurrentIndexCached, navActiveClass, navStr, navStrCurrent, autoplay, autoplayTimeout, autoplayDirection, autoplayText, autoplayHoverPause, autoplayButton, autoplayButtonHTML, autoplayResetOnVisibility, autoplayHtmlStrings, autoplayTimer, animating, autoplayHoverPaused, autoplayUserPaused, autoplayVisibilityPaused, initPosition, lastPosition, translateInit, disX, disY, panStart, rafIndex, getDist, touch, mouseDrag ].forEach(function(item) { if (item !== null) { console.log(item); } });
1661
+
1662
+ for (var a in this) {
1663
+ if (a !== 'rebuild') { this[a] = null; }
1664
+ }
1665
+ isOn = false;
1666
+ }
1667
+
1668
+ // === ON RESIZE ===
1669
+ // responsive || fixedWidth || autoWidth || !horizontal
1670
+ function onResize (e) {
1671
+ raf(function(){ resizeTasks(getEvent(e)); });
1672
+ }
1673
+
1674
+ function resizeTasks (e) {
1675
+ if (!isOn) { return; }
1676
+ if (nested === 'outer') { events.emit('outerResized', info(e)); }
1677
+ windowWidth = getWindowWidth();
1678
+ var bpChanged,
1679
+ breakpointZoneTem = breakpointZone,
1680
+ needContainerTransform = false;
1681
+
1682
+ if (responsive) {
1683
+ setBreakpointZone();
1684
+ bpChanged = breakpointZoneTem !== breakpointZone;
1685
+ // if (hasRightDeadZone) { needContainerTransform = true; } // *?
1686
+ if (bpChanged) { events.emit('newBreakpointStart', info(e)); }
1687
+ }
1688
+
1689
+ var indChanged,
1690
+ itemsChanged,
1691
+ itemsTem = items,
1692
+ disableTem = disable,
1693
+ freezeTem = freeze,
1694
+ arrowKeysTem = arrowKeys,
1695
+ controlsTem = controls,
1696
+ navTem = nav,
1697
+ touchTem = touch,
1698
+ mouseDragTem = mouseDrag,
1699
+ autoplayTem = autoplay,
1700
+ autoplayHoverPauseTem = autoplayHoverPause,
1701
+ autoplayResetOnVisibilityTem = autoplayResetOnVisibility,
1702
+ indexTem = index;
1703
+
1704
+ if (bpChanged) {
1705
+ var fixedWidthTem = fixedWidth,
1706
+ autoHeightTem = autoHeight,
1707
+ controlsTextTem = controlsText,
1708
+ centerTem = center,
1709
+ autoplayTextTem = autoplayText;
1710
+
1711
+ if (!CSSMQ) {
1712
+ var gutterTem = gutter,
1713
+ edgePaddingTem = edgePadding;
1714
+ }
1715
+ }
1716
+
1717
+ // get option:
1718
+ // fixed width: viewport, fixedWidth, gutter => items
1719
+ // others: window width => all variables
1720
+ // all: items => slideBy
1721
+ arrowKeys = getOption('arrowKeys');
1722
+ controls = getOption('controls');
1723
+ nav = getOption('nav');
1724
+ touch = getOption('touch');
1725
+ center = getOption('center');
1726
+ mouseDrag = getOption('mouseDrag');
1727
+ autoplay = getOption('autoplay');
1728
+ autoplayHoverPause = getOption('autoplayHoverPause');
1729
+ autoplayResetOnVisibility = getOption('autoplayResetOnVisibility');
1730
+
1731
+ if (bpChanged) {
1732
+ disable = getOption('disable');
1733
+ fixedWidth = getOption('fixedWidth');
1734
+ speed = getOption('speed');
1735
+ autoHeight = getOption('autoHeight');
1736
+ controlsText = getOption('controlsText');
1737
+ autoplayText = getOption('autoplayText');
1738
+ autoplayTimeout = getOption('autoplayTimeout');
1739
+
1740
+ if (!CSSMQ) {
1741
+ edgePadding = getOption('edgePadding');
1742
+ gutter = getOption('gutter');
1743
+ }
1744
+ }
1745
+ // update options
1746
+ resetVariblesWhenDisable(disable);
1747
+
1748
+ viewport = getViewportWidth(); // <= edgePadding, gutter
1749
+ if ((!horizontal || autoWidth) && !disable) {
1750
+ setSlidePositions();
1751
+ if (!horizontal) {
1752
+ updateContentWrapperHeight(); // <= setSlidePositions
1753
+ needContainerTransform = true;
1754
+ }
1755
+ }
1756
+ if (fixedWidth || autoWidth) {
1757
+ rightBoundary = getRightBoundary(); // autoWidth: <= viewport, slidePositions, gutter
1758
+ // fixedWidth: <= viewport, fixedWidth, gutter
1759
+ indexMax = getIndexMax(); // autoWidth: <= rightBoundary, slidePositions
1760
+ // fixedWidth: <= rightBoundary, fixedWidth, gutter
1761
+ }
1762
+
1763
+ if (bpChanged || fixedWidth) {
1764
+ items = getOption('items');
1765
+ slideBy = getOption('slideBy');
1766
+ itemsChanged = items !== itemsTem;
1767
+
1768
+ if (itemsChanged) {
1769
+ if (!fixedWidth && !autoWidth) { indexMax = getIndexMax(); } // <= items
1770
+ // check index before transform in case
1771
+ // slider reach the right edge then items become bigger
1772
+ updateIndex();
1773
+ }
1774
+ }
1775
+
1776
+ if (bpChanged) {
1777
+ if (disable !== disableTem) {
1778
+ if (disable) {
1779
+ disableSlider();
1780
+ } else {
1781
+ enableSlider(); // <= slidePositions, rightBoundary, indexMax
1782
+ }
1783
+ }
1784
+ }
1785
+
1786
+ if (freezable && (bpChanged || fixedWidth || autoWidth)) {
1787
+ freeze = getFreeze(); // <= autoWidth: slidePositions, gutter, viewport, rightBoundary
1788
+ // <= fixedWidth: fixedWidth, gutter, rightBoundary
1789
+ // <= others: items
1790
+
1791
+ if (freeze !== freezeTem) {
1792
+ if (freeze) {
1793
+ doContainerTransform(getContainerTransformValue(getStartIndex(0)));
1794
+ freezeSlider();
1795
+ } else {
1796
+ unfreezeSlider();
1797
+ needContainerTransform = true;
1798
+ }
1799
+ }
1800
+ }
1801
+
1802
+ resetVariblesWhenDisable(disable || freeze); // controls, nav, touch, mouseDrag, arrowKeys, autoplay, autoplayHoverPause, autoplayResetOnVisibility
1803
+ if (!autoplay) { autoplayHoverPause = autoplayResetOnVisibility = false; }
1804
+
1805
+ if (arrowKeys !== arrowKeysTem) {
1806
+ arrowKeys ?
1807
+ addEvents(doc, docmentKeydownEvent) :
1808
+ removeEvents(doc, docmentKeydownEvent);
1809
+ }
1810
+ if (controls !== controlsTem) {
1811
+ if (controls) {
1812
+ if (controlsContainer) {
1813
+ showElement(controlsContainer);
1814
+ } else {
1815
+ if (prevButton) { showElement(prevButton); }
1816
+ if (nextButton) { showElement(nextButton); }
1817
+ }
1818
+ } else {
1819
+ if (controlsContainer) {
1820
+ hideElement(controlsContainer);
1821
+ } else {
1822
+ if (prevButton) { hideElement(prevButton); }
1823
+ if (nextButton) { hideElement(nextButton); }
1824
+ }
1825
+ }
1826
+ }
1827
+ if (nav !== navTem) {
1828
+ if (nav) {
1829
+ showElement(navContainer);
1830
+ updateNavVisibility();
1831
+ } else {
1832
+ hideElement(navContainer);
1833
+ }
1834
+ }
1835
+ if (touch !== touchTem) {
1836
+ touch ?
1837
+ addEvents(container, touchEvents, options.preventScrollOnTouch) :
1838
+ removeEvents(container, touchEvents);
1839
+ }
1840
+ if (mouseDrag !== mouseDragTem) {
1841
+ mouseDrag ?
1842
+ addEvents(container, dragEvents) :
1843
+ removeEvents(container, dragEvents);
1844
+ }
1845
+ if (autoplay !== autoplayTem) {
1846
+ if (autoplay) {
1847
+ if (autoplayButton) { showElement(autoplayButton); }
1848
+ if (!animating && !autoplayUserPaused) { startAutoplay(); }
1849
+ } else {
1850
+ if (autoplayButton) { hideElement(autoplayButton); }
1851
+ if (animating) { stopAutoplay(); }
1852
+ }
1853
+ }
1854
+ if (autoplayHoverPause !== autoplayHoverPauseTem) {
1855
+ autoplayHoverPause ?
1856
+ addEvents(container, hoverEvents) :
1857
+ removeEvents(container, hoverEvents);
1858
+ }
1859
+ if (autoplayResetOnVisibility !== autoplayResetOnVisibilityTem) {
1860
+ autoplayResetOnVisibility ?
1861
+ addEvents(doc, visibilityEvent) :
1862
+ removeEvents(doc, visibilityEvent);
1863
+ }
1864
+
1865
+ if (bpChanged) {
1866
+ if (fixedWidth !== fixedWidthTem || center !== centerTem) { needContainerTransform = true; }
1867
+
1868
+ if (autoHeight !== autoHeightTem) {
1869
+ if (!autoHeight) { innerWrapper.style.height = ''; }
1870
+ }
1871
+
1872
+ if (controls && controlsText !== controlsTextTem) {
1873
+ prevButton.innerHTML = controlsText[0];
1874
+ nextButton.innerHTML = controlsText[1];
1875
+ }
1876
+
1877
+ if (autoplayButton && autoplayText !== autoplayTextTem) {
1878
+ var i = autoplay ? 1 : 0,
1879
+ html = autoplayButton.innerHTML,
1880
+ len = html.length - autoplayTextTem[i].length;
1881
+ if (html.substring(len) === autoplayTextTem[i]) {
1882
+ autoplayButton.innerHTML = html.substring(0, len) + autoplayText[i];
1883
+ }
1884
+ }
1885
+ } else {
1886
+ if (center && (fixedWidth || autoWidth)) { needContainerTransform = true; }
1887
+ }
1888
+
1889
+ if (itemsChanged || fixedWidth && !autoWidth) {
1890
+ pages = getPages();
1891
+ updateNavVisibility();
1892
+ }
1893
+
1894
+ indChanged = index !== indexTem;
1895
+ if (indChanged) {
1896
+ events.emit('indexChanged', info());
1897
+ needContainerTransform = true;
1898
+ } else if (itemsChanged) {
1899
+ if (!indChanged) { additionalUpdates(); }
1900
+ } else if (fixedWidth || autoWidth) {
1901
+ doLazyLoad();
1902
+ updateSlideStatus();
1903
+ updateLiveRegion();
1904
+ }
1905
+
1906
+ if (itemsChanged && !carousel) { updateGallerySlidePositions(); }
1907
+
1908
+ if (!disable && !freeze) {
1909
+ // non-mediaqueries: IE8
1910
+ if (bpChanged && !CSSMQ) {
1911
+ // middle wrapper styles
1912
+
1913
+ // inner wrapper styles
1914
+ if (edgePadding !== edgePaddingTem || gutter !== gutterTem) {
1915
+ innerWrapper.style.cssText = getInnerWrapperStyles(edgePadding, gutter, fixedWidth, speed, autoHeight);
1916
+ }
1917
+
1918
+ if (horizontal) {
1919
+ // container styles
1920
+ if (carousel) {
1921
+ container.style.width = getContainerWidth(fixedWidth, gutter, items);
1922
+ }
1923
+
1924
+ // slide styles
1925
+ var str = getSlideWidthStyle(fixedWidth, gutter, items) +
1926
+ getSlideGutterStyle(gutter);
1927
+
1928
+ // remove the last line and
1929
+ // add new styles
1930
+ removeCSSRule(sheet, getCssRulesLength(sheet) - 1);
1931
+ addCSSRule(sheet, '#' + slideId + ' > .tns-item', str, getCssRulesLength(sheet));
1932
+ }
1933
+ }
1934
+
1935
+ // auto height
1936
+ if (autoHeight) { doAutoHeight(); }
1937
+
1938
+ if (needContainerTransform) {
1939
+ doContainerTransformSilent();
1940
+ indexCached = index;
1941
+ }
1942
+ }
1943
+
1944
+ if (bpChanged) { events.emit('newBreakpointEnd', info(e)); }
1945
+ }
1946
+
1947
+
1948
+
1949
+
1950
+
1951
+ // === INITIALIZATION FUNCTIONS === //
1952
+ function getFreeze () {
1953
+ if (!fixedWidth && !autoWidth) {
1954
+ var a = center ? items - (items - 1) / 2 : items;
1955
+ return slideCount <= a;
1956
+ }
1957
+
1958
+ var width = fixedWidth ? (fixedWidth + gutter) * slideCount : slidePositions[slideCount],
1959
+ vp = edgePadding ? viewport + edgePadding * 2 : viewport + gutter;
1960
+
1961
+ if (center) {
1962
+ vp -= fixedWidth ? (viewport - fixedWidth) / 2 : (viewport - (slidePositions[index + 1] - slidePositions[index] - gutter)) / 2;
1963
+ }
1964
+
1965
+ return width <= vp;
1966
+ }
1967
+
1968
+ function setBreakpointZone () {
1969
+ breakpointZone = 0;
1970
+ for (var bp in responsive) {
1971
+ bp = parseInt(bp); // convert string to number
1972
+ if (windowWidth >= bp) { breakpointZone = bp; }
1973
+ }
1974
+ }
1975
+
1976
+ // (slideBy, indexMin, indexMax) => index
1977
+ var updateIndex = (function () {
1978
+ return loop ?
1979
+ carousel ?
1980
+ // loop + carousel
1981
+ function () {
1982
+ var leftEdge = indexMin,
1983
+ rightEdge = indexMax;
1984
+
1985
+ leftEdge += slideBy;
1986
+ rightEdge -= slideBy;
1987
+
1988
+ // adjust edges when has edge paddings
1989
+ // or fixed-width slider with extra space on the right side
1990
+ if (edgePadding) {
1991
+ leftEdge += 1;
1992
+ rightEdge -= 1;
1993
+ } else if (fixedWidth) {
1994
+ if ((viewport + gutter)%(fixedWidth + gutter)) { rightEdge -= 1; }
1995
+ }
1996
+
1997
+ if (cloneCount) {
1998
+ if (index > rightEdge) {
1999
+ index -= slideCount;
2000
+ } else if (index < leftEdge) {
2001
+ index += slideCount;
2002
+ }
2003
+ }
2004
+ } :
2005
+ // loop + gallery
2006
+ function() {
2007
+ if (index > indexMax) {
2008
+ while (index >= indexMin + slideCount) { index -= slideCount; }
2009
+ } else if (index < indexMin) {
2010
+ while (index <= indexMax - slideCount) { index += slideCount; }
2011
+ }
2012
+ } :
2013
+ // non-loop
2014
+ function() {
2015
+ index = Math.max(indexMin, Math.min(indexMax, index));
2016
+ };
2017
+ })();
2018
+
2019
+ function disableUI () {
2020
+ if (!autoplay && autoplayButton) { hideElement(autoplayButton); }
2021
+ if (!nav && navContainer) { hideElement(navContainer); }
2022
+ if (!controls) {
2023
+ if (controlsContainer) {
2024
+ hideElement(controlsContainer);
2025
+ } else {
2026
+ if (prevButton) { hideElement(prevButton); }
2027
+ if (nextButton) { hideElement(nextButton); }
2028
+ }
2029
+ }
2030
+ }
2031
+
2032
+ function enableUI () {
2033
+ if (autoplay && autoplayButton) { showElement(autoplayButton); }
2034
+ if (nav && navContainer) { showElement(navContainer); }
2035
+ if (controls) {
2036
+ if (controlsContainer) {
2037
+ showElement(controlsContainer);
2038
+ } else {
2039
+ if (prevButton) { showElement(prevButton); }
2040
+ if (nextButton) { showElement(nextButton); }
2041
+ }
2042
+ }
2043
+ }
2044
+
2045
+ function freezeSlider () {
2046
+ if (frozen) { return; }
2047
+
2048
+ // remove edge padding from inner wrapper
2049
+ if (edgePadding) { innerWrapper.style.margin = '0px'; }
2050
+
2051
+ // add class tns-transparent to cloned slides
2052
+ if (cloneCount) {
2053
+ var str = 'tns-transparent';
2054
+ for (var i = cloneCount; i--;) {
2055
+ if (carousel) { addClass(slideItems[i], str); }
2056
+ addClass(slideItems[slideCountNew - i - 1], str);
2057
+ }
2058
+ }
2059
+
2060
+ // update tools
2061
+ disableUI();
2062
+
2063
+ frozen = true;
2064
+ }
2065
+
2066
+ function unfreezeSlider () {
2067
+ if (!frozen) { return; }
2068
+
2069
+ // restore edge padding for inner wrapper
2070
+ // for mordern browsers
2071
+ if (edgePadding && CSSMQ) { innerWrapper.style.margin = ''; }
2072
+
2073
+ // remove class tns-transparent to cloned slides
2074
+ if (cloneCount) {
2075
+ var str = 'tns-transparent';
2076
+ for (var i = cloneCount; i--;) {
2077
+ if (carousel) { removeClass(slideItems[i], str); }
2078
+ removeClass(slideItems[slideCountNew - i - 1], str);
2079
+ }
2080
+ }
2081
+
2082
+ // update tools
2083
+ enableUI();
2084
+
2085
+ frozen = false;
2086
+ }
2087
+
2088
+ function disableSlider () {
2089
+ if (disabled) { return; }
2090
+
2091
+ sheet.disabled = true;
2092
+ container.className = container.className.replace(newContainerClasses.substring(1), '');
2093
+ removeAttrs(container, ['style']);
2094
+ if (loop) {
2095
+ for (var j = cloneCount; j--;) {
2096
+ if (carousel) { hideElement(slideItems[j]); }
2097
+ hideElement(slideItems[slideCountNew - j - 1]);
2098
+ }
2099
+ }
2100
+
2101
+ // vertical slider
2102
+ if (!horizontal || !carousel) { removeAttrs(innerWrapper, ['style']); }
2103
+
2104
+ // gallery
2105
+ if (!carousel) {
2106
+ for (var i = index, l = index + slideCount; i < l; i++) {
2107
+ var item = slideItems[i];
2108
+ removeAttrs(item, ['style']);
2109
+ removeClass(item, animateIn);
2110
+ removeClass(item, animateNormal);
2111
+ }
2112
+ }
2113
+
2114
+ // update tools
2115
+ disableUI();
2116
+
2117
+ disabled = true;
2118
+ }
2119
+
2120
+ function enableSlider () {
2121
+ if (!disabled) { return; }
2122
+
2123
+ sheet.disabled = false;
2124
+ container.className += newContainerClasses;
2125
+ doContainerTransformSilent();
2126
+
2127
+ if (loop) {
2128
+ for (var j = cloneCount; j--;) {
2129
+ if (carousel) { showElement(slideItems[j]); }
2130
+ showElement(slideItems[slideCountNew - j - 1]);
2131
+ }
2132
+ }
2133
+
2134
+ // gallery
2135
+ if (!carousel) {
2136
+ for (var i = index, l = index + slideCount; i < l; i++) {
2137
+ var item = slideItems[i],
2138
+ classN = i < index + items ? animateIn : animateNormal;
2139
+ item.style.left = (i - index) * 100 / items + '%';
2140
+ addClass(item, classN);
2141
+ }
2142
+ }
2143
+
2144
+ // update tools
2145
+ enableUI();
2146
+
2147
+ disabled = false;
2148
+ }
2149
+
2150
+ function updateLiveRegion () {
2151
+ var str = getLiveRegionStr();
2152
+ if (liveregionCurrent.innerHTML !== str) { liveregionCurrent.innerHTML = str; }
2153
+ }
2154
+
2155
+ function getLiveRegionStr () {
2156
+ var arr = getVisibleSlideRange(),
2157
+ start = arr[0] + 1,
2158
+ end = arr[1] + 1;
2159
+ return start === end ? start + '' : start + ' to ' + end;
2160
+ }
2161
+
2162
+ function getVisibleSlideRange (val) {
2163
+ if (val == null) { val = getContainerTransformValue(); }
2164
+ var start = index, end, rangestart, rangeend;
2165
+
2166
+ // get range start, range end for autoWidth and fixedWidth
2167
+ if (center || edgePadding) {
2168
+ if (autoWidth || fixedWidth) {
2169
+ rangestart = - (parseFloat(val) + edgePadding);
2170
+ rangeend = rangestart + viewport + edgePadding * 2;
2171
+ }
2172
+ } else {
2173
+ if (autoWidth) {
2174
+ rangestart = slidePositions[index];
2175
+ rangeend = rangestart + viewport;
2176
+ }
2177
+ }
2178
+
2179
+ // get start, end
2180
+ // - check auto width
2181
+ if (autoWidth) {
2182
+ slidePositions.forEach(function(point, i) {
2183
+ if (i < slideCountNew) {
2184
+ if ((center || edgePadding) && point <= rangestart + 0.5) { start = i; }
2185
+ if (rangeend - point >= 0.5) { end = i; }
2186
+ }
2187
+ });
2188
+
2189
+ // - check percentage width, fixed width
2190
+ } else {
2191
+
2192
+ if (fixedWidth) {
2193
+ var cell = fixedWidth + gutter;
2194
+ if (center || edgePadding) {
2195
+ start = Math.floor(rangestart/cell);
2196
+ end = Math.ceil(rangeend/cell - 1);
2197
+ } else {
2198
+ end = start + Math.ceil(viewport/cell) - 1;
2199
+ }
2200
+
2201
+ } else {
2202
+ if (center || edgePadding) {
2203
+ var a = items - 1;
2204
+ if (center) {
2205
+ start -= a / 2;
2206
+ end = index + a / 2;
2207
+ } else {
2208
+ end = index + a;
2209
+ }
2210
+
2211
+ if (edgePadding) {
2212
+ var b = edgePadding * items / viewport;
2213
+ start -= b;
2214
+ end += b;
2215
+ }
2216
+
2217
+ start = Math.floor(start);
2218
+ end = Math.ceil(end);
2219
+ } else {
2220
+ end = start + items - 1;
2221
+ }
2222
+ }
2223
+
2224
+ start = Math.max(start, 0);
2225
+ end = Math.min(end, slideCountNew - 1);
2226
+ }
2227
+
2228
+ return [start, end];
2229
+ }
2230
+
2231
+ function doLazyLoad () {
2232
+ if (lazyload && !disable) {
2233
+ var arg = getVisibleSlideRange();
2234
+ arg.push(lazyloadSelector);
2235
+
2236
+ getImageArray.apply(null, arg).forEach(function (img) {
2237
+ if (!hasClass(img, imgCompleteClass)) {
2238
+ // stop propagation transitionend event to container
2239
+ var eve = {};
2240
+ eve[TRANSITIONEND] = function (e) { e.stopPropagation(); };
2241
+ addEvents(img, eve);
2242
+
2243
+ addEvents(img, imgEvents);
2244
+
2245
+ // update src
2246
+ img.src = getAttr(img, 'data-src');
2247
+
2248
+ // update srcset
2249
+ var srcset = getAttr(img, 'data-srcset');
2250
+ if (srcset) { img.srcset = srcset; }
2251
+
2252
+ addClass(img, 'loading');
2253
+ }
2254
+ });
2255
+ }
2256
+ }
2257
+
2258
+ function onImgLoaded (e) {
2259
+ imgLoaded(getTarget(e));
2260
+ }
2261
+
2262
+ function onImgFailed (e) {
2263
+ imgFailed(getTarget(e));
2264
+ }
2265
+
2266
+ function imgLoaded (img) {
2267
+ addClass(img, 'loaded');
2268
+ imgCompleted(img);
2269
+ }
2270
+
2271
+ function imgFailed (img) {
2272
+ addClass(img, 'failed');
2273
+ imgCompleted(img);
2274
+ }
2275
+
2276
+ function imgCompleted (img) {
2277
+ addClass(img, imgCompleteClass);
2278
+ removeClass(img, 'loading');
2279
+ removeEvents(img, imgEvents);
2280
+ }
2281
+
2282
+ function getImageArray (start, end, imgSelector) {
2283
+ var imgs = [];
2284
+ if (!imgSelector) { imgSelector = 'img'; }
2285
+
2286
+ while (start <= end) {
2287
+ forEach(slideItems[start].querySelectorAll(imgSelector), function (img) { imgs.push(img); });
2288
+ start++;
2289
+ }
2290
+
2291
+ return imgs;
2292
+ }
2293
+
2294
+ // check if all visible images are loaded
2295
+ // and update container height if it's done
2296
+ function doAutoHeight () {
2297
+ var imgs = getImageArray.apply(null, getVisibleSlideRange());
2298
+ raf(function(){ imgsLoadedCheck(imgs, updateInnerWrapperHeight); });
2299
+ }
2300
+
2301
+ function imgsLoadedCheck (imgs, cb) {
2302
+ // execute callback function if all images are complete
2303
+ if (imgsComplete) { return cb(); }
2304
+
2305
+ // check image classes
2306
+ imgs.forEach(function (img, index) {
2307
+ if (!lazyload && img.complete) { imgCompleted(img); } // Check image.complete
2308
+ if (hasClass(img, imgCompleteClass)) { imgs.splice(index, 1); }
2309
+ });
2310
+
2311
+ // execute callback function if selected images are all complete
2312
+ if (!imgs.length) { return cb(); }
2313
+
2314
+ // otherwise execute this functiona again
2315
+ raf(function(){ imgsLoadedCheck(imgs, cb); });
2316
+ }
2317
+
2318
+ function additionalUpdates () {
2319
+ doLazyLoad();
2320
+ updateSlideStatus();
2321
+ updateLiveRegion();
2322
+ updateControlsStatus();
2323
+ updateNavStatus();
2324
+ }
2325
+
2326
+
2327
+ function update_carousel_transition_duration () {
2328
+ if (carousel && autoHeight) {
2329
+ middleWrapper.style[TRANSITIONDURATION] = speed / 1000 + 's';
2330
+ }
2331
+ }
2332
+
2333
+ function getMaxSlideHeight (slideStart, slideRange) {
2334
+ var heights = [];
2335
+ for (var i = slideStart, l = Math.min(slideStart + slideRange, slideCountNew); i < l; i++) {
2336
+ heights.push(slideItems[i].offsetHeight);
2337
+ }
2338
+
2339
+ return Math.max.apply(null, heights);
2340
+ }
2341
+
2342
+ // update inner wrapper height
2343
+ // 1. get the max-height of the visible slides
2344
+ // 2. set transitionDuration to speed
2345
+ // 3. update inner wrapper height to max-height
2346
+ // 4. set transitionDuration to 0s after transition done
2347
+ function updateInnerWrapperHeight () {
2348
+ var maxHeight = autoHeight ? getMaxSlideHeight(index, items) : getMaxSlideHeight(cloneCount, slideCount),
2349
+ wp = middleWrapper ? middleWrapper : innerWrapper;
2350
+
2351
+ if (wp.style.height !== maxHeight) { wp.style.height = maxHeight + 'px'; }
2352
+ }
2353
+
2354
+ // get the distance from the top edge of the first slide to each slide
2355
+ // (init) => slidePositions
2356
+ function setSlidePositions () {
2357
+ slidePositions = [0];
2358
+ var attr = horizontal ? 'left' : 'top',
2359
+ attr2 = horizontal ? 'right' : 'bottom',
2360
+ base = slideItems[0].getBoundingClientRect()[attr];
2361
+
2362
+ forEach(slideItems, function(item, i) {
2363
+ // skip the first slide
2364
+ if (i) { slidePositions.push(item.getBoundingClientRect()[attr] - base); }
2365
+ // add the end edge
2366
+ if (i === slideCountNew - 1) { slidePositions.push(item.getBoundingClientRect()[attr2] - base); }
2367
+ });
2368
+ }
2369
+
2370
+ // update slide
2371
+ function updateSlideStatus () {
2372
+ var range = getVisibleSlideRange(),
2373
+ start = range[0],
2374
+ end = range[1];
2375
+
2376
+ forEach(slideItems, function(item, i) {
2377
+ // show slides
2378
+ if (i >= start && i <= end) {
2379
+ if (hasAttr(item, 'aria-hidden')) {
2380
+ removeAttrs(item, ['aria-hidden', 'tabindex']);
2381
+ addClass(item, slideActiveClass);
2382
+ }
2383
+ // hide slides
2384
+ } else {
2385
+ if (!hasAttr(item, 'aria-hidden')) {
2386
+ setAttrs(item, {
2387
+ 'aria-hidden': 'true',
2388
+ 'tabindex': '-1'
2389
+ });
2390
+ removeClass(item, slideActiveClass);
2391
+ }
2392
+ }
2393
+ });
2394
+ }
2395
+
2396
+ // gallery: update slide position
2397
+ function updateGallerySlidePositions () {
2398
+ var l = index + Math.min(slideCount, items);
2399
+ for (var i = slideCountNew; i--;) {
2400
+ var item = slideItems[i];
2401
+
2402
+ if (i >= index && i < l) {
2403
+ // add transitions to visible slides when adjusting their positions
2404
+ addClass(item, 'tns-moving');
2405
+
2406
+ item.style.left = (i - index) * 100 / items + '%';
2407
+ addClass(item, animateIn);
2408
+ removeClass(item, animateNormal);
2409
+ } else if (item.style.left) {
2410
+ item.style.left = '';
2411
+ addClass(item, animateNormal);
2412
+ removeClass(item, animateIn);
2413
+ }
2414
+
2415
+ // remove outlet animation
2416
+ removeClass(item, animateOut);
2417
+ }
2418
+
2419
+ // removing '.tns-moving'
2420
+ setTimeout(function() {
2421
+ forEach(slideItems, function(el) {
2422
+ removeClass(el, 'tns-moving');
2423
+ });
2424
+ }, 300);
2425
+ }
2426
+
2427
+ // set tabindex on Nav
2428
+ function updateNavStatus () {
2429
+ // get current nav
2430
+ if (nav) {
2431
+ navCurrentIndex = navClicked >= 0 ? navClicked : getCurrentNavIndex();
2432
+ navClicked = -1;
2433
+
2434
+ if (navCurrentIndex !== navCurrentIndexCached) {
2435
+ var navPrev = navItems[navCurrentIndexCached],
2436
+ navCurrent = navItems[navCurrentIndex];
2437
+
2438
+ setAttrs(navPrev, {
2439
+ 'tabindex': '-1',
2440
+ 'aria-label': navStr + (navCurrentIndexCached + 1)
2441
+ });
2442
+ removeClass(navPrev, navActiveClass);
2443
+
2444
+ setAttrs(navCurrent, {'aria-label': navStr + (navCurrentIndex + 1) + navStrCurrent});
2445
+ removeAttrs(navCurrent, 'tabindex');
2446
+ addClass(navCurrent, navActiveClass);
2447
+
2448
+ navCurrentIndexCached = navCurrentIndex;
2449
+ }
2450
+ }
2451
+ }
2452
+
2453
+ function getLowerCaseNodeName (el) {
2454
+ return el.nodeName.toLowerCase();
2455
+ }
2456
+
2457
+ function isButton (el) {
2458
+ return getLowerCaseNodeName(el) === 'button';
2459
+ }
2460
+
2461
+ function isAriaDisabled (el) {
2462
+ return el.getAttribute('aria-disabled') === 'true';
2463
+ }
2464
+
2465
+ function disEnableElement (isButton, el, val) {
2466
+ if (isButton) {
2467
+ el.disabled = val;
2468
+ } else {
2469
+ el.setAttribute('aria-disabled', val.toString());
2470
+ }
2471
+ }
2472
+
2473
+ // set 'disabled' to true on controls when reach the edges
2474
+ function updateControlsStatus () {
2475
+ if (!controls || rewind || loop) { return; }
2476
+
2477
+ var prevDisabled = (prevIsButton) ? prevButton.disabled : isAriaDisabled(prevButton),
2478
+ nextDisabled = (nextIsButton) ? nextButton.disabled : isAriaDisabled(nextButton),
2479
+ disablePrev = (index <= indexMin) ? true : false,
2480
+ disableNext = (!rewind && index >= indexMax) ? true : false;
2481
+
2482
+ if (disablePrev && !prevDisabled) {
2483
+ disEnableElement(prevIsButton, prevButton, true);
2484
+ }
2485
+ if (!disablePrev && prevDisabled) {
2486
+ disEnableElement(prevIsButton, prevButton, false);
2487
+ }
2488
+ if (disableNext && !nextDisabled) {
2489
+ disEnableElement(nextIsButton, nextButton, true);
2490
+ }
2491
+ if (!disableNext && nextDisabled) {
2492
+ disEnableElement(nextIsButton, nextButton, false);
2493
+ }
2494
+ }
2495
+
2496
+ // set duration
2497
+ function resetDuration (el, str) {
2498
+ if (TRANSITIONDURATION) { el.style[TRANSITIONDURATION] = str; }
2499
+ }
2500
+
2501
+ function getSliderWidth () {
2502
+ return fixedWidth ? (fixedWidth + gutter) * slideCountNew : slidePositions[slideCountNew];
2503
+ }
2504
+
2505
+ function getCenterGap (num) {
2506
+ if (num == null) { num = index; }
2507
+
2508
+ var gap = edgePadding ? gutter : 0;
2509
+ return autoWidth ? ((viewport - gap) - (slidePositions[num + 1] - slidePositions[num] - gutter))/2 :
2510
+ fixedWidth ? (viewport - fixedWidth) / 2 :
2511
+ (items - 1) / 2;
2512
+ }
2513
+
2514
+ function getRightBoundary () {
2515
+ var gap = edgePadding ? gutter : 0,
2516
+ result = (viewport + gap) - getSliderWidth();
2517
+
2518
+ if (center && !loop) {
2519
+ result = fixedWidth ? - (fixedWidth + gutter) * (slideCountNew - 1) - getCenterGap() :
2520
+ getCenterGap(slideCountNew - 1) - slidePositions[slideCountNew - 1];
2521
+ }
2522
+ if (result > 0) { result = 0; }
2523
+
2524
+ return result;
2525
+ }
2526
+
2527
+ function getContainerTransformValue (num) {
2528
+ if (num == null) { num = index; }
2529
+
2530
+ var val;
2531
+ if (horizontal && !autoWidth) {
2532
+ if (fixedWidth) {
2533
+ val = - (fixedWidth + gutter) * num;
2534
+ if (center) { val += getCenterGap(); }
2535
+ } else {
2536
+ var denominator = TRANSFORM ? slideCountNew : items;
2537
+ if (center) { num -= getCenterGap(); }
2538
+ val = - num * 100 / denominator;
2539
+ }
2540
+ } else {
2541
+ val = - slidePositions[num];
2542
+ if (center && autoWidth) {
2543
+ val += getCenterGap();
2544
+ }
2545
+ }
2546
+
2547
+ if (hasRightDeadZone) { val = Math.max(val, rightBoundary); }
2548
+
2549
+ val += (horizontal && !autoWidth && !fixedWidth) ? '%' : 'px';
2550
+
2551
+ return val;
2552
+ }
2553
+
2554
+ function doContainerTransformSilent (val) {
2555
+ resetDuration(container, '0s');
2556
+ doContainerTransform(val);
2557
+ }
2558
+
2559
+ function doContainerTransform (val) {
2560
+ if (val == null) { val = getContainerTransformValue(); }
2561
+ if (textDirection === 'rtl' && val.charAt(0) === '-') {
2562
+ val = val.substr(1);
2563
+ }
2564
+ container.style[transformAttr] = transformPrefix + val + transformPostfix;
2565
+ }
2566
+
2567
+ function animateSlide (number, classOut, classIn, isOut) {
2568
+ var l = number + items;
2569
+ if (!loop) { l = Math.min(l, slideCountNew); }
2570
+
2571
+ for (var i = number; i < l; i++) {
2572
+ var item = slideItems[i];
2573
+
2574
+ // set item positions
2575
+ if (!isOut) { item.style.left = (i - index) * 100 / items + '%'; }
2576
+
2577
+ if (animateDelay && TRANSITIONDELAY) {
2578
+ item.style[TRANSITIONDELAY] = item.style[ANIMATIONDELAY] = animateDelay * (i - number) / 1000 + 's';
2579
+ }
2580
+ removeClass(item, classOut);
2581
+ addClass(item, classIn);
2582
+
2583
+ if (isOut) { slideItemsOut.push(item); }
2584
+ }
2585
+ }
2586
+
2587
+ // make transfer after click/drag:
2588
+ // 1. change 'transform' property for mordern browsers
2589
+ // 2. change 'left' property for legacy browsers
2590
+ var transformCore = (function () {
2591
+ return carousel ?
2592
+ function () {
2593
+ resetDuration(container, '');
2594
+ if (TRANSITIONDURATION || !speed) {
2595
+ // for morden browsers with non-zero duration or
2596
+ // zero duration for all browsers
2597
+ doContainerTransform();
2598
+ // run fallback function manually
2599
+ // when duration is 0 / container is hidden
2600
+ if (!speed || !isVisible(container)) { onTransitionEnd(); }
2601
+
2602
+ } else {
2603
+ // for old browser with non-zero duration
2604
+ jsTransform(container, transformAttr, transformPrefix, transformPostfix, getContainerTransformValue(), speed, onTransitionEnd);
2605
+ }
2606
+
2607
+ if (!horizontal) { updateContentWrapperHeight(); }
2608
+ } :
2609
+ function () {
2610
+ slideItemsOut = [];
2611
+
2612
+ var eve = {};
2613
+ eve[TRANSITIONEND] = eve[ANIMATIONEND] = onTransitionEnd;
2614
+ removeEvents(slideItems[indexCached], eve);
2615
+ addEvents(slideItems[index], eve);
2616
+
2617
+ animateSlide(indexCached, animateIn, animateOut, true);
2618
+ animateSlide(index, animateNormal, animateIn);
2619
+
2620
+ // run fallback function manually
2621
+ // when transition or animation not supported / duration is 0
2622
+ if (!TRANSITIONEND || !ANIMATIONEND || !speed || !isVisible(container)) { onTransitionEnd(); }
2623
+ };
2624
+ })();
2625
+
2626
+ function render (e, sliderMoved) {
2627
+ if (updateIndexBeforeTransform) { updateIndex(); }
2628
+
2629
+ // render when slider was moved (touch or drag) even though index may not change
2630
+ if (index !== indexCached || sliderMoved) {
2631
+ // events
2632
+ events.emit('indexChanged', info());
2633
+ events.emit('transitionStart', info());
2634
+ if (autoHeight) { doAutoHeight(); }
2635
+
2636
+ // pause autoplay when click or keydown from user
2637
+ if (animating && e && ['click', 'keydown'].indexOf(e.type) >= 0) { stopAutoplay(); }
2638
+
2639
+ running = true;
2640
+ transformCore();
2641
+ }
2642
+ }
2643
+
2644
+ /*
2645
+ * Transfer prefixed properties to the same format
2646
+ * CSS: -Webkit-Transform => webkittransform
2647
+ * JS: WebkitTransform => webkittransform
2648
+ * @param {string} str - property
2649
+ *
2650
+ */
2651
+ function strTrans (str) {
2652
+ return str.toLowerCase().replace(/-/g, '');
2653
+ }
2654
+
2655
+ // AFTER TRANSFORM
2656
+ // Things need to be done after a transfer:
2657
+ // 1. check index
2658
+ // 2. add classes to visible slide
2659
+ // 3. disable controls buttons when reach the first/last slide in non-loop slider
2660
+ // 4. update nav status
2661
+ // 5. lazyload images
2662
+ // 6. update container height
2663
+ function onTransitionEnd (event) {
2664
+ // check running on gallery mode
2665
+ // make sure trantionend/animationend events run only once
2666
+ if (carousel || running) {
2667
+ events.emit('transitionEnd', info(event));
2668
+
2669
+ if (!carousel && slideItemsOut.length > 0) {
2670
+ for (var i = 0; i < slideItemsOut.length; i++) {
2671
+ var item = slideItemsOut[i];
2672
+ // set item positions
2673
+ item.style.left = '';
2674
+
2675
+ if (ANIMATIONDELAY && TRANSITIONDELAY) {
2676
+ item.style[ANIMATIONDELAY] = '';
2677
+ item.style[TRANSITIONDELAY] = '';
2678
+ }
2679
+ removeClass(item, animateOut);
2680
+ addClass(item, animateNormal);
2681
+ }
2682
+ }
2683
+
2684
+ /* update slides, nav, controls after checking ...
2685
+ * => legacy browsers who don't support 'event'
2686
+ * have to check event first, otherwise event.target will cause an error
2687
+ * => or 'gallery' mode:
2688
+ * + event target is slide item
2689
+ * => or 'carousel' mode:
2690
+ * + event target is container,
2691
+ * + event.property is the same with transform attribute
2692
+ */
2693
+ if (!event ||
2694
+ !carousel && event.target.parentNode === container ||
2695
+ event.target === container && strTrans(event.propertyName) === strTrans(transformAttr)) {
2696
+
2697
+ if (!updateIndexBeforeTransform) {
2698
+ var indexTem = index;
2699
+ updateIndex();
2700
+ if (index !== indexTem) {
2701
+ events.emit('indexChanged', info());
2702
+
2703
+ doContainerTransformSilent();
2704
+ }
2705
+ }
2706
+
2707
+ if (nested === 'inner') { events.emit('innerLoaded', info()); }
2708
+ running = false;
2709
+ indexCached = index;
2710
+ }
2711
+ }
2712
+
2713
+ }
2714
+
2715
+ // # ACTIONS
2716
+ function goTo (targetIndex, e) {
2717
+ if (freeze) { return; }
2718
+
2719
+ // prev slideBy
2720
+ if (targetIndex === 'prev') {
2721
+ onControlsClick(e, -1);
2722
+
2723
+ // next slideBy
2724
+ } else if (targetIndex === 'next') {
2725
+ onControlsClick(e, 1);
2726
+
2727
+ // go to exact slide
2728
+ } else {
2729
+ if (running) {
2730
+ if (preventActionWhenRunning) { return; } else { onTransitionEnd(); }
2731
+ }
2732
+
2733
+ var absIndex = getAbsIndex(),
2734
+ indexGap = 0;
2735
+
2736
+ if (targetIndex === 'first') {
2737
+ indexGap = - absIndex;
2738
+ } else if (targetIndex === 'last') {
2739
+ indexGap = carousel ? slideCount - items - absIndex : slideCount - 1 - absIndex;
2740
+ } else {
2741
+ if (typeof targetIndex !== 'number') { targetIndex = parseInt(targetIndex); }
2742
+
2743
+ if (!isNaN(targetIndex)) {
2744
+ // from directly called goTo function
2745
+ if (!e) { targetIndex = Math.max(0, Math.min(slideCount - 1, targetIndex)); }
2746
+
2747
+ indexGap = targetIndex - absIndex;
2748
+ }
2749
+ }
2750
+
2751
+ // gallery: make sure new page won't overlap with current page
2752
+ if (!carousel && indexGap && Math.abs(indexGap) < items) {
2753
+ var factor = indexGap > 0 ? 1 : -1;
2754
+ indexGap += (index + indexGap - slideCount) >= indexMin ? slideCount * factor : slideCount * 2 * factor * -1;
2755
+ }
2756
+
2757
+ index += indexGap;
2758
+
2759
+ // make sure index is in range
2760
+ if (carousel && loop) {
2761
+ if (index < indexMin) { index += slideCount; }
2762
+ if (index > indexMax) { index -= slideCount; }
2763
+ }
2764
+
2765
+ // if index is changed, start rendering
2766
+ if (getAbsIndex(index) !== getAbsIndex(indexCached)) {
2767
+ render(e);
2768
+ }
2769
+
2770
+ }
2771
+ }
2772
+
2773
+ // on controls click
2774
+ function onControlsClick (e, dir) {
2775
+ if (running) {
2776
+ if (preventActionWhenRunning) { return; } else { onTransitionEnd(); }
2777
+ }
2778
+ var passEventObject;
2779
+
2780
+ if (!dir) {
2781
+ e = getEvent(e);
2782
+ var target = getTarget(e);
2783
+
2784
+ while (target !== controlsContainer && [prevButton, nextButton].indexOf(target) < 0) { target = target.parentNode; }
2785
+
2786
+ var targetIn = [prevButton, nextButton].indexOf(target);
2787
+ if (targetIn >= 0) {
2788
+ passEventObject = true;
2789
+ dir = targetIn === 0 ? -1 : 1;
2790
+ }
2791
+ }
2792
+
2793
+ if (rewind) {
2794
+ if (index === indexMin && dir === -1) {
2795
+ goTo('last', e);
2796
+ return;
2797
+ } else if (index === indexMax && dir === 1) {
2798
+ goTo('first', e);
2799
+ return;
2800
+ }
2801
+ }
2802
+
2803
+ if (dir) {
2804
+ index += slideBy * dir;
2805
+ if (autoWidth) { index = Math.floor(index); }
2806
+ // pass e when click control buttons or keydown
2807
+ render((passEventObject || (e && e.type === 'keydown')) ? e : null);
2808
+ }
2809
+ }
2810
+
2811
+ // on nav click
2812
+ function onNavClick (e) {
2813
+ if (running) {
2814
+ if (preventActionWhenRunning) { return; } else { onTransitionEnd(); }
2815
+ }
2816
+
2817
+ e = getEvent(e);
2818
+ var target = getTarget(e), navIndex;
2819
+
2820
+ // find the clicked nav item
2821
+ while (target !== navContainer && !hasAttr(target, 'data-nav')) { target = target.parentNode; }
2822
+ if (hasAttr(target, 'data-nav')) {
2823
+ var navIndex = navClicked = Number(getAttr(target, 'data-nav')),
2824
+ targetIndexBase = fixedWidth || autoWidth ? navIndex * slideCount / pages : navIndex * items,
2825
+ targetIndex = navAsThumbnails ? navIndex : Math.min(Math.ceil(targetIndexBase), slideCount - 1);
2826
+ goTo(targetIndex, e);
2827
+
2828
+ if (navCurrentIndex === navIndex) {
2829
+ if (animating) { stopAutoplay(); }
2830
+ navClicked = -1; // reset navClicked
2831
+ }
2832
+ }
2833
+ }
2834
+
2835
+ // autoplay functions
2836
+ function setAutoplayTimer () {
2837
+ autoplayTimer = setInterval(function () {
2838
+ onControlsClick(null, autoplayDirection);
2839
+ }, autoplayTimeout);
2840
+
2841
+ animating = true;
2842
+ }
2843
+
2844
+ function stopAutoplayTimer () {
2845
+ clearInterval(autoplayTimer);
2846
+ animating = false;
2847
+ }
2848
+
2849
+ function updateAutoplayButton (action, txt) {
2850
+ setAttrs(autoplayButton, {'data-action': action});
2851
+ autoplayButton.innerHTML = autoplayHtmlStrings[0] + action + autoplayHtmlStrings[1] + txt;
2852
+ }
2853
+
2854
+ function startAutoplay () {
2855
+ setAutoplayTimer();
2856
+ if (autoplayButton) { updateAutoplayButton('stop', autoplayText[1]); }
2857
+ }
2858
+
2859
+ function stopAutoplay () {
2860
+ stopAutoplayTimer();
2861
+ if (autoplayButton) { updateAutoplayButton('start', autoplayText[0]); }
2862
+ }
2863
+
2864
+ // programaitcally play/pause the slider
2865
+ function play () {
2866
+ if (autoplay && !animating) {
2867
+ startAutoplay();
2868
+ autoplayUserPaused = false;
2869
+ }
2870
+ }
2871
+ function pause () {
2872
+ if (animating) {
2873
+ stopAutoplay();
2874
+ autoplayUserPaused = true;
2875
+ }
2876
+ }
2877
+
2878
+ function toggleAutoplay () {
2879
+ if (animating) {
2880
+ stopAutoplay();
2881
+ autoplayUserPaused = true;
2882
+ } else {
2883
+ startAutoplay();
2884
+ autoplayUserPaused = false;
2885
+ }
2886
+ }
2887
+
2888
+ function onVisibilityChange () {
2889
+ if (doc.hidden) {
2890
+ if (animating) {
2891
+ stopAutoplayTimer();
2892
+ autoplayVisibilityPaused = true;
2893
+ }
2894
+ } else if (autoplayVisibilityPaused) {
2895
+ setAutoplayTimer();
2896
+ autoplayVisibilityPaused = false;
2897
+ }
2898
+ }
2899
+
2900
+ function mouseoverPause () {
2901
+ if (animating) {
2902
+ stopAutoplayTimer();
2903
+ autoplayHoverPaused = true;
2904
+ }
2905
+ }
2906
+
2907
+ function mouseoutRestart () {
2908
+ if (autoplayHoverPaused) {
2909
+ setAutoplayTimer();
2910
+ autoplayHoverPaused = false;
2911
+ }
2912
+ }
2913
+
2914
+ // keydown events on document
2915
+ function onDocumentKeydown (e) {
2916
+ e = getEvent(e);
2917
+ var keyIndex = [KEYS.LEFT, KEYS.RIGHT].indexOf(e.keyCode);
2918
+
2919
+ if (keyIndex >= 0) {
2920
+ onControlsClick(e, keyIndex === 0 ? -1 : 1);
2921
+ }
2922
+ }
2923
+
2924
+ // on key control
2925
+ function onControlsKeydown (e) {
2926
+ e = getEvent(e);
2927
+ var keyIndex = [KEYS.LEFT, KEYS.RIGHT].indexOf(e.keyCode);
2928
+
2929
+ if (keyIndex >= 0) {
2930
+ if (keyIndex === 0) {
2931
+ if (!prevButton.disabled) { onControlsClick(e, -1); }
2932
+ } else if (!nextButton.disabled) {
2933
+ onControlsClick(e, 1);
2934
+ }
2935
+ }
2936
+ }
2937
+
2938
+ // set focus
2939
+ function setFocus (el) {
2940
+ el.focus();
2941
+ }
2942
+
2943
+ // on key nav
2944
+ function onNavKeydown (e) {
2945
+ e = getEvent(e);
2946
+ var curElement = doc.activeElement;
2947
+ if (!hasAttr(curElement, 'data-nav')) { return; }
2948
+
2949
+ // var code = e.keyCode,
2950
+ var keyIndex = [KEYS.LEFT, KEYS.RIGHT, KEYS.ENTER, KEYS.SPACE].indexOf(e.keyCode),
2951
+ navIndex = Number(getAttr(curElement, 'data-nav'));
2952
+
2953
+ if (keyIndex >= 0) {
2954
+ if (keyIndex === 0) {
2955
+ if (navIndex > 0) { setFocus(navItems[navIndex - 1]); }
2956
+ } else if (keyIndex === 1) {
2957
+ if (navIndex < pages - 1) { setFocus(navItems[navIndex + 1]); }
2958
+ } else {
2959
+ navClicked = navIndex;
2960
+ goTo(navIndex, e);
2961
+ }
2962
+ }
2963
+ }
2964
+
2965
+ function getEvent (e) {
2966
+ e = e || win.event;
2967
+ return isTouchEvent(e) ? e.changedTouches[0] : e;
2968
+ }
2969
+ function getTarget (e) {
2970
+ return e.target || win.event.srcElement;
2971
+ }
2972
+
2973
+ function isTouchEvent (e) {
2974
+ return e.type.indexOf('touch') >= 0;
2975
+ }
2976
+
2977
+ function preventDefaultBehavior (e) {
2978
+ e.preventDefault ? e.preventDefault() : e.returnValue = false;
2979
+ }
2980
+
2981
+ function getMoveDirectionExpected () {
2982
+ return getTouchDirection(toDegree(lastPosition.y - initPosition.y, lastPosition.x - initPosition.x), swipeAngle) === options.axis;
2983
+ }
2984
+
2985
+ function onPanStart (e) {
2986
+ if (running) {
2987
+ if (preventActionWhenRunning) { return; } else { onTransitionEnd(); }
2988
+ }
2989
+
2990
+ if (autoplay && animating) { stopAutoplayTimer(); }
2991
+
2992
+ panStart = true;
2993
+ if (rafIndex) {
2994
+ caf(rafIndex);
2995
+ rafIndex = null;
2996
+ }
2997
+
2998
+ var $ = getEvent(e);
2999
+ events.emit(isTouchEvent(e) ? 'touchStart' : 'dragStart', info(e));
3000
+
3001
+ if (!isTouchEvent(e) && ['img', 'a'].indexOf(getLowerCaseNodeName(getTarget(e))) >= 0) {
3002
+ preventDefaultBehavior(e);
3003
+ }
3004
+
3005
+ lastPosition.x = initPosition.x = $.clientX;
3006
+ lastPosition.y = initPosition.y = $.clientY;
3007
+ if (carousel) {
3008
+ translateInit = parseFloat(container.style[transformAttr].replace(transformPrefix, ''));
3009
+ resetDuration(container, '0s');
3010
+ }
3011
+ }
3012
+
3013
+ function onPanMove (e) {
3014
+ if (panStart) {
3015
+ var $ = getEvent(e);
3016
+ lastPosition.x = $.clientX;
3017
+ lastPosition.y = $.clientY;
3018
+
3019
+ if (carousel) {
3020
+ if (!rafIndex) { rafIndex = raf(function(){ panUpdate(e); }); }
3021
+ } else {
3022
+ if (moveDirectionExpected === '?') { moveDirectionExpected = getMoveDirectionExpected(); }
3023
+ if (moveDirectionExpected) { preventScroll = true; }
3024
+ }
3025
+
3026
+ if ((typeof e.cancelable !== 'boolean' || e.cancelable) && preventScroll) {
3027
+ e.preventDefault();
3028
+ }
3029
+ }
3030
+ }
3031
+
3032
+ function panUpdate (e) {
3033
+ if (!moveDirectionExpected) {
3034
+ panStart = false;
3035
+ return;
3036
+ }
3037
+ caf(rafIndex);
3038
+ if (panStart) { rafIndex = raf(function(){ panUpdate(e); }); }
3039
+
3040
+ if (moveDirectionExpected === '?') { moveDirectionExpected = getMoveDirectionExpected(); }
3041
+ if (moveDirectionExpected) {
3042
+ if (!preventScroll && isTouchEvent(e)) { preventScroll = true; }
3043
+
3044
+ try {
3045
+ if (e.type) { events.emit(isTouchEvent(e) ? 'touchMove' : 'dragMove', info(e)); }
3046
+ } catch(err) {}
3047
+
3048
+ var x = translateInit,
3049
+ dist = getDist(lastPosition, initPosition);
3050
+ if (!horizontal || fixedWidth || autoWidth) {
3051
+ x += dist;
3052
+ x += 'px';
3053
+ } else {
3054
+ var percentageX = TRANSFORM ? dist * items * 100 / ((viewport + gutter) * slideCountNew): dist * 100 / (viewport + gutter);
3055
+ x += percentageX;
3056
+ x += '%';
3057
+ }
3058
+
3059
+ container.style[transformAttr] = transformPrefix + x + transformPostfix;
3060
+ }
3061
+ }
3062
+
3063
+ function onPanEnd (e) {
3064
+ if (panStart) {
3065
+ if (rafIndex) {
3066
+ caf(rafIndex);
3067
+ rafIndex = null;
3068
+ }
3069
+ if (carousel) { resetDuration(container, ''); }
3070
+ panStart = false;
3071
+
3072
+ var $ = getEvent(e);
3073
+ lastPosition.x = $.clientX;
3074
+ lastPosition.y = $.clientY;
3075
+ var dist = getDist(lastPosition, initPosition);
3076
+
3077
+ if (Math.abs(dist)) {
3078
+ // drag vs click
3079
+ if (!isTouchEvent(e)) {
3080
+ // prevent "click"
3081
+ var target = getTarget(e);
3082
+ addEvents(target, {'click': function preventClick (e) {
3083
+ preventDefaultBehavior(e);
3084
+ removeEvents(target, {'click': preventClick});
3085
+ }});
3086
+ }
3087
+
3088
+ if (carousel) {
3089
+ rafIndex = raf(function() {
3090
+ if (horizontal && !autoWidth) {
3091
+ var indexMoved = - dist * items / (viewport + gutter);
3092
+ indexMoved = dist > 0 ? Math.floor(indexMoved) : Math.ceil(indexMoved);
3093
+ if (textDirection === 'rtl') {
3094
+ index += indexMoved * -1;
3095
+ } else {
3096
+ index += indexMoved;
3097
+ }
3098
+ } else {
3099
+ var moved = - (translateInit + dist);
3100
+ if (moved <= 0) {
3101
+ index = indexMin;
3102
+ } else if (moved >= slidePositions[slideCountNew - 1]) {
3103
+ index = indexMax;
3104
+ } else {
3105
+ var i = 0;
3106
+ while (i < slideCountNew && moved >= slidePositions[i]) {
3107
+ index = i;
3108
+ if (moved > slidePositions[i] && dist < 0) { index += 1; }
3109
+ i++;
3110
+ }
3111
+ }
3112
+ }
3113
+
3114
+ render(e, dist);
3115
+ events.emit(isTouchEvent(e) ? 'touchEnd' : 'dragEnd', info(e));
3116
+ });
3117
+ } else {
3118
+ if (moveDirectionExpected) {
3119
+ onControlsClick(e, dist > 0 ? -1 : 1);
3120
+ }
3121
+ }
3122
+ }
3123
+ }
3124
+
3125
+ // reset
3126
+ if (options.preventScrollOnTouch === 'auto') { preventScroll = false; }
3127
+ if (swipeAngle) { moveDirectionExpected = '?'; }
3128
+ if (autoplay && !animating) { setAutoplayTimer(); }
3129
+ }
3130
+
3131
+ // === RESIZE FUNCTIONS === //
3132
+ // (slidePositions, index, items) => vertical_conentWrapper.height
3133
+ function updateContentWrapperHeight () {
3134
+ var wp = middleWrapper ? middleWrapper : innerWrapper;
3135
+ wp.style.height = slidePositions[index + items] - slidePositions[index] + 'px';
3136
+ }
3137
+
3138
+ function getPages () {
3139
+ var rough = fixedWidth ? (fixedWidth + gutter) * slideCount / viewport : slideCount / items;
3140
+ return Math.min(Math.ceil(rough), slideCount);
3141
+ }
3142
+
3143
+ /*
3144
+ * 1. update visible nav items list
3145
+ * 2. add "hidden" attributes to previous visible nav items
3146
+ * 3. remove "hidden" attrubutes to new visible nav items
3147
+ */
3148
+ function updateNavVisibility () {
3149
+ if (!nav || navAsThumbnails) { return; }
3150
+
3151
+ if (pages !== pagesCached) {
3152
+ var min = pagesCached,
3153
+ max = pages,
3154
+ fn = showElement;
3155
+
3156
+ if (pagesCached > pages) {
3157
+ min = pages;
3158
+ max = pagesCached;
3159
+ fn = hideElement;
3160
+ }
3161
+
3162
+ while (min < max) {
3163
+ fn(navItems[min]);
3164
+ min++;
3165
+ }
3166
+
3167
+ // cache pages
3168
+ pagesCached = pages;
3169
+ }
3170
+ }
3171
+
3172
+ function info (e) {
3173
+ return {
3174
+ container: container,
3175
+ slideItems: slideItems,
3176
+ navContainer: navContainer,
3177
+ navItems: navItems,
3178
+ controlsContainer: controlsContainer,
3179
+ hasControls: hasControls,
3180
+ prevButton: prevButton,
3181
+ nextButton: nextButton,
3182
+ items: items,
3183
+ slideBy: slideBy,
3184
+ cloneCount: cloneCount,
3185
+ slideCount: slideCount,
3186
+ slideCountNew: slideCountNew,
3187
+ index: index,
3188
+ indexCached: indexCached,
3189
+ displayIndex: getCurrentSlide(),
3190
+ navCurrentIndex: navCurrentIndex,
3191
+ navCurrentIndexCached: navCurrentIndexCached,
3192
+ pages: pages,
3193
+ pagesCached: pagesCached,
3194
+ sheet: sheet,
3195
+ isOn: isOn,
3196
+ event: e || {},
3197
+ };
3198
+ }
3199
+
3200
+ return {
3201
+ version: '2.9.3',
3202
+ getInfo: info,
3203
+ events: events,
3204
+ goTo: goTo,
3205
+ play: play,
3206
+ pause: pause,
3207
+ isOn: isOn,
3208
+ updateSliderHeight: updateInnerWrapperHeight,
3209
+ refresh: initSliderTransform,
3210
+ destroy: destroy,
3211
+ rebuild: function() {
3212
+ return tns(extend(options, optionsElements));
3213
+ }
3214
+ };
3215
+ };
3216
+
3217
+ return tns;
3218
+ })();