PageSourceSearch

https://ostseeimmo.de/wp-content/plugins/frymo/inc/elementor/assets/js/frymo-listing.js?ver=1.55.0

js ostseeimmo.de collected 2026-10-07 05:00:11 UTC 28,537 bytes, 682 lines download raw bytes

1jQuery(window).on('elementor/frontend/init', function () {
2
3    const FRMListingHandler = function ($scope, $) {
4        // Prevent duplicate bindings when Elementor re-initializes the same widget (e.g. after AJAX updates)
5        $scope.off('.frymoListing');
6
7        // Get the widget configuration
8        function getWidgetConfig() {
9            var rawData = $scope.data('frymo-widget-settings'); // Get data attribute
10
11            if (!rawData) {
12                console.warn('Warning: frymo-widget-settings is missing or undefined.');
13                return {}; // Return empty object as a fallback
14            }
15
16            try {
17                // Attempt to parse JSON if the data is a JSON string
18                return typeof rawData === "string" ? JSON.parse(rawData) : rawData;
19            } catch (error) {
20                console.error('Error parsing widget config:', error);
21                return {}; // Return empty object on error
22            }
23        }
24
25        if ($scope.find('.swiper-slider-container.frymo-listing-slider').length) {
26            var sliderSettings = JSON.parse($scope.find('.swiper-slider-container.frymo-listing-slider').attr('data-slider-settings'));
27        } else {
28            var sliderSettings = {
29                'enable_slider': ''
30            }
31        }
32
33        // External event.
34        $scope.on( 'update_widget.frymoListing', function ( event, listingConfig, frymoQuery, $triggerWidget ) {
35            update_widget( listingConfig, frymoQuery, $triggerWidget );
36        } );
37
38        /**
39         * Helper function to parse current URL and frymo_query from browser.
40         * 
41         * @param {Object} listingConfig - Widget configuration
42         * @returns {Object} Object containing currentUrl, hashFragment, and frymoQuery
43         */
44        function parseCurrentUrlAndQuery(listingConfig) {
45            // Get the full URL and strip out the hash fragment
46            var fullUrl = window.location.href;
47            var hashIndex = fullUrl.indexOf('#');
48            var hashFragment = '';
49            var currentUrl = fullUrl;
50
51            if (hashIndex !== -1) {
52                hashFragment = fullUrl.substring(hashIndex); // keep the '#' too
53                currentUrl = fullUrl.substring(0, hashIndex); // manipulate this
54            }
55
56            // Get current frymo_query param from URL if present
57            var frymoQueryRaw = location.search.split('frymo_query=')[1];
58            var frymoQuery = {};
59
60            if (typeof frymoQueryRaw !== 'undefined') {
61                try {
62                    frymoQuery = JSON.parse(decodeURIComponent(frymoQueryRaw));
63                } catch (err) {
64                    console.warn('Invalid frymo_query parameter', err);
65                }
66            }
67
68            if (typeof listingConfig.query_id === 'undefined') {
69                listingConfig.query_id = '';
70            }
71
72            if (typeof frymoQuery[listingConfig.query_id] === 'undefined') {
73                frymoQuery[listingConfig.query_id] = {};
74            }
75
76            return {
77                currentUrl: currentUrl,
78                hashFragment: hashFragment,
79                frymoQuery: frymoQuery
80            };
81        }
82
83        /**
84         * Helper function to build URL with frymo_query parameter.
85         * 
86         * @param {string} currentUrl - Base URL without hash
87         * @param {string} hashFragment - Hash fragment to reattach
88         * @param {Object} frymoQuery - Query object
89         * @returns {string} New URL with query parameters
90         */
91        function buildUrlWithFrymoQuery(currentUrl, hashFragment, frymoQuery) {
92            // Clean up empty query_id objects
93            Object.keys(frymoQuery).forEach(function(queryId) {
94                if (Object.keys(frymoQuery[queryId]).length === 0) {
95                    delete frymoQuery[queryId];
96                }
97            });
98
99            var newUrl;
100            if (Object.keys(frymoQuery).length !== 0) {
101                newUrl = frymoReplaceUrlParam(currentUrl, 'frymo_query', encodeURIComponent(JSON.stringify(frymoQuery)));
102            } else {
103                newUrl = frymoRemoveParameterFromUrl(currentUrl, 'frymo_query');
104            }
105
106            // Reattach hash fragment if any
107            newUrl += hashFragment;
108
109            return newUrl;
110        }
111
112        /**
113         * Helper function to build frymoQuery and URL for pagination/load more.
114         * 
115         * @param {Object} listingConfig - Widget configuration
116         * @param {number} pagenum - Page number to load
117         * @returns {Object} Object containing frymoQuery (for AJAX), newUrl, and shouldUpdateHistory flag
118         */
119        function buildFrymoQueryAndUrl(listingConfig, pagenum) {
120            var urlData = parseCurrentUrlAndQuery(listingConfig);
121            var currentUrl = urlData.currentUrl;
122            var hashFragment = urlData.hashFragment;
123            var frymoQuery = urlData.frymoQuery;
124
125            // Check if pagination is enabled by checking for .wp-pagenavi presence
126            var isPaginationEnabled = $scope.find('.wp-pagenavi').length > 0;
127
128            // Always include pagenum in query for AJAX to work (if > 1)
129            if (pagenum > 1) {
130                frymoQuery[listingConfig.query_id]['pagenum'] = pagenum;
131            } else {
132                delete frymoQuery[listingConfig.query_id]['pagenum'];
133            }
134
135            // For URL updates: only include pagenum if pagination is enabled
136            var frymoQueryForUrl = {};
137            if (isPaginationEnabled) {
138                frymoQueryForUrl = JSON.parse(JSON.stringify(frymoQuery)); // Deep copy
139            } else {
140                // Exclude pagenum from URL query when pagination is disabled
141                frymoQueryForUrl = JSON.parse(JSON.stringify(frymoQuery)); // Deep copy
142                if (frymoQueryForUrl[listingConfig.query_id] && 'pagenum' in frymoQueryForUrl[listingConfig.query_id]) {
143                    delete frymoQueryForUrl[listingConfig.query_id]['pagenum'];
144                }
145            }
146
147            // Build the new URL with updated frymo_query (only if pagination is enabled)
148            var newUrl = currentUrl;
149            if (isPaginationEnabled) {
150                newUrl = buildUrlWithFrymoQuery(currentUrl, hashFragment, frymoQueryForUrl);
151            } else {
152                newUrl += hashFragment;
153            }
154
155            return {
156                frymoQuery: frymoQuery,
157                newUrl: newUrl,
158                shouldUpdateHistory: isPaginationEnabled
159            };
160        }
161
162        // Pagination click
163        // $scope.on('click.frymoListing', '.wp-pagenavi:not(.current_query):not(.apply-type-reload
163) a:not(.current), .load-more-wrapper .frymo-submit', function (e) {
164        $scope.on('click.frymoListing', '.wp-pagenavi:not(.apply-type-reload) a:not(.current), .load-more-wrapper .frymo-submit', function (e) {
165            e.preventDefault();
166
167            var $body = $('body');
168            if ($body.hasClass('elementor-editor-active') || $body.hasClass('elementor-editor-preview')) {
169                return;
170            }
171
172            var listingConfig = getWidgetConfig();
173            var pagenum = parseInt($(this).attr('data-page'));
174            var type = $(this).attr('data-type') || 'pagination';
175
176            // Build frymoQuery and URL using helper function
177            var queryResult = buildFrymoQueryAndUrl(listingConfig, pagenum);
178            var frymoQuery = queryResult.frymoQuery;
179            var newUrl = queryResult.newUrl;
180            var shouldUpdateHistory = queryResult.shouldUpdateHistory;
181
182            if (type == 'pagination') {
183                // Update the widget with the new query data
184                update_widget(listingConfig, frymoQuery);
185            } else if (type == 'load-more') {
186                // Load more button click
187                loadMoreAddNewItems(listingConfig, frymoQuery);
188            }
189
190            // Push the new URL to browser history (only if pagination is enabled)
191            if (shouldUpdateHistory) {
192                window.history.pushState(JSON.stringify(frymoQuery), '', newUrl);
193            }
194        });
195
196
197
198        // Event handler for order-by select change
199        $scope.on('change.frymoListing', '.frymo-orderby-select', function (e) {
200            var listingConfig = getWidgetConfig();
201            var orderBy = $(this).val();
202
203            // Parse current URL and query using helper function
204            var urlData = parseCurrentUrlAndQuery(listingConfig);
205            var currentUrl = urlData.currentUrl;
206            var hashFragment = urlData.hashFragment;
207            var frymoQuery = urlData.frymoQuery;
208
209            // Update or remove the order_by parameter
210            if (orderBy && orderBy !== 'default') {
211                frymoQuery[listingConfig.query_id]['order_by'] = orderBy;
212            } else {
213                delete frymoQuery[listingConfig.query_id]['order_by'];
214            }
215
216            // Remove pagination when changing sort order
217            delete frymoQuery[listingConfig.query_id]['pagenum'];
218
219            // Build the new URL with updated query parameters
220            var newUrl = buildUrlWithFrymoQuery(currentUrl, hashFragment, frymoQuery);
221
222            // Remove any /2/ or pagination-style path segments
223            newUrl = frymoRemovePageSegmentFromUrl(newUrl);
224
225            // Update the widget and browser history
226            update_widget(listingConfig, frymoQuery);
227
228            // Clean up title if pagination was previously in it
229            frymoRemovePaginationFromTitle();
230
231            // Push clean URL to browser history so it persists on reload
232            window.history.pushState(JSON.stringify(frymoQuery), '', newUrl);
233        });
234
235
236
237
238
239
240
241
242
243        // // Navigation
244        // $(window).on('popstate', function (event) {
245
246        //     console.log('event', event);
247
248        //     var widgetConfig = getWidgetConfig();
249        //     var newConfig = widgetConfig;
250        //     var state = JSON.parse(event.originalEvent.state);
251        //     if (state !== null) {
252        //         newConfig = Object.assign(widgetConfig, state[widgetConfig.query_id]);
253        //     }
254
255        //     update_widget(newConfig, {}, undefined, event);
256        // });
257
258
259
260        // Infinite scroll observer instance
261        var infiniteScrollObserver = null;
262        var isInfiniteScrollLoading = false;
263
264        // Initialize infinite scroll observer
265        function initInfiniteScroll() {
266            // Check if infinite scroll is enabled
267            var listingConfig = getWidgetConfig();
268            if (listingConfig.load_more_type !== 'infinite_scroll') {
269                return;
270            }
271
272            // Disconnect existing observer if any
273            if (infiniteScrollObserver) {
274                infiniteScrollObserver.disconnect();
275                infiniteScrollObserver = null;
276            }
277
278            // Check if sentinel element exists
279            var $sentinel = $scope.find('.frymo-infinite-sentinel');
280            if (!$sentinel.length || $sentinel.closest('.load-more-wrapper').is(':hidden')) {
281                return;
282            }
283
284            // Don't initialize in Elementor editor
285            var $body = $('body');
286            if ($body.hasClass('elementor-editor-active') || $body.hasClass('elementor-editor-preview')) {
287                return;
288            }
289
290            // Check if IntersectionObserver is supported
291            if (typeof IntersectionObserver === 'undefined') {
292                console.warn('IntersectionObserver is not supported in this browser');
293                return;
294            }
295
296            // Create observer with threshold slightly before element comes into view
297            infiniteScrollObserver = new IntersectionObserver(function(entries) {
298                entries.forEach(function(entry) {
299                    // Only trigger if sentinel is intersecting and not currently loading
300                    if (entry.isIntersecting && !isInfiniteScrollLoading) {
301                        var $target = $(entry.target);
302                        var pagenum = parseInt($target.attr('data-page'));
303                        
304                        if (pagenum && !isNaN(pagenum)) {
305                            isInfiniteScrollLoading = true;
306                            
307                            // Build frymoQuery and URL using helper function
308                            var queryResult = buildFrymoQueryAndUrl(listingConfig, pagenum);
309                            var frymoQuery = queryResult.frymoQuery;
310                            var newUrl = queryResult.newUrl;
311                            var shouldUpdateHistory = queryResult.shouldUpdateHistory;
312
313                            // Load more items
314                            loadMoreAddNewItems(listingConfig, frymoQuery);
315
316                            // Push the new URL to browser history (only if pagination is enabled)
317                            if (shouldUpdateHistory) {
318                                window.history.pushState(JSON.stringify(frymoQuery), '', newUrl);
319                            }
320                        }
321                    }
322                });
323            }, {
324                root: null,
325                rootMargin: '200px', // Start loading 200px before sentinel comes into view
326                threshold: 0.1
327            });
328
329            // Observe the sentinel element
330            infiniteScrollObserver.observe($sentinel[0]);
331        }
332
333        // Load more AJAX request
334        function loadMoreAddNewItems(listingConfig = {}, frymoQuery = {}) {
335            var $loadMoreWrapper = $scope.find('.load-more-wrapper');
336            var $paginationWrapper = $scope.find('.wp-pagenavi');
337            $loadMoreWrapper.addClass('frymo-loading');
338            $paginationWrapper.addClass('frymo-loading');
339
340            var data = {
341                action: 'frymo_render_listing_widget',
342                listing_config: listingConfig,
343                frymo_query: JSON.stringify(frymoQuery)
344            };
345
346			$.post(frymo.ajaxurl, data)
347				.success(function (response) {
348                    // Get <article> elements from response
349					var $response = $(response);
350                    var $articles = $response.find('article');
351                    
352                    // Append articles to $scope.find('.frymo-listing')
353                    $scope.find('.frymo-listing').append($articles);
354                    init_elementor_widgets($articles);
355                    init_all_image_sliders();
356
357    				// Handle load more button (.load-more-wrapper .frymo-submit) in current scope with the one from response
358					var $newLoadMoreButton = $response.find('.load-more-wrapper .frymo-submit');
359					var $existingLoadMoreButton = $scope.find('.load-more-wrapper .frymo-submit');
360					
361					if ($newLoadMoreButton.length) {
362						// New load more button found, replace or append it
363						if ($existingLoadMoreButton.length) {
364							$existingLoadMoreButton.replaceWith($newLoadMoreButton);
365						} else {
366							$scope.find('.load-more-wrapper').append($newLoadMoreButton);
367						}
368					} else {
369						// No load more button in response (last page), hide the existing one
370						if ($existingLoadMoreButton.length) {
371							$existingLoadMoreButton.closest('.load-more-wrapper').hide();
372						}
373					}
374
375    				// Handle infinite scroll sentinel (.frymo-infinite-sentinel) replacement
376					var $newInfiniteSentinel = $response.find('.frymo-infinite-sentinel');
377					var $existingInfiniteSentinel = $scope.find('.frymo-infinite-sentinel');
378					
379					if ($newInfiniteSentinel.length) {
380						// New infinite scroll sentinel found, replace the existing one
381						if ($existingInfiniteSentinel.length) {
382							$existingInfiniteSentinel.replaceWith($newInfiniteSentinel);
383						} else {
384							// Replace the entire wrapper
385							var $existingWrapper = $scope.find('.load-more-wrapper[data-type="infinite_scroll"]');
386							var $newWrapper = $response.find('.load-more-wrapper[data-type="infinite_scroll"]');
387							if ($existingWrapper.length && $newWrapper.length) {
388								$existingWrapper.replaceWith($newWrapper);
389							}
390						}
391						// Re-initialize observer after new sentinel is added
392						if (infiniteScrollObserver) {
393							infiniteScrollObserver.disconnect();
394							initInfiniteScroll();
395						}
396					} else {
397						// No infinite scroll sentinel in response (last page), hide the wrapper
398						if ($existingInfiniteSentinel.length) {
399							$existingInfiniteSentinel.closest('.load-more-wrapper').hide();
400						}
401						// Disconnect observer when no more pages
402						if (infiniteScrollObserver) {
403							infiniteScrollObserver.disconnect();
404							infiniteScrollObserver = null;
405						}
406					}
407
408    				// Replace pagination (.wp-pagenavi) in current scope with the one from response
409					var $newPagenavi = $response.find('.wp-pagenavi');
410					if ($newPagenavi.length) {
411						var $existingPagenavi = $scope.find('.wp-pagenavi');
412						if ($existingPagenavi.length) {
413							$existingPagenavi.replaceWith($newPagenavi);
414						} else {
415							$scope.append($newPagenavi);
416						}
417					}
418
419				})
420				.fail(function (jqXHR, textStatus, errorThrown) {
421					console.error('AJAX error', textStatus, errorThrown);
422				})
423				.always(function () {
424                    $loadMoreWrapper.removeClass('frymo-loading');
425                    $paginationWrapper.removeClass('frymo-loading');
426                    // Reset infinite scroll loading flag
427                    isInfiniteScrollLoading = false;
428				});
429        }
430
431
432
433
434        // Update listing function
435        function update_widget( listingConfig = {}, frymoQuery = {}, $filter = $scope, event = {} ) {
436
437            $scope.fadeTo(100, 0.5);
438            $filter.addClass('frymo-loading');
439            var data = {
440                action: 'frymo_render_listing_widget',
441                listing_config: listingConfig,
442                frymo_query: JSON.stringify(frymoQuery)
443            };
444
445            // console.log('data', data);
446
447            if (sliderSettings.enable_slider == 'yes') {
448                // update only articles to keep swiper instance
449
450                $.post(frymo.ajaxurl, data, function (response) {
451                    var $response = $(response);
452                    var articles = $response.find('.frymo-listing-item');
453
454                    // Check if we have results
455                    if (articles.length > 0) {
456                        // Has results - update slider items
457                        $scope.find('.frymo-listing .frymo-listing-item').remove();
458                        $scope.find('.frymo-listing').append(articles);
459                        init_elementor_widgets(articles);
460                        $scope.find('.frymo-no-results-message').remove();
461                        init_all_image_sliders();
462
463                        // Check if slider needs to be reinitialized
464                        if (!swiperSlider || !swiperSlider.initialized) {
465                            init_swiper();
466                        } else {
467                            swiperSlider.update();
468                            swiperSlider.slideTo(0, 0);
469                        }
470                    } else {
471                        // No results - replace entire content
472                        var noResultsMessage = $response.find('.frymo-no-results-message');
473                        $scope.find('.frymo-listing').empty().append(noResultsMessage);
474
475                        if (swiperSlider && swiperSlider.initialized) {
476                            swiperSlider.destroy(true, true);
477                            swiperSlider = null;
478                        }
479                    }
480
481                    $scope.fadeTo(200, 1);
482                    $filter.removeClass('frymo-loading');
483
484                    $scope.find('select.frymo-orderby-select').each(function () {
485                        frymoAdjustSelectWidth($(this), 40);
486                    });
487
488                    if (listingConfig.scrolltop_offset) {
489                        $('html, body').stop(true);
490                        $('html, body').animate({
491                            scrollTop: $scope.offset().top - listingConfig.scrolltop_offset
492                        }, 700, 'swing');
493                    }
494                    // Re-initialize infinite scroll after widget update (slider mode)
495                    initInfiniteScroll();
496                });
497            } else {
498                // update widget content
499                $.post(frymo.ajaxurl, data, function (response) {
500                    $scope.html(response).fadeTo(200, 1);
501                    init_elementor_widgets($scope);
502                    init_all_image_sliders();
503
504                    $scope.find('select.frymo-orderby-select').each(function () {
505                        frymoAdjustSelectWidth($(this), 40);
506                    });
507
508                    $filter.removeClass('frymo-loading');
509                    if (listingConfig.scrolltop_offset) {
510                        $('html, body').stop(true);
511                        $('html, body').animate({
512                            scrollTop: $scope.offset().top - listingConfig.scrolltop_offset
513                        }, 700, 'swing');
514                    }
515                    // Re-initialize infinite scroll after widget update
516                    initInfiniteScroll();
517                });
518            }
519        }
520
521        if (sliderSettings.enable_slider == 'yes') {
522            var swiperSlider;
523            init_swiper();
524        }
525
526        function init_swiper() {
527            var sliderElement = $scope.find('.swiper-slider-container');
528            var swiperSliderDataConfig = sliderSettings;
529            var swiperSliderConfig = {
530                speed: parseInt(swiperSliderDataConfig.speed),
531                navigation: {
532                    // TODO: Avoid using .elementor-widget-container due to Elementor optimized HTML feature
533                    prevEl: $scope.find('> .elementor-widget-container > .button-prev')[0] || null,
534                    nextEl: $scope.find('> .elementor-widget-container > .button-next')[0] || null,
535                },
536                breakpoints: swiperSliderDataConfig.breakpoints,
537                spaceBetween: parseInt(swiperSliderDataConfig.spaceBetween),
538                loop: swiperSliderDataConfig.loop,
539                freeMode: swiperSliderDataConfig.freeMode || false,
540                centeredSlides: swiperSliderDataConfig.centeredSlides || false,
541                autoplay: swiperSliderDataConfig.autoplay ? {
542                    delay: swiperSliderDataConfig.autoplay_delay || 1,
543                    disableOnInteraction: false,
544                } : false,
545            };
546
547            if (swiperSliderDataConfig.pagination_type != 'none') {
548                swiperSliderConfig.pagination = {
549                    el: $scope.find('.swiper-pagination')[0], type: swiperSliderDataConfig.pagination_type, clickable: true,
550                };
551            }
552
553            if (swiperSliderDataConfig.autoplay == 'yes') {
554                swiperSliderConfig.autoplay = {
555                    delay: swiperSliderDataConfig.autoplay_delay,
556                };
557            }
558
559            if ('undefined' === typeof Swiper) { // Improved Asset Loading enabled
560                var asyncSwiper = elementorFrontend.utils.swiper;
561                new asyncSwiper(sliderElement[0], swiperSliderConfig).then(function (newSwiperSliderInstance) {
562                    swiperSlider = newSwiperSliderInstance;
563                });
564            } else { // Improved Asset Loading disabled
565                swiperSlider = new Swiper(sliderElement[0], swiperSliderConfig);
566            }
567
568            if (swiperSliderDataConfig.autoplay_pause_on_hover == 'yes') {
569                $(sliderElement).on('mouseenter', function(e){
570                    swiperSlider.autoplay.pause();
571                });
572                $(sliderElement).on('mouseleave', function(e){
573                    swiperSlider.autoplay.run();
574                });
575            }
576        }
577
578        // Maybe init image sliders on hover
579        // var isImagesSliderEnabled = getWidgetConfig();
580
581        if (getWidgetConfig().enable_images_slider == 'yes') {
582
583            // De-duplicate document-level binding across Elementor re-inits
584            $(document).off('touchmove.frymoListingImages touchenter.frymoListingImages mouseenter.frymoListingImages', '.frymo-listing-images-slider');
585            $(document).on('touchmove.frymoListingImages touchenter.frymoListingImages mouseenter.frymoListingImages', '.frymo-listing-images-slider', function () {
586                var $slider_element = $(this);
587                init_images_swiper($slider_element);
588            });
589
590            init_all_image_sliders();
591
592            // $(document).on('click', '.frymo-listing-images-slider .button-next', function (e) {
593            //     e.preventDefault();
594            //     var $slider_element = $(this).closest('.frymo-listing-images-slider');
595            //     init_images_swiper($slider_element, true);
596            // });
597        }
598
599        function init_images_swiper($slider_element, slide_to_next = false) {
600            if ($slider_element.hasClass('swiper-initialized') || $slider_element.hasClass('swiper-container-initialized')) {
601                return false;
602            }
603
604            var images_slider_config = {
605                speed: parseInt(getWidgetConfig().images_transition_duration.size) || 300,
606                navigation: {
607                    prevEl: $slider_element.find('.button-prev')[0],
608                    nextEl: $slider_element.find('.button-next')[0],
609                }, pagination: {
610                    el: $slider_element.find('.swiper-pagination')[0],
611                    type: 'bullets',
612                }
613            };
614
615            if ('undefined' === typeof Swiper) { // Improved Asset Loading enabled
616                var asyncSwiper = elementorFrontend.utils.swiper;
617                new asyncSwiper($slider_element[0], images_slider_config).then(function (newSwiperSliderInstance) {
618                    var swiperImagesSlider = newSwiperSliderInstance;
619                    // if (slide_to_next) {
620                    //     swiperImagesSlider.slideNext()
621                    // }
622                });
623            } else { // Improved Asset Loading disabled
624                var swiperImagesSlider = new Swiper($slider_element[0], images_slider_config);
625                // if (slide_to_next) {
626                //     swiperImagesSlider.slideNext()
627                // }
628            }
629        }
630
631        function init_all_image_sliders() {
632            if (getWidgetConfig().enable_images_slider != 'yes') {
633                return;
634            }
635
636            $scope.find('.frymo-listing-images-slider').each(function () {
637                init_images_swiper($(this));
638            });
639        }
640
641        function init_elementor_widgets($context) {
642            if (
643                typeof elementorFrontend === 'undefined' ||
644                !elementorFrontend.elementsHandler ||
645                typeof elementorFrontend.elementsHandler.runReadyTrigger !== 'function'
646            ) {
647                return;
648            }
649
650            var $elements = $context.filter('.elementor-element').add($context.find('.elementor-element'));
651
652            if (!$elements.length) {
653                $elements = $context;
654            }
655
656            $elements.each(function () {
657                elementorFrontend.elementsHandler.runReadyTrigger($(this));
658            });
659        }
660
661        // Adjust width on page load
662		$scope.find('select.frymo-orderby-select').each(function () {
663			frymoAdjustSelectWidth($(this), 40);
664		});
665
666		// Adjust width on change
667		$scope.on('change.frymoListing', 'select.frymo-orderby-select', function () {
668			frymoAdjustSelectWidth($(this), 40);
669		});
670
671		// Initialize infinite scroll on widget load
672		initInfiniteScroll();
673    };
674
675    elementorFrontend.hooks.addAction('frontend/element_ready/frymo-listing.grid-skin', FRMListingHandler);
676    elementorFrontend.hooks.addAction('frontend/element_ready/frymo-listing.list-skin', FRMListingHandler);
677    elementorFrontend.hooks.addAction('frontend/element_ready/frymo-listing.loop-item-skin', FRMListingHandler);
678
679    ['grid-skin', 'list-skin', 'loop-item-skin', 'table-skin'].forEach(function (skin) {
680        elementorFrontend.hooks.addAction('frontend/element_ready/frymo-fpe-units-listing.' + skin, FRMListingHandler);
681    });
682});

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.