PageSourceSearch

https://strategysystems.com/wp-content/mu-plugins/fx-load-more/src/app.js?ver=1782312627

js strategysystems.com collected 2026-10-08 01:53:46 UTC 6,353 bytes, 247 lines download raw bytes

1var FXLM = ( function( FXLM ) {
2
3
4	// making instances globally accessible in case other plugins/themes need to manipulate
5	FXLM.instances = []
6
7
8	window.addEventListener( 'load', () => {
9		const containers = document.querySelectorAll('.js-load-more-block')
10
11		for( const container of containers ) {
12			const postType 	= container.dataset.loadMorePostType,
13				posts 		= container.querySelector('.js-load-more-posts'),
14				widget 		= container.querySelector('.js-load-more')
15
16			if( null !== widget && null !== posts ) {
17				const instance = new FxLoadMore( container, widget, posts, postType )
18
19				FXLM.instances.push( instance )
20			}
21		}
22	})
23
24
25
26	class FxLoadMore {
27
28		constructor( elContainer, elWidget, elPosts, postType = 'post' ) {
29	
30			// elements
31			this.container 	= elContainer
32			this.widget 	= elWidget
33			this.posts 		= elPosts
34			this.counter	= this.widget.querySelector('.js-load-more-counter')
35			this.progress 	= this.widget.querySelector('.js-load-more-progress')
36			this.btn 		= this.widget.querySelector('.js-load-more-btn')
37	
38			// states
39			this.isFetching		= false
40			this.isShowingAll 	= false
41	
42			// data
43			this.postType 			= postType
44			this.postsPerPage 		= parseInt( FXLM.posts_per_page )
45			this.postTaxonomy 		= null
46			this.postTermId 		= null
47
48			// check container for data attribute for total posts
49			if( undefined !== this.container.dataset.loadMoreTotal ) {
50				this.totalPostCount = this.container.dataset.loadMoreTotal
51
52			// otherwise, check for value passed through wp_localize_script
53			} else {
54				this.totalPostCount = parseInt( FXLM.post_count )
55			}
56
57			// check container for data attribute for current page
58			if( undefined !== this.container.dataset.loadMoreCurrentPage ) {
59				this.currentPage = this.container.dataset.loadMoreCurrentPage
60			} else {
61				this.currentPage = 1
62			}
63
64			// check if for taxonomy
65			if( undefined !== FXLM.post_taxonomy && undefined !== FXLM.post_term_id ) {
66				this.postTaxonomy 	= FXLM.post_taxonomy
67				this.postTermId 	= FXLM.post_term_id
68			}
69
70			// check for search query
71			if( undefined !== this.container.dataset.loadMoreSearchQuery ) {
72				this.searchQuery = this.container.dataset.loadMoreSearchQuery
73			}
74
75			this.currentPostCount = ( this.totalPostCount < this.postsPerPage ) ? this.totalPostCount : this.postsPerPage
76	
77			this.init()
78		}
79	
80	
81		init() {
82			this.progress.max = this.totalPostCount
83	
84			this.updateWidget()
85			this.bind()
86		}
87	
88	
89		bind() {
90			this.btn.addEventListener( 'click', this.handleBtnClick.bind( this ) )
91		}
92	
93	
94		updateWidget() {
95			this.progress.value 	= this.currentPostCount
96			this.counter.innerText 	= `Viewing ${this.currentPostCount} of ${this.totalPostCount}`
97	
98			// have we loaded all posts?
99			if( this.currentPostCount >= this.totalPostCount ) {
100				this.btn.remove()
101
102				// prevents additional Ajax requests
103				this.isShowingAll = true
104			}
105		}
106	
107	
108		async handleBtnClick() {
109			const self = this
110	
111			// avoid duplicate clicks or clicks when all posts are showing
112			if( self.isFetching || self.isShowingAll ) {
113				return
114			}
115
116			// indicate loading state
117			self.setFetchStatus( true )
118	
119			// generate placeholders to avoid CLS
120			const placeholders = self.createPlaceholders( self.getPlaceholderCount() )
121
122			// params for endpoint
123			const params = new URLSearchParams()
124	
125			params.append( 'page', ++self.currentPage )
126			params.append( 'posts_per_page', self.postsPerPage )
127			params.append( 'post_type', self.postType )
128
129			// if archive page, include taxonomy
130			if( null !== self.postTaxonomy && null !== self.postTermId ) {
131				params.append( 'taxonomy', self.postTaxonomy )
132				params.append( 'term_id', self.postTermId )
133			}
134
135			// if search page, include search term
136			if( undefined !== self.searchQuery ) {
137				params.append( 'search', self.searchQuery )
138			}
139	
140			// fetch posts from API endpoint
141			const { post_count, posts } = await self.fetchPosts( params )
142
143			// remove loading state
144			self.setFetchStatus( false )
145
146			// update object data
147			self.currentPostCount += post_count
148
149			// replace placeholder blocks with actual posts
150			self.replacePlaceholdersWithPosts( placeholders, posts )
151
152			// update progress bar
153			self.updateWidget()
154		}
155
156
157		setFetchStatus( isFetching = false ) {
158			this.isFetching = isFetching
159
160			// show/hide loading animations, etc
161			this.btn.classList.toggle( 'is-loading', isFetching )
162		}
163
164
165		getPlaceholderCount() {
166			let count = this.postsPerPage,
167				remaining = this.totalPostCount - this.currentPostCount
168
169			// if less than full page of posts, use that instead
170			if( remaining < count ) {
171				count = remaining
172			}
173			
174			return count
175		}
176
177
178		async fetchPosts( params ) {
179			const response = await fetch( `${FXLM.rest_url}/get-posts?${params.toString()}` )
180
181			return await response.json()
182		}
183
184
185		createPlaceholders( placeholderCount ) {
186			const self 			= this,
187				placeholders 	= [],
188				firstPost 		= self.posts.firstElementChild,
189				elHeight  		= firstPost.offsetHeight
190
191			for( let i = 0; i < placeholderCount; i++ ) {
192				const placeholder 	= firstPost.cloneNode(),  // clone only container
193					innerBlock 		= document.createElement('div')
194
195				placeholder.classList.add( 'placeholder-block', 'is-loading' )
196				placeholder.style.height = `${elHeight}px`
197
198				// add inner block for easier styling (in case using padding with outside block)
199				innerBlock.classList.add( 'placeholder-block__inner' )
200				placeholder.appendChild( innerBlock )
201
202				self.posts.appendChild( placeholder )
203
204				placeholders.push( placeholder )
205			}
206
207			return placeholders
208		}
209
210
211		replacePlaceholdersWithPosts( placeholders, posts ) {
212			const self = this
213
214			// goal is to replace each placeholder block with a post block
215			for( const placeholder of placeholders ) {
216
217				// check if a post exists
218				if( posts.length ) {
219					const post = posts.shift()
220
221					// convert string to HTML
222					const fragment 	= document.createRange().createContextualFragment( post )
223
224					placeholder.replaceWith( fragment )
225
226				// if no posts exist, delete placeholder block
227				} else {
228					placeholder.remove()
229				}
230			}
231
232			// do we still have posts left over?
233			if( posts.length ) {
234
235				// add to end of posts listing
236				for( const post of posts ) {
237					self.posts.insertAdjacentHTML( 'beforeend', post )
238				}
239			}
240		}
241	}	
242
243
244
245	return FXLM
246
247}) ( FXLM || {} )

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.