import {getById, writeError, addClass, writeDebug, writeInfo, h, span} from "/common.module.js"; /** @member {IDBDatabase} db */ let db; function InitDB(upgradeCallback) { writeDebug(`db = ${db?.version}`) if (db) { writeDebug("Closing database"); db.close(); } writeDebug(`db = ${db?.version}`) const DBOpenRequest = db === null || db === undefined ? window.indexedDB.open('mb-storage') : window.indexedDB.open('mb-storage', db.version + 1); DBOpenRequest.onupgradeneeded = (e) => { writeDebug(`Upgrading from ${e.oldVersion} to ${e.newVersion}`); upgradeCallback(e); }; DBOpenRequest.onerror = () => { writeError('Error loading database.'); }; DBOpenRequest.onsuccess = () => { db = DBOpenRequest.result; writeDebug(`db = ${db.version}`) writeDebug("Database opened successfully."); db.onversionchange = () => { writeInfo(`Version changed in another tab, closing database.`); } showStores(); }; DBOpenRequest.onblocked = () => { writeError(`Please close all other tabs with this site open!`); } } export function onLoad() { try { InitDB(null); const newStoreButton = getById('newStoreButton'); newStoreButton.addEventListener('click', async () => { const newStoreName = await getInput("New store", "Store name"); if (newStoreName) { writeDebug("New store submitted."); InitDB((event) => { writeDebug(`Creating new store: ${newStoreName}, version: ${event.newVersion}`); event.target.result.createObjectStore(newStoreName, {autoIncrement: true}); writeDebug("New store created: " + newStoreName); }); } }); } catch (e) { writeError(e.message); throw e; } } function showStores() { const storeList = getById("storeList"); storeList.replaceChildren(); let storeNames = db.objectStoreNames; for (const storeName of storeNames) { const deleteButton = h("button", "Delete"); const openButton = h("button", "Open"); openButton.addEventListener('click', () => { if (openButton.textContent === "Open") { openButton.closeCallback = openStore(storeName, deleteButton); openButton.textContent = "Close"; } else { openButton.closeCallback(); openButton.textContent = "Open"; openButton.closeCallback = null; } }) deleteButton.addEventListener('click', async () => { if (await askForConfirmation(`Are you sure you want to delete store '${storeName}'?`)) { InitDB((event) => { writeDebug(`Deleting store: ${storeName}, version: ${event.newVersion}`); event.target.result.deleteObjectStore(storeName); writeDebug("Deleted store: " + storeName); }); if (openButton.closeCallback) openButton.closeCallback(); } }) const storeElement = span(storeName); storeList.appendChild(storeElement); storeList.appendChild(openButton); storeList.appendChild(deleteButton); } } function openStore(storeName) { const openedStores = getById("openedStores"); const addButton = h("button", "Add"); const storeElement = addClass(h("fieldset", h("legend", storeName + " ", addButton)), "docs"); const closeStore = () => { openedStores.removeChild(storeElement); }; openedStores.appendChild(storeElement); const elementList = addClass(h("div"), "grid-stores"); storeElement.appendChild(elementList); function showItems() { /** @member {Node[]} newChildren*/ const newChildren = []; const store = db.transaction(storeName).objectStore(storeName); store.openCursor().onsuccess = (event) => { /** @member {IDBCursorWithValue} cursor*/ const cursor = event.target.result; if (!cursor) { elementList.replaceChildren(...newChildren); return; } const value = cursor.value; const key = cursor.key; const editItemButton = h("button", "Edit"); const deleteItemButton = h("button", "Delete"); deleteItemButton.addEventListener('click', () => { const transaction = db.transaction(storeName, 'readwrite'); transaction.oncomplete = () => { showItems(); } const store = transaction.objectStore(storeName); store.delete(key); }) editItemButton.addEventListener('click', async () => { const value = await getInput("Edit value", "new value"); if (value) { const transaction = db.transaction(storeName, 'readwrite'); transaction.oncomplete = () => { showItems(); } const store = transaction.objectStore(storeName); store.put(value, key); } }) newChildren.push(span(value)); newChildren.push(editItemButton); newChildren.push(deleteItemButton); cursor.continue(); } } showItems(); addButton.addEventListener('click', async () => { const value = await getInput("New value", "value"); if (value) { const transaction = db.transaction(storeName, "readwrite"); transaction.oncomplete = () => { showItems(); } const store = transaction.objectStore(storeName); store.add(value); } }) return closeStore; } /** * @param {string} title * @param {string} placeholder * @return {Promise} */ function getInput(title, placeholder) { return new Promise((resolve) => { const inputDialog = getById("inputDialog"); const cancelInputButton = getById('cancelInputButton'); const inputDialogInput = getById('inputDialogInput'); const inputForm = getById('inputForm'); inputDialogInput.placeholder = placeholder; getById("inputTitle").textContent = title; inputDialog.showModal(); const cancelCallback = () => { inputDialog.close("cancelInput"); cancelInputButton.removeEventListener("click", cancelCallback); inputForm.removeEventListener("submit", submitCallback); resolve(null); }; cancelInputButton.addEventListener('click', cancelCallback); const submitCallback = () => { const inputValue = inputDialogInput.value; inputDialogInput.value = ''; inputForm.removeEventListener("submit", submitCallback); cancelInputButton.removeEventListener("click", cancelCallback); resolve(inputValue); }; inputForm.addEventListener('submit', submitCallback); }) } function askForConfirmation(question) { return new Promise((resolve) => { const confirmDialog = getById("confirmDialog"); getById("confirmMessage").textContent = question; confirmDialog.showModal(); const cancelConfirmButton = getById("cancelConfirmButton"); const closeCancelled = () => { confirmDialog.close("cancelConfirmation"); resolve(false) }; cancelConfirmButton.addEventListener('click', closeCancelled); const confirmForm = getById("confirmForm"); const submitConfirmation = () => { cancelConfirmButton.removeEventListener('click', closeCancelled); confirmForm.removeEventListener('submit', submitConfirmation); resolve(true); }; confirmForm.addEventListener('submit', submitConfirmation); }) }