| @@ -0,0 +1,242 @@ |
|
1 |
+import {getById, writeError, addClass, writeDebug, writeInfo, h, span} from "/common.module.js"; |
|
2 |
+ |
|
3 |
+ |
|
4 |
+let db; |
|
5 |
+ |
|
6 |
+function InitDB(upgradeCallback) { |
|
7 |
+ writeDebug(`db = ${db?.version}`) |
|
8 |
+ if (db) { |
|
9 |
+ writeDebug("Closing database"); |
|
10 |
+ db.close(); |
|
11 |
+ } |
|
12 |
+ writeDebug(`db = ${db?.version}`) |
|
13 |
+ const DBOpenRequest = db === null || db === undefined |
|
14 |
+ ? window.indexedDB.open('mb-storage') |
|
15 |
+ : window.indexedDB.open('mb-storage', db.version + 1); |
|
16 |
+ |
|
17 |
+ DBOpenRequest.onupgradeneeded = (e) => { |
|
18 |
+ writeDebug(`Upgrading from ${e.oldVersion} to ${e.newVersion}`); |
|
19 |
+ upgradeCallback(e); |
|
20 |
+ }; |
|
21 |
+ |
|
22 |
+ DBOpenRequest.onerror = () => { |
|
23 |
+ writeError('Error loading database.'); |
|
24 |
+ }; |
|
25 |
+ |
|
26 |
+ DBOpenRequest.onsuccess = () => { |
|
27 |
+ db = DBOpenRequest.result; |
|
28 |
+ writeDebug(`db = ${db.version}`) |
|
29 |
+ writeDebug("Database opened successfully."); |
|
30 |
+ db.onversionchange = () => { |
|
31 |
+ writeInfo(`Version changed in another tab, closing database.`); |
|
32 |
+ } |
|
33 |
+ showStores(); |
|
34 |
+ }; |
|
35 |
+ |
|
36 |
+ DBOpenRequest.onblocked = () => { |
|
37 |
+ writeError(`Please close all other tabs with this site open!`); |
|
38 |
+ } |
|
39 |
+ |
|
40 |
+} |
|
41 |
+ |
|
42 |
+export function onLoad() { |
|
43 |
+ try { |
|
44 |
+ InitDB(null); |
|
45 |
+ |
|
46 |
+ const newStoreButton = getById('newStoreButton'); |
|
47 |
+ newStoreButton.addEventListener('click', async () => { |
|
48 |
+ const newStoreName = await getInput("New store", "Store name"); |
|
49 |
+ |
|
50 |
+ if (newStoreName) { |
|
51 |
+ writeDebug("New store submitted."); |
|
52 |
+ InitDB((event) => { |
|
53 |
+ writeDebug(`Creating new store: ${newStoreName}, version: ${event.newVersion}`); |
|
54 |
+ event.target.result.createObjectStore(newStoreName, {autoIncrement: true}); |
|
55 |
+ writeDebug("New store created: " + newStoreName); |
|
56 |
+ }); |
|
57 |
+ } |
|
58 |
+ }); |
|
59 |
+ |
|
60 |
+ } catch (e) { |
|
61 |
+ writeError(e.message); |
|
62 |
+ throw e; |
|
63 |
+ } |
|
64 |
+} |
|
65 |
+ |
|
66 |
+function showStores() { |
|
67 |
+ const storeList = getById("storeList"); |
|
68 |
+ storeList.replaceChildren(); |
|
69 |
+ |
|
70 |
+ let storeNames = db.objectStoreNames; |
|
71 |
+ |
|
72 |
+ for (const storeName of storeNames) { |
|
73 |
+ const deleteButton = h("button", "Delete"); |
|
74 |
+ |
|
75 |
+ const openButton = h("button", "Open"); |
|
76 |
+ openButton.addEventListener('click', () => { |
|
77 |
+ if (openButton.textContent === "Open") { |
|
78 |
+ openButton.closeCallback = openStore(storeName, deleteButton); |
|
79 |
+ openButton.textContent = "Close"; |
|
80 |
+ } else { |
|
81 |
+ openButton.closeCallback(); |
|
82 |
+ openButton.textContent = "Open"; |
|
83 |
+ openButton.closeCallback = null; |
|
84 |
+ } |
|
85 |
+ }) |
|
86 |
+ |
|
87 |
+ deleteButton.addEventListener('click', async () => { |
|
88 |
+ if (await askForConfirmation(`Are you sure you want to delete store '${storeName}'?`)) { |
|
89 |
+ InitDB((event) => { |
|
90 |
+ writeDebug(`Deleting store: ${storeName}, version: ${event.newVersion}`); |
|
91 |
+ event.target.result.deleteObjectStore(storeName); |
|
92 |
+ writeDebug("Deleted store: " + storeName); |
|
93 |
+ }); |
|
94 |
+ |
|
95 |
+ if (openButton.closeCallback) openButton.closeCallback(); |
|
96 |
+ } |
|
97 |
+ }) |
|
98 |
+ |
|
99 |
+ const storeElement = span(storeName); |
|
100 |
+ storeList.appendChild(storeElement); |
|
101 |
+ storeList.appendChild(openButton); |
|
102 |
+ storeList.appendChild(deleteButton); |
|
103 |
+ } |
|
104 |
+} |
|
105 |
+ |
|
106 |
+function openStore(storeName) { |
|
107 |
+ const openedStores = getById("openedStores"); |
|
108 |
+ |
|
109 |
+ const addButton = h("button", "Add"); |
|
110 |
+ const storeElement = addClass(h("fieldset", h("legend", storeName + " ", addButton)), "docs"); |
|
111 |
+ const closeStore = () => { |
|
112 |
+ openedStores.removeChild(storeElement); |
|
113 |
+ }; |
|
114 |
+ |
|
115 |
+ openedStores.appendChild(storeElement); |
|
116 |
+ const elementList = addClass(h("div"), "grid-stores"); |
|
117 |
+ storeElement.appendChild(elementList); |
|
118 |
+ |
|
119 |
+ function showItems() { |
|
120 |
+ |
|
121 |
+ |
|
122 |
+ const newChildren = []; |
|
123 |
+ const store = db.transaction(storeName).objectStore(storeName); |
|
124 |
+ store.openCursor().onsuccess = (event) => { |
|
125 |
+ |
|
126 |
+ const cursor = event.target.result; |
|
127 |
+ if (!cursor) { |
|
128 |
+ elementList.replaceChildren(...newChildren); |
|
129 |
+ return; |
|
130 |
+ } |
|
131 |
+ const value = cursor.value; |
|
132 |
+ const key = cursor.key; |
|
133 |
+ const editItemButton = h("button", "Edit"); |
|
134 |
+ |
|
135 |
+ const deleteItemButton = h("button", "Delete"); |
|
136 |
+ deleteItemButton.addEventListener('click', () => { |
|
137 |
+ const transaction = db.transaction(storeName, 'readwrite'); |
|
138 |
+ transaction.oncomplete = () => { |
|
139 |
+ showItems(); |
|
140 |
+ } |
|
141 |
+ const store = transaction.objectStore(storeName); |
|
142 |
+ store.delete(key); |
|
143 |
+ }) |
|
144 |
+ |
|
145 |
+ editItemButton.addEventListener('click', async () => { |
|
146 |
+ const value = await getInput("Edit value", "new value"); |
|
147 |
+ |
|
148 |
+ if (value) { |
|
149 |
+ const transaction = db.transaction(storeName, 'readwrite'); |
|
150 |
+ transaction.oncomplete = () => { |
|
151 |
+ showItems(); |
|
152 |
+ } |
|
153 |
+ const store = transaction.objectStore(storeName); |
|
154 |
+ store.put(value, key); |
|
155 |
+ } |
|
156 |
+ }) |
|
157 |
+ |
|
158 |
+ newChildren.push(span(value)); |
|
159 |
+ newChildren.push(editItemButton); |
|
160 |
+ newChildren.push(deleteItemButton); |
|
161 |
+ |
|
162 |
+ cursor.continue(); |
|
163 |
+ } |
|
164 |
+ } |
|
165 |
+ |
|
166 |
+ showItems(); |
|
167 |
+ |
|
168 |
+ addButton.addEventListener('click', async () => { |
|
169 |
+ const value = await getInput("New value", "value"); |
|
170 |
+ if (value) { |
|
171 |
+ const transaction = db.transaction(storeName, "readwrite"); |
|
172 |
+ transaction.oncomplete = () => { |
|
173 |
+ showItems(); |
|
174 |
+ } |
|
175 |
+ const store = transaction.objectStore(storeName); |
|
176 |
+ store.add(value); |
|
177 |
+ } |
|
178 |
+ }) |
|
179 |
+ |
|
180 |
+ return closeStore; |
|
181 |
+} |
|
182 |
+ |
|
183 |
+ |
|
184 |
+ |
|
185 |
+ |
|
186 |
+ |
|
187 |
+ |
|
188 |
+function getInput(title, placeholder) { |
|
189 |
+ return new Promise((resolve) => { |
|
190 |
+ const inputDialog = getById("inputDialog"); |
|
191 |
+ const cancelInputButton = getById('cancelInputButton'); |
|
192 |
+ const inputDialogInput = getById('inputDialogInput'); |
|
193 |
+ const inputForm = getById('inputForm'); |
|
194 |
+ |
|
195 |
+ inputDialogInput.placeholder = placeholder; |
|
196 |
+ getById("inputTitle").textContent = title; |
|
197 |
+ |
|
198 |
+ inputDialog.showModal(); |
|
199 |
+ |
|
200 |
+ const cancelCallback = () => { |
|
201 |
+ inputDialog.close("cancelInput"); |
|
202 |
+ cancelInputButton.removeEventListener("click", cancelCallback); |
|
203 |
+ inputForm.removeEventListener("submit", submitCallback); |
|
204 |
+ resolve(null); |
|
205 |
+ }; |
|
206 |
+ |
|
207 |
+ cancelInputButton.addEventListener('click', cancelCallback); |
|
208 |
+ |
|
209 |
+ const submitCallback = () => { |
|
210 |
+ const inputValue = inputDialogInput.value; |
|
211 |
+ inputDialogInput.value = ''; |
|
212 |
+ inputForm.removeEventListener("submit", submitCallback); |
|
213 |
+ cancelInputButton.removeEventListener("click", cancelCallback); |
|
214 |
+ resolve(inputValue); |
|
215 |
+ }; |
|
216 |
+ |
|
217 |
+ inputForm.addEventListener('submit', submitCallback); |
|
218 |
+ }) |
|
219 |
+} |
|
220 |
+ |
|
221 |
+function askForConfirmation(question) { |
|
222 |
+ return new Promise((resolve) => { |
|
223 |
+ const confirmDialog = getById("confirmDialog"); |
|
224 |
+ getById("confirmMessage").textContent = question; |
|
225 |
+ confirmDialog.showModal(); |
|
226 |
+ |
|
227 |
+ const cancelConfirmButton = getById("cancelConfirmButton"); |
|
228 |
+ const closeCancelled = () => { |
|
229 |
+ confirmDialog.close("cancelConfirmation"); |
|
230 |
+ resolve(false) |
|
231 |
+ }; |
|
232 |
+ cancelConfirmButton.addEventListener('click', closeCancelled); |
|
233 |
+ |
|
234 |
+ const confirmForm = getById("confirmForm"); |
|
235 |
+ const submitConfirmation = () => { |
|
236 |
+ cancelConfirmButton.removeEventListener('click', closeCancelled); |
|
237 |
+ confirmForm.removeEventListener('submit', submitConfirmation); |
|
238 |
+ resolve(true); |
|
239 |
+ }; |
|
240 |
+ confirmForm.addEventListener('submit', submitConfirmation); |
|
241 |
+ }) |
|
242 |
+} |
| No newline at end of file |