PageSourceSearch

https://www.chamonix.net/modules/custom/transferfix/js/search-block-display.js?v=1.x

js chamonix.net collected 2026-10-02 03:31:46 UTC 14,831 bytes, 310 lines download raw bytes

1(function ($, Drupal) {
2    Drupal.behaviors.transferfixAll = {
3        attach: function (context, settings) {
4
5            $('.initial-field', context)
6                    .once('transferfix-initialField')
7                    .on('click', function () {
8                        if (!$('.widget-toggle').hasClass('expanded')) {
9                            $('.additional-field').show();
10                            $('.widget-toggle').addClass('expanded');
11                        }
12                    });
13
14            $('.widget-toggle', context)
15                    .once('transferfix-widgetToggle')
16                    .on('click', function () {
17                        if (!$(this).hasClass('expanded')) {
18                            $('.additional-field').show();
19                            $(this).addClass('expanded');
20                        }
21                        else {
22                            $('.additional-field').hide();
23                            $(this).removeClass('expanded');
24                        }
25                    });
26
27            $('.search-toggle', context)
28                    .once('transferfix-searchToggle')
29                    .on('click', function () {
30                        var $toggle = $(this);
31                        var $wrapper = $toggle.next('.widget-wrapper');
32                        var isOpening = $toggle.hasClass('collapsed');
33
34                        // The outer search toggle controls the whole wrapper. Keep
35                        // the date/time fields in sync with the wrapper state.
36                        if (isOpening) {
37                            $wrapper.find('.additional-field').show();
38                            $wrapper.stop(true, true).slideDown();
39                        }
40                        else {
41                            $wrapper.stop(true, true).slideUp(function () {
42                                $wrapper.find('.additional-field').hide();
43                            });
44                        }
45
46                        $toggle.toggleClass('collapsed');
47                    });
48
49//            $('.form-item-from', context)
50//                    .once('insert-flex-break')
51//                    .before('<div class="tfix-flex-break"></div>');
52
53//            $('.form-item-passengers', context)
54//                    .once('insert-flex-filler')
55//                    //.before('<div class="tfix-flex-filler before-passengers"></div>')
56//                    .after('<div class="tfix-flex-filler after-passengers"></div>');
57
58            $('.from-to-switch', context)
59                    .once('transferfix-fromToSwitch')
60                    .on('click touchend', function (e) {
61                        e.preventDefault();
62                        e.stopPropagation();
63
64                        console.log('SWITCH');
65
66                        var $from = $('#edit-from', context);
67                        var $to = $('#edit-to', context);
68
69                        console.log('switch: from(select) = ' + $from.val());
70                        console.log('switch: to(select) = ' + $to.val());
71
72//                        var fromVal = $from.val();
73//                        var toVal = $to.val();
74                        if (!$from.length || !$to.length) {
75                            return;
76                        }
77
78                        var tsFrom = $from[0] && $from[0].tomselect;
79                        var tsTo = $to[0] && $to[0].tomselect;
80
81//                        if (tsFrom && tsTo) {
82//                            // false => not silent; will fire change events inside Tom Select
83//                            tsFrom.setValue(toVal, false);
84//                            tsTo.setValue(fromVal, false);
85//
86//                            // Also emit native change on the underlying <select> for any jQuery listeners
87//                            tsFrom.input.dispatchEvent(new Event('change', {bubbles: true}));
88//                            tsTo.input.dispatchEvent(new Event('change', {bubbles: true}));
89//
90//                            // Only meaningful for 'return' (two datetime labels). If one-way, we do nothing.
91//                            var journey = ($('input[name="journey_type"]:checked', context).val() || '').toLowerCase();
92//                            if (journey !== 'return') {
93//                                return;
94//                            }
95//
96//                            // Grab labels by their 'for' attributes, as rendered by php
97//                            var $startLbl = $('.tfix-container-label[for="start-date"]', context);
98//                            var $endLbl = $('.tfix-container-label[for="end-date"]', context);
99//
100//                            if ($startLbl.length && $endLbl.length && $endLbl.is(':visible')) {
101//                                // Swap the text content between the two labels
102//                                var tmp = $startLbl.text();
103//                                $startLbl.text($endLbl.text());
104//                                $endLbl.text(tmp);
105//                            }
106//                        }
107//                        else {
108//                            // Fallback if Tom Select not ready yet
109//                            $from.val(toVal).trigger('change');
110//                            $to.val(fromVal).trigger('change');
111//                        }
112
113                        // Fallback: if TomSelect not ready, just swap values (old behavior)
114                        if (!tsFrom || !tsTo) {
115                            var f = $from.val(), t = $to.val();
116                            $from.val(t).trigger('change');
117                            $to.val(f).trigger('change');
118                            return;
119                        }
120
121                        // Datasets precomputed in PHP and exposed via drupalSettings
122                        var sets = (settings.transferfix && settings.transferfix.locationSets) || null;
123
124                        if (!sets || !sets.hubs || !sets.dest) {
125                            // degrade gracefully to value swap
126                            var fromVal0 = tsFrom.getValue();
127                            var toVal0 = tsTo.getValue();
128                            tsFrom.setValue(toVal0, false);
129                            tsTo.setValue(fromVal0, false);
130                            return;
131                        }
132
133                        console.log('switch: sets.hubs = ' + sets.hubs);
134
135                        // Ensure roles exist; default to PHP initial split
136                        var fromRole = $from.data('locationType') || 'hubs';
137                        var toRole = $to.data('locationType') || 'dest';
138
139                        // Flip roles on click
140                        var newFromRole = (fromRole === 'hubs') ? 'dest' : 'hubs';
141                        var newToRole = (toRole === 'hubs') ? 'dest' : 'hubs';
142
143                        // Keep current selections before we rebuild datasets
144                        var prevFromVal = tsFrom.getValue();
145                        var prevToVal = tsTo.getValue();
146
147                        // Helper: apply dataset with optgroups preserved
148                        function applySet(ts, list) {
149                            // clear options + optgroups completely
150                            ts.clearOptions();
151                            ts.optgroups = {}; // reset stored groups
152
153                            if (typeof ts.clearCache === 'function') {
154                                ts.clearCache(); // clear render cache if available
155                            }
156
157                            // make sure grouping is enabled
158                            ts.settings.optgroupField = 'optgroup';
159                            ts.settings.lockOptgroupOrder = true;
160
161                            // Build a whitelist of allowed values
162                            var allowed = new Set((list || []).map(function (o) {
163                                return o.value;
164                            }));
165
166                            var seen = new Set();
167                            (list || []).forEach(function (o) {
168                                var g = o.group || '';
169                                if (g && !seen.has(g)) {
170                                    ts.addOptionGroup(g, {label: g});
171                                    seen.add(g);
172                                }
173                                //ts.addOption({value: o.value, text: o.label, optgroup: g});
174                                // keep disabled headers coming from PHP (e.g., resort-region-*)
175                                var opt = {value: o.value, text: o.label, optgroup: g};
176                                if (o.disabled === true) {
177                                    opt.disabled = true;
178                                } // mark as disabled
179                                ts.addOption(opt);
180
181                                // defensive: ensure internal option cache is also flagged as disabled
182                                if (o.disabled === true && ts.options[o.value]) {
183                                    ts.options[o.value].disabled = true;
184                                }
185                            });
186
187                            // Extra safety: purge anything not whitelisted (covers edge TomSelect states)
188                            Object.keys(ts.options).forEach(function (val) {
189                                if (!allowed.has(val)) {
190                                    ts.removeOption(val, true); // true = silent
191                                }
192                            });
193
194                            ts.refreshOptions(false);
195                        }
196
197                        // Rebuild datasets according to NEW roles
198                        var newFromList = sets[newFromRole];
199                        var newToList = sets[newToRole];
200                        applySet(tsFrom, newFromList);
201                        applySet(tsTo, newToList);
202
203                        // Build membership sets to decide where old selections belong
204                        var newFromVals = new Set(newFromList.map(function (o) {
205                            return o.value;
206                        }));
207                        var newToVals = new Set(newToList.map(function (o) {
208                            return o.value;
209                        }));
210
211                        // Decide where to place previous selections
212                        var nextFrom = '';
213                        var nextTo = '';
214
215                        if (prevFromVal) {
216                            if (newFromVals.has(prevFromVal))
217                                nextFrom = prevFromVal;
218                            else if (newToVals.has(prevFromVal))
219                                nextTo = prevFromVal;
220                        }
221                        if (prevToVal) {
222                            if (newToVals.has(prevToVal)) {
223                                if (!nextTo)
224                                    nextTo = prevToVal;
225
226                                else
227                                    nextFrom = prevToVal;
228                            }
229                            else if (newFromVals.has(prevToVal)) {
230                                if (!nextFrom)
231                                    nextFrom = prevToVal;
232
233                                else
234                                    nextTo = prevToVal;
235                            }
236                        }
237
238                        // Apply new selections (not silent -> triggers change handlers such as updateDateTimeLabels)
239                        tsFrom.setValue(nextFrom, false);
240                        tsTo.setValue(nextTo, false);
241
242                        // Persist NEW roles on elements (no body class; reliable across clicks)
243                        $from.data('locationType', newFromRole);
244                        $to.data('locationType', newToRole);
245                    });
246
247            // Journey type toggler: add/remove a class on submit wrapper when "return" is selected
248            $('.tfix-search-form', context).once('tfix-journey-scope').each(function () {
249                var $scope = $(this);
250
251                // Bind once per radio group inside THIS container
252                $scope.find('input[type="radio"][name="journey_type"]')
253                        .once('tfix-journey-bind')
254                        .on('change', function () {
255                            // Always resolve wrapper locally; no reliance on <form> ID
256                            var $wrap = $(this).closest('.tfix-search-form');
257                            if (!$wrap.length) {
258                                // Fallback if markup changes
259                                $wrap = $(this).closest('form');
260                                if (!$wrap.length)
261                                    $wrap = $scope;
262                            }
263
264                            // Determine if "return" is selected (by value/id/label text)
265                            var $checked = $wrap.find('input[type="radio"][name="journey_type"]:checked');
266                            var val = ($checked.val() || '').toLowerCase();
267                            var id = ($checked.attr('id') || '').toLowerCase();
268                            var labelText = '';
269                            if (id) {
270                                var $label = $wrap.find('label[for="' + $checked.attr('id') + '"]');
271                                labelText = ($label.text() || '').toLowerCase().trim();
272                            }
273                            var reReturn = /(return|retur|round\s*trip|roundtrip)/;
274                            var isReturn = reReturn.test(val) || reReturn.test(id) || reReturn.test(labelText);
275
276                            // Toggle class ONLY on this container
277                            $wrap.toggleClass('has-return', !!isReturn);
278                        });
279
280                // Initialize this container
281                initJourneyGroup($scope);
282            });
283
284        }
285    };
286
287    function initJourneyGroup($scope) {
288        // Find radios ONLY inside the given scope
289        var $radios = $scope.find('input[type="radio"][name="journey_type"]');
290        if (!$radios.length)
291            return;
292
293        var $checked = $radios.filter(':checked');
294        if ($checked.length) {
295            // Fire change once to sync UI without duplicates
296            $checked.once('tfix-journey-init').trigger('change');
297            return;
298        }
299
300        // Prefer "return" if available; otherwise first radio
301        var $return = $radios.filter('[value="return"]');
302        if ($return.length) {
303            $return.prop('checked', true).trigger('change');
304        }
305        else {
306            $radios.first().trigger('change');
307        }
308    }
309
310})(jQuery, Drupal);

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.