j1-template 2021.1.25 → 2021.1.29

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/_includes/themes/j1/layouts/content_generator_article_navigator.html +1 -1
  3. data/_includes/themes/j1/layouts/content_generator_blog_archive.html +6 -4
  4. data/_includes/themes/j1/layouts/content_generator_news_panel_posts.html +1 -1
  5. data/_includes/themes/j1/modules/navigator/generator.html +1 -1
  6. data/_includes/themes/j1/procedures/posts/collate_timeline.proc +1 -1
  7. data/_layouts/default.html +1 -0
  8. data/assets/data/panel.html +1 -15
  9. data/assets/themes/j1/adapter/js/j1.js +6 -6
  10. data/assets/themes/j1/adapter/js/j1scroll.js +304 -0
  11. data/assets/themes/j1/core/css/themes/uno-light/bootstrap.css +42 -0
  12. data/assets/themes/j1/core/css/themes/uno-light/bootstrap.min.css +1 -1
  13. data/assets/themes/j1/core/js/template.js +4 -4
  14. data/assets/themes/j1/core/js/template.js.map +1 -1
  15. data/assets/themes/j1/core/js/template.min.js +4 -4
  16. data/assets/themes/j1/core/js/template.min.js.map +1 -1
  17. data/assets/themes/j1/modules/{infiniteScroll → j1Scroll}/css/theme/uno.css +4 -50
  18. data/assets/themes/j1/modules/j1Scroll/css/theme/uno.min.css +15 -0
  19. data/assets/themes/j1/modules/j1Scroll/js/j1scroll.js +263 -0
  20. data/assets/themes/j1/modules/j1Scroll/js/j1scroll.min.js +16 -0
  21. data/assets/themes/j1/modules/showOnScroll/js/showOnScroll.js +19 -7
  22. data/lib/j1/version.rb +1 -1
  23. data/lib/starter_web/Gemfile +1 -1
  24. data/lib/starter_web/_config.yml +1 -1
  25. data/lib/starter_web/_data/blocks/panel.yml +217 -183
  26. data/lib/starter_web/_data/builder/blog_navigator.yml +1 -0
  27. data/lib/starter_web/_data/layouts/home.yml +20 -16
  28. data/lib/starter_web/_data/modules/defaults/navigator.yml +1 -1
  29. data/lib/starter_web/_data/modules/j1scroll.yml +67 -0
  30. data/lib/starter_web/_data/resources.yml +35 -34
  31. data/lib/starter_web/_includes/attributes.asciidoc +1 -1
  32. data/lib/starter_web/_plugins/lunr_index.rb +1 -1
  33. data/lib/starter_web/collections/posts/public/series/_posts/2020-01-01-post-test-series.adoc +1 -1
  34. data/lib/starter_web/collections/posts/public/series/_posts/2020-01-02-post-test-series.adoc +1 -1
  35. data/lib/starter_web/collections/posts/public/series/_posts/2020-01-03-post-test-series.adoc +1 -1
  36. data/lib/starter_web/collections/posts/public/series/_posts/2020-01-04-post-test-series.adoc +1 -1
  37. data/lib/starter_web/index.html +1 -1
  38. data/lib/starter_web/package.json +1 -1
  39. data/lib/starter_web/pages/public/blog/navigator/index.html +3 -2
  40. data/lib/starter_web/pages/public/features/modules.adoc +104 -0
  41. data/lib/starter_web/pages/public/features/platform.adoc +104 -0
  42. data/lib/starter_web/utilsrv/_defaults/package.json +1 -1
  43. data/lib/starter_web/utilsrv/package.json +1 -1
  44. metadata +10 -9
  45. data/assets/themes/j1/adapter/js/infiniteScroll.js +0 -245
  46. data/assets/themes/j1/modules/infiniteScroll/css/theme/uno.min.css +0 -15
  47. data/assets/themes/j1/modules/infiniteScroll/js/infiniteScroll.js +0 -1909
  48. data/assets/themes/j1/modules/infiniteScroll/js/infiniteScroll.min.js +0 -17
  49. data/lib/starter_web/_data/modules/defaults/infiniteScroll.yml +0 -73
  50. data/lib/starter_web/_data/modules/infiniteScroll.yml +0 -66
