1(function() { 2 const API_BASE = "https://pitchseo.com/api"; 3 const container = document.getElementById('pitchseo-audit-widget'); 4 if (!container) return; 5 6 const orgId = container.getAttribute('data-org'); 7 if (!orgId) return; 8 9 const injectStyles = (config) => { 10 const style = document.createElement('style'); 11 const brand = config.brand_color || '#3b82f6'; 12 const bg = config.bg_color || '#ffffff'; 13 14 const isBgDark = (c) => { 15 if (!c) return false; 16 const hex = c.replace('#', ''); 17 if (hex.length < 6) return false; 18 const r = parseInt(hex.substring(0, 2), 16); 19 const g = parseInt(hex.substring(2, 4), 16); 20 const b = parseInt(hex.substring(4, 6), 16); 21 return (r * 0.299 + g * 0.587 + b * 0.114) < 128; 22 }; 23 const textColor = isBgDark(bg) ? '#f8fafc' : '#1e293b'; 24 25 style.innerHTML = ` 26 @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap'); 27 28 #ps-widget-wrapper { 29 font-family: 'Inter', -apple-system, sans-serif; 30 width: 100%; 31 max-width: 400px; 32 margin: 0 auto; 33 padding: 28px; 34 border-radius: 24px; 35 background: ${bg} !important; 36 border: 1px solid rgba(255,255,255,0.05) !important; 37 box-shadow: 0 20px 40px rgba(0,0,0,0.1); 38 text-align: center; 39 color: ${textColor} !important; 40 box-sizing: border-box; 41 } 42 .ps-title { font-size: 20px; font-weight: 800; margin-bottom: 6px; letter-spacing: -0.02em; color: inherit; } 43 .ps-desc { font-size: 13px; opacity: 0.7; margin-bottom: 24px; line-height: 1.5; color: inherit; } 44 45 .ps-group { position: relative; margin-bottom: 10px; } 46 .ps-icon { 47 position: absolute; left: 12px; top: 50%; transform: translateY(-50%); 48 width: 15px; height: 15px; opacity: 0.5; pointer-events: none; 49 color: inherit; 50 } 51 .ps-input { 52 width: 100%; padding: 12px 12px 12px 36px; border-radius: 12px; 53 border: 1px solid rgba(255,255,255,0.15); background: rgba(255,255,255,0.05); 54 font-size: 13.5px; box-sizing: border-box; transition: all 0.2s; 55 color: inherit !important; 56 text-align: left; 57 } 58 .ps-input::placeholder { color: inherit; opacity: 0.4; } 59 .ps-input:focus { outline: none; border-color: ${brand}; background: rgba(255,255,255,0.1); box-shadow: 0 0 0 3px ${brand}15; } 60 61 .ps-button { 62 width: 100%; padding: 12px; border-radius: 10px; border: none; 63 background: ${brand}; color: #fff; font-weight: 700; cursor: pointer; font-size: 14px; 64 margin-top: 6px; transition: all 0.2s; 65 } 66 .ps-button:hover { filter: brightness(1.05); transform: translateY(-1px); } 67 68 .ps-footer { font-size: 10px; margin-top: 15px; text-align: center; color: inherit; opacity: 0.4; } 69 .ps-error { font-size: 11px; color: #ef4444; margin-top: 6px; } 70 .ps-trust { 71 font-size: 11px; margin-top: 12px; text-align: center; 72 display: flex; items-center; justify-content: center; gap: 6px; 73 color: inherit; opacity: 0.7; font-weight: 500; 74 } 75 `; 76 document.head.appendChild(style); 77 }; 78 79 const ICONS = { 80 globe: `<svg class="ps-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>`, 81 user: `<svg class="ps-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>`,
82 mail: `<svg class="ps-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>`, 83 phone: `<svg class="ps-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path></svg>`, 84 trend: `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline><polyline points="17 6 23 6 23 12"></polyline></svg>` 85 }; 86 87 function render(config) { 88 const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; 89 const showLogo = config.show_logo == 1; 90 const showTrust = config.show_trust_factor == 1; 91 const count = Math.min(config.audits_today || 0, config.trust_factor_max || 100); 92 93 container.innerHTML = ` 94 <div id="ps-widget-wrapper" class="${isDark ? 'dark' : ''}"> 95 ${showLogo && config.logo_data ? `<img src="${config.logo_data}" style="max-height: 28px; margin-bottom: 12px;" alt="logo">` : ''} 96 <div class="ps-title">${config.form_title}</div> 97 <div class="ps-desc">${config.form_description}</div> 98 99 <form id="ps-audit-form"> 100 <input type="hidden" name="org_id" value="${config.org_id}"> 101 <div class="ps-group"> 102 ${ICONS.globe} 103 <input type="url" name="website_url" class="ps-input" placeholder="Enter Website URL" required autocomplete="off"> 104 </div> 105 106 <div id="ps-lead-fields" style="display: none; opacity: 0; transition: opacity 0.4s ease;"> 107 ${config.show_name != 0 ? `<div class="ps-group">${ICONS.user}<input type="text" name="name" class="ps-input" placeholder="Your Name"></div>` : ''} 108 ${config.show_email != 0 ? `<div class="ps-group">${ICONS.mail}<input type="email" name="email" class="ps-input" placeholder="Work Email"></div>` : ''} 109 ${config.show_phone != 0 ? `<div class="ps-group">${ICONS.phone}<input type="tel" name="phone" class="ps-input" placeholder="Phone Number"></div>` : ''} 110 </div> 111 112 <button type="submit" id="ps-submit-btn" class="ps-button">${config.button_text}</button> 113 ${showTrust ? `<div class="ps-trust">${ICONS.trend} <span>${count} people audited today</span></div>` : ''} 114 <div id="ps-error-msg" class="ps-error"></div> 115 </form> 116 <div class="ps-footer">Powered by PitchSEO</div> 117 </div> 118 `; 119 120 const form = document.getElementById('ps-audit-form'); 121 const urlInput = form.querySelector('input[name="website_url"]'); 122 const leadFields = document.getElementById('ps-lead-fields'); 123 const submitBtn = document.getElementById('ps-submit-btn'); 124 let step = 1; 125 126 urlInput.addEventListener('input', () => { 127 const hasValue = urlInput.value.trim().length > 3; 128 if (hasValue && step === 1) { 129 leadFields.style.display = 'block'; 130 setTimeout(() => leadFields.style.opacity = '1', 10); 131 step = 2; 132 } else if (!hasValue && step === 2) { 133 leadFields.style.opacity = '0'; 134 setTimeout(() => leadFields.style.display = 'none', 400); 135 step = 1; 136 } 137 }); 138 139 form.addEventListener('submit', async (e) => { 140 e.preventDefault(); 141 const url = urlInput.value.trim(); 142 if (url.length <= 3) return urlInput.focus(); 143 144 submitBtn.disabled = true; 145 submitBtn.innerText = "Processing..."; 146 147 const formData = { 148 org_id: config.org_id || orgId, 149 website_url: url, 150 name: form.querySelector('input[name="name"]')?.value || '', 151 email: form.querySelector('input[name="email"]')?.value || '', 152 phone: form.querySelector('input[name="phone"]')?.value || '' 153 }; 154 155 try { 156 const res = await fetch(`${API_BASE}/embed_submit.php`, { 157 method: 'POST', 158 headers: { 'Content-Type': 'application/json' }, 159 body: JSON.stringify(formData) 160 }); 161 const result = await res.json(); 162 if (result.status === 'success') { 163 container.innerHTML = `<div id="ps-widget-wrapper" style="text-align:center; padding: 40px 20px;"> 164 <div style="color:#10b981; font-size:24px; margin-bottom:10px;">â</div> 165 <div style="font-weight:800; font-size:18px;">Audit Started!</div> 166 <p style="font-size:12px; opacity: 0.7;">${config.success_message}</p> 167 </div>`; 168 } else throw new Error(result.message); 169 } catch (err) { 170 document.getElementById('ps-error-msg').innerText = err.message; 171 submitBtn.disabled = false;
172 submitBtn.innerText = config.button_text; 173 } 174 }); 175 } 176 177 async function init() { 178 try { 179 const res = await fetch(`${API_BASE}/embed_config.php?org_id=${orgId}`); 180 const json = await res.json(); 181 if (json.status === 'success') { 182 injectStyles(json.data); 183 render(json.data); 184 } 185 } catch (e) { 186 console.error("PitchSEO: Failed to load config", e); 187 } 188 } 189 init(); 190})();
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.