Blocket ↔ Marketplace Brygga

Skrapa annonser från Blocket, skapa nya automatiskt och lägg upp på Facebook Marketplace

Size

70.6 KB

Version

2.0

Created

Mar 30, 2026

Updated

12 days ago

1// ==UserScript==
2// @name         Blocket ↔ Marketplace Brygga
3// @namespace    http://tampermonkey.net/
4// @version      2.0
5// @description  Skrapa annonser från Blocket, skapa nya automatiskt och lägg upp på Facebook Marketplace
6// @author       Henrik + Claude
7// @match        https://www.blocket.se/my-items*
8// @match        https://www.blocket.se/recommerce/forsale/item/*
9// @match        https://www.blocket.se/recommerce/create/*
10// @match        https://www.blocket.se/create-item/*
11// @match        https://www.facebook.com/marketplace/*
12// @icon         https://www.blocket.se/favicon.ico
13// @grant        GM_setValue
14// @grant        GM_getValue
15// @grant        GM_xmlhttpRequest
16// @grant        GM_setClipboard
17// @connect      images.blocketcdn.se
18// @connect      www.blocket.se
19// @connect      localhost
20// @run-at       document-idle
21// ==/UserScript==
22
23(function () {
24    'use strict';
25
26    // ==================== GM_ POLYFILL (fungerar utan Tampermonkey också) ====================
27    if (typeof GM_getValue === 'undefined') {
28        window.GM_getValue = function(key, def) {
29            try { var v = localStorage.getItem('gm_'+key); return v !== null ? JSON.parse(v) : def; } catch(e) { return def; }
30        };
31    }
32    if (typeof GM_setValue === 'undefined') {
33        window.GM_setValue = function(key, val) {
34            try { localStorage.setItem('gm_'+key, JSON.stringify(val)); } catch(e) {}
35        };
36    }
37    if (typeof GM_setClipboard === 'undefined') {
38        window.GM_setClipboard = function(text) {
39            navigator.clipboard ? navigator.clipboard.writeText(text) : void 0;
40        };
41    }
42    if (typeof GM_xmlhttpRequest === 'undefined') {
43        window.GM_xmlhttpRequest = function(opts) {
44            var xhr = new XMLHttpRequest();
45            xhr.open(opts.method || 'GET', opts.url);
46            if (opts.headers) Object.keys(opts.headers).forEach(function(k){ xhr.setRequestHeader(k, opts.headers[k]); });
47            xhr.onload = function(){ opts.onload && opts.onload({responseText: xhr.responseText, status: xhr.status}); };
48            xhr.onerror = function(){ opts.onerror && opts.onerror(); };
49            xhr.send(opts.data || null);
50        };
51    }
52
53    // ==================== KONFIGURATION ====================
54    var config = {
55        debugLäge: true,
56        lagringsNyckel: 'btm_annonser_v2',
57        panelBredd: 390,
58        accentFärg: '#e4002b',
59        fbBlå: '#1877f2',
60        bgMörk: '#1a1a2e',
61        bgPanel: '#16213e',
62        textFärg: '#e8e8e8',
63        textDämpat: '#8899aa',
64        succéFärg: '#00c853',
65        varningFärg: '#ff9800'
66    };
67
68    // ==================== TILLSTÅND ====================
69    var tillstånd = {
70        annonser: [],
71        valadAnnons: null,
72        panelÖppen: false,
73        sajt: ''
74    };
75
76    // ==================== BLOCKET KATEGORIER (ALLA GRATIS) ====================
77    // Källa: Blocket /recommerce/create – kartlagd live April 2026
78    // Fordon och Bostad är betalda – används ALDRIG av auto-matcharen
79    var KATEGORIER = {
80        'Elektronik och vitvaror': {
81            id: '93',
82            subs: {
83                'Datorer': { id: '3215', l3: { 'Laptops': '43', 'Stationär dator': '44', 'Datorskärmar': '45', 'Datortillbehör': '46', 'Surfplattor och läsplattor': '48', 'Hårddiskar och lagring': '8367', 'Datorkomponenter': '8368', 'Nätverk': '9434', 'Software': '49', 'Miniräknare': '47' }},
84                'Foto och video': { id: '3904', l3: { 'Systemkameror': '66', 'Kompaktkameror': '502', 'Hybridkameror': '303', 'Videokameror': '504', 'Objektiv': '503', 'Kameraväskor': '500', 'Övrig fotoutrustning': '505' }},
85                'Hushållsapparater': { id: '3216', l3: { 'Dammsugare': '81', 'Kaffebryggare och -maskiner': '506', 'Köksmaskiner och matberedare': '507', 'Vattenkokare': '85', 'Brödrostar': '87', 'Strykjärn': '84', 'Stavmixer och elvispar': '89', 'Mixers och blenders': '83', 'Våffeljärn och smörgåsgrillar': '88', 'Övriga hushållsapparater': '304' }},
86                'Ljud och bild': { id: '3906', l3: { 'TV': '62', 'Högtalare': '53', 'Hörlurar': '51', 'Förstärkare och receivers': '52', 'Hemmabiosystem': '59', 'Projektorer och projektordukar': '60', 'Streaming och digitalboxar': '61', 'PA-system': '54', 'Radio': '302', 'Stereoanläggningar': '55', 'MP3-spelare och portabelt ljud': '50', 'Kablar och tillbehör': '326', 'Blu-ray-spelare': '56', 'DVD-spelare': '57' }},
87                'Telefoner och tillbehör': { id: '3217', l3: { 'Mobiltelefoner': '39', 'Mobiltillbehör': '41', 'Övriga telefoner': '40' }},
88                'TV-spel och spelkonsoler': { id: '3905', l3: { 'Spelkonsoler': '63', 'Spel': '64', 'Gamingtillbehör': '65' }},
89                'Vitvaror': { id: '3907', l3: { 'Tvättmaskiner': '79', 'Kylskåp': '292', 'Frysar': '72', 'Diskmaskiner': '509', 'Spisar': '73', 'Spishällar och -plattor': '75', 'Inbyggnadsugnar': '74', 'Mikrovågsugnar': '508', 'Torktumlare': '510', 'Köksfläktar': '511', 'Övriga vitvaror': '305' }},
90                'Övrig elektronik': { id: '3213', l3: {}}
91            }
92        },
93        'Möbler och inredning': {
94            id: '78',
95            subs: {
96                'Soffor och fåtöljer': { id: '7756', l3: { 'Soffor': '8374', 'Hörnsoffor': '207', 'Bäddsoffor': '206', 'Fåtöljer': '210', 'Soffgrupper': '208', 'Sittpuffar': '209' }},
97                'Bord och stolar': { id: '5196', l3: { 'Matbord': '213', 'Matgrupper': '211', 'Soffbord': '212', 'Stolar och pallar': '216', 'Skrivbord': '214', 'Kontorsstolar': '215', 'Övriga bord och stolar': '217' }},
98                'Sängar och madrasser': { id: '5197', l3: { 'Sängar': '202', 'Madrasser': '203', 'Rammadrasser': '293' }},
99                'Garderober och förvaring': { id: '5198', l3: { 'Garderober': '218', 'Hyllsystem': '222', 'Skåp': '225' }},
100                'Hyllor och byråer': { id: '8345', l3: { 'Hyllor': '219', 'Byråer': '221', 'Sideboards': '220', 'Sängbord': '223', 'TV- och mediamöbler': '224' }},
101                'Lampor': { id: '5181', l3: { 'Taklampor': '226', 'Golvlampor': '228', 'Bordslampor': '229', 'Vägglampor': '227', 'Övriga lampor': '230' }},
102                'Dekoration och prydnader': { id: '5222', l3: { 'Tavlor och ramar': '237', 'Vaser och krukor': '238', 'Speglar': '299', 'Ljusstakar': '240', 'Dekorationsfat och -skålar': '239', 'Övriga prydnader': '241' }},
103                'Mattor och textilier': { id: '5180', l3: { 'Mattor': '231', 'Gardiner': '234', 'Kuddar': '232', 'Dukar': '233', 'Handdukar': '2469', 'Sängkläder': '2468', 'Övriga textilier': '235' }},
104                'Köksutrustning och porslin': { id: '5223', l3: { 'Serviser': '8361', 'Glas': '8360', 'Bestick': '8362', 'Koppar och muggar': '8359', 'Köksredskap': '8364', 'Serveringsfat och -skålar': '8363', 'Förvaringsburkar': '8365', 'Övrig köksutrustning': '8366' }},
105                'Övriga möbler och inredning': { id: '3971', l3: {}}
106            }
107        },
108        'Sport och friluftsliv': {
109            id: '69',
110            subs: {
111                'Cykling': { id: '3963', l3: { 'Cyklar': '257', 'Barncyklar': '8375', 'Cykelutrustning': '258', 'Sparkcyklar': '8376', 'Cykelkläder': '9773', 'Cykelskor': '9774' }},
112                'Träningsmaskiner och -redskap': { id: '5166', l3: { 'Styrketräning': '288', 'Löpband': '516', 'Motionscyklar': '286', 'Crosstrainers': '291', 'Roddmaskiner': '287', 'Träningsutrustning': '289', 'Trappmaskiner': '290' }},
113                'Träningskläder och skor': { id: '3940', l3: { 'Herrkläder': '3980', 'Damkläder': '3981', 'Barnkläder': '3983', 'Unisex': '3982', 'Träningsskor': '3984' }},
114                'Bollsporter': { id: '3961', l3: { 'Fotboll': '245', 'Tennis': '249', 'Padel': '9486', 'Basket': '247', 'Handboll': '246', 'Bordtennis': '9487', 'Badminton': '9485', 'Volleyboll': '248', 'Squash': '9488', 'Övriga bollsporter': '251' }},
115                'Jakt, fiske och camping': { id: '3964', l3: { 'Fiskespön': '271', 'Fiskerullar, flugor och krokar': '273', 'Tält': '277', 'Sovsäckar': '276', 'Ryggsäckar': '278', 'Kanoter och kajaker': '279', 'Kikare och optik': '300', 'Jaktkläder': '269', 'Vapen och ammunition': '268', 'Övrig fiskeutrustning': '275', 'Övrig friluftsutrustning': '280' }},
116                'Vintersport': { id: '3962', l3: { 'Snowboard': '252', 'Alpint': '253', 'Telemark': '254', 'Längdskidor': '255', 'Övrig skidutrustning': '256' }},
117                'Vattensport': { id: '7738', l3: { 'SUP': '260', 'Segling': '261', 'Dykutrustning': '259', 'Surfing': '262', 'Vindsurfing': '338', 'Wingfoil': '2467', 'Övrig vattensportsutrustning': '263' }},
118                'Golf': { id: '5164', l3: { 'Golfset': '9769', 'Golfklubbor': '9770', 'Golfbollar': '9768', 'Golfbagar och -vagnar': '9771', 'Golfkläder': '9766', 'Golfskor': '9767', 'Övrig golfutrustning': '9772' }},
119                'Extremsport': { id: '3938', l3: { 'Klättring': '265', 'Skateboard och longboard': '267', 'Kitesurfing': '264', 'Fallskärmshoppning': '266' }},
120                'Övriga sporter': { id: '3966', l3: {}}
121            }
122        },
123        'Underhållning och hobby': {
124            id: '86',
125            subs: {
126                'Musikinstrument': { id: '92', l3: { 'Gitarrer': '295', 'Basgitarrer': '296', 'Pianon och flyglar': '109', 'Keyboards och synthar': '110', 'Slagverk och trummor': '107', 'Bleckblåsinstrument': '104', 'Träblåsinstrument': '103', 'Stråkinstrument': '105', 'Dragspel': '328', 'Orgel': '337', 'Övriga stränginstrument': '106', 'Ljudutrustning': '108', 'Övriga instrument': '111' }},
127                'Böcker och tidningar': { id: '5209', l3: { 'Skönlitteratur': '514', 'Facklitteratur': '520', 'Kurslitteratur': '513', 'Kokböcker': '94', 'Serietidningar': '515', 'Broschyrer och tidningar': '512', 'Övriga böcker': '97' }},
128                'Sällskapsspel och pussel': { id: '5203', l3: { 'Brädspel': '119', 'Sällskapsspel': '120', 'Pussel': '329', 'Utomhusspel': '121', 'Övriga spel': '122' }},
129                'Samlarobjekt': { id: '285', l3: { 'Mynt och sedlar': '138', 'Frimärken': '137', 'Samlarkort': '396', 'Samlarfigurer': '400', 'Leksaker': '399', 'Militära föremål': '397', 'Skyltar och affischer': '398', 'Pins': '139', 'Vykort': '140', 'Övriga samlarobjekt': '141' }},
130                'Musik och film': { id: '3922', l3: { 'Vinyl': '99', 'CD': '98', 'DVD': '100', 'Blu-ray': '102', 'VHS': '101', 'Kassettband': '402' }},
131                'Hantverk': { id: '7734', l3: { 'Garn': '124', 'Symaskiner': '123', 'Sy- och sticktillbehör': '126', 'Tyg': '125', 'Vävstolar': '127', 'Keramik': '130', 'Träslöjd': '132', 'Färg och målartillbehör': '128', 'Pärlor och smyckesstenar': '131', 'Scrapbooking': '129' }},
132                'Radiostyrda enheter': { id: '7733', l3: { 'Drönare': '401', 'Radiostyrda bilar': '133', 'RC-flygplan': '135', 'RC-helikoptrar': '136', 'Radiostyrda båtar': '134', 'Modelljärnväg': '327' }},
133                'Biljetter och resor': { id: '7735', l3: { 'Flygbiljetter': '113', 'Paketresor': '112', 'Tågbiljetter': '114' }},
134                'Övrigt inom underhållning och hobby': { id: '3973', l3: {}}
135            }
136        },
137        'Kläder, kosmetika och accessoarer': {
138            id: '71',
139            subs: {
140                'Damkläder': { id: '3941', l3: { 'Klänningar': '172', 'Byxor och shorts': '174', 'Jackor': '178', 'Ytterkläder': '173', 'Tröjor och stickat': '177', 'Skjortor och blusar': '180', 'Jeans': '8370', 'Toppar': '176', 'Kjolar': '175', 'T-shirts': '179', 'Underkläder': '181', 'Kostymer och kavajer': '8371', 'Jumpsuits': '8372', 'Övriga damkläder': '182' }},
141                'Herrkläder': { id: '3950', l3: { 'Ytterkläder': '183', 'Byxor och shorts': '184', 'Skjortor': '185', 'Tröjor och stickat': '186', 'T-shirts och linnen': '187', 'Jeans': '8373', 'Kostymer': '189', 'Underkläder': '191', 'Övriga herrkläder': '192' }},
142                'Skor': { id: '3949', l3: { 'Damskor': '194', 'Herrskor': '193' }},
143                'Väskor och plånböcker': { id: '3946', l3: { 'Handväskor': '9473', 'Ryggsäckar': '9472', 'Axelremsväskor': '9474', 'Resväskor': '9476', 'Plånböcker och korthållare': '9471', 'Portföljer och datorväskor': '9477', 'Övriga väskor': '9480' }},
144                'Smycken': { id: '7748', l3: { 'Halsband': '195', 'Ringar': '196', 'Armband': '198', 'Örhängen': '197', 'Övriga smycken': '306' }},
145                'Klockor och armbandsur': { id: '3945', l3: {}},
146                'Kosmetika': { id: '8282', l3: { 'Parfym': '1660', 'Smink': '1659' }},
147                'Övriga kläder, kosmetika och accessoarer': { id: '5204', l3: {}}
148            }
149        },
150        'Föräldrar och barn': {
151            id: '68',
152            subs: {
153                'Leksaker': { id: '3912', l3: { 'LEGO och DUPLO': '9775', 'Byggsatser och klossar': '333', 'Gosedjur, dockor och figurer': '331', 'Leksaksbilar och banor': '332', 'Uteleksaker': '335', 'Babyleksaker': '330', 'Måla och pyssla': '334', 'Övriga leksaker': '336' }},
154                'Barnkläder': { id: '3913', l3: { 'Ytterkläder': '8356', 'Tröjor och stickat': '8354', 'Byxor och shorts': '8352', 'T-shirts och toppar': '8355', 'Kjolar och klänningar': '8357', 'Sovkläder': '8353', 'Bodies och sparkdräkter': '8351', 'Mössor och vantar': '8350', 'Övriga barnkläder': '8358' }},
155                'Barnvagnar': { id: '3914', l3: { 'Liggvagnar': '9460', 'Kombi- och duovagnar': '9461', 'Sittvagnar och sulkys': '9463', 'Joggingvagnar': '9465', 'Resevagnar': '9464', 'Barnvagnstillbehör': '9466' }},
156                'Barnmöbler': { id: '3916', l3: { 'Barnsängar': '312', 'Barnstolar': '313', 'Barnbord': '314', 'Skötbord': '316', 'Förvaring': '315', 'Övriga barnmöbler': '317' }},
157                'Barnskor': { id: '3915', l3: { 'Stövlar': '9449', 'Sneakers': '9458', 'Sandaler': '9453', 'Gummistövlar': '9450', 'Vinterskor': '9451', 'Övriga barnskor': '9459' }},
158                'Bil- och cykelstolar': { id: '3911', l3: { 'Bilbarnstolar och babyskydd': '9446', 'Cykelstolar': '9447' }},
159                'Barnböcker': { id: '8369', l3: {}},
160                'Övrigt för Föräldrar och barn': { id: '3970', l3: {}}
161            }
162        },
163        'Bygg och trädgård': {
164            id: '67',
165            subs: {
166                'Trädgård och utemiljö': { id: '3901', l3: { 'Utemöbler': '142', 'Grillar': '147', 'Pooler och spabad': '150', 'Gräsklippare': '149', 'Trädgårdsredskap': '143', 'Partytält': '144', 'Markiser och parasoller': '146', 'Växter och tillbehör': '339', 'Snöröjning': '148', 'Övrig trädgårdsutrustning': '145' }},
167                'Verktyg': { id: '5219', l3: {}},
168                'Byggmaterial och renovering': { id: '3899', l3: { 'Golv': '167', 'Dörrar': '169', 'Fönster': '168', 'Tapeter och färg': '301', 'Byggsatser': '166', 'Trappor': '346', 'Övrigt byggmaterial': '165' }},
169                'Badrum': { id: '7749', l3: { 'Badkar': '157', 'Duschkabiner och -väggar': '160', 'Handfat': '162', 'Toaletter': '161', 'Blandare': '156', 'Badrumsmöbler': '164', 'Badrumstillbehör': '163', 'Bastu': '158', 'Ångduschar': '159' }},
170                'Värme och ventilation': { id: '5218', l3: { 'Radiatorer': '151', 'Värmepumpar': '154', 'Element': '155', 'Eldstäder': '152', 'Luftkonditionering och fläktar': '297', 'Ved och bränsle': '153' }},
171                'Larm och säkerhet': { id: '8347', l3: {}},
172                'Övrigt inom Trädgård och renovering': { id: '3969', l3: {}}
173            }
174        },
175        'Djur och tillbehör': {
176            id: '77',
177            subs: {
178                'Hundtillbehör': { id: '5193', l3: {}},
179                'Kattillbehör': { id: '5194', l3: {}},
180                'Häst- och ridutrustning': { id: '5195', l3: { 'Sadlar': '9435', 'Ridkläder och utrustning': '9436', 'Hästutrustning': '9437', 'Övrig utrustning': '9438' }},
181                'Övriga djurtillbehör': { id: '5185', l3: {}}
182            }
183        },
184        'Konst och antikt': {
185            id: '76',
186            subs: {
187                'Konst': { id: '5177', l3: { 'Oljemålning': '390', 'Akvarell': '386', 'Akryl': '385', 'Fotografi': '388', 'Teckning': '392', 'Skulptur': '391', 'Tryck': '394', 'Litografi': '389', 'Övrig konst': '395' }},
188                'Keramik, porslin och glas': { id: '5176', l3: {}},
189                'Antika möbler': { id: '5178', l3: {}},
190                'Silverföremål och -bestick': { id: '5179', l3: {}},
191                'Övriga antikviteter': { id: '5175', l3: {}}
192            }
193        },
194        'Affärsverksamhet': {
195            id: '91',
196            subs: {
197                'Kontorsutrustning och inredning': { id: '3105', l3: { 'Kontorsmöbler': '294', 'Skrivare': '243', 'Kopiatorer': '244', 'Kontorsmaterial': '242' }},
198                'Storkök och restaurang': { id: '3103', l3: { 'Köksutrustning': '351', 'Kyl och frys': '352', 'Diskutrustning': '354', 'Matlagning och stekning': '353', 'Inredning': '355', 'Övrig köksutrustning': '356' }},
199                'Övrigt inom Affärsverksamhet': { id: '3104', l3: {}}
200            }
201        }
202    };
203
204    // ==================== KATEGORI-NYCKELORD (för automatisk matchning) ====================
205    // Varje post: [nyckelord] → { huvud, sub, l3 (valfri) }
206    var NYCKELORD_MATCHNING = [
207        // Elektronik – Datorer
208        { ord: ['laptop', 'macbook', 'notebook', 'bärbar dator', 'chromebook', 'thinkpad', 'lenovo', 'dell xps', 'hp pavilion'], huvud: 'Elektronik och vitvaror', sub: 'Datorer', l3: 'Laptops' },
209        { ord: ['stationär dator', 'gaming pc', 'imac', 'mac mini', 'mac pro', 'desktop'], huvud: 'Elektronik och vitvaror', sub: 'Datorer', l3: 'Stationär dator' },
210        { ord: ['surfplatta', 'ipad', 'tablet', 'android-platta'], huvud: 'Elektronik och vitvaror', sub: 'Datorer', l3: 'Surfplattor och läsplattor' },
211        { ord: ['bildskärm', 'monitor', 'datorskärm', 'skärm'], huvud: 'Elektronik och vitvaror', sub: 'Datorer', l3: 'Datorskärmar' },
212        { ord: ['tangentbord', 'mus', 'datormus', 'webbkamera', 'webcam', 'usb-hubb', 'datortillbehör'], huvud: 'Elektronik och vitvaror', sub: 'Datorer', l3: 'Datortillbehör' },
213        { ord: ['ssd', 'hårddisk', 'hdd', 'nvme', 'lagring'], huvud: 'Elektronik och vitvaror', sub: 'Datorer', l3: 'Hårddiskar och lagring' },
214        { ord: ['router', 'switch', 'mesh', 'wifi', 'nätverksutrustning', 'nätverk'], huvud: 'Elektronik och vitvaror', sub: 'Datorer', l3: 'Nätverk' },
215        { ord: ['grafikkort', 'gpu', 'processor', 'cpu', 'ram', 'moderkort', 'datorkomponent'], huvud: 'Elektronik och vitvaror', sub: 'Datorer', l3: 'Datorkomponenter' },
216        // Elektronik – Foto och video
217        { ord: ['systemkamera', 'dslr', 'mirrorless', 'spegelreflexkamera', 'canon eos', 'nikon d', 'sony alpha', 'fujifilm'], huvud: 'Elektronik och vitvaror', sub: 'Foto och video', l3: 'Systemkameror' },
218        { ord: ['kompaktkamera', 'digitalkamera', 'digi-kamera'], huvud: 'Elektronik och vitvaror', sub: 'Foto och video', l3: 'Kompaktkameror' },
219        { ord: ['objektiv', 'lins', 'zoom-lins', 'teleobjektiv'], huvud: 'Elektronik och vitvaror', sub: 'Foto och video', l3: 'Objektiv' },
220        { ord: ['videokamera', 'actionkamera', 'gopro', 'filmkamera'], huvud: 'Elektronik och vitvaror', sub: 'Foto och video', l3: 'Videokameror' },
221        { ord: ['kameraväska', 'kameraremmar', 'stativ', 'fotostativ'], huvud: 'Elektronik och vitvaror', sub: 'Foto och video', l3: 'Övrig fotoutrustning' },
222        // Elektronik – Telefoner
223        { ord: ['iphone', 'samsung galaxy', 'pixel', 'mobiltelefon', 'smartphone', 'oneplus', 'xiaomi', 'huawei', 'mobil'], huvud: 'Elektronik och vitvaror', sub: 'Telefoner och tillbehör', l3: 'Mobiltelefoner' },
224        { ord: ['mobilskal', 'mobilladdare', 'skärmskydd', 'mobiltillbehör', 'airpods', 'earbuds'], huvud: 'Elektronik och vitvaror', sub: 'Telefoner och tillbehör', l3: 'Mobiltillbehör' },
225        // Elektronik – Ljud & Bild
226        { ord: ['tv', 'oled', 'qled', 'televisioner', '4k tv', '8k tv', 'smart tv', 'samsung tv', 'lg tv', 'sony tv'], huvud: 'Elektronik och vitvaror', sub: 'Ljud och bild', l3: 'TV' },
227        { ord: ['högtalare', 'speaker', 'soundbar', 'subwoofer', 'bluetooth-högtalare'], huvud: 'Elektronik och vitvaror', sub: 'Ljud och bild', l3: 'Högtalare' },
228        { ord: ['hörlurar', 'headset', 'in-ear', 'over-ear', 'bose', 'sony wh', 'sennheiser'], huvud: 'Elektronik och vitvaror', sub: 'Ljud och bild', l3: 'Hörlurar' },
229        { ord: ['förstärkare', 'receiver', 'av-receiver', 'stereoförstärkare', 'amplifier'], huvud: 'Elektronik och vitvaror', sub: 'Ljud och bild', l3: 'Förstärkare och receivers' },
230        { ord: ['projektor', 'hemmabio-projektor', 'beamer'], huvud: 'Elektronik och vitvaror', sub: 'Ljud och bild', l3: 'Projektorer och projektordukar' },
231        { ord: ['apple tv', 'chromecast', 'streaming', 'digitalbox', 'tv-box', 'firestick'], huvud: 'Elektronik och vitvaror', sub: 'Ljud och bild', l3: 'Streaming och digitalboxar' },
232        // Elektronik – Spel
233        { ord: ['playstation', 'ps4', 'ps5', 'xbox', 'nintendo switch', 'spelkonsol', 'konsol', 'steam deck'], huvud: 'Elektronik och vitvaror', sub: 'TV-spel och spelkonsoler', l3: 'Spelkonsoler' },
234        { ord: ['tv-spel', 'datorspel', 'videospel', 'playstation-spel', 'xbox-spel', 'nintendo-spel'], huvud: 'Elektronik och vitvaror', sub: 'TV-spel och spelkonsoler', l3: 'Spel' },
235        { ord: ['spelkontroll', 'gaming-headset', 'gamingtillbehör', 'spelmus', 'mekaniskt tangentbord'], huvud: 'Elektronik och vitvaror', sub: 'TV-spel och spelkonsoler', l3: 'Gamingtillbehör' },
236        // Elektronik – Vitvaror
237        { ord: ['tvättmaskin', 'tvätt', 'washer'], huvud: 'Elektronik och vitvaror', sub: 'Vitvaror', l3: 'Tvättmaskiner' },
238        { ord: ['kylskåp', 'kyl', 'sidobyside', 'frys', 'frysbox', 'frysskåp'], huvud: 'Elektronik och vitvaror', sub: 'Vitvaror', l3: 'Kylskåp' },
239        { ord: ['spis', 'spishäll', 'induktion', 'ugn', 'inbyggnadsugn', 'kokplatta'], huvud: 'Elektronik och vitvaror', sub: 'Vitvaror', l3: 'Spisar' },
240        { ord: ['diskmaskin', 'diskare'], huvud: 'Elektronik och vitvaror', sub: 'Vitvaror', l3: 'Diskmaskiner' },
241        { ord: ['torktumlare', 'tork-maskin'], huvud: 'Elektronik och vitvaror', sub: 'Vitvaror', l3: 'Torktumlare' },
242        { ord: ['kaffebryggare', 'espressomaskin', 'kaffemaskin', 'nespresso', 'dolce gusto', 'jura', 'de\'longhi'], huvud: 'Elektronik och vitvaror', sub: 'Hushållsapparater', l3: 'Kaffebryggare och -maskiner' },
243        { ord: ['dammsugare', 'robotdammsugare', 'roomba', 'dyson', 'miele dammsugare'], huvud: 'Elektronik och vitvaror', sub: 'Hushållsapparater', l3: 'Dammsugare' },
244        // Möbler
245        { ord: ['soffa', 'hörnsoffa', 'bäddsoffa', 'divansoffa', 'fåtölj', 'soffgrupp'], huvud: 'Möbler och inredning', sub: 'Soffor och fåtöljer' },
246        { ord: ['matbord', 'matgrupp', 'middagsbord', 'köksbord', 'soffbord', 'sidobord'], huvud: 'Möbler och inredning', sub: 'Bord och stolar' },
247        { ord: ['stol', 'köksstol', 'matstol', 'pinnstol', 'kontorsstol', 'skrivbordsstol'], huvud: 'Möbler och inredning', sub: 'Bord och stolar' },
248        { ord: ['skrivbord', 'arbetsbord', 'hörnskrivbord'], huvud: 'Möbler och inredning', sub: 'Bord och stolar', l3: 'Skrivbord' },
249        { ord: ['säng', 'dubbelsäng', 'enkelsäng', 'sängstomme', 'sänggavel', 'sängram'], huvud: 'Möbler och inredning', sub: 'Sängar och madrasser', l3: 'Sängar' },
250        { ord: ['madrass', 'springmadrass', 'latexmadrass', 'tempur', 'boxspring'], huvud: 'Möbler och inredning', sub: 'Sängar och madrasser', l3: 'Madrasser' },
251        { ord: ['garderob', 'klädskåp', 'wardobe', 'pax', 'ikea garderob'], huvud: 'Möbler och inredning', sub: 'Garderober och förvaring' },
252        { ord: ['hylla', 'bokhylla', 'hyllor', 'kallax', 'lack hylla', 'expedit'], huvud: 'Möbler och inredning', sub: 'Hyllor och byråer', l3: 'Hyllor' },
253        { ord: ['byrå', 'kommod', 'nattyg', 'sängbord', 'sideboard'], huvud: 'Möbler och inredning', sub: 'Hyllor och byråer' },
254        { ord: ['tv-möbel', 'tv-bänk', 'tv-skåp', 'mediastöd', 'tv-ställ'], huvud: 'Möbler och inredning', sub: 'Hyllor och byråer', l3: 'TV- och mediamöbler' },
255        { ord: ['lampa', 'taklampa', 'golvlampa', 'bordslampa', 'vägglampa', 'pendel', 'ljuskrona'], huvud: 'Möbler och inredning', sub: 'Lampor' },
256        { ord: ['matta', 'ullmatta', 'plastmatta', 'gångmatta', 'persisk matta'], huvud: 'Möbler och inredning', sub: 'Mattor och textilier', l3: 'Mattor' },
257        { ord: ['gardiner', 'gardin', 'draperier', 'rullgardin', 'persienn'], huvud: 'Möbler och inredning', sub: 'Mattor och textilier', l3: 'Gardiner' },
258        { ord: ['tavla', 'poster', 'print', 'spegel', 'vas', 'dekoration', 'prydnad'], huvud: 'Möbler och inredning', sub: 'Dekoration och prydnader' },
259        { ord: ['porslin', 'tallrik', 'servis', 'kopp', 'mugg', 'glas', 'bestick', 'köksredskap'], huvud: 'Möbler och inredning', sub: 'Köksutrustning och porslin' },
260        // Sport
261        { ord: ['cykel', 'mountainbike', 'mtb', 'racercykel', 'hybridcykel', 'elcykel', 'fatbike', 'trek', 'specialized'], huvud: 'Sport och friluftsliv', sub: 'Cykling', l3: 'Cyklar' },
262        { ord: ['löpband', 'crosstrainer', 'roddmaskin', 'motionscykel', 'hemmagym', 'träningsmaskin', 'styrketräning'], huvud: 'Sport och friluftsliv', sub: 'Träningsmaskiner och -redskap' },
263        { ord: ['skidor', 'alpint', 'slalom', 'längdskidor', 'skidutrustning', 'snowboard', 'pjäxor'], huvud: 'Sport och friluftsliv', sub: 'Vintersport' },
264        { ord: ['tennis', 'tennisracket', 'padel', 'padelracket', 'squash', 'badminton', 'fotboll', 'handboll'], huvud: 'Sport och friluftsliv', sub: 'Bollsporter' },
265        { ord: ['fiskeutrustning', 'fiskespö', 'fiskerulle', 'flugfiske', 'camping', 'tält', 'sovsäck', 'ryggsäck', 'vandring'], huvud: 'Sport och friluftsliv', sub: 'Jakt, fiske och camping' },
266        { ord: ['golf', 'golfklubbor', 'golfset', 'golfbag'], huvud: 'Sport och friluftsliv', sub: 'Golf' },
267        { ord: ['klättring', 'klätterutrustning', 'klättersko', 'rep', 'säkringsenhet'], huvud: 'Sport och friluftsliv', sub: 'Extremsport', l3: 'Klättring' },
268        { ord: ['skateboard', 'longboard', 'skateboarddäck'], huvud: 'Sport och friluftsliv', sub: 'Extremsport', l3: 'Skateboard och longboard' },
269        { ord: ['kajak', 'kanot', 'sup', 'paddelbräda', 'surfbräda', 'segling', 'dykutrustning'], huvud: 'Sport och friluftsliv', sub: 'Vattensport' },
270        // Musikinstrument
271        { ord: ['gitarr', 'elgitarr', 'akustisk gitarr', 'fender', 'gibson', 'martin gitarr', 'taylor gitarr', 'yamaha gitarr'], huvud: 'Underhållning och hobby', sub: 'Musikinstrument', l3: 'Gitarrer' },
272        { ord: ['basgitarr', 'elbas', 'bas'], huvud: 'Underhållning och hobby', sub: 'Musikinstrument', l3: 'Basgitarrer' },
273        { ord: ['piano', 'flygel', 'digitalpiano', 'steinway', 'yamaha piano'], huvud: 'Underhållning och hobby', sub: 'Musikinstrument', l3: 'Pianon och flyglar' },
274        { ord: ['keyboard', 'synthesizer', 'synthar', 'midi', 'roland', 'korg', 'nord'], huvud: 'Underhållning och hobby', sub: 'Musikinstrument', l3: 'Keyboards och synthar' },
275        { ord: ['trummor', 'trumset', 'cymbal', 'slagverk', 'elektronisk trumma', 'cajon', 'pearl trummor'], huvud: 'Underhållning och hobby', sub: 'Musikinstrument', l3: 'Slagverk och trummor' },
276        { ord: ['trumpet', 'trombon', 'tuba', 'valthorn', 'sackbut', 'bleckblås'], huvud: 'Underhållning och hobby', sub: 'Musikinstrument', l3: 'Bleckblåsinstrument' },
277        { ord: ['violin', 'viola', 'cello', 'kontrabas', 'fiol', 'stråkinstrument'], huvud: 'Underhållning och hobby', sub: 'Musikinstrument', l3: 'Stråkinstrument' },
278        { ord: ['flöjt', 'oboe', 'klarinett', 'saxofon', 'fagott', 'träblås'], huvud: 'Underhållning och hobby', sub: 'Musikinstrument', l3: 'Träblåsinstrument' },
279        { ord: ['pa-utrustning', 'mixing', 'ljudbord', 'mikrofon', 'mixer', 'effektpedal', 'förstärkare gitarr', 'marshall', 'fender amp'], huvud: 'Underhållning och hobby', sub: 'Musikinstrument', l3: 'Ljudutrustning' },
280        // Hobby
281        { ord: ['brädspel', 'sällskapsspel', 'kortspel', 'pussel', 'dungeons', 'warhammer', 'chess'], huvud: 'Underhållning och hobby', sub: 'Sällskapsspel och pussel' },
282        { ord: ['bok', 'böcker', 'roman', 'kurslitteratur', 'faktabok', 'serietidning', 'pocket'], huvud: 'Underhållning och hobby', sub: 'Böcker och tidningar' },
283        { ord: ['vinyl', 'skiva', 'lp', 'singel', 'skivor', 'cd', 'dvd', 'blu-ray', 'film', 'serie dvd'], huvud: 'Underhållning och hobby', sub: 'Musik och film' },
284        { ord: ['drönare', 'drone', 'dji', 'rc-bil', 'radiostyrda', 'modellbil'], huvud: 'Underhållning och hobby', sub: 'Radiostyrda enheter' },
285        { ord: ['symaskin', 'garn', 'tyg', 'stickning', 'virkning', 'hantverk', 'pärlor'], huvud: 'Underhållning och hobby', sub: 'Hantverk' },
286        { ord: ['samlarobjekt', 'mynt', 'frimärken', 'samlarkort', 'pokemon', 'magic kortspel'], huvud: 'Underhållning och hobby', sub: 'Samlarobjekt' },
287        // Kläder
288        { ord: ['damkläder', 'damjacka', 'dambyxor', 'klänning', 'kjol', 'blus', 'dam-', 'kvinst'], huvud: 'Kläder, kosmetika och accessoarer', sub: 'Damkläder' },
289        { ord: ['herrkläder', 'herrjacka', 'herrbyxor', 'kostym', 'skjorta', 'herr-'], huvud: 'Kläder, kosmetika och accessoarer', sub: 'Herrkläder' },
290        { ord: ['skor', 'sneakers', 'stövlar', 'sandaler', 'klackskor', 'nike', 'adidas', 'new balance', 'converse'], huvud: 'Kläder, kosmetika och accessoarer', sub: 'Skor' },
291        { ord: ['handväska', 'ryggsäck', 'väska', 'axelväska', 'resväska', 'portfölj'], huvud: 'Kläder, kosmetika och accessoarer', sub: 'Väskor och plånböcker' },
292        { ord: ['halsband', 'ring', 'armband', 'örhängen', 'smycken', 'guld', 'silver', 'diamant'], huvud: 'Kläder, kosmetika och accessoarer', sub: 'Smycken' },
293        { ord: ['klocka', 'armbandsur', 'rolex', 'omega', 'casio', 'seiko', 'tissot', 'garmin klocka'], huvud: 'Kläder, kosmetika och accessoarer', sub: 'Klockor och armbandsur' },
294        { ord: ['parfym', 'doft', 'eau de toilette', 'eau de parfum', 'smink', 'makeup', 'foundation', 'läppstift'], huvud: 'Kläder, kosmetika och accessoarer', sub: 'Kosmetika' },
295        // Barn
296        { ord: ['lego', 'duplo', 'leksak', 'leksaker', 'barbie', 'hot wheels', 'playmobil', 'fisher-price', 'toy'], huvud: 'Föräldrar och barn', sub: 'Leksaker' },
297        { ord: ['barnkläder', 'babykläder', 'barnstorlek', 'strl', 'ytterkläder barn'], huvud: 'Föräldrar och barn', sub: 'Barnkläder' },
298        { ord: ['barnvagn', 'sittvagn', 'liggvagn', 'kombivagn', 'babybjörn', 'stokke', 'bugaboo', 'emmaljunga', 'britax'], huvud: 'Föräldrar och barn', sub: 'Barnvagnar' },
299        { ord: ['bilbarnstol', 'babyskydd', 'cykelstol barn', 'maxi-cosi'], huvud: 'Föräldrar och barn', sub: 'Bil- och cykelstolar' },
300        { ord: ['barnsäng', 'babysäng', 'spjälsäng', 'barnmöbel', 'barnstol', 'skötbord'], huvud: 'Föräldrar och barn', sub: 'Barnmöbler' },
301        // Bygg & trädgård
302        { ord: ['grill', 'weber', 'gasgrill', 'klotgrill', 'utemöbler', 'trädgårdsmöbler', 'parasoll', 'trädgårdsredskap'], huvud: 'Bygg och trädgård', sub: 'Trädgård och utemiljö' },
303        { ord: ['pool', 'spabad', 'jacuzzi', 'trampolin'], huvud: 'Bygg och trädgård', sub: 'Trädgård och utemiljö', l3: 'Pooler och spabad' },
304        { ord: ['gräsklippare', 'robotgräsklippare', 'husqvarna', 'stiga', 'åkgräsklippare'], huvud: 'Bygg och trädgård', sub: 'Trädgård och utemiljö', l3: 'Gräsklippare' },
305        { ord: ['borrmaskiner', 'cirkelsåg', 'tigersåg', 'elverktyg', 'bosch verktyg', 'makita', 'dewalt', 'skiftnyckel', 'handverktyg'], huvud: 'Bygg och trädgård', sub: 'Verktyg' },
306        { ord: ['badkar', 'dusch', 'handfat', 'toalett', 'wc', 'badrumsmöbler'], huvud: 'Bygg och trädgård', sub: 'Badrum' },
307        { ord: ['värmepump', 'luft/luft', 'luft/vatten', 'bergvärme', 'element', 'braskamin', 'pelletskamin', 'eldstad'], huvud: 'Bygg och trädgård', sub: 'Värme och ventilation' },
308        // Konst
309        { ord: ['tavla', 'oljemålning', 'konstverk', 'akvarell', 'skulptur', 'litografi', 'print', 'konst', 'akryl'], huvud: 'Konst och antikt', sub: 'Konst' },
310        { ord: ['antikt', 'antika möbler', 'porslin', 'keramik', 'kristall', 'servis antik'], huvud: 'Konst och antikt', sub: 'Keramik, porslin och glas' },
311        { ord: ['silver', 'silverbestick', 'silverföremål'], huvud: 'Konst och antikt', sub: 'Silverföremål och -bestick' },
312        // Djur
313        { ord: ['hundkoppel', 'hundhalsband', 'hundbädd', 'hundmat', 'hundtillbehör'], huvud: 'Djur och tillbehör', sub: 'Hundtillbehör' },
314        { ord: ['kattbädd', 'kattlåda', 'kattmat', 'kattleksak', 'kattskrapa'], huvud: 'Djur och tillbehör', sub: 'Kattillbehör' },
315        { ord: ['sadel', 'ridutrustning', 'ridkläder', 'hästutrustning'], huvud: 'Djur och tillbehör', sub: 'Häst- och ridutrustning' },
316    ];
317
318    // ==================== SMART KATEGORI-MATCHARE ====================
319    function hittaBästaGratisKategori(rubrik, beskrivning) {
320        var text = ((rubrik || '') + ' ' + (beskrivning || '')).toLowerCase();
321        var bästPoäng = 0;
322        var bästaMatch = null;
323
324        NYCKELORD_MATCHNING.forEach(function(post) {
325            var poäng = 0;
326            post.ord.forEach(function(nyckelord) {
327                if (text.indexOf(nyckelord.toLowerCase()) !== -1) {
328                    // Längre nyckelord = mer specifik match = högre poäng
329                    poäng += nyckelord.length > 10 ? 3 : nyckelord.length > 5 ? 2 : 1;
330                }
331            });
332            if (poäng > bästPoäng) {
333                bästPoäng = poäng;
334                bästaMatch = post;
335            }
336        });
337
338        if (!bästaMatch) {
339            // Standardkategori om inget matchas
340            return { huvud: 'Elektronik och vitvaror', sub: 'Övrig elektronik', l3: null };
341        }
342
343        var huvudKat = KATEGORIER[bästaMatch.huvud];
344        var subKat = huvudKat ? huvudKat.subs[bästaMatch.sub] : null;
345        var l3Id = null;
346        if (subKat && bästaMatch.l3 && subKat.l3[bästaMatch.l3]) {
347            l3Id = subKat.l3[bästaMatch.l3];
348        }
349
350        return {
351            huvud: bästaMatch.huvud,
352            huvudId: huvudKat ? huvudKat.id : null,
353            sub: bästaMatch.sub,
354            subId: subKat ? subKat.id : null,
355            l3: bästaMatch.l3 || null,
356            l3Id: l3Id,
357            poäng: bästPoäng
358        };
359    }
360
361    // ==================== HJÄLPFUNKTIONER ====================
362    function qs(sel, rot) { try { return (rot || document).querySelector(sel); } catch(e) { return null; } }
363    function qsa(sel, rot) { try { return Array.from((rot || document).querySelectorAll(sel)); } catch(e) { return []; } }
364
365    function skuggRot() {
366        var podium = document.querySelector('podium-layout');
367        return podium && podium.shadowRoot ? podium.shadowRoot : null;
368    }
369    function sqs(sel) { var sr = skuggRot(); return (sr && sr.querySelector(sel)) || qs(sel); }
370
371    function logg() {
372        if (!config.debugLäge) return;
373        console.log.apply(console, ['[BTM]'].concat(Array.from(arguments)));
374    }
375    function fel() {
376        console.error.apply(console, ['[BTM FEL]'].concat(Array.from(arguments)));
377    }
378
379    function detekteraSajt() {
380        var h = location.hostname;
381        if (h.includes('blocket.se')) return 'blocket';
382        if (h.includes('facebook.com')) return 'facebook';
383        return '';
384    }
385
386    function sparaAnnonser(annonser) {
387        try { GM_setValue(config.lagringsNyckel, JSON.stringify(annonser)); } catch(e) { fel('sparaAnnonser:', e); }
388    }
389    function laddaAnnonser() {
390        try { return JSON.parse(GM_getValue(config.lagringsNyckel, '[]')); } catch(e) { return []; }
391    }
392
393    function rensaPris(text) {
394        if (!text) return '';
395        var m = text.replace(/\s/g,'').match(/\d+/g);
396        return m ? m.join('') : '';
397    }
398    function rensaBeskrivning(text) {
399        if (!text) return '';
400        return text.replace(/\s+/g,' ').trim();
401    }
402
403    function sättReaktVärde(el, värde) {
404        var setter = Object.getOwnPropertyDescriptor(
405            el.tagName === 'TEXTAREA' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype,
406            'value'
407        ).set;
408        setter.call(el, värde);
409        el.dispatchEvent(new Event('input', { bubbles: true }));
410        el.dispatchEvent(new Event('change', { bubbles: true }));
411    }
412
413    function sättSelectVärde(sel, val) {
414        sel.value = val;
415        sel.dispatchEvent(new Event('input', { bubbles: true }));
416        sel.dispatchEvent(new Event('change', { bubbles: true }));
417    }
418
419    function visaNotis(meddelande, typ) {
420        var gammal = qs('.btm-notis');
421        if (gammal) gammal.remove();
422        var n = document.createElement('div');
423        n.className = 'btm-notis';
424        n.textContent = meddelande;
425        if (typ === 'fel') n.style.background = '#c62828';
426        else if (typ === 'ok') n.style.background = '#2e7d32';
427        document.body.appendChild(n);
428        setTimeout(function(){ if (n.parentNode) n.remove(); }, 3500);
429    }
430
431    // ==================== BLOCKET SKRAPARE ====================
432    function skrapaAnnonsLista() {
433        var länkElem = qsa('a[href*="/my-items/details/"]');
434        var annonser = [];
435        länkElem.forEach(function(a) {
436            var kontainer = a.closest('[class*="mb-16"]') || a.closest('div');
437            if (!kontainer) return;
438            var idMatch = a.href.match(/details\/(\d+)/);
439            if (!idMatch) return;
440            var id = idMatch[1];
441            var prisEl = kontainer.querySelector('.text-s.truncate');
442            var bildDiv = kontainer.querySelector('div[style*="background-image"]');
443            var miniatyrbild = '';
444            if (bildDiv) {
445                var m = bildDiv.style.backgroundImage.match(/url\(["']?(.+?)["']?\)/);
446                if (m) miniatyrbild = m[1];
447            }
448            annonser.push({
449                id: id,
450                rubrik: a.textContent.trim(),
451                pris: rensaPris(prisEl ? prisEl.textContent : ''),
452                status: (kontainer.querySelector('[class*="badge-positive"]') || {}).textContent || '',
453                miniatyrbild: miniatyrbild,
454                offentligUrl: 'https://www.blocket.se/recommerce/forsale/item/' + id,
455                skrapadVid: new Date().toISOString(),
456                fullData: false,
457                lagtUppFB: false
458            });
459        });
460        return annonser;
461    }
462
463    function skrapaAnnonsDetalj() {
464        var sr = skuggRot();
465        var rot = sr || document;
466        var annons = {};
467
468        var h1 = rot.querySelector('h1');
469        annons.rubrik = h1 ? h1.textContent.trim() : '';
470
471        // Pris
472        var prisText = '';
473        var main = rot.querySelector('main');
474        if (main) {
475            var walker = document.createTreeWalker(main, NodeFilter.SHOW_TEXT, null, false);
476            var nod;
477            while (nod = walker.nextNode()) {
478                var t = nod.textContent.trim();
479                if (/^\d[\d\s]*kr$/.test(t)) { prisText = t; break; }
480            }
481        }
482        annons.pris = rensaPris(prisText);
483
484        // Skick
485        var skickRegion = rot.querySelector('[aria-label="Nyckelinfo"]');
486        if (skickRegion) {
487            annons.skick = skickRegion.textContent.trim().replace(/^Skick\s*:?\s*/, '').trim();
488        }
489
490        // Beskrivning
491        var beskrRegion = rot.querySelector('[aria-label="Om annonsen"]');
492        if (beskrRegion) {
493            var uiArtefakter = ['Plustecken', 'Visa hela beskrivningen', 'Obs: Knappen'];
494            var delar = [];
495            beskrRegion.querySelectorAll('*').forEach(function(el) {
496                if (el.children.length === 0 && el.textContent.trim()) {
497                    var t = el.textContent.trim();
498                    var ärArtefakt = uiArtefakter.some(function(a) { return t.indexOf(a) !== -1; });
499                    if (!ärArtefakt && !el.closest('button')) delar.push(t);
500                }
501            });
502            annons.beskrivning = delar.length ? delar.join('\n') : rensaBeskrivning(beskrRegion.textContent);
503        }
504
505        // Bilder
506        annons.bilder = [];
507        rot.querySelectorAll('img').forEach(function(img) {
508            var src = img.src || '';
509            if (src.includes('blocketcdn.se') && !src.includes('placeholder') && !src.includes('profile')) {
510                var hög = src.replace(/\/dynamic\/\d+[wx][^/]*\//, '/dynamic/1600w/');
511                if (annons.bilder.indexOf(hög) === -1) annons.bilder.push(hög);
512            }
513        });
514
515        // Plats
516        var platsLänk = rot.querySelector('a[href*="/map?adId"]');
517        if (platsLänk) annons.plats = platsLänk.textContent.replace(/^Karta/, '').trim();
518
519        // Kategorier (brödsmulor)
520        annons.kategorier = [];
521        var nav = rot.querySelector('nav');
522        if (nav) {
523            nav.querySelectorAll('a').forEach(function(a) {
524                var t = a.textContent.trim();
525                if (t && t !== 'Mitt Blocket' && t !== 'Mina annonser') annons.kategorier.push(t);
526            });
527        }
528
529        // Annons-ID
530        var idMatch = location.pathname.match(/\/(\d+)$/);
531        annons.id = idMatch ? idMatch[1] : '';
532
533        // Auto-matcha gratis kategori
534        annons.blocketKategori = hittaBästaGratisKategori(annons.rubrik, annons.beskrivning);
535
536        annons.skrapadVid = new Date().toISOString();
537        annons.fullData = true;
538        annons.lagtUppFB = false;
539
540        logg('Skrapade annons:', annons);
541        return annons;
542    }
543
544    // ==================== BLOCKET AUTO-POSTER ====================
545    function öppnaSkapa() {
546        // Navigera till skapa-sidan om vi inte redan är där
547        if (!location.pathname.includes('/create-item') && !location.pathname.includes('/recommerce/create')) {
548            window.location.href = 'https://www.blocket.se/create-item/start';
549        }
550    }
551
552    async function fyllIBlocketFormulär(annons) {
553        if (!annons) { visaNotis('Ingen annons vald', 'fel'); return; }
554        if (!location.pathname.includes('/recommerce/create')) {
555            visaNotis('Navigera till Ny annons → Torget först', 'fel');
556            return;
557        }
558
559        var kategori = annons.blocketKategori || hittaBästaGratisKategori(annons.rubrik, annons.beskrivning);
560        logg('Fyller i formulär med kategori:', kategori);
561
562        var sleep = function(ms) { return new Promise(function(r){ setTimeout(r, ms); }); };
563
564        try {
565            // 1. Välj transaktionstyp (Sälj är standard, men bekräfta)
566            var säljRadio = qs('input[type="radio"][value="FOR_SALE"], input[type="radio"]');
567            if (säljRadio) säljRadio.click();
568
569            // 2. Välj huvudkategori
570            var sels = document.querySelectorAll('select');
571            if (sels[0] && kategori.huvudId) {
572                sättSelectVärde(sels[0], kategori.huvudId);
573                visaNotis('Sätter kategori: ' + kategori.huvud + '…');
574                await sleep(500);
575            }
576
577            // 3. Välj underkategori
578            sels = document.querySelectorAll('select');
579            if (sels[1] && kategori.subId) {
580                sättSelectVärde(sels[1], kategori.subId);
581                await sleep(400);
582            }
583
584            // 4. Välj nivå-3 om den finns
585            sels = document.querySelectorAll('select');
586            if (sels[2] && kategori.l3Id) {
587                sättSelectVärde(sels[2], kategori.l3Id);
588                await sleep(300);
589            }
590
591            // 5. Rubrik
592            var rubrikInput = qs('input[placeholder*="rubrik"], input[aria-label*="rubrik"], input[aria-label*="Rubrik"]');
593            if (!rubrikInput) {
594                var allInputs = qsa('input[type="text"], input:not([type])');
595                rubrikInput = allInputs.find(function(i) { return !i.value && i.offsetParent; });
596            }
597            if (rubrikInput && annons.rubrik) {
598                sättReaktVärde(rubrikInput, annons.rubrik.substring(0, 100));
599                await sleep(200);
600            }
601
602            // 6. Beskrivning
603            var beskrInput = qs('textarea');
604            if (beskrInput && annons.beskrivning) {
605                sättReaktVärde(beskrInput, annons.beskrivning.substring(0, 4000));
606                await sleep(200);
607            }
608
609            // 7. Pris
610            var prisInput = qs('input[type="number"][aria-label*="ris"], input[id*="price"], input[id*="pris"]');
611            if (!prisInput) prisInput = qs('input[type="number"]');
612            if (prisInput && annons.pris) {
613                sättReaktVärde(prisInput, annons.pris);
614                await sleep(200);
615            }
616
617            visaNotis('✓ Formulär ifyllt! Kategori: ' + kategori.huvud + ' → ' + kategori.sub + (kategori.l3 ? ' → ' + kategori.l3 : '') + '. Lägg till bilder manuellt.', 'ok');
618
619        } catch(e) {
620            fel('fyllIBlocketFormulär:', e);
621            visaNotis('Fel vid ifyllning: ' + e.message, 'fel');
622        }
623    }
624
625    // Ladda ner bild från Blocket CDN
626    function laddaNerBild(url) {
627        return new Promise(function(resolve) {
628            GM_xmlhttpRequest({
629                method: 'GET', url: url, responseType: 'blob',
630                onload: function(res) { resolve(res.status === 200 ? res.response : null); },
631                onerror: function() { resolve(null); }
632            });
633        });
634    }
635
636    // ==================== FACEBOOK MARKETPLACE HJÄLP ====================
637    function kartläggSkick(blocketSkick) {
638        if (!blocketSkick) return '';
639        var s = blocketSkick.toLowerCase();
640        if (s.includes('ny') || s.includes('oanvänd') || s.includes('helt ny')) return 'Nytt';
641        if (s.includes('som ny') || s.includes('mycket bra')) return 'Begagnat – som nytt';
642        if (s.includes('bra skick') || s.includes('varsamt')) return 'Begagnat – bra skick';
643        return 'Begagnat – acceptabelt skick';
644    }
645
646    function kartläggFBKategori(blocketKategorier) {
647        if (!blocketKategorier || !blocketKategorier.length) return '';
648        var t = blocketKategorier.join(' ').toLowerCase();
649        if (t.includes('elektronik') || t.includes('dator') || t.includes('video') || t.includes('foto') || t.includes('telefon')) return 'Elektronik';
650        if (t.includes('möbler') || t.includes('inredning') || t.includes('hem') || t.includes('lampa')) return 'Produkter för hemmet';
651        if (t.includes('kläder') || t.includes('skor') || t.includes('mode') || t.includes('accessoar')) return 'Kläder';
652        if (t.includes('sport') || t.includes('cykel') || t.includes('fritid') || t.includes('träning')) return 'Sportutrustning';
653        if (t.includes('musik') || t.includes('instrument')) return 'Musikinstrument';
654        if (t.includes('hobby') || t.includes('spel') || t.includes('samlar')) return 'Hobbyer';
655        if (t.includes('trädgård') || t.includes('bygg') || t.includes('verktyg')) return 'Trädgård och fritid';
656        if (t.includes('barn') || t.includes('familj') || t.includes('leksak')) return 'Familj';
657        if (t.includes('djur')) return 'Husdjurstillbehör';
658        if (t.includes('konst') || t.includes('antik')) return 'Hobbyer';
659        return 'Radannonser';
660    }
661
662    // Hitta FB-formulärfält via label-text (FB använder inte aria-label på inputs)
663    function hittaFBFält(labelText) {
664        var inputs = Array.from(document.querySelectorAll('input[type="text"], textarea'));
665        for (var inp of inputs) {
666            var el = inp;
667            for (var i = 0; i < 8; i++) {
668                el = el.parentElement;
669                if (!el) break;
670                if (el.textContent && el.textContent.trim().startsWith(labelText)) return inp;
671            }
672        }
673        return null;
674    }
675
676    async function fyllIFBFormulär(annons) {
677        if (!annons) { visaNotis('Ingen annons vald', 'fel'); return; }
678        if (!location.pathname.includes('/marketplace/create')) {
679            visaNotis('Gå till Facebook Marketplace → Skapa inlägg → Vara till salu', 'fel');
680            return;
681        }
682
683        visaNotis('Fyller i formuläret…');
684        await sleep(300);
685
686        // ── STEG 1: Titel, Pris ──
687        var textInputs = Array.from(document.querySelectorAll('input[type="text"]'));
688        var rubrikInput = hittaFBFält('Titel') || textInputs[0];
689        var prisInput   = hittaFBFält('Pris')  || textInputs[1];
690
691        if (rubrikInput && annons.rubrik) {
692            rubrikInput.focus();
693            sättReaktVärde(rubrikInput, annons.rubrik);
694            await sleep(200);
695        }
696        if (prisInput && annons.pris) {
697            prisInput.focus();
698            sättReaktVärde(prisInput, String(annons.pris).replace(/\D/g, ''));
699            await sleep(200);
700        }
701
702        // ── BESKRIVNING (textarea, syns direkt eller efter scroll) ──
703        var beskrInput = document.querySelector('textarea');
704        if (beskrInput && annons.beskrivning) {
705            beskrInput.focus();
706            sättReaktVärde(beskrInput, annons.beskrivning);
707            await sleep(200);
708        }
709
710        // ── SKICK – klicka rätt alternativ om det finns ──
711        var fbSkick = kartläggSkick(annons.skick);
712        var skickKnappar = Array.from(document.querySelectorAll('[role="radio"], [role="option"]'));
713        var skickKnapp = skickKnappar.find(function(k) {
714            return k.textContent.trim().toLowerCase().includes(fbSkick.toLowerCase());
715        });
716        if (skickKnapp) { skickKnapp.click(); await sleep(200); }
717
718        // ── SAMMANFATTNING ──
719        var fyllt = [];
720        if (rubrikInput && annons.rubrik) fyllt.push('Titel');
721        if (prisInput && annons.pris) fyllt.push('Pris');
722        if (beskrInput && annons.beskrivning) fyllt.push('Beskrivning');
723        else if (annons.beskrivning) {
724            GM_setClipboard(annons.beskrivning, 'text');
725            fyllt.push('Beskrivning kopierad till urklipp');
726        }
727        if (skickKnapp) fyllt.push('Skick');
728
729        visaNotis('✓ ' + fyllt.join(', ') + ' ifyllt! Ladda upp bilder manuellt.', 'ok');
730    }
731
732    // ==================== CSS ====================
733    function injiceraCSS() {
734        if (document.getElementById('btm-css')) return;
735        var s = document.createElement('style');
736        s.id = 'btm-css';
737        s.textContent = [
738            '#btm-panel{position:fixed!important;top:0!important;right:0!important;left:auto!important;z-index:999999!important;width:400px!important;height:100vh!important;background:#16213e!important;border-left:2px solid #e4002b!important;box-shadow:-4px 0 24px rgba(0,0,0,.5)!important;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif!important;color:#e8e8e8!important;overflow-y:auto!important;overflow-x:hidden!important;transform:translateX(100%)!important;transition:transform .3s cubic-bezier(.4,0,.2,1)!important;margin:0!important;padding:0!important;float:none!important;}',
739            '#btm-panel.btm-öppen{transform:translateX(0)!important;}',
740            '#btm-knapp{position:fixed!important;top:50%!important;right:0!important;left:auto!important;z-index:999998!important;transform:translateY(-50%)!important;width:36px!important;height:80px!important;background:#e4002b!important;border:none!important;border-radius:8px 0 0 8px!important;color:#fff!important;cursor:pointer!important;font-size:16px!important;font-weight:700!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:-2px 2px 8px rgba(0,0,0,.3)!important;transition:right .3s cubic-bezier(.4,0,.2,1),background .2s!important;margin:0!important;padding:0!important;float:none!important;writing-mode:vertical-rl!important;letter-spacing:1px!important;font-size:11px!important;}',
741            '#btm-knapp:hover{background:#ff1744!important;}',
742            '#btm-knapp.btm-förskjuten{right:400px!important;}',
743            '#btm-knapp .btm-badge{position:absolute;top:-6px;right:-6px;width:18px;height:18px;background:#ff9800;border-radius:50%;font-size:10px;color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;writing-mode:horizontal-tb;}',
744            '.btm-rubrik{background:linear-gradient(135deg,#1a1a2e,#16213e);padding:14px 16px;border-bottom:1px solid rgba(255,255,255,.1);}',
745            '.btm-rubrik h2{margin:0 0 3px;font-size:15px;color:#fff;display:flex;align-items:center;gap:6px;}',
746            '.btm-rubrik .btm-under{font-size:11px;color:#8899aa;}',
747            '.btm-sektion{padding:10px 14px;border-bottom:1px solid rgba(255,255,255,.06);}',
748            '.btm-sektion h3{margin:0 0 8px;font-size:11px;color:#e4002b;text-transform:uppercase;letter-spacing:.5px;}',
749            '.btm-knapp-rad{display:flex;gap:5px;flex-wrap:wrap;margin-top:6px;}',
750            '.btm-btn{display:inline-flex;align-items:center;justify-content:center;padding:7px 14px;border:none;border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s;text-decoration:none;line-height:1.2;}',
751            '.btm-btn-röd{background:#e4002b;color:#fff;} .btm-btn-röd:hover{background:#ff1744;}',
752            '.btm-btn-fb{background:#1877f2;color:#fff;} .btm-btn-fb:hover{background:#1565c0;}',
753            '.btm-btn-grå{background:rgba(255,255,255,.1);color:#e8e8e8;} .btm-btn-grå:hover{background:rgba(255,255,255,.2);}',
754            '.btm-btn-block{width:100%;margin-top:5px;}',
755            '.btm-btn-liten{padding:4px 9px;font-size:11px;}',
756            '.btm-kort{background:rgba(255,255,255,.05);border-radius:7px;padding:9px 10px;margin-bottom:7px;cursor:pointer;border:1px solid rgba(255,255,255,.08);transition:all .2s;}',
757            '.btm-kort:hover{background:rgba(255,255,255,.09);border-color:#e4002b44;}',
758            '.btm-kort.btm-vald{border-color:#e4002b;background:rgba(228,0,43,.08);}',
759            '.btm-kort-rubrik{font-size:12px;font-weight:600;margin-bottom:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',
760            '.btm-kort-pris{font-size:13px;font-weight:700;color:#00c853;}',
761            '.btm-kort-meta{font-size:10px;color:#8899aa;margin-top:2px;}',
762            '.btm-etikett{display:inline-block;padding:2px 6px;border-radius:3px;font-size:10px;font-weight:600;margin-left:5px;}',
763            '.btm-et-full{background:#00c85322;color:#00c853;} .btm-et-bas{background:#ff980022;color:#ff9800;} .btm-et-fb{background:#1877f222;color:#1877f2;}',
764            '.btm-detalj-etikett{font-size:10px;text-transform:uppercase;color:#8899aa;letter-spacing:.4px;margin-bottom:2px;}',
765            '.btm-detalj-värde{font-size:12px;line-height:1.4;margin-bottom:7px;}',
766            '.btm-detalj-beskr{max-height:110px;overflow-y:auto;font-size:11px;line-height:1.5;color:#8899aa;}',
767            '.btm-bild-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;margin-bottom:8px;}',
768            '.btm-bild-grid img{width:100%;height:65px;object-fit:cover;border-radius:4px;}',
769            '.btm-tomtext{text-align:center;padding:24px 12px;color:#8899aa;font-size:12px;}',
770            '.btm-kategori-ruta{background:rgba(24,119,242,.08);border:1px solid #1877f222;border-radius:5px;padding:7px 9px;margin-top:5px;font-size:11px;}',
771            '.btm-kategori-ruta .btm-kat-rubrik{font-weight:600;color:#1877f2;margin-bottom:3px;}',
772            '.btm-kategori-ruta .btm-kat-stig{color:#8899aa;}',
773            '.btm-poäng{display:inline-block;padding:1px 5px;border-radius:3px;font-size:10px;background:#1877f222;color:#1877f2;margin-left:4px;}',
774            '.btm-notis{position:fixed!important;bottom:20px!important;left:50%!important;transform:translateX(-50%)!important;background:#323232;color:#fff;padding:9px 20px;border-radius:8px;font-size:12px;z-index:9999999!important;box-shadow:0 4px 16px rgba(0,0,0,.4);animation:btm-in .3s ease,btm-ut .3s ease 3.2s forwards;}',
775            '@keyframes btm-in{from{opacity:0;transform:translateX(-50%) translateY(16px)}}',
776            '@keyframes btm-ut{to{opacity:0;transform:translateX(-50%) translateY(16px)}}',
777        ].join('');
778        document.head.appendChild(s);
779    }
780
781    // ==================== PANEL UI ====================
782    function byggPanel() {
783        qs('#btm-panel')?.remove();
784        qs('#btm-knapp')?.remove();
785
786        var knapp = document.createElement('button');
787        knapp.id = 'btm-knapp';
788        knapp.title = 'Blocket ↔ Marketplace';
789        knapp.innerHTML = 'BTM';
790        document.body.appendChild(knapp);
791
792        var panel = document.createElement('div');
793        panel.id = 'btm-panel';
794        document.body.appendChild(panel);
795
796        knapp.addEventListener('click', function() {
797            tillstånd.panelÖppen = !tillstånd.panelÖppen;
798            panel.classList.toggle('btm-öppen', tillstånd.panelÖppen);
799            knapp.classList.toggle('btm-förskjuten', tillstånd.panelÖppen);
800            if (tillstånd.panelÖppen) renderaPanel();
801        });
802
803        return panel;
804    }
805
806    function renderaPanel() {
807        var panel = qs('#btm-panel');
808        if (!panel) return;
809        var annonser = laddaAnnonser();
810        tillstånd.annonser = annonser;
811        var sajt = detekteraSajt();
812        var url = location.pathname;
813
814        var html = '';
815
816        // ── RUBRIK ──
817        html += '<div class="btm-rubrik">';
818        html += '<h2>🔄 Blocket ↔ Marketplace</h2>';
819        html += '<div class="btm-under">';
820        if (sajt === 'blocket') html += '<span style="color:#e4002b;font-weight:600;">● Blocket</span> — Källsajt';
821        else if (sajt === 'facebook') html += '<span style="color:#1877f2;font-weight:600;">● Facebook</span> — Målsajt';
822        html += ' &nbsp;·&nbsp; ' + annonser.length + ' annons(er) sparad(e)';
823        html += '</div></div>';
824
825        // ── ÅTGÄRDER BEROENDE PÅ SIDA ──
826        if (sajt === 'blocket') {
827            html += '<div class="btm-sektion"><h3>Blocket-åtgärder</h3>';
828            if (url.includes('/my-items') && !url.includes('/details/')) {
829                html += '<button class="btm-btn btm-btn-röd btm-btn-block" id="btm-skrapa-lista">📋 Skanna alla annonser på sidan</button>';
830            }
831            if (url.includes('/recommerce/forsale/item/') || url.includes('/my-items/details/')) {
832                html += '<button class="btm-btn btm-btn-röd btm-btn-block" id="btm-skrapa-detalj">🔍 Skrapa fullständig annonsdata</button>';
833            }
834            if (url.includes('/recommerce/create')) {
835                html += '<button class="btm-btn btm-btn-röd btm-btn-block" id="btm-fyll-blocket">⚡ Fyll i formuläret automatiskt</button>';
836                if (tillstånd.valadAnnons) {
837                    var kat = tillstånd.valadAnnons.blocketKategori || hittaBästaGratisKategori(tillstånd.valadAnnons.rubrik, tillstånd.valadAnnons.beskrivning);
838                    html += '<div class="btm-kategori-ruta"><div class="btm-kat-rubrik">🏷️ Auto-kategori (gratis)</div>';
839                    html += '<div class="btm-kat-stig">' + (kat.huvud || '') + ' › ' + (kat.sub || '') + (kat.l3 ? ' › ' + kat.l3 : '') + '</div>';
840                    html += '<div style="font-size:10px;color:#8899aa;margin-top:3px;">Matchningspoäng: ' + (kat.poäng || 0) + '</div></div>';
841                }
842            }
843            if (!url.includes('/recommerce/create') && !url.includes('/create-item')) {
844                html += '<button class="btm-btn btm-btn-grå btm-btn-block" id="btm-öppna-skapa">➕ Ny annons på Blocket</button>';
845            }
846            html += '</div>';
847        }
848
849        if (sajt === 'facebook') {
850            html += '<div class="btm-sektion"><h3>Facebook Marketplace</h3>';
851            if (url.includes('/marketplace/create')) {
852                html += '<p style="font-size:11px;color:#8899aa;margin:0 0 6px;">Välj en annons nedan och klicka Fyll i.</p>';
853                if (tillstånd.valadAnnons) {
854                    html += '<button class="btm-btn btm-btn-fb btm-btn-block" id="btm-fyll-fb">⚡ Fyll i FB-formuläret</button>';
855                }
856            } else {
857                html += '<p style="font-size:11px;color:#8899aa;margin:0 0 6px;">Gå till Marketplace → Skapa inlägg → Vara till salu</p>';
858                html += '<a href="https://www.facebook.com/marketplace/create/item" class="btm-btn btm-btn-fb btm-btn-block" style="text-align:center;">Skapa nytt inlägg</a>';
859            }
860            html += '</div>';
861        }
862
863        // ── ANNONS-LISTA ──
864        html += '<div class="btm-sektion"><h3>Sparade annonser (' + annonser.length + ')</h3>';
865        if (!annonser.length) {
866            html += '<div class="btm-tomtext">Inga annonser sparade.<br>Gå till Blocket → Mina annonser och klicka Skanna.</div>';
867        } else {
868            annonser.forEach(function(a, i) {
869                var vald = tillstånd.valadAnnons && tillstånd.valadAnnons.id === a.id;
870                html += '<div class="btm-kort' + (vald ? ' btm-vald' : '') + '" data-idx="' + i + '">';
871                html += '<div class="btm-kort-rubrik">' + (a.rubrik || 'Okänd rubrik') + '</div>';
872                html += '<div style="display:flex;justify-content:space-between;align-items:center;">';
873                html += '<span class="btm-kort-pris">' + (a.pris ? a.pris + ' kr' : '—') + '</span>';
874                html += '<span>';
875                if (a.fullData) html += '<span class="btm-etikett btm-et-full">Full</span>';
876                else html += '<span class="btm-etikett btm-et-bas">Bas</span>';
877                if (a.lagtUppFB) html += '<span class="btm-etikett btm-et-fb">FB ✓</span>';
878                html += '</span></div>';
879                if (a.blocketKategori) {
880                    html += '<div class="btm-kort-meta">🏷️ ' + a.blocketKategori.sub + '</div>';
881                }
882                html += '</div>';
883            });
884        }
885        html += '</div>';
886
887        // ── VALD ANNONS DETALJ ──
888        if (tillstånd.valadAnnons) {
889            var a = tillstånd.valadAnnons;
890            html += '<div class="btm-sektion"><h3>Vald annons</h3>';
891            if (a.bilder && a.bilder.length) {
892                html += '<div class="btm-bild-grid">';
893                a.bilder.slice(0,6).forEach(function(url) { html += '<img src="' + url + '" />'; });
894                html += '</div>';
895            } else if (a.miniatyrbild) {
896                html += '<img src="' + a.miniatyrbild + '" style="width:100%;border-radius:6px;margin-bottom:8px;max-height:150px;object-fit:cover;" />';
897            }
898            html += '<div class="btm-detalj-etikett">Rubrik</div><div class="btm-detalj-värde">' + (a.rubrik||'') + '</div>';
899            html += '<div class="btm-detalj-etikett">Pris</div><div class="btm-detalj-värde" style="color:#00c853;font-weight:700;">' + (a.pris||'—') + ' kr</div>';
900            if (a.skick) {
901                html += '<div class="btm-detalj-etikett">Skick</div><div class="btm-detalj-värde">' + a.skick + '</div>';
902            }
903            if (a.plats) {
904                html += '<div class="btm-detalj-etikett">Plats</div><div class="btm-detalj-värde">' + a.plats + '</div>';
905            }
906
907            // Auto-kategori ruta
908            var kat = a.blocketKategori || hittaBästaGratisKategori(a.rubrik, a.beskrivning);
909            if (kat) {
910                html += '<div class="btm-detalj-etikett">Blocket-kategori (gratis)</div>';
911                html += '<div class="btm-kategori-ruta">';
912                html += '<div class="btm-kat-rubrik">🏷️ ' + (kat.huvud||'') + '</div>';
913                html += '<div class="btm-kat-stig">' + (kat.sub||'') + (kat.l3 ? ' › ' + kat.l3 : '') + '</div>';
914                html += '<div style="font-size:10px;color:#8899aa;margin-top:2px;">Matchningspoäng: <span class="btm-poäng">' + (kat.poäng||0) + '</span></div>';
915                html += '</div>';
916            }
917
918            if (sajt === 'facebook') {
919                html += '<div class="btm-detalj-etikett" style="margin-top:6px;">FB-skick</div><div class="btm-detalj-värde" style="color:#1877f2;">' + kartläggSkick(a.skick) + '</div>';
920                html += '<div class="btm-detalj-etikett">FB-kategori</div><div class="btm-detalj-värde" style="color:#1877f2;">' + kartläggFBKategori(a.kategorier) + '</div>';
921            }
922
923            if (a.beskrivning) {
924                html += '<div class="btm-detalj-etikett">Beskrivning</div><div class="btm-detalj-beskr">' + a.beskrivning.replace(/</g,'&lt;').substring(0,500) + '…</div>';
925            }
926
927            html += '<div class="btm-knapp-rad" style="margin-top:8px;">';
928            html += '<button class="btm-btn btm-btn-grå btm-btn-liten" id="btm-kopiera-rubrik">Kopiera rubrik</button>';
929            html += '<button class="btm-btn btm-btn-grå btm-btn-liten" id="btm-kopiera-besk">Kopiera beskrivning</button>';
930            if (a.bilder && a.bilder.length) html += '<button class="btm-btn btm-btn-grå btm-btn-liten" id="btm-ladda-bilder">⬇ Bilder</button>';
931            html += '<button class="btm-btn btm-btn-grå btm-btn-liten" id="btm-öppna-annons" style="color:#1877f2;">Öppna annons</button>';
932            html += '<button class="btm-btn btm-btn-grå btm-btn-liten" id="btm-markera-fb" style="color:#00c853;">✓ Markera som upplagd</button>';
933            html += '<button class="btm-btn btm-btn-grå btm-btn-liten" id="btm-ta-bort" style="color:#ff5252;">Ta bort</button>';
934            html += '</div></div>';
935        }
936
937        // ── FOOTER ──
938        if (annonser.length) {
939            html += '<div class="btm-sektion" style="text-align:center;">';
940            html += '<button class="btm-btn btm-btn-grå btm-btn-liten" id="btm-exportera">⬇ Exportera JSON</button>&nbsp;';
941            html += '<button class="btm-btn btm-btn-grå btm-btn-liten" id="btm-rensa-allt" style="color:#ff5252;">🗑 Rensa allt</button>';
942            html += '</div>';
943        }
944
945        panel.innerHTML = html;
946        bindaHändelser(panel);
947    }
948
949    function bindaHändelser(panel) {
950        // Kort-klick → välj annons
951        qsa('.btm-kort', panel).forEach(function(k) {
952            k.addEventListener('click', function() {
953                var idx = parseInt(k.getAttribute('data-idx'), 10);
954                tillstånd.valadAnnons = laddaAnnonser()[idx] || null;
955                renderaPanel();
956            });
957        });
958
959        var bind = function(id, fn) {
960            var el = qs('#' + id, panel);
961            if (el) el.addEventListener('click', fn);
962        };
963
964        bind('btm-skrapa-lista', function() {
965            var nya = skrapaAnnonsLista();
966            var bef = laddaAnnonser();
967            var befIdn = {}; bef.forEach(function(a){ befIdn[a.id]=true; });
968            var tillagda = 0;
969            nya.forEach(function(a){ if (!befIdn[a.id]){ bef.push(a); tillagda++; } });
970            sparaAnnonser(bef);
971            visaNotis('Skannade ' + nya.length + ' annonser, lade till ' + tillagda + ' nya', 'ok');
972            renderaPanel();
973        });
974
975        bind('btm-skrapa-detalj', function() {
976            var d = skrapaAnnonsDetalj();
977            var bef = laddaAnnonser();
978            var hittad = false;
979            for (var i = 0; i < bef.length; i++) {
980                if (bef[i].id === d.id) {
981                    Object.keys(d).forEach(function(k){ if (d[k] != null && d[k] !== '') bef[i][k] = d[k]; });
982                    hittad = true; break;
983                }
984            }
985            if (!hittad) bef.push(d);
986            sparaAnnonser(bef);
987            tillstånd.valadAnnons = d;
988            visaNotis('✓ Fullständig annonsdata sparad: ' + d.rubrik, 'ok');
989            renderaPanel();
990        });
991
992        bind('btm-fyll-blocket', function() { fyllIBlocketFormulär(tillstånd.valadAnnons); });
993        bind('btm-fyll-fb', function() { fyllIFBFormulär(tillstånd.valadAnnons); });
994        bind('btm-öppna-skapa', function() { öppnaSkapa(); });
995
996        bind('btm-kopiera-rubrik', function() {
997            if (tillstånd.valadAnnons) { GM_setClipboard(tillstånd.valadAnnons.rubrik, 'text'); visaNotis('Rubrik kopierad', 'ok'); }
998        });
999        bind('btm-kopiera-besk', function() {
1000            if (tillstånd.valadAnnons && tillstånd.valadAnnons.beskrivning) { GM_setClipboard(tillstånd.valadAnnons.beskrivning, 'text'); visaNotis('Beskrivning kopierad', 'ok'); }
1001        });
1002
1003        bind('btm-ladda-bilder', async function() {
1004            if (!tillstånd.valadAnnons || !tillstånd.valadAnnons.bilder) return;
1005            visaNotis('Laddar ner ' + tillstånd.valadAnnons.bilder.length + ' bild(er)…');
1006            for (var i = 0; i < tillstånd.valadAnnons.bilder.length; i++) {
1007                var blob = await laddaNerBild(tillstånd.valadAnnons.bilder[i]);
1008                if (blob) {
1009                    var a = document.createElement('a');
1010                    a.href = URL.createObjectURL(blob);
1011                    a.download = tillstånd.valadAnnons.id + '_bild_' + (i+1) + '.jpg';
1012                    document.body.appendChild(a); a.click(); a.remove();
1013                    URL.revokeObjectURL(a.href);
1014                }
1015            }
1016            visaNotis('✓ Bilder nedladdade', 'ok');
1017        });
1018
1019        bind('btm-öppna-annons', function() {
1020            if (tillstånd.valadAnnons && tillstånd.valadAnnons.offentligUrl) window.open(tillstånd.valadAnnons.offentligUrl, '_blank');
1021        });
1022
1023        bind('btm-markera-fb', function() {
1024            if (!tillstånd.valadAnnons) return;
1025            var bef = laddaAnnonser();
1026            bef.forEach(function(a){ if (a.id === tillstånd.valadAnnons.id) a.lagtUppFB = true; });
1027            sparaAnnonser(bef);
1028            tillstånd.valadAnnons.lagtUppFB = true;
1029            visaNotis('✓ Markerad som upplagd på Facebook', 'ok');
1030            renderaPanel();
1031        });
1032
1033        bind('btm-ta-bort', function() {
1034            if (!tillstånd.valadAnnons) return;
1035            var bef = laddaAnnonser().filter(function(a){ return a.id !== tillstånd.valadAnnons.id; });
1036            sparaAnnonser(bef);
1037            tillstånd.valadAnnons = null;
1038            visaNotis('Annons borttagen', 'ok');
1039            renderaPanel();
1040        });
1041
1042        bind('btm-exportera', function() {
1043            var data = JSON.stringify(laddaAnnonser(), null, 2);
1044            var blob = new Blob([data], {type:'application/json'});
1045            var a = document.createElement('a');
1046            a.href = URL.createObjectURL(blob);
1047            a.download = 'blocket_annonser_' + new Date().toISOString().split('T')[0] + '.json';
1048            document.body.appendChild(a); a.click(); a.remove();
1049            visaNotis('✓ JSON exporterad', 'ok');
1050        });
1051
1052        bind('btm-rensa-allt', function() {
1053            if (confirm('Rensa alla sparade annonser?')) {
1054                sparaAnnonser([]); tillstånd.valadAnnons = null;
1055                visaNotis('Alla annonser rensade');
1056                renderaPanel();
1057            }
1058        });
1059    }
1060
1061    // ==================== INIT ====================
1062    function init() {
1063        tillstånd.sajt = detekteraSajt();
1064        logg('Startar på', tillstånd.sajt, '|', location.href);
1065        injiceraCSS();
1066        byggPanel();
1067    }
1068
1069    function vänta(cb, försök) {
1070        försök = försök || 0;
1071        if (document.body) cb();
1072        else if (försök < 50) setTimeout(function(){ vänta(cb, försök+1); }, 100);
1073    }
1074
1075    if (document.body) init();
1076    else if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ vänta(init); });
1077    else vänta(init);
1078
1079})();
1080
Blocket ↔ Marketplace Brygga | Robomonkey