PageSourceSearch

https://www.qelbree.com/themes/custom/qelbree_rebrand_theme/dist/js/components/video-carousel.js?tm6yu2

js qelbree.com collected 2026-10-04 14:57:18 UTC 4,630 bytes, 156 lines download raw bytes

1/**
2 * @file
3 * video-carousel.js
4 *
5 * Behavior for the Video Carousel component (.video-carousel).
6 * Follows the Slick + jQuery pattern established in the theme's carousel.js.
7 *
8 * Slick is initialized on .field__items — each .field__item becomes a slide.
9 */
10
11(function ($, Drupal) {
12  'use strict';
13
14  Drupal.behaviors.videoCarousel = {
15    attach(context) {
16      $('.video-carousel', context).each((i, el) => {
17        if ($(el).data('vc-initialized')) return;
18        $(el).data('vc-initialized', true);
19        new VideoCarousel($(el));
20      });
21    }
22  };
23
24  class VideoCarousel {
25    constructor($el) {
26      this.$el          = $el;
27      this.$track       = $el.find('.field--name-field-video-carousel-cards .field__items');
28      this.$prev        = $el.find('.video-carousel__prev');
29      this.$next        = $el.find('.video-carousel__next');
30      this.$counter     = $el.find('.video-carousel__counter');
31      this.$progressBar = $el.find('.video-carousel__progress-bar');
32
33      if (!this.$track.length) {
34        console.warn('[VideoCarousel] Track element not found.', $el);
35        return;
36      }
37
38      this.initSlick();
39      this.initModal();
40    }
41
42    // Update "X of Y" counter and progress bar fill.
43    updateFooter(currentIndex, slick) {
44      const total = slick.slideCount;
45      const visible = slick.options.slidesToShow;
46      const pages = Math.max(1, total - visible + 1);
47      const currentPage = Math.min(currentIndex + 1, pages);
48
49      this.$counter.text(currentPage + ' of ' + pages);
50      this.$progressBar.css('width', (currentPage / pages * 100) + '%');
51
52      this.$el.toggleClass('video-carousel--single-page', pages === 1);
53    }
54
55    // Slick carousel.
56    initSlick() {
57      this.$track.on('init', (event, slick) => {
58        this.updateFooter(0, slick);
59      });
60
61      this.$track.on('beforeChange', (event, slick, currentSlide, nextSlide) => {
62        this.updateFooter(nextSlide, slick);
63      });
64
65      this.$track.slick({
66        slidesToShow:   3,
67        slidesToScroll: 1,
68        infinite:       false,
69        dots:           false,
70        arrows:         true,
71        speed:          600,
72        accessibility:  true,
73        focusOnChange:  false,
74        prevArrow:      this.$prev,
75        nextArrow:      this.$next,
76        responsive: [
77          {
78            breakpoint: 1024,
79            settings: {
80              slidesToShow: 2,
81            }
82          },
83          {
84            breakpoint: 768,
85            settings: {
86              slidesToShow:  1,
87              centerMode:    true,
88              centerPadding: '10%',
89            }
90          }
91        ]
92      });
93    }
94
95    // Modal / lightbox.
96    initModal() {
97      this.$modal = $(this.buildModal());
98      $('body').append(this.$modal);
99
100      const $content = this.$modal.find('.vc-modal__content');
101      const $close   = this.$modal.find('.vc-modal__close');
102
103      this.$el.on('click', '.video-carousel-card__play', (e) => {
104        const $card       = $(e.currentTarget).closest('.video-carousel-card');
105        const $videoField = $card.find('.video-carousel-card__video');
106        if (!$videoField.length) return;
107
108        const $clone = $videoField.clone().removeAttr('hidden').css({
109          position: 'static',
110          width:    '100%',
111          height:   '100%'
112        });
113
114        $content.empty().append($clone);
115        this.openModal();
116      });
117
118      $close.on('click', () => this.closeModal());
119      this.$modal.on('click', (e) => {
120        if ($(e.target).is(this.$modal)) this.closeModal();
121      });
122
123      $(document).on('keydown.vcModal', (e) => {
124        if (e.key === 'Escape' && this.$modal.attr('aria-hidden') === 'false') {
125          this.closeModal();
126        }
127      });
128    }
129
130    openModal() {
131      this.$modal.attr('aria-hidden', 'false').addClass('is-open');
132      $('body').css('overflow', 'hidden');
133      this.$modal.find('.vc-modal__close').trigger('focus');
134    }
135
136    closeModal() {
137      this.$modal.attr('aria-hidden', 'true').removeClass('is-open');
138      $('body').css('overflow', '');
139      this.$modal.find('.vc-modal__content').empty();
140    }
141
142    buildModal() {
143      return `
144        <div class="vc-modal" aria-hidden="true" role="dialog" aria-modal="true" aria-label="${Drupal.t('Video player')}">
145          <div class="vc-modal__inner">
146            <button class="vc-modal__close" aria-label="${Drupal.t('Close video')}">
147              <span aria-hidden="true">&times;</span>
148            </button>
149            <div class="vc-modal__content"></div>
150          </div>
151        </div>
152      `;
153    }
154  }
155
156})(jQuery, Drupal);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.