@@ -1,1909 +0,0 @@
1
- /*!
2
- * Infinite Scroll PACKAGED v4.0.1
3
- * Automatically add next page
4
- *
5
- * Licensed GPLv3 for open source use
6
- * or Infinite Scroll Commercial License for commercial use
7
- *
8
- * https://infinite-scroll.com
9
- * Copyright 2018-2020 Metafizzy
10
- */
11
-
12
- /**
13
- * Bridget makes jQuery widgets
14
- * v3.0.0
15
- * MIT license
16
- */
17
-
18
- ( function( window, factory ) {
19
- // module definition
20
- if ( typeof module == 'object' && module.exports ) {
21
- // CommonJS
22
- module.exports = factory(
23
- window,
24
- require('jquery'),
25
- );
26
- } else {
27
- // browser global
28
- window.jQueryBridget = factory(
29
- window,
30
- window.jQuery,
31
- );
32
- }
33
-
34
- }( window, function factory( window, jQuery ) {
35
-
36
- // ----- utils ----- //
37
-
38
- // helper function for logging errors
39
- // $.error breaks jQuery chaining
40
- let console = window.console;
41
- let logError = typeof console == 'undefined' ? function() {} :
42
- function( message ) {
43
- console.error( message );
44
- };
45
-
46
- // ----- jQueryBridget ----- //
47
-
48
- function jQueryBridget( namespace, PluginClass, $ ) {
49
- $ = $ || jQuery || window.jQuery;
50
- if ( !$ ) {
51
- return;
52
- }
53
-
54
- // add option method -> $().plugin('option', {...})
55
- if ( !PluginClass.prototype.option ) {
56
- // option setter
57
- PluginClass.prototype.option = function( opts ) {
58
- if ( !opts ) return;
59
-
60
- this.options = Object.assign( this.options || {}, opts );
61
- };
62
- }
63
-
64
- // make jQuery plugin
65
- $.fn[ namespace ] = function( arg0, ...args ) {
66
- if ( typeof arg0 == 'string' ) {
67
- // method call $().plugin( 'methodName', { options } )
68
- return methodCall( this, arg0, args );
69
- }
70
- // just $().plugin({ options })
71
- plainCall( this, arg0 );
72
- return this;
73
- };
74
-
75
- // $().plugin('methodName')
76
- function methodCall( $elems, methodName, args ) {
77
- let returnValue;
78
- let pluginMethodStr = `$().${namespace}("${methodName}")`;
79
-
80
- $elems.each( function( i, elem ) {
81
- // get instance
82
- let instance = $.data( elem, namespace );
83
- if ( !instance ) {
84
- logError( `${namespace} not initialized.` +
85
- ` Cannot call method ${pluginMethodStr}` );
86
- return;
87
- }
88
-
89
- let method = instance[ methodName ];
90
- if ( !method || methodName.charAt( 0 ) == '_' ) {
91
- logError(`${pluginMethodStr} is not a valid method`);
92
- return;
93
- }
94
-
95
- // apply method, get return value
96
- let value = method.apply( instance, args );
97
- // set return value if value is returned, use only first value
98
- returnValue = returnValue === undefined ? value : returnValue;
99
- } );
100
-
101
- return returnValue !== undefined ? returnValue : $elems;
102
- }
103
-
104
- function plainCall( $elems, options ) {
105
- $elems.each( function( i, elem ) {
106
- let instance = $.data( elem, namespace );
107
- if ( instance ) {
108
- // set options & init
109
- instance.option( options );
110
- instance._init();
111
- } else {
112
- // initialize new instance
113
- instance = new PluginClass( elem, options );
114
- $.data( elem, namespace, instance );
115
- }
116
- } );
117
- }
118
-
119
- }
120
-
121
- // ----- ----- //
122
-
123
- return jQueryBridget;
124
-
125
- } ) );
126
- /**
127
- * EvEmitter v2.0.0
128
- * Lil' event emitter
129
- * MIT License
130
- */
131
-
132
- ( function( global, factory ) {
133
- // universal module definition
134
- if ( typeof module == 'object' && module.exports ) {
135
- // CommonJS - Browserify, Webpack
136
- module.exports = factory();
137
- } else {
138
- // Browser globals
139
- global.EvEmitter = factory();
140
- }
141
-
142
- }( typeof window != 'undefined' ? window : this, function() {
143
-
144
- function EvEmitter() {}
145
-
146
- let proto = EvEmitter.prototype;
147
-
148
- proto.on = function( eventName, listener ) {
149
- if ( !eventName || !listener ) return this;
150
-
151
- // set events hash
152
- let events = this._events = this._events || {};
153
- // set listeners array
154
- let listeners = events[ eventName ] = events[ eventName ] || [];
155
- // only add once
156
- if ( !listeners.includes( listener ) ) {
157
- listeners.push( listener );
158
- }
159
-
160
- return this;
161
- };
162
-
163
- proto.once = function( eventName, listener ) {
164
- if ( !eventName || !listener ) return this;
165
-
166
- // add event
167
- this.on( eventName, listener );
168
- // set once flag
169
- // set onceEvents hash
170
- let onceEvents = this._onceEvents = this._onceEvents || {};
171
- // set onceListeners object
172
- let onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
173
- // set flag
174
- onceListeners[ listener ] = true;
175
-
176
- return this;
177
- };
178
-
179
- proto.off = function( eventName, listener ) {
180
- let listeners = this._events && this._events[ eventName ];
181
- if ( !listeners || !listeners.length ) return this;
182
-
183
- let index = listeners.indexOf( listener );
184
- if ( index != -1 ) {
185
- listeners.splice( index, 1 );
186
- }
187
-
188
- return this;
189
- };
190
-
191
- proto.emitEvent = function( eventName, args ) {
192
- let listeners = this._events && this._events[ eventName ];
193
- if ( !listeners || !listeners.length ) return this;
194
-
195
- // copy over to avoid interference if .off() in listener
196
- listeners = listeners.slice( 0 );
197
- args = args || [];
198
- // once stuff
199
- let onceListeners = this._onceEvents && this._onceEvents[ eventName ];
200
-
201
- for ( let listener of listeners ) {
202
- let isOnce = onceListeners && onceListeners[ listener ];
203
- if ( isOnce ) {
204
- // remove listener
205
- // remove before trigger to prevent recursion
206
- this.off( eventName, listener );
207
- // unset once flag
208
- delete onceListeners[ listener ];
209
- }
210
- // trigger listener
211
- listener.apply( this, args );
212
- }
213
-
214
- return this;
215
- };
216
-
217
- proto.allOff = function() {
218
- delete this._events;
219
- delete this._onceEvents;
220
- return this;
221
- };
222
-
223
- return EvEmitter;
224
-
225
- } ) );
226
- /**
227
- * Fizzy UI utils v3.0.0
228
- * MIT license
229
- */
230
-
231
- ( function( global, factory ) {
232
- // universal module definition
233
- if ( typeof module == 'object' && module.exports ) {
234
- // CommonJS
235
- module.exports = factory( global );
236
- } else {
237
- // browser global
238
- global.fizzyUIUtils = factory( global );
239
- }
240
-
241
- }( this, function factory( global ) {
242
-
243
- let utils = {};
244
-
245
- // ----- extend ----- //
246
-
247
- // extends objects
248
- utils.extend = function( a, b ) {
249
- return Object.assign( a, b );
250
- };
251
-
252
- // ----- modulo ----- //
253
-
254
- utils.modulo = function( num, div ) {
255
- return ( ( num % div ) + div ) % div;
256
- };
257
-
258
- // ----- makeArray ----- //
259
-
260
- // turn element or nodeList into an array
261
- utils.makeArray = function( obj ) {
262
- // use object if already an array
263
- if ( Array.isArray( obj ) ) return obj;
264
-
265
- // return empty array if undefined or null. #6
266
- if ( obj === null || obj === undefined ) return [];
267
-
268
- let isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
269
- // convert nodeList to array
270
- if ( isArrayLike ) return [ ...obj ];
271
-
272
- // array of single index
273
- return [ obj ];
274
- };
275
-
276
- // ----- removeFrom ----- //
277
-
278
- utils.removeFrom = function( ary, obj ) {
279
- let index = ary.indexOf( obj );
280
- if ( index != -1 ) {
281
- ary.splice( index, 1 );
282
- }
283
- };
284
-
285
- // ----- getParent ----- //
286
-
287
- utils.getParent = function( elem, selector ) {
288
- while ( elem.parentNode && elem != document.body ) {
289
- elem = elem.parentNode;
290
- if ( elem.matches( selector ) ) return elem;
291
- }
292
- };
293
-
294
- // ----- getQueryElement ----- //
295
-
296
- // use element as selector string
297
- utils.getQueryElement = function( elem ) {
298
- if ( typeof elem == 'string' ) {
299
- return document.querySelector( elem );
300
- }
301
- return elem;
302
- };
303
-
304
- // ----- handleEvent ----- //
305
-
306
- // enable .ontype to trigger from .addEventListener( elem, 'type' )
307
- utils.handleEvent = function( event ) {
308
- let method = 'on' + event.type;
309
- if ( this[ method ] ) {
310
- this[ method ]( event );
311
- }
312
- };
313
-
314
- // ----- filterFindElements ----- //
315
-
316
- utils.filterFindElements = function( elems, selector ) {
317
- // make array of elems
318
- elems = utils.makeArray( elems );
319
-
320
- return elems
321
- // check that elem is an actual element
322
- .filter( ( elem ) => elem instanceof HTMLElement )
323
- .reduce( ( ffElems, elem ) => {
324
- // add elem if no selector
325
- if ( !selector ) {
326
- ffElems.push( elem );
327
- return ffElems;
328
- }
329
- // filter & find items if we have a selector
330
- // filter
331
- if ( elem.matches( selector ) ) {
332
- ffElems.push( elem );
333
- }
334
- // find children
335
- let childElems = elem.querySelectorAll( selector );
336
- // concat childElems to filterFound array
337
- ffElems = ffElems.concat( ...childElems );
338
- return ffElems;
339
- }, [] );
340
- };
341
-
342
- // ----- debounceMethod ----- //
343
-
344
- utils.debounceMethod = function( _class, methodName, threshold ) {
345
- threshold = threshold || 100;
346
- // original method
347
- let method = _class.prototype[ methodName ];
348
- let timeoutName = methodName + 'Timeout';
349
-
350
- _class.prototype[ methodName ] = function() {
351
- clearTimeout( this[ timeoutName ] );
352
-
353
- let args = arguments;
354
- this[ timeoutName ] = setTimeout( () => {
355
- method.apply( this, args );
356
- delete this[ timeoutName ];
357
- }, threshold );
358
- };
359
- };
360
-
361
- // ----- docReady ----- //
362
-
363
- utils.docReady = function( onDocReady ) {
364
- let readyState = document.readyState;
365
- if ( readyState == 'complete' || readyState == 'interactive' ) {
366
- // do async to allow for other scripts to run. metafizzy/flickity#441
367
- setTimeout( onDocReady );
368
- } else {
369
- document.addEventListener( 'DOMContentLoaded', onDocReady );
370
- }
371
- };
372
-
373
- // ----- htmlInit ----- //
374
-
375
- // http://bit.ly/3oYLusc
376
- utils.toDashed = function( str ) {
377
- return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
378
- return $1 + '-' + $2;
379
- } ).toLowerCase();
380
- };
381
-
382
- let console = global.console;
383
-
384
- // allow user to initialize classes via [data-namespace] or .js-namespace class
385
- // htmlInit( Widget, 'widgetName' )
386
- // options are parsed from data-namespace-options
387
- utils.htmlInit = function( WidgetClass, namespace ) {
388
- utils.docReady( function() {
389
- let dashedNamespace = utils.toDashed( namespace );
390
- let dataAttr = 'data-' + dashedNamespace;
391
- let dataAttrElems = document.querySelectorAll( `[${dataAttr}]` );
392
- let jQuery = global.jQuery;
393
-
394
- [ ...dataAttrElems ].forEach( ( elem ) => {
395
- let attr = elem.getAttribute( dataAttr );
396
- let options;
397
- try {
398
- options = attr && JSON.parse( attr );
399
- } catch ( error ) {
400
- // log error, do not initialize
401
- if ( console ) {
402
- console.error( `Error parsing ${dataAttr} on ${elem.className}: ${error}` );
403
- }
404
- return;
405
- }
406
- // initialize
407
- let instance = new WidgetClass( elem, options );
408
- // make available via $().data('namespace')
409
- if ( jQuery ) {
410
- jQuery.data( elem, namespace, instance );
411
- }
412
- } );
413
-
414
- } );
415
- };
416
-
417
- // ----- ----- //
418
-
419
- return utils;
420
-
421
- } ) );
422
- // core
423
- ( function( window, factory ) {
424
- // universal module definition
425
- if ( typeof module == 'object' && module.exports ) {
426
- // CommonJS
427
- module.exports = factory(
428
- window,
429
- require('ev-emitter'),
430
- require('fizzy-ui-utils'),
431
- );
432
- } else {
433
- // browser global
434
- window.InfiniteScroll = factory(
435
- window,
436
- window.EvEmitter,
437
- window.fizzyUIUtils,
438
- );
439
- }
440
-
441
- }( window, function factory( window, EvEmitter, utils ) {
442
-
443
- let jQuery = window.jQuery;
444
- // internal store of all InfiniteScroll intances
445
- let instances = {};
446
-
447
- function InfiniteScroll( element, options ) {
448
- let queryElem = utils.getQueryElement( element );
449
-
450
- if ( !queryElem ) {
451
- console.error( 'Bad element for InfiniteScroll: ' + ( queryElem || element ) );
452
- return;
453
- }
454
- element = queryElem;
455
- // do not initialize twice on same element
456
- if ( element.infiniteScrollGUID ) {
457
- let instance = instances[ element.infiniteScrollGUID ];
458
- instance.option( options );
459
- return instance;
460
- }
461
-
462
- this.element = element;
463
- // options
464
- this.options = { ...InfiniteScroll.defaults };
465
- this.option( options );
466
- // add jQuery
467
- if ( jQuery ) {
468
- this.$element = jQuery( this.element );
469
- }
470
-
471
- this.create();
472
- }
473
-
474
- // defaults
475
- InfiniteScroll.defaults = {
476
- // path: null,
477
- // hideNav: null,
478
- // debug: false,
479
- };
480
-
481
- // create & destroy methods
482
- InfiniteScroll.create = {};
483
- InfiniteScroll.destroy = {};
484
-
485
- let proto = InfiniteScroll.prototype;
486
- // inherit EvEmitter
487
- Object.assign( proto, EvEmitter.prototype );
488
-
489
- // -------------------------- -------------------------- //
490
-
491
- // globally unique identifiers
492
- let GUID = 0;
493
-
494
- proto.create = function() {
495
- // create core
496
- // add id for InfiniteScroll.data
497
- let id = this.guid = ++GUID;
498
- this.element.infiniteScrollGUID = id; // expando
499
- instances[ id ] = this; // associate via id
500
- // properties
501
- this.pageIndex = 1; // default to first page
502
- this.loadCount = 0;
503
- this.updateGetPath();
504
- // bail if getPath not set, or returns falsey #776
505
- let hasPath = this.getPath && this.getPath();
506
- if ( !hasPath ) {
507
- console.error('Disabling InfiniteScroll');
508
- return;
509
- }
510
- this.updateGetAbsolutePath();
511
- this.log( 'initialized', [ this.element.className ] );
512
- this.callOnInit();
513
- // create features
514
- for ( let method in InfiniteScroll.create ) {
515
- InfiniteScroll.create[ method ].call( this );
516
- }
517
- };
518
-
519
- proto.option = function( opts ) {
520
- Object.assign( this.options, opts );
521
- };
522
-
523
- // call onInit option, used for binding events on init
524
- proto.callOnInit = function() {
525
- let onInit = this.options.onInit;
526
- if ( onInit ) {
527
- onInit.call( this, this );
528
- }
529
- };
530
-
531
- // ----- events ----- //
532
-
533
- proto.dispatchEvent = function( type, event, args ) {
534
- this.log( type, args );
535
- let emitArgs = event ? [ event ].concat( args ) : args;
536
- this.emitEvent( type, emitArgs );
537
- // trigger jQuery event
538
- if ( !jQuery || !this.$element ) {
539
- return;
540
- }
541
- // namespace jQuery event
542
- type += '.infiniteScroll';
543
- let $event = type;
544
- if ( event ) {
545
- // create jQuery event
546
- /* eslint-disable-next-line new-cap */
547
- let jQEvent = jQuery.Event( event );
548
- jQEvent.type = type;
549
- $event = jQEvent;
550
- }
551
- this.$element.trigger( $event, args );
552
- };
553
-
554
- let loggers = {
555
- initialized: ( className ) => `on ${className}`,
556
- request: ( path ) => `URL: ${path}`,
557
- load: ( response, path ) => `${response.title || ''}. URL: ${path}`,
558
- error: ( error, path ) => `${error}. URL: ${path}`,
559
- append: ( response, path, items ) => `${items.length} items. URL: ${path}`,
560
- last: ( response, path ) => `URL: ${path}`,
561
- history: ( title, path ) => `URL: ${path}`,
562
- pageIndex: function( index, origin ) {
563
- return `current page determined to be: ${index} from ${origin}`;
564
- },
565
- };
566
-
567
- // log events
568
- proto.log = function( type, args ) {
569
- if ( !this.options.debug ) return;
570
-
571
- let message = `[InfiniteScroll] ${type}`;
572
- let logger = loggers[ type ];
573
- if ( logger ) message += '. ' + logger.apply( this, args );
574
- console.log( message );
575
- };
576
-
577
- // -------------------------- methods used amoung features -------------------------- //
578
-
579
- proto.updateMeasurements = function() {
580
- this.windowHeight = window.innerHeight;
581
- let rect = this.element.getBoundingClientRect();
582
- this.top = rect.top + window.scrollY;
583
- };
584
-
585
- proto.updateScroller = function() {
586
- let elementScroll = this.options.elementScroll;
587
- if ( !elementScroll ) {
588
- // default, use window
589
- this.scroller = window;
590
- return;
591
- }
592
- // if true, set to element, otherwise use option
593
- this.scroller = elementScroll === true ? this.element :
594
- utils.getQueryElement( elementScroll );
595
- if ( !this.scroller ) {
596
- throw new Error(`Unable to find elementScroll: ${elementScroll}`);
597
- }
598
- };
599
-
600
- // -------------------------- page path -------------------------- //
601
-
602
- proto.updateGetPath = function() {
603
- let optPath = this.options.path;
604
- if ( !optPath ) {
605
- console.error(`InfiniteScroll path option required. Set as: ${optPath}`);
606
- return;
607
- }
608
- // function
609
- let type = typeof optPath;
610
- if ( type == 'function' ) {
611
- this.getPath = optPath;
612
- return;
613
- }
614
- // template string: '/pages/{{#}}.html'
615
- let templateMatch = type == 'string' && optPath.match('{{#}}');
616
- if ( templateMatch ) {
617
- this.updateGetPathTemplate( optPath );
618
- return;
619
- }
620
- // selector: '.next-page-selector'
621
- this.updateGetPathSelector( optPath );
622
- };
623
-
624
- proto.updateGetPathTemplate = function( optPath ) {
625
- // set getPath with template string
626
- this.getPath = () => {
627
- let nextIndex = this.pageIndex + 1;
628
- return optPath.replace( '{{#}}', nextIndex );
629
- };
630
- // get pageIndex from location
631
- // convert path option into regex to look for pattern in location
632
- // escape query (?) in url, allows for parsing GET parameters
633
- let regexString = optPath
634
- .replace( /(\\\?|\?)/, '\\?' )
635
- .replace( '{{#}}', '(\\d\\d?\\d?)' );
636
- let templateRe = new RegExp( regexString );
637
- let match = location.href.match( templateRe );
638
-
639
- if ( match ) {
640
- this.pageIndex = parseInt( match[1], 10 );
641
- this.log( 'pageIndex', [ this.pageIndex, 'template string' ] );
642
- }
643
- };
644
-
645
- let pathRegexes = [
646
- // WordPress & Tumblr - example.com/page/2
647
- // Jekyll - example.com/page2
648
- /^(.*?\/?page\/?)(\d\d?\d?)(.*?$)/,
649
- // Drupal - example.com/?page=1
650
- /^(.*?\/?\?page=)(\d\d?\d?)(.*?$)/,
651
- // catch all, last occurence of a number
652
- /(.*?)(\d\d?\d?)(?!.*\d)(.*?$)/,
653
- ];
654
-
655
- // try matching href to pathRegexes patterns
656
- let getPathParts = InfiniteScroll.getPathParts = function( href ) {
657
- if ( !href ) return;
658
- for ( let regex of pathRegexes ) {
659
- let match = href.match( regex );
660
- if ( match ) {
661
- let [ , begin, index, end ] = match;
662
- return { begin, index, end };
663
- }
664
- }
665
- };
666
-
667
- proto.updateGetPathSelector = function( optPath ) {
668
- // parse href of link: '.next-page-link'
669
- let hrefElem = document.querySelector( optPath );
670
- if ( !hrefElem ) {
671
- console.error(`Bad InfiniteScroll path option. Next link not found: ${optPath}`);
672
- return;
673
- }
674
-
675
- let href = hrefElem.getAttribute('href');
676
- let pathParts = getPathParts( href );
677
- if ( !pathParts ) {
678
- console.error(`InfiniteScroll unable to parse next link href: ${href}`);
679
- return;
680
- }
681
-
682
- let { begin, index, end } = pathParts;
683
- this.isPathSelector = true; // flag for checkLastPage()
684
- this.getPath = () => begin + ( this.pageIndex + 1 ) + end;
685
- // get pageIndex from href
686
- this.pageIndex = parseInt( index, 10 ) - 1;
687
- this.log( 'pageIndex', [ this.pageIndex, 'next link' ] );
688
- };
689
-
690
- proto.updateGetAbsolutePath = function() {
691
- let path = this.getPath();
692
- // path doesn't start with http or /
693
- let isAbsolute = path.match( /^http/ ) || path.match( /^\// );
694
- if ( isAbsolute ) {
695
- this.getAbsolutePath = this.getPath;
696
- return;
697
- }
698
-
699
- let { pathname } = location;
700
- // query parameter #829. example.com/?pg=2
701
- let isQuery = path.match( /^\?/ );
702
- // /foo/bar/index.html => /foo/bar
703
- let directory = pathname.substring( 0, pathname.lastIndexOf('/') );
704
- let pathStart = isQuery ? pathname : directory + '/';
705
-
706
- this.getAbsolutePath = () => pathStart + this.getPath();
707
- };
708
-
709
- // -------------------------- nav -------------------------- //
710
-
711
- // hide navigation
712
- InfiniteScroll.create.hideNav = function() {
713
- let nav = utils.getQueryElement( this.options.hideNav );
714
- if ( !nav ) return;
715
-
716
- nav.style.display = 'none';
717
- this.nav = nav;
718
- };
719
-
720
- InfiniteScroll.destroy.hideNav = function() {
721
- if ( this.nav ) this.nav.style.display = '';
722
- };
723
-
724
- // -------------------------- destroy -------------------------- //
725
-
726
- proto.destroy = function() {
727
- this.allOff(); // remove all event listeners
728
- // call destroy methods
729
- for ( let method in InfiniteScroll.destroy ) {
730
- InfiniteScroll.destroy[ method ].call( this );
731
- }
732
-
733
- delete this.element.infiniteScrollGUID;
734
- delete instances[ this.guid ];
735
- // remove jQuery data. #807
736
- if ( jQuery && this.$element ) {
737
- jQuery.removeData( this.element, 'infiniteScroll' );
738
- }
739
- };
740
-
741
- // -------------------------- utilities -------------------------- //
742
-
743
- // https://remysharp.com/2010/07/21/throttling-function-calls
744
- InfiniteScroll.throttle = function( fn, threshold ) {
745
- threshold = threshold || 200;
746
- let last, timeout;
747
-
748
- return function() {
749
- let now = +new Date();
750
- let args = arguments;
751
- let trigger = () => {
752
- last = now;
753
- fn.apply( this, args );
754
- };
755
- if ( last && now < last + threshold ) {
756
- // hold on to it
757
- clearTimeout( timeout );
758
- timeout = setTimeout( trigger, threshold );
759
- } else {
760
- trigger();
761
- }
762
- };
763
- };
764
-
765
- InfiniteScroll.data = function( elem ) {
766
- elem = utils.getQueryElement( elem );
767
- let id = elem && elem.infiniteScrollGUID;
768
- return id && instances[ id ];
769
- };
770
-
771
- // set internal jQuery, for Webpack + jQuery v3
772
- InfiniteScroll.setJQuery = function( jqry ) {
773
- jQuery = jqry;
774
- };
775
-
776
- // -------------------------- setup -------------------------- //
777
-
778
- utils.htmlInit( InfiniteScroll, 'infinite-scroll' );
779
-
780
- // add noop _init method for jQuery Bridget. #768
781
- proto._init = function() {};
782
-
783
- let { jQueryBridget } = window;
784
- if ( jQuery && jQueryBridget ) {
785
- jQueryBridget( 'infiniteScroll', InfiniteScroll, jQuery );
786
- }
787
-
788
- // -------------------------- -------------------------- //
789
-
790
- return InfiniteScroll;
791
-
792
- } ) );
793
- // page-load
794
- ( function( window, factory ) {
795
- // universal module definition
796
- if ( typeof module == 'object' && module.exports ) {
797
- // CommonJS
798
- module.exports = factory(
799
- window,
800
- require('./core'),
801
- );
802
- } else {
803
- // browser global
804
- factory(
805
- window,
806
- window.InfiniteScroll,
807
- );
808
- }
809
-
810
- }( window, function factory( window, InfiniteScroll ) {
811
-
812
- let proto = InfiniteScroll.prototype;
813
-
814
- Object.assign( InfiniteScroll.defaults, {
815
- // append: false,
816
- loadOnScroll: true,
817
- checkLastPage: true,
818
- responseBody: 'text',
819
- domParseResponse: true,
820
- // prefill: false,
821
- // outlayer: null,
822
- } );
823
-
824
- InfiniteScroll.create.pageLoad = function() {
825
- this.canLoad = true;
826
- this.on( 'scrollThreshold', this.onScrollThresholdLoad );
827
- this.on( 'load', this.checkLastPage );
828
- if ( this.options.outlayer ) {
829
- this.on( 'append', this.onAppendOutlayer );
830
- }
831
- };
832
-
833
- proto.onScrollThresholdLoad = function() {
834
- if ( this.options.loadOnScroll ) this.loadNextPage();
835
- };
836
-
837
- let domParser = new DOMParser();
838
-
839
- proto.loadNextPage = function() {
840
- if ( this.isLoading || !this.canLoad ) return;
841
-
842
- let { responseBody, domParseResponse, fetchOptions } = this.options;
843
- let path = this.getAbsolutePath();
844
- this.isLoading = true;
845
- if ( typeof fetchOptions == 'function' ) fetchOptions = fetchOptions();
846
-
847
- let fetchPromise = fetch( path, fetchOptions )
848
- .then( ( response ) => {
849
- if ( !response.ok ) {
850
- let error = new Error( response.statusText );
851
- this.onPageError( error, path, response );
852
- return { response };
853
- }
854
-
855
- return response[ responseBody ]().then( ( body ) => {
856
- let canDomParse = responseBody == 'text' && domParseResponse;
857
- if ( canDomParse ) {
858
- body = domParser.parseFromString( body, 'text/html' );
859
- }
860
- if ( response.status == 204 ) {
861
- this.lastPageReached( body, path );
862
- return { body, response };
863
- } else {
864
- return this.onPageLoad( body, path, response );
865
- }
866
- } );
867
- } )
868
- .catch( ( error ) => {
869
- this.onPageError( error, path );
870
- } );
871
-
872
- this.dispatchEvent( 'request', null, [ path, fetchPromise ] );
873
-
874
- return fetchPromise;
875
- };
876
-
877
- proto.onPageLoad = function( body, path, response ) {
878
- // done loading if not appending
879
- if ( !this.options.append ) {
880
- this.isLoading = false;
881
- }
882
- this.pageIndex++;
883
- this.loadCount++;
884
- this.dispatchEvent( 'load', null, [ body, path, response ] );
885
- return this.appendNextPage( body, path, response );
886
- };
887
-
888
- proto.appendNextPage = function( body, path, response ) {
889
- let { append, responseBody, domParseResponse } = this.options;
890
- // do not append json
891
- let isDocument = responseBody == 'text' && domParseResponse;
892
- if ( !isDocument || !append ) return { body, response };
893
-
894
- let items = body.querySelectorAll( append );
895
- let promiseValue = { body, response, items };
896
- // last page hit if no items. #840
897
- if ( !items || !items.length ) {
898
- this.lastPageReached( body, path );
899
- return promiseValue;
900
- }
901
-
902
- let fragment = getItemsFragment( items );
903
- let appendReady = () => {
904
- this.appendItems( items, fragment );
905
- this.isLoading = false;
906
- this.dispatchEvent( 'append', null, [ body, path, items, response ] );
907
- return promiseValue;
908
- };
909
-
910
- // TODO add hook for option to trigger appendReady
911
- if ( this.options.outlayer ) {
912
- return this.appendOutlayerItems( fragment, appendReady );
913
- } else {
914
- return appendReady();
915
- }
916
- };
917
-
918
- proto.appendItems = function( items, fragment ) {
919
- if ( !items || !items.length ) return;
920
-
921
- // get fragment if not provided
922
- fragment = fragment || getItemsFragment( items );
923
- refreshScripts( fragment );
924
- this.element.appendChild( fragment );
925
- };
926
-
927
- function getItemsFragment( items ) {
928
- // add items to fragment
929
- let fragment = document.createDocumentFragment();
930
- if ( items ) fragment.append( ...items );
931
- return fragment;
932
- }
933
-
934
- // replace <script>s with copies so they load
935
- // <script>s added by InfiniteScroll will not load
936
- // similar to https://stackoverflow.com/questions/610995
937
- function refreshScripts( fragment ) {
938
- let scripts = fragment.querySelectorAll('script');
939
- for ( let script of scripts ) {
940
- let freshScript = document.createElement('script');
941
- // copy attributes
942
- let attrs = script.attributes;
943
- for ( let attr of attrs ) {
944
- freshScript.setAttribute( attr.name, attr.value );
945
- }
946
- // copy inner script code. #718, #782
947
- freshScript.innerHTML = script.innerHTML;
948
- script.parentNode.replaceChild( freshScript, script );
949
- }
950
- }
951
-
952
- // ----- outlayer ----- //
953
-
954
- proto.appendOutlayerItems = function( fragment, appendReady ) {
955
- let imagesLoaded = InfiniteScroll.imagesLoaded || window.imagesLoaded;
956
- if ( !imagesLoaded ) {
957
- console.error('[InfiniteScroll] imagesLoaded required for outlayer option');
958
- this.isLoading = false;
959
- return;
960
- }
961
- // append once images loaded
962
- return new Promise( function( resolve ) {
963
- imagesLoaded( fragment, function() {
964
- let bodyResponse = appendReady();
965
- resolve( bodyResponse );
966
- } );
967
- } );
968
- };
969
-
970
- proto.onAppendOutlayer = function( response, path, items ) {
971
- this.options.outlayer.appended( items );
972
- };
973
-
974
- // ----- checkLastPage ----- //
975
-
976
- // check response for next element
977
- proto.checkLastPage = function( body, path ) {
978
- let { checkLastPage, path: pathOpt } = this.options;
979
- if ( !checkLastPage ) return;
980
-
981
- // if path is function, check if next path is truthy
982
- if ( typeof pathOpt == 'function' ) {
983
- let nextPath = this.getPath();
984
- if ( !nextPath ) {
985
- this.lastPageReached( body, path );
986
- return;
987
- }
988
- }
989
- // get selector from checkLastPage or path option
990
- let selector;
991
- if ( typeof checkLastPage == 'string' ) {
992
- selector = checkLastPage;
993
- } else if ( this.isPathSelector ) {
994
- // path option is selector string
995
- selector = pathOpt;
996
- }
997
- // check last page for selector
998
- // bail if no selector or not document response
999
- if ( !selector || !body.querySelector ) return;
1000
-
1001
- // check if response has selector
1002
- let nextElem = body.querySelector( selector );
1003
- if ( !nextElem ) this.lastPageReached( body, path );
1004
- };
1005
-
1006
- proto.lastPageReached = function( body, path ) {
1007
- this.canLoad = false;
1008
- this.dispatchEvent( 'last', null, [ body, path ] );
1009
- };
1010
-
1011
- // ----- error ----- //
1012
-
1013
- proto.onPageError = function( error, path, response ) {
1014
- this.isLoading = false;
1015
- this.canLoad = false;
1016
- this.dispatchEvent( 'error', null, [ error, path, response ] );
1017
- return error;
1018
- };
1019
-
1020
- // -------------------------- prefill -------------------------- //
1021
-
1022
- InfiniteScroll.create.prefill = function() {
1023
- if ( !this.options.prefill ) return;
1024
-
1025
- let append = this.options.append;
1026
- if ( !append ) {
1027
- console.error(`append option required for prefill. Set as :${append}`);
1028
- return;
1029
- }
1030
- this.updateMeasurements();
1031
- this.updateScroller();
1032
- this.isPrefilling = true;
1033
- this.on( 'append', this.prefill );
1034
- this.once( 'error', this.stopPrefill );
1035
- this.once( 'last', this.stopPrefill );
1036
- this.prefill();
1037
- };
1038
-
1039
- proto.prefill = function() {
1040
- let distance = this.getPrefillDistance();
1041
- this.isPrefilling = distance >= 0;
1042
- if ( this.isPrefilling ) {
1043
- this.log('prefill');
1044
- this.loadNextPage();
1045
- } else {
1046
- this.stopPrefill();
1047
- }
1048
- };
1049
-
1050
- proto.getPrefillDistance = function() {
1051
- // element scroll
1052
- if ( this.options.elementScroll ) {
1053
- return this.scroller.clientHeight - this.scroller.scrollHeight;
1054
- }
1055
- // window
1056
- return this.windowHeight - this.element.clientHeight;
1057
- };
1058
-
1059
- proto.stopPrefill = function() {
1060
- this.log('stopPrefill');
1061
- this.off( 'append', this.prefill );
1062
- };
1063
-
1064
- // -------------------------- -------------------------- //
1065
-
1066
- return InfiniteScroll;
1067
-
1068
- } ) );
1069
- // scroll-watch
1070
- ( function( window, factory ) {
1071
- // universal module definition
1072
- if ( typeof module == 'object' && module.exports ) {
1073
- // CommonJS
1074
- module.exports = factory(
1075
- window,
1076
- require('./core'),
1077
- require('fizzy-ui-utils'),
1078
- );
1079
- } else {
1080
- // browser global
1081
- factory(
1082
- window,
1083
- window.InfiniteScroll,
1084
- window.fizzyUIUtils,
1085
- );
1086
- }
1087
-
1088
- }( window, function factory( window, InfiniteScroll, utils ) {
1089
-
1090
- let proto = InfiniteScroll.prototype;
1091
-
1092
- // default options
1093
- Object.assign( InfiniteScroll.defaults, {
1094
- scrollThreshold: 400,
1095
- // elementScroll: null,
1096
- } );
1097
-
1098
- InfiniteScroll.create.scrollWatch = function() {
1099
- // events
1100
- this.pageScrollHandler = this.onPageScroll.bind( this );
1101
- this.resizeHandler = this.onResize.bind( this );
1102
-
1103
- let scrollThreshold = this.options.scrollThreshold;
1104
- let isEnable = scrollThreshold || scrollThreshold === 0;
1105
- if ( isEnable ) this.enableScrollWatch();
1106
- };
1107
-
1108
- InfiniteScroll.destroy.scrollWatch = function() {
1109
- this.disableScrollWatch();
1110
- };
1111
-
1112
- proto.enableScrollWatch = function() {
1113
- if ( this.isScrollWatching ) return;
1114
-
1115
- this.isScrollWatching = true;
1116
- this.updateMeasurements();
1117
- this.updateScroller();
1118
- // TODO disable after error?
1119
- this.on( 'last', this.disableScrollWatch );
1120
- this.bindScrollWatchEvents( true );
1121
- };
1122
-
1123
- proto.disableScrollWatch = function() {
1124
- if ( !this.isScrollWatching ) return;
1125
-
1126
- this.bindScrollWatchEvents( false );
1127
- delete this.isScrollWatching;
1128
- };
1129
-
1130
- proto.bindScrollWatchEvents = function( isBind ) {
1131
- let addRemove = isBind ? 'addEventListener' : 'removeEventListener';
1132
- this.scroller[ addRemove ]( 'scroll', this.pageScrollHandler );
1133
- window[ addRemove ]( 'resize', this.resizeHandler );
1134
- };
1135
-
1136
- proto.onPageScroll = InfiniteScroll.throttle( function() {
1137
- let distance = this.getBottomDistance();
1138
- if ( distance <= this.options.scrollThreshold ) {
1139
- this.dispatchEvent('scrollThreshold');
1140
- }
1141
- } );
1142
-
1143
- proto.getBottomDistance = function() {
1144
- let bottom, scrollY;
1145
- if ( this.options.elementScroll ) {
1146
- bottom = this.scroller.scrollHeight;
1147
- scrollY = this.scroller.scrollTop + this.scroller.clientHeight;
1148
- } else {
1149
- bottom = this.top + this.element.clientHeight;
1150
- scrollY = window.scrollY + this.windowHeight;
1151
- }
1152
- return bottom - scrollY;
1153
- };
1154
-
1155
- proto.onResize = function() {
1156
- this.updateMeasurements();
1157
- };
1158
-
1159
- utils.debounceMethod( InfiniteScroll, 'onResize', 150 );
1160
-
1161
- // -------------------------- -------------------------- //
1162
-
1163
- return InfiniteScroll;
1164
-
1165
- } ) );
1166
- // history
1167
- ( function( window, factory ) {
1168
- // universal module definition
1169
- if ( typeof module == 'object' && module.exports ) {
1170
- // CommonJS
1171
- module.exports = factory(
1172
- window,
1173
- require('./core'),
1174
- require('fizzy-ui-utils'),
1175
- );
1176
- } else {
1177
- // browser global
1178
- factory(
1179
- window,
1180
- window.InfiniteScroll,
1181
- window.fizzyUIUtils,
1182
- );
1183
- }
1184
-
1185
- }( window, function factory( window, InfiniteScroll, utils ) {
1186
-
1187
- let proto = InfiniteScroll.prototype;
1188
-
1189
- Object.assign( InfiniteScroll.defaults, {
1190
- history: 'replace',
1191
- // historyTitle: false,
1192
- } );
1193
-
1194
- let link = document.createElement('a');
1195
-
1196
- // ----- create/destroy ----- //
1197
-
1198
- InfiniteScroll.create.history = function() {
1199
- if ( !this.options.history ) return;
1200
-
1201
- // check for same origin
1202
- link.href = this.getAbsolutePath();
1203
- // MS Edge does not have origin on link
1204
- // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12236493/
1205
- let linkOrigin = link.origin || link.protocol + '//' + link.host;
1206
- let isSameOrigin = linkOrigin == location.origin;
1207
- if ( !isSameOrigin ) {
1208
- console.error( '[InfiniteScroll] cannot set history with different origin: ' +
1209
- `${link.origin} on ${location.origin} . History behavior disabled.` );
1210
- return;
1211
- }
1212
-
1213
- // two ways to handle changing history
1214
- if ( this.options.append ) {
1215
- this.createHistoryAppend();
1216
- } else {
1217
- this.createHistoryPageLoad();
1218
- }
1219
- };
1220
-
1221
- proto.createHistoryAppend = function() {
1222
- this.updateMeasurements();
1223
- this.updateScroller();
1224
- // array of scroll positions of appended pages
1225
- this.scrollPages = [
1226
- // first page
1227
- {
1228
- top: 0,
1229
- path: location.href,
1230
- title: document.title,
1231
- },
1232
- ];
1233
- this.scrollPage = this.scrollPages[0];
1234
- // events
1235
- this.scrollHistoryHandler = this.onScrollHistory.bind( this );
1236
- this.unloadHandler = this.onUnload.bind( this );
1237
- this.scroller.addEventListener( 'scroll', this.scrollHistoryHandler );
1238
- this.on( 'append', this.onAppendHistory );
1239
- this.bindHistoryAppendEvents( true );
1240
- };
1241
-
1242
- proto.bindHistoryAppendEvents = function( isBind ) {
1243
- let addRemove = isBind ? 'addEventListener' : 'removeEventListener';
1244
- this.scroller[ addRemove ]( 'scroll', this.scrollHistoryHandler );
1245
- window[ addRemove ]( 'unload', this.unloadHandler );
1246
- };
1247
-
1248
- proto.createHistoryPageLoad = function() {
1249
- this.on( 'load', this.onPageLoadHistory );
1250
- };
1251
-
1252
- InfiniteScroll.destroy.history =
1253
- proto.destroyHistory = function() {
1254
- let isHistoryAppend = this.options.history && this.options.append;
1255
- if ( isHistoryAppend ) {
1256
- this.bindHistoryAppendEvents( false );
1257
- }
1258
- };
1259
-
1260
- // ----- append history ----- //
1261
-
1262
- proto.onAppendHistory = function( response, path, items ) {
1263
- // do not proceed if no items. #779
1264
- if ( !items || !items.length ) return;
1265
-
1266
- let firstItem = items[0];
1267
- let elemScrollY = this.getElementScrollY( firstItem );
1268
- // resolve path
1269
- link.href = path;
1270
- // add page data to hash
1271
- this.scrollPages.push({
1272
- top: elemScrollY,
1273
- path: link.href,
1274
- title: response.title,
1275
- });
1276
- };
1277
-
1278
- proto.getElementScrollY = function( elem ) {
1279
- if ( this.options.elementScroll ) {
1280
- return elem.offsetTop - this.top;
1281
- } else {
1282
- let rect = elem.getBoundingClientRect();
1283
- return rect.top + window.scrollY;
1284
- }
1285
- };
1286
-
1287
- proto.onScrollHistory = function() {
1288
- // cycle through positions, find biggest without going over
1289
- let scrollPage = this.getClosestScrollPage();
1290
- // set history if changed
1291
- if ( scrollPage != this.scrollPage ) {
1292
- this.scrollPage = scrollPage;
1293
- this.setHistory( scrollPage.title, scrollPage.path );
1294
- }
1295
- };
1296
-
1297
- utils.debounceMethod( InfiniteScroll, 'onScrollHistory', 150 );
1298
-
1299
- proto.getClosestScrollPage = function() {
1300
- let scrollViewY;
1301
- if ( this.options.elementScroll ) {
1302
- scrollViewY = this.scroller.scrollTop + this.scroller.clientHeight / 2;
1303
- } else {
1304
- scrollViewY = window.scrollY + this.windowHeight / 2;
1305
- }
1306
-
1307
- let scrollPage;
1308
- for ( let page of this.scrollPages ) {
1309
- if ( page.top >= scrollViewY ) break;
1310
-
1311
- scrollPage = page;
1312
- }
1313
- return scrollPage;
1314
- };
1315
-
1316
- proto.setHistory = function( title, path ) {
1317
- let optHistory = this.options.history;
1318
- let historyMethod = optHistory && history[ optHistory + 'State' ];
1319
- if ( !historyMethod ) return;
1320
-
1321
- history[ optHistory + 'State' ]( null, title, path );
1322
- if ( this.options.historyTitle ) document.title = title;
1323
- this.dispatchEvent( 'history', null, [ title, path ] );
1324
- };
1325
-
1326
- // scroll to top to prevent initial scroll-reset after page refresh
1327
- // https://stackoverflow.com/a/18633915/182183
1328
- proto.onUnload = function() {
1329
- if ( this.scrollPage.top === 0 ) return;
1330
-
1331
- // calculate where scroll position would be on refresh
1332
- let scrollY = window.scrollY - this.scrollPage.top + this.top;
1333
- // disable scroll event before setting scroll #679
1334
- this.destroyHistory();
1335
- scrollTo( 0, scrollY );
1336
- };
1337
-
1338
- // ----- load history ----- //
1339
-
1340
- // update URL
1341
- proto.onPageLoadHistory = function( response, path ) {
1342
- this.setHistory( response.title, path );
1343
- };
1344
-
1345
- // -------------------------- -------------------------- //
1346
-
1347
- return InfiniteScroll;
1348
-
1349
- } ) );
1350
- // button
1351
- ( function( window, factory ) {
1352
- // universal module definition
1353
- if ( typeof module == 'object' && module.exports ) {
1354
- // CommonJS
1355
- module.exports = factory(
1356
- window,
1357
- require('./core'),
1358
- require('fizzy-ui-utils'),
1359
- );
1360
- } else {
1361
- // browser global
1362
- factory(
1363
- window,
1364
- window.InfiniteScroll,
1365
- window.fizzyUIUtils,
1366
- );
1367
- }
1368
-
1369
- }( window, function factory( window, InfiniteScroll, utils ) {
1370
-
1371
- // -------------------------- InfiniteScrollButton -------------------------- //
1372
-
1373
- class InfiniteScrollButton {
1374
- constructor( element, infScroll ) {
1375
- this.element = element;
1376
- this.infScroll = infScroll;
1377
- // events
1378
- this.clickHandler = this.onClick.bind( this );
1379
- this.element.addEventListener( 'click', this.clickHandler );
1380
- infScroll.on( 'request', this.disable.bind( this ) );
1381
- infScroll.on( 'load', this.enable.bind( this ) );
1382
- infScroll.on( 'error', this.hide.bind( this ) );
1383
- infScroll.on( 'last', this.hide.bind( this ) );
1384
- }
1385
-
1386
- onClick( event ) {
1387
- event.preventDefault();
1388
- this.infScroll.loadNextPage();
1389
- }
1390
-
1391
- enable() {
1392
- this.element.removeAttribute('disabled');
1393
- }
1394
-
1395
- disable() {
1396
- this.element.disabled = 'disabled';
1397
- }
1398
-
1399
- hide() {
1400
- this.element.style.display = 'none';
1401
- }
1402
-
1403
- destroy() {
1404
- this.element.removeEventListener( 'click', this.clickHandler );
1405
- }
1406
-
1407
- }
1408
-
1409
- // -------------------------- InfiniteScroll methods -------------------------- //
1410
-
1411
- // InfiniteScroll.defaults.button = null;
1412
-
1413
- InfiniteScroll.create.button = function() {
1414
- let buttonElem = utils.getQueryElement( this.options.button );
1415
- if ( buttonElem ) {
1416
- this.button = new InfiniteScrollButton( buttonElem, this );
1417
- }
1418
- };
1419
-
1420
- InfiniteScroll.destroy.button = function() {
1421
- if ( this.button ) this.button.destroy();
1422
- };
1423
-
1424
- // -------------------------- -------------------------- //
1425
-
1426
- InfiniteScroll.Button = InfiniteScrollButton;
1427
-
1428
- return InfiniteScroll;
1429
-
1430
- } ) );
1431
- // status
1432
- ( function( window, factory ) {
1433
- // universal module definition
1434
- if ( typeof module == 'object' && module.exports ) {
1435
- // CommonJS
1436
- module.exports = factory(
1437
- window,
1438
- require('./core'),
1439
- require('fizzy-ui-utils'),
1440
- );
1441
- } else {
1442
- // browser global
1443
- factory(
1444
- window,
1445
- window.InfiniteScroll,
1446
- window.fizzyUIUtils,
1447
- );
1448
- }
1449
-
1450
- }( window, function factory( window, InfiniteScroll, utils ) {
1451
-
1452
- let proto = InfiniteScroll.prototype;
1453
-
1454
- // InfiniteScroll.defaults.status = null;
1455
-
1456
- InfiniteScroll.create.status = function() {
1457
- let statusElem = utils.getQueryElement( this.options.status );
1458
- if ( !statusElem ) return;
1459
-
1460
- // elements
1461
- this.statusElement = statusElem;
1462
- this.statusEventElements = {
1463
- request: statusElem.querySelector('.infinite-scroll-request'),
1464
- error: statusElem.querySelector('.infinite-scroll-error'),
1465
- last: statusElem.querySelector('.infinite-scroll-last'),
1466
- };
1467
- // events
1468
- this.on( 'request', this.showRequestStatus );
1469
- this.on( 'error', this.showErrorStatus );
1470
- this.on( 'last', this.showLastStatus );
1471
- this.bindHideStatus('on');
1472
- };
1473
-
1474
- proto.bindHideStatus = function( bindMethod ) {
1475
- let hideEvent = this.options.append ? 'append' : 'load';
1476
- this[ bindMethod ]( hideEvent, this.hideAllStatus );
1477
- };
1478
-
1479
- proto.showRequestStatus = function() {
1480
- this.showStatus('request');
1481
- };
1482
-
1483
- proto.showErrorStatus = function() {
1484
- this.showStatus('error');
1485
- };
1486
-
1487
- proto.showLastStatus = function() {
1488
- this.showStatus('last');
1489
- // prevent last then append event race condition from showing last status #706
1490
- this.bindHideStatus('off');
1491
- };
1492
-
1493
- proto.showStatus = function( eventName ) {
1494
- show( this.statusElement );
1495
- this.hideStatusEventElements();
1496
- let eventElem = this.statusEventElements[ eventName ];
1497
- show( eventElem );
1498
- };
1499
-
1500
- proto.hideAllStatus = function() {
1501
- hide( this.statusElement );
1502
- this.hideStatusEventElements();
1503
- };
1504
-
1505
- proto.hideStatusEventElements = function() {
1506
- for ( let type in this.statusEventElements ) {
1507
- let eventElem = this.statusEventElements[ type ];
1508
- hide( eventElem );
1509
- }
1510
- };
1511
-
1512
- // -------------------------- -------------------------- //
1513
-
1514
- function hide( elem ) {
1515
- setDisplay( elem, 'none' );
1516
- }
1517
-
1518
- function show( elem ) {
1519
- setDisplay( elem, 'block' );
1520
- }
1521
-
1522
- function setDisplay( elem, value ) {
1523
- if ( elem ) {
1524
- elem.style.display = value;
1525
- }
1526
- }
1527
-
1528
- // -------------------------- -------------------------- //
1529
-
1530
- return InfiniteScroll;
1531
-
1532
- } ) );
1533
- /*!
1534
- * imagesLoaded v4.1.4
1535
- * JavaScript is all like "You images are done yet or what?"
1536
- * MIT License
1537
- */
1538
-
1539
- ( function( window, factory ) { 'use strict';
1540
- // universal module definition
1541
-
1542
- /*global define: false, module: false, require: false */
1543
-
1544
- if ( typeof define == 'function' && define.amd ) {
1545
- // AMD
1546
- define( [
1547
- 'ev-emitter/ev-emitter'
1548
- ], function( EvEmitter ) {
1549
- return factory( window, EvEmitter );
1550
- });
1551
- } else if ( typeof module == 'object' && module.exports ) {
1552
- // CommonJS
1553
- module.exports = factory(
1554
- window,
1555
- require('ev-emitter')
1556
- );
1557
- } else {
1558
- // browser global
1559
- window.imagesLoaded = factory(
1560
- window,
1561
- window.EvEmitter
1562
- );
1563
- }
1564
-
1565
- })( typeof window !== 'undefined' ? window : this,
1566
-
1567
- // -------------------------- factory -------------------------- //
1568
-
1569
- function factory( window, EvEmitter ) {
1570
-
1571
- 'use strict';
1572
-
1573
- var $ = window.jQuery;
1574
- var console = window.console;
1575
-
1576
- // -------------------------- helpers -------------------------- //
1577
-
1578
- // extend objects
1579
- function extend( a, b ) {
1580
- for ( var prop in b ) {
1581
- a[ prop ] = b[ prop ];
1582
- }
1583
- return a;
1584
- }
1585
-
1586
- var arraySlice = Array.prototype.slice;
1587
-
1588
- // turn element or nodeList into an array
1589
- function makeArray( obj ) {
1590
- if ( Array.isArray( obj ) ) {
1591
- // use object if already an array
1592
- return obj;
1593
- }
1594
-
1595
- var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
1596
- if ( isArrayLike ) {
1597
- // convert nodeList to array
1598
- return arraySlice.call( obj );
1599
- }
1600
-
1601
- // array of single index
1602
- return [ obj ];
1603
- }
1604
-
1605
- // -------------------------- imagesLoaded -------------------------- //
1606
-
1607
- /**
1608
- * @param {Array, Element, NodeList, String} elem
1609
- * @param {Object or Function} options - if function, use as callback
1610
- * @param {Function} onAlways - callback function
1611
- */
1612
- function ImagesLoaded( elem, options, onAlways ) {
1613
- // coerce ImagesLoaded() without new, to be new ImagesLoaded()
1614
- if ( !( this instanceof ImagesLoaded ) ) {
1615
- return new ImagesLoaded( elem, options, onAlways );
1616
- }
1617
- // use elem as selector string
1618
- var queryElem = elem;
1619
- if ( typeof elem == 'string' ) {
1620
- queryElem = document.querySelectorAll( elem );
1621
- }
1622
- // bail if bad element
1623
- if ( !queryElem ) {
1624
- console.error( 'Bad element for imagesLoaded ' + ( queryElem || elem ) );
1625
- return;
1626
- }
1627
-
1628
- this.elements = makeArray( queryElem );
1629
- this.options = extend( {}, this.options );
1630
- // shift arguments if no options set
1631
- if ( typeof options == 'function' ) {
1632
- onAlways = options;
1633
- } else {
1634
- extend( this.options, options );
1635
- }
1636
-
1637
- if ( onAlways ) {
1638
- this.on( 'always', onAlways );
1639
- }
1640
-
1641
- this.getImages();
1642
-
1643
- if ( $ ) {
1644
- // add jQuery Deferred object
1645
- this.jqDeferred = new $.Deferred();
1646
- }
1647
-
1648
- // HACK check async to allow time to bind listeners
1649
- setTimeout( this.check.bind( this ) );
1650
- }
1651
-
1652
- ImagesLoaded.prototype = Object.create( EvEmitter.prototype );
1653
-
1654
- ImagesLoaded.prototype.options = {};
1655
-
1656
- ImagesLoaded.prototype.getImages = function() {
1657
- this.images = [];
1658
-
1659
- // filter & find items if we have an item selector
1660
- this.elements.forEach( this.addElementImages, this );
1661
- };
1662
-
1663
- /**
1664
- * @param {Node} element
1665
- */
1666
- ImagesLoaded.prototype.addElementImages = function( elem ) {
1667
- // filter siblings
1668
- if ( elem.nodeName == 'IMG' ) {
1669
- this.addImage( elem );
1670
- }
1671
- // get background image on element
1672
- if ( this.options.background === true ) {
1673
- this.addElementBackgroundImages( elem );
1674
- }
1675
-
1676
- // find children
1677
- // no non-element nodes, #143
1678
- var nodeType = elem.nodeType;
1679
- if ( !nodeType || !elementNodeTypes[ nodeType ] ) {
1680
- return;
1681
- }
1682
- var childImgs = elem.querySelectorAll('img');
1683
- // concat childElems to filterFound array
1684
- for ( var i=0; i < childImgs.length; i++ ) {
1685
- var img = childImgs[i];
1686
- this.addImage( img );
1687
- }
1688
-
1689
- // get child background images
1690
- if ( typeof this.options.background == 'string' ) {
1691
- var children = elem.querySelectorAll( this.options.background );
1692
- for ( i=0; i < children.length; i++ ) {
1693
- var child = children[i];
1694
- this.addElementBackgroundImages( child );
1695
- }
1696
- }
1697
- };
1698
-
1699
- var elementNodeTypes = {
1700
- 1: true,
1701
- 9: true,
1702
- 11: true
1703
- };
1704
-
1705
- ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) {
1706
- var style = getComputedStyle( elem );
1707
- if ( !style ) {
1708
- // Firefox returns null if in a hidden iframe https://bugzil.la/548397
1709
- return;
1710
- }
1711
- // get url inside url("...")
1712
- var reURL = /url\((['"])?(.*?)\1\)/gi;
1713
- var matches = reURL.exec( style.backgroundImage );
1714
- while ( matches !== null ) {
1715
- var url = matches && matches[2];
1716
- if ( url ) {
1717
- this.addBackground( url, elem );
1718
- }
1719
- matches = reURL.exec( style.backgroundImage );
1720
- }
1721
- };
1722
-
1723
- /**
1724
- * @param {Image} img
1725
- */
1726
- ImagesLoaded.prototype.addImage = function( img ) {
1727
- var loadingImage = new LoadingImage( img );
1728
- this.images.push( loadingImage );
1729
- };
1730
-
1731
- ImagesLoaded.prototype.addBackground = function( url, elem ) {
1732
- var background = new Background( url, elem );
1733
- this.images.push( background );
1734
- };
1735
-
1736
- ImagesLoaded.prototype.check = function() {
1737
- var _this = this;
1738
- this.progressedCount = 0;
1739
- this.hasAnyBroken = false;
1740
- // complete if no images
1741
- if ( !this.images.length ) {
1742
- this.complete();
1743
- return;
1744
- }
1745
-
1746
- function onProgress( image, elem, message ) {
1747
- // HACK - Chrome triggers event before object properties have changed. #83
1748
- setTimeout( function() {
1749
- _this.progress( image, elem, message );
1750
- });
1751
- }
1752
-
1753
- this.images.forEach( function( loadingImage ) {
1754
- loadingImage.once( 'progress', onProgress );
1755
- loadingImage.check();
1756
- });
1757
- };
1758
-
1759
- ImagesLoaded.prototype.progress = function( image, elem, message ) {
1760
- this.progressedCount++;
1761
- this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
1762
- // progress event
1763
- this.emitEvent( 'progress', [ this, image, elem ] );
1764
- if ( this.jqDeferred && this.jqDeferred.notify ) {
1765
- this.jqDeferred.notify( this, image );
1766
- }
1767
- // check if completed
1768
- if ( this.progressedCount == this.images.length ) {
1769
- this.complete();
1770
- }
1771
-
1772
- if ( this.options.debug && console ) {
1773
- console.log( 'progress: ' + message, image, elem );
1774
- }
1775
- };
1776
-
1777
- ImagesLoaded.prototype.complete = function() {
1778
- var eventName = this.hasAnyBroken ? 'fail' : 'done';
1779
- this.isComplete = true;
1780
- this.emitEvent( eventName, [ this ] );
1781
- this.emitEvent( 'always', [ this ] );
1782
- if ( this.jqDeferred ) {
1783
- var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve';
1784
- this.jqDeferred[ jqMethod ]( this );
1785
- }
1786
- };
1787
-
1788
- // -------------------------- -------------------------- //
1789
-
1790
- function LoadingImage( img ) {
1791
- this.img = img;
1792
- }
1793
-
1794
- LoadingImage.prototype = Object.create( EvEmitter.prototype );
1795
-
1796
- LoadingImage.prototype.check = function() {
1797
- // If complete is true and browser supports natural sizes,
1798
- // try to check for image status manually.
1799
- var isComplete = this.getIsImageComplete();
1800
- if ( isComplete ) {
1801
- // report based on naturalWidth
1802
- this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
1803
- return;
1804
- }
1805
-
1806
- // If none of the checks above matched, simulate loading on detached element.
1807
- this.proxyImage = new Image();
1808
- this.proxyImage.addEventListener( 'load', this );
1809
- this.proxyImage.addEventListener( 'error', this );
1810
- // bind to image as well for Firefox. #191
1811
- this.img.addEventListener( 'load', this );
1812
- this.img.addEventListener( 'error', this );
1813
- this.proxyImage.src = this.img.src;
1814
- };
1815
-
1816
- LoadingImage.prototype.getIsImageComplete = function() {
1817
- // check for non-zero, non-undefined naturalWidth
1818
- // fixes Safari+InfiniteScroll+Masonry bug infinite-scroll#671
1819
- return this.img.complete && this.img.naturalWidth;
1820
- };
1821
-
1822
- LoadingImage.prototype.confirm = function( isLoaded, message ) {
1823
- this.isLoaded = isLoaded;
1824
- this.emitEvent( 'progress', [ this, this.img, message ] );
1825
- };
1826
-
1827
- // ----- events ----- //
1828
-
1829
- // trigger specified handler for event type
1830
- LoadingImage.prototype.handleEvent = function( event ) {
1831
- var method = 'on' + event.type;
1832
- if ( this[ method ] ) {
1833
- this[ method ]( event );
1834
- }
1835
- };
1836
-
1837
- LoadingImage.prototype.onload = function() {
1838
- this.confirm( true, 'onload' );
1839
- this.unbindEvents();
1840
- };
1841
-
1842
- LoadingImage.prototype.onerror = function() {
1843
- this.confirm( false, 'onerror' );
1844
- this.unbindEvents();
1845
- };
1846
-
1847
- LoadingImage.prototype.unbindEvents = function() {
1848
- this.proxyImage.removeEventListener( 'load', this );
1849
- this.proxyImage.removeEventListener( 'error', this );
1850
- this.img.removeEventListener( 'load', this );
1851
- this.img.removeEventListener( 'error', this );
1852
- };
1853
-
1854
- // -------------------------- Background -------------------------- //
1855
-
1856
- function Background( url, element ) {
1857
- this.url = url;
1858
- this.element = element;
1859
- this.img = new Image();
1860
- }
1861
-
1862
- // inherit LoadingImage prototype
1863
- Background.prototype = Object.create( LoadingImage.prototype );
1864
-
1865
- Background.prototype.check = function() {
1866
- this.img.addEventListener( 'load', this );
1867
- this.img.addEventListener( 'error', this );
1868
- this.img.src = this.url;
1869
- // check if image is already complete
1870
- var isComplete = this.getIsImageComplete();
1871
- if ( isComplete ) {
1872
- this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
1873
- this.unbindEvents();
1874
- }
1875
- };
1876
-
1877
- Background.prototype.unbindEvents = function() {
1878
- this.img.removeEventListener( 'load', this );
1879
- this.img.removeEventListener( 'error', this );
1880
- };
1881
-
1882
- Background.prototype.confirm = function( isLoaded, message ) {
1883
- this.isLoaded = isLoaded;
1884
- this.emitEvent( 'progress', [ this, this.element, message ] );
1885
- };
1886
-
1887
- // -------------------------- jQuery -------------------------- //
1888
-
1889
- ImagesLoaded.makeJQueryPlugin = function( jQuery ) {
1890
- jQuery = jQuery || window.jQuery;
1891
- if ( !jQuery ) {
1892
- return;
1893
- }
1894
- // set local variable
1895
- $ = jQuery;
1896
- // $().imagesLoaded()
1897
- $.fn.imagesLoaded = function( options, callback ) {
1898
- var instance = new ImagesLoaded( this, options, callback );
1899
- return instance.jqDeferred.promise( $(this) );
1900
- };
1901
- };
1902
- // try making plugin
1903
- ImagesLoaded.makeJQueryPlugin();
1904
-
1905
- // -------------------------- -------------------------- //
1906
-
1907
- return ImagesLoaded;
1908
-
1909
- });