Price Tracker Pro — WhatsApp Alerts

Track product prices (Amazon/eBay/AliExpress/Booking). Background checks. WhatsApp alert on drop.

Size

116.0 KB

Version

1.3.61

Created

Nov 9, 2025

Updated

20 days ago

1// ==UserScript==
2// @name		Price Tracker Pro — WhatsApp Alerts
3// @description		Track product prices (Amazon/eBay/AliExpress/Booking). Background checks. WhatsApp alert on drop.
4// @version		1.3.61
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.getValue
11// @grant		GM.setValue
12// @grant		GM.notification
13// @grant		GM.openInTab
14// @grant		GM.setClipboard
15// @namespace		io.robomonkey.price.tracker.pro
16// @author		You
17// @connect		*
18// ==/UserScript==
19(function() {
20    'use strict';
21
22    // Prevent multiple initializations
23    if (window.stockTrackerInitialized) {
24        console.log('Stock Tracker already initialized');
25        return;
26    }
27    window.stockTrackerInitialized = true;
28
29    const STORAGE_KEY = 'stock_tracker_items';
30    const PREFS_KEY = 'stock_tracker_prefs';
31    
32    // Blocked domains - sites where the extension should NOT run
33    const BLOCKED_DOMAINS = [
34        'google.com',
35        'gmail.com',
36        'youtube.com',
37        'facebook.com',
38        'twitter.com',
39        'x.com',
40        'instagram.com',
41        'linkedin.com',
42        'reddit.com',
43        'wikipedia.org',
44        'github.com',
45        'stackoverflow.com',
46        'openai.com',
47        'chatgpt.com',
48        'chat.openai.com',
49        'claude.ai',
50        'anthropic.com',
51        'bing.com',
52        'yahoo.com',
53        'docs.google.com',
54        'drive.google.com',
55        'dropbox.com',
56        'notion.so',
57        'slack.com',
58        'discord.com',
59        'telegram.org',
60        'messenger.com',
61        'zoom.us',
62        'meet.google.com',
63        'teams.microsoft.com',
64        'office.com',
65        'microsoft.com',
66        'apple.com',
67        'icloud.com',
68        'netflix.com',
69        'spotify.com',
70        'twitch.tv',
71        'tiktok.com',
72        'pinterest.com',
73        'tumblr.com',
74        'medium.com',
75        'wordpress.com',
76        'blogger.com',
77        'vimeo.com',
78        'dailymotion.com',
79        'soundcloud.com',
80        'news.ycombinator.com',
81        'producthunt.com'
82    ];
83    
84    // Check if current site is blocked
85    function isBlockedSite() {
86        const host = location.host.toLowerCase();
87        return BLOCKED_DOMAINS.some(domain => host.includes(domain));
88    }
89    
90    // Check if site looks like an e-commerce site
91    function isEcommerceSite() {
92        // Check for common e-commerce indicators in the page
93        const indicators = [
94            // Price indicators
95            '[itemprop="price"]',
96            '[class*="price"]',
97            '[data-price]',
98            '[id*="price"]',
99            // Product indicators
100            '[itemprop="product"]',
101            '[class*="product"]',
102            '[data-product]',
103            // Cart indicators
104            '[class*="cart"]',
105            '[id*="cart"]',
106            '[class*="basket"]',
107            // Buy/Add to cart buttons
108            'button[class*="add-to-cart"]',
109            'button[class*="buy"]',
110            'button[id*="add-to-cart"]',
111            // Size selectors
112            '[class*="size"]',
113            'select[name*="size"]',
114            // Common e-commerce meta tags
115            'meta[property="og:type"][content*="product"]',
116            'meta[name="product"]'
117        ];
118        
119        // Check if any indicators exist
120        for (const selector of indicators) {
121            if (document.querySelector(selector)) {
122                return true;
123            }
124        }
125        
126        // Check URL patterns
127        const url = location.href.toLowerCase();
128        const ecommercePatterns = [
129            '/product/',
130            '/item/',
131            '/p/',
132            '/dp/',
133            '/pd/',
134            'product-',
135            'item-',
136            '/shop/',
137            '/store/'
138        ];
139        
140        return ecommercePatterns.some(pattern => url.includes(pattern));
141    }
142
143    // Storage helpers
144    const load = async (key, defaultValue) => {
145        try {
146            const val = await GM.getValue(key);
147            return val !== undefined ? JSON.parse(val) : defaultValue;
148        } catch(err) {
149            console.error('[Stock Tracker] Load error:', err);
150            return defaultValue;
151        }
152    };
153
154    const save = async (key, value) => {
155        try {
156            await GM.setValue(key, JSON.stringify(value));
157        } catch(err) {
158            console.error('[Stock Tracker] Save error:', err);
159        }
160    };
161
162    const getItems = () => load(STORAGE_KEY, []);
163    const saveItems = (items) => save(STORAGE_KEY, items);
164    const getPrefs = () => load(PREFS_KEY, { waPhone: '', checkInterval: 5, notifications: true, soundAlerts: true, autoAddToCart: false });
165    const savePrefs = (prefs) => save(PREFS_KEY, prefs);
166
167    // Countdown timer state
168    let nextCheckTime = null;
169    let countdownInterval = null;
170
171    // Time formatting helpers
172    function formatTimeAgo(timestamp) {
173        const now = Date.now();
174        const diff = now - timestamp;
175        const minutes = Math.floor(diff / 60000);
176        const hours = Math.floor(diff / 3600000);
177        const days = Math.floor(diff / 86400000);
178        
179        if (minutes < 1) return 'עכשיו';
180        if (minutes < 60) return `לפני ${minutes} דקות`;
181        if (hours < 24) return `לפני ${hours} שעות`;
182        return `לפני ${days} ימים`;
183    }
184
185    // Parse price from text
186    function parsePrice(val) {
187        if (val == null) return null;              // null/undefined
188        if (typeof val === 'number') return val;   // כבר מספר
189        // כל דבר אחר – נהפוך למחרוזת בבטחה
190        let str = String(val);
191        // ננקה תווים שמטשטשים
192        str = str.replace(/[^\d.,\-]/g, '').trim();
193
194        // טיפול במבנים כמו "1,234.56" או "1.234,56"
195        // אם יש גם נקודות וגם פסיקים – נחליט ממי אלפים ומי עשרוניים
196        const hasDot = str.includes('.');
197        const hasComma = str.includes(',');
198        if (hasDot && hasComma) {
199            // אם הפסיק מופיע אחרי הנקודה – כנראה שהפסיק הוא אלפים ונשאיר נקודה כעשרוני
200            if (str.lastIndexOf(',') < str.lastIndexOf('.')) {
201                str = str.replace(/,/g, '');
202            } else {
203                // אחרת – הנקודה היא אלפים, נהפוך פסיק לעשרוני
204                str = str.replace(/\./g, '').replace(',', '.');
205            }
206        } else {
207            // רק פסיקים -> נניח שזה עשרוני אירופאי
208            if (hasComma && !hasDot) str = str.replace(',', '.');
209            // רק נקודות -> נשאיר כמו שהוא
210        }
211
212        const num = parseFloat(str);
213        return isNaN(num) ? null : num;
214    }
215    
216    // Normalize size label for comparison
217    function normalizeSizeLabel(s) {
218        return String(s || '')
219            .toLowerCase()
220            .replace(/size|מידה|talla|größe|taglia|taille|尺码/gi, '')
221            .replace(/eu|uk|us|it|fr|de|men|women|unisex/gi, '')
222            .replace(/[()]/g, '')
223            .replace(/\s*\/\s*/g, ' ')
224            .replace(/\s+/g, ' ')
225            .trim();
226    }
227    
228    // Generate size label variants for flexible matching
229    function labelVariants(s) {
230        const n = normalizeSizeLabel(s);
231        const toks = n.split(' ').filter(Boolean);
232        
233        const variants = new Set([n]);
234        if (toks.length >= 2) {
235            variants.add(toks.find(t => /^[xsml]{1,3}$/.test(t)) || n);
236            const num = toks.find(t => /^\d{2,3}$/.test(t));
237            if (num) variants.add(num);
238        }
239        variants.add(n.replace(/\s+/g, ''));
240        
241        return Array.from(variants).filter(Boolean);
242    }
243    
244    // Check if two size labels match (flexible comparison)
245    function sizeMatches(jsonSize, savedLabel) {
246        const A = labelVariants(jsonSize);
247        const B = labelVariants(savedLabel);
248        return A.some(a => B.some(b => a === b || a.includes(b) || b.includes(a)));
249    }
250
251    // Sleep helper
252    function sleep(ms) {
253        return new Promise(resolve => setTimeout(resolve, ms));
254    }
255
256    // Queue auto-cart task
257    async function queueAutoCart(task) {
258        const key = 'st_autocart_queue';
259        const q = await load(key, []);
260        const now = Date.now();
261        const recent = q.filter(x => now - x.time < 30*60*1000);
262        const exists = recent.some(x => x.url === task.url && x.size === task.size);
263        if (!exists) {
264            recent.push({ ...task, time: now });
265            await save(key, recent);
266            const u = new URL(task.url);
267            u.searchParams.set('st_autocart', '1');
268            u.searchParams.set('st_size', encodeURIComponent(task.size));
269            u.searchParams.set('st_site', task.site);
270            GM.openInTab(u.toString(), { active: false, insert: true });
271            showToast(`🛒 מנסה להוסיף לסל: ${task.size}`, 'info');
272        }
273    }
274
275    // Auto-cart for Zara
276    async function autoCartZara(sizeLabel) {
277        console.log('[Stock Tracker] AutoCart Zara: Starting for size', sizeLabel);
278        
279        // First, click the main add-to-cart button to open size selector
280        const addBtn = document.querySelector('button[data-qa-action="add-to-cart"]');
281        if (addBtn) {
282            console.log('[Stock Tracker] AutoCart Zara: Clicking add-to-cart button to open size selector');
283            addBtn.click();
284            await sleep(1500); // Wait longer for size selector to appear
285        } else {
286            console.warn('[Stock Tracker] AutoCart Zara: Add-to-cart button not found');
287            showToast('❌ לא נמצא כפתור הוספה לסל', 'error');
288            return;
289        }
290
291        // Find all size elements
292        const sizeElements = Array.from(document.querySelectorAll('li.size-selector-sizes__size'));
293        console.log('[Stock Tracker] AutoCart Zara: Found', sizeElements.length, 'size elements');
294        
295        // Find the matching size
296        const targetSize = sizeElements.find(li => {
297            const labelEl = li.querySelector('.size-selector-sizes-size__label');
298            const label = labelEl?.textContent?.trim();
299            console.log('[Stock Tracker] AutoCart Zara: Checking size:', label);
300            return label && label === sizeLabel;
301        });
302        
303        if (!targetSize) {
304            console.warn('[Stock Tracker] AutoCart Zara: Size not found:', sizeLabel);
305            showToast(`❌ מידה ${sizeLabel} לא נמצאה`, 'error');
306            return;
307        }
308
309        // Check if size is available (NOT disabled AND NOT unavailable)
310        const isDisabled = targetSize.classList.contains('size-selector-sizes__size--disabled');
311        const isUnavailable = targetSize.classList.contains('size-selector-sizes-size--unavailable');
312        
313        if (isDisabled || isUnavailable) {
314            console.warn('[Stock Tracker] AutoCart Zara: Size is unavailable:', sizeLabel);
315            showToast(`❌ מידה ${sizeLabel} לא זמינה`, 'error');
316            return;
317        }
318
319        // Click the button inside the size element
320        const sizeButton = targetSize.querySelector('button.size-selector-sizes-size__button');
321        if (!sizeButton) {
322            console.warn('[Stock Tracker] AutoCart Zara: Size button not found');
323            showToast('❌ לא נמצא כפתור המידה', 'error');
324            return;
325        }
326
327        console.log('[Stock Tracker] AutoCart Zara: Clicking size button for:', sizeLabel);
328        sizeButton.scrollIntoView({behavior:'smooth', block:'center'});
329        await sleep(300);
330        sizeButton.click();
331        await sleep(800); // Wait for size selection to process
332
333        // Now find and click the final add-to-cart button
334        const finalAddBtn = document.querySelector('button[data-qa-action="add-to-cart"]');
335        if (finalAddBtn && finalAddBtn.textContent.includes('הוספה לסל')) {
336            console.log('[Stock Tracker] AutoCart Zara: Clicking final add-to-cart button');
337            finalAddBtn.click();
338            await sleep(1500); // Wait for cart update
339            
340            // Check if item was added to cart
341            const cartBadge = document.querySelector('[data-qa="header-cart"] [data-qa*="counter"], [data-qa="cart-counter"]');
342            if (cartBadge) {
343                console.log('[Stock Tracker] AutoCart Zara: Cart counter now =', cartBadge.textContent);
344            }
345            
346            showToast(`✅ מידה ${sizeLabel} נוספה לסל!`, 'success');
347        } else {
348            console.warn('[Stock Tracker] AutoCart Zara: Final add-to-cart button not found or not ready');
349            showToast('⚠️ לא הצלחתי להוסיף לסל', 'error');
350        }
351    }
352
353    // Auto-cart for Bershka
354    async function autoCartBershka(sizeLabel) {
355        console.log('[Stock Tracker] AutoCart Bershka: Starting for size', sizeLabel);
356        await sleep(600);
357        
358        const sizeButtons = Array.from(document.querySelectorAll('button[data-qa-anchor="sizeListItem"], .ui--dot-item, button[aria-label*="מידה"], button[aria-label*="Size"]'));
359        console.log('[Stock Tracker] AutoCart Bershka: Found', sizeButtons.length, 'size buttons');
360        
361        const btn = sizeButtons.find(b => {
362            const raw = (b.querySelector('.text__label, .size__name, [data-qa="size-name"]')?.textContent || b.getAttribute('aria-label') || b.textContent || '').trim();
363            return sizeMatches(raw.replace(/^מידה\s*/,'').trim(), sizeLabel);
364        });
365        
366        if (!btn) {
367            console.warn('[Stock Tracker] AutoCart Bershka: Size not found', sizeLabel);
368            return;
369        }
370
371        if (btn.disabled || btn.getAttribute('aria-disabled') === 'true' || btn.classList.contains('is-unavailable')) {
372            console.warn('[Stock Tracker] AutoCart Bershka: Size appears unavailable in UI');
373            return;
374        }
375
376        console.log('[Stock Tracker] AutoCart Bershka: Clicking size button');
377        btn.scrollIntoView({behavior:'smooth', block:'center'});
378        btn.click();
379        await sleep(300);
380
381        const addBtn = document.querySelector('button[data-qa="add-to-cart"], button[aria-label*="הוסף"], button[aria-label*="Add"]');
382        if (addBtn) {
383            console.log('[Stock Tracker] AutoCart Bershka: Clicking add-to-cart button');
384            addBtn.click();
385            await sleep(1200);
386        }
387
388        const miniCart = document.querySelector('[data-qa="minicart"]') || document.querySelector('[class*="minicart"]');
389        if (miniCart) {
390            console.log('[Stock Tracker] AutoCart Bershka: Mini cart opened');
391        }
392
393        showToast(`🛒 נוספה לסל ב-Bershka: ${sizeLabel}`, 'success');
394    }
395
396    // Auto-cart generic fallback
397    async function autoCartGeneric(sizeLabel) {
398        console.log('[Stock Tracker] AutoCart Generic: Starting for size', sizeLabel);
399        
400        const candidates = Array.from(document.querySelectorAll(
401            'button[class*="size"], [role="button"][class*="size"], [data-size], select[name*="size"], select[id*="size"], button[aria-label*="Size"], button[aria-label*="מידה"]'
402        ));
403        
404        const select = candidates.find(el => el.tagName === 'SELECT');
405        if (select) {
406            const opt = Array.from(select.options).find(o => sizeMatches((o.textContent||'').trim(), sizeLabel));
407            if (opt) {
408                console.log('[Stock Tracker] AutoCart Generic: Selecting size from dropdown');
409                select.value = opt.value;
410                select.dispatchEvent(new Event('change', {bubbles:true}));
411                await sleep(300);
412            }
413        } else {
414            const btn = candidates.find(el => sizeMatches((el.textContent||el.getAttribute('data-size')||el.getAttribute('aria-label')||'').trim(), sizeLabel));
415            if (btn && !btn.disabled) {
416                console.log('[Stock Tracker] AutoCart Generic: Clicking size button');
417                btn.click();
418                await sleep(300);
419            }
420        }
421
422        const addBtn = document.querySelector('button[id*="add-to-cart"], button[class*="add-to-cart"], [aria-label*="Add to cart"], [aria-label*="הוסף לסל"]');
423        if (addBtn) {
424            console.log('[Stock Tracker] AutoCart Generic: Clicking add-to-cart button');
425            addBtn.click();
426            await sleep(1200);
427        }
428
429        showToast(`🛒 נוספה לסל: ${sizeLabel}`, 'success');
430    }
431
432    // Get URL parameter
433    function getParam(name) {
434        try {
435            return new URL(location.href).searchParams.get(name);
436        } catch {
437            return null;
438        }
439    }
440
441    // Run auto-cart if requested via URL parameters
442    async function runAutoCartIfRequested() {
443        const flag = getParam('st_autocart');
444        const wanted = getParam('st_size') ? decodeURIComponent(getParam('st_size')) : null;
445        if (!flag || !wanted) return;
446
447        console.log('[Stock Tracker] AutoCart requested for size:', wanted);
448        await sleep(1000);
449
450        const host = location.host.replace('www.','');
451        try {
452            if (host.includes('zara.com')) {
453                await autoCartZara(wanted);
454            } else if (host.includes('bershka.com')) {
455                await autoCartBershka(wanted);
456            } else {
457                await autoCartGeneric(wanted);
458            }
459        } catch(e){
460            console.warn('[Stock Tracker] AutoCart error:', e);
461        }
462    }
463
464    // Extract site name from URL
465    function getSiteName(url) {
466        try {
467            const hostname = new URL(url).hostname;
468            // Remove www. and get domain name
469            const domain = hostname.replace(/^www\d?\./, '');
470            
471            // Map of known sites to friendly names
472            const siteNames = {
473                'zara.com': 'Zara',
474                'bershka.com': 'Bershka',
475                'hm.com': 'H&M',
476                'nike.com': 'Nike',
477                'amazon.com': 'Amazon',
478                'amazon.co.uk': 'Amazon UK',
479                'amazon.de': 'Amazon DE',
480                'ebay.com': 'eBay',
481                'aliexpress.com': 'AliExpress',
482                'asos.com': 'ASOS',
483                'shein.com': 'SHEIN',
484                'mango.com': 'Mango',
485                'pullandbear.com': 'Pull&Bear',
486                'stradivarius.com': 'Stradivarius',
487                'massimodutti.com': 'Massimo Dutti',
488                'gap.com': 'Gap',
489                'uniqlo.com': 'Uniqlo',
490                'adidas.com': 'Adidas',
491                'puma.com': 'Puma',
492                'reebok.com': 'Reebok',
493                'newbalance.com': 'New Balance',
494                'underarmour.com': 'Under Armour'
495            };
496            
497            // Check if we have a friendly name
498            for (const [key, name] of Object.entries(siteNames)) {
499                if (domain.includes(key)) {
500                    return name;
501                }
502            }
503            
504            // Otherwise, capitalize first letter of domain
505            const mainDomain = domain.split('.')[0];
506            return mainDomain.charAt(0).toUpperCase() + mainDomain.slice(1);
507        } catch {
508            return 'Unknown';
509        }
510    }
511
512    // Play notification sound
513    function playNotificationSound() {
514        try {
515            const audioContext = new (window.AudioContext || window.webkitAudioContext)();
516            const oscillator = audioContext.createOscillator();
517            const gainNode = audioContext.createGain();
518            
519            oscillator.connect(gainNode);
520            gainNode.connect(audioContext.destination);
521            
522            oscillator.frequency.value = 800;
523            oscillator.type = 'sine';
524            
525            gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
526            gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
527            
528            oscillator.start(audioContext.currentTime);
529            oscillator.stop(audioContext.currentTime + 0.5);
530        } catch (err) {
531            console.error('[Stock Tracker] Sound error:', err);
532        }
533    }
534
535    // Update FAB badge
536    async function updateFabBadge() {
537        const items = await getItems();
538        const fab = document.querySelector('.st-fab');
539        if (!fab) return;
540        
541        const outOfStockCount = items.reduce((sum, item) => 
542            sum + item.sizes.filter(s => !s.inStock).length, 0
543        );
544        
545        // Remove old badge
546        const oldBadge = fab.querySelector('.st-badge');
547        if (oldBadge) oldBadge.remove();
548        
549        // Add new badge if there are out of stock items
550        if (outOfStockCount > 0) {
551            const badge = document.createElement('span');
552            badge.className = 'st-badge';
553            badge.textContent = outOfStockCount;
554            fab.appendChild(badge);
555            
556            // Add pulse animation if items are out of stock
557            fab.classList.add('st-fab-alert');
558        } else {
559            fab.classList.remove('st-fab-alert');
560        }
561    }
562
563    // Universal site adapters
564    const adapters = {
565        // Zara
566        'zara.com': {
567            title: () => {
568                const titleEl = document.querySelector('h1.product-detail-info__header-name, .product-detail-info__header-name, h1[class*="product"]');
569                return titleEl?.textContent?.trim() || document.querySelector('h1')?.textContent?.trim();
570            },
571            price: () => {
572                const priceEl = document.querySelector('[data-qa*="price"], .money-amount, .price__amount, .current-price-elem');
573                return parsePrice(priceEl?.textContent);
574            },
575            image: () => {
576                return document.querySelector('.product-detail-images img, picture img, .media-image__image, img[class*="product"]')?.src ||
577                       document.querySelector('main img')?.src;
578            },
579            getSizes: () => {
580                const addBtn = document.querySelector('button[data-qa-action="add-to-cart"]');
581                if (addBtn) addBtn.click();
582                
583                return new Promise(resolve => {
584                    setTimeout(() => {
585                        const sizeElements = document.querySelectorAll('li.size-selector-sizes__size, .product-size-info__main-label');
586                        const sizes = Array.from(sizeElements).map(li => {
587                            const labelEl = li.querySelector('.size-selector-sizes-size__label') || li;
588                            const label = labelEl?.textContent?.trim().replace(/coming\s?soon/gi, '').trim();
589                            
590                            // Check if size is OUT OF STOCK (disabled/unavailable)
591                            const isOutOfStock = li.classList.contains('size-selector-sizes-size--disabled') ||
592                                               li.classList.contains('size-selector-sizes-size--unavailable') ||
593                                               li.textContent?.toLowerCase().includes('coming soon');
594                            
595                            // Check if size is IN STOCK (enabled and NOT disabled/unavailable)
596                            const isInStock = li.classList.contains('size-selector-sizes-size--enabled') && !isOutOfStock;
597                            
598                            return { label, element: li, inStock: isInStock };
599                        }).filter(s => s.label && s.label.length <= 10);
600                        
601                        console.log('[Stock Tracker] Zara found sizes:', sizes.length, sizes.map(s => `${s.label}: ${s.inStock ? 'IN STOCK' : 'OUT OF STOCK'}`));
602                        resolve(sizes);
603                    }, 1500);
604                });
605            }
606        },
607        
608        // Bershka
609        'bershka.com': {
610            title: () => {
611                const titleEl = document.querySelector('h1.product-detail-info-layout__title, .product-detail-info-layout__title');
612                const title = titleEl?.textContent?.trim();
613                console.log('[Stock Tracker] Bershka title:', title);
614                return title;
615            },
616            price: () => {
617                const priceEl = document.querySelector('[data-qa-anchor="productItemPrice"], .current-price-elem, .price-elem');
618                const price = parsePrice(priceEl?.textContent);
619                console.log('[Stock Tracker] Bershka price:', price);
620                return price;
621            },
622            image: () => {
623                const img = document.querySelector('img[class*="product-detail"], img[class*="media-image"], picture img, main img')?.src;
624                console.log('[Stock Tracker] Bershka image:', img ? 'found' : 'not found');
625                return img;
626            },
627            getSizes: () => {
628                return new Promise(resolve => {
629                    setTimeout(() => {
630                        const sizeElements = document.querySelectorAll('button[data-qa-anchor="sizeListItem"], .ui--dot-item, button[aria-label*="מידה"]');
631                        const sizes = Array.from(sizeElements).map(btn => {
632                            const labelEl = btn.querySelector('.text__label') || btn;
633                            const label = labelEl?.textContent?.trim() || btn.getAttribute('aria-label')?.replace('מידה ', '').trim();
634                            const isOutOfStock = btn.disabled || 
635                                               btn.classList.contains('is-disabled') ||
636                                               btn.classList.contains('is-unavailable') ||
637                                               btn.getAttribute('aria-disabled') === 'true';
638                            
639                            return { label, element: btn, inStock: !isOutOfStock };
640                        }).filter(s => s.label && s.label.length <= 10);
641                        
642                        console.log('[Stock Tracker] Bershka found sizes:', sizes.length, sizes.map(s => s.label));
643                        resolve(sizes);
644                    }, 1000);
645                });
646            }
647        },
648        
649        // H&M
650        'hm.com': {
651            title: () => {
652                const titleEl = document.querySelector('h1.ProductName-module--productTitle, .ProductName-module--productTitle, h1[class*="product"]');
653                return titleEl?.textContent?.trim() || document.querySelector('h1')?.textContent?.trim();
654            },
655            price: () => {
656                const priceEl = document.querySelector('.ProductPrice-module--currentPrice, [data-testid="price"], .price');
657                return parsePrice(priceEl?.textContent);
658            },
659            image: () => {
660                return document.querySelector('.ProductImages-module--image img, figure img, img[class*="product"]')?.src ||
661                       document.querySelector('main img')?.src;
662            },
663            getSizes: () => {
664                return new Promise(resolve => {
665                    setTimeout(() => {
666                        // H&M uses DIV elements with role="radio" for size selection
667                        const sizeElements = document.querySelectorAll('div[data-testid^="sizeButton-"], div[role="radio"][id^="sizeButton"]');
668                        const sizes = Array.from(sizeElements).map(div => {
669                            // Extract size label from the inner div text
670                            const label = div.querySelector('div')?.textContent?.trim().replace(/\s+/g, ' ').trim();
671                            
672                            // Check stock status from aria-label
673                            const ariaLabel = div.getAttribute('aria-label') || '';
674                            const isOutOfStock = ariaLabel.toLowerCase().includes('není skladem') || 
675                                               ariaLabel.toLowerCase().includes('out of stock') ||
676                                               ariaLabel.toLowerCase().includes('not available') ||
677                                               div.classList.contains('cf813c') || // H&M's out-of-stock class
678                                               div.querySelector('.da0c1b'); // Strikethrough text class
679                            
680                            return { label, element: div, inStock: !isOutOfStock };
681                        }).filter(s => s.label && s.label.length <= 10);
682                        
683                        console.log('[Stock Tracker] H&M found sizes:', sizes.length, sizes.map(s => `${s.label}: ${s.inStock ? 'IN STOCK' : 'OUT OF STOCK'}`));
684                        resolve(sizes);
685                    }, 1000);
686                });
687            }
688        },
689
690        // Nike
691        'nike.com': {
692            title: () => {
693                const titleEl = document.querySelector('h1#pdp_product_title, #pdp_product_title, h1[class*="product"]');
694                return titleEl?.textContent?.trim() || document.querySelector('h1')?.textContent?.trim();
695            },
696            price: () => {
697                const priceEl = document.querySelector('[data-test="product-price"], .product-price, [class*="price"]');
698                return parsePrice(priceEl?.textContent);
699            },
700            image: () => {
701                return document.querySelector('.product-image img, picture img, img[class*="product"]')?.src ||
702                       document.querySelector('main img')?.src;
703            },
704            getSizes: () => {
705                return new Promise(resolve => {
706                    setTimeout(() => {
707                        const sizeElements = document.querySelectorAll('[data-qa="size-available"], .size-grid-button, button[class*="size"]');
708                        const sizes = Array.from(sizeElements).map(btn => {
709                            const label = btn.textContent?.trim();
710                            const isOutOfStock = btn.disabled || btn.classList.contains('unavailable');
711                            return { label, element: btn, inStock: !isOutOfStock };
712                        }).filter(s => s.label && s.label.length <= 10);
713                        
714                        console.log('[Stock Tracker] Nike found sizes:', sizes.length);
715                        resolve(sizes);
716                    }, 1000);
717                });
718            }
719        },
720
721        // Amazon
722        'amazon.com': {
723            title: () => {
724                const titleEl = document.querySelector('#productTitle, h1.product-title, h1[id*="product"]');
725                return titleEl?.textContent?.trim() || document.querySelector('h1')?.textContent?.trim();
726            },
727            price: () => {
728                const priceEl = document.querySelector('.a-price .a-offscreen, #priceblock_ourprice, .a-price-whole, [class*="price"]');
729                return parsePrice(priceEl?.textContent);
730            },
731            image: () => {
732                return document.querySelector('#landingImage, #imgBlkFront, img[class*="product"]')?.src ||
733                       document.querySelector('main img')?.src;
734            },
735            getSizes: () => {
736                return new Promise(resolve => {
737                    setTimeout(() => {
738                        const sizeElements = document.querySelectorAll('#native_dropdown_selected_size_name option, .size-option, select[name*="size"] option');
739                        const sizes = Array.from(sizeElements).map(opt => {
740                            const label = opt.textContent?.trim();
741                            const isOutOfStock = opt.disabled || label.toLowerCase().includes('out of stock');
742                            return { label, element: opt, inStock: !isOutOfStock };
743                        }).filter(s => s.label && !s.label.includes('Select') && s.label.length <= 10);
744                        
745                        console.log('[Stock Tracker] Amazon found sizes:', sizes.length);
746                        resolve(sizes);
747                    }, 1000);
748                });
749            }
750        },
751
752        // Generic fallback for any site
753        'generic': {
754            title: () => {
755                // Try multiple selectors in order of specificity
756                const selectors = [
757                    'h1[class*="product"][class*="title"]',
758                    'h1[class*="product"][class*="name"]',
759                    'h1.product-detail-info-layout__title',
760                    '.product-detail-info-layout__title',
761                    'h1[class*="product"]',
762                    '[itemprop="name"]',
763                    '.product-title',
764                    '.product-name',
765                    '[data-testid*="product"][data-testid*="title"]',
766                    '[data-testid*="product"][data-testid*="name"]',
767                    'h1'
768                ];
769                
770                for (const selector of selectors) {
771                    const el = document.querySelector(selector);
772                    const text = el?.textContent?.trim();
773                    if (text && text.length > 0 && text.length < 200) {
774                        console.log('[Stock Tracker] Found title with selector:', selector, text);
775                        return text;
776                    }
777                }
778                
779                console.log('[Stock Tracker] Using document.title as fallback');
780                return document.title;
781            },
782            price: () => {
783                const priceSelectors = [
784                    '[data-qa-anchor="productItemPrice"]',
785                    '.current-price-elem',
786                    '[itemprop="price"]',
787                    '[class*="price"]:not([class*="strike"]):not([class*="old"]):not([class*="original"])',
788                    '[data-price]',
789                    '[data-testid*="price"]',
790                    '[id*="price"]',
791                    'span[class*="current"]',
792                    'div[class*="current"]',
793                    '.product-price',
794                    '.price'
795                ];
796                
797                for (const selector of priceSelectors) {
798                    const elements = document.querySelectorAll(selector);
799                    for (const el of elements) {
800                        const price = parsePrice(el.textContent || el.getAttribute('content') || el.getAttribute('data-price'));
801                        if (price && price > 0) {
802                            console.log('[Stock Tracker] Found price with selector:', selector, price);
803                            return price;
804                        }
805                    }
806                }
807                console.log('[Stock Tracker] No price found');
808                return null;
809            },
810            image: () => {
811                const imageSelectors = [
812                    'img[class*="product-detail"]',
813                    'img[class*="media-image"]',
814                    '[itemprop="image"]',
815                    '.product-image img',
816                    '.product-img img',
817                    'img[class*="product"]',
818                    '[data-testid*="product"] img',
819                    'picture img',
820                    'main img',
821                    'article img'
822                ];
823                
824                for (const selector of imageSelectors) {
825                    const img = document.querySelector(selector);
826                    if (img?.src && !img.src.includes('data:image')) {
827                        console.log('[Stock Tracker] Found image with selector:', selector);
828                        return img.src;
829                    }
830                }
831                
832                console.log('[Stock Tracker] No image found');
833                return null;
834            },
835            getSizes: () => {
836                return new Promise(resolve => {
837                    setTimeout(() => {
838                        const sizeElements = document.querySelectorAll(
839                            'button[data-qa-anchor="sizeListItem"], ' +
840                            'button[class*="size"]:not([class*="guide"]):not([class*="help"]), ' +
841                            'button[aria-label*="מידה"], ' +
842                            'button[aria-label*="size"], ' +
843                            '.ui--dot-item, ' +
844                            '.size-option, ' +
845                            '[data-size], ' +
846                            '[data-testid*="size"]:not([data-testid*="guide"]), ' +
847                            'select[name*="size"] option, ' +
848                            'select[id*="size"] option, ' +
849                            'li[class*="size"]:not([class*="guide"]), ' +
850                            'div[class*="size"][role="button"], ' +
851                            'span[class*="size"][role="button"]'
852                        );
853                        
854                        const sizes = Array.from(sizeElements).map(el => {
855                            let label = el.textContent?.trim() || el.getAttribute('data-size') || el.value;
856                            
857                            // Extract size from aria-label if needed
858                            if (!label || label.length > 20) {
859                                const ariaLabel = el.getAttribute('aria-label');
860                                if (ariaLabel) {
861                                    label = ariaLabel.replace(/מידה\s*/gi, '').replace(/size\s*/gi, '').trim();
862                                }
863                            }
864                            
865                            // Clean up label
866                            if (label) {
867                                label = label.replace(/\s+/g, ' ').trim();
868                            }
869                            
870                            const isOutOfStock = el.disabled || 
871                                               el.classList.contains('disabled') ||
872                                               el.classList.contains('unavailable') ||
873                                               el.classList.contains('out-of-stock') ||
874                                               el.classList.contains('sold-out') ||
875                                               el.classList.contains('is-disabled') ||
876                                               el.classList.contains('is-unavailable') ||
877                                               el.getAttribute('aria-disabled') === 'true' ||
878                                               el.textContent?.toLowerCase().includes('out of stock') ||
879                                               el.textContent?.toLowerCase().includes('sold out') ||
880                                               el.textContent?.toLowerCase().includes('coming soon') ||
881                                               el.textContent?.toLowerCase().includes('unavailable');
882                            return { label, element: el, inStock: !isOutOfStock };
883                        }).filter(s => s.label && s.label.length > 0 && s.label.length <= 20 && !s.label.toLowerCase().includes('select') && !s.label.toLowerCase().includes('choose') && !s.label.toLowerCase().includes('guide'));
884                        
885                        console.log('[Stock Tracker] Generic found sizes:', sizes.length, sizes.map(s => s.label));
886                        resolve(sizes);
887                    }, 1000);
888                });
889            }
890        }
891    };
892
893    const getAdapter = () => {
894        const host = location.host.replace('www.', '').replace('www2.', '');
895        
896        // Try specific adapter first
897        for (const [domain, adapter] of Object.entries(adapters)) {
898            if (domain !== 'generic' && host.includes(domain)) {
899                return adapter;
900            }
901        }
902        
903        // Fallback to generic adapter
904        return adapters.generic;
905    };
906
907    // CSS selector builder
908    function buildSelector(node) {
909        if (!node) return '';
910        if (node.id) return `#${CSS.escape(node.id)}`;
911        
912        const parts = [];
913        let el = node;
914        let depth = 0;
915        
916        while (el && el.nodeType === 1 && depth < 5) {
917            let seg = el.tagName.toLowerCase();
918            
919            // For size selectors, use stable classes only (not status-dependent classes)
920            if (el.classList.length) {
921                const stableClasses = Array.from(el.classList).filter(c => 
922                    !c.includes('disabled') && 
923                    !c.includes('unavailable') && 
924                    !c.includes('enabled') &&
925                    !c.includes('selected') &&
926                    !c.includes('active')
927                );
928                if (stableClasses.length > 0) {
929                    seg += '.' + stableClasses.slice(0, 2).map(c => CSS.escape(c)).join('.');
930                }
931            }
932            
933            const siblings = Array.from(el.parentElement?.children || []).filter(x => x.tagName === el.tagName);
934            if (siblings.length > 1) {
935                const idx = siblings.indexOf(el) + 1;
936                seg += `:nth-of-type(${idx})`;
937            }
938            
939            parts.unshift(seg);
940            el = el.parentElement;
941            depth++;
942        }
943        
944        return parts.join(' > ');
945    }
946
947    // Check stock from HTML
948    function checkStockFromHTML(html, selector) {
949        try {
950            const doc = new DOMParser().parseFromString(html, 'text/html');
951            const el = doc.querySelector(selector);
952            if (!el) return null; // לא מצאנו את האלמנט – לא משנים מצב
953
954            // 1) attributes הכי אמין
955            if (el.hasAttribute('disabled') || el.getAttribute('aria-disabled') === 'true') return false;
956
957            // 2) טקסט – נבדוק קודם ביטויים של "לא במלאי"
958            const txt = (el.textContent || '').toLowerCase().replace(/\s+/g, ' ').trim();
959            const cls = (el.className || '').toLowerCase();
960
961            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;
962            if (NEG.test(txt) || /(out-of-stock|sold-out|unavailable|is-unavailable|oos)/i.test(cls)) return false;
963
964            // 3) חיובי מפורש
965            const POS = /\b(in\s*stock|available\s+now|add\s*to\s*cart|buy\s*now)\b/i;
966            if (POS.test(txt) || /(available|is-available)/i.test(cls)) return true;
967
968            // לא הצלחנו להכריע – אל תשנה מצב קיים
969            return null;
970        } catch {
971            return null;
972        }
973    }
974
975    // Extract availability from JSON-LD structured data
976    function extractAvailabilityFromJSONLD(html) {
977        try {
978            const doc = new DOMParser().parseFromString(html, 'text/html');
979            const scripts = Array.from(doc.querySelectorAll('script[type="application/ld+json"]'));
980            const pairs = []; // [{label, inStock}]
981
982            for (const s of scripts) {
983                const txt = (s.textContent || '').trim();
984                if (!txt) continue;
985
986                const candidates = [];
987                try { 
988                    candidates.push(JSON.parse(txt)); 
989                } catch {
990                    try { 
991                        candidates.push(JSON.parse('[' + txt.replace(/}\s*,\s*{/g, '},{') + ']')); 
992                    } catch {}
993                }
994
995                for (const root of candidates.flat()) {
996                    const nodes = Array.isArray(root) ? root : [root];
997                    for (const node of nodes) {
998                        const offers = node?.offers ?? node?.Offers;
999                        if (!offers) continue;
1000                        
1001                        for (const off of (Array.isArray(offers) ? offers : [offers])) {
1002                            const rawLabel = off?.size || off?.sku || off?.name || '';
1003                            let avail = off?.availability ?? off?.Availability ?? '';
1004                            if (!rawLabel) continue;
1005
1006                            let inStock = null;
1007                            if (typeof avail === 'string') {
1008                                const a = avail.toLowerCase();
1009                                if (a.includes('instock') || a.includes('limitedavailability')) inStock = true;
1010                                else if (a.includes('outofstock') || a.includes('soldout')) inStock = false;
1011                            } else if (typeof avail === 'boolean') {
1012                                inStock = !!avail;
1013                            }
1014
1015                            if (inStock !== null) {
1016                                const clean = String(rawLabel).trim();
1017                                if (clean) pairs.push({ label: clean, inStock });
1018                            }
1019                        }
1020                    }
1021                }
1022            }
1023            
1024            return pairs.length ? pairs : null;
1025        } catch (e) {
1026            console.warn('[Stock Tracker] JSON-LD parse error:', e);
1027            return null;
1028        }
1029    }
1030    
1031    // Extract Inditex (Zara/Bershka) offers from inline scripts
1032    function extractInditexOffers(html) {
1033        try {
1034            const doc = new DOMParser().parseFromString(html, 'text/html');
1035            const scripts = Array.from(doc.querySelectorAll('script'));
1036            const pairs = []; // [{label, inStock}]
1037
1038            const AVAIL_MAP = { 
1039                IN_STOCK: true, 
1040                LIMITED_STOCK: true, 
1041                OUT_OF_STOCK: false, 
1042                SOLD_OUT: false, 
1043                UNAVAILABLE: false 
1044            };
1045
1046            for (const s of scripts) {
1047                const txt = s.textContent || '';
1048                if (!txt || txt.length < 50) continue;
1049
1050                // 1) Try to catch records like {..."availability":"IN_STOCK"...,"size":"M"...}
1051                const objRegex = /\{[^{}]*"(?:availability|stockStatus)"\s*:\s*"([A-Z_]+)"[^{}]*"(?:size|sizeName|name|label|variantSize)"\s*:\s*"([^"]+)"[^{}]*\}/g;
1052                let m;
1053                while ((m = objRegex.exec(txt)) !== null) {
1054                    const rawAvail = (m[1] || '').toUpperCase();
1055                    const rawLabel = (m[2] || '').trim();
1056                    if (!rawLabel) continue;
1057                    const inStock = AVAIL_MAP.hasOwnProperty(rawAvail) ? AVAIL_MAP[rawAvail] : null;
1058                    if (inStock !== null) pairs.push({ label: rawLabel, inStock });
1059                }
1060
1061                // 2) Sometimes built as array of skuStocks: [{"size":"M","availability":"IN_STOCK"}, ...]
1062                const arrMatch = txt.match(/"skuStocks?"\s*:\s*(\[[\s\S]*?\])/i);
1063                if (arrMatch) {
1064                    try {
1065                        const arr = JSON.parse(arrMatch[1]);
1066                        for (const r of arr) {
1067                            const rawLabel = (r.size || r.sizeName || r.name || r.label || '').trim();
1068                            const rawAvail = String(r.availability || r.stockStatus || '').toUpperCase();
1069                            if (!rawLabel) continue;
1070                            const inStock = AVAIL_MAP.hasOwnProperty(rawAvail) ? AVAIL_MAP[rawAvail] : null;
1071                            if (inStock !== null) pairs.push({ label: rawLabel, inStock });
1072                        }
1073                    } catch {}
1074                }
1075            }
1076
1077            // Remove duplicates - prefer IN_STOCK if there's a conflict
1078            const seen = new Map();
1079            for (const p of pairs) {
1080                const key = normalizeSizeLabel(p.label);
1081                if (!seen.has(key)) {
1082                    seen.set(key, p);
1083                } else if (p.inStock === true) {
1084                    seen.set(key, p); // Prefer IN_STOCK
1085                }
1086            }
1087            
1088            const out = Array.from(seen.values());
1089            return out.length ? out : null;
1090        } catch (e) {
1091            console.warn('[Stock Tracker] extractInditexOffers error:', e);
1092            return null;
1093        }
1094    }
1095
1096    // Check stock from live DOM (when product page is open)
1097    function liveStockFromOpenTab(selector, url) {
1098        try {
1099            const here = location.href.split('#')[0].split('?')[0];
1100            if (here !== url) return null;
1101            const node = document.querySelector(selector);
1102            if (!node) return null;
1103            
1104            // For Zara - check for --enabled class (positive indicator)
1105            if (node.classList.contains('size-selector-sizes-size--enabled')) {
1106                // Check if it's also disabled/unavailable
1107                const isDisabled = node.classList.contains('size-selector-sizes-size--disabled') ||
1108                                 node.classList.contains('size-selector-sizes-size--unavailable') ||
1109                                 node.textContent?.toLowerCase().includes('coming soon');
1110                return !isDisabled;
1111            }
1112            
1113            // Standard checks for other sites
1114            const disabled = node.hasAttribute('disabled') || node.getAttribute('aria-disabled') === 'true';
1115            const txt = (node.textContent||'').toLowerCase();
1116            const cls = (node.className||'').toLowerCase();
1117            if (disabled) return false;
1118            if (/(sold\s?out|out\s?of\s?stock|unavailable|coming\s?soon|notify\s?me)/i.test(txt)) return false;
1119            if (/(out-of-stock|sold-out|unavailable|is-unavailable)/i.test(cls)) return false;
1120            
1121            // Check for positive indicators
1122            if (/(in-stock|is-available|available)\b/i.test(cls)) return true;
1123            
1124            // לא בטוח? אל תחרוץ - תחזיר null (לא true!)
1125            return null;
1126        } catch { return null; }
1127    }
1128
1129    // Fetch page HTML
1130    async function fetchHTML(url) {
1131        return new Promise((resolve, reject) => {
1132            GM.xmlhttpRequest({
1133                method: 'GET',
1134                url,
1135                timeout: 30000,
1136                onload: (res) => {
1137                    if (res.status >= 200 && res.status < 300) {
1138                        resolve(res.responseText);
1139                    } else {
1140                        reject(new Error(`HTTP ${res.status}`));
1141                    }
1142                },
1143                onerror: (err) => reject(new Error('Network error: ' + (err?.error || 'Unknown'))),
1144                ontimeout: () => reject(new Error('Timeout'))
1145            });
1146        });
1147    }
1148
1149    // WhatsApp helpers
1150    function openWhatsApp(text, phone = '') {
1151        const url = new URL('https://web.whatsapp.com/send');
1152        if (phone) url.searchParams.set('phone', phone.replace(/[^\d]/g, ''));
1153        url.searchParams.set('text', text);
1154        
1155        (async () => {
1156            await GM.setValue('wa_pending_text', text);
1157            await GM.setValue('wa_pending_send', 'true');
1158            GM.openInTab(url.toString(), false);
1159        })();
1160    }
1161
1162    async function autoSendWhatsApp() {
1163        if (!location.host.includes('web.whatsapp.com')) return;
1164        
1165        const shouldSend = await GM.getValue('wa_pending_send');
1166        const text = await GM.getValue('wa_pending_text');
1167        
1168        if (shouldSend === 'true' && text) {
1169            await GM.setValue('wa_pending_send', '');
1170            await GM.setValue('wa_pending_text', '');
1171            
1172            console.log('[Stock Tracker] Auto-sending WhatsApp message...');
1173            console.log('[Stock Tracker] Message text:', text);
1174            
1175            let attempts = 0;
1176            const checkComposer = setInterval(async () => {
1177                attempts++;
1178                console.log('[Stock Tracker] Attempt', attempts, '/ 30');
1179                
1180                // Try multiple selectors for the composer
1181                const composer = document.querySelector('footer div[contenteditable="true"][role="textbox"]') ||
1182                                document.querySelector('div[contenteditable="true"][data-tab="10"]') ||
1183                                document.querySelector('div[contenteditable="true"][data-lexical-editor="true"]');
1184                
1185                if (composer && composer.offsetParent !== null) {
1186                    clearInterval(checkComposer);
1187                    console.log('[Stock Tracker] Composer found!');
1188                    
1189                    composer.focus();
1190                    await new Promise(resolve => setTimeout(resolve, 500));
1191                    
1192                    composer.innerHTML = '';
1193                    
1194                    const lines = text.split('\n');
1195                    lines.forEach((line, idx) => {
1196                        if (idx > 0) composer.appendChild(document.createElement('br'));
1197                        composer.appendChild(document.createTextNode(line));
1198                    });
1199                    
1200                    composer.dispatchEvent(new InputEvent('input', { bubbles: true }));
1201                    composer.dispatchEvent(new Event('change', { bubbles: true }));
1202                    
1203                    console.log('[Stock Tracker] Text inserted, waiting before sending...');
1204                    await new Promise(resolve => setTimeout(resolve, 1500));
1205                    
1206                    // Try multiple selectors for send button - including DIV with role="button"
1207                    const sendBtn = document.querySelector('footer div[aria-label="שליחה"][role="button"]') ||
1208                                   document.querySelector('footer [aria-label="שליחה"][role="button"]') ||
1209                                   document.querySelector('div[aria-label="שליחה"][role="button"]') ||
1210                                   document.querySelector('button[aria-label="שליחה"]') ||
1211                                   document.querySelector('button[aria-label*="Send"]') ||
1212                                   document.querySelector('footer button[aria-label="שליחה"]') ||
1213                                   document.querySelector('footer button[aria-label*="Send"]') ||
1214                                   document.querySelector('span[data-icon="send"]')?.closest('button') ||
1215                                   document.querySelector('span[data-icon="send"]')?.closest('div[role="button"]') ||
1216                                   document.querySelector('button[data-tab="11"]');
1217                    
1218                    if (sendBtn) {
1219                        console.log('[Stock Tracker] Send button found, clicking...');
1220                        sendBtn.click();
1221                        console.log('[Stock Tracker] ✅ Message sent!');
1222                    } else {
1223                        console.error('[Stock Tracker] Send button not found!');
1224                        console.log('[Stock Tracker] Trying to find any button in footer...');
1225                        const allFooterButtons = document.querySelectorAll('footer button, footer div[role="button"]');
1226                        console.log('[Stock Tracker] Found', allFooterButtons.length, 'buttons in footer');
1227                    }
1228                } else {
1229                    if (attempts % 5 === 0) {
1230                        console.log('[Stock Tracker] Still waiting for composer...');
1231                    }
1232                }
1233                
1234                if (attempts >= 30) {
1235                    clearInterval(checkComposer);
1236                    console.error('[Stock Tracker] Timeout: Could not find composer after 30 attempts');
1237                }
1238            }, 1000);
1239        }
1240    }
1241
1242    // Check stock for all items
1243    async function checkAllStock() {
1244        const items = await getItems();
1245        const prefs = await getPrefs();
1246        
1247        if (items.length === 0) {
1248            showToast('אין מוצרים במעקב', 'error');
1249            return;
1250        }
1251        
1252        // Update status indicator
1253        const statusText = document.querySelector('#st-status-text');
1254        if (statusText) {
1255            statusText.textContent = '🟡 בודק מלאי...';
1256            statusText.style.color = '#fbbf24';
1257        }
1258        
1259        showToast(`🔄 בודק ${items.length} מוצרים...`, 'info');
1260        console.log('[Stock Tracker] ========================================');
1261        console.log('[Stock Tracker] Starting stock check for', items.length, 'items...');
1262        console.log('[Stock Tracker] Current time:', new Date().toLocaleString('he-IL'));
1263        console.log('[Stock Tracker] ========================================');
1264        
1265        let foundInStock = 0;
1266        let updatedItems = [...items];
1267        let successCount = 0;
1268        let failCount = 0;
1269        
1270        for (let i = 0; i < updatedItems.length; i++) {
1271            const item = updatedItems[i];
1272            console.log(`[Stock Tracker] [${i+1}/${updatedItems.length}] Checking: ${item.title}`);
1273            console.log(`[Stock Tracker] URL: ${item.url}`);
1274            
1275            let retries = 3;
1276            let success = false;
1277            
1278            while (retries > 0 && !success) {
1279                try {
1280                    console.log(`[Stock Tracker] Fetching HTML (attempt ${4-retries}/3)...`);
1281                    const html = await fetchHTML(item.url);
1282                    console.log(`[Stock Tracker] HTML fetched successfully (${html.length} chars)`);
1283                    
1284                    // Check if this is an Inditex site (Zara/Bershka)
1285                    const isInditex = item.url.includes('bershka.com') || item.url.includes('zara.com');
1286                    
1287                    // Try Inditex-specific extraction first (for Zara/Bershka)
1288                    const inditexPairs = isInditex ? extractInditexOffers(html) : null;
1289                    if (inditexPairs) {
1290                        console.log('[Stock Tracker] Inditex pairs:', inditexPairs.map(p => `${p.label}:${p.inStock ? 'IN' : 'OUT'}`));
1291                    }
1292                    
1293                    // Try JSON-LD as fallback
1294                    const jsonLdPairs = extractAvailabilityFromJSONLD(html);
1295                    if (jsonLdPairs) {
1296                        console.log('[Stock Tracker] JSON-LD pairs:', jsonLdPairs.map(p => `${p.label}:${p.inStock ? 'IN' : 'OUT'}`));
1297                    }
1298                    
1299                    for (const size of item.sizes) {
1300                        console.log(`[Stock Tracker] Checking size: ${size.label} (was: ${size.inStock ? 'IN STOCK' : size.inStock === false ? 'OUT OF STOCK' : 'UNKNOWN'})`);
1301                        
1302                        let newStock = null;
1303                        
1304                        // 1) Inditex-specific extraction (highest priority for Zara/Bershka)
1305                        if (inditexPairs) {
1306                            const hit = inditexPairs.find(p => sizeMatches(p.label, size.label));
1307                            if (hit) {
1308                                newStock = !!hit.inStock;
1309                                console.log(`[Stock Tracker] Inditex matched "${size.label}" ⇢ ${newStock ? 'IN' : 'OUT'} STOCK (source "${hit.label}")`);
1310                            }
1311                        }
1312                        
1313                        // 2) JSON-LD (if no Inditex match)
1314                        if (newStock === null && jsonLdPairs) {
1315                            const hit = jsonLdPairs.find(p => sizeMatches(p.label, size.label));
1316                            if (hit) {
1317                                newStock = !!hit.inStock;
1318                                console.log(`[Stock Tracker] JSON-LD matched "${size.label}" ⇢ ${newStock ? 'IN' : 'OUT'} STOCK (source "${hit.label}")`);
1319                            }
1320                        }
1321                        
1322                        // 3) Live DOM (only if we're on the product page)
1323                        if (newStock === null) {
1324                            newStock = liveStockFromOpenTab(size.selector, item.url);
1325                            if (newStock !== null) {
1326                                console.log(`[Stock Tracker] Live DOM: ${newStock ? 'IN' : 'OUT'} STOCK`);
1327                            }
1328                        }
1329                        
1330                        // 4) HTML static – don't run on Inditex (causes false positives)
1331                        if (newStock === null && !isInditex) {
1332                            newStock = checkStockFromHTML(html, size.selector);
1333                            console.log(`[Stock Tracker] HTML static: ${newStock === null ? 'NULL' : newStock ? 'IN' : 'OUT'}`);
1334                        }
1335                        
1336                        if (newStock === null) {
1337                            console.warn('[Stock Tracker] Could not determine stock for:', size.label, 'URL=', item.url);
1338                        }
1339                        
1340                        const prev = size.inStock;
1341                        const now = newStock !== null ? newStock : prev;
1342                        
1343                        // טריגר כשעוברים מ-(false או null) → true
1344                        if ((prev === false || prev == null) && now === true) {
1345                            foundInStock++;
1346                            console.log('[Stock Tracker] 🔔🔔🔔 BACK IN STOCK DETECTED! 🔔🔔🔔');
1347                            console.log('[Stock Tracker] Product:', item.title);
1348                            console.log('[Stock Tracker] Size:', size.label);
1349                            console.log('[Stock Tracker] Price:', item.price);
1350                            
1351                            const message = `🔔 חזרה למלאי!\n\n${item.title}\nמידה: ${size.label}\nמחיר: ${item.price}\n\n${item.url}`;
1352                            
1353                            if (prefs.waPhone) {
1354                                console.log('[Stock Tracker] Sending WhatsApp to:', prefs.waPhone);
1355                                openWhatsApp(message, prefs.waPhone);
1356                            } else {
1357                                console.log('[Stock Tracker] No WhatsApp phone configured');
1358                            }
1359                            
1360                            if (prefs.notifications) {
1361                                showToast(`🔔 ${size.label} חזרה למלאי!`, 'success');
1362                            }
1363                            
1364                            if (prefs.soundAlerts) {
1365                                playNotificationSound();
1366                            }
1367                            
1368                            // Auto-add to cart if enabled
1369                            if (prefs.autoAddToCart) {
1370                                queueAutoCart({
1371                                    url: item.url,
1372                                    size: size.label,
1373                                    site: item.url.includes('zara.com') ? 'zara' : item.url.includes('bershka.com') ? 'bershka' : 'generic',
1374                                    title: item.title
1375                                });
1376                            }
1377                        }
1378                        
1379                        size.inStock = now;
1380                        size.lastChecked = Date.now();
1381                    }
1382                    
1383                    success = true;
1384                    successCount++;
1385                    console.log(`[Stock Tracker] ✅ Successfully checked: ${item.title}`);
1386                    
1387                } catch (err) {
1388                    retries--;
1389                    console.error(`[Stock Tracker] ❌ Error checking ${item.title} (attempt ${3-retries}/3):`, err);
1390                    
1391                    if (retries > 0) {
1392                        console.log('[Stock Tracker] Waiting 2 seconds before retry...');
1393                        await new Promise(resolve => setTimeout(resolve, 2000));
1394                    } else {
1395                        failCount++;
1396                        console.error('[Stock Tracker] ❌ Failed after 3 retries:', item.title);
1397                    }
1398                }
1399            }
1400            console.log('[Stock Tracker] ----------------------------------------');
1401        }
1402        
1403        await saveItems(updatedItems);
1404        
1405        // Update status indicator
1406        if (statusText) {
1407            statusText.textContent = '🟢 המערכת פעילה';
1408            statusText.style.color = '#22c55e';
1409        }
1410        
1411        console.log('[Stock Tracker] ========================================');
1412        console.log('[Stock Tracker] Stock check completed!');
1413        console.log('[Stock Tracker] Success:', successCount, '| Failed:', failCount);
1414        console.log('[Stock Tracker] Found in stock:', foundInStock);
1415        console.log('[Stock Tracker] ========================================');
1416        
1417        if (foundInStock > 0) {
1418            showToast(`✅ נמצאו ${foundInStock} מידות במלאי!`, 'success');
1419        } else {
1420            showToast(`✅ בדיקה הושלמה (${successCount}/${items.length} הצליחו)`, 'info');
1421        }
1422        
1423        if (failCount > 0) {
1424            console.warn(`[Stock Tracker] ${failCount} items failed to check`);
1425        }
1426        
1427        await renderList();
1428        await renderStats();
1429        await updateFabBadge();
1430    }
1431
1432    // Check stock for a single item
1433    async function checkSingleItem(itemId) {
1434        const items = await getItems();
1435        const prefs = await getPrefs();
1436        const item = items.find(i => i.id === itemId);
1437        
1438        if (!item) {
1439            showToast('⚠️ מוצר לא נמצא', 'error');
1440            return;
1441        }
1442        
1443        showToast(`🔄 בודק: ${item.title}...`, 'info');
1444        console.log('[Stock Tracker] Checking single item:', item.title);
1445        
1446        let retries = 3;
1447        let success = false;
1448        
1449        while (retries > 0 && !success) {
1450            try {
1451                const html = await fetchHTML(item.url);
1452                const isInditex = item.url.includes('bershka.com') || item.url.includes('zara.com');
1453                const inditexPairs = isInditex ? extractInditexOffers(html) : null;
1454                const jsonLdPairs = extractAvailabilityFromJSONLD(html);
1455                
1456                let foundInStock = 0;
1457                
1458                for (const size of item.sizes) {
1459                    let newStock = null;
1460                    
1461                    if (inditexPairs) {
1462                        const hit = inditexPairs.find(p => sizeMatches(p.label, size.label));
1463                        if (hit) newStock = !!hit.inStock;
1464                    }
1465                    
1466                    if (newStock === null && jsonLdPairs) {
1467                        const hit = jsonLdPairs.find(p => sizeMatches(p.label, size.label));
1468                        if (hit) newStock = !!hit.inStock;
1469                    }
1470                    
1471                    if (newStock === null) {
1472                        newStock = liveStockFromOpenTab(size.selector, item.url);
1473                    }
1474                    
1475                    if (newStock === null && !isInditex) {
1476                        newStock = checkStockFromHTML(html, size.selector);
1477                    }
1478                    
1479                    const prev = size.inStock;
1480                    const now = newStock !== null ? newStock : prev;
1481                    
1482                    if ((prev === false || prev == null) && now === true) {
1483                        foundInStock++;
1484                        const message = `🔔 חזרה למלאי!\n\n${item.title}\nמידה: ${size.label}\nמחיר: ${item.price}\n\n${item.url}`;
1485                        
1486                        if (prefs.waPhone) {
1487                            openWhatsApp(message, prefs.waPhone);
1488                        }
1489                        
1490                        if (prefs.notifications) {
1491                            showToast(`🔔 ${size.label} חזרה למלאי!`, 'success');
1492                        }
1493                        
1494                        if (prefs.soundAlerts) {
1495                            playNotificationSound();
1496                        }
1497                    }
1498                    
1499                    size.inStock = now;
1500                    size.lastChecked = Date.now();
1501                }
1502                
1503                await saveItems(items);
1504                success = true;
1505                
1506                if (foundInStock > 0) {
1507                    showToast(`✅ נמצאו ${foundInStock} מידות במלאי!`, 'success');
1508                } else {
1509                    showToast('✅ בדיקה הושלמה - אין שינויים', 'info');
1510                }
1511                
1512            } catch (err) {
1513                retries--;
1514                console.error(`[Stock Tracker] Error checking item (attempt ${3-retries}/3):`, err);
1515                
1516                if (retries === 0) {
1517                    showToast('❌ שגיאה בבדיקת המוצר', 'error');
1518                }
1519            }
1520        }
1521        
1522        await renderList();
1523        await renderStats();
1524        await updateFabBadge();
1525    }
1526
1527    // Export data
1528    async function exportData() {
1529        const items = await getItems();
1530        const prefs = await getPrefs();
1531        
1532        const data = {
1533            items,
1534            prefs,
1535            exportDate: new Date().toISOString(),
1536            version: '1.0'
1537        };
1538        
1539        const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
1540        const url = URL.createObjectURL(blob);
1541        const a = document.createElement('a');
1542        a.href = url;
1543        a.download = `stock-tracker-backup-${Date.now()}.json`;
1544        a.click();
1545        URL.revokeObjectURL(url);
1546        
1547        showToast('✅ הנתונים יוצאו בהצלחה!', 'success');
1548    }
1549
1550    // Import data
1551    async function importData() {
1552        const input = document.createElement('input');
1553        input.type = 'file';
1554        input.accept = 'application/json';
1555        
1556        input.onchange = async (e) => {
1557            const file = e.target.files[0];
1558            if (!file) return;
1559            
1560            try {
1561                const text = await file.text();
1562                const data = JSON.parse(text);
1563                
1564                if (data.items) await saveItems(data.items);
1565                if (data.prefs) await savePrefs(data.prefs);
1566                
1567                await renderList();
1568                await loadPrefsToUI();
1569                await updateFabBadge();
1570                showToast('✅ הנתונים יובאו בהצלחה!', 'success');
1571            } catch (err) {
1572                console.error('[Stock Tracker] Import error:', err);
1573                showToast('❌ שגיאה בייבוא הנתונים', 'error');
1574            }
1575        };
1576        
1577        input.click();
1578    }
1579
1580    // Clear all data
1581    async function clearAllData() {
1582        if (!confirm('האם אתה בטוח שברצונך למחוק את כל הנתונים?')) return;
1583        
1584        await saveItems([]);
1585        await renderList();
1586        await renderStats();
1587        await updateFabBadge();
1588        showToast('🗑️ כל הנתונים נמחקו', 'info');
1589    }
1590
1591    // Quick add from current page
1592    async function quickAddAllSizes() {
1593        const adapter = getAdapter();
1594        if (!adapter) {
1595            showToast('⚠️ אתר זה לא נתמך כרגע', 'error');
1596            return;
1597        }
1598
1599        const title = adapter.title();
1600        const price = adapter.price();
1601        const image = adapter.image();
1602        const url = location.href.split('#')[0].split('?')[0];
1603
1604        if (!title) {
1605            showToast('⚠️ לא הצלחתי לזהות את המוצר', 'error');
1606            return;
1607        }
1608
1609        showToast('⏳ מוסיף את כל המידות...', 'info');
1610
1611        const sizes = await adapter.getSizes();
1612        
1613        if (!sizes || sizes.length === 0) {
1614            showToast('⚠️ לא נמצאו מידות', 'error');
1615            return;
1616        }
1617
1618        const items = await getItems();
1619        
1620        const selectedSizeData = sizes.map(size => ({
1621            label: size.label,
1622            selector: buildSelector(size.element),
1623            inStock: size.inStock,
1624            lastChecked: Date.now()
1625        }));
1626        
1627        items.push({
1628            id: Date.now().toString(),
1629            title,
1630            price: price || 'לא זמין',
1631            image,
1632            url,
1633            sizes: selectedSizeData,
1634            addedAt: Date.now()
1635        });
1636        
1637        await saveItems(items);
1638        await renderList();
1639        await renderStats();
1640        await updateFabBadge();
1641        showToast(`✅ נוספו ${sizes.length} מידות למעקב!`, 'success');
1642    }
1643
1644    // Copy product link
1645    async function copyProductLink(url) {
1646        try {
1647            await GM.setClipboard(url);
1648            showToast('✅ הקישור הועתק ללוח!', 'success');
1649        } catch (err) {
1650            console.error('[Stock Tracker] Copy error:', err);
1651            showToast('❌ שגיאה בהעתקת הקישור', 'error');
1652        }
1653    }
1654
1655    // UI Styles
1656    const styles = `
1657        .st-fab {
1658            position: fixed;
1659            right: 20px;
1660            bottom: 20px;
1661            z-index: 999999;
1662            background: linear-gradient(135deg, #25D366 0%, #22c55e 100%);
1663            color: #fff;
1664            border: none;
1665            border-radius: 50px;
1666            padding: 16px 24px;
1667            font-weight: 800;
1668            font-size: 16px;
1669            cursor: pointer;
1670            box-shadow: 0 8px 24px rgba(37, 211, 102, 0.4);
1671            transition: all 0.3s ease;
1672        }
1673        .st-fab:hover {
1674            transform: translateY(-2px);
1675            box-shadow: 0 12px 32px rgba(37, 211, 102, 0.6);
1676        }
1677        
1678        .st-fab-alert {
1679            animation: pulse 2s infinite;
1680        }
1681        
1682        @keyframes pulse {
1683            0%, 100% {
1684                box-shadow: 0 8px 24px rgba(37, 211, 102, 0.4);
1685            }
1686            50% {
1687                box-shadow: 0 8px 24px rgba(239, 68, 68, 0.6);
1688            }
1689        }
1690        
1691        .st-badge {
1692            position: absolute;
1693            top: -8px;
1694            right: -8px;
1695            background: #ef4444;
1696            color: #fff;
1697            border-radius: 50%;
1698            width: 24px;
1699            height: 24px;
1700            display: flex;
1701            align-items: center;
1702            justify-content: center;
1703            font-size: 12px;
1704            font-weight: 800;
1705            border: 2px solid #fff;
1706            animation: bounce 1s infinite;
1707        }
1708        
1709        @keyframes bounce {
1710            0%, 100% {
1711                transform: scale(1);
1712            }
1713            50% {
1714                transform: scale(1.1);
1715            }
1716        }
1717        
1718        .st-panel {
1719            position: fixed;
1720            right: 20px;
1721            top: 50%;
1722            transform: translateY(-50%);
1723            z-index: 999999;
1724            background: linear-gradient(180deg, #1a1a1a 0%, #0a0a0a 100%);
1725            color: #fff;
1726            border: 1px solid #333;
1727            border-radius: 20px;
1728            width: 520px;
1729            max-height: 90vh;
1730            overflow: hidden;
1731            display: none;
1732            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.8);
1733        }
1734        
1735        .st-header {
1736            padding: 24px;
1737            background: linear-gradient(135deg, #25D366 0%, #22c55e 100%);
1738            display: flex;
1739            justify-content: space-between;
1740            align-items: center;
1741            font-weight: 800;
1742            font-size: 20px;
1743        }
1744        
1745        .st-body {
1746            padding: 24px;
1747            max-height: calc(90vh - 90px);
1748            overflow-y: auto;
1749        }
1750        
1751        .st-body::-webkit-scrollbar {
1752            width: 8px;
1753        }
1754        
1755        .st-body::-webkit-scrollbar-track {
1756            background: #1a1a1a;
1757        }
1758        
1759        .st-body::-webkit-scrollbar-thumb {
1760            background: #444;
1761            border-radius: 4px;
1762        }
1763        
1764        .st-body::-webkit-scrollbar-thumb:hover {
1765            background: #555;
1766        }
1767        
1768        .st-input {
1769            width: 100%;
1770            background: #2a2a2a;
1771            color: #fff;
1772            border: 1px solid #444;
1773            border-radius: 10px;
1774            padding: 14px 16px;
1775            margin: 8px 0;
1776            font-size: 14px;
1777            box-sizing: border-box;
1778            transition: all 0.2s;
1779        }
1780        
1781        .st-input:focus {
1782            outline: none;
1783            border-color: #25D366;
1784            box-shadow: 0 0 0 3px rgba(37, 211, 102, 0.1);
1785        }
1786        
1787        .st-checkbox {
1788            display: flex;
1789            align-items: center;
1790            gap: 12px;
1791            margin: 12px 0;
1792            cursor: pointer;
1793            padding: 10px;
1794            border-radius: 8px;
1795            transition: background 0.2s;
1796        }
1797        
1798        .st-checkbox:hover {
1799            background: rgba(255, 255, 255, 0.05);
1800        }
1801        
1802        .st-checkbox input {
1803            width: 20px;
1804            height: 20px;
1805            cursor: pointer;
1806            accent-color: #25D366;
1807        }
1808        
1809        .st-btn {
1810            background: linear-gradient(135deg, #25D366 0%, #22c55e 100%);
1811            color: #fff;
1812            border: none;
1813            border-radius: 10px;
1814            padding: 14px 20px;
1815            font-weight: 700;
1816            cursor: pointer;
1817            width: 100%;
1818            margin: 8px 0;
1819            font-size: 14px;
1820            transition: all 0.2s ease;
1821        }
1822        .st-btn:hover {
1823            transform: translateY(-2px);
1824            box-shadow: 0 6px 16px rgba(37, 211, 102, 0.4);
1825        }
1826        
1827        .st-btn-secondary {
1828            background: #2a2a2a;
1829            border: 1px solid #444;
1830        }
1831        .st-btn-secondary:hover {
1832            background: #333;
1833            box-shadow: 0 4px 12px rgba(255, 255, 255, 0.1);
1834        }
1835        
1836        .st-btn-danger {
1837            background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
1838        }
1839        
1840        .st-btn-danger:hover {
1841            box-shadow: 0 6px 16px rgba(239, 68, 68, 0.4);
1842        }
1843        
1844        .st-item {
1845            background: linear-gradient(135deg, #2a2a2a 0%, #252525 100%);
1846            border: 1px solid #444;
1847            border-radius: 16px;
1848            padding: 20px;
1849            margin: 16px 0;
1850            transition: all 0.3s;
1851        }
1852        
1853        .st-item:hover {
1854            border-color: #25D366;
1855            box-shadow: 0 8px 24px rgba(37, 211, 102, 0.15);
1856            transform: translateY(-2px);
1857        }
1858        
1859        .st-size {
1860            display: inline-block;
1861            background: #1a1a1a;
1862            border: 2px solid #444;
1863            border-radius: 10px;
1864            padding: 10px 14px;
1865            margin: 6px 4px;
1866            font-size: 13px;
1867            font-weight: 700;
1868            cursor: pointer;
1869            transition: all 0.2s;
1870        }
1871        
1872        .st-size.in-stock {
1873            border-color: #22c55e;
1874            color: #22c55e;
1875            background: rgba(34, 197, 94, 0.1);
1876        }
1877        
1878        .st-size.out-of-stock {
1879            border-color: #ef4444;
1880            color: #ef4444;
1881            background: rgba(239, 68, 68, 0.1);
1882        }
1883        
1884        .st-size:hover {
1885            transform: scale(1.08);
1886        }
1887        
1888        .st-toast {
1889            position: fixed;
1890            top: 20px;
1891            right: 20px;
1892            background: #1a1a1a;
1893            color: #fff;
1894            padding: 16px 20px;
1895            border-radius: 12px;
1896            z-index: 9999999;
1897            box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
1898            font-weight: 700;
1899            border-left: 4px solid #25D366;
1900            animation: slideIn 0.3s ease;
1901        }
1902        
1903        @keyframes slideIn {
1904            from {
1905                transform: translateX(400px);
1906                opacity: 0;
1907            }
1908            to {
1909                transform: translateX(0);
1910                opacity: 1;
1911            }
1912        }
1913        
1914        .st-toast.error {
1915            border-left-color: #ef4444;
1916        }
1917        
1918        .st-toast.info {
1919            border-left-color: #3b82f6;
1920        }
1921        
1922        .st-stats {
1923            background: linear-gradient(135deg, #2a2a2a 0%, #252525 100%);
1924            border: 1px solid #444;
1925            border-radius: 16px;
1926            padding: 20px;
1927            margin: 16px 0;
1928            display: grid;
1929            grid-template-columns: repeat(3, 1fr);
1930            gap: 16px;
1931            text-align: center;
1932        }
1933        
1934        .st-stat {
1935            padding: 16px;
1936            background: #1a1a1a;
1937            border-radius: 12px;
1938            transition: all 0.2s;
1939        }
1940        
1941        .st-stat:hover {
1942            background: #222;
1943            transform: translateY(-2px);
1944        }
1945        
1946        .st-stat-value {
1947            font-size: 28px;
1948            font-weight: 800;
1949            color: #25D366;
1950            margin-bottom: 4px;
1951        }
1952        
1953        .st-stat-label {
1954            font-size: 12px;
1955            color: #999;
1956            margin-top: 4px;
1957        }
1958        
1959        .st-section {
1960            margin: 24px 0;
1961        }
1962        
1963        .st-section-title {
1964            font-size: 14px;
1965            font-weight: 700;
1966            color: #999;
1967            text-transform: uppercase;
1968            letter-spacing: 1px;
1969            margin-bottom: 12px;
1970        }
1971        
1972        .st-info-box {
1973            background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 100%);
1974            border: 1px solid #3b82f6;
1975            border-radius: 12px;
1976            padding: 16px;
1977            margin: 16px 0;
1978        }
1979        
1980        .st-info-title {
1981            font-weight: 700;
1982            margin-bottom: 8px;
1983            color: #60a5fa;
1984            font-size: 14px;
1985        }
1986        
1987        .st-info-content {
1988            font-size: 12px;
1989            color: #93c5fd;
1990            line-height: 1.6;
1991        }
1992        
1993        .st-countdown-box {
1994            text-align: center;
1995            padding: 16px;
1996            background: linear-gradient(135deg, #2a2a2a 0%, #252525 100%);
1997            border-radius: 12px;
1998            margin: 16px 0;
1999            border: 1px solid #444;
2000        }
2001        
2002        .st-countdown-label {
2003            color: #999;
2004            font-size: 12px;
2005            margin-bottom: 8px;
2006        }
2007        
2008        .st-countdown-time {
2009            color: #25D366;
2010            font-weight: 700;
2011            font-size: 24px;
2012        }
2013        
2014        .st-button-grid {
2015            display: grid;
2016            grid-template-columns: repeat(5, 1fr);
2017            gap: 8px;
2018            margin-top: 16px;
2019        }
2020        
2021        .st-button-grid button {
2022            padding: 10px 8px;
2023            font-size: 11px;
2024            white-space: nowrap;
2025        }
2026    `;
2027
2028    // Create UI
2029    function createUI() {
2030        const styleEl = document.createElement('style');
2031        styleEl.textContent = styles;
2032        document.head.appendChild(styleEl);
2033
2034        const fab = document.createElement('button');
2035        fab.className = 'st-fab';
2036        fab.innerHTML = '📦 מעקב מלאי';
2037        document.body.appendChild(fab);
2038        
2039        // Make FAB draggable
2040        let isDragging = false;
2041        let hasMoved = false;
2042        let currentX;
2043        let currentY;
2044        let initialX;
2045        let initialY;
2046        let xOffset = 0;
2047        let yOffset = 0;
2048        
2049        fab.addEventListener('mousedown', dragStart);
2050        document.addEventListener('mousemove', drag);
2051        document.addEventListener('mouseup', dragEnd);
2052        
2053        fab.addEventListener('touchstart', dragStart);
2054        document.addEventListener('touchmove', drag);
2055        document.addEventListener('touchend', dragEnd);
2056        
2057        function dragStart(e) {
2058            if (e.type === 'touchstart') {
2059                initialX = e.touches[0].clientX - xOffset;
2060                initialY = e.touches[0].clientY - yOffset;
2061            } else {
2062                initialX = e.clientX - xOffset;
2063                initialY = e.clientY - yOffset;
2064            }
2065            
2066            if (e.target === fab || fab.contains(e.target)) {
2067                isDragging = true;
2068                hasMoved = false;
2069            }
2070        }
2071        
2072        function drag(e) {
2073            if (isDragging) {
2074                e.preventDefault();
2075                
2076                if (e.type === 'touchmove') {
2077                    currentX = e.touches[0].clientX - initialX;
2078                    currentY = e.touches[0].clientY - initialY;
2079                } else {
2080                    currentX = e.clientX - initialX;
2081                    currentY = e.clientY - initialY;
2082                }
2083                
2084                // Check if moved more than 5px
2085                const distance = Math.sqrt(Math.pow(currentX - xOffset, 2) + Math.pow(currentY - yOffset, 2));
2086                if (distance > 5) {
2087                    hasMoved = true;
2088                }
2089                
2090                xOffset = currentX;
2091                yOffset = currentY;
2092                
2093                setTranslate(currentX, currentY, fab);
2094            }
2095        }
2096        
2097        function dragEnd() {
2098            if (isDragging) {
2099                initialX = currentX;
2100                initialY = currentY;
2101                isDragging = false;
2102                
2103                // Save position
2104                (async () => {
2105                    await GM.setValue('fab_position', JSON.stringify({ x: xOffset, y: yOffset }));
2106                })();
2107            }
2108        }
2109        
2110        function setTranslate(xPos, yPos, el) {
2111            el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
2112        }
2113        
2114        // Restore saved position
2115        (async () => {
2116            const savedPos = await GM.getValue('fab_position');
2117            if (savedPos) {
2118                const pos = JSON.parse(savedPos);
2119                xOffset = pos.x;
2120                yOffset = pos.y;
2121                setTranslate(pos.x, pos.y, fab);
2122            }
2123        })();
2124
2125        const panel = document.createElement('div');
2126        panel.className = 'st-panel';
2127        panel.innerHTML = `
2128            <div class="st-header">
2129                <div>📦 מעקב מלאי + WhatsApp</div>
2130                <button class="st-btn-secondary" id="st-close" style="padding:8px 12px;border-radius:50%;width:auto"></button>
2131            </div>
2132            <div class="st-body">
2133                <div id="st-stats"></div>
2134                
2135                <div style="margin-bottom:20px">
2136                    <label style="display:block;margin-bottom:8px;font-weight:700">📱 מספר WhatsApp</label>
2137                    <input type="text" class="st-input" id="st-phone" placeholder="972501234567">
2138                </div>
2139                
2140                <div style="margin-bottom:20px">
2141                    <label style="display:block;margin-bottom:8px;font-weight:700">⏱️ בדוק מלאי כל (דקות)</label>
2142                    <input type="number" class="st-input" id="st-interval" placeholder="5" min="1" value="5">
2143                    <p style="color:#999;font-size:12px;margin:5px 0 0 0">מינימום: 1 דקה | ברירת מחדל: 5 דקות</p>
2144                </div>
2145                
2146                <label class="st-checkbox">
2147                    <input type="checkbox" id="st-notifications" checked>
2148                    <span>🔔 הצג התראות בדפדפן</span>
2149                </label>
2150                
2151                <label class="st-checkbox">
2152                    <input type="checkbox" id="st-sound" checked>
2153                    <span>🔊 השמע צליל התראה</span>
2154                </label>
2155                
2156                <label class="st-checkbox">
2157                    <input type="checkbox" id="st-autocart">
2158                    <span>🛒 הוסף לסל אוטומטית כשחוזר למלאי</span>
2159                </label>
2160                
2161                <button class="st-btn" id="st-add">➕ הוסף מוצר למעקב</button>
2162                <button class="st-btn" id="st-quick-add" style="background:linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)">⚡ הוסף את כל המידות</button>
2163                <button class="st-btn" id="st-check">🔄 בדוק מלאי עכשיו</button>
2164                <button class="st-btn" id="st-test" style="background:linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)">🧪 בדיקת WhatsApp</button>
2165                
2166                <div id="st-countdown" style="text-align:center;padding:12px;background:#2a2a2a;border-radius:8px;margin:10px 0;color:#25D366;font-weight:700;font-size:14px">
2167                    ⏱️ בדיקה הבאה בעוד: <span id="st-countdown-text">--:--</span>
2168                </div>
2169                
2170                <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px">
2171                    <button class="st-btn st-btn-secondary" id="st-export" style="font-size:12px">💾 ייצוא</button>
2172                    <button class="st-btn st-btn-secondary" id="st-import" style="font-size:12px">📥 ייבוא</button>
2173                </div>
2174                
2175                <button class="st-btn st-btn-secondary st-btn-danger" id="st-clear" style="font-size:12px">🗑️ מחק הכל</button>
2176                
2177                <div style="margin-top:20px">
2178                    <h3 style="margin-bottom:12px">מוצרים במעקב:</h3>
2179                    
2180                    <div style="background:#2a2a8a;border:1px solid #3b82f6;border-radius:8px;padding:12px;margin-bottom:12px">
2181                        <div style="font-weight:700;margin-bottom:6px;color:#60a5fa">ℹ️ איך זה עובד?</div>
2182                        <div style="font-size:12px;color:#93c5fd;line-height:1.5">
2183                            • המערכת בודקת מלאי אוטומטית כל ${document.querySelector('#st-interval')?.value || 5} דקות<br>
2184<strong>חשוב:</strong> השאר דף אחד פתוח בדפדפן (לדוגמה: Zara, H&M)<br>
2185                            • המערכת תשלח התראת WhatsApp כשמידה חוזרת למלאי<br>
2186<span id="st-status-text" style="color:#22c55e;font-weight:700">🟢 המערכת פעילה</span>
2187                        </div>
2188                    </div>
2189                    
2190                    <div style="margin-bottom:12px">
2191                        <input type="text" class="st-input" id="st-search" placeholder="🔍 חפש מוצר..." style="margin:0">
2192                    </div>
2193                    
2194                    <div style="display:flex;gap:8px;margin-bottom:12px;flex-wrap:wrap">
2195                        <select class="st-input" id="st-sort" style="flex:1;margin:0">
2196                            <option value="date-desc">📅 חדשים ראשון</option>
2197                            <option value="date-asc">📅 ישנים ראשון</option>
2198                            <option value="price-desc">💰 מחיר גבוה-נמוך</option>
2199                            <option value="price-asc">💰 מחיר נמוך-גבוה</option>
2200                            <option value="stock-desc">📊 הכי הרבה במלאי</option>
2201                            <option value="stock-asc">📊 הכי פחות במלאי</option>
2202                        </select>
2203                        <select class="st-input" id="st-filter" style="flex:1;margin:0">
2204                            <option value="all">🔍 הכל</option>
2205                            <option value="in-stock">✅ במלאי בלבד</option>
2206                            <option value="out-stock">❌ לא במלאי בלבד</option>
2207                            <option value="partial">⚠️ חלקי במלאי</option>
2208                        </select>
2209                    </div>
2210                    
2211                    <div id="st-list"></div>
2212                </div>
2213            </div>
2214        `;
2215        document.body.appendChild(panel);
2216
2217        // Event listeners
2218        fab.onclick = async () => {
2219            if (hasMoved) {
2220                hasMoved = false;
2221                return; // Don't open panel if dragging
2222            }
2223            const isOpen = panel.style.display === 'block';
2224            panel.style.display = isOpen ? 'none' : 'block';
2225            if (!isOpen) {
2226                await loadPrefsToUI();
2227                await renderList();
2228                await renderStats();
2229            }
2230        };
2231
2232        panel.querySelector('#st-close').onclick = () => {
2233            panel.style.display = 'none';
2234        };
2235
2236        panel.querySelector('#st-add').onclick = addCurrentProduct;
2237        panel.querySelector('#st-quick-add').onclick = quickAddAllSizes;
2238        panel.querySelector('#st-check').onclick = async () => {
2239            await checkAllStock();
2240            // Reset countdown after manual check
2241            const prefs = await getPrefs();
2242            const interval = (prefs.checkInterval || 5) * 60 * 1000;
2243            nextCheckTime = Date.now() + interval;
2244        };
2245        panel.querySelector('#st-test').onclick = async () => {
2246            const prefs = await getPrefs();
2247            const testMessage = '🧪 בדיקת מערכת\n\nזוהי הודעת בדיקה המערכת.\nאם קיבלת הודעה זו - המערכת עובדת תקין! ✅';
2248            
2249            if (prefs.waPhone) {
2250                openWhatsApp(testMessage, prefs.waPhone);
2251                showToast('📱 פותח WhatsApp...', 'info');
2252            } else {
2253                showToast('⚠️ הזן מספר WhatsApp קודם', 'error');
2254            }
2255        };
2256        panel.querySelector('#st-export').onclick = exportData;
2257        panel.querySelector('#st-import').onclick = importData;
2258        panel.querySelector('#st-clear').onclick = clearAllData;
2259
2260        panel.querySelector('#st-phone').onchange = async (e) => {
2261            const prefs = await getPrefs();
2262            prefs.waPhone = e.target.value.trim();
2263            await savePrefs(prefs);
2264            showToast('✅ מספר WhatsApp נשמר', 'success');
2265        };
2266        
2267        panel.querySelector('#st-interval').onchange = async (e) => {
2268            const prefs = await getPrefs();
2269            prefs.checkInterval = Math.max(1, parseInt(e.target.value) || 5);
2270            await savePrefs(prefs);
2271            showToast('✅ זמן ריענון עודכן', 'success');
2272            await startBackgroundCheck();
2273        };
2274        
2275        panel.querySelector('#st-notifications').onchange = async (e) => {
2276            const prefs = await getPrefs();
2277            prefs.notifications = e.target.checked;
2278            await savePrefs(prefs);
2279            showToast(e.target.checked ? '🔔 התראות מופעלות' : '🔕 התראות כבויות', 'info');
2280        };
2281        
2282        panel.querySelector('#st-sound').onchange = async (e) => {
2283            const prefs = await getPrefs();
2284            prefs.soundAlerts = e.target.checked;
2285            await savePrefs(prefs);
2286            if (e.target.checked) {
2287                playNotificationSound();
2288                showToast('🔊 צלילי התראה מופעלים', 'info');
2289            } else {
2290                showToast('🔇 צלילי התראה כבויים', 'info');
2291            }
2292        };
2293        
2294        panel.querySelector('#st-autocart').onchange = async (e) => {
2295            const prefs = await getPrefs();
2296            prefs.autoAddToCart = e.target.checked;
2297            await savePrefs(prefs);
2298            showToast(e.target.checked ? '🛒 הוספה אוטומטית הופעלה' : '🛒 הוספה אוטומטית כובתה', 'info');
2299        };
2300        
2301        // Search, filter, and sort event listeners
2302        const searchEl = panel.querySelector('#st-search');
2303        const sortEl = panel.querySelector('#st-sort');
2304        const filterEl = panel.querySelector('#st-filter');
2305        
2306        if (searchEl) {
2307            searchEl.oninput = () => renderList();
2308        }
2309        
2310        if (sortEl) {
2311            sortEl.onchange = () => renderList();
2312        }
2313        
2314        if (filterEl) {
2315            filterEl.onchange = () => renderList();
2316        }
2317    }
2318
2319    // Toast notification
2320    function showToast(message, type = 'success') {
2321        const toast = document.createElement('div');
2322        toast.className = `st-toast ${type}`;
2323        toast.textContent = message;
2324        document.body.appendChild(toast);
2325        
2326        setTimeout(() => {
2327            toast.style.transition = 'opacity 0.3s';
2328            toast.style.opacity = '0';
2329            setTimeout(() => toast.remove(), 300);
2330        }, 3500);
2331    }
2332
2333    // Load preferences to UI
2334    async function loadPrefsToUI() {
2335        const prefs = await getPrefs();
2336        const phoneEl = document.querySelector('#st-phone');
2337        const intervalEl = document.querySelector('#st-interval');
2338        const notificationsEl = document.querySelector('#st-notifications');
2339        const soundEl = document.querySelector('#st-sound');
2340        const autoCartEl = document.querySelector('#st-autocart');
2341        
2342        if (phoneEl) phoneEl.value = prefs.waPhone || '';
2343        if (intervalEl) intervalEl.value = prefs.checkInterval || 5;
2344        if (notificationsEl) notificationsEl.checked = prefs.notifications !== false;
2345        if (soundEl) soundEl.checked = prefs.soundAlerts !== false;
2346        if (autoCartEl) autoCartEl.checked = !!prefs.autoAddToCart;
2347    }
2348
2349    // Render statistics
2350    async function renderStats() {
2351        const items = await getItems();
2352        const statsEl = document.querySelector('#st-stats');
2353        
2354        if (!statsEl) return;
2355        
2356        const totalItems = items.length;
2357        const totalSizes = items.reduce((sum, item) => sum + item.sizes.length, 0);
2358        const inStockSizes = items.reduce((sum, item) => 
2359            sum + item.sizes.filter(s => s.inStock).length, 0
2360        );
2361        
2362        // Calculate total price
2363        let totalPrice = 0;
2364        let priceCount = 0;
2365        items.forEach(item => {
2366            const price = parsePrice(item.price);
2367            if (price) {
2368                totalPrice += price;
2369                priceCount++;
2370            }
2371        });
2372        
2373        const avgPrice = priceCount > 0 ? (totalPrice / priceCount).toFixed(2) : 0;
2374        
2375        statsEl.innerHTML = `
2376            <div class="st-stat">
2377                <div class="st-stat-value">${totalItems}</div>
2378                <div class="st-stat-label">מוצרים</div>
2379            </div>
2380            <div class="st-stat">
2381                <div class="st-stat-value">${totalSizes}</div>
2382                <div class="st-stat-label">מידות</div>
2383            </div>
2384            <div class="st-stat">
2385                <div class="st-stat-value">${inStockSizes}</div>
2386                <div class="st-stat-label">במלאי</div>
2387            </div>
2388            <div class="st-stat" style="grid-column: span 3">
2389                <div class="st-stat-value" style="color:#fbbf24">${totalPrice.toFixed(2)}</div>
2390                <div class="st-stat-label">סה"כ מחיר כל המוצרים | ממוצע: ₪${avgPrice}</div>
2391            </div>
2392        `;
2393    }
2394
2395    // Add current product
2396    async function addCurrentProduct() {
2397        const adapter = getAdapter();
2398        if (!adapter) {
2399            showToast('⚠️ אתר זה לא נתמך כרגע', 'error');
2400            return;
2401        }
2402
2403        const title = adapter.title();
2404        const price = adapter.price();
2405        const image = adapter.image();
2406        const url = location.href.split('#')[0].split('?')[0];
2407
2408        if (!title) {
2409            showToast('⚠️ לא הצלחתי לזהות את המוצר', 'error');
2410            return;
2411        }
2412
2413        showToast('⏳ מחפש מידות...', 'info');
2414
2415        const sizes = await adapter.getSizes();
2416        
2417        if (!sizes || sizes.length === 0) {
2418            showToast('⚠️ לא נמצאו מידות', 'error');
2419            return;
2420        }
2421
2422        showSizeSelection(title, price || 'לא זמין', image, url, sizes);
2423    }
2424
2425    // Show size selection modal
2426    function showSizeSelection(title, price, image, url, sizes, editingItemId = null) {
2427        const overlay = document.createElement('div');
2428        overlay.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.9);z-index:99999999;display:flex;align-items:center;justify-content:center';
2429        
2430        const modal = document.createElement('div');
2431        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';
2432        
2433        modal.innerHTML = `
2434            <h2 style="margin:0 0 16px 0;color:#fff">${editingItemId ? '✏️ ערוך מידות' : '🧵 בחר מידות למעקב'}</h2>
2435            <p style="color:#999;margin-bottom:20px">${title}</p>
2436            <p style="color:#999;margin-bottom:20px">תקבל התראת WhatsApp כשהמידה חוזרת למלאי</p>
2437            <div id="size-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(80px,1fr));gap:10px;margin-bottom:20px"></div>
2438            <button class="st-btn" id="confirm-sizes">${editingItemId ? 'שמור שינויים' : 'התחל מעקב'}</button>
2439            <button class="st-btn st-btn-secondary" id="cancel-sizes" style="margin-top:0">❌ ביטול</button>
2440        `;
2441        
2442        overlay.appendChild(modal);
2443        document.body.appendChild(overlay);
2444        
2445        const sizeGrid = modal.querySelector('#size-grid');
2446        const selectedSizes = new Set();
2447        
2448        sizes.forEach((size, idx) => {
2449            const btn = document.createElement('button');
2450            btn.style.cssText = `
2451                background: #2a2a2a;
2452                color: #fff;
2453                border: 2px solid ${size.inStock ? '#22c55e' : '#ef4444'};
2454                border-radius: 8px;
2455                padding: 12px;
2456                font-weight: 700;
2457                cursor: pointer;
2458                transition: all 0.2s;
2459            `;
2460            btn.textContent = `${size.label} ${size.inStock ? '✅' : '❌'}`;
2461            
2462            btn.onclick = () => {
2463                if (selectedSizes.has(idx)) {
2464                    selectedSizes.delete(idx);
2465                    btn.style.background = '#2a2a2a';
2466                } else {
2467                    selectedSizes.add(idx);
2468                    btn.style.background = '#25D366';
2469                    btn.style.color = '#000';
2470                }
2471            };
2472            
2473            sizeGrid.appendChild(btn);
2474        });
2475        
2476        modal.querySelector('#confirm-sizes').onclick = async () => {
2477            if (selectedSizes.size === 0) {
2478                showToast('⚠️ בחר לפחות מידה אחת', 'error');
2479                return;
2480            }
2481            
2482            const items = await getItems();
2483            
2484            const selectedSizeData = Array.from(selectedSizes).map(idx => ({
2485                label: sizes[idx].label,
2486                selector: buildSelector(sizes[idx].element),
2487                inStock: sizes[idx].inStock,
2488                lastChecked: Date.now()
2489            }));
2490            
2491            if (editingItemId) {
2492                // Update existing item
2493                const itemIndex = items.findIndex(i => i.id === editingItemId);
2494                if (itemIndex !== -1) {
2495                    items[itemIndex].sizes = selectedSizeData;
2496                    items[itemIndex].price = price;
2497                    showToast(`✅ המוצר עודכן עם ${selectedSizes.size} מידות!`, 'success');
2498                }
2499            } else {
2500                // Add new item
2501                items.push({
2502                    id: Date.now().toString(),
2503                    title,
2504                    price,
2505                    image,
2506                    url,
2507                    sizes: selectedSizeData,
2508                    addedAt: Date.now()
2509                });
2510                showToast(`✅ נוספו ${selectedSizes.size} מידות למעקב!`, 'success');
2511            }
2512            
2513            await saveItems(items);
2514            overlay.remove();
2515            await renderList();
2516            await renderStats();
2517            await updateFabBadge();
2518        };
2519        
2520        modal.querySelector('#cancel-sizes').onclick = () => {
2521            overlay.remove();
2522        };
2523        
2524        overlay.onclick = (e) => {
2525            if (e.target === overlay) overlay.remove();
2526        };
2527    }
2528
2529    // Render items list
2530    async function renderList() {
2531        const items = await getItems();
2532        const listEl = document.querySelector('#st-list');
2533        
2534        if (!listEl) return;
2535        
2536        if (items.length === 0) {
2537            listEl.innerHTML = '<p style="text-align:center;color:#999;padding:20px">אין מוצרים במעקב</p>';
2538            return;
2539        }
2540        
2541        // Get search, sort, and filter values
2542        const searchQuery = document.querySelector('#st-search')?.value?.toLowerCase() || '';
2543        const sortValue = document.querySelector('#st-sort')?.value || 'date-desc';
2544        const filterValue = document.querySelector('#st-filter')?.value || 'all';
2545        
2546        // Filter items
2547        let filteredItems = [...items];
2548        
2549        // Apply search filter
2550        if (searchQuery) {
2551            filteredItems = filteredItems.filter(item => 
2552                item.title.toLowerCase().includes(searchQuery) ||
2553                item.url.toLowerCase().includes(searchQuery)
2554            );
2555        }
2556        
2557        // Apply stock filter
2558        if (filterValue === 'in-stock') {
2559            filteredItems = filteredItems.filter(item => 
2560                item.sizes.every(s => s.inStock)
2561            );
2562        } else if (filterValue === 'out-stock') {
2563            filteredItems = filteredItems.filter(item => 
2564                item.sizes.every(s => !s.inStock)
2565            );
2566        } else if (filterValue === 'partial') {
2567            filteredItems = filteredItems.filter(item => 
2568                item.sizes.some(s => s.inStock) && item.sizes.some(s => !s.inStock)
2569            );
2570        }
2571        
2572        // Apply sorting
2573        filteredItems.sort((a, b) => {
2574            if (sortValue === 'date-desc') {
2575                return b.addedAt - a.addedAt;
2576            } else if (sortValue === 'date-asc') {
2577                return a.addedAt - b.addedAt;
2578            } else if (sortValue === 'price-desc') {
2579                const priceA = parsePrice(a.price) || 0;
2580                const priceB = parsePrice(b.price) || 0;
2581                return priceB - priceA;
2582            } else if (sortValue === 'price-asc') {
2583                const priceA = parsePrice(a.price) || 0;
2584                const priceB = parsePrice(b.price) || 0;
2585                return priceA - priceB;
2586            } else if (sortValue === 'stock-desc') {
2587                const stockA = a.sizes.filter(s => s.inStock).length;
2588                const stockB = b.sizes.filter(s => s.inStock).length;
2589                return stockB - stockA;
2590            } else if (sortValue === 'stock-asc') {
2591                const stockA = a.sizes.filter(s => s.inStock).length;
2592                const stockB = b.sizes.filter(s => s.inStock).length;
2593                return stockA - stockB;
2594            }
2595            return 0;
2596        });
2597        
2598        // Show no results message if filtered list is empty
2599        if (filteredItems.length === 0) {
2600            listEl.innerHTML = '<p style="text-align:center;color:#999;padding:20px">לא נמצאו מוצרים מתאימים</p>';
2601            return;
2602        }
2603        
2604        listEl.innerHTML = filteredItems.map(item => {
2605            const inStockCount = item.sizes.filter(s => s.inStock).length;
2606            const totalCount = item.sizes.length;
2607            const siteName = getSiteName(item.url);
2608            
2609            return `
2610            <div class="st-item" data-item-id="${item.id}">
2611                <div style="display:flex;gap:12px;margin-bottom:12px">
2612                    ${item.image ? `<img src="${item.image}" style="width:60px;height:60px;object-fit:cover;border-radius:8px">` : ''}
2613                    <div style="flex:1">
2614                        <div style="font-weight:700;margin-bottom:4px">${item.title}</div>
2615                        <div style="color:#999;font-size:11px;margin-bottom:4px">🏪 ${siteName}</div>
2616                        <div style="color:#999;font-size:12px">💰 מחיר: ${item.price}</div>
2617                        <div style="color:#999;font-size:12px">📊 מלאי: ${inStockCount}/${totalCount} מידות</div>
2618                        <div style="color:#999;font-size:12px">📅 נוסף: ${formatTimeAgo(item.addedAt)}</div>
2619                        ${item.sizes[0]?.lastChecked ? `<div style="color:#999;font-size:12px">🔄 נבדק: ${formatTimeAgo(item.sizes[0].lastChecked)}</div>` : ''}
2620                    </div>
2621                </div>
2622                <div style="margin:8px 0">
2623                    ${item.sizes.map(s => `
2624                        <span class="st-size ${s.inStock ? 'in-stock' : 'out-of-stock'}" title="לחץ לפתיחת המוצר" onclick="window.open('${item.url}', '_blank')">
2625                            ${s.label} ${s.inStock ? '✅' : '❌'}
2626                        </span>
2627                    `).join('')}
2628                </div>
2629                <div class="st-button-grid">
2630                    <button class="st-btn st-btn-secondary st-action-btn" data-action="check" data-id="${item.id}" style="padding:10px 8px;font-size:11px">🔍 בדוק</button>
2631                    <button class="st-btn st-btn-secondary st-action-btn" data-action="open" data-url="${item.url}" style="padding:10px 8px;font-size:11px">🔗 פתח</button>
2632                    <button class="st-btn st-btn-secondary st-action-btn" data-action="edit" data-id="${item.id}" style="padding:10px 8px;font-size:11px">✏️ ערוך</button>
2633                    <button class="st-btn st-btn-secondary st-action-btn" data-action="copy" data-url="${item.url}" style="padding:10px 8px;font-size:11px">📋 העתק</button>
2634                    <button class="st-btn st-btn-secondary st-action-btn" data-action="remove" data-id="${item.id}" style="padding:10px 8px;font-size:11px">🗑️ מחק</button>
2635                </div>
2636            </div>
2637        `;}).join('');
2638        
2639        // Add event listeners to buttons using event delegation
2640        listEl.querySelectorAll('.st-action-btn').forEach(btn => {
2641            btn.addEventListener('click', async function(e) {
2642                e.preventDefault();
2643                e.stopPropagation();
2644                
2645                const action = this.getAttribute('data-action');
2646                const id = this.getAttribute('data-id');
2647                const url = this.getAttribute('data-url');
2648                
2649                console.log('[Stock Tracker] Button clicked:', action, 'id:', id, 'url:', url);
2650                
2651                if (action === 'check') {
2652                    await checkSingleItem(id);
2653                } else if (action === 'open') {
2654                    window.open(url, '_blank');
2655                } else if (action === 'edit') {
2656                    await editItem(id);
2657                } else if (action === 'copy') {
2658                    await copyProductLink(url);
2659                } else if (action === 'remove') {
2660                    await removeItem(id);
2661                }
2662            });
2663        });
2664    }
2665
2666    // Remove item
2667    async function removeItem(id) {
2668        console.log('[Stock Tracker] Removing item:', id);
2669        
2670        if (!confirm('האם אתה בטוח שברצונך למחוק מוצר זה?')) return;
2671        
2672        const items = await getItems();
2673        const filtered = items.filter(item => item.id !== id);
2674        
2675        console.log('[Stock Tracker] Items before:', items.length, 'after:', filtered.length);
2676        
2677        await saveItems(filtered);
2678        await renderList();
2679        await renderStats();
2680        await updateFabBadge();
2681        showToast('🗑️ מוצר נמחק בהצלחה', 'success');
2682    }
2683
2684    // Edit item
2685    async function editItem(id) {
2686        console.log('[Stock Tracker] Editing item:', id);
2687        
2688        const items = await getItems();
2689        const item = items.find(i => i.id === id);
2690        if (!item) {
2691            showToast('⚠️ מוצר לא נמצא', 'error');
2692            return;
2693        }
2694        
2695        showToast('🔗 פותח את העמוד לעריכה...', 'info');
2696        window.open(item.url, '_blank');
2697    }
2698
2699    // Start background checking
2700    let checkInterval = null;
2701    async function startBackgroundCheck() {
2702        if (checkInterval) clearInterval(checkInterval);
2703        if (countdownInterval) clearInterval(countdownInterval);
2704        
2705        const prefs = await getPrefs();
2706        const interval = (prefs.checkInterval || 5) * 60 * 1000;
2707        
2708        // Set next check time
2709        nextCheckTime = Date.now() + interval;
2710        
2711        // Start countdown display
2712        countdownInterval = setInterval(updateCountdown, 1000);
2713        updateCountdown();
2714        
2715        checkInterval = setInterval(() => {
2716            nextCheckTime = Date.now() + interval;
2717            checkAllStock();
2718        }, interval);
2719        
2720        console.log('[Stock Tracker] Background check started. Interval:', interval / 60000, 'minutes');
2721    }
2722
2723    // Update countdown display
2724    function updateCountdown() {
2725        const countdownEl = document.querySelector('#st-countdown-text');
2726        if (!countdownEl || !nextCheckTime) return;
2727        
2728        const now = Date.now();
2729        const diff = nextCheckTime - now;
2730        
2731        if (diff <= 0) {
2732            countdownEl.textContent = '00:00';
2733            return;
2734        }
2735        
2736        const minutes = Math.floor(diff / 60000);
2737        const seconds = Math.floor((diff % 60000) / 1000);
2738        
2739        countdownEl.textContent = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
2740    }
2741
2742    // Initialize
2743    async function init() {
2744        console.log('[Stock Tracker] Initializing...');
2745        
2746        // Check if site is blocked
2747        if (isBlockedSite()) {
2748            console.log('[Stock Tracker] Blocked site detected, not initializing');
2749            return;
2750        }
2751        
2752        // Always check if we have tracked items - if yes, run background checks
2753        const items = await getItems();
2754        const isEcommerce = isEcommerceSite();
2755        
2756        // Fix old selectors that include status classes
2757        if (items.length > 0) {
2758            let needsSave = false;
2759            items.forEach(item => {
2760                item.sizes.forEach(size => {
2761                    if (typeof size.selector === 'string' && (size.selector.includes('--disabled') || size.selector.includes('--unavailable') || size.selector.includes('--enabled'))) {
2762                        console.log('[Stock Tracker] Fixing old selector for size:', size.label);
2763                        // Remove status classes from selector
2764                        size.selector = size.selector
2765                            .replace(/\.size-selector-sizes-size--disabled/g, '')
2766                            .replace(/\.size-selector-sizes-size--unavailable/g, '')
2767                            .replace(/\.size-selector-sizes-size--enabled/g, '')
2768                            .replace(/\.is-disabled/g, '')
2769                            .replace(/\.is-unavailable/g, '')
2770                            .replace(/\.disabled/g, '')
2771                            .replace(/\.unavailable/g, '')
2772                            .replace(/\.enabled/g, '');
2773                        needsSave = true;
2774                    }
2775                });
2776            });
2777            
2778            if (needsSave) {
2779                await saveItems(items);
2780                console.log('[Stock Tracker] Fixed old selectors, saved updated items');
2781            }
2782        }
2783        
2784        // If we have items, always initialize (even on non-ecommerce sites)
2785        // This ensures background checks run everywhere
2786        if (items.length > 0) {
2787            console.log('[Stock Tracker] Found', items.length, 'tracked items, initializing...');
2788        } else if (!isEcommerce) {
2789            console.log('[Stock Tracker] Not an e-commerce site and no tracked items, not initializing');
2790            return;
2791        }
2792        
2793        if (!document.body) {
2794            await new Promise(resolve => {
2795                if (document.readyState === 'loading') {
2796                    document.addEventListener('DOMContentLoaded', resolve);
2797                } else {
2798                    resolve();
2799                }
2800            });
2801        }
2802        
2803        createUI();
2804        
2805        // Always start background checks if we have items
2806        if (items.length > 0) {
2807            await startBackgroundCheck();
2808            console.log('[Stock Tracker] Background checks started for', items.length, 'items');
2809        }
2810        
2811        await updateFabBadge();
2812        
2813        if (location.host.includes('web.whatsapp.com')) {
2814            autoSendWhatsApp();
2815        }
2816        
2817        // Run auto-cart if requested via URL parameters
2818        runAutoCartIfRequested();
2819        
2820        // Global API
2821        window.stockTracker = {
2822            removeItem,
2823            editItem,
2824            checkAllStock,
2825            exportData,
2826            importData,
2827            copyLink: copyProductLink
2828        };
2829        
2830        console.log('[Stock Tracker] Ready!');
2831    }
2832
2833    init().catch(err => console.error('[Stock Tracker] Init error:', err));
2834
2835})();
Price Tracker Pro — WhatsApp Alerts | Robomonkey