PageSourceSearch

https://www.epogen.com/-/media/Base-Themes/Components-Theme/Scripts/component-carousel.js

js epogen.com collected 2026-09-25 00:31:24 UTC 55,636 bytes, 2,017 lines download raw bytes

1/**
2 * XAContext
3 *
4 * XA.component.carousels module.
5 *
6 * Exposes register (selector, options) method that initializes Carousel
7 * for each element that matches the provided selector, with its given options.
8 * Element used as a carousel must contain a <ul class="slides"> tag.
9 * That tag must be filled with <li> elements. Each <li> element will be considered as a slide.
10 * For more details read the documentation of the register(selector, options) method.
11 *
12 * @requires jQuery JavaScript Library v1.4.4 or higher
13 */
14/* global Hammer:true */
15XA.component.carousels = (function ($) {
16  var pub = {},
17    NavigationBuilder = null,
18    SliderInitializer = null;
19
20  /**
21   * Timer class saves time stamp and counts time difference between
22   * current time (update() method) and saved time stamp.
23   * @constructor
24   */
25  function Timer() {
26    /**
27     * Time difference between last update() call and last set() call
28     *
29     * @member Timer
30     */
31    this.elapsed = 0;
32    /**
33     * Time stamp
34     *
35     * @member Timer
36     */
37    this.stamp = null;
38  }
39
40  /**
41   *  Counts time difference between current time and saved time stamp
42   *
43   * @member Timer
44   */
45  Timer.prototype.update = function () {
46    if (this.stamp !== null) {
47      this.elapsed += this.newStamp() - this.stamp;
48    }
49  };
50
51  /**
52   *  Saves new time stamp
53   *
54   * @member Timer
55   */
56  Timer.prototype.set = function () {
57    this.stamp = this.newStamp();
58  };
59
60  /**
61   *  Counts time difference between current time and saved time stamp
62   *
63   * @member Timer
64   * @return stamp of a current time
65   * @type Number
66   */
67  Timer.prototype.newStamp = function () {
68    return new Date().valueOf();
69  };
70
71  /**
72   *  Resets elapsed time.
73   *
74   * @member Timer
75   */
76  Timer.prototype.reset = function () {
77    this.elapsed = 0;
78  };
79  /**
80   * End of Timer class
81   */
82
83  /**
84   * TimeIndicator class extends Timer class
85   * @constructor
86   * @param {Object} $     a jQuery object
87   * @param {XA.component.carousels.IndicatorViews.View} view  a time indicator view.
88   * @param {Number} timeout   a view timeout
89   * @base Timer
90   */
91  function TimeIndicator($, view, timeout) {
92    /**
93     * A time indicator view.
94     *
95     * @member TimerIndicator
96     */
97    this.view = view;
98
99    this.view.init(timeout);
100  }
101  TimeIndicator.prototype = new Timer();
102  TimeIndicator.constructor = Timer;
103
104  /**
105   * Plays the attached view.
106   *
107   * @member TimerIndicator
108   */
109  TimeIndicator.prototype.play = function () {
110    this.view.play();
111  };
112
113  /**
114   * Pauses the attached view.
115   *
116   * @member TimerIndicator
117   */
118  TimeIndicator.prototype.pause = function () {
119    this.view.pause();
120  };
121
122  /**
123   * Resets elapsed time and resets the attached view.
124   *
125   * @member TimerIndicator
126   */
127  TimeIndicator.prototype.reset = function () {
128    Timer.prototype.reset.call(this);
129    this.view.reset();
130  };
131
132  /**
133   * Counts time difference between current time and saved time stamp
134   * and updates the attached view
135   *
136   * @member TimerIndicator
137   */
138  TimeIndicator.prototype.update = function () {
139    Timer.prototype.update.call(this);
140    this.view.update(this.elapsed);
141  };
142  /**
143   * end of TimeIndicator class
144   */
145
146  /**
147   * NavigationBuilder object creates navigation items
148   */
149  NavigationBuilder = (function ($) {
150    function createItem(settings) {
151      var text = settings.text,
152        $navItem = null;
153
154      settings.text = "";
155      settings.href = "#";
156
157      $navItem = $("<a>", settings);
158      $("<span>", {
159        text: text,
160      }).appendTo($navItem);
161
162      return $navItem;
163    }
164
165    return {
166      /**
167       * Returns the element(s) that match the provided selector or null
168       *
169       * @param {String} selector a selector used for searching
170       * @return a jQuery object containing element(s) that match the provided selector or null if there is no element match
171       * @type Object
172       */
173      getElement: function (selector) {
174        var $element = $(selector);
175
176        if ($element.length === 0) {
177          $element = null;
178        }
179
180        return $element;
181      },
182      /**
183       * Creates navigation items and appends them to $container.
184       *
185       * @param {Object} $container   navigation items container (jQuery object)
186       * @param {String} label   navigation items label template
187       * @param {Number} count    number of navigation items to create
188       */
189      createItems: function ($container, label, count) {
190        var i,
191          pattern = /#\{index\}/g,
192          settings;
193
194        for (i = 0; i < count; i++) {
195          settings = {
196            text: label.replace(pattern, i + 1),
197          };
198          createItem(settings).appendTo($container);
199        }
200      },
201      /**
202       * Creates or selects 'previous'/'next' navigation item.
203       * Mode of action depends on settings object:
204       * var settings = {
205       *     isContainer: Boolean value,
206       *     label: String value,
207       *     selector: String value,
208       *     method: String value
209       * };
210       *
211       * If settings.selector === null or if settings.selector !== null and settings.isContainer == true
212       * navigation item is created and attached to $element.first() (using settings.method).
213       * If settings.selector !== null and settings.isContainer == false
214       * $element.first() is selected as navigation item
215       *
216       * @param {Object} settings settings of navigation item
217       * @param {Object} itemSettings   html attributes of navigation item
218       * @param {Object} $element    navigation items container (jQuery object)
219       * @return The 'prev'/'next' navigation item
220       * @type Object
221       */
222      createPrevNextItem: function (settings, itemSettings, $element) {
223        var $item = null,
224          isSelectorExists = settings.selector !== null;
225
226        if (isSelectorExists) {
227          $element = this.getElement(settings.selector);
228        }
229
230        if ($element !== null) {
231          if (settings.isContainer || !isSelectorExists) {
232            $item = createItem(
233              $.extend(
234                {},
235                {
236                  text: settings.label,
237                },
238                itemSettings
239              )
240            );
241            $element.first()[settings.method]($item);
242          } else {
243            $item = $element.first();
244          }
245        }
246
247        return $item;
248      },
249    };
250  })($);
251  /**
252   * end of NavigationBuilder object
253   */
254
255  /**
256   * Navigation class that contains navigation items.
257   * @constructor
258   */
259  function Navigation($, $wrapper, options) {
260    var defaults = {
261        isEnabled: true,
262        hasPrevNextItems: true,
263        item: {
264          label: "#{index}",
265          selector: null,
266        },
267        prevItem: {
268          label: "<",
269          selector: null,
270          isContainer: false,
271        },
272        nextItem: {
273          label: ">",
274          selector: null,
275          isContainer: false,
276        },
277        slidesCount: 0,
278      },
279      settings = $.extend(true, {}, defaults, options);
280
281    /**
282     * jQuery object
283     *
284     * @member Navigation
285     */
286    this.$ = $;
287    /**
288     * Navigation items (one for each slide if navigation is enabled).
289     *
290     * @member Navigation
291     */
292    this.$items = $();
293    /**
294     * Navigation 'next' item (if prev/next items are enabled).
295     *
296     * @member Navigation
297     */
298    this.$nextItem = $();
299    /**
300     * Navigation 'previous' item (if prev/next items are enabled).
301     *
302     * @member Navigation
303     */
304    this.$prevItem = $();
305    /**
306     * Selected item (one from this.$items or null).
307     *
308     * @member Navigation
309     */
310    this.$selectedItem = null;
311
312    //initialization
313    if (settings.isEnabled) {
314      var self = this;
315      setTimeout(function () {
316        self.prepareItems(settings.item, settings.slidesCount, $wrapper);
317        //select first item on init
318        self.selectItem(0);
319      }, 1000);
320
321      $wrapper.find(".nav").trigger("navigation-created", this);
322    }
323  }
324
325  /**
326   * CSS class used to mark selected item.
327   *
328   * @member Navigation
329   */
330  Navigation.prototype.selectedItemClass = "active";
331
332  /**
333   * Mark navigation item with given index as selected.
334   *
335   * @member Navigation
336   * @param {Number} index     index of navigation item
337   */
338  Navigation.prototype.selectItem = function (index) {
339    if (this.$selectedItem !== null) {
340      this.$selectedItem.removeClass(this.selectedItemClass);
341    }
342    this.$selectedItem = this.$items.eq(index).addClass(this.selectedItemClass);
343  };
344
345  /**
346   * Creates or selects navigation items container and fills it with navigation items.
347   * If navigation items container is not empty, child elements are adopted as navigation items.
348   * Mode of action depends on settings object:
349   * var settings = {
350   *     'label': '#{index}',
351   *     'selector': null
352   * };
353   * If settings.selector === null navigation item container is created.
354   * If settings.selector !== null element that match settings.selector is adapted as navigagation items container.
355   *
356   * @member Navigation
357   * @param {Object} settings settings of navigation items
358   * @param {Number} count   number of navigation items
359   * @param {Object} $wrapper    wrapper of navigation items container (jQuery object)
360   * @return The container filled with navigation items.
361   * @type Object
362   */
363  Navigation.prototype.prepareItems = function (settings, count, $wrapper) {
364    var $container = null;
365
366    if (settings.selector) {
367      $container = NavigationBuilder.getElement(settings.selector);
368    } else {
369      $container = $("<div/>", {
370        class: "nav",
371      });
372      $container.appendTo($wrapper);
373    }
374
375    if ($container) {
376      if ($container.children().length === 0) {
377        NavigationBuilder.createItems($container, settings.label, count);
378      }
379
380      this.$items = $container.children().slice(0, count);
381    }
382
383    return $container;
384  };
385
386  /**
387   * Creates or selects navigation 'previous' and 'next' navigation items (@see NavigationBuilder.createPrevNextItem()).
388   * If navigation items container is not empty, child elements are adopted as navigation items.
389   *
390   * @member Navigation
391   * @param {Object} settings settings of navigation
392   * @param {Object} $container    navigation items container (jQuery object)
393   */
394  Navigation.prototype.preparePrevNextItems = function (settings, $container) {
395    var $item;
396
397    settings.prevItem.method = "prepend";
398    $item = NavigationBuilder.createPrevNextItem(
399      settings.prevItem,
400      {
401        class: "prev",
402      },
403      $container
404    );
405    this.$prevItem = $item === null ? this.$prevItem : $item;
406
407    settings.nextItem.method = "append";
408    $item = NavigationBuilder.createPrevNextItem(
409      settings.nextItem,
410      {
411        class: "next",
412      },
413      $container
414    );
415    this.$nextItem = $item === null ? this.$nextItem : $item;
416  };
417  /**
418   * end of Navigation class
419   */
420
421  /**
422   * class Swiper you can define horizontal swipe for chosing element and set behavior of swipe(left/right).
423   * You can extend this class with vertical swipe if it needs
424   * @param target Element or query of querySelector
425   */
426  function Swiper(target) {
427    let touchStartX = 0,
428      touchStartY = 0,
429      touchEndX = 0,
430      touchEndY = 0,
431      threshold = 50;
432
433    let onSwipeLeftAgent = null,
434      onSwipeRightAgent = null,
435      element = null;
436
437    element =
438      target instanceof EventTarget ? target : document.querySelector(target);
439
440    onTouchStart = (event) => {
441      touchStartX = event.changedTouches[0].screenX;
442      touchStartY = event.changedTouches[0].screenY;
443    };
444
445    onTouchEnd = (event) => {
446      touchEndX = event.changedTouches[0].screenX;
447      touchEndY = event.changedTouches[0].screenY;
448      handleGesture(event);
449    };
450
451    onMouseDown = (event) => {
452      touchStartX = event.screenX;
453      touchStartY = event.screenY;
454    };
455
456    onMouseUp = (event) => {
457      touchEndX = event.screenX;
458      touchEndY = event.screenY;
459      handleGesture(event);
460    };
461
462    element.addEventListener("touchstart", onTouchStart, false);
463
464    element.addEventListener("touchend", onTouchEnd, false);
465
466    element.addEventListener("mousedown", onMouseDown, false);
467
468    element.addEventListener("mouseup", onMouseUp, false);
469
470    /**
471     * method which defines swipe left
472     * @param callback func
473     */
474    this.onSwipeLeft = (callback) => {
475      onSwipeLeftAgent = (event) => callback.call(this, event);
476    };
477
478    /**
479     * method which defines swipe right
480     * @param callback
481     */
482    this.onSwipeRight = (callback) => {
483      onSwipeRightAgent = (event) => callback.call(this, event);
484    };
485
486    this.destroy = () => {
487      element.removeEventListener("touchstart", onTouchStart);
488      element.removeEventListener("touchend", onTouchEnd);
489      element.removeEventListener("mousedown", onMouseDown);
490      element.removeEventListener("mouseup", onMouseUp);
491    };
492
493    function handleGesture(event) {
494      /**
495       * swiped left
496       */
497      if (touchEndX + threshold <= touchStartX) {
498        onSwipeLeftAgent && onSwipeLeftAgent(event);
499      }
500
501      /**
502       * swiped right
503       */
504      if (touchEndX - threshold >= touchStartX) {
505        onSwipeRightAgent && onSwipeRightAgent(event);
506      }
507    }
508  }
509
510  /**
511   * SliderContext class is binded to a Slider class instance
512   * and contains its properties.
513   * @constructor
514   */
515  function SliderContext() {
516    /**
517     * Flag that determines if slide can or cannot be changed (for user request)
518     *
519     * @member SliderContext
520     */
521    this.canChangeSlide = true;
522    /**
523     * Default Slider setting object (@see Slider)
524     *
525     * @member SliderContext
526     */
527    this.defaults = null;
528    /**
529     * Navigation object (@see Navigation)
530     *
531     * @member SliderContext
532     */
533    this.navigation = null;
534    /**
535     * Slider object that this context is attached to.
536     *
537     * @member SliderContext
538     */
539    this.owner = null;
540    /**
541     * Flag that determines if change of a slide can or can not be scheduled.
542     *
543     * @member SliderContext
544     */
545    this.preventScheduling = false;
546    /**
547     * Slider setting object
548     *
549     * @member SliderContext
550     */
551    this.settings = null;
552    /**
553     * Instance of Timer class (@see Timer) that is used to store how much
554     * time is left to change the slide.
555     *
556     * @member SliderContext
557     */
558    this.slideTimer = new Timer();
559    /**
560     * Instance of TimeIndicator class (@see TimeIndicator)
561     *
562     * @member SliderContext
563     */
564    this.timeIndicator = null;
565    /**
566     * Timers array.
567     *
568     * @member SliderContext
569     */
570    this.timers = [];
571    /**
572     * Last timeout identifier.
573     *
574     * @member SliderContext
575     */
576    this.timeoutId = null;
577    /**
578     * Transition (@see XA.component.carousels.Transitions.Transition) used while changing slides
579     *
580     * @member SliderContext
581     */
582    this.transition = null;
583    /**
584     * Transition settings object (@see XA.component.carousels.Transitions.TransitionSettings)
585     *
586     * @member SliderContext
587     */
588    this.transitionSettings =
589      new XA.component.carousels.Transitions.TransitionSettings();
590    /**
591     * jQuery object that contains all slides
592     *
593     * @member SliderContext
594     */
595    this.$slides = null;
596    /**
597     * jQuery object that contains the element that wraps slides
598     *
599     * @member SliderContext
600     */
601    this.$wrapper = null;
602    /**
603     * Reference to method that should change current slide
604     *
605     * @member SliderContext
606     */
607    this.changeCurrentSlide = null;
608    /**
609     * Reference to method that should return jQuery object with current slide
610     *
611     * @member SliderContext
612     */
613    this.getCurrentSlide = null;
614
615    this.timers.push(this.slideTimer);
616  }
617  /**
618   * end of SliderContext class
619   */
620
621  /**
622   * Slider class manages changing of slides.
623   *
624   * Options object:
625   * {
626   *     'navigation': Object value, //navigation settings object
627   *     'timeout': Number value, //time between slides changing
628   *     'transition': String value, //name of transition class (@see XA.component.carousels.Transitions.Transition)
629   *     'isPauseEnabled': Boolean value, //flag that determines if pause feature is enabled
630   *     'timeIndicator': {
631   *         'isEnabled': Boolean value, //flag that determines if time indicator is enabled
632   *         'selector': String value, //selector of time indicator html element
633   *         'view': String value, //name of view class (@see XA.component.carousels.IndicatorViews.View
634   *         'options': Object value //object with options that will be passed to a time indicator view
635   *     }
636   * }
637   * @constructor
638   * @param {Object} $     the jQuery object
639   * @param {SliderContext} context    instance of SliderContext class; must be separate for each instance (@see SliderContext)
640   * @param {Object} options    Slider options object
641   */
642  function Slider($, context, options) {
643    var defaults = {
644      navigation: {},
645      timeout: 10000,
646      transition: "BasicTransition",
647      isPauseEnabled: true,
648      timeIndicator: {
649        isEnabled: false,
650        selector: null,
651        view: "View",
652        options: {},
653      },
654    };
655
656    /**
657     * Instance of SliderContext class (@see SliderContext)
658     *
659     * @member Navigation
660     */
661    this.context = context;
662
663    context.owner = this;
664    context.defaults = defaults;
665    context.settings = $.extend(true, {}, defaults, options);
666    context.transitionSettings.$slides = context.$slides;
667  }
668
669  /**
670   * Executes method with given methodName on each Timer in context.timers (@see Timer)
671   *
672   * @member Slider
673   * @param {String} methodName   name of a method that will be executed on timers
674   * @param {SliderContext} context    current context
675   */
676  Slider.prototype.executeOnTimers = function (methodName, context) {
677    var i = null,
678      timer = null,
679      timers = context.timers;
680
681    for (i = 0; i < timers.length; i++) {
682      timer = timers[i];
683      if (typeof timer[methodName] === "function") {
684        timer[methodName]();
685      }
686    }
687  };
688
689  /**
690   * Deschedules change of a slide, by clearing timeout previously set by setTimeout() function.
691   *
692   * @member Slider
693   * @param {SliderContext} context    current context
694   */
695  Slider.prototype.descheduleSlide = function (context) {
696    if (context.timeoutId !== null) {
697      clearTimeout(context.timeoutId);
698      context.timeoutId = null;
699    }
700  };
701
702  /**
703   * Schedules change of a slide, by setting timeout (using setTimeout() function) if it can.
704   * If cannot, it tries again after short period of time.
705   *
706   * @member Slider
707   * @param {SliderContext} context    current context
708   */
709  Slider.prototype.scheduleSlide = function (context) {
710    var owner = context.owner;
711
712    owner.descheduleSlide(context);
713
714    if (!context.preventScheduling && context.$slides.length > 1) {
715      owner.executeOnTimers("set", context);
716
717      if (context.canChangeSlide) {
718        if (context.timeIndicator !== null) {
719          context.timeIndicator.play();
720        }
721        context.timeoutId = setTimeout(function () {
722          owner.changeCurrentSlideBy(1, context);
723        }, context.settings.timeout - context.slideTimer.elapsed);
724      } else {
725        setTimeout(function () {
726          owner.scheduleSlide(context);
727        }, 100);
728      }
729    }
730  };
731
732  /**
733   * Changes current slide by offset performing context.transition
734   * Any offset value is save (if it is an integer).
735   *
736   * @member Slider
737   * @param {Number} offset    the offset to an index of a new slide
738   * @param {SliderContext} context    current context
739   */
740  Slider.prototype.changeCurrentSlideBy = function (offset, context) {
741    var settings = context.transitionSettings,
742      $currentSlide = context.getCurrentSlide(),
743      $slides = context.$slides;
744
745    if (offset % $slides.length !== 0) {
746      context.canChangeSlide = false;
747
748      settings.offset = offset;
749      settings.$currentSlide = $currentSlide;
750      settings.$nextSlide = $slides.eq(
751        ($currentSlide.index() + offset) % $slides.length
752      );
753
754      context.changeCurrentSlide(settings.$nextSlide);
755      context.transition.perform(context.transitionSettings, offset);
756      context.owner.executeOnTimers("reset", context);
757    }
758
759    var $carousel = $(context.$wrapper).parents(".carousel");
760    $carousel.trigger("slide-changed");
761  };
762
763  /**
764   * Starts automatic slide changing
765   *
766   * @member Slider
767   */
768  Slider.prototype.run = function () {
769    this.executeOnTimers("reset", this.context);
770    this.scheduleSlide(this.context);
771  };
772
773  /**
774   * Cancel the default action of event and changes slides manually.
775   *
776   * @member Slider
777   * @param {Object} event     the event
778   * @param {Number} offset    the offset to an index of a new slide
779   * @param {SliderContext} context    current context
780   */
781  Slider.prototype.onChangeCurrentSlide = function (event, offset, context) {
782    var owner = context.owner;
783
784    event.preventDefault();
785    if (context.canChangeSlide) {
786      owner.descheduleSlide(context);
787      owner.changeCurrentSlideBy(offset, context);
788    }
789  };
790
791  /**
792   * Selects navigation item with given index.
793   *
794   * @member Slider
795   * @param {Number} index     the index of navigation item
796   */
797  Slider.prototype.selectNavigationItem = function (index) {
798    this.context.navigation.selectItem(index);
799  };
800  /**
801   * end of Slider class
802   */
803
804  /**
805   * SliderInitializer object sets up instances of Slider class
806   */
807  SliderInitializer = (function ($) {
808    var DIRECTION = {
809      PREV: -1,
810      NEXT: 1,
811    };
812
813    /**
814     * Attaches events to navigation items.
815     *
816     * @param {SliderContext} context   a context of a Slider class instance
817     */
818    function attachNavigationEvents(context) {
819      var owner = context.owner,
820        $navLinks = context.$wrapper.find(".nav-items"),
821        $prevItemTxt = context.$wrapper.find(".prev-text"),
822        $nextItemTxt = context.$wrapper.find(".next-text"),
823        $carousel = context.$wrapper.parents(".carousel");
824
825      context.navigation.$items.each(function (index) {
826        var $slide = context.$slides.eq(index);
827
828        $(this).click(function (event) {
829          owner.onChangeCurrentSlide(
830            event,
831            $slide.index() - context.getCurrentSlide().index(),
832            context
833          );
834        });
835      });
836
837      $prevItemTxt.click(function (event) {
838        switchSlide(event, context, DIRECTION.PREV);
839      });
840
841      $nextItemTxt.click(function (event) {
842        switchSlide(event, context, DIRECTION.NEXT);
843      });
844
845      context.navigation.$prevItem.click(function (event) {
846        switchSlide(event, context, DIRECTION.PREV);
847      });
848
849      context.navigation.$nextItem.click(function (event) {
850        switchSlide(event, context, DIRECTION.NEXT);
851      });
852
853      $navLinks.on("keydown", function (event) {
854        switch (event.keyCode) {
855          case 37: //Letf arrow
856            switchSlide(event, context, DIRECTION.PREV);
857            $(this).parent().find(".active").focus();
858            break;
859          case 39: //Right arrow
860            switchSlide(event, context, DIRECTION.NEXT);
861            $(this).parent().find(".active").focus();
862            break;
863          case 13: //Enter
864            focusFirstFocusableElement(owner.context.getCurrentSlide());
865        }
866      });
867
868      $carousel.on("slide-changed", function (event) {
869        var $activeLink = $navLinks.find(".active");
870        $activeLink.attr("tabindex", "0");
871
872        if ($navLinks.find(document.activeElement).length) {
873          $activeLink.focus({ preventScroll: true });
874        }
875
876        $navLinks.find(">div").not(".active").attr("tabindex", "-1");
877      });
878    }
879
880    /**
881     * Switches slides back and foreward
882     *
883     * @param {Event} event              an event that caused switch slide action
884     * @param {SliderContext} context    a context of a Slider class instance
885     * @param {number} direction         switch slide direction. -1 - switch to prev slide. 1 - switch to the next slide
886     */
887    function switchSlide(event, context, direction) {
888      var owner = context.owner,
889        navLinks$ = context.$wrapper.find(".nav-items");
890
891      navLinks$.find(".active").attr("tabindex", "-1");
892      owner.onChangeCurrentSlide(event, direction, context);
893      navLinks$.find(".active").attr("tabindex", "0");
894    }
895
896    /**
897     * Focus the first focusable element inside container.
898     *
899     * @param {Container} container  a container element where the first focusable element will be focused
900     */
901    function focusFirstFocusableElement(container) {
902      var focusableElementsString =
903        'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]';
904
905      // Finds all focusable children
906      var focusableElements = container.find(focusableElementsString);
907
908      // If there are focusable elements - select the first one.
909      if (focusableElements.length !== 0) {
910        var firstTabStop = focusableElements[0];
911
912        // Focus first
913        firstTabStop.focus();
914      }
915    }
916
917    /**
918     * Attaches pause event to navigation wrapper.
919     *
920     * @param {SliderContext} context   a context of a Slider class instance
921     */
922    function attachPauseEvent(context) {
923      var owner = context.owner,
924        $element = context.$wrapper,
925        $navLinks = $element.find(".nav a");
926
927      var callbackMouseenter = function () {
928        context.preventScheduling = true;
929        owner.descheduleSlide(context);
930
931        owner.executeOnTimers("update", context);
932        if (context.timeIndicator !== null) {
933          context.timeIndicator.pause();
934        }
935      };
936
937      $element.mouseenter(callbackMouseenter);
938      $navLinks.on("focus", callbackMouseenter);
939
940      var callbackMouseLeave = function () {
941        context.preventScheduling = false;
942        owner.scheduleSlide(context);
943      };
944
945      $element.mouseleave(callbackMouseLeave);
946      $navLinks.on("blur", callbackMouseLeave);
947    }
948
949    /**
950     * Creates time indicator view
951     *
952     * @param {SliderContext} context   a context of a Slider class instance
953     */
954    function createIndicator(context) {
955      var settings = context.settings,
956        timeIndicator = null,
957        ViewConstructor =
958          XA.component.carousels.IndicatorViews[settings.timeIndicator.view],
959        view = null;
960
961      if (ViewConstructor !== null) {
962        view = new ViewConstructor(
963          settings.timeIndicator.selector,
964          settings.timeIndicator.options
965        );
966        timeIndicator = new TimeIndicator($, view, settings.timeout);
967        context.timeIndicator = timeIndicator;
968        context.timers.push(timeIndicator);
969      }
970    }
971
972    /**
973     * Attaches events to navigation.
974     *
975     * @param {SliderContext} context   a context of a Slider class instance
976     */
977    function initializeNavigation(context) {
978      context.settings.navigation.slidesCount = context.$slides.length;
979      context.navigation = new Navigation(
980        $,
981        context.$wrapper,
982        context.settings.navigation
983      );
984
985      setTimeout(function () {
986        attachNavigationEvents(context);
987      }, 1000);
988
989      if (context.settings.isPauseEnabled) {
990        attachPauseEvent(context);
991      }
992    }
993
994    /**
995     * Initializes transition for a given context.
996     *
997     * @param {SliderContext} context   a context of a Slider class instance
998     */
999    function initializeTransition(context) {
1000      var TransitionConstructor = null,
1001        owner = context.owner;
1002
1003      context.transitionSettings.callback = function () {
1004        context.canChangeSlide = true;
1005
1006        if (context.preventScheduling) {
1007          owner.executeOnTimers("reset", context);
1008        } else {
1009          owner.scheduleSlide(context);
1010        }
1011      };
1012
1013      TransitionConstructor =
1014        XA.component.carousels.Transitions[context.settings.transition];
1015      if (
1016        TransitionConstructor === null ||
1017        TransitionConstructor === undefined
1018      ) {
1019        TransitionConstructor =
1020          XA.component.carousels.Transitions[context.defaults.transition];
1021      }
1022      context.transition = new TransitionConstructor();
1023      context.transition.init(context.transitionSettings);
1024    }
1025
1026    return {
1027      /**
1028       * Initializes Slider object with a given context.
1029       *
1030       * @param {SliderContext} context   a context of a Slider class instance
1031       */
1032      initialize: function (context) {
1033        initializeNavigation(context);
1034        initializeTransition(context);
1035
1036        if (context.settings.timeIndicator.isEnabled) {
1037          createIndicator(context);
1038        }
1039      },
1040    };
1041  })($);
1042  /**
1043   * end of SliderInitializer
1044   */
1045
1046  /**
1047   * Carousel class displays one slide (some content) at the time.
1048   * Its behavior depends on settings (passed to init() method).
1049   * For more details @see Slider, @see SliderInitializer, @see Navigation,
1050   * @see NavigationBuilder, @see XA.component.carousels.Transitions.Transition,
1051   * @see XA.component.carousels.IndicatorViews.View
1052   *
1053   * @constructor
1054   */
1055  function Carousel() {
1056    var context = this;
1057
1058    /**
1059     * Instance of a Slider class (@see Slider).
1060     *
1061     * @member Carousel
1062     */
1063    this.slider = null;
1064    /**
1065     * Displayed slide (jQuery object).
1066     *
1067     * @member Carousel
1068     */
1069    this.$currentSlide = null;
1070    /**
1071     * All slides (jQuery object).
1072     *
1073     * @member Carousel
1074     */
1075    this.$slides = null;
1076
1077    /**
1078     * Changes current slide to a $newSlide.
1079     *
1080     * @member Carousel
1081     * @param {Object} $newSlide  the new slide (jQuery) object.
1082     */
1083    this.changeCurrentSlide = function ($newSlide) {
1084      context.$currentSlide = $newSlide;
1085      context.slider.selectNavigationItem($newSlide.index());
1086    };
1087
1088    /**
1089     * Returns current slide.
1090     *
1091     * @member Carousel
1092     * @return current slide (jQuery) object.
1093     * @type Object
1094     */
1095    this.getCurrentSlide = function () {
1096      return context.$currentSlide;
1097    };
1098
1099    this.data = {};
1100  }
1101
1102  /**
1103   * Resets the carousel.
1104   *
1105   * @member Carousel
1106   */
1107  Carousel.prototype.reset = function () {
1108    this.$slides.each(function () {
1109      $(this).hide();
1110    });
1111
1112    this.changeCurrentSlide(this.$slides.first());
1113    this.$currentSlide.show();
1114  };
1115
1116  /**
1117   * Initializes the carousel.
1118   * Wraps carousel content with <div class="wrapper" /> element,
1119   * selects slides (<li /> elements) from <ul class="slides" /> element,
1120   * initializes Slider class instance, resets the carousel, and starts automatic slides changing.
1121   *
1122   * @member Carousel
1123   * @param {Object} $container     jQuery object with a main html element of the carousel
1124   * @param {Object} options     carousel options object
1125   */
1126  Carousel.prototype.init = function ($container, options) {
1127    var $wrapper = $('.wrapper').length ? $('.wrapper') : $("<div/>", {
1128        class: "wrapper",
1129      }),
1130      sliderContext = null;
1131
1132    if (!$('.wrapper').length) {
1133      $wrapper.append($container.children().detach());
1134      $container.append($wrapper);
1135    }
1136    
1137    this.$slides = $wrapper.find(".slides li.slide");
1138
1139    sliderContext = new SliderContext();
1140    sliderContext.changeCurrentSlide = this.changeCurrentSlide;
1141    sliderContext.getCurrentSlide = this.getCurrentSlide;
1142    sliderContext.$slides = this.$slides;
1143    sliderContext.$wrapper = $wrapper;
1144
1145    this.slider = new Slider($, sliderContext, options);
1146    SliderInitializer.initialize(sliderContext);
1147
1148    this.reset();
1149    this.slider.run();
1150  };
1151  /**
1152   * end of Carousel class
1153   */
1154
1155  /* Carousel touch support */
1156  Carousel.prototype.swipeSlide = function () {
1157    var $wrapper = this.slider.context.$wrapper,
1158      swiper = new Swiper($wrapper[0]);
1159
1160    swiper.onSwipeLeft((e) =>
1161      this.slider.context.owner.onChangeCurrentSlide(e, 1, this.slider.context)
1162    );
1163    swiper.onSwipeRight((e) =>
1164      this.slider.context.owner.onChangeCurrentSlide(e, -1, this.slider.context)
1165    );
1166  };
1167
1168  Carousel.prototype.maxSlideInfoHeight = function () {
1169    var maxHeight = 0,
1170      slideInfo =
1171        this.$slides[0] && this.$slides[0].querySelector(".slide-info");
1172
1173    if (slideInfo) {
1174      maxHeight = slideInfo.offsetHeight;
1175    }
1176    for (var i = 1; i < this.$slides.length; i++) {
1177      var slideInfo = this.$slides[i].querySelector(".slide-info");
1178
1179      if (!slideInfo) {
1180        continue;
1181      }
1182
1183      var currSlideInfoHeight = slideInfo.offsetHeight;
1184
1185      if (maxHeight < currSlideInfoHeight) {
1186        maxHeight = currSlideInfoHeight;
1187      }
1188    }
1189    return maxHeight;
1190  };
1191
1192  Carousel.prototype.resizeWrapper = function () {
1193    var slideInfoHeight = this.maxSlideInfoHeight(),
1194      wrapper = this.slider.context.$wrapper;
1195    wrapper.find(".slide-info").css({
1196      "min-height": slideInfoHeight,
1197    });
1198  };
1199
1200  pub.initInstance = function (component) {
1201    const defaultProperties = {
1202      "timeout": 2147483647,
1203      "isPauseEnabled": true,
1204      "transition": "BasicTransition",
1205  };
1206    var properties = component.data("properties") || defaultProperties,
1207      component = component.find(".carousel-inner"),
1208      id = component.attr("data-id"),
1209      self = component.eq(0),
1210      querySlideIndex = parseInt(
1211        XA.queryString.getQueryParam(
1212          component.closest(".component.carousel").attr("id") || "carousel"
1213        )
1214      ),
1215      validQueryParam =
1216        typeof querySlideIndex !== "undefined" &&
1217        !isNaN(querySlideIndex) &&
1218        querySlideIndex < component.find(".sxa-numbers").length,
1219      context,
1220      settings,
1221      $currentSlide,
1222      $slides;
1223
1224    if (properties) {
1225      if (!properties.navigation) {
1226        properties.navigation = {
1227          item: {},
1228          prevItem: {},
1229          nextItem: {},
1230          isEnabled: validQueryParam ? false : true,
1231        };
1232      }
1233      properties.navigation.item.selector = "[data-id='" + id + "'] .nav-items"; //properties.navigation.item.selector;
1234      properties.navigation.prevItem.selector = "#" + id + " .nav";
1235      properties.navigation.nextItem.selector = "#" + id + " .nav";
1236
1237      var carousel = new Carousel($);
1238      carousel.init(component, properties);
1239
1240      $(document).ready(function () {
1241        carousel.resizeWrapper();
1242      });
1243
1244      if (validQueryParam) {
1245        (context = carousel.slider.context),
1246          (settings = context.transitionSettings),
1247          ($currentSlide = context.getCurrentSlide()),
1248          ($slides = context.$slides);
1249
1250        context.canChangeSlide = false;
1251        settings.$currentSlide = $currentSlide;
1252        settings.$nextSlide = $($slides[querySlideIndex]);
1253
1254        context.changeCurrentSlide(settings.$nextSlide);
1255        context.transition.perform(context.transitionSettings);
1256        context.owner.executeOnTimers("reset", context);
1257
1258        context.navigation.prepareItems(
1259          properties.navigation.item,
1260          $slides.length,
1261          $(context.$wrapper)
1262        );
1263        context.navigation.selectItem(querySlideIndex);
1264      } else {
1265        carousel.swipeSlide();
1266      }
1267    }
1268  };
1269
1270  pub.init = function () {
1271    $(".carousel").each(function () {
1272      pub.initInstance($(this));
1273    });
1274  };
1275
1276  return pub;
1277})(jQuery);
1278/**
1279 * end of XA.component.carousels module.
1280 */
1281
1282XA.register("carousels", XA.component.carousels);
1283
1284XA.component.carousels.Transitions = (function () {
1285  var pub = {};
1286
1287  /**
1288   * TransitionSettings class contains data needed for changing slides
1289   * @constructor
1290   */
1291  function TransitionSettings() {}
1292  /**
1293   * Current slide (currently displayed) jQuery object
1294   *
1295   * @member TransitionSettings
1296   */
1297  TransitionSettings.prototype.$currentSlide = null;
1298  /**
1299   * Next slide jQuery object
1300   *
1301   * @member TransitionSettings
1302   */
1303  TransitionSettings.prototype.$nextSlide = null;
1304  /**
1305   * All slides (jQuery object)
1306   *
1307   * @member TransitionSettings
1308   */
1309  TransitionSettings.prototype.$slides = null;
1310  /**
1311   * Offset between $currentSlide and $nextSlide
1312   *
1313   * @member TransitionSettings
1314   */
1315  TransitionSettings.prototype.offset = 0;
1316  /**
1317   * Callback function that must be called after animation.
1318   *
1319   * @member TransitionSettings
1320   */
1321  TransitionSettings.prototype.callback = null;
1322  /**
1323   * end of TransitionSettings class
1324   */
1325  pub.TransitionSettings = TransitionSettings;
1326
1327  /**
1328   * Transition class is a base class of all transitions.
1329   *
1330   * Important: settings.callback() (@see TransitionSettings) have to be called at the end of execution the perform() method.
1331   *
1332   * @constructor
1333   */
1334  function Transition() {}
1335  Transition.prototype = new Transition();
1336  /**
1337   * Initializes a transition with given settings (@see TransitionSettings).
1338   *
1339   * @member Transition
1340   * @param {TransitionSettings} settings  transition settings
1341   */
1342  /* eslint-disable */
1343  Transition.prototype.init = function (settings) {
1344    //implementation using settings
1345  };
1346  /* eslint-enable */
1347  /**
1348   * Returns a factor associated with settings.offset (@see TransitionSettings) that
1349   * can be usefull for some animations.
1350   *
1351   * @member Transition
1352   * @param {TransitionSettings} settings  transition settings
1353   * @return The factor; 1 if offset is higher than 0, -1 otherwise
1354   * @type Number
1355   */
1356  Transition.prototype.factor = function (settings) {
1357    return settings.offset > 0 ? 1 : -1;
1358  };
1359  /**
1360   * This method is called used by carousels to change slides.
1361   *
1362   * Important: settings.callback() (@see TransitionSettings) have to be called at the end of this method execution.
1363   *
1364   * @member Transition
1365   * @param {TransitionSettings} settings  transition settings
1366   */
1367  Transition.prototype.perform = function (settings) {
1368    settings.callback();
1369  };
1370  /**
1371   * End of Transition class
1372   */
1373  pub.Transition = Transition;
1374
1375  /**
1376   * BasicTransition class changes slides without any animation.
1377   * Do not remove this class, because carousels use it by default,
1378   * if requested transition cannot be found.
1379   * @constructor
1380   */
1381  function BasicTransition() {}
1382  BasicTransition.prototype = new Transition();
1383  BasicTransition.constructor = BasicTransition;
1384  /**
1385   * @member BasicTransition
1386   */
1387  BasicTransition.prototype.perform = function (settings) {
1388    settings.$currentSlide.hide();
1389    settings.$nextSlide.show();
1390
1391    settings.callback();
1392  };
1393  /**
1394   * End of BasicTransition class
1395   */
1396  pub.BasicTransition = BasicTransition;
1397
1398  /**
1399   * FadeInTransition class changes slides by fading in new slide.
1400   * This class can be removed if effect is not used.
1401   * @constructor
1402   */
1403  function FadeInTransition() {}
1404  FadeInTransition.prototype = new Transition();
1405  FadeInTransition.constructor = FadeInTransition;
1406  /**
1407   * @member FadeInTransition
1408   */
1409  FadeInTransition.prototype.init = function () {
1410    /*settings.$slides.css({
1411         'position': 'absolute',
1412         'top': '0px',
1413         'left': '0px'
1414         });*/
1415  };
1416  /**
1417   * @member FadeInTransition
1418   */
1419  FadeInTransition.prototype.perform = function (settings) {
1420    var zIndexAttrib = "z-index",
1421      $parent = settings.$slides.parent(),
1422      $nextSlide = settings.$nextSlide;
1423
1424    $parent.css(zIndexAttrib, 0);
1425    $nextSlide.css(zIndexAttrib, 1);
1426    settings.$slides.css({
1427      position: "relative",
1428    });
1429
1430    settings.$nextSlide.css({
1431      position: "absolute",
1432      top: "0",
1433      bottom: "0",
1434      left: "0",
1435      right: "0",
1436    });
1437
1438    $nextSlide.fadeIn(function () {
1439      settings.$currentSlide.hide();
1440
1441      settings.$nextSlide.css({
1442        position: "relative",
1443        left: "0",
1444        transform: "none",
1445      });
1446
1447      $nextSlide.css(zIndexAttrib, "");
1448      $parent.css(zIndexAttrib, "");
1449
1450      settings.callback();
1451    });
1452  };
1453  /**
1454   * End of FadeInTransition class
1455   */
1456  pub.FadeInTransition = FadeInTransition;
1457
1458  /**
1459   * SlideHorizontallyTransition class changes slides by sliding them horizontally.
1460   * This class can be removed if effect is not used.
1461   * If offset (difference between index of currently displayed slide and index of slide that will be shown)
1462   * is greater than 0, slides are sliding to the left. Otherwise (offset is lower than 0), slides are sliding
1463   * to the right.
1464   * When slide change is raised automatically, slides are always sliding to right (offset == 1).
1465   * If user raises slides change, slides are sliding:
1466   * - to left, if offset is greater than 0 (slide with higher index will be shown or user clicks 'next' navigation item),
1467   * - to right, if offset is lower than 0 (slide with lower index will be shown or user clicks 'prev' navigation item).
1468   * @constructor
1469   */
1470  function SlideHorizontallyTransition() {}
1471  SlideHorizontallyTransition.prototype = new Transition();
1472  SlideHorizontallyTransition.constructor = SlideHorizontallyTransition;
1473  /**
1474   * @member SlideHorizontallyTransition
1475   */
1476  SlideHorizontallyTransition.prototype.onAnimationComplete = function (
1477    settings,
1478    $parent
1479  ) {
1480    settings.$currentSlide.hide();
1481    settings.$nextSlide.css({
1482      top: "",
1483      bottom: "",
1484      left: "",
1485      position: "",
1486      width: "",
1487    });
1488
1489    $parent.css({
1490      left: "",
1491    });
1492
1493    $parent.css("margin-left", "");
1494
1495    settings.callback();
1496  };
1497  /**
1498   * @member SlideHorizontallyTransition
1499   */
1500  SlideHorizontallyTransition.prototype.perform = function (settings, offset) {
1501    var factor = this.factor(settings),
1502      onAnimationComplete = this.onAnimationComplete,
1503      width = settings.$currentSlide.width(),
1504      $parent = settings.$slides.parent();
1505
1506    settings.$nextSlide.css({
1507      top: "0",
1508      bottom: "0",
1509      left: "100%",
1510      position: "absolute",
1511      width: "100%",
1512    });
1513    if (offset > 0) {
1514      settings.$nextSlide.css({
1515        left: "100%",
1516      });
1517    } else {
1518      settings.$nextSlide.css({
1519        left: "-100%",
1520      });
1521    }
1522    settings.$nextSlide.show();
1523    $parent.animate(
1524      {
1525        left: -factor * width + "px",
1526      },
1527      function () {
1528        onAnimationComplete(settings, $parent);
1529      }
1530    );
1531  };
1532  /**
1533   * End of SlideHorizontallyTransition class
1534   */
1535  pub.SlideHorizontallyTransition = SlideHorizontallyTransition;
1536
1537  /**
1538   * SlideVerticallyTransition class changes slides by sliding them vertically.
1539   * This class can be removed if this effect is not used.
1540   * If offset (difference between index of currently displayed slide and index of next slide)
1541   * is greater than 0, slides are sliding from up to down. Otherwise (offset is lower than 0), slides are sliding
1542   * from down to up.
1543   * When slide change is raised automatically, slides are always sliding from up to down (offset == 1).
1544   * If user raises slides change, slides are sliding:
1545   * - from up to down, if offset is greater than 0 (slide with higher index will be shown or user clicks 'next' navigation item),
1546   * - from down to up, if offset is lower than 0 (slide with lower index will be shown or user clicks 'prev' navigation item).
1547   * @constructor
1548   */
1549  function SlideVerticallyTransition() {}
1550  SlideVerticallyTransition.prototype = new Transition();
1551  SlideVerticallyTransition.constructor = SlideVerticallyTransition;
1552  /**
1553   * @member SlideVerticallyTransition
1554   */
1555  SlideVerticallyTransition.prototype.onAnimationComplete = function (
1556    settings,
1557    $parent
1558  ) {
1559    settings.$currentSlide.hide();
1560    settings.$nextSlide.css({
1561      top: "",
1562      bottom: "",
1563      left: "",
1564      right: "",
1565      position: "",
1566      width: "",
1567    });
1568
1569    $parent.css("top", "");
1570    $parent.css("margin-top", "");
1571
1572    settings.callback();
1573  };
1574  /**
1575   * @member SlideVerticallyTransition
1576   */
1577  SlideVerticallyTransition.prototype.perform = function (settings, offset) {
1578    var factor = this.factor(settings),
1579      onAnimationComplete = this.onAnimationComplete,
1580      height = settings.$currentSlide.height(),
1581      $parent = settings.$slides.parent();
1582
1583    settings.$nextSlide.css({
1584      left: "0",
1585      right: "0",
1586      position: "absolute",
1587      width: "100%",
1588    });
1589    if (offset > 0) {
1590      settings.$nextSlide.css({
1591        top: "-100%",
1592        bottom: "100%",
1593      });
1594    } else {
1595      settings.$nextSlide.css({
1596        top: "100%",
1597        bottom: "-100%",
1598      });
1599    }
1600    settings.$nextSlide.show();
1601
1602    $parent.animate(
1603      {
1604        top: factor * height,
1605      },
1606      function () {
1607        onAnimationComplete(settings, $parent);
1608      }
1609    );
1610  };
1611  /**
1612   * SlideVerticallyTransition class
1613   */
1614  pub.SlideVerticallyTransition = SlideVerticallyTransition;
1615
1616  return pub;
1617})(jQuery);
1618/**
1619 * end of XA.component.carousels.Transitions module.
1620 */
1621
1622/**
1623 * XAContext
1624 *
1625 * XA.component.carousels.IndicatorViews module.
1626 *
1627 * Exposes View class, which is a base class of all 'time indicator' views used by carousels (@see XA.component.carousels, @see View).
1628 * Also exposes implementation of these 'time indicator' views:
1629 * - ProgressBarView - shows the progress of time with a progress bar,
1630 * - RotatorView - shows the progress of time with the filling up ring.
1631 * Carousel takes the class constructor by name from this module (it is given in options object of carousel; @see XA.component.carousels).
1632 * If there is a need to implement the new 'time indicator' view, create a new class
1633 * by inheriting from View class and add it to this module. During implementation of a new 'time indicator'
1634 * view the XA.component.carousels.IndicatorsView.Tools namespace. It contains:
1635 * - isCssSupported(props) function, which checks if any of CSS properties is supported (props - array of properties),
1636 * - isTransformSupported() function, which checks if CSS 3 'transform' is supported.
1637 *
1638 * Example view implementation (opacity increases with a progress of the time):
1639 * (function() {
1640 *     var View = XA.component.carousels.IndicatorViews.View;
1641 *
1642 *     function SampleView (selector, options) {
1643 *         //base constructor have to be called
1644 *         View.call(this, selector);
1645 *     }
1646 *
1647 *     SampleView.prototype = new View();
1648 *     SampleView.constructor = SampleView;
1649 *
1650 *     SampleView.prototype.init = function (timeout) {
1651 *         //base method have to be called
1652 *         View.prototype.init.call(this, timeout);
1653 *
1654 *         //initialization code - not needed in this example
1655 *     };
1656 *
1657 *     SampleView.prototype.reset = function () {
1658 *         //base method have to be called
1659 *         View.prototype.reset.call(this);
1660 *
1661 *         //reseting 'time indicator' view
1662 *         this.$indicator.stop(true);
1663 *         this.$indicator.css('opacity', 0);
1664 *     };
1665 *
1666 *     SampleView.prototype.pause = function () {
1667 *         //stoping 'time indicator' view animation
1668 *         this.$indicator.stop(true);
1669 *     };
1670 *
1671 *     SampleView.prototype.play = function () {
1672 *         //starting/resuming 'time indicator' view animation
1673 *         this.$indicator.animate({
1674 *             'opacity': 1
1675 *         }, this.timeout - this.timeElapsed, 'linear');
1676 *     };
1677 *
1678 *     XA.component.carousels.IndicatorViews.SampleView = SampleView;
1679 * }());
1680 *
1681 */
1682XA.component.carousels.IndicatorViews = (function ($) {
1683  var pub = {},
1684    supportElementStyle = null;
1685
1686  /**
1687   * Checks if the CSS properties are supported.
1688   *
1689   * @param {Array} props     array of properties to be checked
1690   * @return true if any of CSS properties is supported, false otherwise
1691   * @type Boolean
1692   */
1693  function isCssSupported(props) {
1694    var i,
1695      isSupported = false;
1696
1697    if (supportElementStyle === null) {
1698      supportElementStyle = document.createElement("supportElement").style;
1699    }
1700
1701    for (i = 0; i < props.length; i++) {
1702      if (supportElementStyle[props[i]] !== undefined) {
1703        isSupported = true;
1704        break;
1705      }
1706    }
1707
1708    return isSupported;
1709  }
1710
1711  /**
1712   * Checks if CSS 3 'transform' is supported.
1713   *
1714   * @return true if CSS 3 'transform' is supported, false otherwise
1715   * @type Boolean
1716   */
1717  function isTransformSupported() {
1718    return isCssSupported([
1719      "transformProperty",
1720      "WebkitTransform",
1721      "MozTransform",
1722      "OTransform",
1723      "msTransform",
1724    ]);
1725  }
1726
1727  /**
1728     * XA.component.carousels.IndicatorViews.Tools namespace
1729     *
1730     This namespace can be removed if RotatorView class is not used (@see RotatorView)
1731     */
1732  pub.Tools = {};
1733  pub.Tools.isCssSupported = isCssSupported;
1734  pub.Tools.isTransformSupported = isTransformSupported;
1735  /**
1736   * End of namespace
1737   */
1738
1739  /**
1740   * View class is a base class of all 'time indicator' views
1741   * @constructor
1742   * @param {String} selector a selector used for searching
1743   * @param {Object} options   View options object
1744   */
1745  /* eslint-disable */
1746  function View(selector, options) {
1747    /**
1748     * A jQuery object containing element that will be animated to show time progress.
1749     *
1750     * @member View
1751     */
1752
1753    this.$indicator = $(selector);
1754  }
1755  /* eslint-enable */
1756  /**
1757   * Initializes the view.
1758   *
1759   * @member Viewion
1760   * @param {Number} timeout   a timeout to the end of whole indicator animation
1761   */
1762  View.prototype.init = function (timeout) {
1763    this.timeElapsed = 0;
1764    this.timeout = timeout;
1765  };
1766  /**
1767   * Resets the view
1768   *
1769   * @member View
1770   */
1771  View.prototype.reset = function () {
1772    this.timeElapsed = 0;
1773  };
1774  /**
1775   * Starts/Renews the view animation.
1776   *
1777   * @member View
1778   */
1779  View.prototype.play = function () {};
1780  /**
1781   * Pauses the view animation.
1782   *
1783   * @member View
1784   */
1785  View.prototype.pause = function () {};
1786  /**
1787   * Updates the time that elapsed since animation began.
1788   *
1789   * @member View
1790   */
1791  View.prototype.update = function (timeElapsed) {
1792    this.timeElapsed = timeElapsed;
1793  };
1794  /**
1795   * end of View class
1796   */
1797  pub.View = View;
1798
1799  /**
1800   * ProgressBarView class provides a logic for animate a 'time indicator',
1801   * that looks like progress bar.
1802   *
1803   * Options object:
1804   * {
1805   *     'opacity': Number value //between 0 and 1; default is 0.8
1806   * }
1807   *
1808   * This class can be removed if this kind of view is not used.
1809   *
1810   * @constructor
1811   * @param {String} selector a selector used for searching
1812   * @param {Object} options   View options object
1813   */
1814  function ProgressBarView(selector, options) {
1815    View.call(this, selector);
1816
1817    var defaults = {
1818      opacity: 0.8,
1819    };
1820
1821    this.settings = $.extend(true, {}, defaults, options);
1822
1823    this.$container = this.$indicator;
1824    this.$indicator = this.$indicator.find("div");
1825  }
1826  ProgressBarView.prototype = new View();
1827  ProgressBarView.constructor = ProgressBarView;
1828  /**
1829   * @member ProgressBarView
1830   */
1831  ProgressBarView.prototype.init = function (timeout) {
1832    View.prototype.init.call(this, timeout);
1833
1834    var opacity = this.settings.opacity;
1835    this.$container.css("opacity", opacity);
1836    this.$indicator.css("opacity", opacity);
1837  };
1838  /**
1839   * @member ProgressBarView
1840   */
1841  ProgressBarView.prototype.reset = function () {
1842    View.prototype.reset.call(this);
1843
1844    this.$indicator.stop(true);
1845    this.$indicator.css("width", "0px");
1846  };
1847  /**
1848   * @member ProgressBarView
1849   */
1850  ProgressBarView.prototype.pause = function () {
1851    this.$indicator.stop(true);
1852  };
1853  /**
1854   * @member ProgressBarView
1855   */
1856  ProgressBarView.prototype.play = function () {
1857    this.$indicator.animate(
1858      {
1859        width: "100%",
1860      },
1861      this.timeout - this.timeElapsed,
1862      "linear"
1863    );
1864  };
1865  /**
1866   * end of ProgressBarView class
1867   */
1868  pub.ProgressBarView = ProgressBarView;
1869
1870  /**
1871   * RotatorView class provides a logic for animate a 'time indicator',
1872   * that shows the progress of time with the filling up ring
1873   *
1874   * Options object:
1875   * {
1876   *     'opacity': Number value //between 0 and 1; default is 0.5
1877   * }
1878   *
1879   * This class can be removed if this kind of view is not used.
1880   *
1881   * @constructor
1882   * @param {String} selector a selector used for searching
1883   * @param {Object} options   View options object
1884   */
1885  function RotatorView(selector, options) {
1886    View.call(this, selector);
1887
1888    var defaults = {
1889      opacity: 0.5,
1890    };
1891
1892    this.settings = $.extend(true, {}, defaults, options);
1893    this.$mask = this.$indicator.find("span.mask").first();
1894    this.$rotator = this.$indicator.find("span.rotator").first();
1895
1896    function rotateCss(deg) {
1897      var value = "rotate(" + deg + "deg)";
1898
1899      return {
1900        "-webkit-transform": value,
1901        "-moz-transform": value,
1902        "-o-transform": value,
1903        "-ms-transform": value,
1904        transform: value,
1905      };
1906    }
1907
1908    function animateRotator($rotator, options) {
1909      var defaults = {
1910          fontSize: "180px",
1911          animateOptions: {
1912            step: function (now) {
1913              $rotator.css(rotateCss(now));
1914            },
1915            easing: "linear",
1916          },
1917        },
1918        settings = $.extend(true, {}, defaults, options);
1919
1920      $rotator.animate(
1921        {
1922          "font-size": settings.fontSize,
1923        },
1924        settings.animateOptions
1925      );
1926    }
1927
1928    function phaseTwoAnimation($mask, $rotator, duration) {
1929      $rotator.addClass("half");
1930      $mask.addClass("half");
1931
1932      animateRotator($rotator, {
1933        fontSize: "360px",
1934        animateOptions: {
1935          duration: duration,
1936        },
1937      });
1938    }
1939
1940    function phaseOneAnimation($mask, $rotator, duration, phaseTwoDuration) {
1941      animateRotator($rotator, {
1942        animateOptions: {
1943          duration: duration,
1944          complete: function () {
1945            phaseTwoAnimation($mask, $rotator, phaseTwoDuration);
1946          },
1947        },
1948      });
1949    }
1950
1951    this.resetCss = function () {
1952      this.$rotator.removeClass("half");
1953      this.$rotator.css(rotateCss(0));
1954      this.$rotator.css("font-size", "0px");
1955
1956      this.$mask.removeClass("half");
1957    };
1958
1959    this.playPhaseOne = function () {
1960      var duration = this.timeout / 2 - this.timeElapsed;
1961      phaseOneAnimation(this.$mask, this.$rotator, duration, this.timeout / 2);
1962    };
1963
1964    this.playPhaseTwo = function () {
1965      var duration = this.timeout - this.timeElapsed;
1966      phaseTwoAnimation(this.$mask, this.$rotator, duration);
1967    };
1968  }
1969  RotatorView.prototype = new View();
1970  RotatorView.constructor = RotatorView;
1971  /**
1972   * @member RotatorView
1973   */
1974  RotatorView.prototype.init = function (timeout) {
1975    View.prototype.init.call(this, timeout);
1976
1977    if (isTransformSupported()) {
1978      this.$indicator.show();
1979      this.$mask.css("opacity", this.settings.opacity);
1980    }
1981  };
1982  /**
1983   * @member RotatorView
1984   */
1985  RotatorView.prototype.reset = function () {
1986    View.prototype.reset.call(this);
1987
1988    this.$rotator.stop(true);
1989    this.resetCss();
1990  };
1991  /**
1992   * @member RotatorView
1993   */
1994  RotatorView.prototype.pause = function () {
1995    this.$rotator.stop(true);
1996  };
1997  /**
1998   * @member RotatorView
1999   */
2000  RotatorView.prototype.play = function () {
2001    var phase = this.playPhaseOne;
2002    if (this.timeElapsed >= this.timeout / 2) {
2003      phase = this.playPhaseTwo;
2004    }
2005
2006    phase.call(this);
2007  };
2008  /**
2009   * end of RotatorView class
2010   */
2011  pub.RotatorView = RotatorView;
2012
2013  return pub;
2014})(jQuery);
2015/**
2016 * end of XA.component.carousels.IndicatorViews module.
2017 */

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.