```
// @ts-check
/**
* Reads extension data from localStorage or fetches from data.json if not present.
* Then displays the extensions in the DOM using a template.
*
* Expects the following HTML structure:
* ...
*
*/
(async function () {
'use strict';
/** @type {HTMLTemplateElement|null} */
const template = /** @type {HTMLTemplateElement|null} */ (document.getElementById('extension-template'));
/** @type {HTMLElement|null} */
const extensionTable = document.querySelector('.extensions');
if (!template || !(template instanceof HTMLTemplateElement)) {
console.error("template is not good.");
return;
}
/**
* @typedef {Object} Extension
* @property {string} logo - URL or path to the extension's logo image
* @property {string} name - Name of the extension
* @property {string} description - Description of the extension
* @property {boolean} isActive - Whether the extension is active
*/
/**
* Gets the list of extensions from localStorage or fetches from data.json.
* @returns {Promise}
*/
const getExtensions = async () => {
let extensions = localStorage.getItem("extensionData");
if (!extensions || extensions === "null") {
// read from file
try {
const response = await fetch('data.json'); // Fetch JSON file
extensions = await response.json(); // Parse JSON
localStorage.setItem("extensionData", JSON.stringify(extensions));
} catch (error) {
console.error('Error fetching JSON:', error);
extensions = "";
}
} else {
try {
extensions = JSON.parse(extensions);
} catch (e) {
console.error('Error parsing extension data from localStorage:', e);
extensions = "";
}
}
return Array.isArray(extensions) ? extensions : [];
}
/**
* Displays the list of extensions in the DOM.
* @param {Extension[]} data - Array of extension objects to display
* @returns {void}
*/
const displayExtensions = (data) => {
if (!data) {
console.error("No data to display or data is not an array.");
return;
}
for (const extension of data) {
// Clone the new row and insert it into the table
const clone = template.content.cloneNode(true);
if (!(clone instanceof DocumentFragment)) {
console.error("Clone is not a DocumentFragment.");
return;
}
/** @type {HTMLImageElement|null} */
let image = clone.querySelector("img");
if (!image) {
console.error("Image element not found in the template.");
return;
}
image.src = extension.logo;
/** @type {HTMLHeadingElement|null} */
let h2 = clone.querySelector("h2");
if (!h2) {
console.error("Heading element not found in the template.");
return;
}
h2.textContent = extension.name;
/** @type {HTMLParagraphElement|null} */
let p = clone.querySelector("p");
if (!p) {
console.error("Paragraph element not found in the template.");
return;
}
p.textContent = extension.description;
/** @type {HTMLInputElement|null} */
let active = clone.querySelector("#ms1");
if (!active || !(active instanceof HTMLInputElement)) {
console.error("Checkbox element not found in the template.");
return;
}
active.checked = extension.isActive;
if (!extensionTable) {
console.error("Extension table element not found in the document.");
return;
}
extensionTable.appendChild(clone);
}
}
const extensions = await getExtensions();
displayExtensions(extensions);
})();
```