· 6 years ago · Nov 21, 2019, 11:22 AM
1import throttle from 'lodash.throttle';
2import debounce from 'lodash.debounce';
3
4var callback = function callback() {};
5
6function containsAOSNode(nodes) {
7 var i = void 0,
8 currentNode = void 0,
9 result = void 0;
10
11 for (i = 0; i < nodes.length; i += 1) {
12 currentNode = nodes[i];
13
14 if (currentNode.dataset && currentNode.dataset.aos) {
15 return true;
16 }
17
18 result = currentNode.children && containsAOSNode(currentNode.children);
19
20 if (result) {
21 return true;
22 }
23 }
24
25 return false;
26}
27
28function check(mutations) {
29 if (!mutations) return;
30
31 mutations.forEach(function (mutation) {
32 var addedNodes = Array.prototype.slice.call(mutation.addedNodes);
33 var removedNodes = Array.prototype.slice.call(mutation.removedNodes);
34 var allNodes = addedNodes.concat(removedNodes);
35
36 if (containsAOSNode(allNodes)) {
37 return callback();
38 }
39 });
40}
41
42function getMutationObserver() {
43 return window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
44}
45
46function isSupported() {
47 return !!getMutationObserver();
48}
49
50function ready(selector, fn) {
51 var doc = window.document;
52 var MutationObserver = getMutationObserver();
53
54 var observer = new MutationObserver(check);
55 callback = fn;
56
57 observer.observe(doc.documentElement, {
58 childList: true,
59 subtree: true,
60 removedNodes: true
61 });
62}
63
64var observer = { isSupported: isSupported, ready: ready };
65
66var classCallCheck = function (instance, Constructor) {
67 if (!(instance instanceof Constructor)) {
68 throw new TypeError("Cannot call a class as a function");
69 }
70};
71
72var createClass = function () {
73 function defineProperties(target, props) {
74 for (var i = 0; i < props.length; i++) {
75 var descriptor = props[i];
76 descriptor.enumerable = descriptor.enumerable || false;
77 descriptor.configurable = true;
78 if ("value" in descriptor) descriptor.writable = true;
79 Object.defineProperty(target, descriptor.key, descriptor);
80 }
81 }
82
83 return function (Constructor, protoProps, staticProps) {
84 if (protoProps) defineProperties(Constructor.prototype, protoProps);
85 if (staticProps) defineProperties(Constructor, staticProps);
86 return Constructor;
87 };
88}();
89
90var _extends = Object.assign || function (target) {
91 for (var i = 1; i < arguments.length; i++) {
92 var source = arguments[i];
93
94 for (var key in source) {
95 if (Object.prototype.hasOwnProperty.call(source, key)) {
96 target[key] = source[key];
97 }
98 }
99 }
100
101 return target;
102};
103
104/**
105 * Device detector
106 */
107
108var fullNameRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i;
109var prefixRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
110var fullNameMobileRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i;
111var prefixMobileRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
112
113function ua() {
114 return navigator.userAgent || navigator.vendor || window.opera || '';
115}
116
117var Detector = function () {
118 function Detector() {
119 classCallCheck(this, Detector);
120 }
121
122 createClass(Detector, [{
123 key: 'phone',
124 value: function phone() {
125 var a = ua();
126 return !!(fullNameRe.test(a) || prefixRe.test(a.substr(0, 4)));
127 }
128 }, {
129 key: 'mobile',
130 value: function mobile() {
131 var a = ua();
132 return !!(fullNameMobileRe.test(a) || prefixMobileRe.test(a.substr(0, 4)));
133 }
134 }, {
135 key: 'tablet',
136 value: function tablet() {
137 return this.mobile() && !this.phone();
138 }
139
140 // http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c
141
142 }, {
143 key: 'ie11',
144 value: function ie11() {
145 return '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style;
146 }
147 }]);
148 return Detector;
149}();
150
151var detect = new Detector();
152
153/**
154 * Adds multiple classes on node
155 * @param {DOMNode} node
156 * @param {array} classes
157 */
158var addClasses = function addClasses(node, classes) {
159 return classes && classes.forEach(function (className) {
160 return node.classList.add(className);
161 });
162};
163
164/**
165 * Removes multiple classes from node
166 * @param {DOMNode} node
167 * @param {array} classes
168 */
169var removeClasses = function removeClasses(node, classes) {
170 return classes && classes.forEach(function (className) {
171 return node.classList.remove(className);
172 });
173};
174
175var fireEvent = function fireEvent(eventName, data) {
176 var customEvent = void 0;
177
178 if (detect.ie11()) {
179 customEvent = document.createEvent('CustomEvent');
180 customEvent.initCustomEvent(eventName, true, true, { detail: data });
181 } else {
182 customEvent = new CustomEvent(eventName, {
183 detail: data
184 });
185 }
186
187 return document.dispatchEvent(customEvent);
188};
189
190/**
191 * Set or remove aos-animate class
192 * @param {node} el element
193 * @param {int} top scrolled distance
194 */
195var applyClasses = function applyClasses(el, top) {
196 var options = el.options,
197 position = el.position,
198 node = el.node,
199 data = el.data;
200
201
202 var hide = function hide() {
203 if (!el.animated) return;
204
205 removeClasses(node, options.animatedClassNames);
206 fireEvent('aos:out', node);
207
208 if (el.options.id) {
209 fireEvent('aos:in:' + el.options.id, node);
210 }
211
212 el.animated = false;
213 };
214
215 var show = function show() {
216 if (el.animated) return;
217
218 addClasses(node, options.animatedClassNames);
219
220 fireEvent('aos:in', node);
221 if (el.options.id) {
222 fireEvent('aos:in:' + el.options.id, node);
223 }
224
225 el.animated = true;
226 };
227
228 if (options.mirror && top >= position.out && !options.once) {
229 hide();
230 } else if (top >= position.in) {
231 show();
232 } else if (el.animated && !options.once) {
233 hide();
234 }
235};
236
237/**
238 * Scroll logic - add or remove 'aos-animate' class on scroll
239 *
240 * @param {array} $elements array of elements nodes
241 * @return {void}
242 */
243var handleScroll = function handleScroll($elements) {
244 return $elements.forEach(function (el, i) {
245 return applyClasses(el, window.pageYOffset);
246 });
247};
248
249/**
250 * Get offset of DOM element
251 * like there were no transforms applied on it
252 *
253 * @param {Node} el [DOM element]
254 * @return {Object} [top and left offset]
255 */
256var offset = function offset(el) {
257 var _x = 0;
258 var _y = 0;
259
260 while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
261 _x += el.offsetLeft - (el.tagName != 'BODY' ? el.scrollLeft : 0);
262 _y += el.offsetTop - (el.tagName != 'BODY' ? el.scrollTop : 0);
263 el = el.offsetParent;
264 }
265
266 return {
267 top: _y,
268 left: _x
269 };
270};
271
272/**
273 * Get inline option with a fallback.
274 *
275 * @param {Node} el [Dom element]
276 * @param {String} key [Option key]
277 * @param {String} fallback [Default (fallback) value]
278 * @return {Mixed} [Option set with inline attributes or fallback value if not set]
279 */
280
281var getInlineOption = (function (el, key, fallback) {
282 var attr = el.getAttribute('data-aos-' + key);
283
284 if (typeof attr !== 'undefined') {
285 if (attr === 'true') {
286 return true;
287 } else if (attr === 'false') {
288 return false;
289 }
290 }
291
292 return attr || fallback;
293});
294
295/**
296 * Calculate offset
297 * basing on element's settings like:
298 * - anchor
299 * - offset
300 *
301 * @param {Node} el [Dom element]
302 * @return {Integer} [Final offset that will be used to trigger animation in good position]
303 */
304
305var getPositionIn = function getPositionIn(el, defaultOffset, defaultAnchorPlacement) {
306 var windowHeight = window.innerHeight;
307 var anchor = getInlineOption(el, 'anchor');
308 var inlineAnchorPlacement = getInlineOption(el, 'anchor-placement');
309 var additionalOffset = Number(getInlineOption(el, 'offset', inlineAnchorPlacement ? 0 : defaultOffset));
310 var anchorPlacement = inlineAnchorPlacement || defaultAnchorPlacement;
311 var finalEl = el;
312
313 if (anchor && document.querySelectorAll(anchor)) {
314 finalEl = document.querySelectorAll(anchor)[0];
315 }
316
317 var triggerPoint = offset(finalEl).top - windowHeight;
318
319 switch (anchorPlacement) {
320 case 'top-bottom':
321 // Default offset
322 break;
323 case 'center-bottom':
324 triggerPoint += finalEl.offsetHeight / 2;
325 break;
326 case 'bottom-bottom':
327 triggerPoint += finalEl.offsetHeight;
328 break;
329 case 'top-center':
330 triggerPoint += windowHeight / 2;
331 break;
332 case 'center-center':
333 triggerPoint += windowHeight / 2 + finalEl.offsetHeight / 2;
334 break;
335 case 'bottom-center':
336 triggerPoint += windowHeight / 2 + finalEl.offsetHeight;
337 break;
338 case 'top-top':
339 triggerPoint += windowHeight;
340 break;
341 case 'bottom-top':
342 triggerPoint += windowHeight + finalEl.offsetHeight;
343 break;
344 case 'center-top':
345 triggerPoint += windowHeight + finalEl.offsetHeight / 2;
346 break;
347 }
348
349 return triggerPoint + additionalOffset;
350};
351
352var getPositionOut = function getPositionOut(el, defaultOffset) {
353 var windowHeight = window.innerHeight;
354 var anchor = getInlineOption(el, 'anchor');
355 var additionalOffset = getInlineOption(el, 'offset', defaultOffset);
356 var finalEl = el;
357
358 if (anchor && document.querySelectorAll(anchor)) {
359 finalEl = document.querySelectorAll(anchor)[0];
360 }
361
362 var elementOffsetTop = offset(finalEl).top;
363
364 return elementOffsetTop + finalEl.offsetHeight - additionalOffset;
365};
366
367/* Clearing variables */
368
369var prepare = function prepare($elements, options) {
370 $elements.forEach(function (el, i) {
371 var mirror = getInlineOption(el.node, 'mirror', options.mirror);
372 var once = getInlineOption(el.node, 'once', options.once);
373 var id = getInlineOption(el.node, 'id');
374 var customClassNames = options.useClassNames && el.node.getAttribute('data-aos');
375
376 var animatedClassNames = [options.animatedClassName].concat(customClassNames ? customClassNames.split(' ') : []).filter(function (className) {
377 return typeof className === 'string';
378 });
379
380 if (options.initClassName) {
381 el.node.classList.add(options.initClassName);
382 }
383
384 el.position = {
385 in: getPositionIn(el.node, options.offset, options.anchorPlacement),
386 out: mirror && getPositionOut(el.node, options.offset)
387 };
388
389 el.options = {
390 once: once,
391 mirror: mirror,
392 animatedClassNames: animatedClassNames,
393 id: id
394 };
395 });
396
397 return $elements;
398};
399
400/**
401 * Generate initial array with elements as objects
402 * This array will be extended later with elements attributes values
403 * like 'position'
404 */
405var elements = (function () {
406 var elements = document.querySelectorAll('[data-aos]');
407 return Array.prototype.map.call(elements, function (node) {
408 return { node: node };
409 });
410});
411
412/**
413 * *******************************************************
414 * AOS (Animate on scroll) - wowjs alternative
415 * made to animate elements on scroll in both directions
416 * *******************************************************
417 */
418
419/**
420 * Private variables
421 */
422var $aosElements = [];
423var initialized = false;
424
425/**
426 * Default options
427 */
428var options = {
429 offset: 120,
430 delay: 0,
431 easing: 'ease',
432 duration: 400,
433 disable: false,
434 once: false,
435 mirror: false,
436 anchorPlacement: 'top-bottom',
437 startEvent: 'DOMContentLoaded',
438 animatedClassName: 'aos-animate',
439 initClassName: 'aos-init',
440 useClassNames: false,
441 disableMutationObserver: false,
442 throttleDelay: 99,
443 debounceDelay: 50
444};
445
446// Detect not supported browsers (<=IE9)
447// http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
448var isBrowserNotSupported = function isBrowserNotSupported() {
449 return document.all && !window.atob;
450};
451
452var initializeScroll = function initializeScroll() {
453 // Extend elements objects in $aosElements with their positions
454 $aosElements = prepare($aosElements, options);
455 // Perform scroll event, to refresh view and show/hide elements
456 handleScroll($aosElements);
457
458 /**
459 * Handle scroll event to animate elements on scroll
460 */
461 window.addEventListener('scroll', throttle(function () {
462 handleScroll($aosElements, options.once);
463 }, options.throttleDelay));
464
465 return $aosElements;
466};
467
468/**
469 * Refresh AOS
470 */
471var refresh = function refresh() {
472 var initialize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
473
474 // Allow refresh only when it was first initialized on startEvent
475 if (initialize) initialized = true;
476 if (initialized) initializeScroll();
477};
478
479/**
480 * Hard refresh
481 * create array with new elements and trigger refresh
482 */
483var refreshHard = function refreshHard() {
484 $aosElements = elements();
485
486 if (isDisabled(options.disable) || isBrowserNotSupported()) {
487 return disable();
488 }
489
490 refresh();
491};
492
493/**
494 * Disable AOS
495 * Remove all attributes to reset applied styles
496 */
497var disable = function disable() {
498 $aosElements.forEach(function (el, i) {
499 el.node.removeAttribute('data-aos');
500 el.node.removeAttribute('data-aos-easing');
501 el.node.removeAttribute('data-aos-duration');
502 el.node.removeAttribute('data-aos-delay');
503
504 if (options.initClassName) {
505 el.node.classList.remove(options.initClassName);
506 }
507
508 if (options.animatedClassName) {
509 el.node.classList.remove(options.animatedClassName);
510 }
511 });
512};
513
514/**
515 * Check if AOS should be disabled based on provided setting
516 */
517var isDisabled = function isDisabled(optionDisable) {
518 return optionDisable === true || optionDisable === 'mobile' && detect.mobile() || optionDisable === 'phone' && detect.phone() || optionDisable === 'tablet' && detect.tablet() || typeof optionDisable === 'function' && optionDisable() === true;
519};
520
521/**
522 * Initializing AOS
523 * - Create options merging defaults with user defined options
524 * - Set attributes on <body> as global setting - css relies on it
525 * - Attach preparing elements to options.startEvent,
526 * window resize and orientation change
527 * - Attach function that handle scroll and everything connected to it
528 * to window scroll event and fire once document is ready to set initial state
529 */
530var init = function init(settings) {
531 options = _extends(options, settings);
532
533 // Create initial array with elements -> to be fullfilled later with prepare()
534 $aosElements = elements();
535
536 /**
537 * Disable mutation observing if not supported
538 */
539 if (!options.disableMutationObserver && !observer.isSupported()) {
540 console.info('\n aos: MutationObserver is not supported on this browser,\n code mutations observing has been disabled.\n You may have to call "refreshHard()" by yourself.\n ');
541 options.disableMutationObserver = true;
542 }
543
544 /**
545 * Observe [aos] elements
546 * If something is loaded by AJAX
547 * it'll refresh plugin automatically
548 */
549 if (!options.disableMutationObserver) {
550 observer.ready('[data-aos]', refreshHard);
551 }
552
553 /**
554 * Don't init plugin if option `disable` is set
555 * or when browser is not supported
556 */
557 if (isDisabled(options.disable) || isBrowserNotSupported()) {
558 return disable();
559 }
560
561 /**
562 * Set global settings on body, based on options
563 * so CSS can use it
564 */
565 document.querySelector('body').setAttribute('data-aos-easing', options.easing);
566
567 document.querySelector('body').setAttribute('data-aos-duration', options.duration);
568
569 document.querySelector('body').setAttribute('data-aos-delay', options.delay);
570
571 /**
572 * Handle initializing
573 */
574 if (['DOMContentLoaded', 'load'].indexOf(options.startEvent) === -1) {
575 // Listen to options.startEvent and initialize AOS
576 document.addEventListener(options.startEvent, function () {
577 refresh(true);
578 });
579 } else {
580 window.addEventListener('load', function () {
581 refresh(true);
582 });
583 }
584
585 if (options.startEvent === 'DOMContentLoaded' && ['complete', 'interactive'].indexOf(document.readyState) > -1) {
586 // Initialize AOS if default startEvent was already fired
587 refresh(true);
588 }
589
590 /**
591 * Refresh plugin on window resize or orientation change
592 */
593 window.addEventListener('resize', debounce(refresh, options.debounceDelay, true));
594
595 window.addEventListener('orientationchange', debounce(refresh, options.debounceDelay, true));
596
597 return $aosElements;
598};
599
600/**
601 * Export Public API
602 */
603
604var aos = {
605 init: init,
606 refresh: refresh,
607 refreshHard: refreshHard
608};
609
610export default aos;