1/*! 2 * Name : Just Another Parallax [Jarallax] 3 * Version : 1.7.3 4 * Author : _nK https://nkdev.info 5 * GitHub : https://github.com/nk-o/jarallax 6 */ 7(function (window) { 8 'use strict'; 9 10 // Adapted from https://gist.github.com/paulirish/1579671 11 if(!Date.now) { 12 Date.now = function () { return new Date().getTime(); }; 13 } 14 if(!window.requestAnimationFrame) { 15 (function () { 16 17 var vendors = ['webkit', 'moz']; 18 for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) { 19 var vp = vendors[i]; 20 window.requestAnimationFrame = window[vp+'RequestAnimationFrame']; 21 window.cancelAnimationFrame = window[vp+'CancelAnimationFrame'] 22 || window[vp+'CancelRequestAnimationFrame']; 23 } 24 if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy 25 || !window.requestAnimationFrame || !window.cancelAnimationFrame) { 26 var lastTime = 0; 27 window.requestAnimationFrame = function (callback) { 28 var now = Date.now(); 29 var nextTime = Math.max(lastTime + 16, now); 30 return setTimeout(function () { callback(lastTime = nextTime); }, 31 nextTime - now); 32 }; 33 window.cancelAnimationFrame = clearTimeout; 34 } 35 }()); 36 } 37 38 var supportTransform = (function () { 39 if (!window.getComputedStyle) { 40 return false; 41 } 42 43 var el = document.createElement('p'), 44 has3d, 45 transforms = { 46 'webkitTransform':'-webkit-transform', 47 'OTransform':'-o-transform', 48 'msTransform':'-ms-transform', 49 'MozTransform':'-moz-transform', 50 'transform':'transform' 51 }; 52 53 // Add it to the body to get the computed style. 54 (document.body || document.documentElement).insertBefore(el, null); 55 56 for (var t in transforms) { 57 if (typeof el.style[t] !== 'undefined') { 58 el.style[t] = "translate3d(1px,1px,1px)"; 59 has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]); 60 } 61 } 62 63 (document.body || document.documentElement).removeChild(el); 64 65 return typeof has3d !== 'undefined' && has3d.length > 0 && has3d !== "none"; 66 }()); 67 68 var isAndroid = navigator.userAgent.toLowerCase().indexOf('android') > -1; 69 var isIOs = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; 70 var isOperaOld = !!window.opera; 71 var isEdge = /Edge\/\d+/.test(navigator.userAgent); 72 var isIE11 = /Trident.*rv[ :]*11\./.test(navigator.userAgent); 73 var isIE10 = !!Function('/*@cc_on return document.documentMode===10@*/')(); 74 var isIElt10 = document.all && !window.atob; 75 76 var wndW; 77 var wndH; 78 function updateWndVars () { 79 wndW = window.innerWidth || document.documentElement.clientWidth; 80 wndH = window.innerHeight || document.documentElement.clientHeight; 81 } 82 updateWndVars(); 83 84 // list with all jarallax instances 85 // need to render all in one scroll/resize event 86 var jarallaxList = []; 87 88 // Jarallax instance 89 var Jarallax = (function () { 90 var instanceID = 0; 91 92 function Jarallax_inner (item, userOptions) { 93 var _this = this, 94 dataOptions; 95 96 _this.$item = item; 97 98 _this.defaults = { 99 type : 'scroll', // type of parallax: scroll, scale, opacity, scale-opacity, scroll-opacity 100 speed : 0.5, // supported value from -1 to 2 101 imgSrc : null, 102 imgWidth : null, 103 imgHeight : null, 104 enableTransform : true, 105 elementInViewport : null, 106 zIndex : -100, 107 noAndroid : false,
108 noIos : true, 109 110 // events 111 onScroll : null, // function(calculations) {} 112 onInit : null, // function() {} 113 onDestroy : null, // function() {} 114 onCoverImage : null // function() {} 115 }; 116 dataOptions = JSON.parse(_this.$item.getAttribute('data-jarallax') || '{}'); 117 _this.options = _this.extend({}, _this.defaults, dataOptions, userOptions); 118 119 // stop init if android or ios 120 if(isAndroid && _this.options.noAndroid || isIOs && _this.options.noIos) { 121 return; 122 } 123 124 // fix speed option [-1.0, 2.0] 125 _this.options.speed = Math.min(2, Math.max(-1, parseFloat(_this.options.speed))); 126 127 // custom element to check if parallax in viewport 128 var elementInVP = _this.options.elementInViewport; 129 // get first item from array 130 if(elementInVP && typeof elementInVP === 'object' && typeof elementInVP.length !== 'undefined') { 131 elementInVP = elementInVP[0]; 132 } 133 // check if dom element 134 if(!elementInVP instanceof Element) { 135 elementInVP = null; 136 } 137 _this.options.elementInViewport = elementInVP; 138 139 _this.instanceID = instanceID++; 140 141 _this.image = { 142 src : _this.options.imgSrc || null, 143 $container : null, 144 $item : null, 145 width : _this.options.imgWidth || null, 146 height : _this.options.imgHeight || null, 147 // fix for some devices 148 // use <img> instead of background image - more smoothly 149 useImgTag : isIOs || isAndroid || isOperaOld || isIE11 || isIE10 || isEdge 150 }; 151 152 if(_this.initImg()) { 153 _this.init(); 154 } 155 } 156 157 return Jarallax_inner; 158 }()); 159 160 // add styles to element 161 Jarallax.prototype.css = function (el, styles) { 162 if(typeof styles === 'string') { 163 if(window.getComputedStyle) { 164 return window.getComputedStyle(el).getPropertyValue(styles); 165 } 166 return el.style[styles]; 167 } 168 169 // add transform property with vendor prefixes 170 if(styles.transform) { 171 styles.WebkitTransform = styles.MozTransform = styles.transform; 172 } 173 174 for(var k in styles) { 175 el.style[k] = styles[k]; 176 } 177 return el; 178 }; 179 // Extend like jQuery.extend 180 Jarallax.prototype.extend = function (out) { 181 out = out || {}; 182 for (var i = 1; i < arguments.length; i++) { 183 if (!arguments[i]) { 184 continue; 185 } 186 for (var key in arguments[i]) { 187 if (arguments[i].hasOwnProperty(key)) { 188 out[key] = arguments[i][key]; 189 } 190 } 191 } 192 return out; 193 }; 194 195 // Jarallax functions 196 Jarallax.prototype.initImg = function () { 197 var _this = this; 198 199 // get image src 200 if(_this.image.src === null) { 201 _this.image.src = _this.css(_this.$item, 'background-image').replace(/^url\(['"]?/g,'').replace(/['"]?\)$/g,''); 202 } 203 return !(!_this.image.src || _this.image.src === 'none'); 204 }; 205 206 Jarallax.prototype.init = function () { 207 var _this = this, 208 containerStyles = { 209 position : 'absolute', 210 top : 0, 211 left : 0, 212 width : '100%', 213 height : '100%', 214 overflow : 'hidden', 215 pointerEvents : 'none' 216 }, 217 imageStyles = { 218 position : 'fixed' 219 }; 220 221 // save default user styles 222 _this.$item.setAttribute('data-jarallax-original-styles', _this.$item.getAttribute('style')); 223 224 // set relative position and z-index to the parent 225 if (_this.css(_this.$item, 'position') === 'static') { 226 _this.css(_this.$item, { 227 position: 'relative' 228 }); 229 } 230 if (_this.css(_this.$item, 'z-index') === 'auto') { 231 _this.css(_this.$item, { 232 zIndex: 0 233 }); 234 } 235 236 // container for parallax image 237 _this.image.$container = document.createElement('div'); 238 _this.css(_this.image.$container, containerStyles); 239 _this.css(_this.image.$container, { 240 visibility : 'hidden', 241 'z-index' : _this.options.zIndex 242 }); 243 _this.image.$container.setAttribute('id', 'jarallax-container-' + _this.instanceID); 244 _this.$item.appendChild(_this.image.$container); 245 246 // use img tag 247 if(_this.image.useImgTag && supportTransform && _this.options.enableTransform) { 248 _this.image.$item = document.createElement('img'); 249 _this.image.$item.setAttribute('src', _this.image.src); 250 imageStyles = _this.extend({ 251 'max-width' : 'none' 252 }, containerStyles, imageStyles); 253 } 254 255 // use div with background image 256 else { 257 _this.image.$item = document.createElement('div'); 258 imageStyles = _this.extend({ 259 'background-position' : '50% 50%', 260 'background-size' : '100% auto', 261 'background-repeat' : 'no-repeat no-repeat', 262 'background-image' : 'url("' + _this.image.src + '")' 263 }, containerStyles, imageStyles); 264 } 265 266 // fix for IE9 and less 267 if(isIElt10) { 268 imageStyles.backgroundAttachment = 'fixed'; 269 } 270 271 // check if one of parents have transform style (without this check, scroll transform will be inverted) 272 // discussion - https://github.com/nk-o/jarallax/issues/9 273 _this.parentWithTransform = 0; 274 var $itemParents = _this.$item; 275 while ($itemParents !== null && $itemParents !== document && _this.parentWithTransform === 0) { 276 var parent_transform = _this.css($itemParents, '-webkit-transform') || _this.css($itemParents, '-moz-transform') || _this.css($itemParents, 'transform'); 277 if(parent_transform && parent_transform !== 'none') { 278 _this.parentWithTransform = 1; 279 280 // add transform on parallax container if there is parent with transform 281 _this.css(_this.image.$container, { 282 transform: 'translateX(0) translateY(0)' 283 }); 284 } 285 $itemParents = $itemParents.parentNode; 286 } 287 288 // parallax image 289 _this.css(_this.image.$item, imageStyles); 290 _this.image.$container.appendChild(_this.image.$item); 291 292 // cover image if width and height is ready 293 function initAfterReady () { 294 _this.coverImage(); 295 _this.clipContainer(); 296 _this.onScroll(true); 297 298 // call onInit event 299 if(_this.options.onInit) { 300 _this.options.onInit.call(_this); 301 } 302 303 // timeout to fix IE blinking 304 setTimeout(function () { 305 if(_this.$item) { 306 // remove default user background 307 _this.css(_this.$item, { 308 'background-image' : 'none', 309 'background-attachment' : 'scroll', 310 'background-size' : 'auto' 311 }); 312 } 313 }, 0); 314 } 315 316 if(_this.image.width && _this.image.height) { 317 // init if width and height already exists 318 initAfterReady(); 319 } else { 320 // load image and get width and height 321 _this.getImageSize(_this.image.src, function (width, height) { 322 _this.image.width = width; 323 _this.image.height = height; 324 initAfterReady(); 325 }); 326 } 327 328 jarallaxList.push(_this); 329 }; 330 331 Jarallax.prototype.destroy = function () { 332 var _this = this; 333 334 // remove from instances list 335 for(var k = 0, len = jarallaxList.length; k < len; k++) { 336 if(jarallaxList[k].instanceID === _this.instanceID) { 337 jarallaxList.splice(k, 1); 338 break; 339 } 340 } 341 342 // return styles on container as before jarallax init 343 var originalStylesTag = _this.$item.getAttribute('data-jarallax-original-styles'); 344 _this.$item.removeAttribute('data-jarallax-original-styles'); 345 // null occurs if there is no style tag before jarallax init 346 if(originalStylesTag === 'null') { 347 _this.$item.removeAttribute('style'); 348 } else { 349 _this.$item.setAttribute('style', originalStylesTag); 350 } 351 352 // remove additional dom elements 353 if(_this.$clipStyles) { 354 _this.$clipStyles.parentNode.removeChild(_this.$clipStyles); 355 } 356 _this.image.$container.parentNode.removeChild(_this.image.$container); 357 358 // call onDestroy event 359 if(_this.options.onDestroy) { 360 _this.options.onDestroy.call(_this); 361 } 362 363 // delete jarallax from item 364 delete _this.$item.jarallax; 365 366 // delete all variables 367 for(var n in _this) { 368 delete _this[n]; 369 } 370 }; 371 372 Jarallax.prototype.getImageSize = function (src, callback) { 373 if(!src || !callback) { 374 return; 375 } 376 377 var tempImg = new Image(); 378 tempImg.onload = function () { 379 callback(tempImg.width, tempImg.height); 380 }; 381 tempImg.src = src; 382 }; 383 384 // it will remove some image overlapping 385 // overlapping occur due to an image position fixed inside absolute position element (webkit based browsers works without any fix) 386 Jarallax.prototype.clipContainer = function () { 387 // clip is not working properly on real IE9 and less 388 if(isIElt10) { 389 return; 390 } 391 392 var _this = this, 393 rect = _this.image.$container.getBoundingClientRect(), 394 width = rect.width, 395 height = rect.height; 396 397 if(!_this.$clipStyles) { 398 _this.$clipStyles = document.createElement('style'); 399 _this.$clipStyles.setAttribute('type', 'text/css'); 400 _this.$clipStyles.setAttribute('id', '#jarallax-clip-' + _this.instanceID); 401 var head = document.head || document.getElementsByTagName('head')[0]; 402 head.appendChild(_this.$clipStyles); 403 } 404 405 var styles = [ 406 '#jarallax-container-' + _this.instanceID + ' {', 407 ' clip: rect(0 ' + width + 'px ' + height + 'px 0);', 408 ' clip: rect(0, ' + width + 'px, ' + height + 'px, 0);', 409 '}' 410 ].join('\n'); 411 412 // add clip styles inline (this method need for support IE8 and less browsers) 413 if (_this.$clipStyles.styleSheet){ 414 _this.$clipStyles.styleSheet.cssText = styles; 415 } else { 416 _this.$clipStyles.innerHTML = styles; 417 } 418 }; 419 420 Jarallax.prototype.coverImage = function () { 421 var _this = this; 422 423 if(!_this.image.width || !_this.image.height) { 424 return; 425 } 426 427 var rect = _this.image.$container.getBoundingClientRect(), 428 contW = rect.width, 429 contH = rect.height, 430 contL = rect.left, 431 imgW = _this.image.width, 432 imgH = _this.image.height, 433 speed = _this.options.speed, 434 isScroll = _this.options.type === 'scroll' || _this.options.type === 'scroll-opacity', 435 scrollDist = 0, 436 resultW = 0, 437 resultH = contH, 438 resultML = 0, 439 resultMT = 0; 440 441 // scroll parallax 442 if(isScroll) { 443 // scroll distance and height for image 444 if (speed < 0) { 445 scrollDist = speed * Math.max(contH, wndH); 446 } else { 447 scrollDist = speed * (contH + wndH); 448 } 449 450 // size for scroll parallax 451 if (speed > 1) { 452 resultH = Math.abs(scrollDist - wndH); 453 } else if (speed < 0) { 454 resultH = scrollDist / speed + Math.abs(scrollDist); 455 } else { 456 resultH += Math.abs(wndH - contH) * (1 - speed); 457 } 458 459 scrollDist /= 2; 460 } 461 462 // calculate width relative to height and image size 463 resultW = resultH * imgW / imgH; 464 if(resultW < contW) { 465 resultW = contW; 466 resultH = resultW * imgH / imgW; 467 } 468 469 // when disabled transformations, height should be >= window height 470 _this.bgPosVerticalCenter = 0; 471 if(isScroll && resultH < wndH && (!supportTransform || !_this.options.enableTransform)) { 472 _this.bgPosVerticalCenter = (wndH - resultH) / 2; 473 resultH = wndH; 474 } 475 476 // center parallax image 477 if(isScroll) { 478 resultML = contL + (contW - resultW) / 2; 479 resultMT = (wndH - resultH) / 2; 480 } else { 481 resultML = (contW - resultW) / 2; 482 resultMT = (contH - resultH) / 2; 483 } 484 485 // fix if parents with transform style 486 if(supportTransform && _this.options.enableTransform && _this.parentWithTransform) { 487 resultML -= contL; 488 } 489 490 // store scroll distance 491 _this.parallaxScrollDistance = scrollDist; 492 493 // apply result to item 494 _this.css(_this.image.$item, { 495 width: resultW + 'px', 496 height: resultH + 'px', 497 marginLeft: resultML + 'px', 498 marginTop: resultMT + 'px' 499 }); 500 501 // call onCoverImage event 502 if(_this.options.onCoverImage) { 503 _this.options.onCoverImage.call(_this); 504 } 505 }; 506 507 Jarallax.prototype.isVisible = function () { 508 return this.isElementInViewport || false; 509 }; 510 511 Jarallax.prototype.onScroll = function (force) { 512 var _this = this; 513 514 if(!_this.image.width || !_this.image.height) { 515 return; 516 } 517 518 var rect = _this.$item.getBoundingClientRect(), 519 contT = rect.top, 520 contH = rect.height, 521 styles = { 522 position : 'absolute', 523 visibility : 'visible', 524 backgroundPosition : '50% 50%' 525 }; 526 527 // check if in viewport 528 var viewportRect = rect; 529 if(_this.options.elementInViewport) { 530 viewportRect = _this.options.elementInViewport.getBoundingClientRect(); 531 } 532 _this.isElementInViewport = 533 viewportRect.bottom >= 0 && 534 viewportRect.right >= 0 && 535 viewportRect.top <= wndH && 536 viewportRect.left <= wndW; 537 538 // stop calculations if item is not in viewport 539 if (force ? false : !_this.isElementInViewport) { 540 return; 541 } 542 543 // calculate parallax helping variables 544 var beforeTop = Math.max(0, contT), 545 beforeTopEnd = Math.max(0, contH + contT), 546 afterTop = Math.max(0, -contT), 547 beforeBottom = Math.max(0, contT + contH - wndH), 548 beforeBottomEnd = Math.max(0, contH - (contT + contH - wndH)), 549 afterBottom = Math.max(0, -contT + wndH - contH), 550 fromViewportCenter = 1 - 2 * (wndH - contT) / (wndH + contH); 551 552 // calculate on how percent of section is visible 553 var visiblePercent = 1; 554 if(contH < wndH) { 555 visiblePercent = 1 - (afterTop || beforeBottom) / contH; 556 } else { 557 if(beforeTopEnd <= wndH) { 558 visiblePercent = beforeTopEnd / wndH; 559 } else if (beforeBottomEnd <= wndH) { 560 visiblePercent = beforeBottomEnd / wndH; 561 } 562 } 563 564 // opacity 565 if(_this.options.type === 'opacity' || _this.options.type === 'scale-opacity' || _this.options.type === 'scroll-opacity') { 566 styles.transform = 'translate3d(0, 0, 0)'; 567
567 styles.opacity = visiblePercent; 568 } 569 570 // scale 571 if(_this.options.type === 'scale' || _this.options.type === 'scale-opacity') { 572 var scale = 1; 573 if(_this.options.speed < 0) { 574 scale -= _this.options.speed * visiblePercent; 575 } else { 576 scale += _this.options.speed * (1 - visiblePercent); 577 } 578 styles.transform = 'scale(' + scale + ') translate3d(0, 0, 0)'; 579 } 580 581 // scroll 582 if(_this.options.type === 'scroll' || _this.options.type === 'scroll-opacity') { 583 var positionY = _this.parallaxScrollDistance * fromViewportCenter; 584 585 if(supportTransform && _this.options.enableTransform) { 586 // fix if parents with transform style 587 if(_this.parentWithTransform) { 588 positionY -= contT; 589 } 590 591 styles.transform = 'translate3d(0, ' + positionY + 'px, 0)'; 592 } else if (_this.image.useImgTag) { 593 styles.top = positionY + 'px'; 594 } else { 595 // vertical centering 596 if(_this.bgPosVerticalCenter) { 597 positionY += _this.bgPosVerticalCenter; 598 } 599 styles.backgroundPosition = '50% ' + positionY + 'px'; 600 } 601 602 // fixed position is not work properly for IE9 and less 603 // solution - use absolute position and emulate fixed by using container offset 604 styles.position = isIElt10 ? 'absolute' : 'fixed'; 605 } 606 607 _this.css(_this.image.$item, styles); 608 609 // call onScroll event 610 if(_this.options.onScroll) { 611 _this.options.onScroll.call(_this, { 612 section: rect, 613 614 beforeTop: beforeTop, 615 beforeTopEnd: beforeTopEnd, 616 afterTop: afterTop, 617 beforeBottom: beforeBottom, 618 beforeBottomEnd: beforeBottomEnd, 619 afterBottom: afterBottom, 620 621 visiblePercent: visiblePercent, 622 fromViewportCenter: fromViewportCenter 623 }); 624 } 625 }; 626 627 628 // init events 629 function addEventListener (el, eventName, handler) { 630 if (el.addEventListener) { 631 el.addEventListener(eventName, handler); 632 } else { 633 el.attachEvent('on' + eventName, function (){ 634 handler.call(el); 635 }); 636 } 637 } 638 639 function update (e) { 640 window.requestAnimationFrame(function () { 641 if(e.type !== 'scroll') { 642 updateWndVars(); 643 } 644 for(var k = 0, len = jarallaxList.length; k < len; k++) { 645 // cover image and clip needed only when parallax container was changed 646 if(e.type !== 'scroll') { 647 jarallaxList[k].coverImage(); 648 jarallaxList[k].clipContainer(); 649 } 650 jarallaxList[k].onScroll(); 651 } 652 }); 653 } 654 addEventListener(window, 'scroll', update); 655 addEventListener(window, 'resize', update); 656 addEventListener(window, 'orientationchange', update); 657 addEventListener(window, 'load', update); 658 659 660 // global definition 661 var plugin = function (items) { 662 // check for dom element 663 // thanks: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object 664 if(typeof HTMLElement === "object" ? items instanceof HTMLElement : items && typeof items === "object" && items !== null && items.nodeType === 1 && typeof items.nodeName==="string") { 665 items = [items]; 666 } 667 668 var options = arguments[1], 669 args = Array.prototype.slice.call(arguments, 2), 670 len = items.length, 671 k = 0, 672 ret; 673 674 for (k; k < len; k++) { 675 if (typeof options === 'object' || typeof options === 'undefined') { 676 if(!items[k].jarallax) { 677 items[k].jarallax = new Jarallax(items[k], options); 678 } 679 } 680 else if(items[k].jarallax) { 681 ret = items[k].jarallax[options].apply(items[k].jarallax, args); 682 } 683 if (typeof ret !== 'undefined') { 684 return ret; 685 } 686 } 687 688 return items; 689 }; 690 plugin.constructor = Jarallax; 691 692 // no conflict 693 var oldPlugin = window.jarallax; 694 window.jarallax = plugin; 695 window.jarallax.noConflict = function () { 696 window.jarallax = oldPlugin; 697 return this; 698 }; 699 700 // jQuery support 701 if(typeof jQuery !== 'undefined') { 702 var jQueryPlugin = function () { 703 var args = arguments || []; 704 Array.prototype.unshift.call(args, this); 705 var res = plugin.apply(window, args); 706 return typeof res !== 'object' ? res : this; 707 }; 708 jQueryPlugin.constructor = Jarallax; 709 710 // no conflict 711 var oldJqPlugin = jQuery.fn.jarallax; 712 jQuery.fn.jarallax = jQueryPlugin; 713 jQuery.fn.jarallax.noConflict = function () { 714 jQuery.fn.jarallax = oldJqPlugin; 715 return this; 716 }; 717 } 718 719 // data-jarallax initialization 720 addEventListener(window, 'DOMContentLoaded', function () { 721 plugin(document.querySelectorAll('[data-jarallax], [data-jarallax-video]')); 722 }); 723}(window));
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.