1 2(function(root, factory) { 3 if (typeof define === 'function' && define.amd) { 4 define(factory); 5 } else if (typeof exports === 'object') { 6 module.exports = factory(require, exports, module); 7 } else { 8 root.ScrollReveal = factory(); 9 } 10}(this, function(require, exports, module) { 11 12 13///// ///// ///// ///// 14///// ///// ///// ///// 15///// ///// ///// ///// 16///// ///// ///// ///// 17///// ///// ///// 18///// ///// ///// 19///// ///// ///// ///// 20///// ///// ///// ///// 21 ///// ///// 22 ///// ///// 23///// ///// ///// ///// 24///// ///// ///// ///// 25///// ///// ///// ///// 26///// ///// ///// ///// 27 28/** 29 * ScrollReveal 30 * ------------ 31 * Version : 3.1.4 32 * Website : scrollrevealjs.org 33 * Repo : github.com/jlmakes/scrollreveal.js 34 * Author : Julian Lloyd (@jlmakes) 35 */ 36 37;(function() { 38 'use strict'; 39 40 var 41 sr, 42 Tools, 43 _requestAnimationFrame; 44 45 this.ScrollReveal = (function() { 46 47 /** 48 * Configuration 49 * ------------- 50 * This object signature can be passed directly to the ScrollReveal constructor, 51 * or as the second argument of the `reveal()` method. 52 */ 53 54 ScrollReveal.prototype.defaults = { 55 56 // 'bottom', 'left', 'top', 'right' 57 origin : 'bottom', 58 59 // Can be any valid CSS distance, e.g. '5rem', '10%', '20vw', etc. 60 distance : '20px', 61 62 // Time in milliseconds. 63 duration : 500, 64 delay : 0, 65 66 // Starting angles in degrees, will transition from these values to 0 in all axes. 67 rotate : { x: 0, y: 0, z: 0 }, 68 69 // Starting opacity value, before transitioning to the computed opacity. 70 opacity : 0, 71 72 // Starting scale value, will transition from this value to 1 73 scale : 0.9, 74 75 // Accepts any valid CSS easing, e.g. 'ease', 'ease-in-out', 'linear', etc. 76 easing : 'cubic-bezier(0.6, 0.2, 0.1, 1)', 77 78 // When null, `<html>` is assumed to be the reveal container. You can pass a 79 // DOM node as a custom container, e.g. document.querySelector('.fooContainer') 80 // or a selector, e.g. '.fooContainer' 81 container : null, 82 83 // true/false to control reveal animations on mobile. 84 mobile : true, 85 86 // true: reveals occur every time elements become visible 87 // false: reveals occur once as elements become visible 88 reset : false, 89 90 // 'always' â delay for all reveal animations 91 // 'once' â delay only the first time reveals occur 92 // 'onload' - delay only for animations triggered by first load 93 useDelay : 'always', 94 95 // Change when an element is considered in the viewport. The default value 96 // of 0.20 means 20% of an element must be visible for its reveal to occur. 97 viewFactor : 0.2, 98 99 // Pixel values that alter the container boundaries. 100 // e.g. Set `{ top: 48 }`, if you have a 48px tall fixed toolbar. 101 // -- 102 // Visual Aid: https://scrollrevealjs.org/assets/view
102offset.png 103 viewOffset : { top: 0, right: 0, bottom: 0, left: 0 }, 104 105 // Callbacks that fire for each completed element reveal, and if 106 // `config.reset = true`, for each completed element reset. When creating your 107 // callbacks, remember they are passed the elementâs DOM node that triggered 108 // it as the first argument. 109 afterReveal : function(domEl) {}, 110 afterReset : function(domEl) {} 111 }; 112 113 114 115 function ScrollReveal(config) { 116 117 // Support instantiation without the `new` keyword. 118 if (typeof this == 'undefined' || Object.getPrototypeOf(this) !== ScrollReveal.prototype) { 119 return new ScrollReveal(config) 120 } 121 122 sr = this; // Save reference to instance. 123 sr.tools = new Tools(); // *required utilities 124 125 if (sr.isSupported()) { 126 127 sr.tools.extend(sr.defaults, config || {}); 128 129 _resolveContainer(sr.defaults); 130 131 sr.store = { 132 elements : {}, 133 containers : [] 134 }; 135 136 sr.sequences = {}; 137 sr.history = []; 138 sr.uid = 0; 139 sr.initialized = false; 140 } 141 142 // Note: IE9 only supports console if devtools are open. 143 else if (typeof console !== 'undefined' && console !== null) { 144 console.log('ScrollReveal is not supported in this browser.'); 145 } 146 147 return sr 148 } 149 150 151 152 /** 153 * Check if client supports CSS Transform and CSS Transition. 154 * @return {boolean} 155 */ 156 ScrollReveal.prototype.isSupported = function() { 157 var style = document.documentElement.style; 158 return 'WebkitTransition' in style && 'WebkitTransform' in style 159 || 'transition' in style && 'transform' in style 160 }; 161 162 163 164 /** 165 * Creates a reveal set, a group of elements that will animate when they 166 * become visible. If [interval] is provided, a new sequence is created 167 * that will ensure elements reveal in the order they appear in the DOM. 168 * 169 * @param {string|Node} [selector] The element (node) or elements (selector) to animate. 170 * @param {Object} [config] Override the defaults for this reveal set. 171 * @param {number} [interval] Time between sequenced element animations (milliseconds). 172 * @param {boolean} [sync] Used internally when updating reveals for async content. 173 * 174 * @return {Object} The current ScrollReveal instance. 175 */ 176 ScrollReveal.prototype.reveal = function(selector, config, interval, sync) { 177 178 var 179 container, 180 elements, 181 elem, 182 elemId, 183 sequence, 184 sequenceId; 185 186 // Resolve container. 187 if (config && config.container) { 188 container = _resolveContainer(config); 189 } else { 190 container = sr.defaults.container; 191 } 192 193 // Letâs check to see if a DOM node was passed in as the first argument, 194 // otherwise query the container for all elements matching the selector. 195 if (sr.tools.isNode(selector)) { 196 elements = [selector]; 197 } else { 198 elements = Array.prototype.slice.call(container.querySelectorAll(selector)); 199 } 200 201 if (!elements.length) { 202 console.log('ScrollReveal: reveal on "'+ selector + '" failed, no elements found.'); 203 return sr 204 } 205 206 // No custom configuration was passed, but a sequence interval instead. 207 // letâs shuffle things around to make sure everything works. 208 if (config && typeof config == 'number') { 209 interval = config; 210 config = {}; 211 } 212 213 // Prepare a new sequence if an interval is passed. 214 if (interval && typeof interval == 'number') { 215 sequenceId = _nextUid(); 216 217 sequence = sr.sequences[sequenceId] = { 218 id : sequenceId, 219 interval : interval, 220 elemIds : [], 221 active : false 222 } 223 } 224 225 // Begin main loop to configure ScrollReveal elements. 226 for (var i = 0; i < elements.length; i++) { 227 228 // Check if the element has already been configured and grab it from the store. 229 elemId = elements[i].getAttribute('data-sr-id'); 230 if (elemId) { 231 elem = sr.store.elements[elemId]; 232 } 233 234 // Otherwise, letâs do some basic setup. 235 else { 236 elem = { 237 id : _nextUid(), 238 domEl : elements[i], 239 seen : false, 240 revealing : false 241 }; 242 elem.domEl.setAttribute('data-sr-id', elem.id); 243 } 244 245 // Sequence only setup 246 if (sequence) { 247 248 elem.sequence = { 249 id : sequence.id, 250 index : sequence.elemIds.length 251 }; 252 253 sequence.elemIds.push(elem.id); 254 } 255 256 // New or existing element, itâs time to update its configuration, styles, 257 // and send the updates to our store. 258 _configure(elem, config || {}); 259 _style(elem); 260 _updateStore(elem); 261 262 // We need to make sure elements are set to visibility: visible, even when 263 // on mobile and `config.mobile == false`, or if unsupported. 264 if (sr.tools.isMobile() && !elem.config.mobile || !sr.isSupported()) { 265 elem.domEl.setAttribute('style', elem.styles.inline); 266 elem.disabled = true; 267 } 268 269 // Otherwise, proceed normally. 270 else if (!elem.revealing) { 271 elem.domEl.setAttribute('style', 272 elem.styles.inline 273 + elem.styles.transform.initial 274 ); 275 } 276 } 277 278 // Each `reveal()` is recorded so that when calling `sync()` while working 279 // with asynchronously loaded content, it can re-trace your steps but with 280 // all your new elements now in the DOM. 281
282 // Since `reveal()` is called internally by `sync()`, we donât want to 283 // record or intiialize each reveal during syncing. 284 if (!sync && sr.isSupported()) { 285 _record(selector, config); 286 287 // We push initialization to the event queue using setTimeout, so that we can 288 // give ScrollReveal room to process all reveal calls before putting things into motion. 289 // -- 290 // Philip Roberts - What the heck is the event loop anyway? (JSConf EU 2014) 291 // https://www.youtube.com/watch?v=8aGhZQkoFbQ 292 if (sr.initTimeout) { 293 window.clearTimeout(sr.initTimeout); 294 } 295 sr.initTimeout = window.setTimeout(_init, 0); 296 } 297 298 return sr 299 }; 300 301 302 303 /** 304 * Re-runs `reveal()` for each record stored in history, effectively capturing 305 * any content loaded asynchronously that matches existing reveal set selectors. 306 * 307 * @return {Object} The current ScrollReveal instance. 308 */ 309 ScrollReveal.prototype.sync = function() { 310 if (sr.history.length && sr.isSupported()) { 311 for (var i = 0; i < sr.history.length; i++) { 312 var record = sr.history[i]; 313 sr.reveal(record.selector, record.config, record.interval, true); 314 }; 315 _init(); 316 } else { 317 console.log('ScrollReveal: sync failed, no reveals found.'); 318 } 319 return sr 320 }; 321 322 323 324 /** 325 * Private Methods 326 * --------------- 327 * These methods remain accessible only to the ScrollReveal instance, even 328 * though they only "exist" during instantiation outside of the constructors scope. 329 * -- 330 * http://stackoverflow.com/questions/111102/how-do-javascript-closures-work 331 */ 332 333 function _resolveContainer(config) { 334 var container = config.container; 335 336 // Check if our container is defined by a selector. 337 if (container && typeof container == 'string') { 338 return config.container = window.document.querySelector(container); 339 } 340 341 // Check if our container is defined by a node. 342 else if (container && !sr.tools.isNode(container)) { 343 console.log('ScrollReveal: Invalid container provided, using <html> instead.'); 344 config.container = null; 345 } 346 347 // Otherwise use <html> by default. 348 if (container == null) { 349 config.container = window.document.documentElement; 350 } 351 352 return config.container 353 } 354 355 356 357 /** 358 * A consistent way of creating unique IDs. 359 * @returns {number} 360 */ 361 function _nextUid() { 362 return ++sr.uid; 363 } 364 365 366 367 function _configure(elem, config) { 368 369 // If the element hasnât already been configured, letâs use a clone of the 370 // defaults extended by the configuration passed as the second argument. 371 if (!elem.config) { 372 elem.config = sr.tools.extendClone(sr.defaults, config); 373 } 374 375 // Otherwise, letâs use a clone of the existing element configuration extended 376 // by the configuration passed as the second argument. 377 else { 378 elem.config = sr.tools.extendClone(elem.config, config); 379 } 380 381 // Infer CSS Transform axis from origin string. 382 if (elem.config.origin === 'top' || elem.config.origin === 'bottom') { 383 elem.config.axis = 'Y'; 384 } else { 385 elem.config.axis = 'X'; 386 } 387 388 // Letâs make sure our our pixel distances are negative for top and left. 389 // e.g. config.origin = 'top' and config.distance = '25px' starts at `top: -25px` in CSS. 390 if (elem.config.origin === 'top' || elem.config.origin === 'left') { 391 elem.config.distance = '-' + elem.config.distance; 392 } 393 } 394 395 396 397 function _style(elem) { 398 var computed = window.getComputedStyle(elem.domEl); 399 400 if (!elem.styles) { 401 elem.styles = { 402 transition : {}, 403 transform : {}, 404 computed : {} 405 }; 406 407 // Capture any existing inline styles, and add our visibility override. 408 // -- 409 // See section 4.2. in the Documentation: 410 // https://github.com/jlmakes/scrollreveal.js#42-improve-user-experience 411 elem.styles.inline = elem.domEl.getAttribute('style') || ''; 412 elem.styles.inline += '; visibility: visible; '; 413 414 // grab the elements existing opacity.
415 elem.styles.computed.opacity = computed.opacity; 416 417 // grab the elements existing transitions. 418 if (!computed.transition || computed.transition == 'all 0s ease 0s') { 419 elem.styles.computed.transition = ''; 420 } else { 421 elem.styles.computed.transition = computed.transition + ', '; 422 } 423 } 424 425 // Create transition styles 426 elem.styles.transition.instant = _generateTransition(elem, 0); 427 elem.styles.transition.delayed = _generateTransition(elem, elem.config.delay); 428 429 // Generate transform styles, first with the webkit prefix. 430 elem.styles.transform.initial = ' -webkit-transform:'; 431 elem.styles.transform.target = ' -webkit-transform:'; 432 _generateTransform(elem); 433 434 // And again without any prefix. 435 elem.styles.transform.initial += 'transform:'; 436 elem.styles.transform.target += 'transform:'; 437 _generateTransform(elem); 438 439 } 440 441 442 443 function _generateTransition(elem, delay) { 444 var config = elem.config; 445 446 return '-webkit-transition: ' + elem.styles.computed.transition + 447 '-webkit-transform ' + config.duration / 1000 + 's ' 448 + config.easing + ' ' 449 + delay / 1000 + 's, opacity ' 450 + config.duration / 1000 + 's ' 451 + config.easing + ' ' 452 + delay / 1000 + 's; ' + 453 454 'transition: ' + elem.styles.computed.transition + 455 'transform ' + config.duration / 1000 + 's ' 456 + config.easing + ' ' 457 + delay / 1000 + 's, opacity ' 458 + config.duration / 1000 + 's ' 459 + config.easing + ' ' 460 + delay / 1000 + 's; ' 461 } 462 463 464 465 function _generateTransform(elem) { 466 var config = elem.config; 467 var transform = elem.styles.transform; 468 469 if (parseInt(config.distance)) { 470 transform.initial += ' translate' + config.axis + '(' + config.distance + ')'; 471 transform.target += ' translate' + config.axis + '(0)'; 472 } 473 if (config.scale) { 474 transform.initial += ' scale(' + config.scale + ')'; 475 transform.target += ' scale(1)'; 476 } 477 if (config.rotate.x) { 478 transform.initial += ' rotateX(' + config.rotate.x + 'deg)'; 479 transform.target += ' rotateX(0)'; 480 } 481 if (config.rotate.y) { 482 transform.initial += ' rotateY(' + config.rotate.y + 'deg)'; 483 transform.target += ' rotateY(0)'; 484 } 485 if (config.rotate.z) { 486 transform.initial += ' rotateZ(' + config.rotate.z + 'deg)'; 487 transform.target += ' rotateZ(0)'; 488 } 489 transform.initial += '; opacity: ' + config.opacity + ';'; 490 transform.target += '; opacity: ' + elem.styles.computed.opacity + ';'; 491 } 492 493 494 495 function _updateStore(elem) { 496 var container = elem.config.container; 497 498 // If this elementâs container isnât already in the store, letâs add it. 499 if (container && sr.store.containers.indexOf(container) == -1) { 500 sr.store.containers.push(elem.config.container); 501 } 502 503 // Update the element stored with our new element. 504 sr.store.elements[elem.id] = elem; 505 }; 506 507 508 509 function _record(selector, config, interval) { 510 511 // Save the `reveal()` arguments that triggered this `_record()` call, so we 512 // can re-trace our steps when calling the `sync()` method. 513 var record = { 514 selector : selector, 515 config : config, 516 interval : interval 517 }; 518 sr.history.push(record); 519 } 520 521 522 523 function _init() { 524 if (sr.isSupported()) { 525
526 // Initial animate call triggers valid reveal animations on first load. 527 // Subsequent animate calls are made inside the event handler. 528 _animate(); 529 530 // Then we loop through all container nodes in the store and bind event 531 // listeners to each. 532 for (var i = 0; i < sr.store.containers.length; i++) { 533 sr.store.containers[i].addEventListener('scroll', _handler); 534 sr.store.containers[i].addEventListener('resize', _handler); 535 } 536 537 // Letâs also do a one-time binding of window event listeners. 538 if (!sr.initialized) { 539 window.addEventListener('scroll', _handler); 540 window.addEventListener('resize', _handler); 541 sr.initialized = true; 542 } 543 } 544 return sr 545 } 546 547 548 549 function _handler() { 550 _requestAnimationFrame(_animate); 551 } 552 553 554 555 function _setActiveSequences() { 556 557 var 558 active, 559 elem, 560 elemId, 561 sequence; 562 563 // Loop through all sequences 564 sr.tools.forOwn(sr.sequences, function(sequenceId) { 565 sequence = sr.sequences[sequenceId]; 566 active = false; 567 568 // For each sequenced elemenet, letâs check visibility and if 569 // any are visible, set itâs sequence to active. 570 for (var i = 0; i < sequence.elemIds.length; i++) { 571 elemId = sequence.elemIds[i] 572 elem = sr.store.elements[elemId]; 573 if (_isElemVisible(elem) && !active) { 574 active = true; 575 } 576 } 577 578 sequence.active = active; 579 }); 580 } 581 582 583 584 function _animate() { 585 586 var 587 delayed, 588 elem; 589 590 _setActiveSequences(); 591 592 // Loop through all elements in the store 593 sr.tools.forOwn(sr.store.elements, function(elemId) { 594 595 elem = sr.store.elements[elemId]; 596 delayed = _shouldUseDelay(elem); 597 598 // Letâs see if we should reveal, and if so, whether to use delay. 599 if (_shouldReveal(elem)) { 600 if (delayed) { 601 elem.domEl.setAttribute('style', 602 elem.styles.inline 603 + elem.styles.transform.target 604 + elem.styles.transition.delayed 605 ); 606 } else { 607 elem.domEl.setAttribute('style', 608 elem.styles.inline 609 + elem.styles.transform.target 610 + elem.styles.transition.instant 611 ); 612 } 613 614 // Letâs queue the `afterReveal` callback and tag the element. 615 _queueCallback('reveal', elem, delayed); 616 elem.revealing = true; 617 elem.seen = true; 618 619 if (elem.sequence) { 620 _queueNextInSequence(elem, delayed); 621 } 622 } 623 624 // If we got this far our element shouldnât reveal, but should it reset? 625 else if (_shouldReset(elem)) { 626 elem.domEl.setAttribute('style', 627 elem.styles.inline 628 + elem.styles.transform.initial 629 + elem.styles.transition.instant 630 ); 631 _queueCallback('reset', elem); 632 elem.revealing = false; 633 } 634 }); 635 } 636 637 638 639 /** 640 * Sequence callback that triggers the next element. 641 */ 642 function _queueNextInSequence(elem, delayed) { 643 644 var 645 elapsed = 0, 646 delay = 0, 647 sequence = sr.sequences[elem.sequence.id]; 648 649 // Weâre processing a sequenced element, so let's block other elements in this sequence. 650 sequence.blocked = true; 651 652 // Since weâre triggering animations a part of a sequence after animations on first load, 653 // we need to check for that condition and explicitly add the delay to our timer. 654 if (delayed && elem.config.useDelay == 'onload') {
655 delay = elem.config.delay; 656 } 657 658 // If a sequence timer is already running, capture the elapsed time and clear it. 659 if (elem.sequence.timer) { 660 elapsed = Math.abs(elem.sequence.timer.started - new Date()); 661 window.clearTimeout(elem.sequence.timer); 662 } 663 664 // Start a new timer. 665 elem.sequence.timer = { started: new Date() }; 666 elem.sequence.timer.clock = window.setTimeout(function() { 667 668 // Sequence interval has passed, so unblock the sequence and re-run the handler. 669 sequence.blocked = false; 670 elem.sequence.timer = null; 671 _handler(); 672 673 }, Math.abs(sequence.interval) + delay - elapsed); 674 } 675 676 677 678 function _queueCallback(type, elem, delayed) { 679 680 var 681 elapsed = 0, 682 duration = 0, 683 callback = 'after'; 684 685 // Check which callback weâre working with. 686 switch (type) { 687 688 case 'reveal': 689 duration = elem.config.duration; 690 if (delayed) { 691 duration += elem.config.delay; 692 } 693 callback += 'Reveal'; 694 break 695 696 case 'reset': 697 duration = elem.config.duration; 698 callback += 'Reset'; 699 break 700 } 701 702 // If a timer is already running, capture the elapsed time and clear it. 703 if (elem.timer) { 704 elapsed = Math.abs(elem.timer.started - new Date()); 705 window.clearTimeout(elem.timer.clock); 706 } 707 708 // Start a new timer. 709 elem.timer = { started: new Date() }; 710 elem.timer.clock = window.setTimeout(function() { 711 712 // The timer completed, so letâs fire the callback and null the timer. 713 elem.config[callback](elem.domEl); 714 elem.timer = null; 715 716 }, duration - elapsed); 717 } 718 719 720 721 function _shouldReveal(elem) { 722 if (elem.sequence) { 723 var sequence = sr.sequences[elem.sequence.id]; 724 return sequence.active 725 && !sequence.blocked 726 && !elem.revealing 727 && !elem.disabled 728 } 729 return _isElemVisible(elem) 730 && !elem.revealing 731 && !elem.disabled 732 } 733 734 735 736 function _shouldUseDelay(elem) { 737 var config = elem.config.useDelay; 738 return config === 'always' 739 || (config === 'onload' && !sr.initialized) 740 || (config === 'once' && !elem.seen) 741 } 742 743 744 745 function _shouldReset(elem) { 746 if (elem.sequence) { 747 var sequence = sr.sequences[elem.sequence.id]; 748 return !sequence.active 749 && elem.config.reset 750 && elem.revealing 751 && !elem.disabled 752 } 753 return !_isElemVisible(elem) 754 && elem.config.reset 755 && elem.revealing 756 && !elem.disabled 757 } 758 759 760 761 function _getContainer(container) { 762 return { 763 width : container.clientWidth, 764 height : container.clientHeight 765 } 766 } 767 768 769 770 function _getScrolled(container) { 771 772 // Return the container scroll values, plus the its offset. 773 if (container && container !== window.document.documentElement) { 774 var offset = _getOffset(container); 775 return { 776 x : container.scrollLeft + offset.left, 777 y : container.scrollTop + offset.top 778 } 779 } 780 781 // Otherwise, default to the window objectâs scroll values. 782 else { 783 return { 784 x : window.pageXOffset, 785 y : window.pageYOffset 786 } 787 } 788 } 789 790 791 792 function _getOffset(domEl) { 793 794 var 795 offsetTop = 0, 796 offsetLeft = 0, 797 798 // Grab the elementâs dimensions. 799 offsetHeight = domEl.offsetHeight, 800 offsetWidth = domEl.offsetWidth; 801 802 // Now calculate the distance between the element and its parent, then 803 // again for the parent to its parent, and again etc... until we have the 804 // total distance of the element to the documentâs top and left origin. 805 do { 806 if (!isNaN(domEl.offsetTop)) { 807 offsetTop += domEl.offsetTop; 808 } 809 if (!isNaN(domEl.offsetLeft)) { 810 offsetLeft += domEl.offsetLeft; 811 } 812 } while (domEl = domEl.offsetParent); 813 814 return { 815 top : offsetTop, 816 left : offsetLeft, 817 height : offsetHeight, 818 width : offsetWidth 819 } 820 } 821 822 823 824 function _isElemVisible(elem) { 825 826 var 827 offset = _getOffset(elem.domEl), 828 container = _getContainer(elem.config.container),
829 scrolled = _getScrolled(elem.config.container), 830 vF = elem.config.viewFactor, 831 832 // Define the element geometry. 833 elemHeight = offset.height, 834 elemWidth = offset.width, 835 elemTop = offset.top, 836 elemLeft = offset.left, 837 elemBottom = elemTop + elemHeight, 838 elemRight = elemLeft + elemWidth; 839 840 return confirmBounds() || isPositionFixed() 841 842 function confirmBounds() { 843 844 var 845 // Define the elementâs functional boundaries using its view factor. 846 top = elemTop + elemHeight * vF, 847 left = elemLeft + elemWidth * vF, 848 bottom = elemBottom - elemHeight * vF, 849 right = elemRight - elemWidth * vF, 850 851 // Define the container functional boundaries using its view offset. 852 viewTop = scrolled.y + elem.config.viewOffset.top, 853 viewLeft = scrolled.x + elem.config.viewOffset.left,
854 viewBottom = scrolled.y - elem.config.viewOffset.bottom + container.height, 855 viewRight = scrolled.x - elem.config.viewOffset.right + container.width; 856 857 return top < viewBottom 858 && bottom > viewTop 859 && left > viewLeft 860 && right < viewRight 861 } 862 863 function isPositionFixed() { 864 return (window.getComputedStyle(elem.domEl).position === 'fixed') 865 } 866 } 867 868 869 870 return ScrollReveal 871 872 })(); 873 874 875 /** 876 * helper.tools.js 877 * --------------- 878 * Simple deep object extend, and a few other agnostic helper methods. 879 * gist: https://gist.github.com/jlmakes/9f104e3f1b4d86334987 880 */ 881 882 Tools = (function() { 883 884 Tools.prototype.isObject = function(object) { 885 return object !== null && typeof object === 'object' && object.constructor == Object 886 }; 887 888 Tools.prototype.isNode = function(object) { 889 return typeof Node === 'object' 890 ? object instanceof Node 891 : object && typeof object === 'object' 892 && typeof object.nodeType === 'number' 893 && typeof object.nodeName === 'string' 894 }; 895 896 Tools.prototype.forOwn = function(object, callback) { 897 if (!this.isObject(object)) { 898 throw new TypeError('Expected "object", but received "' + typeof object + '".'); 899 } else { 900 for (var property in object) { 901 if (object.hasOwnProperty(property)) { 902 callback(property); 903 } 904 } 905 } 906 }; 907 908 Tools.prototype.extend = function(target, source) { 909 this.forOwn(source, function(property) { 910 if (this.isObject(source[property])) { 911 if (!target[property] || !this.isObject(target[property])) { 912 target[property] = {}; 913 } 914 this.extend(target[property], source[property]); 915 } else { 916 target[property] = source[property]; 917 } 918 }.bind(this)); 919 return target 920 }; 921 922 Tools.prototype.extendClone = function(target, source) { 923 return this.extend(this.extend({}, target), source) 924 }; 925 926 Tools.prototype.isMobile = function() { 927 return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) 928 }; 929 930 function Tools() {}; 931 return Tools 932 933 })(); 934 935 936 937 _requestAnimationFrame = window.requestAnimationFrame || 938 window.webkitRequestAnimationFrame || 939 window.mozRequestAnimationFrame; 940 941 942 943}).call(this); 944 945return this.ScrollReveal; 946 947}));
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.