1/*********************************************************************************************** 2 jquery.backstretch.js 3**********************************************************************************************/ 4/* 5 * Backstretch 6 * http://srobbin.com/jquery-plugins/backstretch/ 7 * 8 * Copyright (c) 2013 Scott Robbin 9 * Licensed under the MIT license. 10 */ 11 12 13(function ($, window, undefined) { 14 'use strict'; 15 16 /** @const */ 17 var YOUTUBE_REGEXP = /^.*(youtu\.be\/|youtube\.com\/v\/|youtube\.com\/embed\/|youtube\.com\/watch\?v=|youtube\.com\/watch\?.*\&v=)([^#\&\?]*).*/i; 18 19 /* PLUGIN DEFINITION 20 * ========================= */ 21 22 $.fn.backstretch = function (images, options) { 23 var args = arguments; 24 25 /* 26 * Scroll the page one pixel to get the right window height on iOS 27 * Pretty harmless for everyone else 28 */ 29 if ($(window).scrollTop() === 0) { 30 window.scrollTo(0, 0); 31 } 32 33 var returnValues; 34 35 this.each(function (eachIndex) { 36 var $this = $(this), 37 obj = $this.data('backstretch'); 38 39 // Do we already have an instance attached to this element? 40 if (obj) { 41 42 // Is this a method they're trying to execute? 43 if (typeof args[0] === 'string' && 44 typeof obj[args[0]] === 'function') { 45 46 // Call the method 47 var returnValue = obj[args[0]].apply(obj, Array.prototype.slice.call(args, 1)); 48 if (returnValue === obj) { // If a method is chaining 49 returnValue = undefined; 50 } 51 if (returnValue !== undefined) { 52 returnValues = returnValues || []; 53 returnValues[eachIndex] = returnValue; 54 } 55 56 return; // Nothing further to do 57 } 58 59 // Merge the old options with the new 60 options = $.extend(obj.options, options); 61 62 // Remove the old instance 63 if (typeof obj === 'object' && 'destroy' in obj) { 64 obj.destroy(true); 65 } 66 } 67 68 // We need at least one image 69 if (!images || (images && images.length === 0)) { 70 var cssBackgroundImage = $this.css('background-image'); 71 if (cssBackgroundImage && cssBackgroundImage !== 'none') { 72 images = [{ 73 url: $this.css('backgroundImage').replace(/url\(|\)|"|'/g, "") 74 }]; 75 } else { 76 $.error('No images were supplied for Backstretch, or element must have a CSS-defined background image.'); 77 } 78 } 79 80 obj = new Backstretch(this, images, options || {}); 81 $this.data('backstretch', obj); 82 }); 83 84 return returnValues ? returnValues.length === 1 ? returnValues[0] : returnValues : this; 85 }; 86 87 // If no element is supplied, we'll attach to body 88 $.backstretch = function (images, options) { 89 // Return the instance 90 return $('body') 91 .backstretch(images, options) 92 .data('backstretch'); 93 }; 94 95 // Custom selector 96 $.expr[':'].backstretch = function (elem) { 97 return $(elem).data('backstretch') !== undefined; 98 }; 99 100 /* DEFAULTS 101 * ========================= */ 102 103 $.fn.backstretch.defaults = { 104 duration: 5000 // Amount of time in between slides (if slideshow) 105 , 106 transition: 'fade' // Type of transition between slides 107 , 108 transitionDuration: 0 // Duration of transition between slides 109 , 110 animateFirst: true // Animate the transition of first image of slideshow in? 111 , 112 alignX: 0.5 // The x-alignment for the image, can be 'left'|'center'|'right' or any number between 0.0 and 1.0 113 , 114 alignY: 0.5 // The y-alignment for the image, can be 'top'|'center'|'bottom' or any number between 0.0 and 1.0 115 , 116 paused: false // Whether the images should slide after given duration 117 , 118 start: 0 // Index of the first image to show 119 , 120 preload: 2 // How many images preload at a time? 121 , 122 preloadSize: 1 // How many images can we preload in parallel? 123 , 124 resolutionRefreshRate: 2500 // How long to wait before switching resolution? 125 , 126 resolutionChangeRatioThreshold: 0.1 // How much a change should it be before switching resolution? 127 }; 128 129 /* STYLES 130 * 131 * Baked-in styles that we'll apply to our elements. 132 * In an effort to keep the plugin simple, these are not exposed as options. 133 * That said, anyone can override these in their own stylesheet. 134 * ========================= */ 135 var styles = { 136 wrap: { 137 left: 0, 138 top: 0, 139 overflow: 'hidden', 140 margin: 0, 141 padding: 0, 142 height: '100%', 143 width: '100%', 144 zIndex: -999999 145 }, 146 itemWrapper: { 147 position: 'absolute', 148 display: 'none', 149 margin: 0, 150 padding: 0, 151 border: 'none', 152 width: '100%', 153 height: '100%', 154 zIndex: -999999 155 }, 156 item: { 157 position: 'absolute', 158 margin: 0, 159 padding: 0, 160 border: 'none', 161 width: '100%' 162 // , height: '100%' 163 , 164 maxWidth: 'none' 165 } 166 }; 167 168 /* Given an array of different options for an image, 169 * choose the optimal image for the container size. 170 * 171 * Given an image template (a string with {{ width }} and/or 172 * {{height}} inside) and a container object, returns the 173 * image url with the exact values for the size of that 174 * container. 175 * 176 * Returns an array of urls optimized for the specified resolution. 177 * 178 */ 179 var optimalSizeImages = (function () { 180 181 /* Sorts the array of image sizes based on width */ 182 var widthInsertSort = function (arr) { 183 for (var i = 1; i < arr.length; i++) { 184 var tmp = arr[i], 185 j = i; 186 while (arr[j - 1] && parseInt(arr[j - 1].width, 10) > parseInt(tmp.width, 10)) { 187 arr[j] = arr[j - 1]; 188 --j; 189 } 190 arr[j] = tmp; 191 } 192 193 return arr; 194 }; 195 196 /* Given an array of various sizes of the same image and a container width, 197 * return the best image. 198 */ 199 var selectBest = function (containerWidth, containerHeight, imageSizes) { 200 201 var devicePixelRatio = window.devicePixelRatio || 1; 202 var deviceOrientation = getDeviceOrientation(); 203 var windowOrientation = getWindowOrientation(); 204 var wrapperOrientation = (containerHeight > containerWidth) ? 205 'portrait' : 206 (containerWidth > containerHeight ? 'landscape' : 'square'); 207 208 var lastAllowedImage = 0; 209 var testWidth; 210 211 for (var j = 0, image; j < imageSizes.length; j++) { 212 213 image = imageSizes[j]; 214 215 // In case a new image was pushed in, process it: 216 if (typeof image === 'string') { 217 image = imageSizes[j] = { 218 url: image 219 }; 220 } 221 222 if (image.pixelRatio && image.pixelRatio !== 'auto' && parseFloat(image.pixelRatio) !== devicePixelRatio) { 223 // We disallowed choosing this image for current device pixel ratio, 224 // So skip this one. 225 continue; 226 } 227 228 if (image.deviceOrientation && image.deviceOrientation !== deviceOrientation) { 229 // We disallowed choosing this image for current device orientation, 230 // So skip this one. 231 continue; 232 } 233 234 if (image.windowOrientation && image.windowOrientation !== deviceOrientation) { 235 // We disallowed choosing this image for current window orientation, 236 // So skip this one. 237 continue; 238 } 239 240 if (image.orientation && image.orientation !== wrapperOrientation) { 241 // We disallowed choosing this image for current element's orientation, 242 // So skip this one. 243 continue; 244 } 245 246 // Mark this one as the last one we investigated 247 // which does not violate device pixel ratio rules. 248 // We may choose this one later if there's no match. 249 lastAllowedImage = j; 250 251 // For most images, we match the specified width against element width, 252 // And enforcing a limit depending on the "pixelRatio" property if specified. 253 // But if a pixelRatio="auto", then we consider the width as the physical width of the image, 254 // And match it while considering the device's pixel ratio. 255 testWidth = containerWidth; 256 if (image.pixelRatio === 'auto') { 257 containerWidth *= devicePixelRatio; 258 } 259 260 // Stop when the width of the image is larger or equal to the container width 261 if (image.width >= testWidth) { 262 break; 263 } 264 } 265 266 // Use the image located at where we stopped 267 return imageSizes[Math.min(j, lastAllowedImage)]; 268 }; 269 270 var replaceTagsInUrl = function (url, templateReplacer) { 271 272 if (typeof url === 'string') { 273 url = url.replace(/{{(width|height)}}/g, templateReplacer); 274 } else if (url instanceof Array) { 275 for (var i = 0; i < url.length; i++) { 276 if (url[i].src) { 277 url[i].src = replaceTagsInUrl(url[i].src, templateReplacer); 278 } else { 279 url[i] = replaceTagsInUrl(url[i], templateReplacer); 280 } 281 } 282 } 283 284 return url; 285 }; 286 287 return function ($container, images) { 288 var containerWidth = $container.width(), 289 containerHeight = $container.height(); 290 291 var chosenImages = []; 292 293 var templateReplacer = function (match, key) { 294 if (key === 'width') { 295 return containerWidth; 296 } 297 if (key === 'height') { 298 return containerHeight; 299 } 300 return match; 301 }; 302 303 for (var i = 0; i < images.length; i++) { 304 if ($.isArray(images[i])) { 305 images[i] = widthInsertSort(images[i]); 306 var chosen = selectBest(containerWidth, containerHeight, images[i]); 307 chosenImages.push(chosen); 308 } else { 309 // In case a new image was pushed in, process it: 310 if (typeof images[i] === 'string') { 311 images[i] = { 312 url: images[i] 313 }; 314 } 315 316 var item = $.extend({}, images[i]); 317 item.url = replaceTagsInUrl(item.url, templateReplacer); 318 chosenImages.push(item); 319 } 320 } 321 return chosenImages; 322 }; 323 324 })(); 325 326 var isVideoSource = function (source) { 327 return YOUTUBE_REGEXP.test(source.url) || source.isVideo; 328 }; 329 330 /* Preload images */ 331 var preload = (function (sources, startAt, count, batchSize, callback) { 332 // Plugin cache 333 var cache = []; 334 335 // Wrapper for cache 336 var caching = function (image) { 337 for (var i = 0; i < cache.length; i++) { 338 if (cache[i].src === image.src) { 339 return cache[i]; 340 } 341 } 342 cache.push(image); 343 return image; 344 }; 345 346 // Execute callback 347 var exec = function (sources, callback, last) { 348 if (typeof callback === 'function') { 349 callback.call(sources, last); 350 } 351 }; 352 353 // Closure to hide cache 354 return function preload(sources, startAt, count, batchSize, callback) { 355 // Check input data 356 if (typeof sources === 'undefined') { 357 return; 358 } 359 if (!$.isArray(sources)) { 360 sources = [sources]; 361 } 362 363 if (arguments.length < 5 && typeof arguments[arguments.length - 1] === 'function') { 364 callback = arguments[arguments.length - 1]; 365 } 366 367 startAt = (typeof startAt === 'function' || !startAt) ? 0 : startAt; 368 count = (typeof count === 'function' || !count || count < 0) ? sources.length : Math.min(count, sources.length); 369 batchSize = (typeof batchSize === 'function' || !batchSize) ? 1 : batchSize; 370 371 if (startAt >= sources.length) { 372 startAt = 0; 373 count = 0; 374 } 375 if (batchSize < 0) { 376 batchSize = count; 377 } 378 batchSize = Math.min(batchSize, count); 379 380 var next = sources.slice(startAt + batchSize, count - batchSize); 381 sources = sources.slice(startAt, batchSize); 382 count = sources.length; 383 384 // If sources array is empty 385 if (!count) { 386 exec(sources, callback, true); 387 return; 388 } 389 390 // Image loading callback 391 var countLoaded = 0; 392 393 var loaded = function () { 394 countLoaded++; 395 if (countLoaded !== count) { 396 return; 397 } 398 399 exec(sources, callback, !next); 400 preload(next, 0, 0, batchSize, callback); 401 }; 402 403 // Loop sources to preload 404 var image; 405 406 for (var i = 0; i < sources.length; i++) { 407 408 if (isVideoSource(sources[i])) { 409
410 // Do not preload videos. There are issues with that. 411 // First - we need to keep an instance of the preloaded and use that exactly, not a copy. 412 // Second - there are memory issues. 413 // If there will be a requirement from users - I'll try to implement this. 414 415 continue; 416 417 } else { 418 419 image = new Image(); 420 image.src = sources[i].url; 421 422 image = caching(image); 423 424 if (image.complete) { 425 loaded(); 426 } else { 427 $(image).on('load error', loaded); 428 } 429 430 } 431 432 } 433 }; 434 })(); 435 436 /* Process images array */ 437 var processImagesArray = function (images) { 438 var processed = []; 439 for (var i = 0; i < images.length; i++) { 440 if (typeof images[i] === 'string') { 441 processed.push({ 442 url: images[i] 443 }); 444 } else if ($.isArray(images[i])) { 445 processed.push(processImagesArray(images[i])); 446 } else { 447 processed.push(processOptions(images[i])); 448 } 449 } 450 return processed; 451 }; 452 453 /* Process options */ 454 var processOptions = function (options, required) { 455 456 // Convert old options 457 458 // centeredX/centeredY are deprecated 459 if (options.centeredX || options.centeredY) { 460 if (window.console && window.console.log) { 461 window.console.log('jquery.backstretch: `centeredX`/`centeredY` is deprecated, please use `alignX`/`alignY`'); 462 } 463 if (options.centeredX) { 464 options.alignX = 0.5; 465 } 466 if (options.centeredY) { 467 options.alignY = 0.5; 468 } 469 } 470 471 // Deprecated spec 472 if (options.speed !== undefined) { 473 474 if (window.console && window.console.log) { 475 window.console.log('jquery.backstretch: `speed` is deprecated, please use `transitionDuration`'); 476 } 477 478 options.transitionDuration = options.speed; 479 options.transition = 'fade'; 480 } 481 482 // Typo 483 if (options.resolutionChangeRatioTreshold !== undefined) { 484 window.console.log('jquery.backstretch: `treshold` is a typo!'); 485 options.resolutionChangeRatioThreshold = options.resolutionChangeRatioTreshold; 486 } 487 488 // Current spec that needs processing 489 490 if (options.fadeFirst !== undefined) { 491 options.animateFirst = options.fadeFirst; 492 } 493 494 if (options.fade !== undefined) { 495 options.transitionDuration = options.fade; 496 options.transition = 'fade'; 497 } 498 499 if (options.scale) { 500 options.scale = validScale(options.scale); 501 } 502 503 return processAlignOptions(options); 504 }; 505 506 /* Process align options */ 507 var processAlignOptions = function (options, required) { 508 if (options.alignX === 'left') { 509 options.alignX = 0.0; 510 } else if (options.alignX === 'center') { 511 options.alignX = 0.5; 512 } else if (options.alignX === 'right') { 513 options.alignX = 1.0; 514 } else { 515 if (options.alignX !== undefined || required) { 516 options.alignX = parseFloat(options.alignX); 517 if (isNaN(options.alignX)) { 518 options.alignX = 0.5; 519 } 520 } 521 } 522 523 if (options.alignY === 'top') { 524 options.alignY = 0.0; 525 } else if (options.alignY === 'center') { 526 options.alignY = 0.5; 527 } else if (options.alignY === 'bottom') { 528 options.alignY = 1.0; 529 } else { 530 if (options.alignX !== undefined || required) { 531 options.alignY = parseFloat(options.alignY); 532 if (isNaN(options.alignY)) { 533 options.alignY = 0.5; 534 } 535 } 536 } 537 538 return options; 539 }; 540 541 var SUPPORTED_SCALE_OPTIONS = { 542 'cover': 'cover', 543 'fit': 'fit', 544 'fit-smaller': 'fit-smaller', 545 'fill': 'fill' 546 }; 547 548 function validScale(scale) { 549 if (!SUPPORTED_SCALE_OPTIONS.hasOwnProperty(scale)) { 550 return 'cover'; 551 } 552 return scale; 553 } 554 555 /* CLASS DEFINITION 556 * ========================= */ 557 var Backstretch = function (container, images, options) { 558 this.options = $.extend({}, $.fn.backstretch.defaults, options || {}); 559 560 this.firstShow = true; 561 562 // Process options 563 processOptions(this.options, true); 564 565 /* In its simplest form, we allow Backstretch to be called on an image path. 566 * e.g. $.backstretch('/path/to/image.jpg') 567 * So, we need to turn this back into an array.
568 */ 569 this.images = processImagesArray($.isArray(images) ? images : [images]); 570 571 /** 572 * Paused-Option 573 */ 574 if (this.options.paused) { 575 this.paused = true; 576 } 577 578 /** 579 * Start-Option (Index) 580 */ 581 if (this.options.start >= this.images.length) { 582 this.options.start = this.images.length - 1; 583 } 584 if (this.options.start < 0) { 585 this.options.start = 0; 586 } 587 588 // Convenience reference to know if the container is body. 589 this.isBody = container === document.body; 590 591 /* We're keeping track of a few different elements 592 * 593 * Container: the element that Backstretch was called on. 594 * Wrap: a DIV that we place the image into, so we can hide the overflow. 595 * Root: Convenience reference to help calculate the correct height. 596 */ 597 var $window = $(window); 598 this.$container = $(container); 599 this.$root = this.isBody ? supportsFixedPosition ? $window : $(document) : this.$container; 600 601 this.originalImages = this.images; 602 this.images = optimalSizeImages( 603 this.options.alwaysTestWindowResolution ? $window : this.$root, 604 this.originalImages); 605 606 /** 607 * Pre-Loading. 608 * This is the first image, so we will preload a minimum of 1 images. 609 */ 610 preload(this.images, this.options.start || 0, this.options.preload || 1); 611 612 // Don't create a new wrap if one already exists (from a previous instance of Backstretch) 613 var $existing = this.$container.children(".backstretch").first(); 614 this.$wrap = $existing.length ? $existing : 615 $('<div class="backstretch"></div>') 616 .css(this.options.bypassCss ? {} : styles.wrap) 617 .appendTo(this.$container); 618 619 if (!this.options.bypassCss) { 620 621 // Non-body elements need some style adjustments 622 if (!this.isBody) { 623 // If the container is statically positioned, we need to make it relative, 624 // and if no zIndex is defined, we should set it to zero. 625 var position = this.$container.css('position'), 626 zIndex = this.$container.css('zIndex'); 627 628 this.$container.css({ 629 position: position === 'static' ? 'relative' : position, 630 zIndex: zIndex === 'auto' ? 0 : zIndex 631 }); 632 633 // Needs a higher z-index 634 this.$wrap.css({ 635 zIndex: -999998 636 }); 637 } 638 639 // Fixed or absolute positioning? 640 this.$wrap.css({ 641 position: this.isBody && supportsFixedPosition ? 'fixed' : 'absolute' 642 }); 643 644 } 645 646 // Set the first image 647 this.index = this.options.start; 648 this.show(this.index); 649 650 // Listen for resize 651 $window.on('resize.backstretch', $.proxy(this.resize, this)) 652 .on('orientationchange.backstretch', $.proxy(function () { 653 // Need to do this in order to get the right window height 654 if (this.isBody && window.pageYOffset === 0) { 655 window.scrollTo(0, 1); 656 this.resize(); 657 } 658 }, this)); 659 }; 660 661 var performTransition = function (options) { 662 663 var transition = options.transition || 'fade'; 664 665 // Look for multiple options 666 if (typeof transition === 'string' && transition.indexOf('|') > -1) { 667 transition = transition.split('|'); 668 } 669 670 if (transition instanceof Array) { 671 transition = transition[Math.round(Math.random() * (transition.length - 1))]; 672 } 673 674 var $new = options['new']; 675 var $old = options['old'] ? options['old'] : $([]); 676 677 switch (transition.toString().toLowerCase()) { 678 679 default: 680 case 'fade': 681 $new.fadeIn({ 682 duration: options.duration, 683 complete: options.complete, 684 easing: options.easing || undefined 685 }); 686 break; 687 688 case 'fadeinout': 689 case 'fade_in_out': 690 691 var fadeInNew = function () { 692 $new.fadeIn({ 693 duration: options.duration / 2, 694 complete: options.complete, 695 easing: options.easing || undefined 696 }); 697 }; 698 699 if ($old.length) { 700 $old.fadeOut({ 701 duration: options.duration / 2, 702 complete: fadeInNew, 703 easing: options.easing || undefined 704 }); 705 } else { 706 fadeInNew(); 707 } 708 709 break; 710 711 case 'pushleft': 712 case 'push_left': 713 case 'pushright': 714 case 'push_right': 715 case 'pushup': 716 case 'push_up': 717 case 'pushdown': 718 case 'push_down': 719 case 'coverleft': 720 case 'cover_left': 721 case 'coverright': 722 case 'cover_right': 723 case 'coverup': 724 case 'cover_up': 725 case 'coverdown': 726 case 'cover_down': 727 728 var transitionParts = transition.match(/^(cover|push)_?(.*)$/); 729 730 var animProp = transitionParts[2] === 'left' ? 'right' : 731 transitionParts[2] === 'right' ? 'left' : 732 transitionParts[2] === 'down' ? 'top' : 733 transitionParts[2] === 'up' ? 'bottom' : 734 'right'; 735 736 var newCssStart = { 737 'display': '' 738 }, 739 newCssAnim = {}; 740 newCssStart[animProp] = '-100%'; 741 newCssAnim[animProp] = 0; 742 743 $new 744 .css(newCssStart) 745 .animate(newCssAnim, { 746 duration: options.duration, 747 complete: function () { 748 $new.css(animProp, ''); 749 options.complete.apply(this, arguments); 750 }, 751 easing: options.easing || undefined 752 }); 753 754 if (transitionParts[1] === 'push' && $old.length) { 755 var oldCssAnim = {}; 756 oldCssAnim[animProp] = '100%'; 757 758 $old 759 .animate(oldCssAnim, { 760 duration: options.duration, 761 complete: function () { 762 $old.css('display', 'none'); 763 }, 764 easing: options.easing || undefined 765 }); 766 } 767 768 break; 769 } 770 771 }; 772 773 /* PUBLIC METHODS 774 * ========================= */ 775 Backstretch.prototype = { 776 777 resize: function () { 778 try { 779 780 // Check for a better suited image after the resize 781 var $resTest = this.options.alwaysTestWindowResolution ? $(window) : this.$root; 782 var newContainerWidth = $resTest.width(); 783 var newContainerHeight = $resTest.height(); 784 var changeRatioW = newContainerWidth / (this._lastResizeContainerWidth || 0); 785 var changeRatioH = newContainerHeight / (this._lastResizeContainerHeight || 0
785); 786 var resolutionChangeRatioThreshold = this.options.resolutionChangeRatioThreshold || 0.0; 787 788 // check for big changes in container size 789 if ((newContainerWidth !== this._lastResizeContainerWidth || 790 newContainerHeight !== this._lastResizeContainerHeight) && 791 ((Math.abs(changeRatioW - 1) >= resolutionChangeRatioThreshold || isNaN(changeRatioW)) || 792 (Math.abs(changeRatioH - 1) >= resolutionChangeRatioThreshold || isNaN(changeRatioH)))) { 793 794 this._lastResizeContainerWidth = newContainerWidth; 795 this._lastResizeContainerHeight = newContainerHeight; 796 797 // Big change: rebuild the entire images array 798 this.images = optimalSizeImages($resTest, this.originalImages); 799 800 // Preload them (they will be automatically inserted on the next cycle) 801 if (this.options.preload) { 802 preload(this.images, (this.index + 1) % this.images.length, this.options.preload); 803 } 804 805 // In case there is no cycle and the new source is different than the current 806 if (this.images.length === 1 && 807 this._currentImage.url !== this.images[0].url) { 808 809 // Wait a little an update the image being showed 810 var that = this; 811 clearTimeout(that._selectAnotherResolutionTimeout); 812 that._selectAnotherResolutionTimeout = setTimeout(function () { 813 that.show(0); 814 }, this.options.resolutionRefreshRate); 815 } 816 } 817 818 var bgCSS = { 819 left: 0, 820 top: 0, 821 right: 'auto', 822 bottom: 'auto' 823 } 824 825 , 826 boxWidth = this.isBody ? this.$root.width() : this.$root.innerWidth(), 827 boxHeight = this.isBody ? 828 (window.innerHeight ? window.innerHeight : this.$root.height()) : 829 this.$root.innerHeight() 830 831 , 832 naturalWidth = this.$itemWrapper.data('width'), 833 naturalHeight = this.$itemWrapper.data('height') 834 835 , 836 ratio = (naturalWidth / naturalHeight) || 1 837 838 , 839 alignX = this._currentImage.alignX === undefined ? this.options.alignX : this._currentImage.alignX, 840 alignY = this._currentImage.alignY === undefined ? this.options.alignY : this._currentImage.alignY, 841 scale = validScale(this._currentImage.scale || this.options.scale); 842 843 var width, height; 844 845 if (scale === 'fit' || scale === 'fit-smaller') { 846 width = naturalWidth; 847 height = naturalHeight; 848 849 if (width > boxWidth || 850 height > boxHeight || 851 scale === 'fit-smaller') { 852 var boxRatio = boxWidth / boxHeight; 853 if (boxRatio > ratio) { 854 width = Math.floor(boxHeight * ratio); 855 height = boxHeight; 856 } else if (boxRatio < ratio) { 857 width = boxWidth; 858 height = Math.floor(boxWidth / ratio); 859 } else { 860 width = boxWidth; 861 height = boxHeight; 862 } 863 } 864 } else if (scale === 'fill') { 865 width = boxWidth; 866 height = boxHeight; 867 } else { // 'cover' 868 width = Math.max(boxHeight * ratio, boxWidth); 869 height = Math.max(width / ratio, boxHeight); 870 } 871 872 // Make adjustments based on image ratio 873 bgCSS.top = -(height - boxHeight) * alignY; 874 bgCSS.left = -(width - boxWidth) * alignX; 875 bgCSS.width = width; 876 bgCSS.height = height; 877 878 if (!this.options.bypassCss) { 879 880 this.$wrap 881 .css({ 882 width: boxWidth, 883 height: boxHeight 884 }) 885 .find('>.backstretch-item').not('.deleteable') 886 .each(function () { 887 var $wrapper = $(this); 888 $wrapper.find('img,video,iframe') 889 .css(bgCSS); 890 }); 891 } 892 893 var evt = $.Event('backstretch.resize', { 894 relatedTarget: this.$container[0] 895 }); 896 this.$container.trigger(evt, this); 897 898 } catch (err) { 899 // IE7 seems to trigger resize before the image is loaded.
900 // This try/catch block is a hack to let it fail gracefully. 901 } 902 903 return this; 904 } 905 906 // Show the slide at a certain position 907 , 908 show: function (newIndex, overrideOptions) { 909 910 // Validate index 911 if (Math.abs(newIndex) > this.images.length - 1) { 912 return; 913 } 914 915 // Vars 916 var that = this, 917 $oldItemWrapper = that.$wrap.find('>.backstretch-item').addClass('deleteable'), 918 oldVideoWrapper = that.videoWrapper, 919 evtOptions = { 920 relatedTarget: that.$container[0] 921 }; 922 923 // Trigger the "before" event 924 that.$container.trigger($.Event('backstretch.before', evtOptions), [that, newIndex]); 925 926 // Set the new frame index 927 this.index = newIndex; 928 var selectedImage = that.images[newIndex]; 929 930 // Pause the slideshow 931 clearTimeout(that._cycleTimeout); 932 933 // New image 934 935 delete that.videoWrapper; // Current item may not be a video 936 937 var isVideo = isVideoSource(selectedImage); 938 if (isVideo) { 939 that.videoWrapper = new VideoWrapper(selectedImage); 940 that.$item = that.videoWrapper.$video.css('pointer-events', 'none'); 941 } else { 942 that.$item = $('<img />'); 943 } 944 945 that.$itemWrapper = $('<div class="backstretch-item">') 946 .append(that.$item); 947 948 if (this.options.bypassCss) { 949 that.$itemWrapper.css({ 950 'display': 'none' 951 }); 952 } else { 953 that.$itemWrapper.css(styles.itemWrapper); 954 that.$item.css(styles.item); 955 } 956 957 that.$item.bind(isVideo ? 'canplay' : 'load', function (e) { 958 var $this = $(this), 959 $wrapper = $this.parent(), 960 options = $wrapper.data('options'); 961 962 if (overrideOptions) { 963 options = $.extend({}, options, overrideOptions); 964 } 965 966 var imgWidth = this.naturalWidth || this.videoWidth || this.width, 967 imgHeight = this.naturalHeight || this.videoHeight || this.height; 968 969 // Save the natural dimensions 970 $wrapper 971 .data('width', imgWidth) 972 .data('height', imgHeight); 973 974 var getOption = function (opt) { 975 return options[opt] !== undefined ? 976 options[opt] : 977 that.options[opt]; 978 }; 979 980 var transition = getOption('transition'); 981 var transitionEasing = getOption('transitionEasing'); 982 var transitionDuration = getOption('transitionDuration'); 983 984 // Show the image, then delete the old one 985 var bringInNextImage = function () { 986 987 if (oldVideoWrapper) { 988 oldVideoWrapper.stop(); 989 oldVideoWrapper.destroy(); 990 } 991 992 $oldItemWrapper.remove(); 993 994 // Resume the slideshow 995 if (!that.paused && that.images.length > 1) { 996 that.cycle(); 997 } 998 999 // Now we can clear the background on the element, to spare memory 1000 if (!that.options.bypassCss && !that.isBody) { 1001 that.$container.css('background-image', 'none'); 1002 } 1003 1004 // Trigger the "after" and "show" events 1005 // "show" is being deprecated 1006 $(['after', 'show']).each(function () { 1007 that.$container.trigger($.Event('backstretch.' + this, evtOptions), [that, newIndex]); 1008 }); 1009 1010 if (isVideo) { 1011 that.videoWrapper.play(); 1012 } 1013 }; 1014 1015 if ((that.firstShow && !that.options.animateFirst) || !transitionDuration || !transition) { 1016 // Avoid transition on first show or if there's no transitionDuration value 1017 $wrapper.show(); 1018 bringInNextImage(); 1019 } else { 1020 1021 performTransition({ 1022 'new': $wrapper, 1023 old: $oldItemWrapper, 1024 transition: transition, 1025 duration: transitionDuration, 1026 easing: transitionEasing, 1027 complete: bringInNextImage 1028 }); 1029 1030 } 1031 1032 that.firstShow = false;
1033 1034 // Resize 1035 that.resize(); 1036 }); 1037 1038 that.$itemWrapper.appendTo(that.$wrap); 1039 1040 that.$item.attr('alt', selectedImage.alt || ''); 1041 that.$itemWrapper.data('options', selectedImage); 1042 1043 if (!isVideo) { 1044 that.$item.attr('src', selectedImage.url); 1045 } 1046 1047 that._currentImage = selectedImage; 1048 1049 return that; 1050 } 1051 1052 , 1053 current: function () { 1054 return this.index; 1055 } 1056 1057 , 1058 next: function () { 1059 var args = Array.prototype.slice.call(arguments, 0); 1060 args.unshift(this.index < this.images.length - 1 ? this.index + 1 : 0); 1061 return this.show.apply(this, args); 1062 } 1063 1064 , 1065 prev: function () { 1066 var args = Array.prototype.slice.call(arguments, 0); 1067 args.unshift(this.index === 0 ? this.images.length - 1 : this.index - 1); 1068 return this.show.apply(this, args); 1069 } 1070 1071 , 1072 pause: function () { 1073 // Pause the slideshow 1074 this.paused = true; 1075 1076 if (this.videoWrapper) { 1077 this.videoWrapper.pause(); 1078 } 1079 1080 return this; 1081 } 1082 1083 , 1084 resume: function () { 1085 // Resume the slideshow 1086 this.paused = false; 1087 1088 if (this.videoWrapper) { 1089 this.videoWrapper.play(); 1090 } 1091 1092 this.cycle(); 1093 return this; 1094 } 1095 1096 , 1097 cycle: function () { 1098 // Start/resume the slideshow 1099 if (this.images.length > 1) { 1100 // Clear the timeout, just in case 1101 clearTimeout(this._cycleTimeout); 1102 1103 var duration = (this._currentImage && this._currentImage.duration) || this.options.duration; 1104 var isVideo = isVideoSource(this._currentImage); 1105 1106 var callNext = function () { 1107 this.$item.off('.cycle'); 1108 1109 // Check for paused slideshow 1110 if (!this.paused) { 1111 this.next(); 1112 } 1113 }; 1114 1115 // Special video handling 1116 if (isVideo) { 1117 1118 // Leave video at last frame 1119 if (!this._currentImage.loop) { 1120 var lastFrameTimeout = 0; 1121 1122 this.$item 1123 .on('playing.cycle', function () { 1124 var player = $(this).data('player'); 1125 1126 clearTimeout(lastFrameTimeout); 1127 lastFrameTimeout = setTimeout(function () { 1128 player.pause(); 1129 player.$video.trigger('ended'); 1130 }, (player.getDuration() - player.getCurrentTime()) * 1000); 1131 }) 1132 .on('ended.cycle', function () { 1133 clearTimeout(lastFrameTimeout); 1134 }); 1135 } 1136 1137 // On error go to next 1138 this.$item.on('error.cycle initerror.cycle', $.proxy(callNext, this)); 1139 } 1140 1141 if (isVideo && !this._currentImage.duration) { 1142 // It's a video - playing until end 1143 this.$item.on('ended.cycle', $.proxy(callNext, this)); 1144 1145 } else { 1146 // Cycling according to specified duration 1147 this._cycleTimeout = setTimeout($.proxy(callNext, this), duration); 1148 } 1149 1150 } 1151 return this; 1152 } 1153 1154 ,
1155 destroy: function (preserveBackground) { 1156 // Stop the resize events 1157 $(window).off('resize.backstretch orientationchange.backstretch'); 1158 1159 // Stop any videos 1160 if (this.videoWrapper) { 1161 this.videoWrapper.destroy(); 1162 } 1163 1164 // Clear the timeout 1165 clearTimeout(this._cycleTimeout); 1166 1167 // Remove Backstretch 1168 if (!preserveBackground) { 1169 this.$wrap.remove(); 1170 } 1171 this.$container.removeData('backstretch'); 1172 } 1173 }; 1174 1175 /** 1176 * Video Abstraction Layer 1177 * 1178 * Static methods: 1179 * > VideoWrapper.loadYoutubeAPI() -> Call in order to load the Youtube API. 1180 * An 'youtube_api_load' event will be triggered on $(window) when the API is loaded. 1181 * 1182 * Generic: 1183 * > player.type -> type of the video 1184 * > player.video / player.$video -> contains the element holding the video 1185 * > player.play() -> plays the video 1186 * > player.pause() -> pauses the video 1187 * > player.setCurrentTime(position) -> seeks to a position by seconds 1188 * 1189 * Youtube: 1190 * > player.ytId will contain the youtube ID if the source is a youtube url 1191 * > player.ytReady is a flag telling whether the youtube source is ready for playback 1192 * */ 1193 1194 var VideoWrapper = function () { 1195 this.init.apply(this, arguments); 1196 }; 1197 1198 /** 1199 * @param {Object} options 1200 * @param {String|Array<String>|Array<{{src: String, type: String?}}>} options.url 1201 * @param {Boolean} options.loop=false 1202 * @param {Boolean?} options.mute=true 1203 * @param {String?} options.poster 1204 * loop, mute, poster 1205 */ 1206 VideoWrapper.prototype.init = function (options) { 1207 1208 var that = this; 1209 1210 var $video; 1211 1212 var setVideoElement = function () { 1213 that.$video = $video; 1214 that.video = $video[0]; 1215 }; 1216 1217 // Determine video type 1218 1219 var videoType = 'video'; 1220 1221 if (!(options.url instanceof Array) && 1222 YOUTUBE_REGEXP.test(options.url)) { 1223 videoType = 'youtube'; 1224 } 1225 1226 that.type = videoType; 1227 1228 if (videoType === 'youtube') { 1229 1230 // Try to load the API in the meantime 1231 VideoWrapper.loadYoutubeAPI(); 1232 1233 that.ytId = options.url.match(YOUTUBE_REGEXP)[2]; 1234 var src = 'https://www.youtube.com/embed/' + that.ytId + 1235 '?rel=0&autoplay=0&showinfo=0&controls=0&modestbranding=1' + 1236 '&cc_load_policy=0&disablekb=1&iv_load_policy=3&loop=0' + 1237 '&enablejsapi=1&origin=' + encodeURIComponent(window.location.origin); 1238 1239 that.__ytStartMuted = !!options.mute || options.mute === undefined; 1240 1241 $video = $('<iframe />') 1242 .attr({ 1243 'src_to_load': src 1244 }) 1245 .css({ 1246 'border': 0, 1247 'margin': 0, 1248 'padding': 0 1249 }) 1250 .data('player', that); 1251 1252 if (options.loop) { 1253 $video.on('ended.loop', function () { 1254 if (!that.__manuallyStopped) { 1255 that.play(); 1256 } 1257 }); 1258 } 1259 1260 that.ytReady = false; 1261 1262 setVideoElement(); 1263 1264 if (window['YT'] && window['YT'].loaded) { 1265 that._initYoutube(); 1266 $video.trigger('initsuccess'); 1267 } else { 1268 $(window).one('youtube_api_load', function () { 1269 that._initYoutube(); 1270 $video.trigger('initsuccess'); 1271 }); 1272 } 1273 1274 } else { 1275 // Traditional <video> tag with multiple sources 1276 1277 $video = $('<video>') 1278 .prop('autoplay', false) 1279 .prop('controls', false) 1280 .prop('loop', !!options.loop) 1281 .prop('muted', !!options.mute || options.mute === undefined) 1282 1283 // Let the first frames be available before playback, as we do transitions 1284 .prop('preload', 'auto') 1285 .prop('poster', options.poster || ''); 1286 1287 var sources = (options.url instanceof Array) ? options.url : [options.url]; 1288 1289 for (var i = 0; i < sources.length; i++) { 1290 var sourceItem = sources[i]; 1291 if (typeof (sourceItem) === 'string') { 1292 sourceItem = { 1293 src: sourceItem 1294 }; 1295 } 1296 $('<source>') 1297 .attr('src', sourceItem.src) 1298 // Make sure to not specify type if unknown - 1299 // so the browser will try to autodetect. 1300 .attr('type', sourceItem.type || null) 1301 .appendTo($video); 1302 } 1303 1304 if (!$video[0].canPlayType || !sources.length) { 1305 $video.trigger('initerror'); 1306 } else { 1307 $video.trigger('initsuccess'); 1308 } 1309 1310 setVideoElement(); 1311 } 1312 1313 }; 1314 1315 VideoWrapper.prototype._initYoutube = function () { 1316 var that = this; 1317 1318 var YT = window['YT']; 1319 1320 that.$video 1321 .attr('src', that.$video.attr('src_to_load')) 1322 .removeAttr('src_to_load'); 1323 1324 // It won't init if it's not in the DOM, so we emulate that 1325 var hasParent = !!that.$video[0].parentNode; 1326 if (!hasParent) { 1327 var $tmpParent = $('<div>').css('display', 'none !important').appendTo(document.body); 1328 that.$video.appendTo($tmpParent); 1329 } 1330 1331 var player = new YT.Player(that.video, { 1332 events: { 1333 'onReady': function () { 1334 1335 if (that.__ytStartMuted) { 1336 player.mute(); 1337 } 1338 1339 if (!hasParent) { 1340 // Restore parent to old state - without interrupting any changes 1341 if (that.$video[0].parentNode === $tmpParent[0]) { 1342 that.$video.detach(); 1343 } 1344 $tmpParent.remove(); 1345 } 1346 1347 that.ytReady = true; 1348 that._updateYoutubeSize(); 1349 that.$video.trigger('canplay'); 1350 }, 1351 'onStateChange': function (event) { 1352 switch (event.data) { 1353 case YT.PlayerState.PLAYING: 1354 that.$video.trigger('playing'); 1355 break; 1356 case YT.PlayerState.ENDED: 1357 that.$video.trigger('ended'); 1358 break; 1359 case YT.PlayerState.PAUSED: 1360 that.$video.trigger('pause'); 1361 break; 1362 case YT.PlayerState.BUFFERING: 1363 that.$video.trigger('waiting'); 1364 break; 1365 case YT.PlayerState.CUED: 1366 that.$video.trigger('canplay'); 1367 break; 1368 } 1369 }, 1370 'onPlaybackQualityChange': function () { 1371 that._updateYoutubeSize(); 1372 that.$video.trigger('resize'); 1373 }, 1374 'onError': function (err) { 1375 that.hasError = true; 1376 that.$video.trigger({ 1377 'type': 'error', 1378 'error': err 1379 }); 1380 } 1381 } 1382 }); 1383 1384 that.ytPlayer = player; 1385 1386 return that; 1387 }; 1388 1389 VideoWrapper.prototype._updateYoutubeSize = function () { 1390 var that = this; 1391 1392 switch (that.ytPlayer.getPlaybackQuality() || 'medium') { 1393 case 'small': 1394 that.video.videoWidth = 426; 1395 that.video.videoHeight = 240; 1396 break; 1397 case 'medium': 1398 that.video.videoWidth = 640; 1399 that.video.videoHeight = 360; 1400 break; 1401 default: 1402 case 'large': 1403 that.video.videoWidth = 854; 1404 that.video.videoHeight = 480; 1405 break; 1406 case 'hd720': 1407 that.video.videoWidth = 1280; 1408 that.video.videoHeight = 720; 1409 break; 1410 case 'hd1080': 1411 that.video.videoWidth = 1920; 1412 that.video.videoHeight = 1080; 1413 break; 1414 case 'highres': 1415 that.video.videoWidth = 2560; 1416 that.video.videoHeight = 1440; 1417 break; 1418 } 1419 1420 return that; 1421 }; 1422 1423 VideoWrapper.prototype.play = function () { 1424 var that = this; 1425 1426 that.__manuallyStopped = false;
1427 1428 if (that.type === 'youtube') { 1429 if (that.ytReady) { 1430 that.$video.trigger('play'); 1431 that.ytPlayer.playVideo(); 1432 } 1433 } else { 1434 that.video.play(); 1435 } 1436 1437 return that; 1438 }; 1439 1440 VideoWrapper.prototype.pause = function () { 1441 var that = this; 1442 1443 that.__manuallyStopped = false; 1444 1445 if (that.type === 'youtube') { 1446 if (that.ytReady) { 1447 that.ytPlayer.pauseVideo(); 1448 } 1449 } else { 1450 that.video.pause(); 1451 } 1452 1453 return that; 1454 }; 1455 1456 VideoWrapper.prototype.stop = function () { 1457 var that = this; 1458 1459 that.__manuallyStopped = true; 1460 1461 if (that.type === 'youtube') { 1462 if (that.ytReady) { 1463 that.ytPlayer.pauseVideo(); 1464 that.ytPlayer.seekTo(0); 1465 } 1466 } else { 1467 that.video.pause(); 1468 that.video.currentTime = 0; 1469 } 1470 1471 return that; 1472 }; 1473 1474 VideoWrapper.prototype.destroy = function () { 1475 var that = this; 1476 1477 if (that.ytPlayer) { 1478 that.ytPlayer.destroy(); 1479 } 1480 1481 that.$video.remove(); 1482 1483 return that; 1484 }; 1485 1486 VideoWrapper.prototype.getCurrentTime = function (seconds) { 1487 var that = this; 1488 1489 if (that.type === 'youtube') { 1490 if (that.ytReady) { 1491 return that.ytPlayer.getCurrentTime(); 1492 } 1493 } else { 1494 return that.video.currentTime; 1495 } 1496 1497 return 0; 1498 }; 1499 1500 VideoWrapper.prototype.setCurrentTime = function (seconds) { 1501 var that = this; 1502 1503 if (that.type === 'youtube') { 1504 if (that.ytReady) { 1505 that.ytPlayer.seekTo(seconds, true); 1506 } 1507 } else { 1508 that.video.currentTime = seconds; 1509 } 1510 1511 return that; 1512 }; 1513 1514 VideoWrapper.prototype.getDuration = function () { 1515 var that = this; 1516 1517 if (that.type === 'youtube') { 1518 if (that.ytReady) { 1519 return that.ytPlayer.getDuration(); 1520 } 1521 } else { 1522 return that.video.duration; 1523 } 1524 1525 return 0; 1526 }; 1527 1528 /** 1529 * This will load the youtube API (if not loaded yet) 1530 * Use $(window).one('youtube_api_load', ...) to listen for API loaded event 1531 */ 1532 VideoWrapper.loadYoutubeAPI = function () { 1533 if (window['YT'] && window['__yt_load_event_interval__']) { 1534 return; 1535 } 1536 1537 if (!window['YT'] && !$('script[src*=www\\.youtube\\.com\\/iframe_api]').length) { 1538 $('<script type="text/javascript" src="https://www.youtube.com/iframe_api">').appendTo('body'); 1539 } 1540 1541 window['__yt_load_event_interval__'] = setInterval(function () { 1542 if (window['YT'] && window['YT'].loaded) { 1543 $(window).trigger('youtube_api_load'); 1544 clearTimeout(window['__yt_load_event_interval__']); 1545 delete window['__yt_load_event_interval__']; 1546 } 1547 }, 50); 1548 }; 1549 1550 var getDeviceOrientation = function () { 1551 1552 if ('matchMedia' in window) { 1553 if (window.matchMedia("(orientation: portrait)").matches) { 1554 return 'portrait'; 1555 } else if (window.matchMedia("(orientation: landscape)").matches) { 1556 return 'landscape'; 1557 } 1558 } 1559 1560 if (screen.height > screen.width) { 1561 return 'portrait'; 1562 } 1563 1564 // Even square devices have orientation, 1565 // but a desktop browser may be too old for `matchMedia`. 1566 // Defaulting to `landscape` for the VERY rare case of a square desktop screen is good enough. 1567 return 'landscape'; 1568 }; 1569 1570 var getWindowOrientation = function () { 1571 if (window.innerHeight > window.innerWidth) { 1572 return 'portrait'; 1573 } 1574 if (window.innerWidth > window.innerHeight) { 1575 return 'landscape'; 1576 } 1577 1578 return 'square'; 1579 }; 1580 1581 /* SUPPORTS FIXED POSITION? 1582 * 1583 * Based on code from jQuery Mobile 1.1.0 1584 * http://jquerymobile.com/ 1585 * 1586 * In a nutshell, we need to figure out if fixed positioning is supported. 1587 * Unfortunately, this is very difficult to do on iOS, and usually involves 1588 * injecting content, scrolling the page, etc.. It's ugly. 1589 * jQuery Mobile uses this workaround. It's not ideal, but works. 1590 * 1591 * Modified to detect IE6 1592 * ========================= */ 1593 1594 var supportsFixedPosition = (function () { 1595 var ua = navigator.userAgent, 1596 platform = navigator.platform 1597 // Rendering engine is Webkit, and capture major version 1598 ,
1599 wkmatch = ua.match(/AppleWebKit\/([0-9]+)/), 1600 wkversion = !!wkmatch && wkmatch[1], 1601 ffmatch = ua.match(/Fennec\/([0-9]+)/), 1602 ffversion = !!ffmatch && ffmatch[1], 1603 operammobilematch = ua.match(/Opera Mobi\/([0-9]+)/), 1604 omversion = !!operammobilematch && operammobilematch[1], 1605 iematch = ua.match(/MSIE ([0-9]+)/), 1606 ieversion = !!iematch && iematch[1]; 1607 1608 return !( 1609 // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5) 1610 ((platform.indexOf("iPhone") > -1 || platform.indexOf("iPad") > -1 || platform.indexOf( 1611 "iPod") > -1) && wkversion && wkversion < 534) || 1612 1613 // Opera Mini 1614 (window.operamini && ({}).toString.call(window.operamini) === "[object OperaMini]") || 1615 (operammobilematch && omversion < 7458) || 1616 1617 //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2) 1618 (ua.indexOf("Android") > -1 && wkversion && wkversion < 533) || 1619 1620 // Firefox Mobile before 6.0 - 1621 (ffversion && ffversion < 6) || 1622 1623 // WebOS less than 3 1624 ("palmGetResource" in window && wkversion && wkversion < 534) || 1625 1626 // MeeGo 1627 (ua.indexOf("MeeGo") > -1 && ua.indexOf("NokiaBrowser/8.5.0") > -1) || 1628 1629 // IE6 1630 (ieversion && ieversion <= 6) 1631 ); 1632 }()); 1633 1634}(jQuery, window)); 1635 1636/*********************************************************************************************** 1637 scripts.js 1638**********************************************************************************************/ 1639$(document).ready(function () { 1640 $(".nav--mobile").click(function () { 1641 $("#NavMobile li").removeClass("active"); //Remove class active from Any li when open Overlay 1642 $("#level3").empty(); //empty level3 when open Overlay 1643 $("#Overlay").css("position", "absolute"); 1644 $(".open--menu").css("top", "-15px"); 1645 /*********Window 575******/ 1646 if (window.matchMedia('(max-width: 575px)').matches) { 1647 $(".open--menu").css("top", "-15px"); 1648 /*********Window 400******/ 1649 } else if (window.matchMedia('(max-width: 400px)').matches) { 1650 $(".open--menu").css("top", "-15px"); 1651 $(".open--menu").css("padding", "0"); 1652 } 1653 $(".open--menu").css("border", "none"); 1654 $("#backtoTop").css("display", "none !important"); 1655 $("#scrollUp i").css("display", "none !important"); 1656 $(".open--menu").removeClass("hover"); 1657 $(".open--menu").removeClass("toggleOverlay"); 1658 $(".hamburger__line").css("margin", "4px auto 0"); 1659 $(".hamburger__line").css("width", "1.7em"); 1660 $(".close").css("display", "none"); // Close before toggle 1661 $(".close").css("top", "-100px"); // Close before toggle 1662 /*********Start Toggle Menu******/ 1663 $('.hamburger__line').toggleClass("hamburger__line-active"); 1664 $("#NavMobile >li>ul.submenu").hide(); 1665 $("#NavMobile").css("display", "block"); 1666 $("#level3").css("display", "none"); 1667 $('.mobile--overlay').toggleClass("mobile--overlay_height"); 1668 $('body').toggleClass("body--lock"); 1669 /*********End Toggle Menu******/ 1670 /*********Start Overlay hamburger and close button******/ 1671 if ($(".hamburger__line").hasClass("hamburger__line-active")) { 1672 $(".open--menu").addClass("hover"); 1673 $(".open--menu").addClass("toggleOverlay"); 1674 // $(".open--menu").css("top", "30px"); 1675 $(".open--menu").css("border", "1px solid #ffffff63"); 1676 $(".open--menu").css("width", "41px"); 1677 $(".open--menu").css("height", "41px"); 1678 $(".hamburger__line").css("margin", "6px auto 0"); 1679 $(".hamburger__line").css("width", "1.2em"); 1680 $(".close").toggle(); // Close After toggle 1681 $(".close").css("top", "5px"); // Close After toggle 1682 $("span.open--menu.hover").css("top", "0px"); // Close After toggle 1683 } 1684 /*********End Overlay hamburger and close button******/ 1685 /*********Window 776******/ 1686 if (window.matchMedia('(max-width: 776px)').matches) { 1687 $("span.open--menu.hover").css("top", "-65px"); 1688 $("span.open--menu.hover").css("left", "66px !important"); 1689 } 1690 }); 1691 /*********End Hamuger Menu******/ 1692 /******************Arrows************/ 1693 $('#NavMobile > li>ul.submenu').parent().find("i.ic").addClass("fa-solid fa-angle-right"); 1694 $('li.child>a').after("<i class='fa-solid fa-angle-right'></i>"); 1695 if ($('#NavMobile > li>ul.submenu>
1695li:not(.child)')) { 1696 $('#NavMobile > li>ul.submenu>li:not(.child)>a').after("<i class='icon-next'></i>"); 1697 } 1698 /*********reset Overlay background******/ 1699 $("#scrollUp").css("diplay", "block"); 1700 $("#level3").empty(); 1701 /*********reset Overlay background******/ 1702 $("#NavMobile").on('click', 'li', function () { 1703 $("#Overlay").css("position", "fixed"); 1704 $("#NavMobile >li>ul.submenu").hide(); 1705 /*********start grey color******/ 1706 $("#NavMobile >li ").addClass("grey"); 1707 /*********max-776px******/ 1708 if (window.matchMedia('(max-width: 776px)').matches) { 1709 $(".grey a").css("color", "#fff"); 1710 } else { 1711 $(".grey a").css("color", "#a6adb3"); 1712 } 1713 /*********End grey color******/ 1714 $("#NavMobile >li.active").removeClass("active"); 1715 $(this).siblings().find("li").removeClass("active"); // remove active class from any li @another submenu 1716 $(this).addClass("active"); //white 1717 /***************Startsubmenu******************/ 1718 if ($(this).hasClass("active")) { 1719 $("#NavMobile >li.active >ul.submenu").show(); 1720 } else { 1721 $("#NavMobile >li.active >ul.submenu").hide(); 1722 } 1723 /***************Endsubmenu******************/ 1724 /***************Display Level 3******************/ 1725 if ($("#NavMobile >li.active").hasClass("hasBranch")) { 1726 $("#level3").css("display", "block"); 1727 } else { 1728 $("#level3").css("display", "none"); 1729 } 1730 /***************End Display Level 3******************/ 1731 /**********Hide Level 3 when open another li*********/ 1732 if ($("#NavMobile >li").hasClass("active")) { 1733 if (!$(this).hasClass("hasBranch")) { 1734 $("#level3").empty(); 1735 } 1736 } 1737 }); //end click li 1738 /***********Start Random Overlay Background****************/ 1739 $("li.nav-item >a").click(function (e) { 1740 e.preventDefault(); 1741 var path = 'img/main-menu/'; 1742 var bgArray = ['t0.webp', 't1.webp', 't2.webp', 't3.webp', 't4.webp', 't5.webp', 't6.webp', 't7.webp', 't8.webp', 't9.webp']; 1743 var bg = bgArray[Math.floor(Math.random() * bgArray.length)]; 1744 $("#Overlay").backstretch([path + bg], { 1745 fade: 750, 1746 duration: 4000 1747 }); 1748 }); 1749 /***************End Overlay Background******************/ 1750}); 1751/*******************start Level 3****************************/ 1752$("ul.submenu").on('click', 'li', function () { 1753 if (window.matchMedia('(max-width: 991px)').matches) { 1754 $(".reverse").css("display", "block"); 1755 $(".reverse").html("<i class='fa-solid fa-angle-left'></i>"); 1756 } 1757 $("#level3").empty(); 1758 $("ul.submenu li ").addClass("grey"); 1759 $("ul.submenu>li.active").removeClass("active"); 1760 $(this).addClass("active"); //white 1761 /******start Print Title of level3*****/ 1762 var secondLevel_li = $("ul.submenu li.active>a").text(); 1763 $('.level3_Title').html("<i class='icon-next'></i>" + secondLevel_li); 1764 /******end Print Title of level3*****/ 1765 if ($("ul.submenu>li.active").hasClass("child")) { 1766 var content = $("li.active.child>.branch").html(); 1767 $("#level3").toggle().html(content); 1768 } else { 1769 $("#level3").empty(); 1770 } 1771}); 1772/*************************End Level 3********************************/ 1773/*************start Move Level 3 Mobile*********************/ 1774if (window.matchMedia('(max-width: 991px)').matches) { 1775 $("ul.submenu").on('click', 'li', function () { 1776 $("ul.submenu >li.active").removeClass("active"); 1777 $("#level3").css("display", "none"); 1778 // adding classname 'active' to current click li 1779 $(this).addClass("active "); //white 1780 if ($("li.child").hasClass("active")) { 1781 $("#level3").css("display", "block"); 1782 $("ul.submenu").addClass("wow fadeInLeft"); 1783 $("#NavMobile").css("display", "none"); 1784 $("ul.quickLinks2").addClass("absolute"); 1785 } 1786 }); 1787} 1788/*************************End Move Level 3**********************/
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.