PageSourceSearch

https://gymnasium-gruenwald.de/wp-content/plugins/gg-skifahrt-manager/assets/js/script.js?ver=1.1.1

js gymnasium-gruenwald.de collected 2026-10-05 00:41:49 UTC 13,902 bytes, 358 lines download raw bytes

1jQuery(document).ready(function ($) {
2    const $form = $('#gg-skifahrt-form');
3    const $wrapper = $('.gg-skifahrt-wrapper');
4
5    // Navigation
6    $('.btn-next').on('click', function () {
7        if (validateStep($(this).closest('.form-step'))) {
8            const nextStep = $(this).data('to');
9            goToStep(nextStep);
10        }
11    });
12
13    $('.btn-prev').on('click', function () {
14        const prevStep = $(this).data('to');
15        goToStep(prevStep);
16    });
17
18    function goToStep(step) {
19        $('.form-step').removeClass('active');
20        $('.form-step[data-step="' + step + '"]').addClass('active');
21
22        $('.gg-progress-bar .step').removeClass('active');
23        $('.gg-progress-bar .step[data-step="' + step + '"]').addClass('active');
24
25        if (step == 3 && typeof resizeCanvas === 'function') {
26            setTimeout(resizeCanvas, 100); // Small delay to ensure display:block is applied
27        }
28
29        $('html, body').animate({
30            scrollTop: $wrapper.offset().top - 100
31        }, 500);
32    }
33
34    // Live Validation on Blur/Change
35    $form.on('input change blur', '[required]', function () {
36        validateField($(this));
37    });
38
39    function validateField($input) {
40        const type = $input.attr('type');
41        let isValidRaw = false;
42
43        if (type === 'checkbox' || type === 'radio') {
44            const name = $input.attr('name');
45            const $group = $input.closest('.checkbox-group, .radio-group, .highlight-label');
46
47            // Special case for 'terms_accepted': ALL checkboxes must be checked
48            if (name === 'terms_accepted[]') {
49                const totalCheckboxes = $group.find('input[type="checkbox"]').length;
50                const checkedCheckboxes = $group.find('input[type="checkbox"]:checked').length;
51
52                if (checkedCheckboxes === totalCheckboxes) {
53                    isValidRaw = true;
54                    $group.addClass('valid').css('border-color', '#9CCB3B');
55                    $input.closest('.form-group').find('.error-msg').remove();
56                } else {
57                    $group.removeClass('valid').css('border-color', '');
58                }
59            } else {
60                // Default: at least one checked
61                if ($form.find('input[name="' + name + '"]:checked').length > 0) {
62                    isValidRaw = true;
63                    $group.addClass('valid').css('border-color', '#9CCB3B');
64                    $input.closest('.form-group').find('.error-msg').remove();
65                } else {
66                    $group.removeClass('valid').css('border-color', '');
67                }
68            }
69        } else {
70            if ($input.val().trim() !== '') {
71                // Email check
72                if (type === 'email') {
73                    if (isValidEmail($input.val())) {
74                        isValidRaw = true;
75                    }
76                } else {
77                    isValidRaw = true;
78                }
79            }
80
81            if (isValidRaw) {
82                $input.addClass('valid').removeClass('error');
83                $input.next('.error-msg').remove();
84            } else {
85                $input.removeClass('valid');
86            }
87        }
88        return isValidRaw;
89    }
90
91    // Validation
92    function validateStep($step) {
93        let isValid = true;
94
95        // Remove old errors
96        $step.find('.error').removeClass('error');
97        $step.find('.error-msg').remove();
98
99        // Check required fields
100        $step.find('[required]').each(function () {
101            const $input = $(this);
102            if (!validateField($input) && $input.attr('type') !== 'hidden') {
103                // Double check visibility - sometimes hidden tabs cause issues
104                if ($input.is(':visible') || $input.attr('type') === 'email' || $input.attr('type') === 'text') {
105                    isValid = false;
106                    const type = $input.attr('type');
107
108                    if (type === 'checkbox' || type === 'radio') {
109                        $input.closest('.form-group').find('label').first().addClass('error-text');
110                        $input.closest('.form-group').append('<small class="error-msg">Bitte auswählen.</small>');
111                        $input.closest('.checkbox-group, .radio-group, .highlight-label').css('border', '1px solid #ff5252');
112                    } else {
113                        $input.addClass('error');
114                        $input.after('<small class="error-msg">Bitte füllen Sie dieses Feld aus.</small>');
115                    }
116                    console.log('Validation failed for:', $input.attr('name'));
117                }
118            }
119        });
120
121        // Specific Email Validation not caught by above if field was optional (but here all are required mostly)
122        // Kept for robustness
123        $step.find('input[type="email"]').each(function () {
124            if ($(this).val() !== '' && !isValidEmail($(this).val())) {
125                isValid = false;
126                $(this).addClass('error').removeClass('valid');
127                if ($(this).next('.error-msg').length === 0) {
128                    $(this).after('<small class="error-msg">Bitte eine gültige E-Mail Adresse eingeben.</small>');
129                }
130            }
131        });
132
133        return isValid;
134    }
135
136    function isValidEmail(email) {
137        const pattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
138        return pattern.test(email);
139    }
140
141    // Signature Pad Logic
142    const canvas = document.getElementById('signature-pad');
143    let resizeCanvas; // Define here
144
145    if (canvas) {
146        const ctx = canvas.getContext('2d');
147        let isDrawing = false;
148
149        // Resize canvas to fill parent width
150        resizeCanvas = function () {
151            const ratio = Math.max(window.devicePixelRatio || 1, 1);
152            canvas.width = canvas.offsetWidth * ratio;
153            canvas.height = canvas.offsetHeight * ratio;
154            ctx.scale(ratio, ratio);
155        };
156        window.addEventListener("resize", resizeCanvas);
157        // Initial resize
158        resizeCanvas();
159
160        function getPos(e) {
161            const rect = canvas.getBoundingClientRect();
162            let clientX = e.clientX;
163            let clientY = e.clientY;
164
165            if (e.touches && e.touches.length > 0) {
166                clientX = e.touches[0].clientX;
167                clientY = e.touches[0].clientY;
168            }
169
170            return {
171                x: clientX - rect.left,
172                y: clientY - rect.top
173            };
174        }
175
176        function startDraw(e) {
177            e.preventDefault();
178            isDrawing = true;
179            const pos = getPos(e);
180            ctx.beginPath();
181            ctx.moveTo(pos.x, pos.y);
182
183            // Mark validation
184            $('#signature').val('signed').trigger('change');
185            $(canvas).parent().css('border-color', '#9CCB3B');
186        }
187
188        function draw(e) {
189            if (!isDrawing) return;
190            e.preventDefault();
191            const pos = getPos(e);
192            ctx.lineTo(pos.x, pos.y);
193            ctx.stroke();
194        }
195
196        function stopDraw() {
197            isDrawing = false;
198            // Update hidden input with data URL
199            if (!isCanvasBlank(canvas)) {
200                $('#signature').val(canvas.toDataURL());
201            }
202        }
203
204        // Mouse Events
205        canvas.addEventListener('mousedown', startDraw);
206        canvas.addEventListener('mousemove', draw);
207        canvas.addEventListener('mouseup', stopDraw);
208        canvas.addEventListener('mouseout', stopDraw);
209
210        // Touch Events
211        canvas.addEventListener('touchstart', startDraw);
212        canvas.addEventListener('touchmove', draw);
213        canvas.addEventListener('touchend', stopDraw);
214
215        // Clear Button
216        $('#clear-signature').on('click', function () {
217            ctx.clearRect(0, 0, canvas.width, canvas.height);
218            $('#signature').val('');
219            $(canvas).parent().css('border-color', '#e0e0e0');
220        });
221
222        function isCanvasBlank(canvas) {
223            const blank = document.createElement('canvas');
224            blank.width = canvas.width;
225            blank.height = canvas.height;
226            return canvas.toDataURL() === blank.toDataURL();
227        }
228    }
229
230    // Submission
231    $form.on('submit', function (e) {
232        e.preventDefault();
233
234        // Final validation of current step (Step 3)
235        if (!validateStep($('.form-step[data-step="3"]'))) {
236            return;
237        }
238
239        // Debug: Log all form values
240        console.log('Form serialization:', $form.serialize());
241
242        // Manual collection to ensure no data loss involved with FormData on hidden fields
243        var dataObj = {};
244        var formArray = $form.serializeArray();
245        $.each(formArray, function (i, field) {
246            // Handle multiple values (checkboxes)
247            if (dataObj[field.name]) {
248                if (!Array.isArray(dataObj[field.name])) {
249                    dataObj[field.name] = [dataObj[field.name]];
250                }
251                dataObj[field.name].push(field.value);
252            } else {
253                dataObj[field.name] = field.value;
254            }
255        });
256
257        // FORCE Manual check of critical fields (Step 1) using scoped find
258        dataObj['firstname'] = $form.find('input[name="firstname"]').val();
259        dataObj['lastname'] = $form.find('input[name="lastname"]').val();
260        dataObj['email'] = $form.find('input[name="email"]').val();
261        dataObj['class'] = $form.find('select[name="class"]').val();
262
263        // Critical Check
264        if (!dataObj['firstname'] || !dataObj['lastname'] || !dataObj['email']) {
265            alert("Kritischer Fehler: Pflichtfelder konnten nicht ausgelesen werden. Bitte Seite neu laden.");
266            return;
267        }
268
269        // Add action and nonce explicitly
270        dataObj['action'] = 'gg_skifahrt_submit';
271        dataObj['nonce'] = ggSkifahrtAjax.nonce;
272
273        const $submitBtn = $(this).find('.btn-submit');
274        $submitBtn.addClass('loading').prop('disabled', true).text('Wird gesendet...');
275
276        $.ajax({
277            url: ggSkifahrtAjax.ajax_url,
278            type: 'POST',
279            data: dataObj, // Use plain object
280            // processData: true, // Default is true, needed for object
281            // contentType: 'application/x-www-form-urlencoded; charset=UTF-8', // Default
282            success: function (response) {
283                $submitBtn.removeClass('loading').prop('disabled', false).text('Anmeldung abschicken');
284
285                if (response.success) {
286                    $form.hide();
287                    $('.form-success').fadeIn();
288                    $('.gg-progress-bar').hide();
289                    $('html, body').animate({
290                        scrollTop: $wrapper.offset().top - 100
291                    }, 500);
292                } else {
293                    alert('Fehler: ' + (response.data || 'Ein unbekannter Fehler ist aufgetreten.'));
294                }
295            },
296            error: function (xhr, status, error) {
297                $submitBtn.removeClass('loading').prop('disabled', false).text('Anmeldung abschicken');
298                console.error('AJAX Error:', error);
299                alert('Ein Netzwerkfehler ist aufgetreten: ' + error);
300            }
301        });
302    });
303
304    // Table Sorting
305    $('.gg-skifahrt-results-wrapper th[data-sort]').on('click', function () {
306        const $table = $(this).closest('table');
307        const $tbody = $table.find('tbody');
308        const rows = $tbody.find('tr').toArray();
309        const index = $(this).index();
310        const type = $(this).data('sort');
311        const isAsc = !$(this).hasClass('asc'); // Toggle
312
313        $table.find('th').removeClass('asc desc');
314        $(this).addClass(isAsc ? 'asc' : 'desc');
315
316        rows.sort(function (a, b) {
317            const valA = $(a).find('td').eq(index).text().trim();
318            const valB = $(b).find('td').eq(index).text().trim();
319
320            if (type === 'date') {
321                // Simple date compare assuming format allows it or fallback to string
322                return isAsc ? new Date(valA) - new Date(valB) : new Date(valB) - new Date(valA);
323            }
324
325            return isAsc ? valA.localeCompare(valB) : valB.localeCompare(valA);
326        });
327
328        $tbody.append(rows);
329    });
330
331    // Delete All Results
332    $('#gg-skifahrt-delete-all').on('click', function (e) {
333        e.preventDefault();
334        if (confirm('SIND SIE SICHER? \n\nDies löscht ALLE Anmeldungen unwiderruflich aus der Datenbank. Dies kann nicht rückgängig gemacht werden!')) {
335            if (confirm('Wirklich sicher?')) {
336                $.ajax({
337                    url: ggSkifahrtAjax.ajax_url,
338                    type: 'POST',
339                    data: {
340                        action: 'gg_skifahrt_delete_all',
341                        nonce: ggSkifahrtAjax.nonce
342                    },
343                    success: function (response) {
344                        if (response.success) {
345                            alert('Alle Daten wurden gelöscht.');
346                            location.reload();
347                        } else {
348                            alert('Fehler: ' + response.data);
349                        }
350                    },
351                    error: function () {
352                        alert('Netzwerkfehler beim Löschen.');
353                    }
354                });
355            }
356        }
357    });
358});

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.