PageSourceSearch

https://seneweb.com/js/senwww-ads-sdk.js

js seneweb.com collected 2026-10-02 03:00:25 UTC 11,521 bytes, 337 lines download raw bytes

1/**
2 * SenWWW Ads SDK v1.1 - Fixed image_path support
3 * Automatically loads and displays advertisements from www.seneweb.com/ads/
4 */
5class SenWWWAdsSDK {
6    constructor(apiBase = null) {
7        this.apiBase = apiBase || window.location.origin + '/ads';
8        this.debugMode = false;
9        this.observedAds = new Set();
10        
11        // Auto-initialize when DOM is ready
12        if (document.readyState === 'loading') {
13            document.addEventListener('DOMContentLoaded', () => this.autoInit());
14        } else {
15            this.autoInit();
16        }
17    }
18
19    log(...args) {
20        if (this.debugMode) {
21            console.log('[SenWWW Ads]', ...args);
22        }
23    }
24
25    async autoInit() {
26        this.log('Auto-initializing SDK...');
27        const containers = document.querySelectorAll('[data-senwww-ads-zone]');
28        
29        for (const container of containers) {
30            const zoneName = container.getAttribute('data-senwww-ads-zone');
31            const cssClass = container.getAttribute('data-senwww-ads-class') || '';
32            const showEmpty = container.getAttribute('data-senwww-ads-show-empty') !== 'false';
33            const clear = container.getAttribute('data-senwww-ads-clear') !== 'false';
34            const limit = container.getAttribute('data-senwww-ads-limit');
35            
36            await this.renderAds(container, zoneName, { cssClass, showEmpty, clear, limit });
37        }
38    }
39
40    async renderAds(container, zoneName, options = {}) {
41        const { cssClass = '', showEmpty = true, clear = true, limit = null } = options;
42        
43        try {
44            this.log(`Loading ads for zone: ${zoneName}${limit ? ' (limit: ' + limit + ')' : ''}`);
45            
46            const url = `${this.apiBase}/zone/name/${encodeURIComponent(zoneName)}${limit ? '?limit=' + limit : ''}`;
47            const response = await fetch(url);
48            const data = await response.json();
49            
50            if (!data.success) {
51                throw new Error(data.message || 'Failed to load ads');
52            }
53
54            if (clear) {
55                container.innerHTML = '';
56            }
57
58            if (data.ads.length === 0) {
59                if (showEmpty) {
60                    container.innerHTML = '<div class="senwww-ads-empty">No ads available for ' + zoneName + '</div>';
61                }
62                return;
63            }
64
65            // Create ads container
66            const adsContainer = document.createElement('div');
67            adsContainer.className = `senwww-ads-zone ${cssClass}`.trim();
68            
69            data.ads.forEach(ad => {
70                const adElement = this.createAdElement(ad);
71                adsContainer.appendChild(adElement);
72            });
73
74            container.appendChild(adsContainer);
75            
76            // Setup impression tracking
77            this.setupImpressionTracking(container);
78            
79            this.log(`Rendered ${data.ads.length} ads for zone: ${zoneName}`);
80            
81        } catch (error) {
82            this.log('Error loading ads:', error.message);
83            if (showEmpty) {
84                container.innerHTML = '<div class="senwww-ads-error">Failed to load ads: ' + error.message + '</div>';
85            }
86        }
87    }
88
89    createAdElement(ad) {
90        const wrapper = document.createElement('div');
91        wrapper.className = 'senwww-ad';
92        wrapper.setAttribute('data-ad-id', ad.id);
93        wrapper.setAttribute('data-zone-id', ad.zone_id);
94
95        let content;
96        switch (ad.type) {
97            case 'html':
98                content = document.createElement('div');
99                content.className = 'senwww-ad-html';
100                content.innerHTML = ad.content;
101                break;
102                
103            case 'image':
104                content = document.createElement('img');
105                content.className = 'senwww-ad-image';
106                content.src = ad.image_path || ad.content;
107                content.alt = ad.title;
108                content.style.maxWidth = '100%';
109                content.style.height = 'auto';
110                break;
111                
112            case 'text':
113                content = document.createElement('div');
114                content.className = 'senwww-ad-text';
115                content.textContent = ad.content;
116                content.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
117                content.style.color = 'white';
118                content.style.padding = '15px';
119                content.style.borderRadius = '6px';
120                content.style.fontWeight = 'bold';
121                content.style.textAlign = 'center';
122                break;
123                
124            default:
125                content = document.createElement('div');
126                content.textContent = ad.content;
127        }
128
129        if (ad.url) {
130            const link = document.createElement('a');
131            link.className = 'senwww-ad-link';
132            link.href = ad.url;
133            link.target = '_blank';
134            link.rel = 'noopener noreferrer';
135            link.style.textDecoration = 'none';
136            link.style.display = 'block';
137            link.appendChild(content);
138            
139            // Add hover effect
140            link.addEventListener('mouseenter', () => {
141                link.style.opacity = '0.8';
142                link.style.transform = 'translateY(-1px)';
143                link.style.transition = 'all 0.2s ease';
144            });
145            
146            link.addEventListener('mouseleave', () => {
147                link.style.opacity = '1';
148                link.style.transform = 'translateY(0)';
149            });
150            
151            link.addEventListener('click', (e) => {
152                this.trackClick(ad.id, ad.zone_id);
153            });
154            
155            wrapper.appendChild(link);
156        } else {
157            wrapper.appendChild(content);
158        }
159
160        return wrapper;
161    }
162
163    setupImpressionTracking(container) {
164        if (!('IntersectionObserver' in window)) {
165            // Fallback: track immediately if no IntersectionObserver
166            this.trackVisibleAds(container);
167            return;
168        }
169
170        const observer = new IntersectionObserver((entries) => {
171            entries.forEach(entry => {
172                if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
173                    const adId = entry.target.getAttribute('data-ad-id');
174                    const zoneId = entry.target.getAttribute('data-zone-id');
175                    
176                    if (!this.observedAds.has(adId)) {
177                        this.observedAds.add(adId);
178                        this.trackImpression(parseInt(adId), zoneId ? parseInt(zoneId) : null);
179                        observer.unobserve(entry.target);
180                    }
181                }
182            });
183        }, { threshold: 0.5 });
184
185        container.querySelectorAll('.senwww-ad').forEach(ad => {
186            observer.observe(ad);
187        });
188    }
189
190    trackVisibleAds(container) {
191        container.querySelectorAll('.senwww-ad').forEach(ad => {
192            const adId = ad.getAttribute('data-ad-id');
193            const zoneId = ad.getAttribute('data-zone-id');
194            
195            if (!this.observedAds.has(adId)) {
196                this.observedAds.add(adId);
197                this.trackImpression(parseInt(adId), zoneId ? parseInt(zoneId) : null);
198            }
199        });
200    }
201
202    async trackImpression(adId, zoneId = null) {
203        try {
204            this.log(`Tracking impression for ad ${adId}`);
205            
206            const response = await fetch(`${this.apiBase}/track/impression`, {
207                method: 'POST',
208                headers: {
209                    'Content-Type': 'application/json',
210                    'X-Requested-With': 'XMLHttpRequest',
211                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || ''
212                },
213                body: JSON.stringify({
214                    ad_id: adId,
215                    zone_id: zoneId
216                })
217            });
218            
219            if (response.ok) {
220                const data = await response.json();
221                if (data.success) {
222                    this.log(`✓ Impression tracked for ad ${adId}`);
223                }
224            }
225        } catch (error) {
226            this.log('Error tracking impression:', error.message);
227        }
228    }
229
230    async trackClick(adId, zoneId = null) {
231        try {
232            this.log(`Tracking click for ad ${adId}`);
233            
234            const response = await fetch(`${this.apiBase}/track/click`, {
235                method: 'POST',
236                headers: {
237                    'Content-Type': 'application/json',
238                    'X-Requested-With': 'XMLHttpRequest',
239                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || ''
240                },
241                body: JSON.stringify({
242                    ad_id: adId,
243                    zone_id: zoneId,
244                    referrer: document.referrer,
245                    user_agent: navigator.userAgent
246                })
247            });
248            
249            if (response.ok) {
250                const data = await response.json();
251                if (data.success) {
252                    this.log(`✓ Click tracked for ad ${adId}`);
253                }
254            }
255        } catch (error) {
256            this.log('Error tracking click:', error.message);
257        }
258    }
259
260    // Public methods for manual control
261    enableDebug() {
262        this.debugMode = true;
263        this.log('Debug mode enabled');
264    }
265
266    disableDebug() {
267        this.debugMode = false;
268    }
269
270    async refreshZone(zoneName, options = {}) {
271        const containers = document.querySelectorAll(`[data-senwww-ads-zone="${zoneName}"]`);
272        for (const container of containers) {
273            await this.renderAds(container, zoneName, options);
274        }
275    }
276}
277
278// Global instance
279window.senwwwAds = new SenWWWAdsSDK();
280
281// Export for module usage
282if (typeof module !== 'undefined' && module.exports) {
283    module.exports = SenWWWAdsSDK;
284}
285
286// Add some basic CSS styles
287if (!document.querySelector('#senwww-ads-styles')) {
288    const style = document.createElement('style');
289    style.id = 'senwww-ads-styles';
290    style.textContent = `
291        .senwww-ad {
292            margin: 10px 0;
293            transition: all 0.3s ease;
294        }
295        
296        .senwww-ad-link {
297            text-decoration: none;
298            display: block;
299        }
300        
301        .senwww-ad-link:hover {
302            opacity: 0.8;
303            transform: translateY(-1px);
304            transition: all 0.2s ease;
305        }
306        
307        .senwww-ad-image {
308            max-width: 100%;
309            height: auto;
310            border-radius: 4px;
311        }
312        
313        .senwww-ad-html {
314            border-radius: 4px;
315        }
316        
317        .senwww-ads-empty {
318            color: #7f8c8d;
319            font-style: italic;
320            text-align: center;
321            padding: 20px;
322            font-size: 14px;
323        }
324        
325        .senwww-ads-error {
326            color: #e74c3c;
327            text-align: center;
328            padding: 20px;
329            font-size: 14px;
330            background: #ffeaa7;
331            border-radius: 4px;
332        }
333    `;
334    document.head.appendChild(style);
335}
336
337console.log('SenWWW Ads SDK v1.0 loaded successfully');

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.