Stock Watch Pro + WhatsApp Alerts

מעקב מלאי ומחירים עם התראות WhatsApp. תומך ב-Zara, Bershka, H&M, Nike, Amazon, eBay ועוד. כולל סינון, מיון והוספה אוטומטית לעגלה.

Size

151.2 KB

Version

4.1.37

Created

Nov 9, 2025

Updated

24 days ago

1// ==UserScript==
2// @name		Stock Watch Pro + WhatsApp Alerts
3// @description		מעקב מלאי ומחירים עם התראות WhatsApp. תומך ב-Zara, Bershka, H&M, Nike, Amazon, eBay ועוד. כולל סינון, מיון והוספה אוטומטית לעגלה.
4// @version		4.1.37
5// @match		https://web.whatsapp.com/*
6// @match		https://*/*
7// @match		http://*/*
8// @icon		https://www.svgrepo.com/show/331488/price-tag.svg
9// @grant		GM_xmlhttpRequest
10// @grant		GM.xmlHttpRequest
11// @grant		GM.getValue
12// @grant		GM.setValue
13// @grant		GM.openInTab
14// @grant		GM.setClipboard
15// @namespace		io.robomonkey.stockwatch.plus
16// @connect		*
17// ==/UserScript==
18(function() {
19    'use strict';
20
21    // Prevent multiple initializations
22    if (window.__SW_PLUS_BOOTED__) {
23        console.log('[Stock Tracker] Already initialized');
24        return;
25    }
26    window.__SW_PLUS_BOOTED__ = true;
27
28    const STORAGE_KEY = 'sw_plus_items_v4';
29    const PREFS_KEY = 'sw_plus_prefs_v4';
30    
31    // Blocked domains - sites where the extension should NOT run
32    const BLOCKED_DOMAINS = [
33        'google.com', 'gmail.com', 'youtube.com', 'facebook.com', 'twitter.com', 'x.com',
34        'instagram.com', 'linkedin.com', 'reddit.com', 'wikipedia.org', 'github.com',
35        'stackoverflow.com', 'openai.com', 'chatgpt.com', 'chat.openai.com', 'claude.ai',
36        'anthropic.com', 'bing.com', 'yahoo.com', 'docs.google.com', 'drive.google.com',
37        'dropbox.com', 'notion.so', 'slack.com', 'discord.com', 'telegram.org',
38        'messenger.com', 'zoom.us', 'meet.google.com', 'teams.microsoft.com',
39        'office.com', 'microsoft.com', 'apple.com', 'icloud.com', 'netflix.com',
40        'spotify.com', 'twitch.tv', 'tiktok.com', 'pinterest.com', 'tumblr.com',
41        'medium.com', 'wordpress.com', 'blogger.com', 'vimeo.com', 'dailymotion.com',
42        'soundcloud.com', 'news.ycombinator.com', 'producthunt.com'
43    ];
44    
45    // Check if current site is blocked
46    function isBlockedSite() {
47        const host = location.host.toLowerCase();
48        return BLOCKED_DOMAINS.some(domain => host.includes(domain));
49    }
50    
51    // Check if site looks like an e-commerce site
52    function isEcommerceSite() {
53        // Check for common e-commerce indicators in the DOM
54        const indicators = [
55            '[itemprop="price"]', '[class*="price"]', '[data-price]', '[id*="price"]',
56            '[itemprop="product"]', '[class*="product"]', '[data-product]',
57            '[class*="cart"]', '[id*="cart"]', '[class*="basket"]', '[class*="bag"]',
58            'button[class*="add-to-cart"]', 'button[class*="buy"]', 'button[id*="add-to-cart"]',
59            'button[class*="add-to-bag"]', 'button[id*="buy"]',
60            '[class*="size"]', 'select[name*="size"]', '[data-size]',
61            'meta[property="og:type"][content*="product"]', 'meta[name="product"]',
62            '[class*="checkout"]', '[id*="checkout"]',
63            '[class*="wishlist"]', '[class*="favorite"]',
64            'button[class*="purchase"]', 'button[id*="purchase"]',
65            '[data-testid*="product"]', '[data-testid*="price"]', '[data-testid*="cart"]'
66        ];
67        
68        // Check if any indicator exists
69        for (const selector of indicators) {
70            if (document.querySelector(selector)) {
71                console.log('[Stock Tracker] E-commerce indicator found:', selector);
72                return true;
73            }
74        }
75        
76        // Check URL patterns
77        const url = location.href.toLowerCase();
78        const ecommercePatterns = [
79            '/product/', '/item/', '/p/', '/dp/', '/pd/', '/products/', '/items/',
80            'product-', 'item-', '/shop/', '/store/', '/buy/', '/cart/',
81            '/checkout/', 'product_id', 'item_id', 'sku=', '/catalog/',
82            '/merchandise/', '/goods/', '/article/'
83        ];
84        
85        for (const pattern of ecommercePatterns) {
86            if (url.includes(pattern)) {
87                console.log('[Stock Tracker] E-commerce URL pattern found:', pattern);
88                return true;
89            }
90        }
91        
92        // Check for common Israeli e-commerce domains
93        const host = location.host.toLowerCase();
94        const israeliEcommerce = [
95            'ksp.co.il', 'ivory.co.il', 'zap.co.il', 'bug.co.il', 'terminal-x.com',
96            'factory54.co.il', 'golf.co.il', 'castro.com', 'fox.co.il', 'hoodies.co.il',
97            'twentyfourseven.co.il', 'americaneagle.co.il', 'adidas.co.il', 'nike.co.il',
98            'renuar.co.il', 'mashlanu.co.il', 'shoez.co.il', 'delta.co.il', 'naama-bezalel.com',
99            'shilav.co.il', 'urbanica.co.il', 'mango.co.il', 'hmisrael.co.il'
100        ];
101        
102        for (const domain of israeliEcommerce) {
103            if (host.includes(domain)) {
104                console.log('[Stock Tracker] Israeli e-commerce site detected:', domain);
105                return true;
106            }
107        }
108        
109        // Check for common global e-commerce domains
110        const globalEcommerce = [
111            'amazon.', 'ebay.', 'aliexpress.', 'etsy.', 'walmart.', 'target.com',
112            'bestbuy.', 'newegg.', 'wayfair.', 'overstock.', 'zappos.',
113            'asos.', 'shein.', 'boohoo.', 'prettylittlething.', 'fashionnova.',
114            'zara.', 'hm.', 'uniqlo.', 'gap.', 'oldnavy.', 'bananarepublic.',
115            'nike.', 'adidas.', 'puma.', 'reebok.', 'underarmour.',
116            'macys.', 'nordstrom.', 'bloomingdales.', 'saksfifthavenue.',
117            'shopify.', 'bigcartel.', 'wix.com/stores', 'squarespace.com/commerce'
118        ];
119        
120        for (const domain of globalEcommerce) {
121            if (host.includes(domain)) {
122                console.log('[Stock Tracker] Global e-commerce site detected:', domain);
123                return true;
124            }
125        }
126        
127        console.log('[Stock Tracker] Not detected as e-commerce site');
128        return false;
129    }
130
131    // Storage helpers
132    const load = async (key, defaultValue) => {
133        try {
134            const val = await GM.getValue(key);
135            return val !== undefined ? JSON.parse(val) : defaultValue;
136        } catch(err) {
137            console.error('[Stock Tracker] Load error:', err);
138            return defaultValue;
139        }
140    };
141
142    const save = async (key, value) => {
143        try {
144            await GM.setValue(key, JSON.stringify(value));
145        } catch(err) {
146            console.error('[Stock Tracker] Save error:', err);
147        }
148    };
149
150    const getItems = () => load(STORAGE_KEY, []);
151    const saveItems = (items) => save(STORAGE_KEY, items);
152    const getPrefs = () => load(PREFS_KEY, { waPhone: '', checkIntervalMin: 5, throttleMinutesWA: 3, notifications: true, soundAlerts: true, autoAddToCart: true });
153    const savePrefs = (prefs) => save(PREFS_KEY, prefs);
154
155    // Countdown timer state
156    let nextCheckTime = null;
157    let countdownInterval = null;
158
159    // Time formatting helpers
160    function formatTimeAgo(timestamp) {
161        const now = Date.now();
162        const diff = now - timestamp;
163        const minutes = Math.floor(diff / 60000);
164        const hours = Math.floor(diff / 3600000);
165        const days = Math.floor(diff / 86400000);
166        
167        if (minutes < 1) return 'עכשיו';
168        if (minutes < 60) return `לפני ${minutes} דקות`;
169        if (hours < 24) return `לפני ${hours} שעות`;
170        return `לפני ${days} ימים`;
171    }
172
173    // Parse price from text
174    function parsePrice(val) {
175        if (val == null) return null;
176        if (typeof val === 'number') return val;
177        let str = String(val);
178        str = str.replace(/[^\d.,\-]/g, '').trim();
179
180        const hasDot = str.includes('.');
181        const hasComma = str.includes(',');
182        if (hasDot && hasComma) {
183            if (str.lastIndexOf(',') < str.lastIndexOf('.')) {
184                str = str.replace(/,/g, '');
185            } else {
186                str = str.replace(/\./g, '').replace(',', '.');
187            }
188        } else {
189            if (hasComma && !hasDot) str = str.replace(',', '.');
190        }
191
192        const num = parseFloat(str);
193        return isNaN(num) ? null : num;
194    }
195    
196    // Normalize size label for comparison
197    function normalizeSizeLabel(s) {
198        return String(s || '')
199            .toLowerCase()
200            .replace(/size|מידה|talla|größe|taglia|taille|尺码/gi, '')
201            .replace(/eu|uk|us|it|fr|de|men|women|unisex/gi, '')
202            .replace(/[()]/g, '')
203            .replace(/\s*\/\s*/g, ' ')
204            .replace(/\s+/g, ' ')
205            .trim();
206    }
207    
208    // Generate size label variants for flexible matching
209    function labelVariants(s) {
210        const n = normalizeSizeLabel(s);
211        const toks = n.split(' ').filter(Boolean);
212        
213        const variants = new Set([n]);
214        if (toks.length >= 2) {
215            variants.add(toks.find(t => /^[xsml]{1,3}$/.test(t)) || n);
216            const num = toks.find(t => /^\d{2,3}$/.test(t));
217            if (num) variants.add(num);
218        }
219        variants.add(n.replace(/\s+/g, ''));
220        
221        return Array.from(variants).filter(Boolean);
222    }
223    
224    // Check if two size labels match (flexible comparison)
225    function sizeMatches(jsonSize, savedLabel) {
226        const A = labelVariants(jsonSize);
227        const B = labelVariants(savedLabel);
228        return A.some(a => B.some(b => a === b || a.includes(b) || b.includes(a)));
229    }
230
231    // Extract site name from URL
232    function getSiteName(url) {
233        try {
234            const hostname = new URL(url).hostname;
235            const domain = hostname.replace(/^www\d?\./, '');
236            
237            const siteNames = {
238                'zara.com': 'Zara', 'bershka.com': 'Bershka', 'hm.com': 'H&M', 'nike.com': 'Nike',
239                'amazon.com': 'Amazon', 'amazon.co.uk': 'Amazon UK', 'amazon.de': 'Amazon DE',
240                'ebay.com': 'eBay', 'aliexpress.com': 'AliExpress', 'asos.com': 'ASOS',
241                'shein.com': 'SHEIN', 'mango.com': 'Mango', 'pullandbear.com': 'Pull&Bear',
242                'stradivarius.com': 'Stradivarius', 'massimodutti.com': 'Massimo Dutti',
243                'gap.com': 'Gap', 'uniqlo.com': 'Uniqlo', 'adidas.com': 'Adidas', 'puma.com': 'Puma'
244            };
245            
246            for (const [key, name] of Object.entries(siteNames)) {
247                if (domain.includes(key)) return name;
248            }
249            
250            const mainDomain = domain.split('.')[0];
251            return mainDomain.charAt(0).toUpperCase() + mainDomain.slice(1);
252        } catch {
253            return 'Unknown';
254        }
255    }
256
257    // Play notification sound
258    function playNotificationSound() {
259        try {
260            const audioContext = new (window.AudioContext || window.webkitAudioContext)();
261            const oscillator = audioContext.createOscillator();
262            const gainNode = audioContext.createGain();
263            
264            oscillator.connect(gainNode);
265            gainNode.connect(audioContext.destination);
266            
267            oscillator.frequency.value = 800;
268            oscillator.type = 'sine';
269            
270            gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
271            gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
272            
273            oscillator.start(audioContext.currentTime);
274            oscillator.stop(audioContext.currentTime + 0.5);
275        } catch (err) {
276            console.error('[Stock Tracker] Sound error:', err);
277        }
278    }
279
280    // Update FAB badge
281    async function updateFabBadge() {
282        const items = await getItems();
283        const fab = document.querySelector('.st-fab');
284        if (!fab) return;
285        
286        const outOfStockCount = items.reduce((sum, item) => {
287            if (item.mode === 'stock') return sum + item.sizes.filter(s => !s.inStock).length;
288            return sum;
289        }, 0);
290        
291        const oldBadge = fab.querySelector('.st-badge');
292        if (oldBadge) oldBadge.remove();
293        
294        if (outOfStockCount > 0) {
295            const badge = document.createElement('span');
296            badge.className = 'st-badge';
297            badge.textContent = outOfStockCount;
298            fab.appendChild(badge);
299            fab.classList.add('st-fab-alert');
300        } else {
301            fab.classList.remove('st-fab-alert');
302        }
303    }
304
305    // Universal site adapters
306    const adapters = {
307        'zara.com': {
308            mode: 'stock',
309            title: () => {
310                const titleEl = document.querySelector('h1.product-detail-info__header-name, .product-detail-info__header-name, h1[class*="product"]');
311                return titleEl?.textContent?.trim() || document.querySelector('h1')?.textContent?.trim();
312            },
313            price: () => {
314                const priceEl = document.querySelector('[data-qa*="price"], .money-amount, .price__amount, .current-price-elem');
315                return parsePrice(priceEl?.textContent);
316            },
317            image: () => {
318                return document.querySelector('.product-detail-images img, picture img, .media-image__image, img[class*="product"]')?.src ||
319                       document.querySelector('main img')?.src;
320            },
321            getSizes: () => {
322                const addBtn = document.querySelector('button[data-qa-action="add-to-cart"]');
323                if (addBtn) addBtn.click();
324                
325                return new Promise(resolve => {
326                    setTimeout(() => {
327                        const sizeElements = document.querySelectorAll('li.size-selector-sizes__size, .product-size-info__main-label');
328                        const sizes = Array.from(sizeElements).map(li => {
329                            const labelEl = li.querySelector('.size-selector-sizes-size__label') || li;
330                            const label = labelEl?.textContent?.trim().replace(/coming\s?soon/gi, '').trim();
331                            
332                            // Check if explicitly disabled/unavailable
333                            const isExplicitlyDisabled = li.classList.contains('size-selector-sizes-size--disabled') ||
334                                               li.classList.contains('size-selector-sizes-size--unavailable') ||
335                                               li.textContent?.toLowerCase().includes('coming soon');
336                            
337                            // If has --enabled class and NOT disabled, it's in stock
338                            const isInStock = li.classList.contains('size-selector-sizes-size--enabled') && !isExplicitlyDisabled;
339                            
340                            console.log(`[Stock Tracker] Zara size ${label}: enabled=${li.classList.contains('size-selector-sizes-size--enabled')}, disabled=${isExplicitlyDisabled}, inStock=${isInStock}`);
341                            
342                            return { label, element: li, inStock: isInStock };
343                        }).filter(s => s.label && s.label.length <= 12);
344                        
345                        console.log('[Stock Tracker] Zara found sizes:', sizes.length, sizes.map(s => `${s.label}: ${s.inStock ? 'IN' : 'OUT'}`));
346                        resolve(sizes);
347                    }, 1500);
348                });
349            }
350        },
351        
352        'bershka.com': {
353            mode: 'stock',
354            title: () => {
355                const titleEl = document.querySelector('h1.product-detail-info-layout__title, .product-detail-info-layout__title');
356                return titleEl?.textContent?.trim();
357            },
358            price: () => {
359                const priceEl = document.querySelector('[data-qa-anchor="productItemPrice"], .current-price-elem, .price-elem');
360                return parsePrice(priceEl?.textContent);
361            },
362            image: () => {
363                return document.querySelector('img[class*="product-detail"], img[class*="media-image"], picture img, main img')?.src;
364            },
365            getSizes: () => {
366                return new Promise(resolve => {
367                    setTimeout(() => {
368                        const sizeElements = document.querySelectorAll('button[data-qa-anchor="sizeListItem"], .ui--dot-item, button[aria-label*="מידה"]');
369                        const sizes = Array.from(sizeElements).map(btn => {
370                            const labelEl = btn.querySelector('.text__label') || btn;
371                            const label = labelEl?.textContent?.trim() || btn.getAttribute('aria-label')?.replace('מידה ', '').trim();
372                            const isOutOfStock = btn.disabled || 
373                                               btn.classList.contains('is-disabled') ||
374                                               btn.classList.contains('is-unavailable') ||
375                                               btn.getAttribute('aria-disabled') === 'true';
376                            
377                            return { label, element: btn, inStock: !isOutOfStock };
378                        }).filter(s => s.label && s.label.length <= 12);
379                        
380                        console.log('[Stock Tracker] Bershka found sizes:', sizes.length);
381                        resolve(sizes);
382                    }, 1000);
383                });
384            }
385        },
386        
387        'hm.com': {
388            mode: 'stock',
389            title: () => {
390                const titleEl = document.querySelector('h1.ProductName-module--productTitle, .ProductName-module--productTitle, h1[class*="product"]');
391                return titleEl?.textContent?.trim() || document.querySelector('h1')?.textContent?.trim();
392            },
393            price: () => {
394                const priceEl = document.querySelector('.ProductPrice-module--currentPrice, [data-testid="price"], .price');
395                return parsePrice(priceEl?.textContent);
396            },
397            image: () => {
398                return document.querySelector('.ProductImages-module--image img, figure img, img[class*="product"]')?.src ||
399                       document.querySelector('main img')?.src;
400            },
401            getSizes: () => {
402                return new Promise(resolve => {
403                    setTimeout(() => {
404                        const sizeElements = document.querySelectorAll('div[data-testid^="sizeButton-"], div[role="radio"][id^="sizeButton"]');
405                        const sizes = Array.from(sizeElements).map(div => {
406                            const label = div.querySelector('div')?.textContent?.trim().replace(/\s+/g, ' ').trim();
407                            
408                            const ariaLabel = div.getAttribute('aria-label') || '';
409                            const isOutOfStock = ariaLabel.toLowerCase().includes('není skladem') || 
410                                               ariaLabel.toLowerCase().includes('out of stock') ||
411                                               ariaLabel.toLowerCase().includes('not available') ||
412                                               div.classList.contains('cf813c') ||
413                                               div.querySelector('.da0c1b');
414                            
415                            return { label, element: div, inStock: !isOutOfStock };
416                        }).filter(s => s.label && s.label.length <= 12);
417                        
418                        console.log('[Stock Tracker] H&M found sizes:', sizes.length);
419                        resolve(sizes);
420                    }, 1000);
421                });
422            }
423        },
424
425        'nike.com': {
426            mode: 'stock',
427            title: () => {
428                const titleEl = document.querySelector('h1#pdp_product_title, #pdp_product_title, h1[class*="product"]');
429                return titleEl?.textContent?.trim() || document.querySelector('h1')?.textContent?.trim();
430            },
431            price: () => {
432                const priceEl = document.querySelector('[data-test="product-price"], .product-price, [class*="price"]');
433                return parsePrice(priceEl?.textContent);
434            },
435            image: () => {
436                return document.querySelector('.product-image img, picture img, img[class*="product"]')?.src ||
437                       document.querySelector('main img')?.src;
438            },
439            getSizes: () => {
440                return new Promise(resolve => {
441                    setTimeout(() => {
442                        const sizeElements = document.querySelectorAll('[data-qa="size-available"], .size-grid-button, button[class*="size"]');
443                        const sizes = Array.from(sizeElements).map(btn => {
444                            const label = btn.textContent?.trim();
445                            const isOutOfStock = btn.disabled || btn.classList.contains('unavailable');
446                            return { label, element: btn, inStock: !isOutOfStock };
447                        }).filter(s => s.label && s.label.length <= 12);
448                        
449                        console.log('[Stock Tracker] Nike found sizes:', sizes.length);
450                        resolve(sizes);
451                    }, 1000);
452                });
453            }
454        },
455
456        'amazon.': {
457            mode: 'stock',
458            title: () => {
459                const titleEl = document.querySelector('#productTitle, h1.product-title, h1[id*="product"]');
460                return titleEl?.textContent?.trim() || document.querySelector('h1')?.textContent?.trim();
461            },
462            price: () => {
463                const priceEl = document.querySelector('.a-price .a-offscreen, #priceblock_ourprice, .a-price-whole, [class*="price"]');
464                return parsePrice(priceEl?.textContent);
465            },
466            image: () => {
467                return document.querySelector('#landingImage, #imgBlkFront, img[class*="product"]')?.src ||
468                       document.querySelector('main img')?.src;
469            },
470            getSizes: () => {
471                return new Promise(resolve => {
472                    setTimeout(() => {
473                        // Check if product is out of stock or unavailable
474                        const outOfStock = document.querySelector('#outOfStock, #availability .a-color-price, #availability .a-color-state');
475                        if (outOfStock) {
476                            const text = outOfStock.textContent?.toLowerCase() || '';
477                            if (text.includes('out of stock') || 
478                                text.includes('unavailable') || 
479                                text.includes('cannot be dispatched') ||
480                                text.includes('not available')) {
481                                console.log('[Stock Tracker] Amazon - Product unavailable:', text.substring(0, 100));
482                                resolve([{ label: 'OS', element: document.body, inStock: false }]);
483                                return;
484                            }
485                        }
486                        
487                        // First check for size dropdown
488                        const sizeSelect = document.querySelector('#native_dropdown_selected_size_name, select[name*="size"], select[id*="size"]');
489                        if (sizeSelect) {
490                            const sizeElements = sizeSelect.querySelectorAll('option');
491                            const sizes = Array.from(sizeElements).map(opt => {
492                                const label = opt.textContent?.trim();
493                                const isOutOfStock = opt.disabled || label.toLowerCase().includes('out of stock');
494                                return { label, element: opt, inStock: !isOutOfStock };
495                            }).filter(s => s.label && !s.label.toLowerCase().includes('select') && s.label.length <= 30);
496                            
497                            if (sizes.length > 0) {
498                                console.log('[Stock Tracker] Amazon found sizes:', sizes.length);
499                                resolve(sizes);
500                                return;
501                            }
502                        }
503                        
504                        // Check for variation dropdowns (color, style, etc)
505                        const variationSelects = document.querySelectorAll('select[name*="dropdown"], select[id*="native"]');
506                        for (const select of variationSelects) {
507                            const options = Array.from(select.querySelectorAll('option')).slice(1); // Skip first "Select" option
508                            if (options.length > 0 && options.length < 50) {
509                                const sizes = options.map(opt => {
510                                    const label = opt.textContent?.trim();
511                                    const isOutOfStock = opt.disabled || label.toLowerCase().includes('out of stock');
512                                    return { label, element: opt, inStock: !isOutOfStock };
513                                }).filter(s => s.label && !s.label.toLowerCase().includes('select') && s.label.length <= 50);
514                                
515                                if (sizes.length > 0) {
516                                    console.log('[Stock Tracker] Amazon found variation options:', sizes.length);
517                                    resolve(sizes);
518                                    return;
519                                }
520                            }
521                        }
522                        
523                        // No variations found - check if product is available
524                        const addToCartBtn = document.querySelector('#add-to-cart-button, input[id="add-to-cart-button"], button[id*="add-to-cart"]');
525                        const isAvailable = addToCartBtn && !addToCartBtn.disabled;
526                        
527                        console.log('[Stock Tracker] Amazon - No variations, single product. Available:', isAvailable);
528                        resolve([{ label: 'OS', element: document.body, inStock: isAvailable }]);
529                    }, 1000);
530                });
531            }
532        },
533
534        'ebay.': {
535            mode: 'stock',
536            title: () => {
537                const titleEl = document.querySelector('h1.x-item-title__mainTitle, h1[class*="title"]');
538                return titleEl?.textContent?.trim();
539            },
540            price: () => {
541                const priceEl = document.querySelector('.x-price-primary, .x-price-approx, [itemprop="price"]');
542                return parsePrice(priceEl?.textContent);
543            },
544            image: () => {
545                return document.querySelector('.ux-image-carousel-item img, [id*="icImg"]')?.src;
546            },
547            getSizes: () => {
548                return new Promise(resolve => {
549                    setTimeout(() => {
550                        const select = document.querySelector('select[aria-label*="Select"], select[class*="msku"]');
551                        if (select) {
552                            const options = [...select.querySelectorAll('option')].slice(1);
553                            const sizes = options.map(opt => {
554                                const label = opt.textContent?.trim();
555                                const disabled = opt.disabled;
556                                return label && label.length <= 30 ? { label, element: opt, inStock: !disabled } : null;
557                            }).filter(Boolean);
558                            resolve(sizes);
559                        } else {
560                            const disabled = !!document.querySelector('.ux-action .btn[disabled], .vi-acc-del-range');
561                            resolve([{ label: 'OS', element: document.body, inStock: !disabled }]);
562                        }
563                    }, 1000);
564                });
565            }
566        },
567
568        'ksp.co.il': {
569            mode: 'stock',
570            title: () => document.querySelector('h1, .product-title')?.textContent?.trim() || document.title,
571            price: () => parsePrice(document.querySelector('[itemprop="price"], .product-price, .price')?.textContent),
572            image: () => document.querySelector('img[itemprop="image"], .product-image img, picture img')?.src,
573            getSizes: () => {
574                const disabled = !!document.querySelector('button[disabled][id*="add"], button[disabled][class*="add"], .sold-out');
575                return Promise.resolve([{ label: 'OS', element: document.body, inStock: !disabled }]);
576            }
577        },
578
579        'ivory.co.il': {
580            mode: 'stock',
581            title: () => document.querySelector('h1, .product-name')?.textContent?.trim() || document.title,
582            price: () => parsePrice(document.querySelector('[itemprop="price"], .price, .product-price')?.textContent),
583            image: () => document.querySelector('img[itemprop="image"], .product-image img')?.src,
584            getSizes: () => {
585                const disabled = !!document.querySelector('button[disabled][class*="add"], .out-of-stock');
586                return Promise.resolve([{ label: 'OS', element: document.body, inStock: !disabled }]);
587            }
588        },
589
590        'gap.': {
591            mode: 'stock',
592            title: () => {
593                const titleEl = document.querySelector('h1, [data-testid*="product"] h1, .product-title');
594                return titleEl?.textContent?.trim() || document.title;
595            },
596            price: () => {
597                const priceEl = document.querySelector('span[data-testid="sf7"], [data-testid*="price"] span, .price, [class*="price"]');
598                return parsePrice(priceEl?.textContent);
599            },
600            image: () => {
601                return document.querySelector('img[alt*="product"], picture img, main img')?.src;
602            },
603            getSizes: () => {
604                return new Promise(resolve => {
605                    setTimeout(() => {
606                        // GAP uses links with role="radio" for sizes
607                        const sizeLinks = document.querySelectorAll('a[role="radio"][href*="selectedVariant"], a[role="radio"][href*="size"]');
608                        
609                        if (sizeLinks.length > 0) {
610                            const sizes = Array.from(sizeLinks).map(link => {
611                                const label = link.querySelector('span')?.textContent?.trim() || link.textContent?.trim();
612                                const isOutOfStock = link.classList.contains('disabled') || 
613                                                   link.classList.contains('unavailable') ||
614                                                   link.getAttribute('aria-disabled') === 'true';
615                                
616                                return { label, element: link, inStock: !isOutOfStock };
617                            }).filter(s => s.label && s.label.length <= 12);
618                            
619                            console.log('[Stock Tracker] GAP found sizes:', sizes.length);
620                            resolve(sizes);
621                        } else {
622                            // Fallback to buttons
623                            const sizeButtons = document.querySelectorAll('button[class*="size"], button[data-testid*="size"]');
624                            const sizes = Array.from(sizeButtons).map(btn => {
625                                const label = btn.textContent?.trim();
626                                const isOutOfStock = btn.disabled || btn.classList.contains('unavailable');
627                                return { label, element: btn, inStock: !isOutOfStock };
628                            }).filter(s => s.label && s.label.length <= 12);
629                            
630                            console.log('[Stock Tracker] GAP found sizes (fallback):', sizes.length);
631                            resolve(sizes);
632                        }
633                    }, 1000);
634                });
635            }
636        },
637
638        'skyscanner.': {
639            mode: 'price',
640            title: () => document.querySelector('[data-test-id="search-summary"]')?.textContent?.trim() || document.title,
641            price: () => parsePrice(document.querySelector('[data-test-id*="price"], [class*="price"]')?.textContent),
642            image: () => null
643        },
644
645        'generic': {
646            mode: 'stock',
647            title: () => {
648                const selectors = [
649                    'h1[class*="product"][class*="title"]', 'h1[class*="product"][class*="name"]',
650                    'h1.product-detail-info-layout__title', '.product-detail-info-layout__title',
651                    'h1[class*="product"]', '[itemprop="name"]', '.product-title', '.product-name',
652                    '[data-testid*="product"][data-testid*="title"]', 'h1'
653                ];
654                
655                for (const selector of selectors) {
656                    const el = document.querySelector(selector);
657                    const text = el?.textContent?.trim();
658                    if (text && text.length > 0 && text.length < 200) return text;
659                }
660                
661                return document.title;
662            },
663            price: () => {
664                const priceSelectors = [
665                    '[data-qa-anchor="productItemPrice"]', '.current-price-elem', '[itemprop="price"]',
666                    '[class*="price"]:not([class*="strike"]):not([class*="old"]):not([class*="original"])',
667                    '[data-price]', '[data-testid*="price"]', '[id*="price"]',
668                    'span[class*="current"]', 'div[class*="current"]', '.product-price', '.price'
669                ];
670                
671                for (const selector of priceSelectors) {
672                    const elements = document.querySelectorAll(selector);
673                    for (const el of elements) {
674                        const price = parsePrice(el.textContent || el.getAttribute('content') || el.getAttribute('data-price'));
675                        if (price && price > 0) return price;
676                    }
677                }
678                return null;
679            },
680            image: () => {
681                const imageSelectors = [
682                    'img[class*="product-detail"]', 'img[class*="media-image"]', '[itemprop="image"]',
683                    '.product-image img', '.product-img img', 'img[class*="product"]',
684                    '[data-testid*="product"] img', 'picture img', 'main img', 'article img'
685                ];
686                
687                for (const selector of imageSelectors) {
688                    const img = document.querySelector(selector);
689                    if (img?.src && !img.src.includes('data:image')) return img.src;
690                }
691                return null;
692            },
693            getSizes: () => {
694                return new Promise(resolve => {
695                    setTimeout(() => {
696                        // First try to find size links (like GAP)
697                        const sizeLinks = document.querySelectorAll(
698                            'a[role="radio"][href*="size"], a[role="radio"][href*="variant"], ' +
699                            'a[role="radio"][href*="Size"], a[role="radio"][href*="Variant"]'
700                        );
701                        
702                        if (sizeLinks.length > 0) {
703                            const sizes = Array.from(sizeLinks).map(link => {
704                                const label = link.querySelector('span')?.textContent?.trim() || link.textContent?.trim();
705                                const isOutOfStock = link.classList.contains('disabled') || 
706                                                   link.classList.contains('unavailable') ||
707                                                   link.getAttribute('aria-disabled') === 'true';
708                                
709                                return { label, element: link, inStock: !isOutOfStock };
710                            }).filter(s => s.label && s.label.length > 0 && s.label.length <= 20);
711                            
712                            if (sizes.length > 0) {
713                                console.log('[Stock Tracker] Found size links:', sizes.length);
714                                resolve(sizes);
715                                return;
716                            }
717                        }
718                        
719                        // Then try buttons and other elements
720                        const sizeElements = document.querySelectorAll(
721                            'button[data-qa-anchor="sizeListItem"], ' +
722                            'button[class*="size"]:not([class*="guide"]):not([class*="help"]), ' +
723                            'button[aria-label*="מידה"], button[aria-label*="size"], ' +
724                            '.ui--dot-item, .size-option, [data-size], ' +
725                            '[data-testid*="size"]:not([data-testid*="guide"]), ' +
726                            'select[name*="size"] option, select[id*="size"] option, ' +
727                            'li[class*="size"]:not([class*="guide"]), ' +
728                            'div[class*="size"][role="button"], span[class*="size"][role="button"]'
729                        );
730                        
731                        const sizes = Array.from(sizeElements).map(el => {
732                            let label = el.textContent?.trim() || el.getAttribute('data-size') || el.value;
733                            
734                            if (!label || label.length > 20) {
735                                const ariaLabel = el.getAttribute('aria-label');
736                                if (ariaLabel) {
737                                    label = ariaLabel.replace(/מידה\s*/gi, '').replace(/size\s*/gi, '').trim();
738                                }
739                            }
740                            
741                            if (label) label = label.replace(/\s+/g, ' ').trim();
742                            
743                            const isOutOfStock = el.disabled || 
744                                               el.classList.contains('disabled') ||
745                                               el.classList.contains('unavailable') ||
746                                               el.classList.contains('out-of-stock') ||
747                                               el.classList.contains('sold-out') ||
748                                               el.classList.contains('is-disabled') ||
749                                               el.classList.contains('is-unavailable') ||
750                                               el.getAttribute('aria-disabled') === 'true' ||
751                                               el.textContent?.toLowerCase().includes('out of stock') ||
752                                               el.textContent?.toLowerCase().includes('sold out') ||
753                                               el.textContent?.toLowerCase().includes('coming soon') ||
754                                               el.textContent?.toLowerCase().includes('unavailable');
755                            return { label, element: el, inStock: !isOutOfStock };
756                        }).filter(s => s.label && s.label.length > 0 && s.label.length <= 20 && 
757                                     !s.label.toLowerCase().includes('select') && 
758                                     !s.label.toLowerCase().includes('choose') && 
759                                     !s.label.toLowerCase().includes('guide'));
760                        
761                        if (sizes.length === 0) {
762                            const disabled = !!document.querySelector('.out-of-stock, .sold-out, button[disabled]');
763                            resolve([{ label: 'OS', element: document.body, inStock: !disabled }]);
764                        } else {
765                            resolve(sizes);
766                        }
767                    }, 1000);
768                });
769            }
770        }
771    };
772
773    const getAdapter = () => {
774        const host = location.host.replace('www.', '').replace('www2.', '');
775        
776        for (const [domain, adapter] of Object.entries(adapters)) {
777            if (domain !== 'generic' && host.includes(domain)) {
778                return adapter;
779            }
780        }
781        
782        return adapters.generic;
783    };
784
785    // CSS selector builder
786    function buildSelector(node) {
787        if (!node) return '';
788        if (node.id) return `#${CSS.escape(node.id)}`;
789        
790        const parts = [];
791        let el = node;
792        let depth = 0;
793        
794        while (el && el.nodeType === 1 && depth < 5) {
795            let seg = el.tagName.toLowerCase();
796            
797            if (el.classList.length) {
798                const stableClasses = Array.from(el.classList).filter(c => 
799                    !c.includes('disabled') && 
800                    !c.includes('unavailable') && 
801                    !c.includes('enabled') &&
802                    !c.includes('selected') &&
803                    !c.includes('active') &&
804                    !c.includes('is-') &&
805                    !c.includes('--')
806                );
807                if (stableClasses.length > 0) {
808                    seg += '.' + stableClasses.slice(0, 2).map(c => CSS.escape(c)).join('.');
809                }
810            }
811            
812            const siblings = Array.from(el.parentElement?.children || []).filter(x => x.tagName === el.tagName);
813            if (siblings.length > 1) {
814                const idx = siblings.indexOf(el) + 1;
815                seg += `:nth-of-type(${idx})`;
816            }
817            
818            parts.unshift(seg);
819            el = el.parentElement;
820            depth++;
821        }
822        
823        return parts.join(' > ');
824    }
825
826    // Check stock from HTML
827    function checkStockFromHTML(html, selector) {
828        try {
829            const doc = new DOMParser().parseFromString(html, 'text/html');
830            const el = doc.querySelector(selector);
831            if (!el) {
832                console.log('[Stock Tracker] checkStockFromHTML: Element not found with selector:', selector);
833                return null;
834            }
835
836            // For Zara - check if element has --enabled class (means in stock)
837            if (el.classList.contains('size-selector-sizes-size')) {
838                const hasEnabled = el.classList.contains('size-selector-sizes-size--enabled');
839                const hasDisabled = el.classList.contains('size-selector-sizes-size--disabled');
840                const hasUnavailable = el.classList.contains('size-selector-sizes-size--unavailable');
841                
842                console.log(`[Stock Tracker] Zara element classes: enabled=${hasEnabled}, disabled=${hasDisabled}, unavailable=${hasUnavailable}`);
843                
844                // If has --enabled and NOT disabled/unavailable, it's in stock
845                if (hasEnabled && !hasDisabled && !hasUnavailable) {
846                    console.log('[Stock Tracker] ✓ Zara size is IN STOCK (has --enabled, no --disabled)');
847                    return true;
848                }
849                
850                // If has --disabled or --unavailable, it's out of stock
851                if (hasDisabled || hasUnavailable) {
852                    console.log('[Stock Tracker] ✗ Zara size is OUT OF STOCK (has --disabled or --unavailable)');
853                    return false;
854                }
855            }
856
857            if (el.hasAttribute('disabled') || el.getAttribute('aria-disabled') === 'true') return false;
858
859            const txt = (el.textContent || '').toLowerCase().replace(/\s+/g, ' ').trim();
860            const cls = (el.className || '').toLowerCase();
861
862            const NEG = /(not\s+available|unavailable|sold\s*out|out\s*of\s*stock|coming\s*soon|notify\s*me|לא\s*במלאי|אזל\s*המלאי|אין\s*מלאי|غير متوفر|agotado|non\s+disponibile|nicht\s+verfügbar|épuisé)/i;
863            if (NEG.test(txt) || /(out-of-stock|sold-out|unavailable|is-unavailable|oos)/i.test(cls)) return false;
864
865            const POS = /\b(in\s*stock|available\s+now|add\s*to\s*cart|buy\s*now)\b/i;
866            if (POS.test(txt) || /(available|is-available)/i.test(cls)) return true;
867
868            return null;
869        } catch (err) {
870            console.error('[Stock Tracker] checkStockFromHTML error:', err);
871            return null;
872        }
873    }
874
875    // Extract availability from JSON-LD structured data
876    function extractAvailabilityFromJSONLD(html) {
877        try {
878            const doc = new DOMParser().parseFromString(html, 'text/html');
879            const scripts = Array.from(doc.querySelectorAll('script[type="application/ld+json"]'));
880            const pairs = [];
881
882            for (const s of scripts) {
883                const txt = (s.textContent || '').trim();
884                if (!txt) continue;
885
886                const candidates = [];
887                try { 
888                    candidates.push(JSON.parse(txt)); 
889                } catch {
890                    try { 
891                        candidates.push(JSON.parse('[' + txt.replace(/}\s*,\s*{/g, '},{') + ']')); 
892                    } catch {}
893                }
894
895                for (const root of candidates.flat()) {
896                    const nodes = Array.isArray(root) ? root : [root];
897                    for (const node of nodes) {
898                        const offers = node?.offers ?? node?.Offers;
899                        if (!offers) continue;
900                        
901                        for (const off of (Array.isArray(offers) ? offers : [offers])) {
902                            const rawLabel = off?.size || off?.sku || off?.name || '';
903                            let avail = off?.availability ?? off?.Availability ?? '';
904                            if (!rawLabel) continue;
905
906                            let inStock = null;
907                            if (typeof avail === 'string') {
908                                const a = avail.toLowerCase();
909                                if (a.includes('instock') || a.includes('limitedavailability')) inStock = true;
910                                else if (a.includes('outofstock') || a.includes('soldout')) inStock = false;
911                            } else if (typeof avail === 'boolean') {
912                                inStock = !!avail;
913                            }
914
915                            if (inStock !== null) {
916                                const clean = String(rawLabel).trim();
917                                if (clean) pairs.push({ label: clean, inStock });
918                            }
919                        }
920                    }
921                }
922            }
923            
924            return pairs.length ? pairs : null;
925        } catch (e) {
926            console.warn('[Stock Tracker] JSON-LD parse error:', e);
927            return null;
928        }
929    }
930    
931    // Extract Inditex (Zara/Bershka) offers from inline scripts
932    function extractInditexOffers(html) {
933        try {
934            const doc = new DOMParser().parseFromString(html, 'text/html');
935            const scripts = Array.from(doc.querySelectorAll('script'));
936            const pairs = [];
937
938            const AVAIL_MAP = { 
939                IN_STOCK: true, LIMITED_STOCK: true, 
940                OUT_OF_STOCK: false, SOLD_OUT: false, UNAVAILABLE: false 
941            };
942
943            for (const s of scripts) {
944                const txt = s.textContent || '';
945                if (!txt || txt.length < 50) continue;
946
947                // Try to find JSON objects with size and availability data
948                // Pattern 1: Look for objects with "size" and "availability" or "stockStatus"
949                const jsonObjRegex = /\{[^{}]*?"(?:size|sizeName|name|label|variantSize)"\s*:\s*"([^"]+)"[^{}]*?"(?:availability|stockStatus)"\s*:\s*"([A-Z_]+)"[^{}]*?\}/g;
950                let m;
951                while ((m = jsonObjRegex.exec(txt)) !== null) {
952                    const rawLabel = (m[1] || '').trim();
953                    const rawAvail = (m[2] || '').toUpperCase();
954                    if (!rawLabel) continue;
955                    const inStock = AVAIL_MAP.hasOwnProperty(rawAvail) ? AVAIL_MAP[rawAvail] : null;
956                    if (inStock !== null) {
957                        console.log(`[Stock Tracker] 🔍 Inditex regex found: "${rawLabel}" = ${rawAvail}${inStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
958                        pairs.push({ label: rawLabel, inStock });
959                    }
960                }
961                
962                // Pattern 2: Reverse order - availability before size
963                const reverseRegex = /\{[^{}]*?"(?:availability|stockStatus)"\s*:\s*"([A-Z_]+)"[^{}]*?"(?:size|sizeName|name|label|variantSize)"\s*:\s*"([^"]+)"[^{}]*?\}/g;
964                while ((m = reverseRegex.exec(txt)) !== null) {
965                    const rawAvail = (m[1] || '').toUpperCase();
966                    const rawLabel = (m[2] || '').trim();
967                    if (!rawLabel) continue;
968                    const inStock = AVAIL_MAP.hasOwnProperty(rawAvail) ? AVAIL_MAP[rawAvail] : null;
969                    if (inStock !== null) {
970                        console.log(`[Stock Tracker] 🔍 Inditex regex (reverse) found: "${rawLabel}" = ${rawAvail}${inStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
971                        pairs.push({ label: rawLabel, inStock });
972                    }
973                }
974
975                // Pattern 3: Look for skuStocks array
976                const arrMatch = txt.match(/"(?:skuStocks?|sizes|variants)"\s*:\s*(\[[\s\S]*?\])/i);
977                if (arrMatch) {
978                    try {
979                        const arr = JSON.parse(arrMatch[1]);
980                        console.log(`[Stock Tracker] 🔍 Found array with ${arr.length} items`);
981                        for (const r of arr) {
982                            const rawLabel = (r.size || r.sizeName || r.name || r.label || r.variantSize || '').trim();
983                            const rawAvail = String(r.availability || r.stockStatus || '').toUpperCase();
984                            if (!rawLabel) continue;
985                            const inStock = AVAIL_MAP.hasOwnProperty(rawAvail) ? AVAIL_MAP[rawAvail] : null;
986                            if (inStock !== null) {
987                                console.log(`[Stock Tracker] 🔍 Inditex array: "${rawLabel}" = ${rawAvail}${inStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
988                                pairs.push({ label: rawLabel, inStock });
989                            }
990                        }
991                    } catch (e) {
992                        console.warn('[Stock Tracker] Failed to parse array:', e);
993                    }
994                }
995                
996                // Pattern 4: Look for window.__PRELOADED_STATE__ or similar global objects
997                if (txt.includes('__PRELOADED_STATE__') || txt.includes('window.zara') || txt.includes('window.bershka')) {
998                    console.log('[Stock Tracker] 🔍 Found preloaded state, searching for product data...');
999                    
1000                    // Try to extract the entire state object
1001                    const stateMatch = txt.match(/(?:__PRELOADED_STATE__|window\.(?:zara|bershka))\s*=\s*(\{[\s\S]*?\});/);
1002                    if (stateMatch) {
1003                        try {
1004                            const state = JSON.parse(stateMatch[1]);
1005                            console.log('[Stock Tracker] 🔍 Parsed preloaded state');
1006                            
1007                            // Recursively search for size/availability data
1008                            function searchForSizes(obj, depth = 0) {
1009                                if (depth > 10 || !obj || typeof obj !== 'object') return;
1010                                
1011                                // Check if this object has size and availability
1012                                if (obj.size && obj.availability) {
1013                                    const rawLabel = String(obj.size || obj.sizeName || obj.name || '').trim();
1014                                    const rawAvail = String(obj.availability || obj.stockStatus || '').toUpperCase();
1015                                    if (rawLabel) {
1016                                        const inStock = AVAIL_MAP.hasOwnProperty(rawAvail) ? AVAIL_MAP[rawAvail] : null;
1017                                        if (inStock !== null) {
1018                                            console.log(`[Stock Tracker] 🔍 Found in state: "${rawLabel}" = ${rawAvail}${inStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
1019                                            pairs.push({ label: rawLabel, inStock });
1020                                        }
1021                                    }
1022                                }
1023                                
1024                                // Recurse into arrays and objects
1025                                if (Array.isArray(obj)) {
1026                                    obj.forEach(item => searchForSizes(item, depth + 1));
1027                                } else {
1028                                    Object.values(obj).forEach(val => searchForSizes(val, depth + 1));
1029                                }
1030                            }
1031                            
1032                            searchForSizes(state);
1033                        } catch (e) {
1034                            console.warn('[Stock Tracker] Failed to parse preloaded state:', e);
1035                        }
1036                    }
1037                }
1038            }
1039
1040            const seen = new Map();
1041            for (const p of pairs) {
1042                const key = normalizeSizeLabel(p.label);
1043                if (!seen.has(key)) {
1044                    seen.set(key, p);
1045                } else if (p.inStock === true) {
1046                    seen.set(key, p);
1047                }
1048            }
1049            
1050            const out = Array.from(seen.values());
1051            console.log(`[Stock Tracker] 🔍 Inditex extraction complete: ${out.length} unique sizes found`);
1052            if (out.length > 0) {
1053                console.log('[Stock Tracker] 🔍 Extracted sizes:', out.map(s => `${s.label}: ${s.inStock ? 'IN' : 'OUT'}`));
1054            }
1055            return out.length ? out : null;
1056        } catch (e) {
1057            console.warn('[Stock Tracker] extractInditexOffers error:', e);
1058            return null;
1059        }
1060    }
1061
1062    // Check stock from live DOM (when product page is open)
1063    function liveStockFromOpenTab(selector, url) {
1064        try {
1065            const here = location.href.split('#')[0].split('?')[0];
1066            if (here !== url) return null;
1067            const node = document.querySelector(selector);
1068            if (!node) return null;
1069            
1070            if (node.classList.contains('size-selector-sizes-size--enabled')) {
1071                const isDisabled = node.classList.contains('size-selector-sizes-size--disabled') ||
1072                                 node.classList.contains('size-selector-sizes-size--unavailable') ||
1073                                 node.textContent?.toLowerCase().includes('coming soon');
1074                return !isDisabled;
1075            }
1076            
1077            const disabled = node.hasAttribute('disabled') || node.getAttribute('aria-disabled') === 'true';
1078            const txt = (node.textContent||'').toLowerCase();
1079            const cls = (node.className||'').toLowerCase();
1080            if (disabled) return false;
1081            if (/(sold\s?out|out\s?of\s?stock|unavailable|coming\s?soon|notify\s?me)/i.test(txt)) return false;
1082            if (/(out-of-stock|sold-out|unavailable|is-unavailable)/i.test(cls)) return false;
1083            
1084            if (/(in-stock|is-available|available)\b/i.test(cls)) return true;
1085            
1086            return null;
1087        } catch { return null; }
1088    }
1089
1090    // Fetch page HTML
1091    async function fetchHTML(url) {
1092        try {
1093            console.log('[Stock Tracker] Fetching URL:', url);
1094            
1095            return new Promise((resolve, reject) => {
1096                GM_xmlhttpRequest({
1097                    method: 'GET',
1098                    url: url,
1099                    headers: {
1100                        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
1101                        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
1102                        'Accept-Language': 'en-US,en;q=0.5',
1103                        'Cache-Control': 'no-cache',
1104                        'Pragma': 'no-cache'
1105                    },
1106                    timeout: 30000,
1107                    onload: function(response) {
1108                        if (response.status >= 200 && response.status < 300) {
1109                            console.log('[Stock Tracker] Fetched successfully:', response.responseText.length, 'chars');
1110                            resolve(response.responseText);
1111                        } else {
1112                            reject(new Error(`HTTP ${response.status}`));
1113                        }
1114                    },
1115                    onerror: function(error) {
1116                        console.error('[Stock Tracker] Fetch error:', error);
1117                        reject(new Error('Network error'));
1118                    },
1119                    ontimeout: function() {
1120                        console.error('[Stock Tracker] Fetch timeout');
1121                        reject(new Error('Request timeout'));
1122                    }
1123                });
1124            });
1125        } catch (err) {
1126            console.error('[Stock Tracker] Fetch error:', err);
1127            throw err;
1128        }
1129    }
1130
1131    // WhatsApp helpers
1132    function openWhatsApp(text, phone = '') {
1133        const url = new URL('https://web.whatsapp.com/send');
1134        if (phone) url.searchParams.set('phone', phone.replace(/[^\d]/g, ''));
1135        url.searchParams.set('text', text);
1136        
1137        (async () => {
1138            await GM.setValue('wa_pending_text', text);
1139            await GM.setValue('wa_pending_send', 'true');
1140            await GM.setValue('wa_pending_time', Date.now().toString());
1141            GM.openInTab(url.toString(), false);
1142        })();
1143    }
1144
1145    async function autoSendWhatsApp() {
1146        if (!location.host.includes('web.whatsapp.com')) return;
1147        
1148        const shouldSend = await GM.getValue('wa_pending_send');
1149        const text = await GM.getValue('wa_pending_text');
1150        const pendingTime = await GM.getValue('wa_pending_time');
1151        
1152        if (shouldSend === 'true' && text) {
1153            if (pendingTime) {
1154                const age = Date.now() - parseInt(pendingTime);
1155                if (age > 120000) {
1156                    console.log('[Stock Tracker] Message too old, clearing...');
1157                    await GM.setValue('wa_pending_send', '');
1158                    await GM.setValue('wa_pending_text', '');
1159                    await GM.setValue('wa_pending_time', '');
1160                    return;
1161                }
1162            }
1163
1164            await GM.setValue('wa_pending_send', '');
1165            await GM.setValue('wa_pending_text', '');
1166            await GM.setValue('wa_pending_time', '');
1167            
1168            console.log('[Stock Tracker] Auto-sending WhatsApp message...');
1169            
1170            let attempts = 0;
1171            const maxAttempts = 40;
1172            
1173            const checkComposer = setInterval(async () => {
1174                attempts++;
1175                console.log(`[Stock Tracker] Attempt ${attempts}/${maxAttempts} - Looking for composer...`);
1176                
1177                // Try multiple selectors for the composer
1178                const composerSelectors = [
1179                    'footer div[contenteditable="true"][role="textbox"]',
1180                    'div[contenteditable="true"][data-tab="10"]',
1181                    'div[contenteditable="true"][data-lexical-editor="true"]',
1182                    'div[contenteditable="true"][data-testid="conversation-compose-box-input"]',
1183                    'div[contenteditable="true"][title*="Type a message"]',
1184                    'div[contenteditable="true"][title*="הקלד הודעה"]',
1185                    'footer div[contenteditable="true"]',
1186                    'div[role="textbox"][contenteditable="true"]'
1187                ];
1188                
1189                let composer = null;
1190                for (const selector of composerSelectors) {
1191                    composer = document.querySelector(selector);
1192                    if (composer && composer.offsetParent !== null) {
1193                        console.log('[Stock Tracker] Found composer with selector:', selector);
1194                        break;
1195                    }
1196                }
1197                
1198                if (composer && composer.offsetParent !== null) {
1199                    clearInterval(checkComposer);
1200                    console.log('[Stock Tracker] Composer found! Writing message...');
1201                    
1202                    try {
1203                        // Focus the composer
1204                        composer.focus();
1205                        await new Promise(resolve => setTimeout(resolve, 300));
1206                        
1207                        // Clear existing content
1208                        composer.innerHTML = '';
1209                        
1210                        // Insert text with line breaks
1211                        const lines = text.split('\n');
1212                        lines.forEach((line, idx) => {
1213                            if (idx > 0) composer.appendChild(document.createElement('br'));
1214                            composer.appendChild(document.createTextNode(line));
1215                        });
1216                        
1217                        // Trigger input events
1218                        composer.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: true }));
1219                        composer.dispatchEvent(new Event('change', { bubbles: true }));
1220                        composer.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: 'a' }));
1221                        composer.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: 'a' }));
1222                        
1223                        console.log('[Stock Tracker] Message written, waiting before sending...');
1224                        await new Promise(resolve => setTimeout(resolve, 2000));
1225                        
1226                        // Try multiple selectors for the send button - IMPROVED
1227                        const sendButtonSelectors = [
1228                            'span[data-icon="wds-ic-send-filled"]',
1229                            'span[data-icon="send"]',
1230                            'button[aria-label*="שליחה"]',
1231                            'button[aria-label*="Send"]',
1232                            'div[aria-label*="שליחה"][role="button"]',
1233                            'div[aria-label*="Send"][role="button"]',
1234                            'footer button[aria-label*="Send"]',
1235                            'footer button[aria-label*="שליחה"]',
1236                            'footer div[aria-label*="Send"][role="button"]',
1237                            'footer div[aria-label*="שליחה"][role="button"]',
1238                            'button[data-testid="send"]',
1239                            'footer button[data-tab="11"]',
1240                            'footer button span[data-icon="send"]'
1241                        ];
1242                        
1243                        let sendBtn = null;
1244                        for (const selector of sendButtonSelectors) {
1245                            const element = document.querySelector(selector);
1246                            if (element) {
1247                                // If it's a span, get the parent button/div with role="button"
1248                                if (element.tagName === 'SPAN') {
1249                                    sendBtn = element.closest('button') || element.closest('div[role="button"]');
1250                                } else {
1251                                    sendBtn = element;
1252                                }
1253                                if (sendBtn) {
1254                                    console.log('[Stock Tracker] Found send button with selector:', selector);
1255                                    break;
1256                                }
1257                            }
1258                        }
1259                        
1260                        if (sendBtn) {
1261                            console.log('[Stock Tracker] Clicking send button...');
1262                            console.log('[Stock Tracker] Send button element:', sendBtn.tagName, sendBtn.className);
1263                            
1264                            // Try multiple click methods
1265                            sendBtn.click();
1266                            await new Promise(resolve => setTimeout(resolve, 100));
1267                            
1268                            // Dispatch mouse events as backup
1269                            sendBtn.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
1270                            sendBtn.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }));
1271                            sendBtn.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
1272                            
1273                            // Try pressing Enter key as final backup
1274                            composer.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: 'Enter', code: 'Enter', keyCode: 13 }));
1275                            
1276                            console.log('[Stock Tracker] ✅ Message sent successfully!');
1277                            
1278                            // Show success notification
1279                            setTimeout(() => {
1280                                const notification = document.createElement('div');
1281                                notification.style.cssText = `
1282                                    position: fixed;
1283                                    top: 20px;
1284                                    right: 20px;
1285                                    background: #25D366;
1286                                    color: white;
1287                                    padding: 16px 24px;
1288                                    border-radius: 12px;
1289                                    font-weight: 700;
1290                                    z-index: 999999;
1291                                    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
1292                                `;
1293                                notification.textContent = '✅ הודעה נשלחה בהצלחה!';
1294                                document.body.appendChild(notification);
1295                                
1296                                setTimeout(() => {
1297                                    notification.style.transition = 'opacity 0.3s';
1298                                    notification.style.opacity = '0';
1299                                    setTimeout(() => notification.remove(), 300);
1300                                }, 3000);
1301                            }, 500);
1302                        } else {
1303                            console.error('[Stock Tracker] ❌ Send button not found!');
1304                            console.error('[Stock Tracker] Available elements in footer:', 
1305                                Array.from(document.querySelectorAll('footer *[role="button"], footer button')).map(b => ({
1306                                    tag: b.tagName,
1307                                    ariaLabel: b.getAttribute('aria-label'),
1308                                    classes: b.className,
1309                                    dataIcon: b.getAttribute('data-icon') || b.querySelector('[data-icon]')?.getAttribute('data-icon')
1310                                }))
1311                            );
1312                            
1313                            // Show error notification
1314                            const notification = document.createElement('div');
1315                            notification.style.cssText = `
1316                                position: fixed;
1317                                top: 20px;
1318                                right: 20px;
1319                                background: #ef4444;
1320                                color: white;
1321                                padding: 16px 24px;
1322                                border-radius: 12px;
1323                                font-weight: 700;
1324                                z-index: 999999;
1325                                box-shadow: 0 4px 12px rgba(0,0,0,0.3);
1326                            `;
1327                            notification.textContent = '⚠️ לא מצאתי כפתור שליחה - שלח ידנית';
1328                            document.body.appendChild(notification);
1329                            
1330                            setTimeout(() => {
1331                                notification.style.transition = 'opacity 0.3s';
1332                                notification.style.opacity = '0';
1333                                setTimeout(() => notification.remove(), 300);
1334                            }, 5000);
1335                        }
1336                    } catch (err) {
1337                        console.error('[Stock Tracker] Error during send process:', err);
1338                    }
1339                }
1340                
1341                if (attempts >= maxAttempts) {
1342                    clearInterval(checkComposer);
1343                    console.error('[Stock Tracker] ❌ Timeout: Could not find composer after', maxAttempts, 'attempts');
1344                    
1345                    // Show timeout notification
1346                    const notification = document.createElement('div');
1347                    notification.style.cssText = `
1348                        position: fixed;
1349                        top: 20px;
1350                        right: 20px;
1351                        background: #ef4444;
1352                        color: white;
1353                        padding: 16px 24px;
1354                        border-radius: 12px;
1355                        font-weight: 700;
1356                        z-index: 999999;
1357                        box-shadow: 0 4px 12px rgba(0,0,0,0.3);
1358                    `;
1359                    notification.textContent = '⚠️ לא הצלחתי למצוא את תיבת ההודעות';
1360                    document.body.appendChild(notification);
1361                    
1362                    setTimeout(() => {
1363                        notification.style.transition = 'opacity 0.3s';
1364                        notification.style.opacity = '0';
1365                        setTimeout(() => notification.remove(), 300);
1366                    }, 5000);
1367                }
1368            }, 1000);
1369        }
1370    }
1371
1372    // Check stock for all items
1373    async function checkAllStock() {
1374        const items = await getItems();
1375        const prefs = await getPrefs();
1376        
1377        if (items.length === 0) {
1378            showToast('אין מוצרים במעקב', 'error');
1379            return;
1380        }
1381        
1382        const statusText = document.querySelector('#st-status-text');
1383        if (statusText) {
1384            statusText.textContent = '🟡 בודק מלאי...';
1385            statusText.style.color = '#fbbf24';
1386        }
1387        
1388        showToast(`🔄 בודק ${items.length} מוצרים...`, 'info');
1389        console.log('[Stock Tracker] ========================================');
1390        console.log('[Stock Tracker] Starting stock check for', items.length, 'items...');
1391        console.log('[Stock Tracker] ========================================');
1392        
1393        let foundInStock = 0;
1394        let priceDrops = 0;
1395        let updatedItems = [...items];
1396        let successCount = 0;
1397        
1398        for (let i = 0; i < updatedItems.length; i++) {
1399            const item = updatedItems[i];
1400            console.log(`[Stock Tracker] [${i+1}/${updatedItems.length}] Checking: ${item.title}`);
1401            
1402            let retries = 3;
1403            let success = false;
1404            
1405            while (retries > 0 && !success) {
1406                try {
1407                    const html = await fetchHTML(item.url);
1408                    const isInditex = item.url.includes('bershka.com') || item.url.includes('zara.com');
1409                    const inditexPairs = isInditex ? extractInditexOffers(html) : null;
1410                    const jsonLdPairs = extractAvailabilityFromJSONLD(html);
1411                    
1412                    if (item.mode === 'price') {
1413                        const adapter = getAdapter();
1414                        const liveNow = adapter.price?.() ?? null;
1415                        let best = liveNow ?? item.priceNow ?? null;
1416                        
1417                        if (best == null && jsonLdPairs && jsonLdPairs[0]) {
1418                            best = parsePrice(jsonLdPairs[0].label);
1419                        }
1420                        
1421                        const oldPrice = item.priceNow;
1422                        item.priceNow = best;
1423                        item.lastChecked = Date.now();
1424                        
1425                        if (item.priceNow != null && item.targetPrice != null && item.priceNow <= item.targetPrice) {
1426                            foundInStock++;
1427                            const msg = `✈️ ירידת מחיר!\n\n${item.title}\nמחיר נוכחי: ₪${item.priceNow}\nיעד: ₪${item.targetPrice}\n\n${item.url}`;
1428                            if (prefs.waPhone) openWhatsApp(msg, prefs.waPhone);
1429                            if (prefs.soundAlerts) playNotificationSound();
1430                        } else if (oldPrice != null && item.priceNow != null && item.priceNow < oldPrice) {
1431                            priceDrops++;
1432                            const msg = `💰 ירידת מחיר!\n\n${item.title}\nמחיר קודם: ₪${oldPrice}\nמחיר חדש: ₪${item.priceNow}\nחיסכון: ₪${(oldPrice - item.priceNow).toFixed(2)}\n\n${item.url}`;
1433                            if (prefs.waPhone && prefs.notifications) openWhatsApp(msg, prefs.waPhone);
1434                            if (prefs.soundAlerts) playNotificationSound();
1435                        }
1436                    } else {
1437                        for (const size of item.sizes) {
1438                            let newStock = null;
1439                            
1440                            // First check live DOM if we're on the same page
1441                            const currentUrl = location.href.split('#')[0].split('?')[0];
1442                            if (currentUrl === item.url) {
1443                                console.log(`[Stock Tracker] 🔍 We're on the product page! Checking live DOM for "${size.label}"`);
1444                                newStock = liveStockFromOpenTab(size.selector, item.url);
1445                                if (newStock !== null) {
1446                                    console.log(`[Stock Tracker] ✓ Live DOM matched "${size.label}" ⇢ ${newStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
1447                                }
1448                            }
1449                            
1450                            if (newStock === null && inditexPairs) {
1451                                const hit = inditexPairs.find(p => sizeMatches(p.label, size.label));
1452                                if (hit) {
1453                                    newStock = !!hit.inStock;
1454                                    console.log(`[Stock Tracker] ✓ Inditex matched "${size.label}" ⇢ ${newStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
1455                                }
1456                            }
1457                            
1458                            if (newStock === null && jsonLdPairs) {
1459                                const hit = jsonLdPairs.find(p => sizeMatches(p.label, size.label));
1460                                if (hit) {
1461                                    newStock = !!hit.inStock;
1462                                    console.log(`[Stock Tracker] ✓ JSON-LD matched "${size.label}" ⇢ ${newStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
1463                                }
1464                            }
1465                            
1466                            if (newStock === null && !isInditex) {
1467                                newStock = checkStockFromHTML(html, size.selector);
1468                                if (newStock !== null) {
1469                                    console.log(`[Stock Tracker] ✓ HTML selector matched "${size.label}" ⇢ ${newStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
1470                                }
1471                            }
1472                            
1473                            // Store previous state BEFORE updating
1474                            const previousStock = size.inStock;
1475                            
1476                            // Update to new state (or keep old if we couldn't determine)
1477                            const currentStock = newStock !== null ? newStock : previousStock;
1478                            
1479                            console.log(`[Stock Tracker] 📊 Size "${size.label}": PREVIOUS=${previousStock === true ? 'IN' : previousStock === false ? 'OUT' : 'UNKNOWN'} → CURRENT=${currentStock === true ? 'IN' : currentStock === false ? 'OUT' : 'UNKNOWN'}`);
1480                            
1481                            // Check if it became in stock (was out, now in)
1482                            const becameInStock = (previousStock === false || previousStock === null || previousStock === undefined) && currentStock === true;
1483                            
1484                            if (becameInStock) {
1485                                const ts = Date.now();
1486                                const lastAlert = size.lastAlertAt || 0;
1487                                const throttleMs = (prefs.throttleMinutesWA || 3) * 60 * 1000;
1488                                const tooSoon = (ts - lastAlert) < throttleMs;
1489                                
1490                                console.log(`[Stock Tracker] 🎉 SIZE BACK IN STOCK: "${size.label}"!`);
1491                                console.log(`[Stock Tracker] Last alert: ${lastAlert ? new Date(lastAlert).toLocaleTimeString() : 'never'}, Too soon: ${tooSoon}`);
1492                                
1493                                if (!tooSoon) {
1494                                    foundInStock++;
1495                                    size.lastAlertAt = ts;
1496                                    
1497                                    console.log('[Stock Tracker] 🔔 TRIGGERING ALL ALERTS for:', item.title, size.label);
1498                                    
1499                                    const message = `🔔 חזרה למלאי!\n\n${item.title}\nמידה: ${size.label}\nמחיר: ${item.price}\n\n${item.url}`;
1500                                    
1501                                    // 1. WhatsApp notification
1502                                    if (prefs.waPhone) {
1503                                        console.log('[Stock Tracker] 📱 Sending WhatsApp message...');
1504                                        openWhatsApp(message, prefs.waPhone);
1505                                    }
1506                                    
1507                                    // 2. Browser notification
1508                                    if (prefs.notifications) {
1509                                        console.log('[Stock Tracker] 🔔 Showing browser notification...');
1510                                        showToast(`🔔 ${size.label} חזרה למלאי!`, 'success');
1511                                    }
1512                                    
1513                                    // 3. Sound alert - ALWAYS play sound when item is back in stock
1514                                    console.log('[Stock Tracker] 🔊 Playing sound alert...');
1515                                    playNotificationSound();
1516                                    
1517                                    // 4. Auto add to cart
1518                                    if (prefs.autoAddToCart) {
1519                                        console.log('[Stock Tracker] 🛒 Auto-adding to cart...');
1520                                        await addToCart(item.url, size.selector, size.label);
1521                                    }
1522                                } else {
1523                                    console.log('[Stock Tracker] ⏰ Alert throttled - too soon since last alert');
1524                                }
1525                            }
1526                            
1527                            // Update the size stock status
1528                            size.inStock = currentStock;
1529                            size.lastChecked = Date.now();
1530                        }
1531                    }
1532                    
1533                    success = true;
1534                    successCount++;
1535                    
1536                } catch (err) {
1537                    retries--;
1538                    console.error(`[Stock Tracker] Error (attempt ${3-retries}/3):`, err);
1539                    console.error('[Stock Tracker] Error details:', err.message, err.stack);
1540                    
1541                    if (retries > 0) {
1542                        await new Promise(resolve => setTimeout(resolve, 2000));
1543                    }
1544                }
1545            }
1546        }
1547        
1548        await saveItems(updatedItems);
1549        
1550        if (statusText) {
1551            statusText.textContent = '🟢 המערכת פעילה';
1552            statusText.style.color = '#22c55e';
1553        }
1554        
1555        console.log('[Stock Tracker] Check completed! Found:', foundInStock, 'Price drops:', priceDrops, 'Success:', successCount);
1556        
1557        if (foundInStock > 0 || priceDrops > 0) {
1558            const msg = [];
1559            if (foundInStock > 0) msg.push(`${foundInStock} מידות במלאי`);
1560            if (priceDrops > 0) msg.push(`${priceDrops} ירידות מחיר`);
1561            showToast(`✅ נמצאו: ${msg.join(', ')}!`, 'success');
1562        } else {
1563            showToast(`✅ בדיקה הושלמה (${successCount}/${items.length})`, 'info');
1564        }
1565        
1566        await renderList();
1567        await renderStats();
1568        await updateFabBadge();
1569    }
1570
1571    // Check stock for a single item
1572    async function checkSingleItem(itemId) {
1573        const items = await getItems();
1574        const prefs = await getPrefs();
1575        const item = items.find(i => i.id === itemId);
1576        
1577        if (!item) {
1578            showToast('⚠️ מוצר לא נמצא', 'error');
1579            return;
1580        }
1581        
1582        showToast(`🔄 בודק: ${item.title}...`, 'info');
1583        console.log('[Stock Tracker] Checking single item:', item.title);
1584        console.log('[Stock Tracker] Item URL:', item.url);
1585        console.log('[Stock Tracker] Item mode:', item.mode);
1586        console.log('[Stock Tracker] Item sizes:', item.sizes?.length || 0);
1587        
1588        let retries = 3;
1589        let success = false;
1590        
1591        while (retries > 0 && !success) {
1592            try {
1593                console.log(`[Stock Tracker] Fetching item data (attempt ${4-retries}/3)...`);
1594                const html = await fetchHTML(item.url);
1595                console.log('[Stock Tracker] HTML fetched successfully, length:', html.length);
1596                
1597                const isInditex = item.url.includes('bershka.com') || item.url.includes('zara.com');
1598                const inditexPairs = isInditex ? extractInditexOffers(html) : null;
1599                const jsonLdPairs = extractAvailabilityFromJSONLD(html);
1600                
1601                console.log('[Stock Tracker] Inditex pairs found:', inditexPairs?.length || 0);
1602                console.log('[Stock Tracker] JSON-LD pairs found:', jsonLdPairs?.length || 0);
1603                
1604                let foundInStock = 0;
1605                
1606                if (item.mode === 'price') {
1607                    const adapter = getAdapter();
1608                    const liveNow = adapter.price?.() ?? null;
1609                    let best = liveNow ?? item.priceNow ?? null;
1610                    
1611                    item.priceNow = best;
1612                    item.lastChecked = Date.now();
1613                    
1614                    console.log('[Stock Tracker] Price check - Current:', item.priceNow, 'Target:', item.targetPrice);
1615                    
1616                    if (item.priceNow != null && item.targetPrice != null && item.priceNow <= item.targetPrice) {
1617                        foundInStock++;
1618                        const msg = `✈️ ירידת מחיר!\n\n${item.title}\nמחיר נוכחי: ₪${item.priceNow}\nיעד: ₪${item.targetPrice}\n\n${item.url}`;
1619                        if (prefs.waPhone) openWhatsApp(msg, prefs.waPhone);
1620                        if (prefs.soundAlerts) playNotificationSound();
1621                    }
1622                } else {
1623                    console.log('[Stock Tracker] Checking', item.sizes.length, 'sizes...');
1624                    for (const size of item.sizes) {
1625                        let newStock = null;
1626                        
1627                        // First check live DOM if we're on the same page
1628                        const currentUrl = location.href.split('#')[0].split('?')[0];
1629                        if (currentUrl === item.url) {
1630                            console.log(`[Stock Tracker] 🔍 We're on the product page! Checking live DOM for "${size.label}"`);
1631                            newStock = liveStockFromOpenTab(size.selector, item.url);
1632                            if (newStock !== null) {
1633                                console.log(`[Stock Tracker] ✓ Live DOM matched "${size.label}" ⇢ ${newStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
1634                            }
1635                        }
1636                        
1637                        if (newStock === null && inditexPairs) {
1638                            const hit = inditexPairs.find(p => sizeMatches(p.label, size.label));
1639                            if (hit) {
1640                                newStock = !!hit.inStock;
1641                                console.log(`[Stock Tracker] ✓ Inditex matched "${size.label}" ⇢ ${newStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
1642                            }
1643                        }
1644                        
1645                        if (newStock === null && jsonLdPairs) {
1646                            const hit = jsonLdPairs.find(p => sizeMatches(p.label, size.label));
1647                            if (hit) {
1648                                newStock = !!hit.inStock;
1649                                console.log(`[Stock Tracker] ✓ JSON-LD matched "${size.label}" ⇢ ${newStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
1650                            }
1651                        }
1652                        
1653                        if (newStock === null && !isInditex) {
1654                            newStock = checkStockFromHTML(html, size.selector);
1655                            if (newStock !== null) {
1656                                console.log(`[Stock Tracker] ✓ HTML selector matched "${size.label}" ⇢ ${newStock ? 'IN STOCK' : 'OUT OF STOCK'}`);
1657                            }
1658                        }
1659                        
1660                        const prev = size.inStock;
1661                        const now = newStock !== null ? newStock : prev;
1662                        
1663                        console.log(`[Stock Tracker] Size "${size.label}": ${prev === true ? 'WAS IN' : 'WAS OUT'}${now === true ? 'NOW IN' : 'NOW OUT'}`);
1664                        
1665                        if ((prev === false || prev == null) && now === true) {
1666                            foundInStock++;
1667                            const message = `🔔 חזרה למלאי!\n\n${item.title}\nמידה: ${size.label}\nמחיר: ${item.price}\n\n${item.url}`;
1668                            
1669                            console.log('[Stock Tracker] 🎉 SIZE BACK IN STOCK!', size.label);
1670                            
1671                            if (prefs.waPhone) openWhatsApp(message, prefs.waPhone);
1672                            if (prefs.notifications) showToast(`🔔 ${size.label} חזרה למלאי!`, 'success');
1673                            if (prefs.soundAlerts) playNotificationSound();
1674                        }
1675                        
1676                        size.inStock = now;
1677                        size.lastChecked = Date.now();
1678                    }
1679                }
1680                
1681                await saveItems(items);
1682                success = true;
1683                
1684                console.log('[Stock Tracker] ✅ Check completed successfully!');
1685                console.log('[Stock Tracker] Found in stock:', foundInStock);
1686                
1687                if (foundInStock > 0) {
1688                    showToast(`✅ נמצאו ${foundInStock} מידות במלאי!`, 'success');
1689                } else {
1690                    showToast('✅ בדיקה הושלמה - אין שינויים', 'info');
1691                }
1692                
1693            } catch (err) {
1694                retries--;
1695                console.error(`[Stock Tracker] ❌ Error checking item (attempt ${3-retries}/3):`, err);
1696                console.error('[Stock Tracker] Error details:', err.message, err.stack);
1697                
1698                if (retries === 0) {
1699                    showToast('❌ שגיאה בבדיקת המוצר', 'error');
1700                } else {
1701                    console.log('[Stock Tracker] Retrying in 2 seconds...');
1702                    await new Promise(resolve => setTimeout(resolve, 2000));
1703                }
1704            }
1705        }
1706        
1707        await renderList();
1708        await renderStats();
1709        await updateFabBadge();
1710    }
1711
1712    // Remove item
1713    async function removeItem(id) {
1714        if (!confirm('האם אתה בטוח שברצונך למחוק מוצר זה?')) return;
1715        
1716        const items = await getItems();
1717        const filtered = items.filter(item => item.id !== id);
1718        
1719        await saveItems(filtered);
1720        await renderList();
1721        await renderStats();
1722        await updateFabBadge();
1723        showToast('🗑️ מוצר נמחק בהצלחה', 'success');
1724    }
1725
1726    // Clear all data
1727    async function clearAllData() {
1728        if (!confirm('האם אתה בטוח שברצונך למחוק את כל הנתונים?')) return;
1729        
1730        await saveItems([]);
1731        await renderList();
1732        await renderStats();
1733        await updateFabBadge();
1734        showToast('🗑️ כל הנתונים נמחקו', 'info');
1735    }
1736
1737    // Quick add from current page
1738    async function quickAddAllSizes() {
1739        const adapter = getAdapter();
1740        if (!adapter) {
1741            showToast('⚠️ אתר זה לא נתמך כרגע', 'error');
1742            return;
1743        }
1744
1745        const title = adapter.title();
1746        const price = adapter.price();
1747        const image = adapter.image();
1748        const url = location.href.split('#')[0].split('?')[0];
1749
1750        if (!title) {
1751            showToast('⚠️ לא הצלחתי לזהות את המוצר', 'error');
1752            return;
1753        }
1754
1755        showToast('⏳ מוסיף את כל המידות...', 'info');
1756
1757        const sizes = await adapter.getSizes();
1758        
1759        if (!sizes || sizes.length === 0) {
1760            showToast('⚠️ לא נמצאו מידות', 'error');
1761            return;
1762        }
1763
1764        const items = await getItems();
1765        
1766        const selectedSizeData = sizes.map(size => ({
1767            label: size.label,
1768            selector: buildSelector(size.element),
1769            inStock: size.inStock,
1770            lastChecked: Date.now(),
1771            lastAlertAt: 0
1772        }));
1773        
1774        items.push({
1775            id: Date.now().toString(),
1776            mode: adapter.mode || 'stock',
1777            title,
1778            price: price ? `${price}` : 'לא זמין',
1779            image,
1780            url,
1781            sizes: selectedSizeData,
1782            addedAt: Date.now()
1783        });
1784        
1785        await saveItems(items);
1786        await renderList();
1787        await renderStats();
1788        await updateFabBadge();
1789        showToast(`✅ נוספו ${sizes.length} מידות למעקב!`, 'success');
1790    }
1791
1792    // Copy product link
1793    async function copyProductLink(url) {
1794        try {
1795            await GM.setClipboard(url);
1796            showToast('✅ הקישור הועתק ללוח!', 'success');
1797        } catch (err) {
1798            console.error('[Stock Tracker] Copy error:', err);
1799            showToast('❌ שגיאה בהעתקת הקישור', 'error');
1800        }
1801    }
1802
1803    // Add to cart - Universal function for all sites
1804    async function addToCart(productUrl, sizeSelector, sizeLabel) {
1805        try {
1806            console.log('[Stock Tracker] 🛒 Attempting to add to cart:', sizeLabel);
1807            console.log('[Stock Tracker] Product URL:', productUrl);
1808            console.log('[Stock Tracker] Size selector:', sizeSelector);
1809            
1810            // Open product page in new tab
1811            GM.openInTab(productUrl, false);
1812            
1813            // Store the add-to-cart task
1814            await GM.setValue('pending_cart_add', JSON.stringify({
1815                url: productUrl,
1816                selector: sizeSelector,
1817                label: sizeLabel,
1818                timestamp: Date.now()
1819            }));
1820            
1821            console.log('[Stock Tracker] ✅ Opened product page for cart addition');
1822            showToast(`🛒 פותח דף מוצר להוספה לסל: ${sizeLabel}`, 'info');
1823            
1824        } catch (err) {
1825            console.error('[Stock Tracker] Add to cart error:', err);
1826            showToast('⚠️ לא הצלחתי להוסיף לסל - פתח ידנית', 'error');
1827        }
1828    }
1829
1830    // Auto add to cart when product page loads
1831    async function autoAddToCartOnLoad() {
1832        try {
1833            const pendingTask = await GM.getValue('pending_cart_add');
1834            if (!pendingTask) return;
1835            
1836            const task = JSON.parse(pendingTask);
1837            const age = Date.now() - task.timestamp;
1838            
1839            // Task expired after 30 seconds
1840            if (age > 30000) {
1841                await GM.setValue('pending_cart_add', '');
1842                return;
1843            }
1844            
1845            const currentUrl = location.href.split('#')[0].split('?')[0];
1846            if (currentUrl !== task.url) return;
1847            
1848            // Clear the task
1849            await GM.setValue('pending_cart_add', '');
1850            
1851            console.log('[Stock Tracker] 🛒 Auto-adding to cart:', task.label);
1852            
1853            // Wait for page to load
1854            await new Promise(resolve => setTimeout(resolve, 3000));
1855            
1856            // For Zara - first click "Add to Cart" to open size selector
1857            if (task.url.includes('zara.com')) {
1858                console.log('[Stock Tracker] Zara detected - opening size selector first');
1859                const addToCartBtn = document.querySelector('button[data-qa-action="add-to-cart"]');
1860                if (addToCartBtn) {
1861                    console.log('[Stock Tracker] Clicking add to cart to open size selector...');
1862                    addToCartBtn.click();
1863                    await new Promise(resolve => setTimeout(resolve, 1500));
1864                }
1865            }
1866            
1867            // Find and click the size button
1868            const sizeBtn = document.querySelector(task.selector);
1869            if (sizeBtn) {
1870                console.log('[Stock Tracker] Found size button, clicking...');
1871                sizeBtn.click();
1872                await new Promise(resolve => setTimeout(resolve, 1000));
1873            } else {
1874                console.warn('[Stock Tracker] Size button not found with selector:', task.selector);
1875                
1876                // Fallback: try to find by label
1877                const sizeElements = document.querySelectorAll('li.size-selector-sizes__size, button[class*="size"], [data-size]');
1878                for (const el of sizeElements) {
1879                    const label = el.textContent?.trim() || el.getAttribute('data-size');
1880                    if (label === task.label) {
1881                        console.log('[Stock Tracker] Found size by label, clicking...');
1882                        const btn = el.querySelector('button') || el;
1883                        btn.click();
1884                        await new Promise(resolve => setTimeout(resolve, 1000));
1885                        break;
1886                    }
1887                }
1888            }
1889            
1890            // Wait a bit more for size selection to register
1891            await new Promise(resolve => setTimeout(resolve, 500));
1892            
1893            // Find and click add to cart button - Universal selectors
1894            const addToCartSelectors = [
1895                // Zara
1896                'button[data-qa-action="add-to-cart"]',
1897                'button[class*="add-to-cart"]',
1898                // Bershka
1899                'button[data-qa-anchor="addToBag"]',
1900                'button[class*="add-to-bag"]',
1901                // H&M
1902                'button[data-testid="add-to-bag"]',
1903                'button[class*="addToBag"]',
1904                // Nike
1905                'button[aria-label*="Add to Bag"]',
1906                'button[data-qa="add-to-cart"]',
1907                // Amazon
1908                '#add-to-cart-button',
1909                'input[id="add-to-cart-button"]',
1910                // Generic
1911                'button[id*="add-to-cart"]',
1912                'button[id*="add-to-bag"]',
1913                'button[class*="add-to-basket"]',
1914                'button[aria-label*="הוסף לסל"]',
1915                'button[aria-label*="הוספה לסל"]',
1916                'button:has-text("הוסף לסל")',
1917                'button:has-text("Add to Cart")',
1918                'button:has-text("Add to Bag")',
1919                '[data-testid*="add-to-cart"]',
1920                '[data-testid*="add-to-bag"]'
1921            ];
1922            
1923            let addBtn = null;
1924            for (const selector of addToCartSelectors) {
1925                addBtn = document.querySelector(selector);
1926                if (addBtn && !addBtn.disabled) {
1927                    console.log('[Stock Tracker] Found add to cart button:', selector);
1928                    break;
1929                }
1930            }
1931            
1932            // Fallback: search by text content
1933            if (!addBtn) {
1934                const buttons = document.querySelectorAll('button');
1935                for (const btn of buttons) {
1936                    const text = btn.textContent?.trim().toLowerCase();
1937                    if (text && (
1938                        text.includes('add to cart') || 
1939                        text.includes('add to bag') || 
1940                        text.includes('הוסף לסל') ||
1941                        text.includes('הוספה לסל') ||
1942                        text.includes('añadir a la bolsa') ||
1943                        text.includes('in den warenkorb')
1944                    )) {
1945                        addBtn = btn;
1946                        console.log('[Stock Tracker] Found add button by text:', text);
1947                        break;
1948                    }
1949                }
1950            }
1951            
1952            if (addBtn && !addBtn.disabled) {
1953                console.log('[Stock Tracker] Clicking add to cart button...');
1954                addBtn.click();
1955                console.log('[Stock Tracker] ✅ Added to cart successfully!');
1956                
1957                // Show success message after a delay
1958                setTimeout(() => {
1959                    showToast(`${task.label} נוסף לסל!`, 'success');
1960                    playNotificationSound();
1961                }, 1500);
1962            } else {
1963                console.warn('[Stock Tracker] Add to cart button not found or disabled');
1964                showToast('⚠️ לא מצאתי כפתור הוספה לסל', 'error');
1965            }
1966            
1967        } catch (err) {
1968            console.error('[Stock Tracker] Auto add to cart error:', err);
1969        }
1970    }
1971
1972    // UI Styles
1973    const styles = `
1974        .st-fab {
1975            position: fixed;
1976            right: 20px;
1977            bottom: 20px;
1978            z-index: 2147483646;
1979            background: linear-gradient(135deg, #25D366 0%, #22c55e 100%);
1980            color: #fff;
1981            border: none;
1982            border-radius: 50px;
1983            padding: 16px 24px;
1984            font-weight: 800;
1985            font-size: 16px;
1986            cursor: pointer;
1987            box-shadow: 0 8px 24px rgba(37, 211, 102, 0.4);
1988            transition: all 0.3s ease;
1989        }
1990        .st-fab:hover {
1991            transform: translateY(-2px);
1992            box-shadow: 0 12px 32px rgba(37, 211, 102, 0.6);
1993        }
1994        
1995        .st-fab-alert {
1996            animation: pulse 2s infinite;
1997        }
1998        
1999        @keyframes pulse {
2000            0%, 100% { box-shadow: 0 8px 24px rgba(37, 211, 102, 0.4); }
2001            50% { box-shadow: 0 8px 24px rgba(239, 68, 68, 0.6); }
2002        }
2003        
2004        .st-badge {
2005            position: absolute;
2006            top: -8px;
2007            right: -8px;
2008            background: #ef4444;
2009            color: #fff;
2010            border-radius: 50%;
2011            width: 24px;
2012            height: 24px;
2013            display: flex;
2014            align-items: center;
2015            justify-content: center;
2016            font-size: 12px;
2017            font-weight: 800;
2018            border: 2px solid #fff;
2019            animation: bounce 1s infinite;
2020        }
2021        
2022        @keyframes bounce {
2023            0%, 100% { transform: scale(1); }
2024            50% { transform: scale(1.1); }
2025        }
2026        
2027        .st-panel {
2028            position: fixed;
2029            right: 20px;
2030            top: 50%;
2031            transform: translateY(-50%);
2032            z-index: 2147483646;
2033            background: linear-gradient(180deg, #1a1a1a 0%, #0a0a0a 100%);
2034            color: #fff;
2035            border: 1px solid #333;
2036            border-radius: 20px;
2037            width: 520px;
2038            max-height: 90vh;
2039            overflow: hidden;
2040            display: none;
2041            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.8);
2042        }
2043        
2044        .st-header {
2045            padding: 24px;
2046            background: linear-gradient(135deg, #25D366 0%, #22c55e 100%);
2047            display: flex;
2048            justify-content: space-between;
2049            align-items: center;
2050            font-weight: 800;
2051            font-size: 20px;
2052        }
2053        
2054        .st-body {
2055            padding: 24px;
2056            max-height: calc(90vh - 90px);
2057            overflow-y: auto;
2058        }
2059        
2060        .st-body::-webkit-scrollbar { width: 8px; }
2061        .st-body::-webkit-scrollbar-track { background: #1a1a1a; }
2062        .st-body::-webkit-scrollbar-thumb { background: #444; border-radius: 4px; }
2063        .st-body::-webkit-scrollbar-thumb:hover { background: #555; }
2064        
2065        .st-input {
2066            width: 100%;
2067            background: #2a2a2a;
2068            color: #fff;
2069            border: 1px solid #444;
2070            border-radius: 10px;
2071            padding: 14px 16px;
2072            margin: 8px 0;
2073            font-size: 14px;
2074            box-sizing: border-box;
2075            transition: all 0.2s;
2076        }
2077        
2078        .st-input:focus {
2079            outline: none;
2080            border-color: #25D366;
2081            box-shadow: 0 0 0 3px rgba(37, 211, 102, 0.1);
2082        }
2083        
2084        .st-checkbox {
2085            display: flex;
2086            align-items: center;
2087            gap: 12px;
2088            margin: 12px 0;
2089            cursor: pointer;
2090            padding: 10px;
2091            border-radius: 8px;
2092            transition: background 0.2s;
2093        }
2094        
2095        .st-checkbox:hover { background: rgba(255, 255, 255, 0.05); }
2096        .st-checkbox input { width: 20px; height: 20px; cursor: pointer; accent-color: #25D366; }
2097        
2098        .st-btn {
2099            background: linear-gradient(135deg, #25D366 0%, #22c55e 100%);
2100            color: #fff;
2101            border: none;
2102            border-radius: 10px;
2103            padding: 14px 20px;
2104            font-weight: 700;
2105            cursor: pointer;
2106            width: 100%;
2107            margin: 8px 0;
2108            font-size: 14px;
2109            transition: all 0.2s ease;
2110        }
2111        .st-btn:hover {
2112            transform: translateY(-2px);
2113            box-shadow: 0 6px 16px rgba(37, 211, 102, 0.4);
2114        }
2115        
2116        .st-btn-secondary {
2117            background: #2a2a2a;
2118            border: 1px solid #444;
2119        }
2120        .st-btn-secondary:hover {
2121            background: #333;
2122            box-shadow: 0 4px 12px rgba(255, 255, 255, 0.1);
2123        }
2124        
2125        .st-btn-danger {
2126            background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
2127        }
2128        .st-btn-danger:hover {
2129            box-shadow: 0 6px 16px rgba(239, 68, 68, 0.4);
2130        }
2131        
2132        .st-btn-purple {
2133            background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
2134        }
2135        
2136        .st-btn-blue {
2137            background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
2138        }
2139        
2140        .st-item {
2141            background: linear-gradient(135deg, #2a2a2a 0%, #252525 100%);
2142            border: 1px solid #444;
2143            border-radius: 16px;
2144            padding: 20px;
2145            margin: 16px 0;
2146            transition: all 0.3s;
2147        }
2148        
2149        .st-item:hover {
2150            border-color: #25D366;
2151            box-shadow: 0 8px 24px rgba(37, 211, 102, 0.15);
2152            transform: translateY(-2px);
2153        }
2154        
2155        .st-size {
2156            display: inline-block;
2157            background: #1a1a1a;
2158            border: 2px solid #444;
2159            border-radius: 10px;
2160            padding: 10px 14px;
2161            margin: 6px 4px;
2162            font-size: 13px;
2163            font-weight: 700;
2164            cursor: pointer;
2165            transition: all 0.2s;
2166        }
2167        
2168        .st-size.in-stock {
2169            border-color: #22c55e;
2170            color: #22c55e;
2171            background: rgba(34, 197, 94, 0.1);
2172        }
2173        
2174        .st-size.out-of-stock {
2175            border-color: #ef4444;
2176            color: #ef4444;
2177            background: rgba(239, 68, 68, 0.1);
2178        }
2179        
2180        .st-size:hover { transform: scale(1.08); }
2181        
2182        .st-toast {
2183            position: fixed;
2184            top: 20px;
2185            right: 20px;
2186            background: #1a1a1a;
2187            color: #fff;
2188            padding: 16px 20px;
2189            border-radius: 12px;
2190            z-index: 2147483647;
2191            box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
2192            font-weight: 700;
2193            border-left: 4px solid #25D366;
2194            animation: slideIn 0.3s ease;
2195        }
2196        
2197        @keyframes slideIn {
2198            from { transform: translateX(400px); opacity: 0; }
2199            to { transform: translateX(0); opacity: 1; }
2200        }
2201        
2202        .st-toast.error { border-left-color: #ef4444; }
2203        .st-toast.info { border-left-color: #3b82f6; }
2204        
2205        .st-stats {
2206            background: linear-gradient(135deg, #2a2a2a 0%, #252525 100%);
2207            border: 1px solid #444;
2208            border-radius: 16px;
2209            padding: 20px;
2210            margin: 16px 0;
2211            display: grid;
2212            grid-template-columns: repeat(3, 1fr);
2213            gap: 16px;
2214            text-align: center;
2215        }
2216        
2217        .st-stat {
2218            padding: 16px;
2219            background: #1a1a1a;
2220            border-radius: 12px;
2221            transition: all 0.2s;
2222        }
2223        
2224        .st-stat:hover {
2225            background: #222;
2226            transform: translateY(-2px);
2227        }
2228        
2229        .st-stat-value {
2230            font-size: 28px;
2231            font-weight: 800;
2232            color: #25D366;
2233            margin-bottom: 4px;
2234        }
2235        
2236        .st-stat-label {
2237            font-size: 12px;
2238            color: #999;
2239            margin-top: 4px;
2240        }
2241        
2242        .st-info-box {
2243            background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 100%);
2244            border: 1px solid #3b82f6;
2245            border-radius: 12px;
2246            padding: 16px;
2247            margin: 16px 0;
2248        }
2249        
2250        .st-info-title {
2251            font-weight: 700;
2252            margin-bottom: 8px;
2253            color: #60a5fa;
2254            font-size: 14px;
2255        }
2256        
2257        .st-info-content {
2258            font-size: 12px;
2259            color: #93c5fd;
2260            line-height: 1.6;
2261        }
2262        
2263        .st-countdown-box {
2264            text-align: center;
2265            padding: 16px;
2266            background: linear-gradient(135deg, #2a2a2a 0%, #252525 100%);
2267            border-radius: 12px;
2268            margin: 16px 0;
2269            border: 1px solid #444;
2270        }
2271        
2272        .st-countdown-label {
2273            color: #999;
2274            font-size: 12px;
2275            margin-bottom: 8px;
2276        }
2277        
2278        .st-countdown-time {
2279            color: #25D366;
2280            font-weight: 700;
2281            font-size: 24px;
2282        }
2283        
2284        .st-button-grid {
2285            display: grid;
2286            grid-template-columns: repeat(5, 1fr);
2287            gap: 8px;
2288            margin-top: 16px;
2289        }
2290        
2291        .st-button-grid button {
2292            padding: 10px 8px;
2293            font-size: 11px;
2294            white-space: nowrap;
2295        }
2296        
2297        .st-tag {
2298            display: inline-block;
2299            padding: 4px 8px;
2300            border-radius: 999px;
2301            background: #1f2937;
2302            color: #9ca3af;
2303            margin: 2px;
2304            font-size: 11px;
2305        }
2306    `;
2307
2308    // Create UI
2309    function createUI() {
2310        const styleEl = document.createElement('style');
2311        styleEl.textContent = styles;
2312        document.head.appendChild(styleEl);
2313
2314        const fab = document.createElement('button');
2315        fab.className = 'st-fab';
2316        fab.innerHTML = '📦 מעקב מלאי/מחיר';
2317        document.body.appendChild(fab);
2318        
2319        // Make FAB draggable
2320        let isDragging = false;
2321        let hasMoved = false;
2322        let currentX, currentY, initialX, initialY, xOffset = 0, yOffset = 0;
2323        
2324        fab.addEventListener('mousedown', dragStart);
2325        document.addEventListener('mousemove', drag);
2326        document.addEventListener('mouseup', dragEnd);
2327        fab.addEventListener('touchstart', dragStart);
2328        document.addEventListener('touchmove', drag);
2329        document.addEventListener('touchend', dragEnd);
2330        
2331        function dragStart(e) {
2332            if (e.type === 'touchstart') {
2333                initialX = e.touches[0].clientX - xOffset;
2334                initialY = e.touches[0].clientY - yOffset;
2335            } else {
2336                initialX = e.clientX - xOffset;
2337                initialY = e.clientY - yOffset;
2338            }
2339            
2340            if (e.target === fab || fab.contains(e.target)) {
2341                isDragging = true;
2342                hasMoved = false;
2343            }
2344        }
2345        
2346        function drag(e) {
2347            if (isDragging) {
2348                e.preventDefault();
2349                
2350                if (e.type === 'touchmove') {
2351                    currentX = e.touches[0].clientX - initialX;
2352                    currentY = e.touches[0].clientY - initialY;
2353                } else {
2354                    currentX = e.clientX - initialX;
2355                    currentY = e.clientY - initialY;
2356                }
2357                
2358                const distance = Math.sqrt(Math.pow(currentX - xOffset, 2) + Math.pow(currentY - yOffset, 2));
2359                if (distance > 5) hasMoved = true;
2360                
2361                xOffset = currentX;
2362                yOffset = currentY;
2363                fab.style.transform = `translate3d(${currentX}px, ${currentY}px, 0)`;
2364            }
2365        }
2366        
2367        function dragEnd() {
2368            if (isDragging) {
2369                initialX = currentX;
2370                initialY = currentY;
2371                isDragging = false;
2372                (async () => { await GM.setValue('fab_position', JSON.stringify({ x: xOffset, y: yOffset })); })();
2373            }
2374        }
2375        
2376        (async () => {
2377            const savedPos = await GM.getValue('fab_position');
2378            if (savedPos) {
2379                const pos = JSON.parse(savedPos);
2380                xOffset = pos.x;
2381                yOffset = pos.y;
2382                fab.style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;
2383            }
2384        })();
2385
2386        const panel = document.createElement('div');
2387        panel.className = 'st-panel';
2388        panel.innerHTML = `
2389            <div class="st-header">
2390                <div>📦 WhatsApp Alerts + Filters</div>
2391                <button class="st-btn-secondary" id="st-close" style="padding:8px 12px;border-radius:50%;width:auto;border:0"></button>
2392            </div>
2393            <div class="st-body">
2394                <div id="st-stats"></div>
2395                
2396                <div style="margin-bottom:20px">
2397                    <label style="display:block;margin-bottom:8px;font-weight:700">📱 מספר WhatsApp</label>
2398                    <input type="text" class="st-input" id="st-phone" placeholder="972501234567">
2399                </div>
2400                
2401                <div style="margin-bottom:20px">
2402                    <label style="display:block;margin-bottom:8px;font-weight:700">⏱️ בדוק מלאי כל (דקות)</label>
2403                    <input type="number" class="st-input" id="st-interval" placeholder="5" min="1" value="5">
2404                </div>
2405                
2406                <label class="st-checkbox">
2407                    <input type="checkbox" id="st-notifications" checked>
2408                    <span>🔔 הצג התראות בדפדפן</span>
2409                </label>
2410                
2411                <label class="st-checkbox">
2412                    <input type="checkbox" id="st-sound" checked>
2413                    <span>🔊 השמע צליל התראה</span>
2414                </label>
2415                
2416                <label class="st-checkbox">
2417                    <input type="checkbox" id="st-autocart" checked>
2418                    <span>🛒 הוסף לסל אוטומטית</span>
2419                </label>
2420                
2421                <button class="st-btn" id="st-add">➕ הוסף מהעמוד</button>
2422                <button class="st-btn st-btn-purple" id="st-quick-add">⚡ הוסף את כל המידות</button>
2423                <button class="st-btn st-btn-secondary" id="st-check">🔄 בדוק עכשיו</button>
2424                <button class="st-btn st-btn-blue" id="st-test">🧪 בדיקת WhatsApp</button>
2425                
2426                <div class="st-countdown-box">
2427                    <div class="st-countdown-label">⏱️ בדיקה הבאה בעוד:</div>
2428                    <div class="st-countdown-time" id="st-countdown-text">--:--</div>
2429                </div>
2430                
2431                <button class="st-btn st-btn-secondary st-btn-danger" id="st-clear" style="font-size:12px">🗑️ מחק הכל</button>
2432                
2433                <div style="margin-top:20px">
2434                    <h3 style="margin-bottom:12px">מוצרים במעקב:</h3>
2435                    
2436                    <div class="st-info-box">
2437                        <div class="st-info-title">ℹ️ איך זה עובד?</div>
2438                        <div class="st-info-content">
2439                            • המערכת בודקת מלאי אוטומטית כל <span id="st-interval-display">5</span> דקות<br>
2440<strong>חשוב:</strong> השאר דף אחד פתוח בדפדפן<br>
2441                            • המערכת תשלח התראת WhatsApp כשמידה חוזרת למלאי<br>
2442<span id="st-status-text" style="color:#22c55e;font-weight:700">🟢 המערכת פעילה</span>
2443                        </div>
2444                    </div>
2445                    
2446                    <div style="margin-bottom:12px">
2447                        <input type="text" class="st-input" id="st-search" placeholder="🔍 חפש מוצר..." style="margin:0">
2448                    </div>
2449                    
2450                    <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:12px">
2451                        <select class="st-input" id="st-filter" style="margin:0">
2452                            <option value="all">הכל</option>
2453                            <option value="stock-in">במלאי (הכל במלאי)</option>
2454                            <option value="stock-out">לא במלאי (הכל חסר)</option>
2455                            <option value="stock-partial">חלקי במלאי</option>
2456                            <option value="mode-stock">מצב: מלאי</option>
2457                            <option value="mode-price">מצב: מחיר</option>
2458                        </select>
2459                        <select class="st-input" id="st-sort" style="margin:0">
2460                            <option value="date-desc">חדש → ישן</option>
2461                            <option value="date-asc">ישן → חדש</option>
2462                            <option value="title-asc">שם א׳→ת׳</option>
2463                            <option value="title-desc">שם ת׳→א׳</option>
2464                            <option value="site-asc">דומיין א׳→ת׳</option>
2465                            <option value="price-asc">מחיר נמוך→גבוה</option>
2466                            <option value="price-desc">מחיר גבוה→נמוך</option>
2467                            <option value="stock-desc">יותר במלאי</option>
2468                            <option value="stock-asc">פחות במלאי</option>
2469                        </select>
2470                    </div>
2471                    
2472                    <div id="st-list"></div>
2473                </div>
2474            </div>
2475        `;
2476        document.body.appendChild(panel);
2477
2478        // Event listeners
2479        fab.onclick = async () => {
2480            if (hasMoved) {
2481                hasMoved = false;
2482                return;
2483            }
2484            const isOpen = panel.style.display === 'block';
2485            panel.style.display = isOpen ? 'none' : 'block';
2486            if (!isOpen) {
2487                await loadPrefsToUI();
2488                await renderList();
2489                await renderStats();
2490                applyDarkMode();
2491            }
2492        };
2493
2494        panel.querySelector('#st-close').onclick = () => {
2495            panel.style.display = 'none';
2496        };
2497
2498        panel.querySelector('#st-add').onclick = addCurrentProduct;
2499        panel.querySelector('#st-quick-add').onclick = quickAddAllSizes;
2500        panel.querySelector('#st-check').onclick = async () => {
2501            await checkAllStock();
2502            const prefs = await getPrefs();
2503            const interval = (prefs.checkIntervalMin || 5) * 60 * 1000;
2504            nextCheckTime = Date.now() + interval;
2505        };
2506        panel.querySelector('#st-test').onclick = async () => {
2507            const phoneInput = document.querySelector('#st-phone');
2508            const phoneValue = phoneInput ? phoneInput.value.trim() : '';
2509            
2510            if (!phoneValue) {
2511                showToast('⚠️ הזן מספר WhatsApp קודם', 'error');
2512                return;
2513            }
2514            
2515            const prefs = await getPrefs();
2516            prefs.waPhone = phoneValue;
2517            await savePrefs(prefs);
2518            
2519            const testMessage = '🧪 בדיקת מערכת\n\nזוהי הודעת בדיקה.\nאם קיבלת הודעה זו - המערכת עובדת תקין! ✅';
2520            openWhatsApp(testMessage, prefs.waPhone);
2521            showToast('📱 פותח WhatsApp...', 'info');
2522        };
2523        panel.querySelector('#st-clear').onclick = clearAllData;
2524
2525        panel.querySelector('#st-phone').onchange = async (e) => {
2526            const prefs = await getPrefs();
2527            prefs.waPhone = e.target.value.trim();
2528            await savePrefs(prefs);
2529            showToast('✅ מספר WhatsApp נשמר', 'success');
2530        };
2531        
2532        panel.querySelector('#st-interval').onchange = async (e) => {
2533            const prefs = await getPrefs();
2534            prefs.checkIntervalMin = Math.max(1, parseInt(e.target.value) || 5);
2535            await savePrefs(prefs);
2536            const display = document.querySelector('#st-interval-display');
2537            if (display) display.textContent = prefs.checkIntervalMin;
2538            showToast('✅ זמן ריענון עודכן', 'success');
2539            await startBackgroundCheck();
2540        };
2541        
2542        panel.querySelector('#st-notifications').onchange = async (e) => {
2543            const prefs = await getPrefs();
2544            prefs.notifications = e.target.checked;
2545            await savePrefs(prefs);
2546            showToast(e.target.checked ? '🔔 התראות מופעלות' : '🔕 התראות כבויות', 'info');
2547        };
2548        
2549        panel.querySelector('#st-sound').onchange = async (e) => {
2550            const prefs = await getPrefs();
2551            prefs.soundAlerts = e.target.checked;
2552            await savePrefs(prefs);
2553            if (e.target.checked) {
2554                playNotificationSound();
2555                showToast('🔊 צלילי התראה מופעלים', 'info');
2556            } else {
2557                showToast('🔇 צלילי התראה כבויים', 'info');
2558            }
2559        };
2560        
2561        panel.querySelector('#st-autocart').onchange = async (e) => {
2562            const prefs = await getPrefs();
2563            prefs.autoAddToCart = e.target.checked;
2564            await savePrefs(prefs);
2565            showToast(e.target.checked ? '🛒 הוספה אוטומטית הופעל' : '🛒 הוספה אוטומטית כובה', 'info');
2566        };
2567        
2568        const searchEl = panel.querySelector('#st-search');
2569        const sortEl = panel.querySelector('#st-sort');
2570        const filterEl = panel.querySelector('#st-filter');
2571        
2572        if (searchEl) searchEl.oninput = () => renderList();
2573        if (sortEl) sortEl.onchange = () => renderList();
2574        if (filterEl) filterEl.onchange = () => renderList();
2575    }
2576
2577    // Toast notification
2578    function showToast(message, type = 'success') {
2579        const toast = document.createElement('div');
2580        toast.className = `st-toast ${type}`;
2581        toast.textContent = message;
2582        document.body.appendChild(toast);
2583        
2584        setTimeout(() => {
2585            toast.style.transition = 'opacity 0.3s';
2586            toast.style.opacity = '0';
2587            setTimeout(() => toast.remove(), 300);
2588        }, 3500);
2589    }
2590
2591    // Load preferences to UI
2592    async function loadPrefsToUI() {
2593        const prefs = await getPrefs();
2594        const phoneEl = document.querySelector('#st-phone');
2595        const intervalEl = document.querySelector('#st-interval');
2596        const notificationsEl = document.querySelector('#st-notifications');
2597        const soundEl = document.querySelector('#st-sound');
2598        const autoCartEl = document.querySelector('#st-autocart');
2599        const displayEl = document.querySelector('#st-interval-display');
2600        
2601        if (phoneEl) phoneEl.value = prefs.waPhone || '';
2602        if (intervalEl) intervalEl.value = prefs.checkIntervalMin || 5;
2603        if (notificationsEl) notificationsEl.checked = prefs.notifications !== false;
2604        if (soundEl) soundEl.checked = prefs.soundAlerts !== false;
2605        if (autoCartEl) autoCartEl.checked = prefs.autoAddToCart !== false;
2606        if (displayEl) displayEl.textContent = prefs.checkIntervalMin || 5;
2607    }
2608
2609    // Render statistics
2610    async function renderStats() {
2611        const items = await getItems();
2612        const statsEl = document.querySelector('#st-stats');
2613        
2614        if (!statsEl) return;
2615        
2616        const totalItems = items.length;
2617        const totalSizes = items.reduce((sum, item) => item.mode === 'stock' ? sum + item.sizes.length : sum, 0);
2618        const inStockSizes = items.reduce((sum, item) => 
2619            item.mode === 'stock' ? sum + item.sizes.filter(s => s.inStock).length : sum, 0
2620        );
2621        
2622        let totalPrice = 0;
2623        let priceCount = 0;
2624        items.forEach(item => {
2625            const price = item.mode === 'price' ? item.priceNow : parsePrice(item.price);
2626            if (price) {
2627                totalPrice += price;
2628                priceCount++;
2629            }
2630        });
2631        
2632        const avgPrice = priceCount > 0 ? (totalPrice / priceCount).toFixed(2) : 0;
2633        
2634        statsEl.innerHTML = `
2635            <div class="st-stat">
2636                <div class="st-stat-value">${totalItems}</div>
2637                <div class="st-stat-label">מוצרים</div>
2638            </div>
2639            <div class="st-stat">
2640                <div class="st-stat-value">${totalSizes}</div>
2641                <div class="st-stat-label">מידות</div>
2642            </div>
2643            <div class="st-stat">
2644                <div class="st-stat-value">${inStockSizes}</div>
2645                <div class="st-stat-label">במלאי</div>
2646            </div>
2647            <div class="st-stat" style="grid-column: span 3">
2648                <div class="st-stat-value" style="color:#fbbf24">${totalPrice.toFixed(2)}</div>
2649                <div class="st-stat-label">סה"כ מחיר | ממוצע: ₪${avgPrice}</div>
2650            </div>
2651        `;
2652    }
2653
2654    // Add current product
2655    async function addCurrentProduct() {
2656        const adapter = getAdapter();
2657        if (!adapter) {
2658            showToast('⚠️ אתר זה לא נתמך כרגע', 'error');
2659            return;
2660        }
2661
2662        const title = adapter.title();
2663        const price = adapter.price();
2664        const image = adapter.image();
2665        const url = location.href.split('#')[0].split('?')[0];
2666
2667        if (!title) {
2668            showToast('⚠️ לא הצלחתי לזהות את המוצר', 'error');
2669            return;
2670        }
2671
2672        if (adapter.mode === 'price') {
2673            const target = prompt('הזן מחיר יעד (לדוגמה 750):', price ?? '');
2674            const tNum = target ? parsePrice(String(target)) : null;
2675            if (!tNum) {
2676                showToast('⚠️ מחיר יעד לא תקין');
2677                return;
2678            }
2679            const items = await getItems();
2680            items.push({
2681                id: Date.now().toString(),
2682                mode: 'price',
2683                title, url, image,
2684                priceNow: price ?? null,
2685                targetPrice: tNum,
2686                lastChecked: 0,
2687                addedAt: Date.now()
2688            });
2689            await saveItems(items);
2690            showToast('✅ נוסף מעקב מחיר');
2691            await renderList();
2692            await renderStats();
2693            return;
2694        }
2695
2696        showToast('⏳ מחפש מידות...', 'info');
2697
2698        const sizes = await adapter.getSizes();
2699        
2700        if (!sizes || sizes.length === 0) {
2701            showToast('⚠️ לא נמצאו מידות', 'error');
2702            return;
2703        }
2704
2705        showSizeSelection(title, price ? `${price}` : 'לא זמין', image, url, sizes);
2706    }
2707
2708    // Show size selection modal
2709    function showSizeSelection(title, price, image, url, sizes) {
2710        const overlay = document.createElement('div');
2711        overlay.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.9);z-index:2147483647;display:flex;align-items:center;justify-content:center';
2712        
2713        const modal = document.createElement('div');
2714        modal.style.cssText = 'background:#1a1a1a;border:1px solid #333;border-radius:16px;padding:24px;max-width:500px;width:90%;max-height:80vh;overflow-y:auto';
2715        
2716        modal.innerHTML = `
2717            <h2 style="margin:0 0 16px 0;color:#fff">🧵 בחר מידות למעקב</h2>
2718            <p style="color:#999;margin-bottom:20px">${title}</p>
2719            <p style="color:#999;margin-bottom:20px">תקבל התראת WhatsApp כשהמידה חוזרת למלאי</p>
2720            <div id="size-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(80px,1fr));gap:10px;margin-bottom:20px"></div>
2721            <button class="st-btn" id="confirm-sizes">✅ התחל מעקב</button>
2722            <button class="st-btn st-btn-secondary" id="cancel-sizes" style="margin-top:0">❌ ביטול</button>
2723        `;
2724        
2725        overlay.appendChild(modal);
2726        document.body.appendChild(overlay);
2727        
2728        const sizeGrid = modal.querySelector('#size-grid');
2729        const selectedSizes = new Set();
2730        
2731        sizes.forEach((size, idx) => {
2732            const btn = document.createElement('button');
2733            btn.style.cssText = `
2734                background: #2a2a2a;
2735                color: #fff;
2736                border: 2px solid ${size.inStock ? '#22c55e' : '#ef4444'};
2737                border-radius: 8px;
2738                padding: 12px;
2739                font-weight: 700;
2740                cursor: pointer;
2741                transition: all 0.2s;
2742            `;
2743            btn.textContent = `${size.label} ${size.inStock ? '✅' : '❌'}`;
2744            
2745            btn.onclick = () => {
2746                if (selectedSizes.has(idx)) {
2747                    selectedSizes.delete(idx);
2748                    btn.style.background = '#2a2a2a';
2749                    btn.style.color = '#fff';
2750                } else {
2751                    selectedSizes.add(idx);
2752                    btn.style.background = '#25D366';
2753                    btn.style.color = '#000';
2754                }
2755            };
2756            
2757            sizeGrid.appendChild(btn);
2758        });
2759        
2760        modal.querySelector('#confirm-sizes').onclick = async () => {
2761            if (selectedSizes.size === 0) {
2762                showToast('⚠️ בחר לפחות מידה אחת', 'error');
2763                return;
2764            }
2765            
2766            const items = await getItems();
2767            
2768            const selectedSizeData = Array.from(selectedSizes).map(idx => ({
2769                label: sizes[idx].label,
2770                selector: buildSelector(sizes[idx].element),
2771                inStock: sizes[idx].inStock,
2772                lastChecked: Date.now(),
2773                lastAlertAt: 0
2774            }));
2775            
2776            items.push({
2777                id: Date.now().toString(),
2778                mode: 'stock',
2779                title, price, image, url,
2780                sizes: selectedSizeData,
2781                addedAt: Date.now()
2782            });
2783            
2784            await saveItems(items);
2785            overlay.remove();
2786            await renderList();
2787            await renderStats();
2788            await updateFabBadge();
2789            showToast(`✅ נוספו ${selectedSizes.size} מידות למעקב!`, 'success');
2790        };
2791        
2792        modal.querySelector('#cancel-sizes').onclick = () => {
2793            overlay.remove();
2794        };
2795        
2796        overlay.onclick = (e) => {
2797            if (e.target === overlay) overlay.remove();
2798        };
2799    }
2800
2801    // Render items list
2802    async function renderList() {
2803        const items = await getItems();
2804        const listEl = document.querySelector('#st-list');
2805        
2806        if (!listEl) return;
2807        
2808        if (items.length === 0) {
2809            listEl.innerHTML = '<p style="text-align:center;color:#999;padding:20px">אין מוצרים במעקב</p>';
2810            return;
2811        }
2812        
2813        const searchQuery = document.querySelector('#st-search')?.value?.toLowerCase() || '';
2814        const sortValue = document.querySelector('#st-sort')?.value || 'date-desc';
2815        const filterValue = document.querySelector('#st-filter')?.value || 'all';
2816        
2817        let filteredItems = [...items];
2818        
2819        if (searchQuery) {
2820            filteredItems = filteredItems.filter(item => 
2821                item.title.toLowerCase().includes(searchQuery) ||
2822                item.url.toLowerCase().includes(searchQuery)
2823            );
2824        }
2825        
2826        filteredItems = filteredItems.filter(item => {
2827            if (filterValue === 'mode-stock') return item.mode === 'stock';
2828            if (filterValue === 'mode-price') return item.mode === 'price';
2829            if (item.mode === 'stock') {
2830                const inC = item.sizes.filter(s => s.inStock).length;
2831                const total = item.sizes.length;
2832                if (filterValue === 'stock-in') return inC === total;
2833                if (filterValue === 'stock-out') return inC === 0;
2834                if (filterValue === 'stock-partial') return inC > 0 && inC < total;
2835            }
2836            return true;
2837        });
2838        
2839        filteredItems.sort((a, b) => {
2840            if (sortValue === 'date-desc') return (b.addedAt || 0) - (a.addedAt || 0);
2841            if (sortValue === 'date-asc') return (a.addedAt || 0) - (b.addedAt || 0);
2842            if (sortValue === 'title-asc') return (a.title || '').localeCompare(b.title || '');
2843            if (sortValue === 'title-desc') return (b.title || '').localeCompare(a.title || '');
2844            if (sortValue === 'site-asc') {
2845                const da = new URL(a.url).hostname;
2846                const db = new URL(b.url).hostname;
2847                return da.localeCompare(db);
2848            }
2849            if (sortValue === 'price-asc' || sortValue === 'price-desc') {
2850                const pa = a.mode === 'price' ? (a.priceNow ?? 9e15) : parsePrice(a.price) || 9e15;
2851                const pb = b.mode === 'price' ? (b.priceNow ?? 9e15) : parsePrice(b.price) || 9e15;
2852                return sortValue === 'price-asc' ? (pa - pb) : (pb - pa);
2853            }
2854            if (sortValue === 'stock-desc' || sortValue === 'stock-asc') {
2855                const ca = a.mode === 'stock' ? a.sizes.filter(x => x.inStock).length : -1;
2856                const cb = b.mode === 'stock' ? b.sizes.filter(x => x.inStock).length : -1;
2857                return sortValue === 'stock-desc' ? (cb - ca) : (ca - cb);
2858            }
2859            return 0;
2860        });
2861        
2862        if (filteredItems.length === 0) {
2863            listEl.innerHTML = '<p style="text-align:center;color:#999;padding:20px">לא נמצאו מוצרים מתאימים</p>';
2864            return;
2865        }
2866        
2867        listEl.innerHTML = filteredItems.map(item => {
2868            if (item.mode === 'price') {
2869                return `<div class="st-item" data-item-id="${item.id}">
2870                    ${item.image ? `<img src="${item.image}" style="width:60px;height:60px;object-fit:cover;border-radius:8px;margin-bottom:12px">` : ''}
2871                    <div style="font-weight:700;margin-bottom:4px">${item.title}</div>
2872                    <div style="color:#999;font-size:11px;margin-bottom:4px">🏪 ${getSiteName(item.url)}</div>
2873                    <div class="st-tag">✈️ מצב: מחיר</div>
2874                    <div class="st-tag">🎯 יעד: ${item.targetPrice ? `${item.targetPrice}` : '—'}</div>
2875                    <div class="st-tag">מחיר נוכחי: ${item.priceNow != null ? `${item.priceNow}` : '—'}</div>
2876                    <div class="st-tag">נוסף: ${formatTimeAgo(item.addedAt)}</div>
2877                    ${item.lastChecked ? `<div class="st-tag">🔄 נבדק: ${formatTimeAgo(item.lastChecked)}</div>` : ''}
2878                    <div class="st-button-grid">
2879                        <button class="st-btn st-btn-secondary st-action-btn" data-action="check" data-id="${item.id}">🔍 בדוק</button>
2880                        <button class="st-btn st-btn-secondary st-action-btn" data-action="open" data-url="${item.url}">🔗 פתח</button>
2881                        <button class="st-btn st-btn-secondary st-action-btn" data-action="copy" data-url="${item.url}">📋 העתק</button>
2882                        <button class="st-btn st-btn-secondary st-action-btn" data-action="share" data-id="${item.id}">📱 שתף</button>
2883                        <button class="st-btn st-btn-secondary st-action-btn" data-action="remove" data-id="${item.id}">🗑️ מחק</button>
2884                    </div>
2885                </div>`;
2886            } else {
2887                const inStockCount = item.sizes.filter(s => s.inStock).length;
2888                const totalCount = item.sizes.length;
2889                const siteName = getSiteName(item.url);
2890                
2891                return `<div class="st-item" data-item-id="${item.id}">
2892                    <div style="display:flex;gap:12px;margin-bottom:12px">
2893                        ${item.image ? `<img src="${item.image}" style="width:60px;height:60px;object-fit:cover;border-radius:8px">` : ''}
2894                        <div style="flex:1">
2895                            <div style="font-weight:700;margin-bottom:4px">${item.title}</div>
2896                            <div style="color:#999;font-size:11px;margin-bottom:4px">🏪 ${siteName}</div>
2897                            <div class="st-tag">🧵 מצב: מלאי</div>
2898                            <div class="st-tag">📊 ${inStockCount}/${totalCount} במלאי</div>
2899                            <div class="st-tag">💰 ${item.price || 'לא זמין'}</div>
2900                            <div class="st-tag">נוסף: ${formatTimeAgo(item.addedAt)}</div>
2901                            ${item.sizes[0]?.lastChecked ? `<div class="st-tag">🔄 נבדק: ${formatTimeAgo(item.sizes[0].lastChecked)}</div>` : ''}
2902                        </div>
2903                    </div>
2904                    <div style="margin:8px 0">
2905                        ${item.sizes.map(s => `
2906                            <span class="st-size ${s.inStock ? 'in-stock' : 'out-of-stock'}" title="לחץ לפתיחת המוצר" onclick="window.open('${item.url}', '_blank')">
2907                                ${s.label} ${s.inStock ? '✅' : '❌'}
2908                            </span>
2909                        `).join('')}
2910                    </div>
2911                    <div class="st-button-grid">
2912                        <button class="st-btn st-btn-secondary st-action-btn" data-action="check" data-id="${item.id}">🔍 בדוק</button>
2913                        <button class="st-btn st-btn-secondary st-action-btn" data-action="open" data-url="${item.url}">🔗 פתח</button>
2914                        <button class="st-btn st-btn-secondary st-action-btn" data-action="edit" data-id="${item.id}">✏️ ערוך</button>
2915                        <button class="st-btn st-btn-secondary st-action-btn" data-action="copy" data-url="${item.url}">📋 העתק</button>
2916                        <button class="st-btn st-btn-secondary st-action-btn" data-action="share" data-id="${item.id}">📱 שתף</button>
2917                        <button class="st-btn st-btn-secondary st-action-btn" data-action="remove" data-id="${item.id}">🗑️ מחק</button>
2918                    </div>
2919                </div>`;
2920            }
2921        }).join('');
2922        
2923        listEl.querySelectorAll('.st-action-btn').forEach(btn => {
2924            btn.addEventListener('click', async function(e) {
2925                e.preventDefault();
2926                e.stopPropagation();
2927                
2928                const action = this.getAttribute('data-action');
2929                const id = this.getAttribute('data-id');
2930                const url = this.getAttribute('data-url');
2931                
2932                if (action === 'check') {
2933                    await checkSingleItem(id);
2934                } else if (action === 'open') {
2935                    window.open(url, '_blank');
2936                } else if (action === 'edit') {
2937                    window.open(url, '_blank');
2938                } else if (action === 'copy') {
2939                    await copyProductLink(url);
2940                } else if (action === 'share') {
2941                    const items = await getItems();
2942                    const item = items.find(i => i.id === id);
2943                    if (item) await shareProductOnWhatsApp(item);
2944                } else if (action === 'remove') {
2945                    await removeItem(id);
2946                }
2947            });
2948        });
2949        
2950        // Render recommendations at the bottom
2951        await renderRecommendations();
2952    }
2953
2954    // Remove item
2955    async function removeItem(id) {
2956        if (!confirm('האם אתה בטוח שברצונך למחוק מוצר זה?')) return;
2957        
2958        const items = await getItems();
2959        const filtered = items.filter(item => item.id !== id);
2960        
2961        await saveItems(filtered);
2962        await renderList();
2963        await renderStats();
2964        await updateFabBadge();
2965        showToast('🗑️ מוצר נמחק בהצלחה', 'success');
2966    }
2967
2968    // Start background checking
2969    let checkInterval = null;
2970    async function startBackgroundCheck() {
2971        if (checkInterval) clearInterval(checkInterval);
2972        if (countdownInterval) clearInterval(countdownInterval);
2973        
2974        const prefs = await getPrefs();
2975        const interval = (prefs.checkIntervalMin || 5) * 60 * 1000;
2976        
2977        // Check if there's a saved next check time
2978        const savedNextCheck = await GM.getValue('next_check_time');
2979        if (savedNextCheck) {
2980            const savedTime = parseInt(savedNextCheck);
2981            const now = Date.now();
2982            
2983            // If saved time is in the future, use it
2984            if (savedTime > now) {
2985                nextCheckTime = savedTime;
2986                console.log('[Stock Tracker] Restored next check time from storage');
2987            } else {
2988                // Saved time has passed, check now and set new time
2989                nextCheckTime = now + interval;
2990                await GM.setValue('next_check_time', nextCheckTime.toString());
2991                console.log('[Stock Tracker] Saved time passed, checking now...');
2992                setTimeout(() => checkAllStock(), 2000);
2993            }
2994        } else {
2995            // No saved time, set new one
2996            nextCheckTime = Date.now() + interval;
2997            await GM.setValue('next_check_time', nextCheckTime.toString());
2998        }
2999        
3000        countdownInterval = setInterval(updateCountdown, 1000);
3001        updateCountdown();
3002        
3003        checkInterval = setInterval(async () => {
3004            const now = Date.now();
3005            if (now >= nextCheckTime) {
3006                nextCheckTime = now + interval;
3007                await GM.setValue('next_check_time', nextCheckTime.toString());
3008                await checkAllStock();
3009            }
3010        }, 5000); // Check every 5 seconds if it's time to run
3011        
3012        console.log('[Stock Tracker] Background check started. Interval:', interval / 60000, 'minutes');
3013        console.log('[Stock Tracker] Next check at:', new Date(nextCheckTime).toLocaleTimeString('he-IL'));
3014    }
3015
3016    // Update countdown display
3017    function updateCountdown() {
3018        const countdownEl = document.querySelector('#st-countdown-text');
3019        if (!countdownEl || !nextCheckTime) return;
3020        
3021        const now = Date.now();
3022        const diff = nextCheckTime - now;
3023        
3024        if (diff <= 0) {
3025            countdownEl.textContent = '00:00';
3026            return;
3027        }
3028        
3029        const minutes = Math.floor(diff / 60000);
3030        const seconds = Math.floor((diff % 60000) / 1000);
3031        
3032        countdownEl.textContent = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
3033    }
3034
3035    // Keep-alive mechanism to prevent tab from sleeping
3036    function keepAlive() {
3037        setInterval(() => {
3038            // Ping to keep the script active
3039            console.log('[Stock Tracker] Keep-alive ping:', new Date().toLocaleTimeString('he-IL'));
3040        }, 60000); // Every minute
3041    }
3042
3043    // Visibility change handler - resume checking when tab becomes visible
3044    function handleVisibilityChange() {
3045        document.addEventListener('visibilitychange', async () => {
3046            if (!document.hidden) {
3047                console.log('[Stock Tracker] Tab became visible, resuming...');
3048                const items = await getItems();
3049                if (items.length > 0) {
3050                    await startBackgroundCheck();
3051                    await updateFabBadge();
3052                }
3053            }
3054        });
3055    }
3056
3057    // Initialize
3058    async function init() {
3059        console.log('[Stock Tracker] Initializing...');
3060        
3061        // Always run on WhatsApp for auto-send functionality
3062        if (location.host.includes('web.whatsapp.com')) {
3063            console.log('[Stock Tracker] WhatsApp detected - running auto-send');
3064            autoSendWhatsApp();
3065            return;
3066        }
3067        
3068        if (isBlockedSite()) {
3069            console.log('[Stock Tracker] Blocked site detected');
3070            return;
3071        }
3072        
3073        const items = await getItems();
3074        const isEcommerce = isEcommerceSite();
3075        
3076        // Track page view for recommendations
3077        if (isEcommerce) {
3078            await trackPageView();
3079        }
3080        
3081        // Check if we need to auto-add to cart
3082        await autoAddToCartOnLoad();
3083        
3084        // Fix old selectors
3085        if (items.length > 0) {
3086            let needsSave = false;
3087            items.forEach(item => {
3088                if (item.mode === 'stock') {
3089                    item.sizes.forEach(size => {
3090                        if (typeof size.selector === 'string' && 
3091                            (size.selector.includes('--disabled') || 
3092                             size.selector.includes('--unavailable') || 
3093                             size.selector.includes('--enabled'))) {
3094                            size.selector = size.selector
3095                                .replace(/\.size-selector-sizes-size--disabled/g, '')
3096                                .replace(/\.size-selector-sizes-size--unavailable/g, '')
3097                                .replace(/\.size-selector-sizes-size--enabled/g, '')
3098                                .replace(/\.is-disabled/g, '')
3099                                .replace(/\.is-unavailable/g, '')
3100                                .replace(/\.disabled/g, '')
3101                                .replace(/\.unavailable/g, '')
3102                                .replace(/\.enabled/g, '');
3103                            needsSave = true;
3104                        }
3105                    });
3106                }
3107            });
3108            
3109            if (needsSave) {
3110                await saveItems(items);
3111                console.log('[Stock Tracker] Fixed old selectors');
3112            }
3113        }
3114        
3115        if (items.length === 0 && !isEcommerce) {
3116            console.log('[Stock Tracker] Not an e-commerce site and no tracked items');
3117            return;
3118        }
3119        
3120        if (!document.body) {
3121            await new Promise(resolve => {
3122                if (document.readyState === 'loading') {
3123                    document.addEventListener('DOMContentLoaded', resolve);
3124                } else {
3125                    resolve();
3126                }
3127            });
3128        }
3129        
3130        createUI();
3131        
3132        if (items.length > 0) {
3133            await startBackgroundCheck();
3134            keepAlive(); // Start keep-alive mechanism
3135            handleVisibilityChange(); // Handle tab visibility changes
3136        }
3137        
3138        await updateFabBadge();
3139        
3140        console.log('[Stock Tracker] Ready!');
3141        console.log('[Stock Tracker] 💡 TIP: Keep this tab open for automatic stock checking');
3142    }
3143
3144    // Share product on WhatsApp
3145    async function shareProductOnWhatsApp(item) {
3146        const prefs = await getPrefs();
3147        
3148        let message = `🛍️ ${item.title}\n\n`;
3149        
3150        if (item.mode === 'price') {
3151            message += `💰 מחיר: ${item.priceNow ? `${item.priceNow}` : 'לא זמין'}\n`;
3152            if (item.targetPrice) message += `🎯 יעד: ₪${item.targetPrice}\n`;
3153        } else {
3154            message += `💰 ${item.price}\n`;
3155            const inStock = item.sizes.filter(s => s.inStock);
3156            const outStock = item.sizes.filter(s => !s.inStock);
3157            
3158            if (inStock.length > 0) {
3159                message += `\n✅ במלאי (${inStock.length}):\n`;
3160                message += inStock.map(s => `${s.label}`).join('\n');
3161            }
3162            
3163            if (outStock.length > 0) {
3164                message += `\n\n❌ לא במלאי (${outStock.length}):\n`;
3165                message += outStock.map(s => `${s.label}`).join('\n');
3166            }
3167        }
3168        
3169        message += `\n\n🔗 ${item.url}`;
3170        
3171        openWhatsApp(message, prefs.waPhone);
3172        showToast('📱 פותח WhatsApp...', 'info');
3173    }
3174
3175    // Track page view for recommendations
3176    async function trackPageView() {
3177        try {
3178            const adapter = getAdapter();
3179            const title = adapter.title();
3180            const price = adapter.price();
3181            const image = adapter.image();
3182            const url = location.href.split('#')[0].split('?')[0];
3183            
3184            if (!title || !url) return;
3185            
3186            const viewHistory = await load('sw_plus_view_history', []);
3187            
3188            // Check if this URL was already viewed recently (within 1 hour)
3189            const recentView = viewHistory.find(v => v.url === url && (Date.now() - v.timestamp) < 3600000);
3190            if (recentView) {
3191                recentView.viewCount++;
3192                recentView.timestamp = Date.now();
3193            } else {
3194                viewHistory.push({
3195                    url,
3196                    title,
3197                    price: price ? `${price}` : 'לא זמין',
3198                    image,
3199                    siteName: getSiteName(url),
3200                    timestamp: Date.now(),
3201                    viewCount: 1
3202                });
3203            }
3204            
3205            // Keep only last 50 views
3206            if (viewHistory.length > 50) {
3207                viewHistory.sort((a, b) => b.timestamp - a.timestamp);
3208                viewHistory.splice(50);
3209            }
3210            
3211            await save('sw_plus_view_history', viewHistory);
3212            console.log('[Stock Tracker] Page view tracked:', title);
3213        } catch (err) {
3214            console.error('[Stock Tracker] Track view error:', err);
3215        }
3216    }
3217
3218    // Get recommended products based on view history
3219    async function getRecommendedProducts() {
3220        try {
3221            const viewHistory = await load('sw_plus_view_history', []);
3222            const items = await getItems();
3223            
3224            // Filter out products already in tracking
3225            const trackedUrls = new Set(items.map(i => i.url));
3226            const recommendations = viewHistory
3227                .filter(v => !trackedUrls.has(v.url))
3228                .sort((a, b) => b.viewCount - a.viewCount)
3229                .slice(0, 6);
3230            
3231            return recommendations;
3232        } catch (err) {
3233            console.error('[Stock Tracker] Get recommendations error:', err);
3234            return [];
3235        }
3236    }
3237
3238    // Render recommendations section
3239    async function renderRecommendations() {
3240        const listEl = document.querySelector('#st-list');
3241        if (!listEl) return;
3242        
3243        const recommendations = await getRecommendedProducts();
3244        
3245        if (recommendations.length === 0) return;
3246        
3247        const recsHTML = `
3248            <div style="margin-top:32px;padding-top:24px;border-top:2px solid #333">
3249                <h3 style="margin-bottom:16px;color:#fbbf24;font-size:16px">⭐ מוצרים שצפית בהם</h3>
3250                <div style="display:grid;grid-template-columns:repeat(2,1fr);gap:12px">
3251                    ${recommendations.map(rec => `
3252                        <div class="st-rec-card" data-url="${rec.url}" style="
3253                            background:linear-gradient(135deg,#2a2a2a 0%,#252525 100%);
3254                            border:1px solid #444;
3255                            border-radius:12px;
3256                            padding:12px;
3257                            cursor:pointer;
3258                            transition:all 0.3s;
3259                        ">
3260                            ${rec.image ? `<img src="${rec.image}" style="width:100%;height:120px;object-fit:cover;border-radius:8px;margin-bottom:8px">` : ''}
3261                            <div style="font-weight:700;font-size:12px;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${rec.title}</div>
3262                            <div style="color:#999;font-size:10px;margin-bottom:4px">🏪 ${rec.siteName}</div>
3263                            <div style="color:#fbbf24;font-size:11px;font-weight:700">${rec.price}</div>
3264                            <div style="color:#666;font-size:10px;margin-top:4px">👁️ צפיות: ${rec.viewCount}</div>
3265                        </div>
3266                    `).join('')}
3267                </div>
3268            </div>
3269        `;
3270        
3271        listEl.insertAdjacentHTML('beforeend', recsHTML);
3272        
3273        // Add click handlers to recommendation cards
3274        document.querySelectorAll('.st-rec-card').forEach(card => {
3275            card.addEventListener('click', function() {
3276                const url = this.getAttribute('data-url');
3277                window.open(url, '_blank');
3278            });
3279            
3280            card.addEventListener('mouseenter', function() {
3281                this.style.borderColor = '#25D366';
3282                this.style.transform = 'translateY(-2px)';
3283                this.style.boxShadow = '0 8px 24px rgba(37, 211, 102, 0.15)';
3284            });
3285            
3286            card.addEventListener('mouseleave', function() {
3287                this.style.borderColor = '#444';
3288                this.style.transform = 'translateY(0)';
3289                this.style.boxShadow = 'none';
3290            });
3291        });
3292    }
3293
3294    // Check if dark mode should be enabled
3295    function shouldUseDarkMode() {
3296        const hour = new Date().getHours();
3297        return hour >= 20 || hour < 6; // 8 PM to 6 AM
3298    }
3299
3300    // Apply dark mode to panel
3301    function applyDarkMode() {
3302        const panel = document.querySelector('.st-panel');
3303        if (!panel) return;
3304        
3305        if (shouldUseDarkMode()) {
3306            panel.style.background = 'linear-gradient(180deg, #0a0a0a 0%, #000000 100%)';
3307            panel.style.borderColor = '#222';
3308        } else {
3309            panel.style.background = 'linear-gradient(180deg, #1a1a1a 0%, #0a0a0a 100%)';
3310            panel.style.borderColor = '#333';
3311        }
3312    }
3313
3314    init().catch(err => console.error('[Stock Tracker] Init error:', err));
3315
3316})();
Stock Watch Pro + WhatsApp Alerts | Robomonkey