diff options
| author | Jakob L. Kreuze <zerodaysfordays@sdf.org> | 2025-11-26 15:32:10 -0500 |
|---|---|---|
| committer | Jakob L. Kreuze <zerodaysfordays@sdf.org> | 2025-11-26 15:32:10 -0500 |
| commit | 5d38e8c780a38a3344c43be4fff5c717fcf8a736 (patch) | |
| tree | e7598e8713609eada38d2d84479d545dabc13795 /card-editor.js | |
| parent | 5dd9489868d24140d4006cc80edf3a3e16de8290 (diff) | |
Document deck editor
Diffstat (limited to 'card-editor.js')
| -rw-r--r-- | card-editor.js | 185 |
1 files changed, 0 insertions, 185 deletions
diff --git a/card-editor.js b/card-editor.js deleted file mode 100644 index b60185e..0000000 --- a/card-editor.js +++ /dev/null @@ -1,185 +0,0 @@ -/* Copyright © 2025 Jakob L. Kreuze - - This file is part of SRS Anywhere. - - SRS Anywhere is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation, either version 3 of the - License, or (at your option) any later version. - - SRS Anywhere is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public - License along with SRS Anywhere. If not, see <https://www.gnu.org/licenses/>. */ - -let data = []; -let fieldKeys = {}; - -function loadJSON(text) { - try { - const parsed = JSON.parse(text); - if (!Array.isArray(parsed)) throw new Error('JSON must be an array of objects'); - data = parsed.map(obj => ({...obj})); - computeFieldKeys(); - renderTable(); - } catch (e) { - alert('Error parsing JSON: ' + e.message); - } -} - -function partition(arr, key = 'type') { - return arr.reduce((acc, item) => { - const k = item[key] || 'default'; - const prop = String(k); - if (!acc[prop]) acc[prop] = []; - acc[prop].push(item); - return acc; - }, {}); -} - -function computeFieldKeys() { - const partitioned = partition(data); - Object.entries(partitioned).forEach(([type, cards]) => { - const keys = new Set(); - cards.forEach(card => { - Object.keys(card).forEach(k => { - if (!k.startsWith('_')) keys.add(k); - }); - }) - fieldKeys[type] = Array.from(keys); - }); -} - -function renderTable() { - const container = document.getElementById('table-container'); - container.innerHTML = ''; // clear previous - - if (!data.length) return; - - Object.entries(fieldKeys).forEach(([type, keys]) => { - const table = document.createElement('table'); - const thead = document.createElement('thead'); - const headerRow = document.createElement('tr'); - const addButton = document.createElement('button'); - - table.style = "margin-bottom: 32px;" - - // Build header - keys.forEach(k => { - const th = document.createElement('th'); - th.textContent = k; - headerRow.appendChild(th); - }); - thead.appendChild(headerRow); - table.appendChild(thead); - - // Extra header for Delete button - const delTh = document.createElement('th'); - delTh.textContent = 'Delete'; - headerRow.appendChild(delTh); - - // Build body - const tbody = document.createElement('tbody'); - data.forEach((obj, rowIndex) => { - if ((obj.type || 'default') != type) { - return; - } - const tr = document.createElement('tr'); - keys.forEach(k => { - const td = document.createElement('td'); - const input = document.createElement('textarea'); - input.rows = 2; - input.value = obj[k] !== undefined ? obj[k] : ''; - input.dataset.row = rowIndex; - input.dataset.key = k; - input.addEventListener('input', onFieldChange); - td.appendChild(input); - tr.appendChild(td); - }); - tbody.appendChild(tr); - - // Delete button column - const delTd = document.createElement('td'); - const delBtn = document.createElement('button'); - delBtn.textContent = 'x'; - delBtn.className = 'delete'; - delBtn.dataset.row = rowIndex; - delBtn.addEventListener('click', () => { - data.splice(idx, 1); - renderTable(); - }); - delTd.appendChild(delBtn); - tr.appendChild(delTd); - }); - - table.appendChild(tbody); - - addButton.innerHTML = '+'; - addButton.addEventListener('click', () => { - const newObj = {}; - keys.forEach(k => newObj[k] = ''); - newObj.id = crypto.randomUUID(); - data.push(newObj); - renderTable(); - }); - - container.appendChild(addButton); - container.appendChild(table); - }); -} - -function onFieldChange(e) { - const row = e.target.dataset.row; - const key = e.target.dataset.key; - const value = e.target.value; - data[row][key] = value; -} - -function removeEmptyStrings(obj, inPlace = false) { - if (typeof obj !== 'object' || obj === null) { - throw new TypeError('Expected an object'); - } - - // If we’re mutating, work directly on the original - const target = inPlace ? obj : {}; - - for (const key in obj) { - if (!Object.prototype.hasOwnProperty.call(obj, key)) continue; - - const value = obj[key]; - - if (value === '') { - if (!inPlace) continue; // skip adding it to the new object - delete target[key]; // remove from the original - } else { - target[key] = value; // copy non‑empty value - } - } - - return target; -} - -window.addEventListener('load', function() { - document.getElementById('cards-input').addEventListener('change', evt => { - const file = evt.target.files[0]; - if (!file) return; - const reader = new FileReader(); - reader.onload = e => loadJSON(e.target.result); - reader.readAsText(file); - }); - - document.getElementById('save-button').addEventListener('click', () => { - if (!data.length) return; - data.forEach(removeEmptyStrings); - const blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json'}); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'edited.json'; - a.click(); - URL.revokeObjectURL(url); - }); -}) |