1jQuery(document).ready(function ($) { 2 /** 3 * Inline Validation System 4 */ 5 function initInlineValidation() { 6 // Add validation error container after each required field 7 $('.mrm-form-wrapper-inner form').each(function() { 8 $(this).find('[required]').each(function() { 9 var $field = $(this); 10 var $formGroup = $field.closest('.mrm-form-group, .mrm-input-group'); 11 12 // Add error message container if not exists 13 if ($formGroup.length && !$formGroup.find('.mrm-validation-error').length) { 14 $formGroup.append('<span class="mrm-validation-error">This field is required</span>'); 15 } 16 17 // Override browser default validation tooltip 18 $field.on('invalid', function(e) { 19 e.preventDefault(); 20 showFieldError($field); 21 }); 22 23 // Clear error on input 24 $field.on('input change', function() { 25 clearFieldError($field); 26 }); 27 }); 28 }); 29 } 30 31 function showFieldError($field) { 32 var $formGroup = $field.closest('.mrm-form-group, .mrm-input-group'); 33 var errorMessage = getValidationMessage($field); 34 35 $formGroup.addClass('has-error'); 36 $formGroup.find('.mrm-validation-error').text(errorMessage); 37 } 38 39 function clearFieldError($field) { 40 var $formGroup = $field.closest('.mrm-form-group, .mrm-input-group'); 41 $formGroup.removeClass('has-error'); 42 } 43 44 function getValidationMessage($field) { 45 var field = $field[0]; 46 47 if (field.validity.valueMissing) { 48 return 'This field is required'; 49 } 50 if (field.validity.typeMismatch) { 51 if (field.type === 'email') { 52 return 'Please enter a valid email address'; 53 } 54 return 'Please enter a valid value'; 55 } 56 if (field.validity.patternMismatch) { 57 return 'Please match the requested format'; 58 } 59 60 return 'Please fill out this field correctly'; 61 } 62 63 function validateForm($form) { 64 var isValid = true; 65 66 $form.find('[required]').each(function() { 67 var $field = $(this); 68 var field = $field[0]; 69 70 if (!field.checkValidity()) { 71 showFieldError($field); 72 isValid = false; 73 } else { 74 clearFieldError($field); 75 } 76 }); 77 78 // Focus first invalid field 79 if (!isValid) { 80 $form.find('.has-error').first().find('input, select, textarea').focus(); 81 } 82 83 return isValid; 84 } 85 86 // Initialize inline validation 87 initInlineValidation(); 88 89 /** 90 * Shortcode form submission ajax 91 */ 92 93 $(".mrm-form-wrapper-inner form").on("submit",function (e){ 94 e.preventDefault(); 95 96 // Run inline validation first 97 if (!validateForm($(this))) { 98 return false; 99 } 100 101 $(this).find(".response").html(""); 102 $(this).find(".mrm-submit-button").addClass("show-loader"); 103 $(this).find(".mrm-submit-button").attr('disabled','disabled'); 104 let that = this; 105 var data = new FormData(); 106 data.append( "post_data", $(that).serialize() ); 107 data.append( "wp_nonce", window.MRM_Frontend_Vars.nonce ); // This nonce is created in the following file: /app/Internal/Frontend/FrontendAssets.php 108 data.append( "action", "mrm_submit_form"); 109 // Capture source URL and UTM parameters for submission tracking. 110 data.append( "source_url", window.location.href ); 111 var _mintUrlParams = new URLSearchParams( window.location.search ); 112 [ "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content" ].forEach( function( key ) { 113 if ( _mintUrlParams.has( key ) ) { 114 data.append( key, _mintUrlParams.get( key ) ); 115 } 116 } ); 117 fetch(window.MRM_Frontend_Vars.rest_api_url+"mint-mail/v1/mint-form-submit", 118 { 119 method: "POST", 120 body: data 121 }) 122 .then(function(res){ return res.json(); }) 123 .then(function(response){ 124 $(that).find(".mrm-submit-button").removeClass("show-loader"); 125 $(that).find(".mrm-submit-button").removeAttr('disabled'); 126 if (response.status === "success") { 127 $(".response").addClass("mintmrm-success"); 128 $(".response").removeClass("mintmrm-error"); 129 $(that).find(".mrm-submit-button").removeClass("show-loader"); 130 if (response.confirmation_type === "same_page") { 131 if (response.after_form_submission === "hide_form") { 132 $(".mrm-form-wrapper form").hide(); 133 $(".mrm-form-wrapper").hide(); 134 } else if ( 135 response.after_form_submission === "reset_form" 136 ) { 137 $("#mrm-form")[0].reset(); 138 } 139 } 140 if (response.confirmation_type === "to_a_page") { 141 if (response.redirect_page) { 142 setTimeout(function () { 143 window.location.href = response.redirect_page; 144 }, 2000); 145 } else { 146 setTimeout(function () { 147 $(that).find(".response").html( 148 "Redirect URL not found" 149 ); 150 }, 2000); 151 } 152 } 153 if (response.confirmation_type === "to_a_custom_url") { 154 if (response.custom_url !== "") { 155 setTimeout(function () { 156 window.location.href = response.custom_url; 157 }, 1000); 158 } 159 } 160 if (response.confirmation_type === "download") { 161 fetch(response.file_url) 162 .then(response => response.blob()) 163 .then(blob => { 164 const url = window.URL.createObjectURL(blob); 165 const link = document.createElement('a'); 166 link.href = url; 167 link.download = response.file_name || 'download'; 168 link.style.display = 'none'; 169 170 document.body.appendChild(link); 171 link.click(); 172 document.body.removeChild(link); 173 174 // Clean up the object URL 175 window.URL.revokeObjectURL(url); 176 }) 177 .catch(error => { 178 console.error('Download failed:', error); 179 // Fallback to direct link 180 window.open(response.file_url, '_blank'); 181 }); 182 183 if (response.after_form_submission === "hide_form") { 184 $(".mrm-form-wrapper form").hide(); 185 $(".mrm-form-wrapper").hide(); 186 } else if (response.after_form_submission === "reset_form") { 187 $("#mrm-form")[0].reset(); 188 } 189 } 190 $(that).parent().find(".response").html(response.message); 191 $(that).find(".response").html(response.message); 192 } else if (response.status == "failed") { 193 $(that).find(".response").addClass("mintmrm-error"); 194 $(that).find(".response").removeClass("mintmrm-success"); 195 $(that).find(".response").html(response.message); 196 $(that).find(".mrm-submit-button").removeAttr('disabled'); 197 } 198 199 }) 200 }) 201 202 function modifyQueryString(input) { 203 // Split the input string into key-value pairs 204 const pairs = input.split('&'); 205 const result = []; 206 const mrmList = []; 207
208 pairs.forEach(pair => { 209 const [key, value] = pair.split('='); 210 211 // Keep the key-value pairs that are not list items 212 if (!/^\d+$/.test(key)) { 213 result.push(`${key}=${value}`); 214 } else { 215 // Collect list item keys 216 mrmList.push(key); 217 } 218 }); 219 220 // Append the mrm_list items in the desired format 221 mrmList.forEach(item => { 222 result.push(`mrm_list%5B%5D=${item}`); 223 }); 224 225 // Join the result array into a string 226 return result.join('&'); 227 } 228 229 $("#mrm-preference-form").on("submit", function (e) { 230 e.preventDefault(); 231 jQuery(".response").html(""); 232 $(".mrm-pref-submit-button").addClass("show-loader"); 233 var data = new FormData(); 234 var postData = jQuery("#mrm-preference-form").serialize(); 235 var modifiedQueryString = modifyQueryString(postData); 236 postData = modifiedQueryString; 237 data.append( "post_data", modifiedQueryString ); 238 data.append( "wp_nonce", window.MRM_Frontend_Vars.nonce ); // This nonce is created in the following file: /app/Internal/Frontend/FrontendAssets.php 239 data.append( "action", "mrm_preference_update_by_user"); 240 fetch(window.MRM_Frontend_Vars.rest_api_url+"mint-mail/v1/mint-preference-submit", 241 { 242 method: "POST", 243 body: data 244 }) 245 .then(function(res){ return res.json(); }) 246 .then(function(response){ 247 if (response.status == "success") { 248 $(".mrm-pref-submit-button").removeClass("show-loader"); 249 $("#mrm-preference-form").hide(); 250 $(".response").addClass("mintmrm-success"); 251 $(".response").removeClass("mintmrm-error"); 252 $(".response").html(response.message); 253 } else if (response.status == "failed") { 254 jQuery(".response").addClass("mintmrm-error"); 255 jQuery(".response").removeClass("mintmrm-success"); 256 jQuery(".response").html(response.message); 257 $(".mrm-pref-submit-button").removeClass("show-loader"); 258 } 259 }) 260 }); 261 262 /** 263 * Set cokokie 264 * @param name 265 * @param value 266 * @param daysToLive 267 */ 268 function setCookie(cName, cValue, expDays) { 269 let date = new Date(); 270 date.setTime(date.getTime() + expDays * 24 * 60 * 60 * 1000); 271 const expires = "expires=" + date.toUTCString(); 272 273 var value = { show: cValue, expire: date.getTime() }; 274 275 document.cookie = 276 cName + "=" + JSON.stringify(value) + "; " + expires + "; path=/"; 277 } 278 /** 279 * Form Close button Function 280 */ 281 282 283 $(".mrm-form-close").on("click", function () { 284 $(this).parent().parent().hide(); 285 var form_id = $(this).attr('form-id') 286 let that = this; 287 288 // For "every_time" frequency, clear session mark so next exit intent can re-trigger 289 var formWrapper = $(this).closest('[data-exit-intent="true"]'); 290 if (formWrapper.length && formWrapper.attr('data-exit-intent-frequency') === 'every_time') { 291 var sessionKey = 'exitIntentShownForms'; 292 var shownForms = JSON.parse(sessionStorage.getItem(sessionKey) || '{}'); 293 delete shownForms[form_id]; 294 sessionStorage.setItem(sessionKey, JSON.stringify(shownForms)); 295 } 296 297 var data = new FormData(); 298 data.append( "form_id", form_id ); 299 data.append( "action", "set_mint_mail_cookie_for_form"); 300 data.append( "wp_nonce", window.MRM_Frontend_Vars.nonce ); // This nonce is created in the following file: /app/Internal/Frontend/FrontendAssets.php 301 302 fetch(window.MRM_Frontend_Vars.rest_api_url+"mint-mail/v1/mint-form-cookie-submit", 303 { 304 method: "POST", 305 body: data 306 }) 307 .then(function(res){ return res.json(); }) 308 .then(function(response){ 309 if( "success" === response.status){ 310 $(that).parent().parent().hide(); 311 }else{ 312 $(that).parent().parent().hide(); 313 } 314 }) 315 }); 316 317 if ($("#mint-google-recaptcha").length > 0) { 318 var recaptch_mint = $('[id="mint-google-recaptcha"]'); 319 if(window.MRM_Frontend_Vars.recaptcha_settings.enable && window.MRM_Frontend_Vars.recaptcha_settings.api_version === "v2_visible"){ 320 recaptch_mint.append(`
vendor: 75 bytes, line 320
320<script src="https://www.google.com/recaptcha/api.js" async defer></script>
320`) 321 } 322 if(window.MRM_Frontend_Vars.recaptcha_settings.enable && window.MRM_Frontend_Vars.recaptcha_settings.api_version === "v2_visible"){ 323 recaptch_mint.append(`<div class="g-recaptcha" data-sitekey="`+window.MRM_Frontend_Vars.recaptcha_settings.v2_visible.site_key+`"></div>`) 324 325 document.addEventListener("DOMContentLoaded", function() { 326 var site_key = window.MRM_Frontend_Vars.recaptcha_settings.v3_invisible.site_key; 327 grecaptcha.ready(function() { 328 grecaptcha.execute(site_key, { action: "homepage" }).then(function(token) { 329 document.getElementById("g-recaptcha-response").value = token; 330 }); 331 }); 332 }); 333 } 334 } 335 336 $(".mint-form-button").on('click',function (){ 337 $(this).parent().parent().find('#mrm-popup').css("display","flex"); 338 }) 339 340 /** 341 * Exit Intent Detection 342 * Triggers popup when user moves mouse towards the top of the page (exit intent) 343 * Supports frequency: once_per_session, once_per_x_days, every_time 344 */ 345 function initExitIntentDetection() { 346 const sessionKey = 'exitIntentShownForms'; 347 348 // Initialize sessionStorage 349 if (!sessionStorage.getItem(sessionKey)) { 350 sessionStorage.setItem(sessionKey, JSON.stringify({})); 351 } 352 353 function hasShownInSession(formId) { 354 const shownForms = JSON.parse(sessionStorage.getItem(sessionKey) || '{}'); 355 return shownForms[formId] === true; 356 } 357 358 function markAsShownInSession(formId) { 359 const shownForms = JSON.parse(sessionStorage.getItem(sessionKey) || '{}'); 360 shownForms[formId] = true; 361 sessionStorage.setItem(sessionKey, JSON.stringify(shownForms)); 362 } 363 364 function setFrequencyCookie(formId, days) { 365 var date = new Date(); 366 date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); 367 document.cookie = 'mrm_exit_intent_' + formId + '=shown; expires=' + date.toUTCString() + '; path=/'; 368 } 369 370 function hasFrequencyCookie(formId) { 371 return document.cookie.split('; ').some(function(row) { 372 return row.startsWith('mrm_exit_intent_' + formId + '='); 373 }); 374 } 375 376 function canShowForm(formWrapper) { 377 var formId = formWrapper.getAttribute('data-form-id'); 378 var frequency = formWrapper.getAttribute('data-exit-intent-frequency') || 'once_per_session'; 379 380 if (frequency === 'every_time') { 381 // For every_time, still prevent multiple triggers within same mouse-exit event 382 // but allow re-trigger on next exit intent 383 return true; 384 } 385 386 if (frequency === 'once_per_x_days') { 387 return !hasFrequencyCookie(formId); 388 } 389 390 // Default: once_per_session 391 return !hasShownInSession(formId); 392 } 393 394 function markFormAsShown(formWrapper) { 395 var formId = formWrapper.getAttribute('data-form-id'); 396 var frequency = formWrapper.getAttribute('data-exit-intent-frequency') || 'once_per_session'; 397 var days = parseInt(formWrapper.getAttribute('data-exit-intent-frequency-days')) || 7; 398 399 // Always mark in session to prevent rapid re-triggers 400 markAsShownInSession(formId); 401 402 if (frequency === 'once_per_x_days') { 403 setFrequencyCookie(formId, days); 404 } 405 } 406 407 function showExitIntentForms() { 408 var exitIntentForms = document.querySelectorAll('[data-exit-intent="true"]'); 409 410 exitIntentForms.forEach(function(formWrapper) { 411 var formId = formWrapper.getAttribute('data-form-id'); 412 if (!formId) return; 413 414 if (!canShowForm(formWrapper)) return; 415 416 // Check for dismiss cookie 417 var cookieExists = document.cookie.split('; ').some(function(row) { 418 return row.startsWith('mrm_form_cookie_' + formId + '='); 419 }); 420 421 if (!cookieExists) { 422 formWrapper.classList.remove('mrm-form-disable'); 423 formWrapper.style.display = 'flex'; 424 markFormAsShown(formWrapper); 425 } 426 }); 427 } 428 429 // Detect when mouse leaves the window from the top (exit intent) 430 document.addEventListener('mouseleave', function(e) { 431 if (e.clientY > 0) return; 432 showExitIntentForms(); 433 }); 434 435 // Alternative: Detect cursor position very close to top 436 let lastClientY = null; 437 const exitThreshold = 0; 438 439 document.addEventListener('mousemove', function(e) { 440 const currentY = e.clientY; 441 442 if (currentY > exitThreshold) { 443 lastClientY = currentY; 444 return; 445 } 446 447 if (lastClientY !== null && currentY < lastClientY) { 448 showExitIntentForms(); 449 } 450 451 lastClientY = currentY; 452 }); 453 } 454 455 // Initialize exit-intent detection when DOM is ready 456 if (document.readyState === 'loading') { 457 document.addEventListener('DOMContentLoaded', initExitIntentDetection); 458 } else { 459 initExitIntentDetection(); 460 } 461 462 463 $('#mrm-unsubscribe-cancel').on('click', function(e) { 464 e.preventDefault(); 465 window.close(); 466 }); 467 468 // Toggle dropdown visibility 469 $('.mintmrm-dropdown-button').on('click', function(e) { 470 e.preventDefault(); 471 $(this).toggleClass('show'); 472 $('.add-contact.mintmrm-dropdown').toggleClass('show'); 473 }); 474 475 // Close dropdown when clicking outside 476 $(document).on('click', function(e) { 477 if (!$(e.target).closest('.mintmrm-dropdown-button, .add-contact.mintmrm-dropdown').length) { 478 $('.mintmrm-dropdown-button').removeClass('show'); 479 $('.add-contact.mintmrm-dropdown').removeClass('show'); 480 } 481 }); 482 483 const updateDropdownButton = () => { 484 const $dropdownButton = $('.mintmrm-dropdown-button'); 485 const $checkboxes = $('.mintmrm-dropdown-list input[type="checkbox"]').not('#all-items-create'); 486 const $checkedCheckboxes = $checkboxes.filter(':checked'); 487 488 $dropdownButton.empty(); 489 if ($checkedCheckboxes.length > 0) { 490 $checkedCheckboxes.each(function() { 491 const title = $(this).next('label').text(); 492 $dropdownButton.append( 493 `<span class="single-list mintmrm-tag-list">${title}
494 <span class="close-list" title="Delete">✕</span> 495 </span>` 496 ); 497 }); 498 } else { 499 $dropdownButton.text('Select Tag'); 500 } 501 }; 502 503 // Handle individual checkbox selection 504 $('.mintmrm-dropdown-list input[type="checkbox"]').not('#all-items-create').on('change', function() { 505 const $allItemsCheckbox = $('#all-items-create'); 506 const $checkboxes = $('.mintmrm-dropdown-list input[type="checkbox"]').not($allItemsCheckbox); 507 const $checkedCheckboxes = $checkboxes.filter(':checked'); 508 509 // Update "Select All Items" checkbox 510 if ($checkedCheckboxes.length === $checkboxes.length) { 511 $allItemsCheckbox.prop('checked', true); 512 } else { 513 $allItemsCheckbox.prop('checked', false); 514 } 515 516 updateDropdownButton(); 517 }); 518 519 // Handle "Select All Items" checkbox 520 $('#all-items-create').on('change', function() { 521 const $checkboxes = $('.mintmrm-dropdown-list input[type="checkbox"]').not(this); 522 $checkboxes.prop('checked', $(this).is(':checked')); 523 updateDropdownButton(); 524 }); 525 526 // Remove item from button when close icon is clicked 527 $(document).on('click', '.close-list', function() { 528 const $checkbox = $(`input[type="checkbox"][value="${$(this).parent().text().trim()}"]`); 529 $checkbox.prop('checked', false).trigger('change'); 530 }); 531 532 // Search functionality on input 533 $('.searchbar.mintmrm-dropdown-list input[type="search"]').on('input', function() { 534 const searchValue = $(this).val().toLowerCase(); 535 const $items = $('.option-section .single-column'); 536 let hasMatch = false; 537 538 $items.each(function() { 539 const itemText = $(this).find('label').text().toLowerCase(); 540 const isMatch = itemText.includes(searchValue); 541 $(this).toggle(isMatch); 542 if (isMatch) { 543 hasMatch = true; 544 } 545 }); 546 547 // Show "No items found" if no matches 548 if (!hasMatch) { 549 if (!$('.no-items-found').length) { 550 $('.option-section').append('<div class="no-items-found">No items found</div>'); 551 } 552 } else { 553 $('.no-items-found').remove(); 554 } 555 }); 556});
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.