summaryrefslogtreecommitdiff
path: root/data-viewer/data-viewer.js
blob: 89f5b2e7ac2e9a85d8629f77d45761c5b8946f94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
document.addEventListener("DOMContentLoaded", function () {
  const BROWSER_STORAGE_KEY = "transparentZenSettings";
  const SKIP_FORCE_THEMING_KEY = "skipForceThemingList";
  const SKIP_THEMING_KEY = "skipThemingList";
  const REPOSITORY_URL_KEY = "stylesRepositoryUrl";
  const DEFAULT_REPOSITORY_URL =
    "https://sameerasw.github.io/my-internet/styles.json";

  const globalSettingsElement = document.getElementById("global-settings-data");
  const skipListElement = document.getElementById("skip-list-data");
  const combinedWebsitesElement = document.getElementById(
    "combined-websites-data"
  );
  const deleteAllButton = document.getElementById("delete-all-data");
  const versionElement = document.getElementById("addon-version");
  const disableTransparencyToggle = document.getElementById(
    "disable-transparency"
  );
  // New toggle elements
  const disableHoverToggle = document.getElementById("disable-hover");
  const disableFooterToggle = document.getElementById("disable-footer");

  // Repository URL Elements
  const repositoryUrlInput = document.getElementById("repository-url");
  const setRepositoryUrlButton = document.getElementById("set-repository-url");
  const resetRepositoryUrlButton = document.getElementById(
    "reset-repository-url"
  );
  const repositoryUrlStatus = document.getElementById("repository-url-status");

  // Backup & Restore Elements
  const exportButton = document.getElementById("export-settings");
  const importFileInput = document.getElementById("import-file");
  const importStatusElement = document.getElementById("import-status");

  // Load and display the data
  loadAllData();

  // Display addon version
  displayAddonVersion();

  // Event listener for disable transparency toggle
  disableTransparencyToggle.addEventListener("change", function () {
    saveTransparencySettings(this.checked);
  });

  // Event listeners for new toggles
  disableHoverToggle.addEventListener("change", function () {
    saveHoverSettings(this.checked);
  });

  disableFooterToggle.addEventListener("change", function () {
    saveFooterSettings(this.checked);
  });

  // Event listener for delete all data button
  deleteAllButton.addEventListener("click", function () {
    if (
      confirm(
        "WARNING: This will delete ALL extension data including settings, website styles, and preferences. This action cannot be undone!\n\nAre you sure you want to proceed?"
      )
    ) {
      deleteAllData();
    }
  });

  // Repository URL event listeners
  setRepositoryUrlButton.addEventListener("click", setRepositoryUrl);
  resetRepositoryUrlButton.addEventListener("click", resetRepositoryUrl);

  // New event listeners for export and import functionality
  exportButton.addEventListener("click", exportSettings);
  importFileInput.addEventListener("change", importSettings);

  // Load the repository URL from storage
  loadRepositoryUrl();

  async function loadRepositoryUrl() {
    try {
      const data = await browser.storage.local.get(REPOSITORY_URL_KEY);
      const repositoryUrl = data[REPOSITORY_URL_KEY] || DEFAULT_REPOSITORY_URL;
      repositoryUrlInput.value = repositoryUrl;
    } catch (error) {
      console.error("Error loading repository URL:", error);
      repositoryUrlInput.value = DEFAULT_REPOSITORY_URL;
    }
  }

  async function setRepositoryUrl() {
    try {
      const newUrl = repositoryUrlInput.value.trim();

      if (!newUrl) {
        showRepositoryUrlStatus("Repository URL cannot be empty", "error");
        return;
      }

      // Simple URL validation
      try {
        new URL(newUrl);
      } catch (e) {
        showRepositoryUrlStatus("Invalid URL format", "error");
        return;
      }

      // Save the new URL to storage
      await browser.storage.local.set({ [REPOSITORY_URL_KEY]: newUrl });

      showRepositoryUrlStatus("Repository URL saved successfully", "success");

      // Prompt the user to clear styles data
      if (
        confirm(
          "Would you like to clear existing styles data to avoid conflicts with the new repository?\n\nThis will clear saved styles and website-specific settings, but keep your global settings."
        )
      ) {
        await clearStylesData();
      }
    } catch (error) {
      console.error("Error setting repository URL:", error);
      showRepositoryUrlStatus(`Error saving URL: ${error.message}`, "error");
    }
  }

  async function resetRepositoryUrl() {
    try {
      repositoryUrlInput.value = DEFAULT_REPOSITORY_URL;
      await browser.storage.local.set({
        [REPOSITORY_URL_KEY]: DEFAULT_REPOSITORY_URL,
      });

      showRepositoryUrlStatus("Repository URL reset to default", "success");

      // Prompt to clear styles data
      if (
        confirm(
          "Would you like to clear existing styles data to avoid conflicts?\n\nThis will clear saved styles and website-specific settings, but keep your global settings."
        )
      ) {
        await clearStylesData();
      }
    } catch (error) {
      console.error("Error resetting repository URL:", error);
      showRepositoryUrlStatus(`Error resetting URL: ${error.message}`, "error");
    }
  }

  async function clearStylesData() {
    try {
      // Get all storage data to filter what to keep and what to remove
      const allData = await browser.storage.local.get(null);

      // Create a new object with just the data we want to keep
      const dataToKeep = {};

      // Keep global settings
      if (allData[BROWSER_STORAGE_KEY]) {
        dataToKeep[BROWSER_STORAGE_KEY] = allData[BROWSER_STORAGE_KEY];
      }

      // Keep repository URL
      if (allData[REPOSITORY_URL_KEY]) {
        dataToKeep[REPOSITORY_URL_KEY] = allData[REPOSITORY_URL_KEY];
      }

      // Clear all storage first
      await browser.storage.local.clear();

      // Then restore the data we want to keep
      await browser.storage.local.set(dataToKeep);

      // Refresh the data display
      loadAllData();

      showRepositoryUrlStatus("Styles data cleared successfully", "success");
    } catch (error) {
      console.error("Error clearing styles data:", error);
      showRepositoryUrlStatus(`Error clearing data: ${error.message}`, "error");
    }
  }

  function showRepositoryUrlStatus(message, type) {
    repositoryUrlStatus.textContent = message;
    repositoryUrlStatus.className = `repository-url-status status-${type}`;

    // Clear the message after 5 seconds
    setTimeout(() => {
      repositoryUrlStatus.textContent = "";
      repositoryUrlStatus.className = "repository-url-status";
    }, 5000);
  }

  async function deleteAllData() {
    try {
      // Clear all storage data
      await browser.storage.local.clear();

      // Show confirmation message
      alert(
        "All data has been deleted successfully. The page will now reload."
      );

      // Reload the page to show empty state
      window.location.reload();
    } catch (error) {
      console.error("Error deleting data:", error);
      alert("An error occurred while trying to delete data: " + error.message);
    }
  }

  async function saveTransparencySettings(isDisabled) {
    try {
      const data = await browser.storage.local.get(BROWSER_STORAGE_KEY);
      const settings = data[BROWSER_STORAGE_KEY] || {};

      // Update the disableTransparency setting
      settings.disableTransparency = isDisabled;

      await browser.storage.local.set({ [BROWSER_STORAGE_KEY]: settings });
      alert(
        `Transparency has been ${
          isDisabled ? "disabled" : "enabled"
        } globally. This will affect all websites.`
      );
    } catch (error) {
      console.error("Error saving transparency settings:", error);
      alert(
        "An error occurred while saving the transparency setting: " +
          error.message
      );
    }
  }

  // New functions to save hover and footer settings
  async function saveHoverSettings(isDisabled) {
    try {
      const data = await browser.storage.local.get(BROWSER_STORAGE_KEY);
      const settings = data[BROWSER_STORAGE_KEY] || {};

      // Update the disableHover setting
      settings.disableHover = isDisabled;

      await browser.storage.local.set({ [BROWSER_STORAGE_KEY]: settings });
      alert(
        `Hover effects have been ${
          isDisabled ? "disabled" : "enabled"
        } globally. This will affect all websites.`
      );
    } catch (error) {
      console.error("Error saving hover settings:", error);
      alert(
        "An error occurred while saving the hover effects setting: " +
          error.message
      );
    }
  }

  async function saveFooterSettings(isDisabled) {
    try {
      const data = await browser.storage.local.get(BROWSER_STORAGE_KEY);
      const settings = data[BROWSER_STORAGE_KEY] || {};

      // Update the disableFooter setting
      settings.disableFooter = isDisabled;

      await browser.storage.local.set({ [BROWSER_STORAGE_KEY]: settings });
      alert(
        `Footers have been ${
          isDisabled ? "hidden" : "shown"
        } globally. This will affect all websites.`
      );
    } catch (error) {
      console.error("Error saving footer settings:", error);
      alert(
        "An error occurred while saving the footer setting: " + error.message
      );
    }
  }

  // Export settings functionality
  async function exportSettings() {
    try {
      // Retrieve all storage data to find site-specific settings
      const allData = await browser.storage.local.get(null);

      // Extract only the settings we want to backup
      const settingsToBackup = {
        [BROWSER_STORAGE_KEY]: allData[BROWSER_STORAGE_KEY] || {},
        [SKIP_FORCE_THEMING_KEY]: allData[SKIP_FORCE_THEMING_KEY] || [],
        [SKIP_THEMING_KEY]: allData[SKIP_THEMING_KEY] || [],
        [REPOSITORY_URL_KEY]:
          allData[REPOSITORY_URL_KEY] || DEFAULT_REPOSITORY_URL,
      };

      // Also extract site-specific settings (keys that start with BROWSER_STORAGE_KEY.)
      const siteSpecificSettings = {};
      for (const [key, value] of Object.entries(allData)) {
        if (key.startsWith(BROWSER_STORAGE_KEY + ".")) {
          siteSpecificSettings[key] = value;
        }
      }

      // Add export timestamp and version
      const manifest = browser.runtime.getManifest();
      const exportData = {
        exportDate: new Date().toISOString(),
        addonVersion: manifest.version,
        settings: settingsToBackup,
        siteSettings: siteSpecificSettings,
      };

      // Convert to JSON
      const jsonData = JSON.stringify(exportData, null, 2);

      // Create a blob and download link
      const blob = new Blob([jsonData], { type: "application/json" });
      const url = URL.createObjectURL(blob);

      // Create a temporary anchor and trigger download
      const a = document.createElement("a");
      a.href = url;
      a.download = `zen-internet-settings-${
        new Date().toISOString().split("T")[0]
      }.json`;
      document.body.appendChild(a);
      a.click();

      // Cleanup
      setTimeout(() => {
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
      }, 0);

      // Show success message
      showImportStatus("Settings exported successfully!", "success");
    } catch (error) {
      console.error("Error exporting settings:", error);
      showImportStatus(`Export failed: ${error.message}`, "error");
    }
  }

  // Import settings functionality
  async function importSettings(event) {
    try {
      const file = event.target.files[0];
      if (!file) return;

      const reader = new FileReader();

      reader.onload = async (e) => {
        try {
          const importData = JSON.parse(e.target.result);

          // Validate the imported data structure
          if (
            !importData.settings ||
            !importData.settings[BROWSER_STORAGE_KEY]
          ) {
            throw new Error("Invalid settings file format");
          }

          // Confirm the import
          if (
            confirm(
              `Are you sure you want to import settings from ${importData.exportDate}? This will overwrite your current settings.`
            )
          ) {
            // First store the global settings, lists, and repository URL
            const importOperations = {
              [BROWSER_STORAGE_KEY]: importData.settings[BROWSER_STORAGE_KEY],
              [SKIP_FORCE_THEMING_KEY]:
                importData.settings[SKIP_FORCE_THEMING_KEY] || [],
              [SKIP_THEMING_KEY]: importData.settings[SKIP_THEMING_KEY] || [],
              [REPOSITORY_URL_KEY]:
                importData.settings[REPOSITORY_URL_KEY] ||
                DEFAULT_REPOSITORY_URL,
            };

            // Then add any site-specific settings if they exist
            if (importData.siteSettings) {
              for (const [key, value] of Object.entries(
                importData.siteSettings
              )) {
                importOperations[key] = value;
              }
            }

            // Apply all settings at once
            await browser.storage.local.set(importOperations);

            showImportStatus(
              "Settings imported successfully! Reloading...",
              "success"
            );

            // Reload the page after a short delay
            setTimeout(() => {
              window.location.reload();
            }, 1500);
          } else {
            // User cancelled
            importFileInput.value = "";
            showImportStatus("Import cancelled", "error");
          }
        } catch (parseError) {
          console.error("Error parsing import file:", parseError);
          showImportStatus(`Import failed: ${parseError.message}`, "error");
        }
      };

      reader.readAsText(file);
    } catch (error) {
      console.error("Error handling import:", error);
      showImportStatus(`Import failed: ${error.message}`, "error");
    }
  }

  // Helper function to show import status messages
  function showImportStatus(message, type) {
    importStatusElement.textContent = message;
    importStatusElement.className = `import-status status-${type}`;

    // Clear the message after 5 seconds
    setTimeout(() => {
      importStatusElement.textContent = "";
      importStatusElement.className = "import-status";
    }, 5000);
  }

  async function displayAddonVersion() {
    const manifest = browser.runtime.getManifest();
    versionElement.textContent = `Version: ${manifest.version}`;
  }

  async function loadAllData() {
    try {
      // Load all data from storage
      const data = await browser.storage.local.get(null);

      // Display global settings
      const globalSettings = data[BROWSER_STORAGE_KEY] || {};
      displayGlobalSettings(globalSettings);

      // Set the toggle states
      disableTransparencyToggle.checked =
        globalSettings.disableTransparency || false;
      // Set new toggle states
      disableHoverToggle.checked = globalSettings.disableHover || false;
      disableFooterToggle.checked = globalSettings.disableFooter || false;

      // Display skip/enable lists for both forced and non-forced websites
      const skipForceList = data[SKIP_FORCE_THEMING_KEY] || [];
      const skipThemingList = data[SKIP_THEMING_KEY] || [];
      displayCombinedSkipLists(
        skipForceList,
        skipThemingList,
        globalSettings.whitelistMode,
        globalSettings.whitelistStyleMode
      );

      // Display combined websites and settings
      displayCombinedWebsiteData(data);

      console.info("Data loaded successfully");
    } catch (error) {
      console.error("Error loading data:", error);
    }
  }

  function displayGlobalSettings(settings) {
    globalSettingsElement.innerHTML = "";

    const table = document.createElement("table");
    table.classList.add("data-table");

    // Table header
    const thead = document.createElement("thead");
    const headerRow = document.createElement("tr");
    headerRow.innerHTML = `<th>Setting</th><th>Value</th>`;
    thead.appendChild(headerRow);
    table.appendChild(thead);

    // Table body
    const tbody = document.createElement("tbody");

    for (const [key, value] of Object.entries(settings)) {
      // Skip lastFetchedTime as it will be formatted differently
      if (key === "lastFetchedTime") continue;

      const row = document.createElement("tr");
      row.innerHTML = `
        <td>${formatSettingName(key)}</td>
        <td>${formatSettingValue(value)}</td>
      `;
      tbody.appendChild(row);
    }

    // Add last fetched time with formatted date if available
    if (settings.lastFetchedTime) {
      const row = document.createElement("tr");
      row.innerHTML = `
        <td>${formatSettingName("lastFetchedTime")}</td>
        <td>${new Date(settings.lastFetchedTime).toLocaleString()}</td>
      `;
      tbody.appendChild(row);
    }

    table.appendChild(tbody);
    globalSettingsElement.appendChild(table);
  }

  function displayCombinedSkipLists(
    skipForceList,
    skipThemingList,
    isWhitelistMode,
    isWhitelistStyleMode
  ) {
    skipListElement.innerHTML = "";

    // Create title and description section
    const titleSection = document.createElement("div");
    titleSection.className = "list-title-section";

    const forceModeName = isWhitelistMode ? "Whitelist" : "Blacklist";
    const styleModeName = isWhitelistStyleMode ? "Whitelist" : "Blacklist";

    titleSection.innerHTML = `
      <h3>Website Lists Overview</h3>
      <div class="mode-info">
        <div><strong>Force Styling Mode:</strong> ${forceModeName} Mode (${
      isWhitelistMode
        ? "only apply to sites in the list"
        : "apply to all except sites in the list"
    })</div>
        <div><strong>General Styling Mode:</strong> ${styleModeName} Mode (${
      isWhitelistStyleMode
        ? "only apply to sites in the list"
        : "apply to all except sites in the list"
    })</div>
      </div>
    `;

    skipListElement.appendChild(titleSection);

    // Add Clear Both Lists button
    if (skipForceList.length > 0 || skipThemingList.length > 0) {
      const clearListButton = document.createElement("button");
      clearListButton.classList.add(
        "action-button",
        "secondary",
        "clear-list-button"
      );
      clearListButton.innerHTML =
        '<i class="fas fa-trash"></i> Clear All Website Lists';
      clearListButton.addEventListener("click", function () {
        if (
          confirm(
            `Are you sure you want to clear ALL website lists? This will reset both Forced and Regular styling lists and may affect how styling is applied to websites.`
          )
        ) {
          clearAllSkipLists();
        }
      });
      skipListElement.appendChild(clearListButton);
    }

    // Create container for the two tables
    const tablesContainer = document.createElement("div");
    tablesContainer.className = "tables-container";

    // Create force styling list
    const forceListSection = createSingleListSection(
      skipForceList,
      isWhitelistMode,
      "Force Styling List",
      isWhitelistMode
        ? "Sites where forced styling IS applied"
        : "Sites where forced styling is NOT applied",
      SKIP_FORCE_THEMING_KEY
    );

    // Create regular styling list
    const regularListSection = createSingleListSection(
      skipThemingList,
      isWhitelistStyleMode,
      "Regular Styling List",
      isWhitelistStyleMode
        ? "Sites where regular styling IS applied"
        : "Sites where regular styling is NOT applied",
      SKIP_THEMING_KEY
    );

    tablesContainer.appendChild(forceListSection);
    tablesContainer.appendChild(regularListSection);
    skipListElement.appendChild(tablesContainer);
  }

  function createSingleListSection(
    list,
    isWhitelistMode,
    title,
    description,
    storageKey
  ) {
    const section = document.createElement("div");
    section.className = "list-section";

    const sectionTitle = document.createElement("h4");
    sectionTitle.textContent = title;
    section.appendChild(sectionTitle);

    const sectionDescription = document.createElement("p");
    sectionDescription.className = "list-description";
    sectionDescription.textContent = description;
    section.appendChild(sectionDescription);

    if (list.length === 0) {
      const emptyMessage = document.createElement("div");
      emptyMessage.className = "no-data";
      emptyMessage.textContent = "No websites in this list";
      section.appendChild(emptyMessage);
      return section;
    }

    const table = document.createElement("table");
    table.classList.add("data-table");

    // Table header
    const thead = document.createElement("thead");
    const headerRow = document.createElement("tr");
    headerRow.innerHTML = `<th>Website</th><th>Action</th>`;
    thead.appendChild(headerRow);
    table.appendChild(thead);

    // Table body
    const tbody = document.createElement("tbody");

    for (const site of list) {
      const row = document.createElement("tr");

      // Website column
      const siteCell = document.createElement("td");
      siteCell.textContent = site;
      row.appendChild(siteCell);

      // Action column
      const actionCell = document.createElement("td");
      const removeButton = document.createElement("button");
      removeButton.className = "remove-site-button";
      removeButton.innerHTML = '<i class="fas fa-times"></i>';
      removeButton.title = "Remove from list";
      removeButton.addEventListener("click", function () {
        removeSiteFromList(site, storageKey);
      });
      actionCell.appendChild(removeButton);
      row.appendChild(actionCell);

      tbody.appendChild(row);
    }

    table.appendChild(tbody);
    section.appendChild(table);
    return section;
  }

  async function removeSiteFromList(site, listKey) {
    try {
      // Get current list
      const data = await browser.storage.local.get(listKey);
      const list = data[listKey] || [];

      // Remove the site
      const newList = list.filter((item) => item !== site);

      // Save updated list
      await browser.storage.local.set({ [listKey]: newList });

      // Refresh the display
      loadAllData();

      console.log(`Removed ${site} from ${listKey}`);
    } catch (error) {
      console.error(`Error removing site from list: ${error}`);
      alert(`An error occurred: ${error.message}`);
    }
  }

  async function clearAllSkipLists() {
    try {
      // Clear both lists at once
      await browser.storage.local.remove([
        SKIP_FORCE_THEMING_KEY,
        SKIP_THEMING_KEY,
      ]);
      alert("All website lists have been cleared successfully.");
      loadAllData(); // Reload data to reflect changes
    } catch (error) {
      console.error("Error clearing lists:", error);
      alert(
        "An error occurred while trying to clear the lists: " + error.message
      );
    }
  }

  function displayCombinedWebsiteData(data) {
    combinedWebsitesElement.innerHTML = "";

    const styles = data.styles || {};
    const websites = styles.website || {};
    const websiteKeys = Object.keys(websites);

    // Find all site-specific settings
    const siteSettings = {};
    for (const [key, value] of Object.entries(data)) {
      if (key.startsWith(BROWSER_STORAGE_KEY + ".")) {
        const siteName = key.substring(BROWSER_STORAGE_KEY.length + 1);
        siteSettings[siteName] = value;
      }
    }

    if (websiteKeys.length === 0) {
      combinedWebsitesElement.innerHTML =
        '<div class="no-data">No websites found. Try fetching styles first.</div>';
      return;
    }

    // Create search filter
    const searchContainer = document.createElement("div");
    searchContainer.classList.add("search-container");

    const searchInput = document.createElement("input");
    searchInput.type = "text";
    searchInput.placeholder = "Search websites...";
    searchInput.classList.add("search-input");
    searchInput.addEventListener("input", function () {
      filterWebsites(this.value.toLowerCase());
    });

    const searchIcon = document.createElement("i");
    searchIcon.className = "fas fa-search search-icon";

    searchContainer.appendChild(searchIcon);
    searchContainer.appendChild(searchInput);

    combinedWebsitesElement.appendChild(searchContainer);

    // Create expand all button
    const expandAllButton = document.createElement("button");
    expandAllButton.classList.add(
      "action-button",
      "secondary",
      "view-all-button"
    );
    expandAllButton.textContent = "Expand All";
    expandAllButton.addEventListener("click", function () {
      const expanded = this.textContent === "Collapse All";
      const panels = document.querySelectorAll(".website-panel");

      panels.forEach((panel) => {
        const header = panel.querySelector(".website-header");
        const content = panel.querySelector(".website-content");

        if (expanded) {
          header.classList.remove("active");
          content.style.maxHeight = null;

          // Also collapse all CSS blocks
          content.querySelectorAll(".css-block-header").forEach((cssHeader) => {
            cssHeader.classList.remove("active");
            const cssContent = cssHeader.nextElementSibling;
            if (cssContent) cssContent.style.maxHeight = null;
          });
        } else {
          header.classList.add("active");
          content.style.maxHeight = content.scrollHeight + "px";
        }
      });

      this.textContent = expanded ? "Expand All" : "Collapse All";
    });

    combinedWebsitesElement.appendChild(expandAllButton);

    const websitesContainer = document.createElement("div");
    websitesContainer.classList.add("websites-container");
    combinedWebsitesElement.appendChild(websitesContainer);

    // Sort websites alphabetically
    websiteKeys.sort();

    // Create panels for each website
    for (const website of websiteKeys) {
      const websitePanel = document.createElement("div");
      websitePanel.classList.add("website-panel");
      websitePanel.dataset.website = website.toLowerCase();

      const header = document.createElement("div");
      header.classList.add("website-header");

      // Create website name with feature count
      const features = websites[website];
      const featureCount = Object.keys(features).length;

      // Get site settings if available
      const siteName = website.replace(".css", "");
      let domainName;
      // Declare settingsData at a higher scope so it's accessible throughout the function
      let settingsData = {};

      // Handle wildcard sites correctly
      if (siteName.startsWith("+")) {
        domainName = siteName.slice(1);
        // For wildcard sites, we need to find any matching domain in settings
        const matchingDomains = Object.keys(siteSettings).filter(
          (domain) => domain === domainName || domain.endsWith(`.${domainName}`)
        );

        // Use the first matching domain's settings if any found
        const settingsKey =
          matchingDomains.length > 0 ? matchingDomains[0] : null;
        settingsData = settingsKey ? siteSettings[settingsKey] : {};
      } else {
        // For direct domains, just check the domain and www.domain
        domainName = siteName;
        settingsData =
          siteSettings[domainName] || siteSettings[`www.${domainName}`] || {};
      }

      header.innerHTML = `
        <div class="website-header-content">
          <span class="website-name">${website}</span>
          <span class="feature-count">${featureCount} features</span>
        </div>
      `;

      header.addEventListener("click", function () {
        this.classList.toggle("active");
        const content = this.nextElementSibling;
        if (content.style.maxHeight) {
          content.style.maxHeight = null;
        } else {
          content.style.maxHeight = content.scrollHeight + "px";
        }
      });

      const content = document.createElement("div");
      content.classList.add("website-content");

      // Create CSS blocks for each feature
      for (const [feature, css] of Object.entries(features)) {
        const cssBlock = document.createElement("div");
        cssBlock.classList.add("css-block");

        // Get the feature's enabled status from site settings
        const isEnabled = settingsData[feature] !== false; // true by default

        // Create the block header with feature name and status
        const cssBlockHeader = document.createElement("div");
        cssBlockHeader.classList.add("css-block-header");
        cssBlockHeader.innerHTML = `
          <span class="feature-name">${feature}</span>
          <span class="feature-status ${isEnabled ? "enabled" : "disabled"}">${
          isEnabled ? "Enabled" : "Disabled"
        }</span>
        `;

        // Make the CSS block header toggleable
        cssBlockHeader.addEventListener("click", function (e) {
          // Don't expand if clicking on status badge
          if (e.target.classList.contains("feature-status")) return;

          this.classList.toggle("active");
          const cssContent = this.nextElementSibling;
          if (cssContent.style.maxHeight) {
            cssContent.style.maxHeight = null;
          } else {
            cssContent.style.maxHeight = cssContent.scrollHeight + "px";
          }
        });

        // Create the CSS content area
        const cssContent = document.createElement("div");
        cssContent.classList.add("css-content");

        const cssCode = document.createElement("pre");
        cssCode.classList.add("css-code");
        cssCode.textContent = css;
        cssContent.appendChild(cssCode);

        cssBlock.appendChild(cssBlockHeader);
        cssBlock.appendChild(cssContent);
        content.appendChild(cssBlock);
      }

      websitePanel.appendChild(header);
      websitePanel.appendChild(content);
      websitesContainer.appendChild(websitePanel);
    }

    // Filter function for search
    function filterWebsites(query) {
      const panels = websitesContainer.querySelectorAll(".website-panel");

      panels.forEach((panel) => {
        const website = panel.dataset.website;
        if (website.includes(query)) {
          panel.style.display = "";
        } else {
          panel.style.display = "none";
        }
      });
    }
  }

  // Helper Functions
  function formatSettingName(name) {
    // Convert camelCase to Title Case with spaces
    return name
      .replace(/([A-Z])/g, " $1")
      .replace(/^./, (str) => str.toUpperCase());
  }

  function formatSettingValue(value) {
    if (typeof value === "boolean") {
      return value
        ? '<span class="badge enabled">Enabled</span>'
        : '<span class="badge disabled">Disabled</span>';
    } else if (value === null) {
      return '<span class="null-value">null</span>';
    } else if (typeof value === "object") {
      return '<span class="object-value">{Object}</span>';
    } else {
      return value;
    }
  }
});