NewBestPastebins

ViGGO

Oct 6th, 2025
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 31.04 KB | None | 0 0
  1. // ==UserScript==
  2. // @name ViGGO Sletter - Endelige Version 2
  3. // @namespace http://tampermonkey.net/
  4. // @version 2025-06-26
  5. // @description Sletter overflødige ViGGO beskeder lynhurtigt!
  6. // @author Oliver AAE 24-25
  7. // @match https://*.viggo.dk/Basic*
  8. // @match https://viggo.dk/Basic*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=viggo.dk
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const addStyles = () => {
  17. const style = document.createElement('style');
  18. style.textContent = `
  19. .trash-icon {
  20. position: absolute;
  21. top: 8px;
  22. right: 8px;
  23. width: 18px;
  24. height: 18px;
  25. background-color: #f44336;
  26. border-radius: 50%;
  27. display: flex;
  28. align-items: center;
  29. justify-content: center;
  30. color: white;
  31. font-size: 10px;
  32. font-weight: bold;
  33. z-index: 10;
  34. box-shadow: 0 0 3px rgba(0,0,0,0.5);
  35. }
  36.  
  37. .trash-icon::after {
  38. content: "🗑️";
  39. }
  40.  
  41. .help-popup {
  42. position: fixed;
  43. top: 50%;
  44. left: 50%;
  45. transform: translate(-50%, -50%);
  46. width: 90%;
  47. max-width: 600px;
  48. max-height: 90vh;
  49. background: #1a1a1a;
  50. color: #f1f1f1;
  51. padding: 25px;
  52. border-radius: 15px;
  53. box-shadow: 0 10px 30px rgba(0,0,0,0.5);
  54. z-index: 10001;
  55. overflow-y: auto;
  56. font-family: 'Arial', sans-serif;
  57. display: none;
  58. }
  59.  
  60. .help-overlay {
  61. position: fixed;
  62. top: 0;
  63. left: 0;
  64. width: 100%;
  65. height: 100%;
  66. background: rgba(0,0,0,0.7);
  67. backdrop-filter: blur(5px);
  68. z-index: 10000;
  69. display: none;
  70. }
  71.  
  72. .help-popup h2 {
  73. color: #007BFF;
  74. margin-top: 0;
  75. border-bottom: 1px solid #333;
  76. padding-bottom: 10px;
  77. }
  78.  
  79. .help-popup ol {
  80. text-align: left;
  81. padding-left: 20px;
  82. }
  83.  
  84. .help-popup li {
  85. margin-bottom: 15px;
  86. line-height: 1.5;
  87. }
  88.  
  89. .close-help {
  90. position: absolute;
  91. top: 15px;
  92. right: 15px;
  93. background: #555;
  94. border: none;
  95. color: white;
  96. width: 30px;
  97. height: 30px;
  98. border-radius: 50%;
  99. cursor: pointer;
  100. font-size: 18px;
  101. }
  102. `;
  103. document.head.appendChild(style);
  104. };
  105.  
  106. (() => {
  107. addStyles();
  108.  
  109. let lastFolderId = null;
  110. let markedForDeletion = new Set();
  111.  
  112. // Get current domain dynamically
  113. const getCurrentDomain = () => {
  114. return window.location.origin;
  115. };
  116.  
  117. const monitorFolderChange = () => {
  118. setInterval(() => {
  119. const urlParams = new URLSearchParams(window.location.search);
  120. const currentFolderId = urlParams.get('folder');
  121.  
  122. if (currentFolderId !== lastFolderId) {
  123. lastFolderId = currentFolderId;
  124. markedForDeletion.clear();
  125.  
  126. const notificationBanner = document.getElementById('viggo-folder-notification');
  127. if (notificationBanner) notificationBanner.remove();
  128.  
  129. if (currentFolderId === '0') {
  130. const banner = document.createElement('div');
  131. banner.id = 'viggo-folder-notification';
  132. banner.textContent = '📨 Du er nu i "Sendt post" mappen!';
  133. banner.style.position = 'fixed';
  134. banner.style.top = '0';
  135. banner.style.left = '0';
  136. banner.style.width = '100%';
  137. banner.style.padding = '12px';
  138. banner.style.backgroundColor = '#ff9800';
  139. banner.style.color = 'black';
  140. banner.style.textAlign = 'center';
  141. banner.style.zIndex = '9999';
  142. banner.style.fontWeight = 'bold';
  143. banner.style.fontSize = '16px';
  144. banner.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.3)';
  145. document.body.appendChild(banner);
  146. }
  147. }
  148. }, 1000);
  149. };
  150.  
  151. let cancelRequested = false;
  152.  
  153. const styleButton = (button, bgColor) => {
  154. button.style.padding = '12px 18px';
  155. button.style.width = '100%';
  156. button.style.backgroundColor = bgColor;
  157. button.style.border = 'none';
  158. button.style.color = 'white';
  159. button.style.borderRadius = '10px';
  160. button.style.cursor = 'pointer';
  161. button.style.fontSize = '16px';
  162. button.style.transition = 'background-color 0.3s ease-in-out';
  163. button.style.marginBottom = '10px';
  164. };
  165.  
  166. const styleInput = (input) => {
  167. input.style.width = '100%';
  168. input.style.padding = '10px';
  169. input.style.marginBottom = '15px';
  170. input.style.borderRadius = '10px';
  171. input.style.border = '1px solid #333';
  172. input.style.backgroundColor = '#2b2b2b';
  173. input.style.color = '#f1f1f1';
  174. input.style.fontSize = '16px';
  175. };
  176.  
  177. const extractMessageIds = () => {
  178. const isSentFolder = window.location.pathname.includes("/Folder/0");
  179. markedForDeletion.clear();
  180.  
  181. if (isSentFolder) {
  182. const messageList = document.querySelector("ul.list-messages");
  183. if (!messageList) return [];
  184.  
  185. const checkboxes = messageList.querySelectorAll('input[type="checkbox"][name="MessageId"]');
  186. return Array.from(checkboxes)
  187. .map(cb => cb.value)
  188. .filter(id => /^\d{5,6}$/.test(id)); // Accept 5 or 6 digit IDs
  189. } else {
  190. const messageElements = document.querySelectorAll('li[data-drag-id]');
  191. return Array.from(messageElements)
  192. .map(el => el.getAttribute('data-drag-id'))
  193. .filter(id => /^\d{5,6}$/.test(id));
  194. }
  195. };
  196.  
  197. const markMessagesForDeletion = (idsToDelete) => {
  198. // Remove existing trash icons
  199. document.querySelectorAll('.trash-icon').forEach(icon => icon.remove());
  200.  
  201. if (window.location.pathname.includes("/Folder/0")) {
  202. // Sent messages
  203. idsToDelete.forEach(id => {
  204. const checkbox = document.querySelector(`input[type="checkbox"][value="${id}"]`);
  205. if (checkbox) {
  206. const listItem = checkbox.closest('li');
  207. if (listItem) {
  208. const trashIcon = document.createElement('div');
  209. trashIcon.className = 'trash-icon';
  210. listItem.appendChild(trashIcon);
  211. }
  212. }
  213. });
  214. } else {
  215. idsToDelete.forEach(id => {
  216. const message = document.querySelector(`li[data-drag-id="${id}"]`);
  217. if (message) {
  218. const trashIcon = document.createElement('div');
  219. trashIcon.className = 'trash-icon';
  220. message.appendChild(trashIcon);
  221. }
  222. });
  223. }
  224. };
  225.  
  226. const showLoadingEffect = () => {
  227. const messageList = document.getElementById('message-list');
  228. if (messageList) {
  229. messageList.style.filter = 'blur(5px)';
  230. messageList.style.transition = 'filter 0.3s ease';
  231. document.getElementById("message-list").style.pointerEvents = "none";
  232. document.getElementById("message-list").style.userSelect = "none";
  233. document.getElementById("message-list").style.opacity = "0.6";
  234. }
  235.  
  236. const loadingContainer = document.createElement('div');
  237. loadingContainer.classList.add('custom-loading-container');
  238. loadingContainer.innerHTML = `
  239. <style>
  240. .custom-loading-container {
  241. position: fixed;
  242. top: 50vh;
  243. left: 50%;
  244. transform: translate(-250%, -50%);
  245. z-index: 9999;
  246. text-align: center;
  247. font-family: sans-serif;
  248. color: #333;
  249. }
  250.  
  251. .spinner {
  252. width: 40px;
  253. height: 40px;
  254. margin: 10px auto 0;
  255. border-radius: 50%;
  256. border: 5px solid #ccc;
  257. border-top-color: #007bff;
  258. animation: spin 1s linear infinite;
  259. }
  260.  
  261. @keyframes spin {
  262. to { transform: rotate(360deg); }
  263. }
  264.  
  265. .checkmark {
  266. width: 40px;
  267. height: 40px;
  268. display: inline-block;
  269. border-radius: 50%;
  270. background-color: #28a745;
  271. position: relative;
  272. animation: fadeIn 0.5s ease-in-out forwards;
  273. }
  274.  
  275. .checkmark::after {
  276. content: '';
  277. position: absolute;
  278. left: 12px;
  279. top: 6px;
  280. width: 10px;
  281. height: 20px;
  282. border: solid white;
  283. border-width: 0 4px 4px 0;
  284. transform: rotate(45deg);
  285. }
  286.  
  287. @keyframes fadeIn {
  288. from { opacity: 0; transform: scale(0.8); }
  289. to { opacity: 1; transform: scale(1); }
  290. }
  291. </style>
  292. <div id="loading-text">Sletter</div>
  293. <div class="spinner" id="spinner"></div>
  294. `;
  295. document.body.appendChild(loadingContainer);
  296. return loadingContainer;
  297. };
  298.  
  299. const finishLoadingEffect = (container) => {
  300. const spinner = container.querySelector('#spinner');
  301. const text = container.querySelector('#loading-text');
  302.  
  303. if (spinner) spinner.remove();
  304. if (text) text.textContent = 'Sådan!';
  305.  
  306. const checkmark = document.createElement('div');
  307. checkmark.className = 'checkmark';
  308. container.appendChild(checkmark);
  309.  
  310. const audio = new Audio('https://notificationsounds.com/storage/sounds/file-sounds-1085-definite.mp3');
  311. audio.volume = 0.6;
  312. audio.play().catch(err => console.warn('Audio failed to play:', err));
  313.  
  314. setTimeout(() => location.reload(), 1500);
  315. };
  316.  
  317. const deleteMessages = async (messageIds, statusText, progressBar, loadingContainer) => {
  318. const currentDomain = getCurrentDomain();
  319. let url;
  320. if (window.location.href.includes("/Folder/0")) {
  321. url = `${currentDomain}/Basic/Message/DeleteMessages/?folderId=0`;
  322. } else {
  323. url = `${currentDomain}/Basic/Message/DeleteMessages/?folderId=11`;
  324. }
  325.  
  326. let deleted = 0;
  327. let i = 0;
  328.  
  329. while (i < messageIds.length) {
  330. if (cancelRequested) {
  331. statusText.textContent = `Annulleret efter ${deleted} / ${messageIds.length} beskeder.`;
  332. return;
  333. }
  334.  
  335. const bulkSize = Math.floor(Math.random() * (175 - 75 + 1)) + 75;
  336. const chunk = messageIds.slice(i, i + bulkSize);
  337. const payload = chunk.map(id => `MessageId=${encodeURIComponent(id)}`).join('&');
  338.  
  339. try {
  340. const res = await fetch(url, {
  341. method: 'DELETE',
  342. headers: {
  343. 'Accept': '*/*',
  344. 'Content-Type': 'application/x-www-form-urlencoded',
  345. 'X-Requested-With': 'XMLHttpRequest'
  346. },
  347. body: payload,
  348. credentials: 'include'
  349. });
  350.  
  351. if (res.ok) {
  352. deleted += chunk.length;
  353. progressBar.value = (deleted / messageIds.length) * 100;
  354. statusText.textContent = `Sletter ${deleted} / ${messageIds.length}`;
  355. } else {
  356. console.error('Sletning fejlede:', res.statusText);
  357. }
  358. } catch (err) {
  359. console.error('Netværksfejl ved sletning:', err);
  360. }
  361.  
  362. i += bulkSize;
  363. }
  364.  
  365. statusText.textContent = 'Sletning færdig - opdaterer siden...';
  366. finishLoadingEffect(loadingContainer);
  367. };
  368.  
  369. const showConfirmation = (count, onConfirm) => {
  370. const overlay = document.createElement('div');
  371. overlay.style.position = 'fixed';
  372. overlay.style.top = 0;
  373. overlay.style.left = 0;
  374. overlay.style.width = '100%';
  375. overlay.style.height = '100%';
  376. overlay.style.backgroundColor = 'rgba(0, 0, 0, 0)';
  377. overlay.style.backdropFilter = 'blur(0px)';
  378. overlay.style.transition = 'background-color 300ms ease, backdrop-filter 300ms ease';
  379. overlay.style.zIndex = '9999';
  380. overlay.style.pointerEvents = 'auto';
  381. document.body.appendChild(overlay);
  382.  
  383. requestAnimationFrame(() => {
  384. overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.3)';
  385. overlay.style.backdropFilter = 'blur(5px)';
  386. });
  387.  
  388. const confirmBox = document.createElement('div');
  389. confirmBox.style.position = 'fixed';
  390. confirmBox.style.top = '50%';
  391. confirmBox.style.left = '50%';
  392. confirmBox.style.transform = 'translate(-50%, -50%) scale(0.9)';
  393. confirmBox.style.opacity = '0';
  394. confirmBox.style.transition = 'transform 250ms ease, opacity 250ms ease';
  395. confirmBox.style.background = '#1a1a1a';
  396. confirmBox.style.color = '#fff';
  397. confirmBox.style.padding = '20px';
  398. confirmBox.style.borderRadius = '12px';
  399. confirmBox.style.boxShadow = '0 0 15px rgba(0,0,0,0.5)';
  400. confirmBox.style.zIndex = '10000';
  401. confirmBox.innerHTML = `<p style="font-size:16px;margin-bottom:15px;">Er du sikker på, at du vil slette <strong>${count}</strong> besked(er)?</p>`;
  402.  
  403. const btnYes = document.createElement('button');
  404. btnYes.textContent = '✅ Ja, slet';
  405. styleButton(btnYes, '#f44336');
  406. btnYes.style.width = '45%';
  407. btnYes.style.marginRight = '10%';
  408.  
  409. const btnNo = document.createElement('button');
  410. btnNo.textContent = '❌ Annuller';
  411. styleButton(btnNo, '#555');
  412. btnNo.style.width = '45%';
  413.  
  414. [btnYes, btnNo].forEach(btn => {
  415. btn.style.transition = 'transform 150ms ease';
  416. btn.addEventListener('mouseenter', () => btn.style.transform = 'scale(1.05)');
  417. btn.addEventListener('mouseleave', () => btn.style.transform = 'scale(1)');
  418. });
  419.  
  420. const removePopup = () => {
  421. overlay.style.backgroundColor = 'rgba(0, 0, 0, 0)';
  422. overlay.style.backdropFilter = 'blur(0px)';
  423. confirmBox.style.opacity = '0';
  424. confirmBox.style.transform = 'translate(-50%, -50%) scale(0.9)';
  425. setTimeout(() => {
  426. confirmBox.remove();
  427. overlay.remove();
  428. }, 250);
  429. };
  430.  
  431. btnYes.addEventListener('click', () => {
  432. removePopup();
  433. onConfirm();
  434. });
  435.  
  436. btnNo.addEventListener('click', () => {
  437. removePopup();
  438. });
  439.  
  440. confirmBox.appendChild(btnYes);
  441. confirmBox.appendChild(btnNo);
  442. document.body.appendChild(confirmBox);
  443.  
  444. requestAnimationFrame(() => {
  445. confirmBox.style.opacity = '1';
  446. confirmBox.style.transform = 'translate(-50%, -50%) scale(1)';
  447. });
  448. };
  449.  
  450. const createUI = () => {
  451. setInterval(() => {
  452. const isSentView = window.location.href.includes("/Folder/0");
  453. const existingBanner = document.getElementById('sent-mode-banner');
  454.  
  455. if (isSentView && !existingBanner) {
  456. const sentModeBanner = document.createElement('div');
  457. sentModeBanner.id = 'sent-mode-banner';
  458. sentModeBanner.textContent = '📤 Du ser sendte beskeder';
  459. sentModeBanner.style.backgroundColor = '#444';
  460. sentModeBanner.style.color = '#ffc107';
  461. sentModeBanner.style.padding = '8px';
  462. sentModeBanner.style.borderRadius = '8px';
  463. sentModeBanner.style.marginBottom = '10px';
  464. sentModeBanner.style.textAlign = 'center';
  465. uiContainer.appendChild(sentModeBanner);
  466. } else if (!isSentView && existingBanner) {
  467. existingBanner.remove();
  468. }
  469. }, 1000);
  470.  
  471. // Create help popup elements
  472. const helpOverlay = document.createElement('div');
  473. helpOverlay.className = 'help-overlay';
  474.  
  475. const helpPopup = document.createElement('div');
  476. helpPopup.className = 'help-popup';
  477. helpPopup.innerHTML = `
  478. <h2>Brugsanvisning</h2>
  479. <ol>
  480. <li>Klik "🔎 Find alle besked ID(er)" for at samle ID'erne.</li>
  481. <li>Udfyld antallet af beskeder, du vil beholde fra toppen af indbakken.</li>
  482. <li>Klik "🗑️ Slet Beskeder" for at starte sletningen.</li>
  483. <li>Du kan manuelt fjerne ID(er) på enkelte beskeder fra bunden eller midten af mængden, hvis du ønsker det. Rækkefølgen på ID(erne) i tekstfeltet er den samme som i indbakken.</li>
  484. <li>⛔ Du kan afbryde sletningen med "Annuller".</li>
  485. <li>✅ Når sletningen er færdig, genindlæses siden automatisk.</li>
  486. </ol>
  487. <button class="close-help">×</button>
  488. `;
  489.  
  490. document.body.appendChild(helpOverlay);
  491. document.body.appendChild(helpPopup);
  492.  
  493. // Help popup controls
  494. document.querySelector('.close-help').addEventListener('click', () => {
  495. helpOverlay.style.display = 'none';
  496. helpPopup.style.display = 'none';
  497. });
  498.  
  499. helpOverlay.addEventListener('click', (e) => {
  500. if (e.target === helpOverlay) {
  501. helpOverlay.style.display = 'none';
  502. helpPopup.style.display = 'none';
  503. }
  504. });
  505.  
  506. const uiContainer = document.createElement('div');
  507. uiContainer.style.width = '300px';
  508. uiContainer.style.position = 'fixed';
  509. uiContainer.style.top = '85px';
  510. uiContainer.style.right = '2px';
  511. uiContainer.style.backgroundColor = '#1a1a1a';
  512. uiContainer.style.color = '#f1f1f1';
  513. uiContainer.style.padding = '20px';
  514. uiContainer.style.borderRadius = '15px';
  515. uiContainer.style.boxShadow = '0 6px 12px rgba(0, 0, 0, 0.3)';
  516. uiContainer.style.zIndex = '9999';
  517. uiContainer.style.fontFamily = "'Arial', sans-serif";
  518. uiContainer.style.display = 'none';
  519.  
  520. const header = document.createElement('div');
  521. header.innerHTML = '🗑️ <strong>Viggo Besked Sletter</strong>';
  522. header.style.marginBottom = '15px';
  523. header.style.fontSize = '18px';
  524. uiContainer.appendChild(header);
  525.  
  526. const label = document.createElement('label');
  527. label.textContent = 'Skriv Besked ID(er) (komma-separeret):';
  528. label.style.display = 'block';
  529. label.style.marginBottom = '5px';
  530.  
  531. const textarea = document.createElement('textarea');
  532. textarea.style.width = '100%';
  533. textarea.style.height = '100px';
  534. textarea.style.padding = '10px';
  535. textarea.style.borderRadius = '10px';
  536. textarea.style.border = '1px solid #333';
  537. textarea.style.marginBottom = '15px';
  538. textarea.style.backgroundColor = '#2b2b2b';
  539. textarea.style.color = '#f1f1f1';
  540. textarea.style.resize = 'none';
  541.  
  542. const extractButton = document.createElement('button');
  543. extractButton.textContent = '🔎 Find alle besked ID(er)';
  544. styleButton(extractButton, '#4CAF50');
  545. extractButton.addEventListener('click', () => {
  546. document.getElementById("message-list").style.pointerEvents = "none";
  547. document.getElementById("message-list").style.userSelect = "none";
  548. document.getElementById("message-list").style.opacity = "0.6";
  549.  
  550. (function autoScrollMessageList() {
  551. const element = document.getElementById('message-list');
  552. if (!element) return console.warn('Element #message-list not found');
  553.  
  554. const interval = setInterval(() => {
  555. const maxScroll = element.scrollHeight - element.clientHeight;
  556. const currentScroll = element.scrollTop;
  557.  
  558. element.scrollTop += 10;
  559.  
  560. if (currentScroll >= maxScroll - 1) {
  561. clearInterval(interval);
  562. const messageIds = extractMessageIds();
  563. textarea.value = messageIds.join(', ');
  564. console.log('Reached bottom of #message-list');
  565. document.getElementById("message-list").style.pointerEvents = "auto";
  566. document.getElementById("message-list").style.userSelect = "auto";
  567. document.getElementById("message-list").style.opacity = "1";
  568.  
  569. // Update deletion marks
  570. const topCount = parseInt(topExclusionInput.value) || 0;
  571. const allIds = textarea.value.split(',').map(id => id.trim()).filter(Boolean);
  572. const idsToDelete = topCount > 0 ? allIds.slice(topCount) : allIds;
  573. markMessagesForDeletion(idsToDelete);
  574. }
  575. }, 1);
  576. })();
  577. });
  578.  
  579. const topExclusionLabel = document.createElement('label');
  580. topExclusionLabel.textContent = 'Antal beskeder der skal beholdes fra toppen:';
  581. topExclusionLabel.style.display = 'block';
  582. topExclusionLabel.style.marginBottom = '5px';
  583.  
  584. const topExclusionInput = document.createElement('input');
  585. topExclusionInput.type = 'number';
  586. styleInput(topExclusionInput);
  587. topExclusionInput.addEventListener('input', () => {
  588. const topCount = parseInt(topExclusionInput.value) || 0;
  589. const allIds = textarea.value.split(',').map(id => id.trim()).filter(Boolean);
  590. const idsToDelete = topCount > 0 ? allIds.slice(topCount) : allIds;
  591. markMessagesForDeletion(idsToDelete);
  592. });
  593.  
  594. const statusText = document.createElement('div');
  595. statusText.style.marginBottom = '10px';
  596.  
  597. const progressBar = document.createElement('progress');
  598. progressBar.style.width = '100%';
  599. progressBar.max = 100;
  600. progressBar.value = 0;
  601.  
  602. const cancelButton = document.createElement('button');
  603. cancelButton.textContent = '⛔ Annuller';
  604. styleButton(cancelButton, '#555');
  605. cancelButton.style.marginTop = '10px';
  606. cancelButton.addEventListener('click', () => {
  607. cancelRequested = true;
  608. });
  609.  
  610. const deleteButton = document.createElement('button');
  611. deleteButton.textContent = '🗑️ Slet Beskeder';
  612. styleButton(deleteButton, '#f44336');
  613. deleteButton.addEventListener('click', () => {
  614. cancelRequested = false;
  615. const topCount = parseInt(topExclusionInput.value) || 0;
  616. const allIds = textarea.value.split(',').map(id => id.trim()).filter(Boolean);
  617. const idsToDelete = topCount > 0 ? allIds.slice(topCount) : allIds;
  618.  
  619. if (idsToDelete.length === 0) {
  620. statusText.textContent = 'Ingen beskeder valgt.';
  621. return;
  622. }
  623.  
  624. showConfirmation(idsToDelete.length, async () => {
  625. deleteButton.disabled = true;
  626. deleteButton.style.opacity = '0.5';
  627. deleteButton.textContent = '⏳ Sletter...';
  628. statusText.textContent = `Sletter 0 / ${idsToDelete.length}`;
  629. const loadingContainer = showLoadingEffect();
  630. await deleteMessages(idsToDelete, statusText, progressBar, loadingContainer);
  631. deleteButton.disabled = false;
  632. deleteButton.style.opacity = '1';
  633. deleteButton.textContent = '🗑️ Slet Beskeder';
  634. });
  635. });
  636.  
  637. const helpButton = document.createElement('button');
  638. helpButton.textContent = '📘 Brugsanvisning';
  639. styleButton(helpButton, '#007BFF');
  640. helpButton.addEventListener('click', () => {
  641. helpOverlay.style.display = 'block';
  642. helpPopup.style.display = 'block';
  643. });
  644.  
  645. uiContainer.appendChild(label);
  646. uiContainer.appendChild(textarea);
  647. uiContainer.appendChild(extractButton);
  648. uiContainer.appendChild(topExclusionLabel);
  649. uiContainer.appendChild(topExclusionInput);
  650. uiContainer.appendChild(deleteButton);
  651. uiContainer.appendChild(cancelButton);
  652. uiContainer.appendChild(statusText);
  653. uiContainer.appendChild(progressBar);
  654. uiContainer.appendChild(helpButton);
  655.  
  656. document.body.appendChild(uiContainer);
  657.  
  658. const toggleButton = document.createElement('button');
  659. toggleButton.textContent = '🔽 Åben Sletter';
  660. toggleButton.id = 'customToggleButton';
  661. toggleButton.style.position = 'fixed';
  662. toggleButton.style.top = '41px';
  663. toggleButton.style.right = '-2px';
  664. toggleButton.style.backgroundColor = '#2b2a2d';
  665. toggleButton.style.color = '#f1f1f1';
  666. toggleButton.style.padding = '8px 14px';
  667. toggleButton.style.border = 'none';
  668. toggleButton.style.cursor = 'pointer';
  669. toggleButton.style.width = '164px';
  670. toggleButton.style.fontSize = '18px';
  671. toggleButton.style.zIndex = '10000';
  672.  
  673. toggleButton.addEventListener('click', () => {
  674. const isVisible = uiContainer.style.display === 'block';
  675. uiContainer.style.display = isVisible ? 'none' : 'block';
  676. toggleButton.textContent = isVisible ? '🔽 Åben Sletter' : '🔼 Luk Sletter';
  677. toggleButton.classList.toggle('active');
  678. });
  679.  
  680. document.body.appendChild(toggleButton);
  681.  
  682. const style = document.createElement('style');
  683. style.textContent = `
  684. #customToggleButton {
  685. border-radius: 0 0 0 5px;
  686. position: relative;
  687. overflow: hidden;
  688. background-color: rgba(43, 42, 45, 1);
  689. }
  690.  
  691. #customToggleButton::after {
  692. content: '';
  693. position: absolute;
  694. bottom: 0;
  695. left: 50%;
  696. height: 2px;
  697. width: 0%;
  698. background-color: #f1f1f1;
  699. transition: width 0.3s ease, left 0.3s ease;
  700. transform: translateX(-50%);
  701. }
  702.  
  703. #customToggleButton::after {
  704. content: '';
  705. position: absolute;
  706. bottom: 0;
  707. left: 50%;
  708. height: 2px;
  709. width: 0%;
  710. background-color: #f1f1f1;
  711. transform: translateX(-50%);
  712. transition: none;
  713. }
  714.  
  715. #customToggleButton:hover::after {
  716. width: 100%;
  717. left: 0;
  718. transform: none;
  719. transition: width 0.3s ease, left 0.3s ease;
  720. }
  721.  
  722. #customToggleButton:hover {
  723. background-color: rgba(43, 42, 45, 0.1);
  724. }
  725.  
  726. #customToggleButton:focus {
  727. outline: none;
  728. box-shadow: none;
  729. }
  730.  
  731. #customToggleButton:hover::after {
  732. width: 100%;
  733. left: 0;
  734. transform: none;
  735. }
  736.  
  737. #customToggleButton.active::after {
  738. background-color: #117596;
  739. height: 4px;
  740. }
  741. `;
  742. document.head.appendChild(style);
  743. };
  744.  
  745. createUI();
  746. monitorFolderChange();
  747. })();
  748. })();
Advertisement
Add Comment
Please, Sign In to add comment