var editorType = ''; var editorFields = []; var currentID = null; var allIDs = []; var treeData = { root: [], dirs: {} }; var collapsedDirs = {}; function initEditor(type, fields) { editorType = type; editorFields = fields; loadList(); var hash = window.location.hash.substring(1); if (hash) { loadItem(hash); } } function loadList() { API.get('/api/' + editorType).then(function(data) { if (Array.isArray(data)) { treeData = { root: data, dirs: {} }; allIDs = data.slice(); } else { treeData = { root: data.root || [], dirs: data.dirs || {} }; allIDs = treeData.root.slice(); Object.keys(treeData.dirs).sort().forEach(function(dir) { Array.prototype.push.apply(allIDs, treeData.dirs[dir]); }); } renderList(''); }).catch(function(e) { notify('Failed to load list: ' + e.message, 'error'); }); } function renderList(filter) { var el = $('#listEntries'); var f = (filter || '').toLowerCase(); var html = ''; var hasDirs = Object.keys(treeData.dirs).length > 0; if (treeData.root.length > 0 || !hasDirs) { html += renderDirGroup(hasDirs ? 'Root' : '', treeData.root, f); } if (hasDirs) { Object.keys(treeData.dirs).sort().forEach(function(dir) { html += renderDirGroup(dir, treeData.dirs[dir], f); }); } el.innerHTML = html; } function renderDirGroup(name, ids, filter) { if (ids.length === 0 && !filter) return ''; var isFiltering = !!filter; var displayIds = ids; if (isFiltering) { displayIds = ids.filter(function(id) { return String(id).toLowerCase().indexOf(filter) >= 0; }); if (displayIds.length === 0) return ''; } var expanded = isFiltering ? true : (collapsedDirs[name] === false || (!name && collapsedDirs[''] === undefined)); var displayName = name || editorType.charAt(0).toUpperCase() + editorType.slice(1); var h = '
'; if (name) { h += '
'; h += '' + (expanded ? '▼' : '▶') + ''; h += '' + esc(displayName) + ''; h += '' + (isFiltering ? displayIds.length + '/' + ids.length : ids.length) + ''; h += '
'; } h += ''; h += '
'; return h; } function toggleDir(name) { collapsedDirs[name] = collapsedDirs[name] === false ? true : false; renderList(''); } function filterList(val) { renderList(val); } function loadItem(id) { currentID = id; renderList(''); window.location.hash = id; API.get('/api/' + editorType + '/' + encodeURIComponent(id)).then(function(data) { renderEditor(id, data); }).catch(function(e) { $('#editorMain').innerHTML = '

Failed to load: ' + esc(e.message) + '

'; }); } function renderEditor(id, data) { var obj = data; var html = '

' + esc(editorType.charAt(0).toUpperCase() + editorType.slice(1)) + ': ' + esc(id) + '

'; html += '
'; html += '
'; html += '
'; html += '
'; editorFields.forEach(function(f) { var val = getNested(obj, f.key); val = val === undefined || val === null ? '' : val; var cls = f.type === 'textarea' ? 'form-group full' : 'form-group'; if (f.type === 'textarea') { var displayVal; if (f.encode) { displayVal = f.encode(val); } else if (typeof val === 'object') { displayVal = JSON.stringify(val, null, 2); } else { displayVal = String(val); } html += '
'; } else if (f.type === 'checkbox') { html += '
'; } else if (f.type === 'number') { html += '
'; } else if (f.type === 'color') { html += '
'; } else { html += '
'; } }); html += '
'; html += '
'; html += ''; html += '
'; html += ''; html += ''; html += '
'; $('#editorMain').innerHTML = html; setTimeout(function() { document.querySelectorAll('.color-field').forEach(function(el) { bindColorField(el); }); }, 50); } function saveItem(id) { var data = {}; editorFields.forEach(function(f) { var el = $('#f_' + f.key); if (!el) return; var val; if (f.type === 'checkbox') { val = el.checked; } else if (f.type === 'number') { val = parseFloat(el.value) || 0; } else if (f.type === 'textarea') { val = el.value; } else { val = el.value; } if (f.parser) { val = f.parser(val); } setNested(data, f.key, val); }); data.id = id; API.put('/api/' + editorType + '/' + encodeURIComponent(id), data).then(function() { notify(editorType + ' ' + id + ' saved', 'success'); updateUndoBar(); }).catch(function(e) { notify('Save failed: ' + e.message, 'error'); }); } function deleteItem(id) { if (!confirm('Delete ' + editorType + ' "' + id + '"?')) return; API.del('/api/' + editorType + '/' + encodeURIComponent(id)).then(function() { notify(editorType + ' ' + id + ' deleted', 'success'); updateUndoBar(); currentID = null; $('#editorMain').innerHTML = '

Deleted

'; loadList(); }).catch(function(e) { notify('Delete failed: ' + e.message, 'error'); }); } function createNew() { var name = prompt(editorType + ' ID:'); if (!name) return; API.post('/api/' + editorType, {id: name}).then(function() { notify(editorType + ' ' + name + ' created', 'success'); updateUndoBar(); loadList(); loadItem(name); }).catch(function(e) { notify('Create failed: ' + e.message, 'error'); }); } function switchTab(e, tab) { e.target.parentElement.querySelectorAll('.tab').forEach(function(t) { t.classList.remove('active'); }); e.target.classList.add('active'); var tabs = $('#editorMain').querySelectorAll('.tab-content'); tabs.forEach(function(t) { t.style.display = 'none'; }); var target = document.getElementById('tab' + tab.charAt(0).toUpperCase() + tab.slice(1)); if (target) target.style.display = 'block'; } function getNested(obj, path) { return path.split('.').reduce(function(o, k) { return o && o[k] !== undefined ? o[k] : undefined; }, obj); } function setNested(obj, path, val) { var keys = path.split('.'); var last = keys.pop(); var target = keys.reduce(function(o, k) { if (!o[k]) o[k] = {}; return o[k]; }, obj); target[last] = val; } function esc(s) { return String(s || '').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } function escAttr(s) { return String(s || '').replace(/&/g,'&').replace(/"/g,'"'); } window.refreshPage = function() { loadList(); if (currentID) loadItem(currentID); };