1/** 2 * WP-Force Images Download â AJAX Download & Size Picker 3 * 4 * Intercepts form submissions to provide seamless in-page downloads 5 * without opening a new browser tab. Falls back to normal form POST 6 * if JavaScript is unavailable or the AJAX request fails. 7 * 8 * @package WPFID 9 * @since 2.0 10 * @version 2.6 11 */ 12 13/* ========================================================================= 14 * EMAIL GATE HANDLER 15 * Global function â called from inline onsubmit on the email gate form. 16 * Step 1: Save email via AJAX (PHP sets the HMAC cookie in the response). 17 * Step 2: Trigger file download via the same AJAX download endpoint. 18 * ======================================================================= */ 19function wpfidHandleEmailGate( event, form, gateId ) { 20 event.preventDefault(); 21 22 var emailInput = form.querySelector( 'input[name="wpfid_email"]' ); 23 var email = emailInput ? emailInput.value.trim() : ''; 24 25 if ( ! email || ! /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test( email ) ) { 26 if ( emailInput ) { emailInput.style.borderColor = '#e53e3e'; } 27 return false; 28 } 29 30 var submitBtn = form.querySelector( '[type="submit"]' ); 31 if ( submitBtn ) { 32 submitBtn.disabled = true; 33 submitBtn.style.opacity = '0.7'; 34 } 35 36 var ajaxUrl = ( typeof wpfid_ajax !== 'undefined' ) ? wpfid_ajax.ajax_url : '/wp-admin/admin-ajax.php'; 37 var emailNonce = form.querySelector( '[name="_wpfid_email_nonce"]' ); 38 var picUrlInput = form.querySelector( '[name="wpfid_pic_url"]' ); 39 var newNameInput = form.querySelector( '[name="new_name"]' ); 40 var postIdInput = form.querySelector( '[name="wpfid_post_id"]' ); 41 var picNonce = form.querySelector( 'input[name="pic_url_nonce"]' ); 42 var newNonce = form.querySelector( 'input[name="new_name_nonce"]' ); 43 44 /* Step 1: Save e-mail. 45 * credentials:'same-origin' ensures the Set-Cookie from the PHP response 46 * (the HMAC token) is stored so Step 2's request can send it. */ 47 var saveBody = new FormData(); 48 saveBody.append( 'action', 'wpfid_save_email' ); 49 saveBody.append( 'wpfid_email', email );
50 saveBody.append( '_wpfid_email_nonce', emailNonce ? emailNonce.value : '' ); 51 saveBody.append( 'wpfid_post_id', postIdInput ? postIdInput.value : '' ); 52 53 fetch( ajaxUrl, { method: 'POST', body: saveBody, credentials: 'same-origin' } ) 54 .then( function ( res ) { 55 if ( ! res.ok ) { throw new Error( 'Email save failed: ' + res.status ); } 56 return res.json(); 57 } ) 58 .then( function ( json ) { 59 if ( ! json || ! json.success ) { 60 throw new Error( json && json.data && json.data.message ? json.data.message : 'Email save rejected' ); 61 } 62 63 /* Step 2: Download â cookie from Step 1 is now in the browser. */ 64 var dlBody = new FormData(); 65 dlBody.append( 'action', 'wpfid_ajax_download' ); 66 dlBody.append( 'wpfid_pic_url', picUrlInput ? picUrlInput.value : '' ); 67 dlBody.append( 'new_name', newNameInput ? newNameInput.value : '' ); 68 dlBody.append( 'wpfid_post_id', postIdInput ? postIdInput.value : '' ); 69 dlBody.append( 'pic_url_nonce', picNonce ? picNonce.value : '' ); 70 dlBody.append( 'new_name_nonce', newNonce ? newNonce.value : '' ); 71 72 return fetch( ajaxUrl, { method: 'POST', body: dlBody, credentials: 'same-origin' } ); 73 } ) 74 .then( function ( response ) { 75 if ( ! response.ok ) { throw new Error( 'Download failed: ' + response.status ); } 76 77 /* Guard: if server returned an HTML error page instead of binary data */ 78 var ct = response.headers.get( 'Content-Type' ) || ''; 79 if ( ct.indexOf( 'text/html' ) !== -1 ) { 80 throw new Error( 'Server returned an error page instead of a file.' ); 81 } 82 83 /* Extract filename from Content-Disposition header */ 84 var filename = 'download'; 85 var disposition = response.headers.get( 'Content-Disposition' ); 86 if ( disposition ) { 87 var utf8Match = disposition.match( /filename\*=UTF-8''(.+)/i ); 88 if ( utf8Match ) { 89 filename = decodeURIComponent( utf8Match[1].split( ';' )[0].trim() ); 90 } else { 91 var match = disposition.match( /filename[^;=\n]*=["']?([^"';\n]*)["']?/ ); 92 if ( match && match[1] ) { filename = decodeURIComponent( match[1] ); } 93 } 94 } else { 95 var rawUrl = picUrlInput ? picUrlInput.value : ''; 96 if ( rawUrl ) { filename = rawUrl.split( '/' ).pop().split( '?' )[0] || 'download'; } 97 } 98 99 return response.blob().then( function ( blob ) { 100 return { blob: blob, filename: filename }; 101 } ); 102 } ) 103 .then( function ( data ) { 104 var objUrl = window.URL.createObjectURL( data.blob ); 105 var anchor = document.createElement( 'a' ); 106 anchor.href = objUrl; 107 anchor.download = data.filename; 108 anchor.style.display = 'none'; 109 document.body.appendChild( anchor ); 110 anchor.click(); 111 document.body.removeChild( anchor ); 112 window.URL.revokeObjectURL( objUrl ); 113 114 var gate = document.getElementById( gateId ); 115 if ( gate ) { 116 var successMsg = ( typeof wpfid_ajax !== 'undefined' && wpfid_ajax.success_text ) 117 ? wpfid_ajax.success_text 118 : 'Thank you! Your download has started.'; 119 gate.innerHTML = '<p style="color:#2f855a;margin:10px 0;font-size:0.9em;display:flex;align-items:center;gap:6px;">' 120 + '<span class="dashicons dashicons-yes-alt"></span> ' + successMsg + '</p>'; 121 } 122 } ) 123 .catch( function ( err ) { 124 console.warn( 'WPFID email gate error:', err ); 125 if ( submitBtn ) { submitBtn.disabled = false; submitBtn.style.opacity = '1'; } 126 127 var errEl = form.querySelector( '.wpfid-gate-error' ); 128 if ( ! errEl ) { 129 errEl = document.createElement( 'p' ); 130 errEl.className = 'wpfid-gate-error'; 131 errEl.style.cssText = 'color:#c53030;font-size:0.85em;margin:6px 0 0;'; 132 form.appendChild( errEl ); 133 } 134 errEl.textContent = ( typeof wpfid_ajax !== 'undefined' && wpfid_ajax.error_text ) 135 ? wpfid_ajax.error_text 136 : 'Download failed. Please try again.'; 137 } ); 138 139 return false; 140} 141 142/* ========================================================================= 143 * MAIN DOWNLOAD IIFE 144 * Size picker update + AJAX form intercept for the standard download button. 145 * ======================================================================= */ 146(function () { 147 'use strict'; 148 149 document.addEventListener( 'DOMContentLoaded', function () { 150 151 /* ------------------------------------------------------- 152 * 1. Size Picker â update hidden URL field on change 153 * ----------------------------------------------------- */ 154 document.querySelectorAll( '.wpfid-size-picker' ).forEach( function ( select ) { 155 select.addEventListener( 'change', function () { 156 var form = this.closest( 'form' ); 157 if ( ! form ) { return; } 158 var urlInput = form.querySelector( 'input[name="wpfid_pic_url"]' ); 159 if ( urlInput ) { 160 urlInput.value = this.value; 161 } 162 } ); 163 } ); 164 165 /* ------------------------------------------------------- 166 * 2. AJAX Download â intercept .wpfid-download-form submit 167 * ----------------------------------------------------- */ 168 if ( typeof wpfid_ajax === 'undefined' || ! wpfid_ajax.enabled ) { 169 return; /* AJAX disabled â forms submit normally */ 170 } 171 172 document.querySelectorAll( '.wpfid-download-form' ).forEach( function ( form ) { 173 174 /* Named function so we can removeEventListener in the catch fallback. 175 * (arguments.callee is illegal in strict mode.) */ 176 function handleWpfidSubmit( e ) { 177 e.preventDefault(); 178 179 var formData = new FormData( form ); 180 formData.set( 'action', 'wpfid_ajax_download' ); 181 182 var button = form.querySelector( '.wpfid_button, [type="submit"]' ); 183 var titleSpan = button ? button.querySelector( '.wpfid_title' ) : null; 184 var origText = ''; 185 186 if ( button ) { 187 button.disabled = true; 188 button.style.opacity = '0.7'; 189 if ( titleSpan ) { 190 origText = titleSpan.textContent; 191 titleSpan.textContent = wpfid_ajax.downloading_text || 'Downloadingâ¦'; 192 } 193 } 194 195 fetch( wpfid_ajax.ajax_url, { 196 method : 'POST', 197 body : formData, 198 credentials : 'same-origin', 199 } ) 200 .then( function ( response ) { 201 if ( ! response.ok ) { throw new Error( 'HTTP ' + response.status ); } 202 203 /* Guard: catch wp_die() HTML error pages */ 204 var ct = response.headers.get( 'Content-Type' ) || ''; 205 if ( ct.indexOf( 'text/html' ) !== -1 ) { 206 throw new Error( 'Server returned an error page.' ); 207 } 208 209 /* Extract filename from Content-Disposition header */ 210 var filename = 'download'; 211 var disposition = response.headers.get( 'Content-Disposition' ); 212 if ( disposition ) { 213 var utf8Match = disposition.match( /filename\*=UTF-8''(.+)/i ); 214 if ( utf8Match ) { 215 filename = decodeURIComponent( utf8Match[1].split( ';' )[0].trim() ); 216 } else { 217 var match = disposition.match( /filename[^;=\n]*=["']?([^"';
217\n]*)["']?/ ); 218 if ( match && match[1] ) { filename = decodeURIComponent( match[1] ); } 219 } 220 } 221 222 return response.blob().then( function ( blob ) { 223 return { blob: blob, filename: filename }; 224 } ); 225 } ) 226 .then( function ( data ) { 227 var url = window.URL.createObjectURL( data.blob ); 228 var anchor = document.createElement( 'a' ); 229 anchor.href = url; 230 anchor.download = data.filename; 231 anchor.style.display = 'none'; 232 document.body.appendChild( anchor ); 233 anchor.click(); 234 document.body.removeChild( anchor ); 235 window.URL.revokeObjectURL( url ); 236 237 /* Update download count display (if visible on page) */ 238 var container = form.closest( '.wpfid-wrapper' ) || form.parentElement; 239 var countEl = container ? container.querySelector( '.wpfid-download-count' ) : null; 240 if ( countEl ) { 241 var num = ( parseInt( countEl.getAttribute( 'data-count' ), 10 ) || 0 ) + 1; 242 countEl.setAttribute( 'data-count', num ); 243 countEl.textContent = num + ( 1 === num ? ' download' : ' downloads' ); 244 } 245 } ) 246 .catch( function () { 247 /* Fallback: remove interceptor and submit normally (opens in new tab) */ 248 form.removeEventListener( 'submit', handleWpfidSubmit ); 249 form.setAttribute( 'target', '_blank' ); 250 form.setAttribute( 'rel', 'noopener noreferrer' ); 251 form.submit(); 252 } ) 253 .finally( function () { 254 if ( button ) { 255 button.disabled = false; 256 button.style.opacity = '1'; 257 if ( titleSpan && origText ) { 258 titleSpan.textContent = origText; 259 } 260 } 261 } ); 262 } 263 264 form.addEventListener( 'submit', handleWpfidSubmit ); 265 } ); 266 } ); 267 268}());
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.