PageSourceSearch

https://pitchseo.com/seo_audit.js

js pitchseo.com collected 2026-10-02 01:39:22 UTC 10,601 bytes, 190 lines download raw bytes

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.