diff options
Diffstat (limited to 'card-editor.html')
| -rw-r--r-- | card-editor.html | 226 |
1 files changed, 14 insertions, 212 deletions
diff --git a/card-editor.html b/card-editor.html index b838b59..d4fca8a 100644 --- a/card-editor.html +++ b/card-editor.html @@ -1,215 +1,17 @@ <!DOCTYPE html> <html lang="en"> -<head> -<meta charset="UTF-8"> -<title>JSON List Editor</title> -<style> - body{font-family:Arial,Helvetica,sans-serif;margin:20px;} - h1{margin-bottom:5px;} - textarea{width:100%;height:120px;font-family:monospace;} - button{margin:5px 0;} - table{border-collapse:collapse;width:100%;margin-top:10px;} - th,td{border:1px solid #aaa;padding:4px 6px;text-align:left;} - th{background:#f0f0f0;} - input[type=text]{width:100%;box-sizing:border-box;} - .hidden{display:none;} - .del-btn{background:#e74c3c;color:white;border:none;padding:4px 6px;cursor:pointer;} -</style> -</head> -<body> - -<h1>JSON List Editor</h1> - -<p>Paste or load a JSON array of objects below and click <strong>Load</strong>.</p> - -<textarea id="jsonInput" placeholder='[{"id":1,"name":"Alice","age":30},{"id":2,"name":"Bob","_secret":"xyz"}]'></textarea><br> -<button id="loadBtn">Load</button> -<button id="addBtn">New card</button> -<button id="saveBtn">Download JSON</button> -<input type="file" id="fileInput" accept=".json" style="display:none;"> - -<div id="tableContainer"></div> - -<script> -/* --------------- Global State --------------- */ -let data = []; // array of objects -let fieldKeys = []; // columns that will be shown (no leading "_") - -/* --------------- Load & Parse JSON --------------- */ -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})); // clone to avoid accidental mutations - computeFieldKeys(); - renderTable(); - } catch (e) { - alert('Error parsing JSON: ' + e.message); - } -} - -function computeFieldKeys() { - const keys = new Set(); - data.forEach(obj => { - Object.keys(obj).forEach(k => { - if (!k.startsWith('_')) keys.add(k); - }); - }); - fieldKeys = Array.from(keys); -} - -/* --------------- Render Table --------------- */ -function renderTable() { - const container = document.getElementById('tableContainer'); - container.innerHTML = ''; // clear previous - - if (!data.length) return; - - const table = document.createElement('table'); - const thead = document.createElement('thead'); - const headerRow = document.createElement('tr'); - - // Build header - fieldKeys.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) => { - const tr = document.createElement('tr'); - fieldKeys.forEach(k => { - const td = document.createElement('td'); - const input = document.createElement('textarea'); - input.rows = 2; - // const input = document.createElement('input'); - // input.type = 'text'; - 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 = 'del-btn'; - delBtn.dataset.row = rowIndex; - delBtn.addEventListener('click', () => onDeleteRow(rowIndex)); - delTd.appendChild(delBtn); - tr.appendChild(delTd); - }); - - table.appendChild(tbody); - 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; -} - -/** - * Remove all keys from an object whose value is the empty string. - * - * @param {Object} obj - The source object to clean. - * @param {boolean} [inPlace=false] - If true the original object is mutated. - * @returns {Object} A new object (or the original if inPlace is true) without empty‑string values. - * - * @example - * const data = { a: 'foo', b: '', c: 'bar', d: '' }; - * const cleaned = removeEmptyStrings(data); - * // cleaned === { a: 'foo', c: 'bar' } - */ -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; -} - -function onDeleteRow(idx) { - data.splice(idx, 1); - renderTable(); -} - -/* --------------- File I/O --------------- */ -document.getElementById('loadBtn').addEventListener('click', () => { - const text = document.getElementById('jsonInput').value; - loadJSON(text); -}); - -document.getElementById('saveBtn').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); -}); - -/* --------------- Optional: Drag‑and‑Drop or File Input --------------- */ -document.getElementById('fileInput').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); -}); - -/* --------------- Optional: Load file via button --------------- */ -const fileBtn = document.createElement('button'); -fileBtn.textContent = 'Load from File'; -fileBtn.addEventListener('click', () => document.getElementById('fileInput').click()); -document.body.insertBefore(fileBtn, document.getElementById('tableContainer')); - -/* --------------- Add New Object --------------- */ -document.getElementById('addBtn').addEventListener('click', () => { - if (!fieldKeys.length) { - alert('Load a JSON file first, then you can add a new object.'); - return; - } - const newObj = {}; - fieldKeys.forEach(k => newObj[k] = ''); - newObj.id = crypto.randomUUID(); - data.push(newObj); - renderTable(); -}); -</script> -</body> + <head> + <meta charset="UTF-8"> + <title>SRS Anywhere Card Editor</title> + <link rel="stylesheet" href="style.css"> + </head> + <body> + <div class="status-section"> + <button id="save-button">Download JSON</button> + <input type="file" id="cards-input" accept=".json" /> + </div> + <h1>SRS Anywhere Card Editor</h1> + <div id="table-container"></div> + <script src="card-editor.js"></script> + </body> </html> |