1/** 2 * Gravity Forms Paywall Modal 3 * 4 * Handles form submission interception and modal display for members-only forms 5 * 6 * @package AutostraddleFeatures 7 * @since 1.0.0 8 */ 9 10(function ($) { 11 "use strict"; 12 13 /** 14 * Initialize paywall functionality 15 */ 16 function init() { 17 // Check if user already has access - if so, no need to block anything 18 if (gfPaywallData.hasAccess) { 19 return; 20 } 21 22 // Find all Gravity Forms on the page with paywall enabled 23 $('form[data-gf-paywall-enabled="1"]').each(function () { 24 var $form = $(this); 25 attachPaywallHandlers($form); 26 }); 27 28 // Set up modal close handlers 29 setupModalHandlers(); 30 } 31 32 /** 33 * Attach paywall handlers to a form 34 * 35 * @param {jQuery} $form Form element 36 */ 37 function attachPaywallHandlers($form) { 38 // Intercept form submission 39 $form.on("submit.gfpaywall", function (e) { 40 // Check if we've already validated 41 if ($form.data("gf-paywall-validated")) { 42 return true; 43 } 44 45 // Only
45intercept if user doesn't have access 46 if (!gfPaywallData.hasAccess) { 47 e.preventDefault(); 48 e.stopImmediatePropagation(); 49 showModal($form); 50 return false; 51 } 52 53 return true; 54 }); 55 56 // Intercept button clicks 57 $form 58 .find('.gform_button, .gform_next_button, input[type="submit"]') 59 .on("click.gfpaywall", function (e) { 60 if (!gfPaywallData.hasAccess && !$form.data("gf-paywall-validated")) { 61 e.preventDefault(); 62 e.stopImmediatePropagation(); 63 showModal($form); 64 return false; 65 } 66 }); 67 } 68 69 /** 70 * Show the paywall modal 71 * 72 * @param {jQuery} $form The form that triggered the modal 73 */ 74 function showModal($form) { 75 var $modal = $("#gf-paywall-modal"); 76 77 if (!$modal.length) { 78 console.error("Paywall modal not found"); 79 return; 80 } 81 82 // Get form name and replace placeholder in modal content 83 var formName = $form.attr("data-gf-paywall-form-name") || "this form"; 84 var contentTemplate = gfPaywallData.modalContentTemplate || ""; 85 var modalContent = contentTemplate.replace(/\{form_name\}/g, formName); 86 87 // Update modal content 88 var $modalContent = $modal.find("#gf-paywall-modal-content"); 89 if ($modalContent.length) { 90 $modalContent.text(modalContent); 91 } 92 93 // Show modal 94 $modal.removeClass("hidden"); 95 96 // Prevent body scroll 97 $("body").addClass("overflow-hidden"); 98 99 // Focus trap 100 trapFocus($modal); 101 102 // Trigger custom event 103 $(document).trigger("gf-paywall-modal-opened", [formName]); 104 } 105 106 /** 107 * Hide the paywall modal 108 */ 109 function hideModal() { 110 var $modal = $("#gf-paywall-modal"); 111 112 // Hide modal 113 $modal.addClass("hidden"); 114 115 // Restore body scroll 116 $("body").removeClass("overflow-hidden"); 117 118 // Trigger custom event 119 $(document).trigger("gf-paywall-modal-closed"); 120 } 121 122 /** 123 * Set up modal event handlers 124 */ 125 function setupModalHandlers() { 126 var $modal = $("#gf-paywall-modal"); 127 128 if (!$modal.length) { 129 return; 130 } 131 132 // Close button 133 $modal.find(".gf-paywall-close").on("click", function (e) { 134 e.preventDefault(); 135 hideModal(); 136 }); 137 138 // Close on overlay click 139 $modal.find(".fixed.inset-0.bg-black").on("click", function (e) { 140 if (e.target === this) { 141 hideModal(); 142 } 143 }); 144 145 // Close on ESC key 146 $(document).on("keydown", function (e) { 147 if (e.key === "Escape" && !$modal.hasClass("hidden")) { 148 hideModal(); 149 } 150 }); 151 } 152 153 /** 154 * Trap focus within modal 155 * 156 * @param {jQuery} $modal Modal element 157 */ 158 function trapFocus($modal) { 159 var focusableElements = $modal.find( 160 'a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])' 161 ); 162 var firstElement = focusableElements.first(); 163 var lastElement = focusableElements.last(); 164 165 // Focus first element 166 firstElement.focus(); 167 168 // Trap focus 169 $modal.off("keydown.focustrap").on("keydown.focustrap", function (e) { 170 if (e.key !== "Tab") { 171 return; 172 } 173 174 if (e.shiftKey) { 175 // Shift + Tab 176 if (document.activeElement === firstElement[0]) { 177 e.preventDefault(); 178 lastElement.focus(); 179 } 180 } else { 181 // Tab 182 if (document.activeElement === lastElement[0]) { 183 e.preventDefault(); 184 firstElement.focus(); 185 } 186 } 187 }); 188 } 189 190 /** 191 * Initialize on document ready 192 */ 193 $(document).ready(function () { 194 init(); 195 }); 196 197 /** 198 * Re-initialize after AJAX content load (for multi-page forms) 199 */ 200 $(document).on("gform_post_render", function (event, formId, currentPage) { 201 // Only re-initialize if user doesn't have access 202 if (gfPaywallData.hasAccess) { 203 return; 204 } 205 206 // Re-attach handlers to the form if it has paywall enabled 207 var $form = $("#gform_" + formId); 208 if ($form.attr("data-gf-paywall-enabled") === "1") { 209 // Remove old handlers first 210 $form.off(".gfpaywall"); 211 $form 212 .find('.gform_button, .gform_next_button, input[type="submit"]') 213 .off(".gfpaywall"); 214 215 // Re-attach 216 attachPaywallHandlers($form); 217 } 218 }); 219})(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.