1/** Global Settings **/ 2window.disableLitepickerStyles = true; 3 4/** Detect Mobile devices **/ 5function asp_detect_mobile() { 6 const Devices = [ 7 /Android/i, 8 /webOS/i, 9 /iPhone/i, 10 /iPad/i, 11 /iPod/i, 12 /BlackBerry/i, 13 /Windows Phone/i 14 ]; 15 16 return Devices.some( ( Device ) => { 17 return navigator.userAgent.match( Device ); 18 } ); 19} 20 21/** Change URL state based on search & filters **/ 22function asp_change_url( Data ) { 23 const ExcludeQuery = { 24 'action' : true, 25 'mode' : true, 26 'path' : true, 27 'paged' : 1, 28 'order' : 'DESC', 29 'type' : true, 30 }; 31 const IncludeSearchParams = { 32 'p' : true, 33 'page_id' : true, 34 'post_type' : true, 35 }; 36 37 // Exclude default params from URL 38 const VisibleData = Object.entries( Data ).reduce((acc, [ Key, Value ]) => { 39 if( !Value || Object.keys( ExcludeQuery ).includes( Key ) 40 && ( ExcludeQuery[ Key ] === Value || ExcludeQuery[ Key ] === true ) ) { 41 return acc; 42 } 43 44 return {...acc, [Key]: Value}; 45 }, {}); 46 47 const SearchParams = new URLSearchParams( VisibleData ); 48 const SourceParams = new URLSearchParams( window.location.search ); 49 const PageURL = (window.location.origin + window.location.pathname).replace( /\/page\/[0-9]*/gm, '' ); 50 const Target = new URL( PageURL ); 51 52 // Only WP params to be kept 53 Object.keys( Object.fromEntries( SourceParams ) ).forEach( Key => { 54 if( !Object.keys( IncludeSearchParams ).includes( Key ) ) { 55 SourceParams.delete( Key ); 56 } 57 }); 58 59 // Combine params groups 60 const TargetParams = new URLSearchParams( { 61 ...Object.fromEntries( SourceParams ), 62 ...Object.fromEntries( SearchParams ), 63 } ); 64 65 Target.search = TargetParams.toString(); 66 67 window.history.pushState( 'page', null, Target.toString() ); 68} 69 70/** Define scoped Filters function **/ 71function asp_init_date_pickers( FilterBar ) { 72 73 74 const Pickers = FilterBar.querySelectorAll( '.sermon-date-container' ); 75 const PickersInstances = []; 76 77 if( !Pickers ) { 78 return; 79 } 80 81 Pickers.forEach( Picker => { 82 const Input = Picker.querySelector('.asp-filter-date'); 83 84 PickersInstances.push( new Litepicker( { 85 element: Input, 86 startDate: new Date(), 87 numberOfMonths: 2, 88 numberOfColumns: 2, 89 singleMode: false, 90 resetButton: true, 91 setup: Instance => { 92 Instance.on( 'selected', ( Start, End ) => { 93 if( Start && End ) { 94 Input.dispatchEvent( new CustomEvent( 'asp.filter.change' ) ); 95 } 96 } ); 97 98 Instance.on( 'clear:selection', () => { 99 Input.dispatchEvent( new CustomEvent( 'asp.filter.change' ) ); 100 } ); 101 }, 102 } ) ); 103 104 // After initializing, wrap the Litepicker in a asp-litepicker-wrapper div to reduce conflicts 105 const litepickerEl = document.querySelector('.litepicker'); 106 const wrapperDiv = document.createElement('div'); 107 wrapperDiv.className = 'asp-litepicker-wrapper'; // your desired class name 108 109 // Move litepicker inside the wrapper 110 wrapperDiv.appendChild(litepickerEl); 111 112 // Append the wrapperDiv to the body 113 document.body.appendChild(wrapperDiv); 114 115 } ); 116 117 return PickersInstances; 118} 119 120/** Set initial query values **/ 121function asp_set_initial_query( Query, FilterBar ) { 122 123 if( !FilterBar ) { 124 return Query; 125 } 126 127 const Filters = FilterBar.querySelectorAll( 'input, select' ); 128 129 Filters.forEach( Filter => { 130 Query[ Filter.getAttribute( 'name' ) ] = Filter.value; 131 }); 132 133 return Query; 134} 135 136// Flag to track if the featured content has been loaded initially 137let featuredContentLoaded = false; 138 139/** Initialize and handle change of filters value **/ 140function asp_init_filters( SermonsGrid, FilterBar, Query, DatePickers ) { 141 142 143 const Filters = FilterBar.querySelectorAll( 'input, select' ); 144
145 Filters.forEach( Filter => { 146 // Listen for future changes 147 Filter.addEventListener( 'asp.filter.change', () => { 148 Query[ 'mode' ] = 'filter'; 149 Query[ 'paged' ] = 1; 150 Query[ Filter.getAttribute( 'name' ) ] = Filter.value; 151 } ); 152 153 // Enable custom events for simple filters 154 if( Filter.getAttribute( 'name' ) !== 'sermon_dates' ) { 155 Filter.addEventListener( 'change', () => { 156 featuredContentLoaded = false; 157 Filter.dispatchEvent( new CustomEvent( 'asp.filter.change' ) ); 158 } ); 159 } 160 }); 161 162 SermonsGrid.addEventListener( 'click', Event => { 163 if( !Event.target.classList.contains( 'asp-clear-filter-criteria' ) ) { 164 return; 165 } 166 167 Event.preventDefault(); 168 169 asp_clear_filters( SermonsGrid, FilterBar, Query, DatePickers ); 170 } ); 171} 172 173function asp_clear_filters( SermonsGrid, FilterBar, QueryProxy, DatePickers ) { 174 175 176 const Filters = FilterBar.querySelectorAll( 'input, select' ); 177 const Sermons = SermonsGrid.querySelector( '.sermon-archive-holder' ); 178 const Pagination = SermonsGrid.querySelector( '.asp-pagination-wrapper' ); 179 const FeaturedContent = SermonsGrid.querySelector( '.asp-details-top-holder' ); 180 const CriteriaBar = SermonsGrid.querySelector( '.asp-criteria-box-wrapper' ); 181 182 SermonsGrid.querySelector('.asp-load-more-button')?.removeAttribute('data-asp-page'); 183 184 Filters.forEach( Filter => { 185 if( Filter.nodeName === 'SELECT' ) { 186 Filter.value = Filter.querySelector( 'option' ).value; 187 } else { 188 Filter.value = null; 189 } 190 191 if( Filter.classList.contains( 'asp-filter-date' ) ) { 192 DatePickers.forEach( DatePicker => DatePicker.clearSelection() ); 193 } 194 195 } ); 196 197 // Reset the featuredContentLoaded flag 198 featuredContentLoaded = false; 199 200 // Reset FeaturedContent and CriteriaBar 201 if (FeaturedContent) { 202 FeaturedContent.innerHTML = ''; // or set to default content 203 } 204 if (CriteriaBar) { 205 CriteriaBar.innerHTML = ''; // reset criteria display 206 } 207 208 Sermons.innerHTML = ''; 209 Pagination.innerHTML = ''; 210 QueryProxy.reset = {} 211} 212 213function asp_set_filters( SermonsGrid, FilterBar, QueryProxy, NewFilters ) { 214 215 216 const Filters = FilterBar.querySelectorAll( 'input, select' ); 217 218 Filters.forEach( Filter => { 219 if( Filter.nodeName === 'SELECT' ) { 220 Filter.value = NewFilters[ Filter.getAttribute( 'name' ) ] ?? Filter.querySelector( 'option' ).value; 221 } else { 222 Filter.value = NewFilters[ Filter.getAttribute( 'name' ) ] ?? null; 223 } 224 } ); 225 226 QueryProxy.reset = NewFilters; 227} 228 229/** Replace the content with the newly generated **/ 230function asp_render(SermonsGrid, FilterBar, Data = {}, Override = true) { 231 const CriteriaBar = SermonsGrid.querySelector('.asp-criteria-box-wrapper'); 232 const ArchiveScrollTo = SermonsGrid.querySelector('.asp-archive-ajax-scrollto'); 233 const Sermons = SermonsGrid.querySelector('.sermon-archive-holder'); 234 const FeaturedContent = SermonsGrid.querySelector('.asp-details-top-holder'); 235 const Pagination = SermonsGrid.querySelector('.asp-pagination-wrapper'); 236 237 // Update CriteriaBar and Pagination content 238 if (CriteriaBar) { 239 CriteriaBar.innerHTML = Data.active_filters; 240 } 241 if (Pagination) { 242 Pagination.innerHTML = Data.pagination; 243 } 244 245 // Update FeaturedContent if not a pagination action or if featured content has not been loaded yet 246 if (FeaturedContent && (Override || !featuredContentLoaded)) { 247 FeaturedContent.innerHTML = Data.featured; 248 featuredContentLoaded = true; // Set the flag after initial load or refresh 249 } 250 251 // Update Sermons content 252 if (Data.sermons) { 253 if (Override) { 254 Sermons.innerHTML = Data.sermons; 255 window.scrollTo({ 256 top: ArchiveScrollTo.getBoundingClientRect().y + window.scrollY - 125, 257 behavior: 'smooth' 258 }); 259 } else { 260 Sermons.insertAdjacentHTML('beforeend', Data.sermons); 261 } 262 263 setTimeout(() => { 264 Sermons.querySelectorAll('li').forEach(Sermon => Sermon.removeAttribute('style')); 265 }, 150); 266 } 267} 268 269/** Initialize the filtering, pagination and load more logic **/ 270function asp_init( SermonsGrid ) {
271 const FiltersBar = SermonsGrid.querySelector( '.asp-archive-filter' ); 272 const InfinityScrollHandler = SermonsGrid.querySelector( '.asp-pagination-wrapper' ); 273 const Type = SermonsGrid.getAttribute( 'data-asp-pagination'); 274 275 /** Creates listenable proxy for Query changes **/ 276 let QueryDebouncer; 277 const InitialQuery = { 278 'action' : 'asp_generate_sermons', 279 'paged' : 1, 280 'type' : SermonsGrid.getAttribute( 'data-asp-pagination' ), 281 'path' : window.location.pathname, 282 'order' : 'DESC' 283 } 284 285 const QueryHandler = { 286 set( Target, Property, Value ) { 287 if( Property === 'reset' ) { 288 Object.keys( Target ).forEach( Key => { 289 if( InitialQuery[ Key ] ) { 290 Target[ Key ] = InitialQuery[ Key ]; 291 } else { 292 delete Target[ Key ]; 293 } 294 }); 295 296 Object.keys( Value ).forEach( Key => { 297 Target[ Key ] = Value[ Key ]; 298 }); 299 } else { 300 Target[ Property ] = Value; 301 } 302 303 if( Property !== 'mode' ) { 304 clearInterval( QueryDebouncer ); 305 QueryDebouncer = setTimeout( () => { 306 SermonsGrid.dispatchEvent( new CustomEvent( 'asp.reload' ) ) 307 }, 250 ); 308 } 309 310 return Target; 311 } 312 }; 313 const QueryProxy = new Proxy( asp_set_initial_query( { 314 'action' : 'asp_generate_sermons', 315 'paged' : 1, 316 'type' : SermonsGrid.getAttribute( 'data-asp-pagination' ), 317 'path' : window.location.pathname, 318 'order' : 'DESC' 319 }, FiltersBar ), QueryHandler ); 320 321 /** Init Filters **/ 322 if( FiltersBar ) { 323 const DatePickers = asp_init_date_pickers( FiltersBar ); 324 asp_init_filters( SermonsGrid, FiltersBar, QueryProxy, DatePickers ); 325 } 326 327 /** Init Infinity Scroll on Desktop **/ 328 if( InfinityScrollHandler && Type === 'infinity' && !asp_detect_mobile() ) { 329 const InfinityScrollObserver = new IntersectionObserver( Entries => { 330 Entries.forEach( Entry => { 331 if( Entry.isIntersecting && InfinityScrollHandler.querySelector( '.asp-load-more-button' ) ) { 332 QueryProxy.mode = 'infinity'; 333 QueryProxy.paged = InfinityScrollHandler.querySelector( '.asp-load-more-button' ).getAttribute( 'data-asp-page' ); 334 } 335 } ); 336 }, { 337 root: null, 338 threshold: 0.1, 339 } ); 340 341 InfinityScrollObserver.observe( InfinityScrollHandler ); 342 } 343 344 /** Init Infinity Scroll on Mobile **/ 345 if( InfinityScrollHandler && Type === 'infinity' && asp_detect_mobile() ) { 346 const InfinityScrollObserver = new IntersectionObserver( Entries => { 347 Entries.forEach( Entry => { 348 if( Entry.isIntersecting && InfinityScrollHandler.querySelector( '.asp-load-more-button' ) ) { 349 QueryProxy.mode = 'infinity'; 350 QueryProxy.paged = InfinityScrollHandler.querySelector( '.asp-load-more-button' ).getAttribute( 'data-asp-page' ); 351 } 352 } ); 353 }, { 354 root: null, 355 threshold: 0.1, 356 } ); 357 358 InfinityScrollObserver.observe( InfinityScrollHandler ); 359 } 360 361 /** Init Load More on Mobile **/ 362 if( Type !== 'infinity' && asp_detect_mobile() ) { 363 const LoadMoreButton = InfinityScrollHandler.querySelector('.asp-load-more-button'); 364 if( Type !== 'default') { 365 LoadMoreButton.removeAttribute( 'data-asp-pagination' ); 366 LoadMoreButton.addEventListener( 'click', () => { 367 QueryProxy.mode = 'default'; 368 QueryProxy.paged = LoadMoreButton.querySelector( '.asp-load-more-button' ); 369 }); 370 } 371 } 372 373 /** Init Pagination **/ 374 SermonsGrid.addEventListener( 'click', Event => { 375 if( !InfinityScrollHandler.contains( Event.target ) ) { 376 return; 377 } 378 379 const Page = Event.target.getAttribute('data-asp-page'); 380 const Mode = Event.target.getAttribute( 'data-asp-type' ) ? 'infinity' : 'default'; 381 382 if ( !Page ) { 383 return; 384 } 385 386 Event.preventDefault(); 387 388 QueryProxy.mode = Mode; 389 QueryProxy.paged = Page; 390 }); 391 392 /** Init Any link filtering **/ 393 SermonsGrid.addEventListener( 'click', Event => { 394 const Link = Event.target.href; 395 396 if( InfinityScrollHandler.contains( Event.target ) ) { 397 return; 398 } 399 400 if ( !Link || Link?.indexOf( window.location.pathname ) === -1 401 || Link?.indexOf( '?' ) === -1 402 || Link?.indexOf( '?sermons=' ) > -1 403 || Link?.indexOf( '?page_id=' ) > -1 404 || Link?.indexOf( '?p=' ) > -1 ) { 405 return; 406 } 407 408 if (FiltersBar) { 409 Event.preventDefault(); 410 } 411 412 const SearchParams = new URLSearchParams( Link.substr( Link.indexOf('?') + 1 ) ); 413 const Filters = {}; 414 415 for( let Key of SearchParams.keys() ) { 416 Filters[ Key ] = SearchParams.get( Key ); 417 } 418 419 asp_set_filters( SermonsGrid, FiltersBar, QueryProxy, Filters ); 420 }); 421 422 /** Handle data reload event **/ 423 SermonsGrid.addEventListener( 'asp.reload', () => { 424 if( SermonsGrid.getAttribute( 'data-state' ) === 'pending' ) { 425 return; 426 } 427 428 const RenderMode = QueryProxy.mode; 429 const Data = new FormData(); 430 431 for( let Key in QueryProxy ) { 432 if( !QueryProxy[ Key ] ) { 433 continue; 434 } 435 436 Data.append( Key, QueryProxy[ Key ] ); 437 } 438
439 if( RenderMode !== 'infinity' ) { 440 SermonsGrid.querySelector( '.asp-pagination-wrapper' ).innerHTML = ''; 441 } 442 443 SermonsGrid.setAttribute( 'data-state', 'pending' ); 444 445 fetch( window.asp_ajax.url, { 446 method: 'POST', 447 body: Data 448 } ).then( ( Response ) => Response.json() ).then( Response => { 449 asp_render( SermonsGrid, FiltersBar, Response.data, RenderMode !== 'infinity' ); 450 } ).then( () => { 451 asp_change_url( QueryProxy ); 452 }).finally(() => { 453 SermonsGrid.setAttribute( 'data-state', 'idle' ); 454 }); 455 } ); 456} 457 458/** Initialize Filtering processing with support for multiple filters (shortcodes) **/ 459const SermonsGrids = document.querySelectorAll( '.asp-archive-container' ); 460 461if( SermonsGrids ) { 462 SermonsGrids.forEach( SermonsGrid => { 463 asp_init( SermonsGrid ); 464 } ); 465} 466 467 468/** Improves compatability with 3rd party plugins and themes by removing select2 */ 469(function($) { 470 $(document).ready(function() { 471 $(".asp-filter-order, .asp-filter-series, .asp-filter-topic, .asp-filter-speaker, .asp-filter-book, .asp-filter-campus, .asp-filter-service-type").each(function() { 472 // Check if Select2 is applied and destroy it 473 if ($(this).data('select2')) { 474 $(this).select2('destroy'); 475 } 476 477 // Check if it's wrapped in .fancy-select-wrap and unwrap it 478 if ($(this).parent().hasClass('fancy-select-wrap')) { 479 $(this).unwrap(); 480 } 481 }); 482 }); 483})(jQuery);
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.