PageSourceSearch

https://www.academicimpressions.com/wp-content/mu-plugins/ai-funct…/assets/js/trainings.js?ver=1790021031

js academicimpressions.com collected 2026-09-25 04:11:20 UTC 13,799 bytes, 299 lines download raw bytes

1(function ($) {
2    $(document).ready(function () {
3
4        // ================================================
5        // FACETWP SETUP: Wait until FWP is available
6        // ================================================
7        function initFacetWP() {
8            if (typeof FWP === 'undefined' || typeof FWP.hooks === 'undefined') {
9                setTimeout(initFacetWP, 100);
10                return;
11            }
12
13            /* ============================================================
14               Load More override
15            ============================================================ */
16            FWP.hooks.addFilter('facetwp/template_html', function (resp, params) {
17                if (FWP.is_load_more) {
18                    FWP.is_load_more = false;
19
20                    var $html = $(params.html);
21                    // layout builder
22                    if (0 < $('.fwpl-layout').length) {
23                        $('.fwpl-layout').append($html.html());
24                    } else {
25                        $('.facetwp-template').append($html);
26                    }
27
28                    $('a', $html).first().focus(); // accessibility fix
29                    return true;
30                }
31                return resp;
32            }, 8);
33
34            // Search button reimplementation
35            $(document).on('click', '.facetwp-type-search .search-button', function () {
36                if (!FWP.is_refresh) {
37                    FWP.autoload();
38                }
39            });
40
41            // Sort as links instead of dropdown
42            $(document).on('click', '.facetwp-sort-links a', function (e) {
43                e.preventDefault();
44                FWP.extras.sort = $(this).data('value');
45                FWP.autoload();
46            });
47
48            // Overlay on refresh
49            $(document).on('facetwp-refresh', function () {
50                if (!FWP.soft_refresh) {
51                    $(document).on('facetwp-loaded', loadSlickSlider);
52                }
53                let $el = $('.facetwp-sort');
54                $el.prepend('<div class="facetwp-overlay">');
55                $el.find('.facetwp-overlay').css({
56                    width: $el.width(),
57                    height: $el.height()
58                });
59            });
60
61            $(document).on('facetwp-loaded', function () {
62                $('.facetwp-sort .facetwp-overlay').remove();
63            });
64
65            function loadSlickSlider() {
66                var $domains = $('.domains');
67                if (!$domains.length || $domains.hasClass('slick-initialized')) {
68                    $(document).off('facetwp-loaded', loadSlickSlider);
69                    return;
70                }
71
72                // Roving tabindex: only the current (center) slide link is focusable. Slick clones
73                // slides for infinite mode, so we must scope to the whole slider and use .slick-current.
74                function setDomainRovingTabindex(event, slick) {
75                    if (!slick || !slick.$slider) { return; }
76
77                    // hide everything including clones
78                    slick.$slider.find('.slick-slide')
79                        .attr('aria-hidden', 'true')
80                        .find('a, button, input, select')
81                        .attr('tabindex', '-1');
82
83                    // expose only the current slide
84                    slick.$slider.find('.slick-current')
85                        .attr('aria-hidden', 'false')
86                        .find('.facetwp-domain')
87                        .attr('tabindex', '0');
88                }
89                $domains.on('init afterChange', setDomainRovingTabindex);
90
91                // This announces the current domain to screen readers when forward/backward buttons are used
92                // Since the entire carousel changes, we can't just use it on the current slide
93                function announceDomainChange(event, slick, currentSlide) {
94                    var text = slick.$slides
95                    .eq(currentSlide)
96                    .find('a')
97                    .text();
98
99                    $('#js-domain-carousel-announce').text(text + ', ' + (currentSlide + 1) + ' of ' + slick.slideCount);
100                }
101                $domains.on('afterChange', announceDomainChange);
102
103                $domains.slick({
104                    infinite: true,
105                    centerMode: true,
106                    centerPadding: '100px',
107                    accessibility: false,
108                    focusOnSelect: false,
109                    focusOnChange: false,
110                    speed: 250,
111                    prevArrow: '<button type="button" class="slick-prev" aria-label="Previous domain">Previous</button>',
112                    nextArrow: '<button type="button" class="slick-next" aria-label="Next domain">Next</button>',
113                    slidesToShow: 9,
114                    slidesToScroll: 1,
115                    responsive: [
116                        { breakpoint: 1885, settings: { slidesToShow: 8 } },
117                        { breakpoint: 1660, settings: { slidesToShow: 7 } },
118                        { breakpoint: 1435, settings: { slidesToShow: 6 } },
119                        { breakpoint: 1210, settings: { slidesToShow: 5 } },
120                        { breakpoint: 1050, settings: { slidesToShow: 4 } },
121                        { breakpoint: 885, settings: { slidesToShow: 3 } },
122                        { breakpoint: 720, settings: { slidesToShow: 2 } },
123                        { breakpoint: 575, settings: { slidesToShow: 2, centerPadding: '60px' } },
124                        { breakpoint: 440, settings: { slidesToShow: 1, centerPadding: '100px' } },
125                        { breakpoint: 380, settings: { slidesToShow: 1, centerPadding: '80px' } }
126                    ]
127                });
128
129                $(document).off('facetwp-loaded', loadSlickSlider);
130            }
131
132            // Highlight current sort link
133            $(document).on('facetwp-loaded', function () {
134                $('.facetwp-dropdown').attr('id', 'facetwp-dropdown');
135                $('.facetwp-sort-links a').removeClass('selected');
136                $('.facetwp-sort-links a.sort-' + FWP.extras.sort).addClass('selected');
137            });
138
139            /* ============================================================
140               Google Tag Manager events
141            ============================================================ */
142            let previousFilters = {};
143            let isLoadMoreClicked = false;
144
145            function getCurrentFilters() {
146                let filters = FWP.facets || {};
147                let filterData = {};
148                Object.keys(filters).forEach(function (key) {
149                    let value = filters[key];
150                    if (Array.isArray(value) && value.length > 0) {
151                        value = value.join(', ');
152                    } else if (typeof value === 'string' && value.trim() !== '') {
153                        value = value;
154                    } else {
155                        return;
156                    }
157                    filterData[`filter_${key}`] = value;
158                });
159                return filterData;
160            }
161
162            $(document).on('click', '.facetwp-load-more', function () {
163                isLoadMoreClicked = true;
164                let loadMoreEvent = {
165                    'event': 'facetwp_load_more_clicked',
166                    ...getCurrentFilters()
167                };
168                window.dataLayer = window.dataLayer || [];
169                window.dataLayer.push(loadMoreEvent);
170            });
171
172            $(document).on('facetwp-loaded', function () {
173                if (typeof FWP === 'undefined') return;
174                if (isLoadMoreClicked) {
175                    isLoadMoreClicked = false;
176                    return;
177                }
178
179                let currentFilters = getCurrentFilters();
180                let hasChanges = false;
181                let eventData = { 'event': 'facetwp_filter_applied', ...currentFilters };
182
183                Object.keys(currentFilters).forEach(function (key) {
184                    if (previousFilters[key] !== currentFilters[key]) {
185                        hasChanges = true;
186                    }
187                });
188
189                if (hasChanges) {
190                    window.dataLayer = window.dataLayer || [];
191                    window.dataLayer.push(eventData);
192                    previousFilters = { ...currentFilters };
193                }
194            });
195
196            /* ============================================================
197               Preserve /trainings/{domain}/{subdomain}
198               Prevent FacetWP from wiping the pretty path on soft refresh
199            ============================================================ */
200            (function () {
201                if (typeof FWP === 'undefined') return;
202
203                let lastPretty = '';
204
205                function slug(x) { return (x || '').toString().toLowerCase(); }
206
207                function getFacetValue(facetName) {
208                    // Return the current facet selection if available, else fallback from URL
209                    if (FWP.facets && Array.isArray(FWP.facets[facetName]) && FWP.facets[facetName].length) {
210                        return slug(FWP.facets[facetName][0]);
211                    }
212
213                    // Fallback: parse from URL path when FWP.facets not ready
214                    const pathParts = window.location.pathname.split('/').filter(Boolean);
215                    const idx = pathParts.indexOf('trainings');
216                    if (idx > -1) {
217                        if (facetName === '_domains' && pathParts[idx + 1]) return slug(pathParts[idx + 1]);
218                        if (facetName === '_sub_domains' && pathParts[idx + 2]) return slug(pathParts[idx + 2]);
219                    }
220                    return '';
221                }
222
223                function buildPrettyPath() {
224                    // const domainSel = (FWP.facets && FWP.facets['_domains']) || [];
225                    // const subdomainSel = (FWP.facets && FWP.facets['_sub_domains']) || [];
226                    const domainSel = getFacetValue('_domains');
227                    const subdomainSel = getFacetValue('_sub_domains');
228                    const domain = slug(domainSel || '');
229                    const subdomain = slug(subdomainSel || '');
230                    const segments = ['trainings'];
231                    if (domain && domain !== 'default') segments.push(encodeURIComponent(domain));
232                    if (subdomain && subdomain !== 'default') segments.push(encodeURIComponent(subdomain));
233                    return '/' + segments.join('/') + '/';
234                }
235
236                function buildQueryString() {
237                    const params = [];
238                    if (FWP && FWP.facets) {
239                        Object.keys(FWP.facets).forEach(function (key) {
240                            if (
241                                key === 'domains' || key === 'sub_domains' ||
242                                key === '_domains' || key === '_sub_domains'
243                            ) return; // skip both legacy + underscored facet keys
244                            const val = FWP.facets[key];
245                            if (Array.isArray(val) && val.length) {
246                                params.push(encodeURIComponent(key) + '=' + encodeURIComponent(val.join(',')));
247                            } else if (typeof val === 'string' && val) {
248                                params.push(encodeURIComponent(key) + '=' + encodeURIComponent(val));
249                            }
250                        });
251                    }
252                    if (FWP && FWP.extras && FWP.extras.searched) {
253                        params.push('_s=' + encodeURIComponent(FWP.extras.searched));
254                    }
255                    if (FWP && FWP.paged && FWP.paged > 1) {
256                        params.push('paged=' + encodeURIComponent(FWP.paged));
257                    }
258                    if (FWP && FWP.sort && FWP.sort !== 'default') {
259                        params.push('_sort=' + encodeURIComponent(FWP.sort));
260                    }
261                    return params.length ? ('?' + params.join('&')) : '';
262                }
263
264                function updatePrettyURL(force = false) {
265                    const pretty = buildPrettyPath() + buildQueryString();
266                    if (force || (pretty !== lastPretty && window.location.pathname + window.location.search !== pretty)) {
267                        history.replaceState(null, '', pretty);
268                        lastPretty = pretty;
269                    }
270                }
271                // This replaces anyone landing on the page with the OLD style of parameters with the
272                // New full path
273                // Run only once after first real load (not pre-AJAX)
274                let initialized = false;
275                $(document).on('facetwp-loaded', function () {
276                    if (!initialized) {
277                        initialized = true;
278                        // Delay a little to let FacetWP finish any internal hash cleanup
279                        setTimeout(() => updatePrettyURL(true), 50);
280                    } else {
281                        // On subsequent refreshes, update only if path facets changed
282                        updatePrettyURL();
283                    }
284                });
285
286                // Intercept any direct call to FWP.setHash (FacetWP internal)
287                FWP.setHash = function () {
288                    updatePrettyURL();
289                };
290            })();
291
292
293        }
294
295        // Wait for FacetWP global
296        initFacetWP();
297
298    }); // end document ready
299})(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.