PageSourceSearch

https://waikatotainui.com/wp-content/themes/pro-child/js/form-28-verification.js?ver=1785226713

js waikatotainui.com collected 2026-10-03 22:45:13 UTC 34,561 bytes, 790 lines download raw bytes

1/**
2 * Form 28 - Election Results - Member/Contact Verification
3 *
4 * Page 1: Submitter verified with name + DOB, then Marae Committee check (must match)
5 * Page 2: Representatives verified with name + DOB (email optional)
6 */
7(function($) {
8    var verifyTimeout = null;
9    var committeeTimeout = null;
10    var lastVerifiedHash = null;
11    var lastCommitteeHash = null;
12    var isVerifying = false;
13    var isCheckingCommittee = false;
14    var isCheckingElection = false;
15    var committeeVerified = false;  // Track if committee check passed
16    var memberVerified = false;     // Track if member verification passed
17    var electionVerified = false;   // Track if election status check passed
18    
19    // Rep verification state - includes verified flag
20    var repState = {
21        rep1: { timeout: null, lastHash: null, isVerifying: false, verified: false },
22        rep2: { timeout: null, lastHash: null, isVerifying: false, verified: false },
23        alt: { timeout: null, lastHash: null, isVerifying: false, verified: false }
24    };
25
26    function getForm28() { return $('#gform_28'); }
27    function sfid() { return $('#input_28_47').val() ? $('#input_28_47').val().trim() : ''; }
28
29    // =========================================================================
30    // SUBMITTER VERIFICATION (with DOB) + MARAE COMMITTEE CHECK
31    // =========================================================================
32    
33    function injectSubmitterStatusContainer() {
34        var $field53 = $('#field_28_53');
35        if ($field53.length) {
36            var $content = $field53.find('.gfield_html');
37            if ($content.length === 0) $content = $field53;
38            $content.attr('id', 'wt-verify-status-28');
39        }
40    }
41
42    function setSubmitterStatus(html) {
43        var $status = $('#wt-verify-status-28');
44        if ($status.length) {
45            $status.html(html);
46        } else {
47            var $field53 = $('#field_28_53');
48            if ($field53.length) $field53.find('.gfield_html').first().html(html);
49        }
50    }
51
52    function readSubmitterInputs() {
53        return {
54            first_name: $('#input_28_40_3').val() ? $('#input_28_40_3').val().trim() : '',
55            middle_name: $('#input_28_40_4').val() ? $('#input_28_40_4').val().trim() : '',
56            last_name: $('#input_28_40_6').val() ? $('#input_28_40_6').val().trim() : '',
57            dob: $('#input_28_43').val() ? $('#input_28_43').val().trim() : '',
58            email: $('#input_28_44').val() ? $('#input_28_44').val().trim() : '',
59            marae: $('#input_28_50').val() ? $('#input_28_50').val().trim() : '',
60            role: getSelectedRole()
61        };
62    }
63    
64    function getSelectedRole() {
65        var role = $('#input_28_51').val() || '';
66        if (role === 'Other') {
67            role = $('#input_28_52').val() ? $('#input_28_52').val().trim() : '';
68        }
69        return role;
70    }
71
72    function hashSubmitterInputs(vals) {
73        return vals.first_name + '|' + vals.middle_name + '|' + vals.last_name + '|' + vals.dob;
74    }
75    
76    function hashCommitteeInputs(vals) {
77        return vals.first_name + '|' + vals.middle_name + '|' + vals.last_name + '|' + vals.dob + '|' + vals.marae + '|' + vals.role;
78    }
79
80    function validateSubmitter(vals) {
81        if (!vals.first_name || !vals.last_name || !vals.dob) return false;
82        if (!/^(0?[1-9]|[12][0-9]|3[01])\/(0?[1-9]|1[0-2])\/\d{4}$/.test(vals.dob)) return false;
83        return true;
84    }
85    
86    function validateCommitteeCheck(vals) {
87        return vals.first_name && vals.last_name && vals.dob && vals.marae && vals.role;
88    }
89
90    function submitterVerifyingUI() {
91        setSubmitterStatus('<div style="padding:12px 15px;background-color:#fff3cd;border:1px solid #ffc107;color:#856404;border-radius:4px;font-size:14px;line-height:1.5;font-weight:500;"><strong>Verifying your details...</strong></div>');
92    }
93
94    function submitterVerifiedUI(data) {
95        $('#input_28_47').val(data.sfid || '');
96        var message = '<strong>Found in our records.</strong>';
97        // After member verified, show that we need to check committee
98        var vals = readSubmitterInputs();
99        if (!vals.marae || !vals.role) {
100            message += '<br><span style="color:#856404;font-size:13px;">Please select your Marae and Role to continue.</span>';
101        }
102        setSubmitterStatus('<div style="padding:12px 15px;background-color:#e8f5e8;border:1px solid #a5d6a7;color:#2e7d32;border-radius:4px;font-size:14px;line-height:1.5;font-weight:500;">
102' + message + '</div>');
103    }
104    
105    function committeeVerifyingUI() {
106        var vals = readSubmitterInputs();
107        var message = '<strong>Found in our records.</strong>';
108        message += '<br><span style="color:#856404;"><strong>Checking Marae Committee membership...</strong></span>';
109        setSubmitterStatus('<div style="padding:12px 15px;background-color:#fff3cd;border:1px solid #ffc107;color:#2e7d32;border-radius:4px;font-size:14px;line-height:1.5;font-weight:500;">' + message + '</div>');
110    }
111    
112    function committeeVerifiedUI(data) {
113        committeeVerified = true;
114
115        var message = '<strong>Found in our records.</strong>';
116        var vals = readSubmitterInputs();
117        message += '<br><span style="color:#1565c0;"><strong>✓ Verified as ' + vals.role + ' for ' + vals.marae + '</strong></span>';
118        message += '<br><span style="color:#856404;"><strong>Checking election status...</strong></span>';
119        setSubmitterStatus('<div style="padding:12px 15px;background-color:#fff3cd;border:1px solid #ffc107;color:#2e7d32;border-radius:4px;font-size:14px;line-height:1.5;font-weight:500;">' + message + '</div>');
120
121        // Now check election status for this marae
122        checkElectionStatus(vals.marae, data);
123    }
124    
125    function committeeNotVerifiedUI() {
126        committeeVerified = false;
127        electionVerified = false;
128        disableNextButton();
129
130        var vals = readSubmitterInputs();
131        var message = '<strong>Found in our records.</strong>';
132        message += '<br><span style="color:#c62828;"><strong>✗ Not found as ' + vals.role + ' for ' + vals.marae + '</strong></span>';
133        message += '<br><span style="font-size:13px;color:#555;">You must be registered as a committee member to submit election results. Please contact us on 0800 824 684.</span>';
134        setSubmitterStatus('<div style="padding:12px 15px;background-color:#ffebee;border:1px solid #ef9a9a;color:#2e7d32;border-radius:4px;font-size:14px;line-height:1.5;font-weight:500;">' + message + '</div>');
135    }
136
137    // =========================================================================
138    // ELECTION STATUS CHECK
139    // =========================================================================
140
141    function electionVerifiedUI(committeeData, electionData) {
142        electionVerified = true;
143        enableNextButton();
144
145        // Store election SFID in hidden field (field 63 - needs to be added to form)
146        if (electionData.election_sfid) {
147            $('#input_28_63').val(electionData.election_sfid);
148        }
149
150        var message = '<strong>Found in our records.</strong>';
151        var vals = readSubmitterInputs();
152        message += '<br><span style="color:#1565c0;"><strong>✓ Verified as ' + vals.role + ' for ' + vals.marae + '</strong></span>';
153        message += '<br><span style="color:#2e7d32;"><strong>✓ Election open for results submission</strong></span>';
154        setSubmitterStatus('<div style="padding:12px 15px;background-color:#e8f5e8;border:1px solid #a5d6a7;color:#2e7d32;border-radius:4px;font-size:14px;line-height:1.5;font-weight:500;">' + message + '</div>');
155    }
156
157    function electionNotVerifiedUI(committeeData, errorMessage) {
158        electionVerified = false;
159        disableNextButton();
160
161        var message = '<strong>Found in our records.</strong>';
162        var vals = readSubmitterInputs();
163        message += '<br><span style="color:#1565c0;"><strong>✓ Verified as ' + vals.role + ' for ' + vals.marae + '</strong></span>';
164        message += '<br><span style="color:#c62828;"><strong>✗ ' + (errorMessage || 'No election currently in progress for this marae') + '</strong></span>';
165        message += '<br><span style="font-size:13px;color:#555;">Results may have already been submitted, or the election hasn\'t started yet. Please contact us on 0800 824 684.</span>';
166        setSubmitterStatus('<div style="padding:12px 15px;background-color:#ffebee;border:1px solid #ef9a9a;color:#2e7d32;border-radius:4px;font-size:14px;line-height:1.5;font-weight:500;">' + message + '</div>');
167    }
168
169    function checkElectionStatus(marae, committeeData) {
170        if (isCheckingElection) return;
171        if (!marae) {
172            electionNotVerifiedUI(committeeData, 'Marae not specified');
173            return;
174        }
175
176        isCheckingElection = true;
177
178        $.ajax({
179            url: window.ajaxurl || '/wp-admin/admin-ajax.php',
180            type: 'POST',
181            dataType: 'json',
182            data: {
183                action: 'check_election_status',
184                marae: marae
185            },
186            success: function(resp) {
187                isCheckingElection = false;
188                if (resp && resp.success && resp.data && resp.data.status === 'In Progress') {
189                    electionVerifiedUI(committeeData, resp.data);
190                } else {
191                    var errorMsg = (resp && resp.data && resp.data.message) ? resp.data.message : 'No election in progress';
192                    electionNotVerifiedUI(committeeData, errorMsg);
193                }
194            },
195            error: function(xhr, status, error) {
196                isCheckingElection = false;
197                electionNotVerifiedUI(committeeData, 'Unable to check election status');
198            }
199        });
200    }
201
202    function submitterNotFoundUI() {
203        $('#input_28_47').val('');
204        $('#input_28_63').val('');  // Clear election SFID
205        committeeVerified = false;
206        electionVerified = false;
207        disableNextButton();
208
209        var firstName = $('#input_28_40_3').val() ? $('#input_28_40_3').val().trim() : '';
210        var lastName = $('#input_28_40_6').val() ? $('#input_28_40_6').val().trim() : '';
211        var name = (firstName + ' ' + lastName).trim() || 'You';
212        setSubmitterStatus('<div style="padding:12px 15px;background-color:#ffebee;border:1px solid #ef9a9a;color:#c62828;border-radius:4px;font-size:14px;line-height:1.5;"><strong>' + name + '</strong> was not found in our records with the details provided.<br><span style="font-size:13px;">Please check your details or contact us on 0800 824 684.</span></div>');
213    }
214
215    function submitterWaitingMessage() {
216        committeeVerified = false;
217        electionVerified = false;
218        disableNextButton();
219        
220        var firstName = $('#input_28_40_3').val() ? $('#input_28_40_3').val().trim() : '';
221        var lastName = $('#input_28_40_6').val() ? $('#input_28_40_6').val().trim() : '';
222        var dob = $('#input_28_43').val() ? $('#input_28_43').val().trim() : '';
223        var missingFields = [];
224        if (!firstName) missingFields.push('first name');
225        if (!lastName) missingFields.push('last name');
226        if (!dob) missingFields.push('date of birth');
227        if (missingFields.length > 0 && !sfid()) {
228            setSubmitterStatus('<div style="padding:12px 15px;background-color:#f0f8ff;border:1px solid #b3d4fc;color:#2c3e50;border-radius:4px;font-size:14px;line-height:1.5;"><strong>Verification</strong><br>Please provide your ' + missingFields.join(', ') + ' to verify.</div>');
229        }
230    }
231    
232    // =========================================================================
233    // NEXT BUTTON CONTROL
234    // =========================================================================
235    
236    function disableNextButton() {
237        var $nextBtn = $('#gform_next_button_28_49, .gform_next_button');
238        $nextBtn.prop('disabled', true).css('opacity', '0.5').css('cursor', 'not-allowed');
239    }
240    
241    function enableNextButton() {
242        var $nextBtn = $('#gform_next_button_28_49, .gform_next_button');
243        $nextBtn.prop('disabled', false).css('opacity', '1').css('cursor', 'pointer');
244    }
245
246    // =========================================================================
247    // MEMBER VERIFICATION AJAX
248    // =========================================================================
249
250    function verifySubmitter() {
251        if (isVerifying) return;
252        var vals = readSubmitterInputs();
253        var hash = hashCommitteeInputs(vals);  // Include marae/role in hash
254
255        if (lastVerifiedHash === hash && sfid() && committeeVerified) {
256            // Already verified committee - restore appropriate UI state
257            var message = '<strong>Found in our records.</strong>';
258            message += '<br><span style="color:#1565c0;"><strong>✓ Verified as ' + vals.role + ' for ' + vals.marae + '</strong></span>';
259
260            if (electionVerified) {
261                // Election open - enable button
262                enableNextButton();
263                message += '<br><span style="color:#2e7d32;"><strong>✓ Election open for results submission</strong></span>';
264                setSubmitterStatus('<div style="padding:12px 15px;background-color:#e8f5e8;border:1px solid #a5d6a7;color:#2e7d32;border-radius:4px;font-size:14px;line-height:1.5;font-weight:500;">
264' + message + '</div>');
265            } else {
266                // Election not open - keep button disabled
267                disableNextButton();
268                message += '<br><span style="color:#c62828;"><strong>✗ No election currently in progress for this marae</strong></span>';
269                message += '<br><span style="font-size:13px;color:#555;">Results may have already been submitted, or the election hasn\'t started yet. Please contact us on 0800 824 684.</span>';
270                setSubmitterStatus('<div style="padding:12px 15px;background-color:#ffebee;border:1px solid #ef9a9a;color:#2e7d32;border-radius:4px;font-size:14px;line-height:1.5;font-weight:500;">' + message + '</div>');
271            }
272            return;
273        }
274
275        if (!validateSubmitter(vals)) {
276            submitterWaitingMessage();
277            return;
278        }
279
280        // If marae/role not selected yet, just show waiting message
281        if (!vals.marae || !vals.role) {
282            var message = '<div style="padding:12px 15px;background-color:#f0f8ff;border:1px solid #b3d4fc;color:#2c3e50;border-radius:4px;font-size:14px;line-height:1.5;">';
283            message += '<strong>Verification</strong><br>Please select your Marae and Role to verify.</div>';
284            setSubmitterStatus(message);
285            disableNextButton();
286            return;
287        }
288
289        isVerifying = true;
290        submitterVerifyingUI();
291
292        // Single call with all details - verify member + committee in one request
293        $.ajax({
294            url: window.ajaxurl || '/wp-admin/admin-ajax.php',
295            type: 'POST',
296            dataType: 'json',
297            data: {
298                action: 'verify_form_131_contact',  // Use same endpoint as Form 131
299                form_id: '28',
300                first_name: vals.first_name,
301                middle_name: vals.middle_name,
302                last_name: vals.last_name,
303                dob: vals.dob,
304                committee_role: vals.role,
305                marae: vals.marae
306            },
307            success: function(resp) {
308                isVerifying = false;
309                if (resp && resp.success && resp.data && resp.data.contactMatch && resp.data.sfid) {
310                    lastVerifiedHash = hash;
311                    memberVerified = true;
312                    $('#input_28_47').val(resp.data.sfid);
313
314                    // Check committee status
315                    if (resp.data.isMaraeCommittee === true) {
316                        committeeVerified = true;
317                        committeeVerifiedUI(resp.data);
318                    } else {
319                        committeeVerified = false;
320                        committeeNotVerifiedUI();
321                    }
322                } else {
323                    lastVerifiedHash = null;
324                    memberVerified = false;
325                    committeeVerified = false;
326                    electionVerified = false;
327                    submitterNotFoundUI();
328                }
329            },
330            error: function(xhr, status, error) {
331                isVerifying = false;
332                lastVerifiedHash = null;
333                memberVerified = false;
334                committeeVerified = false;
335                electionVerified = false;
336                submitterNotFoundUI();
337            }
338        });
339    }
340    
341    // =========================================================================
342    // MARAE COMMITTEE CHECK AJAX
343    // =========================================================================
344    
345    function checkMaraeCommittee() {
346        if (isCheckingCommittee) return;
347        if (!memberVerified) return;  // Must be verified as member first
348        
349        var vals = readSubmitterInputs();
350        var hash = hashCommitteeInputs(vals);
351        
352        if (lastCommitteeHash === hash && committeeVerified) return;
353        
354        if (!validateCommitteeCheck(vals)) {
355            // Show message about selecting marae/role
356            var message = '<strong>Found in our records.</strong>';
357            var missing = [];
358            if (!vals.marae) missing.push('Marae');
359            if (!vals.role) missing.push('Role');
360            message += '<br><span style="color:#856404;font-size:13px;">Please select your ' + missing.join(' and ') + ' to continue.</span>';
361            setSubmitterStatus('<div style="padding:12px 15px;background-color:#e8f5e8;border:1px solid #a5d6a7;color:#2e7d32;border-radius:4px;font-size:14px;line-height:1.5;font-weight:500;">
361' + message + '</div>');
362            disableNextButton();
363            return;
364        }
365        
366        isCheckingCommittee = true;
367        committeeVerifyingUI();
368        
369        // Convert DOB from dd/mm/yyyy to ISO format for API
370        var dobParts = vals.dob.split('/');
371        var dobIso = '';
372        if (dobParts.length === 3) {
373            dobIso = dobParts[2] + '-' + dobParts[1].padStart(2, '0') + '-' + dobParts[0].padStart(2, '0');
374        }
375        
376        $.ajax({
377            url: window.ajaxurl || '/wp-admin/admin-ajax.php',
378            type: 'POST',
379            dataType: 'json',
380            data: {
381                action: 'verify_waikato_member',
382                form_id: '131',  // Use form 131 to trigger committee check
383                first_name: vals.first_name,
384                middle_name: vals.middle_name,
385                last_name: vals.last_name,
386                dob: vals.dob,
387                committee_role: vals.role,
388                marae: vals.marae
389            },
390            success: function(resp) {
391                isCheckingCommittee = false;
392                lastCommitteeHash = hash;
393                if (resp && resp.success && resp.data && resp.data.isMaraeCommittee) {
394                    committeeVerifiedUI(resp.data);
395                } else {
396                    committeeNotVerifiedUI();
397                }
398            },
399            error: function(xhr, status, error) {
400                isCheckingCommittee = false;
401                lastCommitteeHash = null;
402                committeeNotVerifiedUI();
403            }
404        });
405    }
406
407    function scheduleSubmitterVerify() {
408        if (verifyTimeout) clearTimeout(verifyTimeout);
409        verifyTimeout = setTimeout(verifySubmitter, 1500);
410    }
411    
412    function scheduleCommitteeCheck() {
413        if (committeeTimeout) clearTimeout(committeeTimeout);
414        committeeTimeout = setTimeout(checkMaraeCommittee, 500);
415    }
416
417    // =========================================================================
418    // REPRESENTATIVE VERIFICATION (name + DOB required, email optional)
419    // =========================================================================
420    
421    function injectRepStatusContainers() {
422        // Rep 1 - field 54
423        var $field54 = $('#field_28_54');
424        if ($field54.length) {
425            var $content = $field54.find('.gfield_html');
426            if ($content.length === 0) $content = $field54;
427            $content.attr('id', 'wt-rep1-status-28');
428        }
429        // Rep 2 - field 55
430        var $field55 = $('#field_28_55');
431        if ($field55.length) {
432            var $content = $field55.find('.gfield_html');
433            if ($content.length === 0) $content = $field55;
434            $content.attr('id', 'wt-rep2-status-28');
435        }
436        // Alternate - field 56
437        var $field56 = $('#field_28_56');
438        if ($field56.length) {
439            var $content = $field56.find('.gfield_html');
440            if ($content.length === 0) $content = $field56;
441            $content.attr('id', 'wt-alt-status-28');
442        }
443    }
444
445    function setRepStatus(repId, html) {
446        var $status = $('#wt-' + repId + '-status-28');
447        if ($status.length) {
448            $status.html(html);
449        }
450    }
451
452    // Field mappings for reps - includes SFID hidden fields and DOB
453    var repFields = {
454        rep1: { first: '1_3', middle: '1_4', last: '1_6', dob: '60', email: '4', sfid: '57' },
455        rep2: { first: '3_3', middle: '3_4', last: '3_6', dob: '61', email: '7', sfid: '58' },
456        alt: { first: '9_3', middle: '9_4', last: '9_6', dob: '62', email: '10', sfid: '59' }
457    };
458
459    function readRepInputs(repId) {
460        var f = repFields[repId];
461        return {
462            first_name: $('#input_28_' + f.first).val() ? $('#input_28_' + f.first).val().trim() : '',
463            middle_name: $('#input_28_' + f.middle).val() ? $('#input_28_' + f.middle).val().trim() : '',
464            last_name: $('#input_28_' + f.last).val() ? $('#input_28_' + f.last).val().trim() : '',
465            dob: $('#input_28_' + f.dob).val() ? $('#input_28_' + f.dob).val().trim() : '',
466            email: $('#input_28_' + f.email).val() ? $('#input_28_' + f.email).val().trim() : ''
467        };
468    }
469
470    function setRepSfid(repId, sfid) {
471        var f = repFields[repId];
472        $('#input_28_' + f.sfid).val(sfid || '');
473    }
474
475    function hashRepInputs(vals) {
476        // Only hash name + DOB - email/phone don't affect verification
477        return vals.first_name + '|' + vals.middle_name + '|' + vals.last_name + '|' + vals.dob;
478    }
479
480    function validateRep(vals) {
481        // Need first name, last name, and valid DOB
482        if (!vals.first_name || !vals.last_name || !vals.dob) return false;
483        // Validate date format (dd/mm/yyyy or d/m/yyyy)
484        if (!/^(0?[1-9]|[12][0-9]|3[01])\/(0?[1-9]|1[0-2])\/\d{4}$/.test(vals.dob)) return false;
485        return true;
486    }
487
488    function repVerifyingUI(repId) {
489        setRepStatus(repId, '<div style="padding:10px 12px;background-color:#fff3cd;border:1px solid #ffc107;color:#856404;border-radius:4px;font-size:13px;line-height:1.4;"><strong>Checking...</strong></div>');
490    }
491
492    function repVerifiedUI(repId, data) {
493        // Store the SFID in hidden field
494        setRepSfid(repId, data.sfid || '');
495        repState[repId].verified = true;
496        
497        var vals = readRepInputs(repId);
498        var name = (vals.first_name + ' ' + vals.last_name).trim();
499        var message = '<strong>' + name + '</strong> found in our records.';
500        setRepStatus(repId, '<div style="padding:10px 12px;background-color:#e8f5e8;border:1px solid #a5d6a7;color:#2e7d32;border-radius:4px;font-size:13px;line-height:1.4;">' + message + '</div>');
501        
502        checkRepVerificationStatus();
503    }
504
505    function repNotFoundUI(repId) {
506        // Clear the SFID field
507        setRepSfid(repId, '');
508        repState[repId].verified = false;
509        
510        var vals = readRepInputs(repId);
511        var name = (vals.first_name + ' ' + vals.last_name).trim() || 'This person';
512        setRepStatus(repId, '<div style="padding:10px 12px;background-color:#ffebee;border:1px solid #ef9a9a;color:#c62828;border-radius:4px;font-size:13px;line-height:1.4;"><strong>' + name + '</strong> was not found in our records.<br><span style="font-size:12px;">Please check the name or contact us on 0800 824 684.</span></div>');
513        
514        checkRepVerificationStatus();
515    }
516    
517    // Check if all filled reps are verified and enable/disable submit
518    function checkRepVerificationStatus() {
519        var allVerified = true;
520
521        // Rep 1 is required - must be filled and verified
522        var rep1Vals = readRepInputs('rep1');
523        if (rep1Vals.first_name && rep1Vals.last_name) {
524            if (!repState.rep1.verified) allVerified = false;
525        } else {
526            // Rep 1 not filled yet - can't submit
527            allVerified = false;
528        }
529
530        // Rep 2 is optional - but if filled, must be verified
531        var rep2Vals = readRepInputs('rep2');
532        if (rep2Vals.first_name && rep2Vals.last_name) {
533            if (!repState.rep2.verified) allVerified = false;
534        }
535
536        // Alt is optional - but if filled, must be verified
537        var altVals = readRepInputs('alt');
538        if (altVals.first_name && altVals.last_name) {
539            if (!repState.alt.verified) allVerified = false;
540        }
541
542        if (allVerified) {
543            enableSubmitButton();
544        } else {
545            disableSubmitButton();
546        }
547    }
548    
549    function disableSubmitButton() {
550        var $submitBtn = $('#gform_submit_button_28, .gform_button[type="submit"]');
551        $submitBtn.prop('disabled', true).css('opacity', '0.5').css('cursor', 'not-allowed');
552    }
553    
554    function enableSubmitButton() {
555        var $submitBtn = $('#gform_submit_button_28, .gform_button[type="submit"]');
556        $submitBtn.prop('disabled', false).css('opacity', '1').css('cursor', 'pointer');
557    }
558
559    function repWaitingMessage(repId) {
560        var vals = readRepInputs(repId);
561        if (!vals.first_name && !vals.last_name && !vals.dob) {
562            setRepStatus(repId, '');
563            return;
564        }
565        var missingFields = [];
566        if (!vals.first_name) missingFields.push('first name');
567        if (!vals.last_name) missingFields.push('last name');
568        if (!vals.dob) missingFields.push('date of birth');
569        if (missingFields.length > 0) {
570            setRepStatus(repId, '<div style="padding:10px 12px;background-color:#f0f8ff;border:1px solid #b3d4fc;color:#2c3e50;border-radius:4px;font-size:13px;line-height:1.4;">Please enter ' + missingFields.join(', ') + '.</div>');
571        }
572    }
573
574    function verifyRep(repId) {
575        var state = repState[repId];
576        if (state.isVerifying) return;
577
578        var vals = readRepInputs(repId);
579        var hash = hashRepInputs(vals);
580
581        // Skip if nothing entered
582        if (!vals.first_name && !vals.last_name) {
583            setRepStatus(repId, '');
584            return;
585        }
586
587        // If hash unchanged and already verified, restore verified state
588        if (state.lastHash === hash && state.verified) {
589            enableSubmitButton();
590            checkRepVerificationStatus();
591            return;
592        }
593        if (state.lastHash === hash) return;
594        
595        if (!validateRep(vals)) {
596            repWaitingMessage(repId);
597            return;
598        }
599        
600        state.isVerifying = true;
601        repVerifyingUI(repId);
602        
603        $.ajax({
604            url: window.ajaxurl || '/wp-admin/admin-ajax.php',
605            type: 'POST',
606            dataType: 'json',
607            data: {
608                action: 'verify_rep_member',
609                form_id: '28',
610                first_name: vals.first_name,
611                middle_name: vals.middle_name,
612                last_name: vals.last_name,
613                dob: vals.dob,
614                email: vals.email
615            },
616            success: function(resp) {
617                state.isVerifying = false;
618                state.lastHash = hash;
619                if (resp && resp.success && resp.data && resp.data.memberMatch && resp.data.sfid) {
620                    repVerifiedUI(repId, resp.data);
621                } else {
622                    repNotFoundUI(repId);
623                }
624            },
625            error: function() {
626                state.isVerifying = false;
627                state.lastHash = hash;
628                repNotFoundUI(repId);
629            }
630        });
631    }
632
633    function scheduleRepVerify(repId) {
634        var state = repState[repId];
635        if (state.timeout) clearTimeout(state.timeout);
636
637        // Check if hash actually changed before resetting verified status
638        var vals = readRepInputs(repId);
639        var hash = hashRepInputs(vals);
640        if (state.lastHash !== hash) {
641            // Hash changed - need to re-verify
642            state.verified = false;
643            disableSubmitButton();
644        }
645
646        state.timeout = setTimeout(function() { verifyRep(repId); }, 1500);
647    }
648
649    // =========================================================================
650    // INITIALIZATION & BINDINGS
651    // =========================================================================
652    
653    function bindForm28Verification() {
654        var $form = getForm28();
655        if (!$form.length) return;
656        
657        // Submitter fields (page 1) - trigger verification
658        $form.on('input change blur', '#input_28_40_3, #input_28_40_4, #input_28_40_6, #input_28_43', function() {
659            scheduleSubmitterVerify();
660        });
661
662        // Marae and Role fields - also trigger verification (single call does both)
663        $form.on('change', '#input_28_50, #input_28_51', function() {
664            lastVerifiedHash = null;  // Force re-verify with new marae/role
665            scheduleSubmitterVerify();
666        });
667
668        // Other role text field
669        $form.on('input blur', '#input_28_52', function() {
670            lastVerifiedHash = null;  // Force re-verify
671            scheduleSubmitterVerify();
672        });
673        
674        // Rep 1 fields (name, DOB only - email/phone don't affect verification)
675        $form.on('input change blur', '#input_28_1_3, #input_28_1_4, #input_28_1_6, #input_28_60', function() {
676            scheduleRepVerify('rep1');
677        });
678
679        // Rep 2 fields (name, DOB only)
680        $form.on('input change blur', '#input_28_3_3, #input_28_3_4, #input_28_3_6, #input_28_61', function() {
681            scheduleRepVerify('rep2');
682        });
683
684        // Alternate fields (name, DOB only)
685        $form.on('input change blur', '#input_28_9_3, #input_28_9_4, #input_28_9_6, #input_28_62', function() {
686            scheduleRepVerify('alt');
687        });
688    }
689
690    function checkExistingSubmitterVerification() {
691        var vals = readSubmitterInputs();
692        if (vals.first_name && vals.last_name && vals.dob) {
693            // Trigger single verification call (member + committee in one)
694            scheduleSubmitterVerify();
695        } else if (vals.first_name || vals.last_name || vals.dob) {
696            submitterWaitingMessage();
697        } else {
698            disableNextButton();
699        }
700    }
701
702    // =========================================================================
703    // COPY PAGE 1 VALUES TO "CERTIFIED BY" SECTION ON PAGE 2
704    // =========================================================================
705    
706    function copySubmitterToCertifiedBy() {
707        // Read values from page 1 fields
708        var firstName = $('#input_28_40_3').val() || '';
709        var middleName = $('#input_28_40_4').val() || '';
710        var lastName = $('#input_28_40_6').val() || '';
711        var email = $('#input_28_44').val() || '';
712        
713        // Get role - field 51 dropdown, or field 52 if "Other"
714        var role = $('#input_28_51').val() || '';
715        if (role === 'Other') {
716            role = $('#input_28_52').val() || 'Other';
717        }
718        
719        // Populate "Certified by" fields (field 18 name, field 21 email, field 19 role)
720        $('#input_28_18_3').val(firstName);
721        $('#input_28_18_4').val(middleName);
722        $('#input_28_18_6').val(lastName);
723        $('#input_28_21').val(email);
724        $('#input_28_19').val(role);
725        
726        // Copy membersfid (field 47) to sfid_submittedby (field 22)
727        var memberSfid = $('#input_28_47').val() || '';
728        $('#input_28_22').val(memberSfid);
729    }
730
731    function initAll() {
732        if (!$('#gform_28').length) return;
733        injectSubmitterStatusContainer();
734        injectRepStatusContainers();
735        bindForm28Verification();
736        
737        // Disable Next button by default until committee verified
738        disableNextButton();
739        
740        checkExistingSubmitterVerification();
741    }
742
743    $(document).ready(function() {
744        initAll();
745    });
746
747    $(document).on('gform_post_render', function(event, formId) {
748        if (parseInt(formId, 10) === 28) {
749            setTimeout(function() {
750                injectSubmitterStatusContainer();
751                injectRepStatusContainers();
752                bindForm28Verification();
753                disableNextButton();
754                checkExistingSubmitterVerification();
755            }, 500);
756        }
757    });
758
759    $(document).on('gform_page_loaded', function(event, formId, currentPage) {
760        if (parseInt(formId, 10) === 28) {
761            setTimeout(function() {
762                if (currentPage == 1) {
763                    injectSubmitterStatusContainer();
764                    disableNextButton();
765                    checkExistingSubmitterVerification();
766                } else if (currentPage == 2) {
767                    injectRepStatusContainers();
768                    
769                    // Disable submit until reps verified
770                    disableSubmitButton();
771                    
772                    // Copy submitter details to "Certified by" section
773                    copySubmitterToCertifiedBy();
774                    
775                    // Check existing rep data and verify
776                    ['rep1', 'rep2', 'alt'].forEach(function(repId) {
777                        var vals = readRepInputs(repId);
778                        if (vals.first_name && vals.last_name) {
779                            scheduleRepVerify(repId);
780                        }
781                    });
782                    
783                    // Initial check of verification status
784                    checkRepVerificationStatus();
785                }
786            }, 500);
787        }
788    });
789
790})(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.