UNPKG

58.3 kBJavaScriptView Raw
1/**
2 * Infinite Ajax Scroll v3.1.0
3 * Turn your existing pagination into infinite scrolling pages with ease
4 *
5 * Commercial use requires one-time purchase of a commercial license
6 * https://infiniteajaxscroll.com/docs/license.html
7 *
8 * Copyright 2014-2023 Webcreate (Jeroen Fiege)
9 * https://infiniteajaxscroll.com
10 */
11(function (global, factory) {
12 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
13 typeof define === 'function' && define.amd ? define(factory) :
14 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.InfiniteAjaxScroll = factory());
15})(this, (function () { 'use strict';
16
17 /*! @license is-dom-node v1.0.4
18
19 Copyright 2018 Fisssion LLC.
20
21 Permission is hereby granted, free of charge, to any person obtaining a copy
22 of this software and associated documentation files (the "Software"), to deal
23 in the Software without restriction, including without limitation the rights
24 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25 copies of the Software, and to permit persons to whom the Software is
26 furnished to do so, subject to the following conditions:
27
28 The above copyright notice and this permission notice shall be included in all
29 copies or substantial portions of the Software.
30
31 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37 SOFTWARE.
38
39 */
40 function isDomNode(x) {
41 return typeof window.Node === 'object'
42 ? x instanceof window.Node
43 : x !== null &&
44 typeof x === 'object' &&
45 typeof x.nodeType === 'number' &&
46 typeof x.nodeName === 'string'
47 }
48
49 /*! @license is-dom-node-list v1.2.1
50
51 Copyright 2018 Fisssion LLC.
52
53 Permission is hereby granted, free of charge, to any person obtaining a copy
54 of this software and associated documentation files (the "Software"), to deal
55 in the Software without restriction, including without limitation the rights
56 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
57 copies of the Software, and to permit persons to whom the Software is
58 furnished to do so, subject to the following conditions:
59
60 The above copyright notice and this permission notice shall be included in all
61 copies or substantial portions of the Software.
62
63 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
64 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
65 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
66 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
67 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
68 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
69 SOFTWARE.
70
71 */
72
73 function isDomNodeList(x) {
74 var prototypeToString = Object.prototype.toString.call(x);
75 var regex = /^\[object (HTMLCollection|NodeList|Object)\]$/;
76
77 return typeof window.NodeList === 'object'
78 ? x instanceof window.NodeList
79 : x !== null &&
80 typeof x === 'object' &&
81 typeof x.length === 'number' &&
82 regex.test(prototypeToString) &&
83 (x.length === 0 || isDomNode(x[0]))
84 }
85
86 /*! @license Tealight v0.3.6
87
88 Copyright 2018 Fisssion LLC.
89
90 Permission is hereby granted, free of charge, to any person obtaining a copy
91 of this software and associated documentation files (the "Software"), to deal
92 in the Software without restriction, including without limitation the rights
93 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
94 copies of the Software, and to permit persons to whom the Software is
95 furnished to do so, subject to the following conditions:
96
97 The above copyright notice and this permission notice shall be included in all
98 copies or substantial portions of the Software.
99
100 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
101 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
102 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
103 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
104 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
105 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
106 SOFTWARE.
107
108 */
109
110 function tealight(target, context) {
111 if ( context === void 0 ) { context = document; }
112
113 if (target instanceof Array) { return target.filter(isDomNode); }
114 if (isDomNode(target)) { return [target]; }
115 if (isDomNodeList(target)) { return Array.prototype.slice.call(target); }
116 if (typeof target === "string") {
117 try {
118 var query = context.querySelectorAll(target);
119 return Array.prototype.slice.call(query);
120 } catch (err) {
121 return [];
122 }
123 }
124 return [];
125 }
126
127 var hasOwn = Object.prototype.hasOwnProperty;
128 var toStr = Object.prototype.toString;
129 var defineProperty = Object.defineProperty;
130 var gOPD = Object.getOwnPropertyDescriptor;
131
132 var isArray = function isArray(arr) {
133 if (typeof Array.isArray === 'function') {
134 return Array.isArray(arr);
135 }
136
137 return toStr.call(arr) === '[object Array]';
138 };
139
140 var isPlainObject = function isPlainObject(obj) {
141 if (!obj || toStr.call(obj) !== '[object Object]') {
142 return false;
143 }
144
145 var hasOwnConstructor = hasOwn.call(obj, 'constructor');
146 var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
147 // Not own constructor property must be Object
148 if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
149 return false;
150 }
151
152 // Own properties are enumerated firstly, so to speed up,
153 // if last one is own, then all properties are own.
154 var key;
155 for (key in obj) { /**/ }
156
157 return typeof key === 'undefined' || hasOwn.call(obj, key);
158 };
159
160 // If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target
161 var setProperty = function setProperty(target, options) {
162 if (defineProperty && options.name === '__proto__') {
163 defineProperty(target, options.name, {
164 enumerable: true,
165 configurable: true,
166 value: options.newValue,
167 writable: true
168 });
169 } else {
170 target[options.name] = options.newValue;
171 }
172 };
173
174 // Return undefined instead of __proto__ if '__proto__' is not an own property
175 var getProperty = function getProperty(obj, name) {
176 if (name === '__proto__') {
177 if (!hasOwn.call(obj, name)) {
178 return void 0;
179 } else if (gOPD) {
180 // In early versions of node, obj['__proto__'] is buggy when obj has
181 // __proto__ as an own property. Object.getOwnPropertyDescriptor() works.
182 return gOPD(obj, name).value;
183 }
184 }
185
186 return obj[name];
187 };
188
189 var extend = function extend() {
190 var arguments$1 = arguments;
191
192 var options, name, src, copy, copyIsArray, clone;
193 var target = arguments[0];
194 var i = 1;
195 var length = arguments.length;
196 var deep = false;
197
198 // Handle a deep copy situation
199 if (typeof target === 'boolean') {
200 deep = target;
201 target = arguments[1] || {};
202 // skip the boolean and the target
203 i = 2;
204 }
205 if (target == null || (typeof target !== 'object' && typeof target !== 'function')) {
206 target = {};
207 }
208
209 for (; i < length; ++i) {
210 options = arguments$1[i];
211 // Only deal with non-null/undefined values
212 if (options != null) {
213 // Extend the base object
214 for (name in options) {
215 src = getProperty(target, name);
216 copy = getProperty(options, name);
217
218 // Prevent never-ending loop
219 if (target !== copy) {
220 // Recurse if we're merging plain objects or arrays
221 if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
222 if (copyIsArray) {
223 copyIsArray = false;
224 clone = src && isArray(src) ? src : [];
225 } else {
226 clone = src && isPlainObject(src) ? src : {};
227 }
228
229 // Never move original objects, clone them
230 setProperty(target, { name: name, newValue: extend(deep, clone, copy) });
231
232 // Don't bring in undefined values
233 } else if (typeof copy !== 'undefined') {
234 setProperty(target, { name: name, newValue: copy });
235 }
236 }
237 }
238 }
239 }
240
241 // Return the modified object
242 return target;
243 };
244
245 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
246
247 /**
248 * lodash (Custom Build) <https://lodash.com/>
249 * Build: `lodash modularize exports="npm" -o ./`
250 * Copyright jQuery Foundation and other contributors <https://jquery.org/>
251 * Released under MIT license <https://lodash.com/license>
252 * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
253 * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
254 */
255
256 /** Used as the `TypeError` message for "Functions" methods. */
257 var FUNC_ERROR_TEXT = 'Expected a function';
258
259 /** Used as references for various `Number` constants. */
260 var NAN = 0 / 0;
261
262 /** `Object#toString` result references. */
263 var symbolTag = '[object Symbol]';
264
265 /** Used to match leading and trailing whitespace. */
266 var reTrim = /^\s+|\s+$/g;
267
268 /** Used to detect bad signed hexadecimal string values. */
269 var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
270
271 /** Used to detect binary string values. */
272 var reIsBinary = /^0b[01]+$/i;
273
274 /** Used to detect octal string values. */
275 var reIsOctal = /^0o[0-7]+$/i;
276
277 /** Built-in method references without a dependency on `root`. */
278 var freeParseInt = parseInt;
279
280 /** Detect free variable `global` from Node.js. */
281 var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
282
283 /** Detect free variable `self`. */
284 var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
285
286 /** Used as a reference to the global object. */
287 var root = freeGlobal || freeSelf || Function('return this')();
288
289 /** Used for built-in method references. */
290 var objectProto = Object.prototype;
291
292 /**
293 * Used to resolve the
294 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
295 * of values.
296 */
297 var objectToString = objectProto.toString;
298
299 /* Built-in method references for those with the same name as other `lodash` methods. */
300 var nativeMax = Math.max,
301 nativeMin = Math.min;
302
303 /**
304 * Gets the timestamp of the number of milliseconds that have elapsed since
305 * the Unix epoch (1 January 1970 00:00:00 UTC).
306 *
307 * @static
308 * @memberOf _
309 * @since 2.4.0
310 * @category Date
311 * @returns {number} Returns the timestamp.
312 * @example
313 *
314 * _.defer(function(stamp) {
315 * console.log(_.now() - stamp);
316 * }, _.now());
317 * // => Logs the number of milliseconds it took for the deferred invocation.
318 */
319 var now = function() {
320 return root.Date.now();
321 };
322
323 /**
324 * Creates a debounced function that delays invoking `func` until after `wait`
325 * milliseconds have elapsed since the last time the debounced function was
326 * invoked. The debounced function comes with a `cancel` method to cancel
327 * delayed `func` invocations and a `flush` method to immediately invoke them.
328 * Provide `options` to indicate whether `func` should be invoked on the
329 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
330 * with the last arguments provided to the debounced function. Subsequent
331 * calls to the debounced function return the result of the last `func`
332 * invocation.
333 *
334 * **Note:** If `leading` and `trailing` options are `true`, `func` is
335 * invoked on the trailing edge of the timeout only if the debounced function
336 * is invoked more than once during the `wait` timeout.
337 *
338 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
339 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
340 *
341 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
342 * for details over the differences between `_.debounce` and `_.throttle`.
343 *
344 * @static
345 * @memberOf _
346 * @since 0.1.0
347 * @category Function
348 * @param {Function} func The function to debounce.
349 * @param {number} [wait=0] The number of milliseconds to delay.
350 * @param {Object} [options={}] The options object.
351 * @param {boolean} [options.leading=false]
352 * Specify invoking on the leading edge of the timeout.
353 * @param {number} [options.maxWait]
354 * The maximum time `func` is allowed to be delayed before it's invoked.
355 * @param {boolean} [options.trailing=true]
356 * Specify invoking on the trailing edge of the timeout.
357 * @returns {Function} Returns the new debounced function.
358 * @example
359 *
360 * // Avoid costly calculations while the window size is in flux.
361 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
362 *
363 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
364 * jQuery(element).on('click', _.debounce(sendMail, 300, {
365 * 'leading': true,
366 * 'trailing': false
367 * }));
368 *
369 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
370 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
371 * var source = new EventSource('/stream');
372 * jQuery(source).on('message', debounced);
373 *
374 * // Cancel the trailing debounced invocation.
375 * jQuery(window).on('popstate', debounced.cancel);
376 */
377 function debounce(func, wait, options) {
378 var lastArgs,
379 lastThis,
380 maxWait,
381 result,
382 timerId,
383 lastCallTime,
384 lastInvokeTime = 0,
385 leading = false,
386 maxing = false,
387 trailing = true;
388
389 if (typeof func != 'function') {
390 throw new TypeError(FUNC_ERROR_TEXT);
391 }
392 wait = toNumber(wait) || 0;
393 if (isObject(options)) {
394 leading = !!options.leading;
395 maxing = 'maxWait' in options;
396 maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
397 trailing = 'trailing' in options ? !!options.trailing : trailing;
398 }
399
400 function invokeFunc(time) {
401 var args = lastArgs,
402 thisArg = lastThis;
403
404 lastArgs = lastThis = undefined;
405 lastInvokeTime = time;
406 result = func.apply(thisArg, args);
407 return result;
408 }
409
410 function leadingEdge(time) {
411 // Reset any `maxWait` timer.
412 lastInvokeTime = time;
413 // Start the timer for the trailing edge.
414 timerId = setTimeout(timerExpired, wait);
415 // Invoke the leading edge.
416 return leading ? invokeFunc(time) : result;
417 }
418
419 function remainingWait(time) {
420 var timeSinceLastCall = time - lastCallTime,
421 timeSinceLastInvoke = time - lastInvokeTime,
422 result = wait - timeSinceLastCall;
423
424 return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
425 }
426
427 function shouldInvoke(time) {
428 var timeSinceLastCall = time - lastCallTime,
429 timeSinceLastInvoke = time - lastInvokeTime;
430
431 // Either this is the first call, activity has stopped and we're at the
432 // trailing edge, the system time has gone backwards and we're treating
433 // it as the trailing edge, or we've hit the `maxWait` limit.
434 return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
435 (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
436 }
437
438 function timerExpired() {
439 var time = now();
440 if (shouldInvoke(time)) {
441 return trailingEdge(time);
442 }
443 // Restart the timer.
444 timerId = setTimeout(timerExpired, remainingWait(time));
445 }
446
447 function trailingEdge(time) {
448 timerId = undefined;
449
450 // Only invoke if we have `lastArgs` which means `func` has been
451 // debounced at least once.
452 if (trailing && lastArgs) {
453 return invokeFunc(time);
454 }
455 lastArgs = lastThis = undefined;
456 return result;
457 }
458
459 function cancel() {
460 if (timerId !== undefined) {
461 clearTimeout(timerId);
462 }
463 lastInvokeTime = 0;
464 lastArgs = lastCallTime = lastThis = timerId = undefined;
465 }
466
467 function flush() {
468 return timerId === undefined ? result : trailingEdge(now());
469 }
470
471 function debounced() {
472 var time = now(),
473 isInvoking = shouldInvoke(time);
474
475 lastArgs = arguments;
476 lastThis = this;
477 lastCallTime = time;
478
479 if (isInvoking) {
480 if (timerId === undefined) {
481 return leadingEdge(lastCallTime);
482 }
483 if (maxing) {
484 // Handle invocations in a tight loop.
485 timerId = setTimeout(timerExpired, wait);
486 return invokeFunc(lastCallTime);
487 }
488 }
489 if (timerId === undefined) {
490 timerId = setTimeout(timerExpired, wait);
491 }
492 return result;
493 }
494 debounced.cancel = cancel;
495 debounced.flush = flush;
496 return debounced;
497 }
498
499 /**
500 * Creates a throttled function that only invokes `func` at most once per
501 * every `wait` milliseconds. The throttled function comes with a `cancel`
502 * method to cancel delayed `func` invocations and a `flush` method to
503 * immediately invoke them. Provide `options` to indicate whether `func`
504 * should be invoked on the leading and/or trailing edge of the `wait`
505 * timeout. The `func` is invoked with the last arguments provided to the
506 * throttled function. Subsequent calls to the throttled function return the
507 * result of the last `func` invocation.
508 *
509 * **Note:** If `leading` and `trailing` options are `true`, `func` is
510 * invoked on the trailing edge of the timeout only if the throttled function
511 * is invoked more than once during the `wait` timeout.
512 *
513 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
514 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
515 *
516 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
517 * for details over the differences between `_.throttle` and `_.debounce`.
518 *
519 * @static
520 * @memberOf _
521 * @since 0.1.0
522 * @category Function
523 * @param {Function} func The function to throttle.
524 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
525 * @param {Object} [options={}] The options object.
526 * @param {boolean} [options.leading=true]
527 * Specify invoking on the leading edge of the timeout.
528 * @param {boolean} [options.trailing=true]
529 * Specify invoking on the trailing edge of the timeout.
530 * @returns {Function} Returns the new throttled function.
531 * @example
532 *
533 * // Avoid excessively updating the position while scrolling.
534 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
535 *
536 * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
537 * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
538 * jQuery(element).on('click', throttled);
539 *
540 * // Cancel the trailing throttled invocation.
541 * jQuery(window).on('popstate', throttled.cancel);
542 */
543 function throttle(func, wait, options) {
544 var leading = true,
545 trailing = true;
546
547 if (typeof func != 'function') {
548 throw new TypeError(FUNC_ERROR_TEXT);
549 }
550 if (isObject(options)) {
551 leading = 'leading' in options ? !!options.leading : leading;
552 trailing = 'trailing' in options ? !!options.trailing : trailing;
553 }
554 return debounce(func, wait, {
555 'leading': leading,
556 'maxWait': wait,
557 'trailing': trailing
558 });
559 }
560
561 /**
562 * Checks if `value` is the
563 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
564 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
565 *
566 * @static
567 * @memberOf _
568 * @since 0.1.0
569 * @category Lang
570 * @param {*} value The value to check.
571 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
572 * @example
573 *
574 * _.isObject({});
575 * // => true
576 *
577 * _.isObject([1, 2, 3]);
578 * // => true
579 *
580 * _.isObject(_.noop);
581 * // => true
582 *
583 * _.isObject(null);
584 * // => false
585 */
586 function isObject(value) {
587 var type = typeof value;
588 return !!value && (type == 'object' || type == 'function');
589 }
590
591 /**
592 * Checks if `value` is object-like. A value is object-like if it's not `null`
593 * and has a `typeof` result of "object".
594 *
595 * @static
596 * @memberOf _
597 * @since 4.0.0
598 * @category Lang
599 * @param {*} value The value to check.
600 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
601 * @example
602 *
603 * _.isObjectLike({});
604 * // => true
605 *
606 * _.isObjectLike([1, 2, 3]);
607 * // => true
608 *
609 * _.isObjectLike(_.noop);
610 * // => false
611 *
612 * _.isObjectLike(null);
613 * // => false
614 */
615 function isObjectLike(value) {
616 return !!value && typeof value == 'object';
617 }
618
619 /**
620 * Checks if `value` is classified as a `Symbol` primitive or object.
621 *
622 * @static
623 * @memberOf _
624 * @since 4.0.0
625 * @category Lang
626 * @param {*} value The value to check.
627 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
628 * @example
629 *
630 * _.isSymbol(Symbol.iterator);
631 * // => true
632 *
633 * _.isSymbol('abc');
634 * // => false
635 */
636 function isSymbol(value) {
637 return typeof value == 'symbol' ||
638 (isObjectLike(value) && objectToString.call(value) == symbolTag);
639 }
640
641 /**
642 * Converts `value` to a number.
643 *
644 * @static
645 * @memberOf _
646 * @since 4.0.0
647 * @category Lang
648 * @param {*} value The value to process.
649 * @returns {number} Returns the number.
650 * @example
651 *
652 * _.toNumber(3.2);
653 * // => 3.2
654 *
655 * _.toNumber(Number.MIN_VALUE);
656 * // => 5e-324
657 *
658 * _.toNumber(Infinity);
659 * // => Infinity
660 *
661 * _.toNumber('3.2');
662 * // => 3.2
663 */
664 function toNumber(value) {
665 if (typeof value == 'number') {
666 return value;
667 }
668 if (isSymbol(value)) {
669 return NAN;
670 }
671 if (isObject(value)) {
672 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
673 value = isObject(other) ? (other + '') : other;
674 }
675 if (typeof value != 'string') {
676 return value === 0 ? value : +value;
677 }
678 value = value.replace(reTrim, '');
679 var isBinary = reIsBinary.test(value);
680 return (isBinary || reIsOctal.test(value))
681 ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
682 : (reIsBadHex.test(value) ? NAN : +value);
683 }
684
685 var lodash_throttle = throttle;
686
687 var defaults$3 = {
688 item: undefined,
689 next: undefined,
690 prev: undefined,
691 pagination: undefined,
692 responseType: 'document',
693 bind: true,
694 scrollContainer: window,
695 spinner: false,
696 logger: true,
697 loadOnScroll: true,
698 negativeMargin: 0,
699 trigger: false,
700 prefill: true,
701 };
702
703 /* eslint no-console: "off" */
704
705 var Assert = {
706 singleElement: function singleElement(elementOrSelector, property) {
707 var $element = tealight(elementOrSelector);
708
709 if ($element.length > 1) {
710 throw new Error(("Expected single element for \"" + property + "\""));
711 }
712
713 if ($element.length === 0) {
714 throw new Error(("Element \"" + elementOrSelector + "\" not found for \"" + property + "\""));
715 }
716 },
717 anyElement: function anyElement(elementOrSelector, property) {
718 var $element = tealight(elementOrSelector);
719
720 if ($element.length === 0) {
721 throw new Error(("Element \"" + elementOrSelector + "\" not found for \"" + property + "\""));
722 }
723 },
724 warn: function warn(fn) {
725 var args = [], len = arguments.length - 1;
726 while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
727
728 try {
729 fn.apply(void 0, args);
730 } catch (e) {
731 if (console && console.warn) {
732 console.warn(e.message);
733 }
734 }
735 }
736 };
737
738 function getScrollPosition(el) {
739 if (el !== window) {
740 return {
741 x: el.scrollLeft,
742 y: el.scrollTop,
743 };
744 }
745
746 var supportPageOffset = window.pageXOffset !== undefined;
747 var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");
748
749 return {
750 x: supportPageOffset ? window.pageXOffset : isCSS1Compat ? document.documentElement.scrollLeft : document.body.scrollLeft,
751 y: supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop
752 };
753 }
754
755 function getRootRect(el) {
756 var rootRect;
757
758 if (el !== window) {
759 rootRect = el.getBoundingClientRect();
760 } else {
761 // Use <html>/<body> instead of window since scroll bars affect size.
762 var html = document.documentElement;
763 var body = document.body;
764
765 rootRect = {
766 top: 0,
767 left: 0,
768 right: html.clientWidth || body.clientWidth,
769 width: html.clientWidth || body.clientWidth,
770 bottom: html.clientHeight || body.clientHeight,
771 height: html.clientHeight || body.clientHeight
772 };
773 }
774
775 return rootRect;
776 }
777
778 function getDistanceToFold(sentinel, scrollContainerScrollPosition, scrollContainerRootRect) {
779 var rootRect = scrollContainerRootRect;
780
781 // this means the container the doesn't have any items yet - it's empty
782 if (!sentinel) {
783 return rootRect.height * -1;
784 }
785
786 var scrollYTop = scrollContainerScrollPosition.y;
787 var boundingRect = sentinel.getBoundingClientRect();
788
789 var scrollYBottom = scrollYTop + rootRect.height;
790 var bottom = scrollYTop + boundingRect.bottom - rootRect.top;
791
792 return Math.trunc(bottom - scrollYBottom);
793 }
794
795 var APPEND = 'append';
796 var APPENDED = 'appended';
797 var PREPEND = 'prepend';
798 var PREPENDED = 'prepended';
799 var BINDED = 'binded';
800 var UNBINDED = 'unbinded';
801 var HIT = 'hit';
802 var TOP = 'top';
803 var LOAD = 'load';
804 var LOADED = 'loaded';
805 var ERROR = 'error';
806 var FIRST = 'first';
807 var LAST = 'last';
808 var NEXT = 'next';
809 var NEXTED = 'nexted';
810 var PREV = 'prev';
811 var PREVED = 'preved';
812 var READY = 'ready';
813 var SCROLLED = 'scrolled';
814 var RESIZED = 'resized';
815 var PAGE = 'page';
816 var PREFILL = 'prefill';
817 var PREFILLED = 'prefilled';
818
819 var events = {
820 APPEND: APPEND,
821 APPENDED: APPENDED,
822 PREPEND: PREPEND,
823 PREPENDED: PREPENDED,
824 BINDED: BINDED,
825 UNBINDED: UNBINDED,
826 HIT: HIT,
827 TOP: TOP,
828 LOAD: LOAD,
829 LOADED: LOADED,
830 ERROR: ERROR,
831 FIRST: FIRST,
832 LAST: LAST,
833 NEXT: NEXT,
834 NEXTED: NEXTED,
835 PREV: PREV,
836 PREVED: PREVED,
837 READY: READY,
838 SCROLLED: SCROLLED,
839 RESIZED: RESIZED,
840 PAGE: PAGE,
841 PREFILL: PREFILL,
842 PREFILLED: PREFILLED,
843 };
844
845 var defaultLastScroll = {
846 y: 0,
847 x: 0,
848 deltaY: 0,
849 deltaX: 0
850 };
851
852 function calculateScroll(scrollContainer, lastScroll) {
853 var scroll = getScrollPosition(scrollContainer);
854
855 scroll.deltaY = scroll.y - (lastScroll ? lastScroll.y : scroll.y);
856 scroll.deltaX = scroll.x - (lastScroll ? lastScroll.x : scroll.x);
857
858 return scroll;
859 }
860
861 function scrollHandler() {
862 var ias = this;
863 var lastScroll = ias._lastScroll || defaultLastScroll;
864
865 var scroll = ias._lastScroll = calculateScroll(ias.scrollContainer, lastScroll);
866
867 this.emitter.emit(SCROLLED, {scroll: scroll});
868 }
869
870 function resizeHandler() {
871 var ias = this;
872 var lastScroll = ias._lastScroll || defaultLastScroll;
873
874 var scroll = ias._lastScroll = calculateScroll(ias.scrollContainer, lastScroll);
875
876 this.emitter.emit(RESIZED, {scroll: scroll});
877 }
878
879 function E () {
880 // Keep this empty so it's easier to inherit from
881 // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
882 }
883
884 E.prototype = {
885 on: function (name, callback, ctx) {
886 var e = this.e || (this.e = {});
887
888 (e[name] || (e[name] = [])).push({
889 fn: callback,
890 ctx: ctx
891 });
892
893 return this;
894 },
895
896 once: function (name, callback, ctx) {
897 var self = this;
898 function listener () {
899 self.off(name, listener);
900 callback.apply(ctx, arguments);
901 }
902 listener._ = callback;
903 return this.on(name, listener, ctx);
904 },
905
906 emit: function (name) {
907 var data = [].slice.call(arguments, 1);
908 var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
909 var i = 0;
910 var len = evtArr.length;
911
912 for (i; i < len; i++) {
913 evtArr[i].fn.apply(evtArr[i].ctx, data);
914 }
915
916 return this;
917 },
918
919 off: function (name, callback) {
920 var e = this.e || (this.e = {});
921 var evts = e[name];
922 var liveEvents = [];
923
924 if (evts && callback) {
925 for (var i = 0, len = evts.length; i < len; i++) {
926 if (evts[i].fn !== callback && evts[i].fn._ !== callback)
927 { liveEvents.push(evts[i]); }
928 }
929 }
930
931 // Remove event from queue to prevent memory leak
932 // Suggested by https://github.com/lazd
933 // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
934
935 (liveEvents.length)
936 ? e[name] = liveEvents
937 : delete e[name];
938
939 return this;
940 }
941 };
942
943 var tinyEmitter = E;
944 var TinyEmitter = E;
945 tinyEmitter.TinyEmitter = TinyEmitter;
946
947 function nextHandler(pageIndex) {
948 var ias = this;
949 var lastResponse = ias._lastResponse || document.body;
950
951 var nextEl = tealight(ias.options.next, lastResponse)[0];
952
953 if (!nextEl) {
954 Assert.warn(Assert.singleElement, ias.options.next, 'options.next');
955
956 return;
957 }
958
959 var nextUrl = nextEl.href;
960
961 return ias.load(nextUrl)
962 .then(function (data) {
963 lastResponse = ias._lastResponse = data.xhr.response;
964
965 var nextEl = tealight(ias.options.next, lastResponse)[0];
966
967 return ias.append(data.items)
968 .then(function () {
969 return !!nextEl;
970 })
971 .then(function (hasNextEl) {
972 // only warn for first page, because at some point it's expected that there is no next element
973 if (!hasNextEl && pageIndex <= 1 && console && console.warn) {
974 console.warn(("Element \"" + (ias.options.next) + "\" not found for \"options.next\" on \"" + (data.url) + "\""));
975 }
976
977 return hasNextEl;
978 });
979 });
980 }
981
982 function prevHandler(pageIndex) {
983 var ias = this;
984 var prevEl = ias._prevEl || tealight(ias.options.prev, document.body)[0];
985
986 if (ias.options.prev === undefined) {
987 return;
988 }
989
990 if (!prevEl) {
991 Assert.warn(Assert.singleElement, ias.options.prev, 'options.prev');
992
993 return;
994 }
995
996 var prevUrl = prevEl.href;
997
998 return ias.load(prevUrl)
999 .then(function (data) {
1000 var prevEl = ias._prevEl = tealight(ias.options.prev, data.xhr.response)[0];
1001
1002 return ias.prepend(data.items)
1003 .then(function () { return !!prevEl; }) // TODO: evaluate if this makes sense
1004 });
1005 }
1006
1007 var defaults$2 = {
1008 element: undefined,
1009 hide: false
1010 };
1011
1012 function expand$3(options) {
1013 if (typeof options === 'string' || (typeof options === 'object' && options.nodeType === Node.ELEMENT_NODE)) {
1014 options = {
1015 element: options,
1016 hide: true,
1017 };
1018 } else if (typeof options === 'boolean') {
1019 options = {
1020 element: undefined,
1021 hide: options,
1022 };
1023 }
1024
1025 return options;
1026 }
1027
1028 var Pagination = function Pagination(ias, options) {
1029 this.options = extend({}, defaults$2, expand$3(options));
1030 this.originalDisplayStyles = new WeakMap();
1031
1032 if (!this.options.hide) {
1033 return;
1034 }
1035
1036 Assert.warn(Assert.anyElement, this.options.element, 'pagination.element');
1037
1038 ias.on(BINDED, this.hide.bind(this));
1039 ias.on(UNBINDED, this.restore.bind(this));
1040 };
1041
1042 Pagination.prototype.hide = function hide () {
1043 var this$1$1 = this;
1044
1045 var els = tealight(this.options.element);
1046
1047 els.forEach(function (el) {
1048 this$1$1.originalDisplayStyles.set(el, window.getComputedStyle(el).display);
1049
1050 el.style.display = 'none';
1051 });
1052 };
1053
1054 Pagination.prototype.restore = function restore () {
1055 var this$1$1 = this;
1056
1057 var els = tealight(this.options.element);
1058
1059 els.forEach(function (el) {
1060 el.style.display = this$1$1.originalDisplayStyles.get(el) || 'block';
1061 });
1062 };
1063
1064 var defaults$1 = {
1065 element: undefined,
1066 delay: 600,
1067 show: function (element) {
1068 element.style.opacity = '1';
1069 },
1070 hide: function (element) {
1071 element.style.opacity = '0';
1072 }
1073 };
1074
1075 function expand$2(options) {
1076 if (typeof options === 'string' || (typeof options === 'object' && options.nodeType === Node.ELEMENT_NODE)) {
1077 options = {
1078 element: options,
1079 };
1080 }
1081
1082 return options;
1083 }
1084
1085 var Spinner = function Spinner(ias, options) {
1086 // no spinner wanted
1087 if (options === false) {
1088 return;
1089 }
1090
1091 this.ias = ias;
1092 this.options = extend({}, defaults$1, expand$2(options));
1093
1094 if (this.options.element !== undefined) {
1095 Assert.singleElement(this.options.element, 'spinner.element');
1096 }
1097
1098 this.element = tealight(this.options.element)[0]; // @todo should we really cache this?
1099 this.hideFn = this.options.hide;
1100 this.showFn = this.options.show;
1101
1102 ias.on(BINDED, this.bind.bind(this));
1103 ias.on(BINDED, this.hide.bind(this));
1104 };
1105
1106 Spinner.prototype.bind = function bind () {
1107 var startTime, endTime, diff, delay, self = this, ias = this.ias;
1108
1109 ias.on(NEXT, function () {
1110 startTime = +new Date();
1111
1112 self.show();
1113 });
1114
1115 ias.on(LAST, function () {
1116 self.hide();
1117 });
1118
1119 // setup delay
1120 ias.on(APPEND, function (event) {
1121 endTime = +new Date();
1122 diff = endTime - startTime;
1123
1124 delay = Math.max(0, self.options.delay - diff);
1125
1126 var _appendFn = event.appendFn.bind({});
1127
1128 event.appendFn = function(items, parent, last) {
1129 return new Promise(function (resolve) {
1130 setTimeout(function() {
1131 self.hide().then(function() {
1132 _appendFn(items, parent, last);
1133 resolve();
1134 });
1135 }, delay);
1136 });
1137 };
1138 });
1139 };
1140
1141 Spinner.prototype.show = function show () {
1142 return Promise.resolve(this.showFn(this.element));
1143 };
1144
1145 Spinner.prototype.hide = function hide () {
1146 return Promise.resolve(this.hideFn(this.element));
1147 };
1148
1149 /* eslint no-console: "off" */
1150
1151 var defaultLogger = {
1152 hit: function () {
1153 console.log("Hit scroll threshold");
1154 },
1155 top: function () {
1156 console.log("Hit top scroll threshold");
1157 },
1158 binded: function () {
1159 console.log("Binded event handlers");
1160 },
1161 unbinded: function () {
1162 console.log("Unbinded event handlers");
1163 },
1164 // scrolled: (event) => {
1165 // console.log('Scrolled');
1166 // },
1167 // resized: (event) => {
1168 // console.log('Resized');
1169 // },
1170 next: function (event) {
1171 console.log(("Next page triggered [pageIndex=" + (event.pageIndex) + "]"));
1172 },
1173 nexted: function (event) {
1174 console.log(("Next page completed [pageIndex=" + (event.pageIndex) + "]"));
1175 },
1176 prev: function (event) {
1177 console.log(("Previous page triggered [pageIndex=" + (event.pageIndex) + "]"));
1178 },
1179 preved: function (event) {
1180 console.log(("Previous page completed [pageIndex=" + (event.pageIndex) + "]"));
1181 },
1182 load: function (event) {
1183 console.log(("Start loading " + (event.url)));
1184 },
1185 loaded: function () {
1186 console.log("Finished loading");
1187 },
1188 append: function () {
1189 console.log("Start appending items");
1190 },
1191 appended: function (event) {
1192 console.log(("Finished appending " + (event.items.length) + " item(s)"));
1193 },
1194 prepend: function () {
1195 console.log("Start prepending items");
1196 },
1197 prepended: function (event) {
1198 console.log(("Finished prepending " + (event.items.length) + " item(s)"));
1199 },
1200 last: function () {
1201 console.log("No more pages left to load");
1202 },
1203 first: function () {
1204 console.log("Reached first page");
1205 },
1206 page: function (event) {
1207 console.log(("Page changed [pageIndex=" + (event.pageIndex) + "]"));
1208 },
1209 prefill: function (event) {
1210 console.log("Start prefilling");
1211 },
1212 prefilled: function (event) {
1213 console.log("Finished prefilling");
1214 },
1215 };
1216
1217 function expand$1(options) {
1218 if (options === true) {
1219 options = defaultLogger;
1220 }
1221
1222 return options;
1223 }
1224
1225 var Logger = function Logger(ias, options) {
1226 // no logger wanted
1227 if (options === false) {
1228 return;
1229 }
1230
1231 var logger = expand$1(options);
1232
1233 Object.keys(logger).forEach(function (key) {
1234 ias.on(key, logger[key]);
1235 });
1236 };
1237
1238 function getPageBreak(pageBreaks, scrollTop, scrollContainer) {
1239 var rootRect = getRootRect(scrollContainer);
1240 var scrollBottom = scrollTop + rootRect.height;
1241
1242 for (var b = pageBreaks.length - 1; b >= 0; b--) {
1243 var bottom = pageBreaks[b].sentinel.getBoundingClientRect().bottom + scrollTop;
1244
1245 if (scrollBottom > bottom) {
1246 var x = Math.min(b + 1, pageBreaks.length - 1);
1247
1248 return pageBreaks[x];
1249 }
1250 }
1251
1252 return pageBreaks[0];
1253 }
1254
1255 var Paging = function Paging(ias) {
1256 this.ias = ias;
1257 this.pageBreaks = [];
1258 this.currentPageIndex = ias.pageIndex;
1259 this.currentScrollTop = 0;
1260
1261 ias.on(BINDED, this.binded.bind(this));
1262 ias.on(NEXT, this.next.bind(this));
1263 ias.on(PREV, this.prev.bind(this));
1264 ias.on(SCROLLED, this.scrolled.bind(this));
1265 ias.on(RESIZED, this.scrolled.bind(this));
1266 };
1267
1268 Paging.prototype.binded = function binded () {
1269 var sentinel = this.ias.sentinel();
1270 if (!sentinel) {
1271 return;
1272 }
1273
1274 this.pageBreaks.push({
1275 pageIndex: this.currentPageIndex,
1276 url: document.location.toString(),
1277 title: document.title,
1278 sentinel: this.ias.sentinel()
1279 });
1280 };
1281
1282 Paging.prototype.next = function next () {
1283 var this$1$1 = this;
1284
1285 var url = document.location.toString();
1286 var title = document.title;
1287
1288 var loaded = function (event) {
1289 url = event.url;
1290
1291 if (event.xhr.response) {
1292 title = event.xhr.response.title;
1293 }
1294 };
1295
1296 this.ias.once(LOADED, loaded);
1297
1298 this.ias.once(NEXTED, function (event) {
1299 this$1$1.pageBreaks.push({
1300 pageIndex: event.pageIndex,
1301 url: url,
1302 title: title,
1303 sentinel: this$1$1.ias.sentinel()
1304 });
1305
1306 this$1$1.update();
1307
1308 this$1$1.ias.off(LOADED, loaded);
1309 });
1310 };
1311
1312 Paging.prototype.prev = function prev () {
1313 var this$1$1 = this;
1314
1315 var url = document.location.toString();
1316 var title = document.title;
1317
1318 var loaded = function (event) {
1319 url = event.url;
1320
1321 if (event.xhr.response) {
1322 title = event.xhr.response.title;
1323 }
1324 };
1325
1326 this.ias.once(LOADED, loaded);
1327
1328 this.ias.once(PREVED, function (event) {
1329 this$1$1.pageBreaks.unshift({
1330 pageIndex: event.pageIndex,
1331 url: url,
1332 title: title,
1333 sentinel: this$1$1.ias.first()
1334 });
1335
1336 this$1$1.update();
1337
1338 this$1$1.ias.off(LOADED, loaded);
1339 });
1340 };
1341
1342 Paging.prototype.scrolled = function scrolled (event) {
1343 this.update(event.scroll.y);
1344 };
1345
1346 Paging.prototype.update = function update (scrollTop) {
1347 this.currentScrollTop = scrollTop || this.currentScrollTop;
1348
1349 var pageBreak = getPageBreak(this.pageBreaks, this.currentScrollTop, this.ias.scrollContainer);
1350
1351 if (pageBreak && pageBreak.pageIndex !== this.currentPageIndex) {
1352 this.ias.emitter.emit(PAGE, pageBreak);
1353
1354 this.currentPageIndex = pageBreak.pageIndex;
1355 }
1356 };
1357
1358 var defaults = {
1359 element: undefined,
1360 when: function (pageIndex) { return true; },
1361 show: function (element) {
1362 element.style.opacity = '1';
1363 },
1364 hide: function (element) {
1365 element.style.opacity = '0';
1366 }
1367 };
1368
1369 function expand(options) {
1370 if (typeof options === 'string' || typeof options === 'function' || (typeof options === 'object' && options.nodeType === Node.ELEMENT_NODE)) {
1371 options = {
1372 element: options,
1373 };
1374 }
1375
1376 if (typeof options.element === 'function') {
1377 options.element = options.element();
1378 }
1379
1380 // expand array to a function, e.g.:
1381 // [0, 1, 2] -> function(pageIndex) { /* return true when pageIndex in [0, 1, 2] */ }
1382 if (options.when && Array.isArray(options.when)) {
1383 var when = options.when;
1384 options.when = function(pageIndex) {
1385 return when.indexOf(pageIndex) !== -1;
1386 };
1387 }
1388
1389 return options;
1390 }
1391
1392 var Trigger = function Trigger(ias, options) {
1393 var this$1$1 = this;
1394
1395 // no trigger wanted
1396 if (options === false) {
1397 return;
1398 }
1399
1400 this.ias = ias;
1401 this.options = extend({}, defaults, expand(options));
1402
1403 if (this.options.element !== undefined) {
1404 Assert.singleElement(this.options.element, 'trigger.element');
1405 }
1406
1407 this.element = tealight(this.options.element)[0]; // @todo should we really cache this?
1408 this.hideFn = this.options.hide;
1409 this.showFn = this.options.show;
1410 this.voter = this.options.when;
1411 this.showing = undefined;
1412 this.enabled = undefined;
1413
1414 ias.on(BINDED, this.bind.bind(this));
1415 ias.on(UNBINDED, this.unbind.bind(this));
1416 ias.on(HIT, this.hit.bind(this));
1417 ias.on(NEXT, function (e) { return this$1$1.ias.once(APPENDED, function () { return this$1$1.update(e.pageIndex); }); });
1418 };
1419
1420 Trigger.prototype.bind = function bind () {
1421 this.hide();
1422 this.update(this.ias.pageIndex);
1423
1424 this.element.addEventListener('click', this.clickHandler.bind(this));
1425 };
1426
1427 Trigger.prototype.unbind = function unbind () {
1428 this.element.removeEventListener('click', this.clickHandler.bind(this));
1429 };
1430
1431 Trigger.prototype.clickHandler = function clickHandler () {
1432 this.hide().then(this.ias.next.bind(this.ias));
1433 };
1434
1435 Trigger.prototype.update = function update (pageIndex) {
1436 this.enabled = this.voter(pageIndex);
1437
1438 if (this.enabled) {
1439 this.ias.disableLoadOnScroll();
1440 } else {
1441 this.ias.enableLoadOnScroll();
1442 }
1443 };
1444
1445 Trigger.prototype.hit = function hit () {
1446 if (!this.enabled) {
1447 return;
1448 }
1449
1450 this.show();
1451 };
1452
1453 Trigger.prototype.show = function show () {
1454 if (this.showing) {
1455 return;
1456 }
1457
1458 this.showing = true;
1459
1460 return Promise.resolve(this.showFn(this.element));
1461 };
1462
1463 Trigger.prototype.hide = function hide () {
1464 if (!this.showing && this.showing !== undefined) {
1465 return;
1466 }
1467
1468 this.showing = false;
1469
1470 return Promise.resolve(this.hideFn(this.element));
1471 };
1472
1473 function appendFn(items, parent, last) {
1474 var sibling = last ? last.nextSibling : null;
1475 var insert = document.createDocumentFragment();
1476
1477 items.forEach(function (item) {
1478 insert.appendChild(item);
1479 });
1480
1481 parent.insertBefore(insert, sibling);
1482 }
1483
1484 function prependFn(items, parent, first) {
1485 var insert = document.createDocumentFragment();
1486
1487 items.forEach(function (item) {
1488 insert.appendChild(item);
1489 });
1490
1491 parent.insertBefore(insert, first);
1492 }
1493
1494 /* eslint no-console: "off" */
1495
1496 var NativeResizeObserver = window.ResizeObserver;
1497
1498 var EventListenerResizeObserver = function EventListenerResizeObserver(el, listener) {
1499 this.el = el;
1500 this.listener = listener;
1501 };
1502
1503 EventListenerResizeObserver.prototype.observe = function observe () {
1504 this.el.addEventListener('resize', this.listener);
1505 };
1506
1507 EventListenerResizeObserver.prototype.unobserve = function unobserve () {
1508 this.el.removeEventListener('resize', this.listener);
1509 };
1510
1511 var NativeWrapperResizeObserver = function NativeWrapperResizeObserver(el, listener) {
1512 this.el = el;
1513 this.listener = listener;
1514 this.ro = new NativeResizeObserver(this.listener);
1515 };
1516
1517 NativeWrapperResizeObserver.prototype.observe = function observe () {
1518 this.ro.observe(this.el);
1519 };
1520
1521 NativeWrapperResizeObserver.prototype.unobserve = function unobserve () {
1522 this.ro.unobserve();
1523 };
1524
1525 var PollingResizeObserver = function PollingResizeObserver(el, listener) {
1526 this.el = el;
1527 this.listener = listener;
1528 this.interval = null;
1529 this.lastHeight = null;
1530 };
1531
1532 PollingResizeObserver.prototype.pollHeight = function pollHeight () {
1533 var height = Math.trunc(getRootRect(this.el).height);
1534
1535 if (this.lastHeight !== null && this.lastHeight !== height) {
1536 this.listener();
1537 }
1538
1539 this.lastHeight = height;
1540 };
1541
1542 PollingResizeObserver.prototype.observe = function observe () {
1543 this.interval = setInterval(this.pollHeight.bind(this), 200);
1544 };
1545
1546 PollingResizeObserver.prototype.unobserve = function unobserve () {
1547 clearInterval(this.interval);
1548 };
1549
1550 function ResizeObserverFactory(ias, el) {
1551 var listener = lodash_throttle(resizeHandler, 200).bind(ias);
1552
1553 if (el === window) {
1554 return new EventListenerResizeObserver(el, listener);
1555 }
1556
1557 if (NativeResizeObserver) {
1558 return new NativeWrapperResizeObserver(el, listener);
1559 }
1560
1561 if (console && console.warn) {
1562 console.warn('ResizeObserver not supported. Falling back on polling.');
1563 }
1564
1565 return new PollingResizeObserver(el, listener);
1566 }
1567
1568 var Prefill = function Prefill(ias, options) {
1569 this.ias = ias;
1570 this.enabled = options;
1571 };
1572
1573 Prefill.prototype.prefill = function prefill () {
1574 var this$1$1 = this;
1575
1576 if (!this.enabled) {
1577 return;
1578 }
1579
1580 this.ias.emitter.emit(events.PREFILL);
1581
1582 return Promise.all([this._prefillNext(), this._prefillPrev()]).then(function () {
1583 this$1$1.ias.emitter.emit(events.PREFILLED);
1584
1585 // @todo reevaluate if we should actually call `measure` here.
1586 this$1$1.ias.measure();
1587 });
1588 };
1589
1590 Prefill.prototype._prefillNext = function _prefillNext () {
1591 var this$1$1 = this;
1592
1593 var distance = this.ias.distance();
1594
1595 if (distance > 0) {
1596 return;
1597 }
1598
1599 return this.ias.next()
1600 .then(function (hasNextUrl) {
1601 if (!hasNextUrl) {
1602 return;
1603 }
1604
1605 var distance = this$1$1.ias.distance();
1606
1607 if (distance < 0) {
1608 return this$1$1._prefillNext();
1609 }
1610 })
1611 ;
1612 };
1613
1614 Prefill.prototype._prefillPrev = function _prefillPrev () {
1615 if (!this.ias.options.prev) {
1616 return;
1617 }
1618
1619 return this.ias.prev();
1620 };
1621
1622 var InfiniteAjaxScroll = function InfiniteAjaxScroll(container, options) {
1623 var this$1$1 = this;
1624 if ( options === void 0 ) options = {};
1625
1626 Assert.singleElement(container, 'container');
1627
1628 this.container = tealight(container)[0];
1629 this.options = extend({}, defaults$3, options);
1630 this.emitter = new tinyEmitter();
1631
1632 this.options.loadOnScroll ? this.enableLoadOnScroll() : this.disableLoadOnScroll();
1633 this.negativeMargin = Math.abs(this.options.negativeMargin);
1634
1635 this.scrollContainer = this.options.scrollContainer;
1636 if (this.options.scrollContainer !== window) {
1637 Assert.singleElement(this.options.scrollContainer, 'options.scrollContainer');
1638
1639 this.scrollContainer = tealight(this.options.scrollContainer)[0];
1640 }
1641
1642 this.nextHandler = nextHandler;
1643 this.prevHandler = prevHandler;
1644
1645 if (this.options.next === false) {
1646 this.nextHandler = function() {};
1647 } else if (typeof this.options.next === 'function') {
1648 this.nextHandler = this.options.next;
1649 }
1650
1651 if (this.options.prev === false) {
1652 this.prevHandler = function() {};
1653 } else if (typeof this.options.prev === 'function') {
1654 this.prevHandler = this.options.prev;
1655 }
1656
1657 this.resizeObserver = ResizeObserverFactory(this, this.scrollContainer);
1658 this._scrollListener = lodash_throttle(scrollHandler, 200).bind(this);
1659
1660 this.ready = false;
1661 this.bindOnReady = true;
1662 this.binded = false;
1663 this.paused = false;
1664 this.pageIndexPrev = 0;
1665 this.pageIndex = this.pageIndexNext = this.sentinel() ? 0 : -1;
1666
1667 this.on(HIT, function () {
1668 if (!this$1$1.loadOnScroll) {
1669 return;
1670 }
1671
1672 this$1$1.next();
1673 });
1674
1675 this.on(TOP, function () {
1676 if (!this$1$1.loadOnScroll) {
1677 return;
1678 }
1679
1680 this$1$1.prev();
1681 });
1682
1683 this.on(SCROLLED, this.measure);
1684 this.on(RESIZED, this.measure);
1685
1686 // initialize extensions
1687 this.pagination = new Pagination(this, this.options.pagination);
1688 this.spinner = new Spinner(this, this.options.spinner);
1689 this.logger = new Logger(this, this.options.logger);
1690 this.paging = new Paging(this);
1691 this.trigger = new Trigger(this, this.options.trigger);
1692 this.prefill = new Prefill(this, this.options.prefill);
1693
1694 // prefill/measure after all plugins are done binding
1695 this.on(BINDED, this.prefill.prefill.bind(this.prefill));
1696
1697 this.hitFirst = this.hitLast = false;
1698
1699 this.on(LAST, function () { return this$1$1.hitLast = true; });
1700 this.on(FIRST, function () { return this$1$1.hitFirst = true; });
1701
1702 var ready = function () {
1703 if (this$1$1.ready) {
1704 return;
1705 }
1706
1707 this$1$1.ready = true;
1708
1709 this$1$1.emitter.emit(READY);
1710
1711 if (this$1$1.bindOnReady && this$1$1.options.bind) {
1712 this$1$1.bind();
1713 }
1714 };
1715
1716 if (document.readyState === "complete" || document.readyState === "interactive") {
1717 setTimeout(ready, 1);
1718 } else {
1719 window.addEventListener('DOMContentLoaded', ready);
1720 }
1721 };
1722
1723 InfiniteAjaxScroll.prototype.bind = function bind () {
1724 if (this.binded) {
1725 return;
1726 }
1727
1728 // If we manually call bind before the dom is ready, we assume that we want
1729 // to take control over the bind flow.
1730 if (!this.ready) {
1731 this.bindOnReady = false;
1732 }
1733
1734 this.scrollContainer.addEventListener('scroll', this._scrollListener);
1735 this.resizeObserver.observe();
1736
1737 this.binded = true;
1738
1739 this.emitter.emit(BINDED);
1740 };
1741
1742 InfiniteAjaxScroll.prototype.unbind = function unbind () {
1743 if (!this.binded) {
1744 if (!this.ready) {
1745 this.once(BINDED, this.unbind);
1746 }
1747
1748 return;
1749 }
1750
1751 this.resizeObserver.unobserve();
1752 this.scrollContainer.removeEventListener('scroll', this._scrollListener);
1753
1754 this.binded = false;
1755
1756 this.emitter.emit(UNBINDED);
1757 };
1758
1759 InfiniteAjaxScroll.prototype.next = function next () {
1760 var this$1$1 = this;
1761
1762 if (this.hitLast) {
1763 return;
1764 }
1765
1766 if (!this.binded) {
1767 if (!this.ready) {
1768 return this.once(BINDED, this.next);
1769 }
1770
1771 return;
1772 }
1773
1774 this.pause();
1775
1776 var pageIndex = this.pageIndexNext + 1;
1777
1778 this.emitter.emit(NEXT, {pageIndex: this.pageIndexNext + 1});
1779
1780 return Promise.resolve(this.nextHandler(pageIndex))
1781 .then(function (hasNextUrl) {
1782 this$1$1.pageIndexNext = pageIndex;
1783
1784 if (!hasNextUrl) {
1785 this$1$1.emitter.emit(LAST);
1786 }
1787
1788 this$1$1.resume();
1789
1790 return hasNextUrl;
1791 }).then(function (hasNextUrl) {
1792 this$1$1.emitter.emit(NEXTED, {pageIndex: this$1$1.pageIndexNext});
1793
1794 return hasNextUrl;
1795 });
1796 };
1797
1798 InfiniteAjaxScroll.prototype.prev = function prev () {
1799 var this$1$1 = this;
1800
1801 if (!this.binded || this.hitFirst) {
1802 return;
1803 }
1804
1805 this.pause();
1806
1807 var pageIndex = this.pageIndexPrev - 1;
1808
1809 this.emitter.emit(PREV, {pageIndex: this.pageIndexPrev - 1});
1810
1811 return Promise.resolve(this.prevHandler(pageIndex))
1812 .then(function (hasPrevUrl) {
1813 this$1$1.pageIndexPrev = pageIndex;
1814
1815 this$1$1.resume();
1816
1817 if (!hasPrevUrl) {
1818 this$1$1.emitter.emit(FIRST);
1819 }
1820
1821 return hasPrevUrl;
1822 }).then(function (hasPrevUrl) {
1823 this$1$1.emitter.emit(PREVED, {pageIndex: this$1$1.pageIndexPrev});
1824
1825 return hasPrevUrl;
1826 });
1827 };
1828
1829 /**
1830 * @param {string} url
1831 * @returns {Promise} returns LOADED event on success
1832 */
1833 InfiniteAjaxScroll.prototype.load = function load (url) {
1834 var ias = this;
1835
1836 return new Promise(function (resolve, reject) {
1837 var xhr = new XMLHttpRequest();
1838
1839 var loadEvent = {
1840 url: url,
1841 xhr: xhr,
1842 method: 'GET',
1843 body: null,
1844 nocache: false,
1845 responseType: ias.options.responseType,
1846 headers: {
1847 'X-Requested-With': 'XMLHttpRequest',
1848 },
1849 };
1850
1851 // event properties are mutable
1852 ias.emitter.emit(LOAD, loadEvent);
1853
1854 var finalUrl = loadEvent.url;
1855 var method = loadEvent.method;
1856 var responseType = loadEvent.responseType;
1857 var headers = loadEvent.headers;
1858 var body = loadEvent.body;
1859
1860 if (!loadEvent.nocache) {
1861 // @see https://developer.mozilla.org/nl/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache
1862 finalUrl = finalUrl + ((/\?/).test(finalUrl) ? "&" : "?") + (new Date()).getTime();
1863 }
1864
1865 xhr.onreadystatechange = function() {
1866 if (xhr.readyState !== XMLHttpRequest.DONE) {
1867 return;
1868 }
1869
1870 if (xhr.status === 0) ;
1871 else if (xhr.status === 200) {
1872 var items = xhr.response;
1873
1874 if (responseType === 'document') {
1875 items = tealight(ias.options.item, xhr.response);
1876 // @todo assert there actually are items in the response
1877 }
1878
1879 // we don't use a shared loadedEvent variable here, because these values should be immutable
1880
1881 ias.emitter.emit(LOADED, {items: items, url: finalUrl, xhr: xhr});
1882
1883 resolve({items: items, url: finalUrl, xhr: xhr});
1884 } else {
1885 ias.emitter.emit(ERROR, {url: finalUrl, method: method, xhr: xhr});
1886
1887 reject(xhr);
1888 }
1889 };
1890
1891 xhr.onerror = function() {
1892 ias.emitter.emit(ERROR, {url: finalUrl, method: method, xhr: xhr});
1893
1894 reject(xhr);
1895 };
1896
1897 xhr.open(method, finalUrl, true);
1898 xhr.responseType = responseType;
1899
1900 for (var header in headers) {
1901 xhr.setRequestHeader(header, headers[header]);
1902 }
1903
1904 xhr.send(body);
1905 });
1906 };
1907
1908 /**
1909 * @param {array<Element>} items
1910 * @param {Element|null} parent
1911 */
1912 InfiniteAjaxScroll.prototype.append = function append (items, parent) {
1913 var ias = this;
1914 parent = parent || ias.container;
1915
1916 var event = {
1917 items: items,
1918 parent: parent,
1919 appendFn: appendFn
1920 };
1921
1922 ias.emitter.emit(APPEND, event);
1923
1924 var executor = function (resolve) {
1925 window.requestAnimationFrame(function () {
1926 Promise.resolve(event.appendFn(event.items, event.parent, ias.sentinel())).then(function () {
1927 resolve({items: items, parent: parent});
1928 });
1929 });
1930 };
1931
1932 return (new Promise(executor)).then(function (event) {
1933 ias.emitter.emit(APPENDED, event);
1934 });
1935 };
1936
1937 /**
1938 * @param {array<Element>} items
1939 * @param {Element|null} parent
1940 */
1941 InfiniteAjaxScroll.prototype.prepend = function prepend (items, parent) {
1942 var this$1$1 = this;
1943
1944 var ias = this;
1945 parent = parent || ias.container;
1946
1947 var event = {
1948 items: items,
1949 parent: parent,
1950 prependFn: prependFn
1951 };
1952
1953 ias.emitter.emit(PREPEND, event);
1954
1955 var executor = function (resolve) {
1956 window.requestAnimationFrame(function () {
1957 var first = ias.first();
1958 var scrollPositionStart = getScrollPosition(this$1$1.scrollContainer);
1959 var topStart = first.getBoundingClientRect().top + scrollPositionStart.y;
1960
1961 Promise.resolve(event.prependFn(event.items, event.parent, ias.first()))
1962 .then(function () {
1963 var scrollPositionEnd = getScrollPosition(this$1$1.scrollContainer);
1964 var topEnd = first.getBoundingClientRect().top + scrollPositionEnd.y;
1965
1966 var deltaY = topEnd - topStart;
1967
1968 this$1$1.scrollContainer.scrollTo(scrollPositionEnd.x, deltaY);
1969 })
1970 .then(function () {
1971 resolve({items: items, parent: parent});
1972 });
1973 });
1974 };
1975
1976 return (new Promise(executor)).then(function (event) {
1977 ias.emitter.emit(PREPENDED, event);
1978 });
1979 };
1980
1981 InfiniteAjaxScroll.prototype.sentinel = function sentinel () {
1982 var items = tealight(this.options.item, this.container);
1983
1984 if (!items.length) {
1985 return null;
1986 }
1987
1988 return items[items.length-1];
1989 };
1990
1991 InfiniteAjaxScroll.prototype.first = function first () {
1992 var items = tealight(this.options.item, this.container);
1993
1994 if (!items.length) {
1995 return null;
1996 }
1997
1998 return items[0];
1999 };
2000
2001 InfiniteAjaxScroll.prototype.pause = function pause () {
2002 this.paused = true;
2003 };
2004
2005 InfiniteAjaxScroll.prototype.resume = function resume () {
2006 this.paused = false;
2007 };
2008
2009 InfiniteAjaxScroll.prototype.enableLoadOnScroll = function enableLoadOnScroll () {
2010 this.loadOnScroll = true;
2011 };
2012
2013 InfiniteAjaxScroll.prototype.disableLoadOnScroll = function disableLoadOnScroll () {
2014 this.loadOnScroll = false;
2015 };
2016
2017 /**
2018 * @deprecated replaced by distanceBottom
2019 */
2020 InfiniteAjaxScroll.prototype.distance = function distance (rootRect, sentinel) {
2021 return this.distanceBottom(rootRect, sentinel);
2022 };
2023
2024 InfiniteAjaxScroll.prototype.distanceBottom = function distanceBottom (rootRect, sentinel) {
2025 var _rootRect = rootRect || getRootRect(this.scrollContainer);
2026 var _sentinel = sentinel || this.sentinel();
2027
2028 var scrollPosition = getScrollPosition(this.scrollContainer);
2029
2030 var distance = getDistanceToFold(_sentinel, scrollPosition, _rootRect);
2031
2032 // apply negative margin
2033 distance -= this.negativeMargin;
2034
2035 return distance;
2036 };
2037
2038 InfiniteAjaxScroll.prototype.distanceTop = function distanceTop () {
2039 var scrollPosition = getScrollPosition(this.scrollContainer);
2040
2041 return scrollPosition.y - this.negativeMargin;
2042 };
2043
2044 InfiniteAjaxScroll.prototype.measure = function measure () {
2045 if (this.paused || (this.hitFirst && this.hitLast)) {
2046 return;
2047 }
2048
2049 var rootRect = getRootRect(this.scrollContainer);
2050
2051 // When the scroll container has no height, this could indicate that
2052 // the element is not visible (display = none). Without a height
2053 // we cannot calculate the distance to fold. On the other hand we don't
2054 // have to, because it's not visible anyway. Our resize observer will
2055 // monitor the height, once it's greater than 0 everything will resume as normal.
2056 if (rootRect.height === 0) {
2057 // @todo DX: show warning in console that this is happening
2058 return;
2059 }
2060
2061 if (!this.hitFirst) {
2062 var distanceTop = this.distanceTop();
2063
2064 if (distanceTop <= 0) {
2065 this.emitter.emit(TOP, {distance: distanceTop});
2066 }
2067 }
2068
2069 if (!this.hitLast) {
2070 var distanceBottom = this.distanceBottom(rootRect, this.sentinel());
2071
2072 if (distanceBottom <= 0) {
2073 this.emitter.emit(HIT, {distance: distanceBottom});
2074 }
2075 }
2076 };
2077
2078 InfiniteAjaxScroll.prototype.on = function on (event, callback) {
2079 this.emitter.on(event, callback, this);
2080
2081 if (event === BINDED && this.binded) {
2082 callback.bind(this)();
2083 }
2084 };
2085
2086 InfiniteAjaxScroll.prototype.off = function off (event, callback) {
2087 this.emitter.off(event, callback, this);
2088 };
2089
2090 InfiniteAjaxScroll.prototype.once = function once (event, callback) {
2091 var this$1$1 = this;
2092
2093 return new Promise(function (resolve) {
2094 this$1$1.emitter.once(event, function() { Promise.resolve(callback.apply(this, arguments)).then(resolve); }, this$1$1);
2095
2096 if (event === BINDED && this$1$1.binded) {
2097 callback.bind(this$1$1)();
2098 resolve();
2099 }
2100 })
2101 };
2102
2103 return InfiniteAjaxScroll;
2104
2105}));