1/*! 2 * Name : Just Another Parallax [Jarallax] 3 * Version : 1.7.1 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 // container for parallax image 222 _this.image.$container = document.createElement('div'); 223 _this.css(_this.image.$container, containerStyles); 224 _this.css(_this.image.$container, { 225 visibility : 'hidden', 226 'z-index' : _this.options.zIndex 227 }); 228 _this.image.$container.setAttribute('id', 'jarallax-container-' + _this.instanceID); 229 _this.$item.appendChild(_this.image.$container); 230 231 // use img tag 232 if(_this.image.useImgTag && supportTransform && _this.options.enableTransform) { 233 _this.image.$item = document.createElement('img'); 234 _this.image.$item.setAttribute('src', _this.image.src); 235 imageStyles = _this.extend({ 236 'max-width' : 'none' 237 }, containerStyles, imageStyles); 238 } 239 240 // use div with background image 241 else { 242 _this.image.$item = document.createElement('div'); 243 imageStyles = _this.extend({ 244 'background-position' : '50% 50%', 245 'background-size' : '100% auto', 246 'background-repeat' : 'no-repeat no-repeat', 247 'background-image' : 'url("' + _this.image.src + '")' 248 }, containerStyles, imageStyles); 249 } 250 251 // fix for IE9 and less 252 if(isIElt10) { 253 imageStyles.backgroundAttachment = 'fixed'; 254 } 255 256 // check if one of parents have transform style (without this check, scroll transform will be inverted) 257 // discussion - https://github.com/nk-o/jarallax/issues/9 258 _this.parentWithTransform = 0; 259 var $itemParents = _this.$item; 260 while ($itemParents !== null && $itemParents !== document && _this.parentWithTransform === 0) { 261 var parent_transform = _this.css($itemParents, '-webkit-transform') || _this.css($itemParents, '-moz-transform') || _this.css($itemParents, 'transform'); 262 if(parent_transform && parent_transform !== 'none') { 263 _this.parentWithTransform = 1; 264 265 // add transform on parallax container if there is parent with transform 266 _this.css(_this.image.$container, { 267 transform: 'translateX(0) translateY(0)' 268 }); 269 } 270 $itemParents = $itemParents.parentNode; 271 } 272 273 // parallax image 274 _this.css(_this.image.$item, imageStyles); 275 _this.image.$container.appendChild(_this.image.$item); 276 277 // cover image if width and height is ready 278 function initAfterReady () { 279 _this.coverImage(); 280 _this.clipContainer(); 281 _this.onScroll(true); 282 283 // save default user styles 284 _this.$item.setAttribute('data-jarallax-original-styles', _this.$item.getAttribute('style')); 285 286 // call onInit event 287 if(_this.options.onInit) { 288 _this.options.onInit.call(_this); 289 } 290 291 // timeout to fix IE blinking 292 setTimeout(function () { 293 if(_this.$item) { 294 // remove default user background 295 _this.css(_this.$item, { 296 'background-image' : 'none', 297 'background-attachment' : 'scroll', 298 'background-size' : 'auto' 299 }); 300 } 301 }, 0); 302 } 303 304 if(_this.image.width && _this.image.height) { 305 // init if width and height already exists 306 initAfterReady(); 307 } else { 308 // load image and get width and height 309 _this.getImageSize(_this.image.src, function (width, height) { 310 _this.image.width = width; 311 _this.image.height = height; 312 initAfterReady(); 313 }); 314 } 315 316 jarallaxList.push(_this); 317 }; 318 319 Jarallax.prototype.destroy = function () { 320 var _this = this; 321 322 // remove from instances list 323 for(var k = 0, len = jarallaxList.length; k < len; k++) { 324 if(jarallaxList[k].instanceID === _this.instanceID) { 325 jarallaxList.splice(k, 1); 326 break; 327 } 328 } 329 330 // return styles on container as before jarallax init 331 var originalStylesTag = _this.$item.getAttribute('data-jarallax-original-styles'); 332 _this.$item.removeAttribute('data-jarallax-original-styles'); 333 // null occurs if there is no style tag before jarallax init 334 if(originalStylesTag === 'null') { 335 _this.$item.removeAttribute('style'); 336 } else { 337 _this.$item.setAttribute('style', originalStylesTag); 338 } 339 340 // remove additional dom elements 341 if(_this.$clipStyles) { 342 _this.$clipStyles.parentNode.removeChild(_this.$clipStyles); 343 } 344 _this.image.$container.parentNode.removeChild(_this.image.$container); 345 346 // call onDestroy event 347 if(_this.options.onDestroy) { 348 _this.options.onDestroy.call(_this); 349 } 350 351 // delete jarallax from item 352 delete _this.$item.jarallax; 353 354 // delete all variables 355 for(var n in _this) { 356 delete _this[n]; 357 } 358 }; 359 360 Jarallax.prototype.getImageSize = function (src, callback) { 361 if(!src || !callback) { 362 return; 363 } 364 365 var tempImg = new Image(); 366 tempImg.onload = function () { 367 callback(tempImg.width, tempImg.height); 368 }; 369 tempImg.src = src; 370 }; 371 372 // it will remove some image overlapping 373 // overlapping occur due to an image position fixed inside absolute position element (webkit based browsers works without any fix) 374 Jarallax.prototype.clipContainer = function () { 375 // clip is not working properly on real IE9 and less 376 if(isIElt10) { 377 return; 378 } 379 380 var _this = this, 381 rect = _this.image.$container.getBoundingClientRect(), 382 width = rect.width, 383 height = rect.height; 384 385 if(!_this.$clipStyles) { 386 _this.$clipStyles = document.createElement('style'); 387 _this.$clipStyles.setAttribute('type', 'text/css'); 388 _this.$clipStyles.setAttribute('id', '#jarallax-clip-' + _this.instanceID); 389 var head = document.head || document.getElementsByTagName('head')[0]; 390 head.appendChild(_this.$clipStyles); 391 } 392 393 var styles = [ 394 '#jarallax-container-' + _this.instanceID + ' {', 395 ' clip: rect(0 ' + width + 'px ' + height + 'px 0);', 396 ' clip: rect(0, ' + width + 'px, ' + height + 'px, 0);', 397 '}' 398 ].join('\n'); 399 400 // add clip styles inline (this method need for support IE8 and less browsers) 401 if (_this.$clipStyles.styleSheet){ 402 _this.$clipStyles.styleSheet.cssText = styles; 403 } else { 404 _this.$clipStyles.innerHTML = styles; 405 } 406 }; 407 408 Jarallax.prototype.coverImage = function () { 409 var _this = this; 410 411 if(!_this.image.width || !_this.image.height) { 412 return; 413 } 414 415 var rect = _this.image.$container.getBoundingClientRect(), 416 contW = rect.width, 417 contH = rect.height, 418 contL = rect.left, 419 imgW = _this.image.width, 420 imgH = _this.image.height, 421 speed = _this.options.speed, 422 isScroll = _this.options.type === 'scroll' || _this.options.type === 'scroll-opacity', 423 scrollDist = 0, 424 resultW = 0, 425 resultH = contH, 426 resultML = 0, 427 resultMT = 0; 428 429 // scroll parallax 430 if(isScroll) { 431 // scroll distance 432 scrollDist = speed * (contH + wndH) / 2; 433 if(speed < 0 || speed > 1) { 434 scrollDist = speed * Math.max(contH, wndH) / 2; 435 } 436 437 // size for scroll parallax 438 if(speed < 0 || speed > 1) { 439 resultH = Math.max(contH, wndH) + Math.abs(scrollDist) * 2; 440 } else { 441 resultH += Math.abs(wndH - contH) * (1 - speed); 442 } 443 } 444 445 // calculate width relative to height and image size 446 resultW = resultH * imgW / imgH; 447 if(resultW < contW) { 448 resultW = contW; 449 resultH = resultW * imgH / imgW; 450 } 451 452 // when disabled transformations, height should be >= window height 453 _this.bgPosVerticalCenter = 0; 454 if(isScroll && resultH < wndH && (!supportTransform || !_this.options.enableTransform)) { 455 _this.bgPosVerticalCenter = (wndH - resultH) / 2; 456 resultH = wndH; 457 } 458 459 // center parallax image 460 if(isScroll) { 461 resultML = contL + (contW - resultW) / 2; 462 resultMT = (wndH - resultH) / 2; 463 } else { 464 resultML = (contW - resultW) / 2; 465 resultMT = (contH - resultH) / 2; 466 } 467 468 // fix if parents with transform style 469 if(supportTransform && _this.options.enableTransform && _this.parentWithTransform) { 470 resultML -= contL; 471 } 472 473 // store scroll distance 474 _this.parallaxScrollDistance = scrollDist; 475 476 // apply result to item 477 _this.css(_this.image.$item, { 478 width: resultW + 'px', 479 height: resultH + 'px', 480 marginLeft: resultML + 'px', 481 marginTop: resultMT + 'px' 482 }); 483 484 // call onCoverImage event 485 if(_this.options.onCoverImage) { 486 _this.options.onCoverImage.call(_this); 487 } 488 }; 489 490 Jarallax.prototype.isVisible = function () { 491 return this.isElementInViewport || false; 492 }; 493 494 Jarallax.prototype.onScroll = function (force) { 495 var _this = this; 496 497 if(!_this.image.width || !_this.image.height) { 498 return; 499 } 500 501 var rect = _this.$item.getBoundingClientRect(), 502 contT = rect.top, 503 contH = rect.height, 504 styles = { 505 position : 'absolute', 506 visibility : 'visible', 507 backgroundPosition : '50% 50%' 508 }; 509 510 // check if in viewport 511 var viewportRect = rect; 512 if(_this.options.elementInViewport) { 513 viewportRect = _this.options.elementInViewport.getBoundingClientRect(); 514 } 515 _this.isElementInViewport = 516 viewportRect.bottom >= 0 && 517 viewportRect.right >= 0 && 518 viewportRect.top <= wndH && 519 viewportRect.left <= wndW; 520 521 // stop calculations if item is not in viewport 522 if (force ? false : !_this.isElementInViewport) { 523 return; 524 } 525 526 // calculate parallax helping variables 527 var beforeTop = Math.max(0, contT), 528 beforeTopEnd = Math.max(0, contH + contT), 529 afterTop = Math.max(0, -contT), 530 beforeBottom = Math.max(0, contT + contH - wndH), 531 beforeBottomEnd = Math.max(0, contH - (contT + contH - wndH)), 532 afterBottom = Math.max(0, -contT + wndH - contH), 533 fromViewportCenter = 1 - 2 * (wndH - contT) / (wndH + contH); 534 535 // calculate on how percent of section is visible 536 var visiblePercent = 1; 537 if(contH < wndH) { 538 visiblePercent = 1 - (afterTop || beforeBottom) / contH; 539 } else { 540 if(beforeTopEnd <= wndH) { 541 visiblePercent = beforeTopEnd / wndH; 542 } else if (beforeBottomEnd <= wndH) { 543 visiblePercent = beforeBottomEnd / wndH; 544 } 545 } 546 547 // opacity 548 if(_this.options.type === 'opacity' || _this.options.type === 'scale-opacity' || _this.options.type === 'scroll-opacity') { 549 styles.transform = 'translate3d(0, 0, 0)'; 550 styles.opacity = visiblePercent; 551 } 552 553 // scale 554 if(_this.options.type === 'scale' || _this.options.type === 'scale-opacity') { 555 var scale = 1; 556 if(_this.options.speed < 0) { 557 scale -= _this.options.speed * visiblePercent; 558 } else { 559 scale += _this.options.speed * (1 - visiblePercent); 560 } 561 styles.transform = 'scale(' + scale + ') translate3d(0, 0, 0)'; 562 } 563 564 // scroll
564 565 if(_this.options.type === 'scroll' || _this.options.type === 'scroll-opacity') { 566 var positionY = _this.parallaxScrollDistance * fromViewportCenter; 567 568 if(supportTransform && _this.options.enableTransform) { 569 // fix if parents with transform style 570 if(_this.parentWithTransform) { 571 positionY -= contT; 572 } 573 574 styles.transform = 'translate3d(0, ' + positionY + 'px, 0)'; 575 } else if (_this.image.useImgTag) { 576 styles.top = positionY + 'px'; 577 } else { 578 // vertical centering 579 if(_this.bgPosVerticalCenter) { 580 positionY += _this.bgPosVerticalCenter; 581 } 582 styles.backgroundPosition = '50% ' + positionY + 'px'; 583 } 584 585 // fixed position is not work properly for IE9 and less 586 // solution - use absolute position and emulate fixed by using container offset 587 styles.position = isIElt10 ? 'absolute' : 'fixed'; 588 } 589 590 _this.css(_this.image.$item, styles); 591 592 // call onScroll event 593 if(_this.options.onScroll) { 594 _this.options.onScroll.call(_this, { 595 section: rect, 596 597 beforeTop: beforeTop, 598 beforeTopEnd: beforeTopEnd, 599 afterTop: afterTop, 600 beforeBottom: beforeBottom, 601 beforeBottomEnd: beforeBottomEnd, 602 afterBottom: afterBottom, 603 604 visiblePercent: visiblePercent, 605 fromViewportCenter: fromViewportCenter 606 }); 607 } 608 }; 609 610 611 // init events 612 function addEventListener (el, eventName, handler) { 613 if (el.addEventListener) { 614 el.addEventListener(eventName, handler); 615 } else { 616 el.attachEvent('on' + eventName, function (){ 617 handler.call(el); 618 }); 619 } 620 } 621 622 function update (e) { 623 window.requestAnimationFrame(function () { 624 if(e.type !== 'scroll') { 625 updateWndVars(); 626 } 627 for(var k = 0, len = jarallaxList.length; k < len; k++) { 628 // cover image and clip needed only when parallax container was changed 629 if(e.type !== 'scroll') { 630 jarallaxList[k].coverImage(); 631 jarallaxList[k].clipContainer(); 632 } 633 jarallaxList[k].onScroll(); 634 } 635 }); 636 } 637 addEventListener(window, 'scroll', update); 638 addEventListener(window, 'resize', update); 639 addEventListener(window, 'orientationchange', update); 640 addEventListener(window, 'load', update); 641 642 643 // global definition 644 var plugin = function (items) { 645 // check for dom element 646 // thanks: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object 647 if(typeof HTMLElement === "object" ? items instanceof HTMLElement : items && typeof items === "object" && items !== null && items.nodeType === 1 && typeof items.nodeName==="string") { 648 items = [items]; 649 } 650 651 var options = arguments[1], 652 args = Array.prototype.slice.call(arguments, 2), 653 len = items.length, 654 k = 0, 655 ret; 656 657 for (k; k < len; k++) { 658 if (typeof options === 'object' || typeof options === 'undefined') { 659 if(!items[k].jarallax) { 660 items[k].jarallax = new Jarallax(items[k], options); 661 } 662 } 663 else if(items[k].jarallax) { 664 ret = items[k].jarallax[options].apply(items[k].jarallax, args); 665 } 666 if (typeof ret !== 'undefined') { 667 return ret; 668 } 669 } 670 671 return items; 672 }; 673 plugin.constructor = Jarallax; 674 675 // no conflict 676 var oldPlugin = window.jarallax; 677 window.jarallax = plugin; 678 window.jarallax.noConflict = function () { 679 window.jarallax = oldPlugin; 680 return this; 681 }; 682 683 // jQuery support 684 if(typeof jQuery !== 'undefined') { 685 var jQueryPlugin = function () { 686 var args = arguments || []; 687 Array.prototype.unshift.call(args, this); 688 var res = plugin.apply(window, args); 689 return typeof res !== 'object' ? res : this; 690 }; 691 jQueryPlugin.constructor = Jarallax; 692 693 // no conflict 694 var oldJqPlugin = jQuery.fn.jarallax; 695 jQuery.fn.jarallax = jQueryPlugin; 696 jQuery.fn.jarallax.noConflict = function () { 697 jQuery.fn.jarallax = oldJqPlugin; 698 return this; 699 }; 700 } 701 702 // data-jarallax initialization 703 addEventListener(window, 'DOMContentLoaded', function () { 704 plugin(document.querySelectorAll('[data-jarallax], [data-jarallax-video]')); 705 }); 706}(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.