1(() => { 2 let FUNC_INDENTIFIER = "success-message"; 3 let DATA_LAYER_TIMEOUT = 4000; 4 5 /** 6 * @param {string} str 7 * @param {number} chunkSize 8 * @param {number} maxChunks 9 * @return {string[] | null} 10 */ 11 function splitStringIntoChunks(str, chunkSize, maxChunks) { 12 if (typeof str !== "string" || str.length === 0) { 13 return null; 14 } 15 16 let chunks = []; 17 for ( 18 let i = 0; 19 i < str.length && chunks.length < maxChunks; 20 i += chunkSize 21 ) { 22 chunks.push(str.substring(i, i + chunkSize)); 23 } 24 return chunks; 25 } 26 27 /** 28 * @param {MktoForm2} mktoForm 29 * @param {{sourceFieldName: string, targetFieldName: string}} options 30 */ 31 function splitFieldValue(mktoForm, options) { 32 mktoForm.onSubmit(() => { 33 let formValues = mktoForm.getValues(); 34 if ( 35 !(options.sourceFieldName in formValues) || 36 formValues[options.sourceFieldName] == null 37 ) { 38 return; 39 } 40 41 let chunks = splitStringIntoChunks( 42 formValues[options.sourceFieldName], 43 40, 44 4, 45 ); 46 if (chunks == null) { 47 return; 48 } 49 50 let hiddenFields = {}; 51 for (let i = 0; i < 4; i += 1) { 52 hiddenFields[ 53 `${options.targetFieldName}${i === 0 ? "" : i}` 54 ] = chunks[i] ?? null; 55 } 56 57 mktoForm.addHiddenFields(hiddenFields); 58 }); 59 } 60 61 /** 62 * @param {string} name 63 * @return {string} 64 */ 65 function getCookieValue(name) { 66 return decodeURIComponent( 67 document.cookie.match( 68 `(^|;)\\s*${name}\\s*=\\s*([^;]+)`, 69 )?.pop() ?? "", 70 ); 71 } 72 73 /** 74 * @param {string} urlQueryString 75 * @param {string[]} urlParams 76 * @return {boolean} 77 */ 78 function hasURLParams(urlQueryString, urlParamNames) { 79 let urlParams = new URLSearchParams(urlQueryString); 80 return urlParamNames.some((urlParamName) => { 81 return urlParams.has(urlParamName); 82 }); 83 } 84 85 /** 86 * @param {MktoForm2} mktoForm 87 * @param {Record<string, string>} fieldMapping 88 */ 89 function setURLParamsAsHiddenFields(mktoForm, fieldMapping) { 90 let urlParams = Object.values(fieldMapping); 91 let urlQueryString = window.location.search; 92 let sessionQueryString = getCookieValue("gtm_storage_query"); 93 let urlSearchParams; 94 95 if ( 96 urlQueryString.length > 0 &&
97 hasURLParams(urlQueryString, urlParams) 98 ) { 99 urlSearchParams = new URLSearchParams(urlQueryString); 100 } else if ( 101 sessionQueryString.length > 0 && 102 hasURLParams(sessionQueryString, urlParams) 103 ) { 104 urlSearchParams = new URLSearchParams(sessionQueryString); 105 } else { 106 return; 107 } 108 109 let fields = Object.keys(fieldMapping); 110 let hiddenFields = {}; 111 for (let field of fields) { 112 let urlParameterName = fieldMapping[field]; 113 114 if (urlSearchParams.has(urlParameterName)) { 115 hiddenFields[field] = urlSearchParams.get(urlParameterName); 116 } 117 } 118 119 mktoForm.addHiddenFields(hiddenFields); 120 } 121 122 /** 123 * @param {Function} callback 124 */ 125 function ready(callback) { 126 if (document.readyState !== "loading") { 127 callback(); 128 } else { 129 document.addEventListener("DOMContentLoaded", callback); 130 } 131 } 132 133 /** 134 * Compares URLs without considering their query strings or hashes. 135 * 136 * @param {string} firstURL 137 * @param {string} secondURL 138 * @return {boolean} 139 */ 140 function isSameURL(firstURL, secondURL) { 141 try { 142 let parsedFirstURL = new URL(firstURL, window.location.href); 143 let parsedSecondURL = new URL(secondURL, window.location.href); 144 145 return ( 146 parsedFirstURL.protocol === parsedSecondURL.protocol && 147 parsedFirstURL.port === parsedSecondURL.port && 148 parsedFirstURL.hostname === parsedSecondURL.hostname && 149 parsedFirstURL.pathname === parsedSecondURL.pathname 150 ); 151 } catch { 152 return firstURL === secondURL; 153 } 154 } 155 156 /** 157 * @param {Record<string, unknown>} dataLayerEvt 158 * @param {number} timeout 159 * @return {Promise<boolean>} 160 */ 161 function sendToDataLayer( 162 dataLayerEvt, 163 timeout = DATA_LAYER_TIMEOUT, 164 ) { 165 return new Promise((resolve) => { 166 if (!Array.isArray(window.dataLayer)) { 167 resolve(false); 168 return; 169 } 170 171 let isPromiseSettled = false; 172 let timeoutId; 173 174 let handleCallback = (isDataLayerPushed) => { 175 if (isPromiseSettled) { 176 return; 177 } 178 179 isPromiseSettled = true; 180 window.clearTimeout(timeoutId); 181 resolve(isDataLayerPushed); 182 }; 183 184 timeoutId = window.setTimeout(() => { 185 handleCallback(false); 186 }, timeout); 187 188 try { 189 window.dataLayer.push({ 190 ...dataLayerEvt, 191 eventTimeout: timeout, 192 eventCallback() { 193 handleCallback(true); 194 }, 195 }); 196 } catch { 197 handleCallback(false); 198 } 199 }); 200 } 201 202 /** 203 * @param {HTMLFormElement} formEl 204 * @param {{messageFieldset?: string}} options 205 * @return {string | null} 206 */ 207 function findSuccessMessage(formEl, options) { 208 let fieldsetEls = formEl.querySelectorAll("fieldset"); 209 let successMessage = null; 210 211 if (options.messageFieldset == null) { 212 return successMessage; 213 } 214 215 for (let fieldsetEl of fieldsetEls) { 216 let legendEl = fieldsetEl.querySelector("legend"); 217 if ( 218 legendEl?.textContent?.trim().toLowerCase() !== 219 options.messageFieldset.trim().toLowerCase() 220 ) { 221 continue; 222 } 223 224 let hasInputEl = 225 fieldsetEl.querySelector("input, select, textarea") !== null; 226 let htmlTextEl = fieldsetEl.querySelector(".mktoHtmlText"); 227 228 if (hasInputEl || !htmlTextEl) { 229 continue; 230 } 231 232 let rowEl = fieldsetEl.closest(".mktoFormRow"); 233 if (rowEl) { 234 rowEl.style.display = "none"; 235 } 236 237 successMessage = htmlTextEl.innerHTML; 238 } 239 240 return successMessage; 241 } 242 243 /** 244 * Waits for the data-layer event and then redirects or displays a message. 245 * 246 * @param {MktoForm2} mktoForm 247 * @param {HTMLFormElement} formEl 248 * @param {{ 249 * messageFieldset?: string, 250 * defaultMessage?: string 251 * }} options 252 * @param {string} followUpURL 253 * @return {Promise<void>} 254 */ 255 async function handleFormSuccess( 256 mktoForm, 257 formEl, 258 options, 259 followUpURL, 260 ) { 261 let storedSuccessMessage = findSuccessMessage( 262 formEl, 263 options, 264 ); 265 266 await sendToDataLayer({ 267 event: "marketo_form_submit", 268 formId: mktoForm.getId(), 269 });
270 271 if (!isSameURL(followUpURL, window.location.href)) { 272 window.location.href = followUpURL; 273 return; 274 } 275 276 let hasStoredSuccessMessage = 277 typeof storedSuccessMessage === "string" && 278 storedSuccessMessage.trim().length > 0; 279 let successMessage = hasStoredSuccessMessage 280 ? storedSuccessMessage 281 : options.defaultMessage; 282 let hasSuccessMessage = 283 typeof successMessage === "string" && 284 successMessage.trim().length > 0; 285 if (hasSuccessMessage) { 286 formEl.innerHTML = successMessage; 287 } 288 } 289 290 /** 291 * @param {MktoForm2} mktoForm 292 * @param {{ 293 * messageFieldset?: string, 294 * defaultMessage?: string 295 * }} options 296 */ 297 function showSuccessMessage(mktoForm, options = {}) { 298 let hasMessageFieldset = 299 Object.prototype.hasOwnProperty.call( 300 options, 301 "messageFieldset", 302 ); 303 let hasDefaultMessage = 304 Object.prototype.hasOwnProperty.call( 305 options, 306 "defaultMessage", 307 ); 308 if (!hasMessageFieldset && !hasDefaultMessage) { 309 console.warn( 310 "Parameters for the form success message are missing.", 311 ); 312 return; 313 } 314 315 let [formEl] = mktoForm.getFormElem(); 316 if (!formEl) { 317 return; 318 } 319 320 // Find and hide the success-message fieldset before submission. 321 findSuccessMessage(formEl, options); 322 323 if ( 324 formEl.getAttribute(`data-${FUNC_INDENTIFIER}`) === "true" 325 ) { 326 return; 327 } 328 329 formEl.setAttribute(`data-${FUNC_INDENTIFIER}`, "true"); 330 331 mktoForm.onSuccess((_formValues, followUpURL) => { 332 handleFormSuccess( 333 mktoForm, 334 formEl, 335 options, 336 followUpURL, 337 ); 338 return false; 339 }); 340 } 341 342 function initMarketoForms() { 343 if (typeof window.MktoForms2 !== "object") { 344 return; 345 } 346 347 window.MktoForms2.whenReady((mktoForm) => { 348 splitFieldValue(mktoForm, { 349 sourceFieldName: "Company", 350 targetFieldName: "Company_additional_name", 351 }); 352 353 setURLParamsAsHiddenFields(mktoForm, { 354 utmsource: "utm_source", 355 utmmedium: "utm_medium", 356 utmcontent: "utm_content", 357 utmcampaign: "utm_campaign", 358 utmterm: "utm_term", 359 }); 360 }); 361 362 window.MktoForms2.whenRendered((mktoForm) => { 363 showSuccessMessage(mktoForm, { 364 messageFieldset: "Success Message", 365 defaultMessage: 366 "<div class=\"mktoFormSuccessImage\"><img src=\"https://go.ksb.com/rs/050-CAG-466/images/default-tick-box.png\"/></div>", 367 }); 368 }); 369 } 370 371 ready(initMarketoForms); 372})();
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.