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 += ' · ' + 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,'<').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> ';
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