1(function () { 2 'use strict'; 3 4 var form = document.getElementById('gs-join-form'); 5 if (!form || !form.dataset.gsJoinForm) { 6 return; 7 } 8 9 // form.action is clobbered by the hidden <input name="action">, so read the attribute. 10 var ajaxUrl = form.getAttribute('action'); 11 var messages = form.querySelector('.gs-join-form__messages'); 12 var submitBtn = form.querySelector('button[type="submit"]'); 13 var defaultBtnText = submitBtn ? submitBtn.textContent : ''; 14 var cfg = window.ctfGsJoin || {}; 15 var submittingText = cfg.submitting || 'Submittingâ¦'; 16 17 function showMessage(text, isError) { 18 if (!messages) { 19 return; 20 } 21 messages.hidden = false; 22 messages.textContent = text; 23 messages.classList.toggle('gs-join-form__messages--error', !!isError); 24 messages.classList.toggle('gs-join-form__messages--success', !isError); 25 } 26 27 function clearFieldErrors() { 28 form.querySelectorAll('.gs-join-form__field-error').forEach(function (el) { 29 el.classList.remove('gs-join-form__field-error'); 30 }); 31 form.querySelectorAll('[data-field-error]').forEach(function (el) { 32 el.removeAttribute('data-field-error'); 33 }); 34 } 35 36 function markFieldErrors(errors) { 37 if (!errors || typeof errors !== 'object') { 38 return; 39 } 40 Object.keys(errors).forEach(function (name) { 41 var field = form.querySelector('[name="' + name + '"]'); 42 if (!field) { 43 return; 44 } 45 var wrap = field.closest('.gs-field, .gs-join-form__fieldset'); 46 if (wrap) { 47 wrap.classList.add('gs-join-form__field-error'); 48 wrap.setAttribute('data-field-error', errors[name]); 49 } 50 }); 51 } 52 53 function bindOtherToggles() { 54 form.querySelectorAll('[data-gs-other-toggle]').forEach(function (radio) { 55 radio.addEventListener('change', function () { 56 var group = radio.name; 57 var otherWrap = form.querySelector('[data-gs-other-for="' + group + '"]'); 58 if (!otherWrap) { 59 return; 60 } 61 var show = form.querySelector('input[name="' + group + '"]:checked'); 62 var isOther = show && show.value === 'other'; 63 otherWrap.hidden = !isOther; 64 var otherInput = otherWrap.querySelector('input'); 65 if (otherInput) { 66 otherInput.required = isOther; 67 if (!isOther) { 68 otherInput.value = ''; 69 } 70 } 71 }); 72 }); 73 } 74 75 bindOtherToggles(); 76 77 form.addEventListener('submit', function (e) { 78 e.preventDefault(); 79 if (form.__ctfSubmitInFlight) { 80 return; 81 } 82 clearFieldErrors(); 83 if (messages) { 84 messages.hidden = true; 85 messages.textContent = ''; 86 } 87 88 if (submitBtn) { 89 submitBtn.disabled = true; 90 submitBtn.textContent = submittingText; 91 } 92 93 form.__ctfSubmitInFlight = true; 94 95 var params = new URLSearchParams(new FormData(form)); 96 var recaptcha = window.CTFRecaptchaHandler || {}; 97 var submitFn = typeof recaptcha.submitWithToken === 'function' 98 ? recaptcha.submitWithToken.bind(recaptcha) 99 : function(action, fn) { 100 var configured = !(window.ctfRecaptchaStrings && (window.ctfRecaptchaStrings.enabled === false || window.ctfRecaptchaStrings.enabled === '0')); 101 var tokenPromise = typeof recaptcha.getToken === 'function' 102 ? recaptcha.getToken(action) 103 : Promise.resolve(''); 104 return tokenPromise.then(function(token) { 105 if (!token && configured) { 106 var msg = (window.ctfRecaptchaStrings && window.ctfRecaptchaStrings.unavailable) 107 || 'We couldn\'t run the security check in this browser. Please try again in Safari or Chrome.'; 108 if (typeof recaptcha.reportClientFailure === 'function') { 109 recaptcha.reportClientFailure(action, { reason: 'script_missing', ua_bucket: 'other' }); 110 } 111 return { 112 success: false, 113 data: { message: msg, recaptcha_unavailable: true } 114 }; 115 } 116 if (token) { 117 params.set('recaptcha_token', token); 118 } 119 return fn(token || '', { reason: null }); 120 }); 121 }; 122 123 Promise.resolve().then(function() { 124 return submitFn('gs_join_submit', function(token, meta) { 125 if (token) { 126 params.set('recaptcha_token', token); 127 } else { 128 params.delete('recaptcha_token'); 129 } 130 if (typeof recaptcha.applyClientReason === 'function') { 131 recaptcha.applyClientReason(params, meta && meta.reason); 132 } 133 return fetch(ajaxUrl, { 134 method: 'POST', 135 credentials: 'same-origin', 136 headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, 137 body: params.toString(), 138 }).then(function(res) { 139 return res.json(); 140 }); 141 }); 142 }) 143 .then(function (data) { 144 if (data && data.success) { 145 // Dedicated landing page (/welcome/, /bienvenue/) when 146 // provisioned; the inline panel below is the fallback. 147 var successUrl = form.getAttribute('data-success-url'); 148 if (successUrl) { 149 window.location.assign(successUrl); 150 return; 151 } 152 form.reset(); 153 form.querySelectorAll('[data-gs-other-for]').forEach(function (el) { 154 el.hidden = true; 155 }); 156 var successPanel = document.querySelector('[data-gs-join-success]'); 157 if (successPanel) { 158 form.hidden = true; 159 successPanel.hidden = false;
160 successPanel.scrollIntoView({ behavior: 'smooth', block: 'center' }); 161 } else { 162 var msg = (data.data && data.data.message) || cfg.success || 'Thanks for signing up!'; 163 showMessage(msg, false); 164 form.scrollIntoView({ behavior: 'smooth', block: 'start' }); 165 } 166 return; 167 } 168 var errMsg = (data && data.data && data.data.message) || cfg.error || 'Something went wrong. Please try again.'; 169 if (data && data.data && data.data.errors) { 170 markFieldErrors(data.data.errors); 171 // Surface the first field error (e.g. email typo suggestion) in the banner. 172 var fieldKeys = Object.keys(data.data.errors); 173 for (var i = 0; i < fieldKeys.length; i++) { 174 if (data.data.errors[fieldKeys[i]]) { 175 errMsg = data.data.errors[fieldKeys[i]]; 176 break; 177 } 178 } 179 } 180 showMessage(errMsg, true); 181 }) 182 .catch(function () { 183 showMessage(cfg.error || 'Something went wrong. Please try again.', true); 184 }) 185 .finally(function () { 186 form.__ctfSubmitInFlight = false; 187 if (submitBtn) { 188 submitBtn.disabled = false; 189 submitBtn.textContent = defaultBtnText; 190 } 191 }); 192 }); 193})();
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.