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.