diff options
Diffstat (limited to 'internal/admin/static')
| -rw-r--r-- | internal/admin/static/cardeditor.js | 78 | ||||
| -rw-r--r-- | internal/admin/static/dropeditor.js | 1 | ||||
| -rw-r--r-- | internal/admin/static/hazardeditor.js | 1 | ||||
| -rw-r--r-- | internal/admin/static/itemeditor.js | 54 | ||||
| -rw-r--r-- | internal/admin/static/mobeditor.js | 15 | ||||
| -rw-r--r-- | internal/admin/static/moduleeditor.js | 1 | ||||
| -rw-r--r-- | internal/admin/static/objecteditor.js | 177 | ||||
| -rw-r--r-- | internal/admin/static/techeditor.js | 1 |
8 files changed, 241 insertions, 87 deletions
diff --git a/internal/admin/static/cardeditor.js b/internal/admin/static/cardeditor.js index f2316e1..8ff5eaf 100644 --- a/internal/admin/static/cardeditor.js +++ b/internal/admin/static/cardeditor.js @@ -175,7 +175,7 @@ function ced_selectSearchResult(el) { function ced_persistSearchToData(input, value) { var ed = window._editorData; - var dataPath = input.getAttribute('data-data-path'); + var dataPath = input.getAttribute('data-field-path'); if (!ed || !dataPath) return; var m = dataPath.match(/^(.+)\[(\d+)\]\.(.+)$/); if (m) { @@ -195,6 +195,48 @@ function ced_persistSearchToData(input, value) { } } +function ced_setPathInData(ed, dataPath, value) { + if (!ed || !dataPath) return; + var m = dataPath.match(/^(.+)\[(\d+)\]\.(.+)$/); + if (m) { + if (!ed[m[1]]) ed[m[1]] = []; + if (!ed[m[1]][m[2]]) ed[m[1]][m[2]] = {}; + ed[m[1]][m[2]][m[3]] = value; + } else { + var m2 = dataPath.match(/^(.+)\.(.+)$/); + if (m2) { + if (!ed[m2[1]]) ed[m2[1]] = {}; + ed[m2[1]][m2[2]] = value; + } else { + ed[dataPath] = value; + } + } +} + +function ced_syncDOMToData(ed) { + if (!ed) return; + var inputs = document.querySelectorAll('[data-field-path]'); + for (var i = 0; i < inputs.length; i++) { + var el = inputs[i]; + var dataPath = el.getAttribute('data-field-path'); + if (!dataPath) continue; + var val; + if (el.tagName === 'TEXTAREA') { + val = el.value; + } else if (el.type === 'checkbox') { + val = el.checked; + } else if (el.type === 'number') { + var raw = el.value.trim(); + val = raw === '' ? '' : (parseFloat(raw) || 0); + } else if (el.tagName === 'SELECT') { + val = el.value; + } else { + val = el.value; + } + ced_setPathInData(ed, dataPath, val); + } +} + function ced_resolveDataPath(obj, path) { return path.split('.').reduce(function(o, k) { if (o === undefined || o === null) return undefined; @@ -236,6 +278,17 @@ function ced_renderField(sec, f, data, idx, subPath, optStyle, cardPathFn, field if (!showIf(sectionData)) hiddenStyle = ' style="display:none"'; } + var pfx = cardPathFn(sec); + var dp; + if (idx >= 0) { + dp = pfx + '[' + idx + '].' + key; + } else if (subPath) { + dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; + } else { + dp = (pfx ? pfx + '.' : '') + key; + } + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; + function wrap(cls, inner) { var w = (wide === 'wide') ? ' obj-wide' : ''; if (wide === 'narrow') w = ' obj-narrow'; @@ -244,19 +297,19 @@ function ced_renderField(sec, f, data, idx, subPath, optStyle, cardPathFn, field } if (type === 'checkbox') { - return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(label) + '</span>'); + return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + dpAttr + '> <span>' + esc(label) + '</span>'); } if (type === 'json') { var jstr = typeof val === 'object' ? JSON.stringify(val) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}">' + esc(jstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}"' + dpAttr + '>' + esc(jstr) + '</textarea>'); } if (type === 'textarea') { var dstr = typeof val === 'object' ? (Array.isArray(val) ? val.map(function(v){return v.text||'';}).join('\n') : JSON.stringify(val)) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3">' + esc(dstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3"' + dpAttr + '>' + esc(dstr) + '</textarea>'); } if (type === 'select') { var opts = hint ? hint.split('|') : []; - var inner = '<span>' + esc(label) + '</span><select id="' + fid + '">'; + var inner = '<span>' + esc(label) + '</span><select id="' + fid + '"' + dpAttr + '>'; opts.forEach(function(o) { inner += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + esc(o) + '</option>'; }); @@ -265,24 +318,15 @@ function ced_renderField(sec, f, data, idx, subPath, optStyle, cardPathFn, field } if (type === 'color') { var swatch = '<span class="color-swatch" style="background:#' + escAttr(String(val) || '808080') + '"></span>'; - return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"></div>'); + return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"' + dpAttr + '></div>'); } if (type === 'search') { var entity = searchEntity || 'items'; var ph = hint ? ' placeholder="' + esc(hint) + '"' : ' placeholder="Search ' + entity + '..."'; - var pfx = cardPathFn(sec); - var dp; - if (idx >= 0) { - dp = pfx + '[' + idx + '].' + key; - } else if (subPath) { - dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; - } else { - dp = (pfx ? pfx + '.' : '') + key; - } - return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); + return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-field-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); } var ph = hint ? ' placeholder="' + esc(hint) + '"' : ''; - return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + '>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + dpAttr + '>'); } // ---- Subtable row add/remove ---- diff --git a/internal/admin/static/dropeditor.js b/internal/admin/static/dropeditor.js index cbd51c7..8e463ec 100644 --- a/internal/admin/static/dropeditor.js +++ b/internal/admin/static/dropeditor.js @@ -84,6 +84,7 @@ function renderDropEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(dropData); if (!dropData || !dropID) return; renderDropEditor(dropID, dropData); } diff --git a/internal/admin/static/hazardeditor.js b/internal/admin/static/hazardeditor.js index ac35e78..a0610dc 100644 --- a/internal/admin/static/hazardeditor.js +++ b/internal/admin/static/hazardeditor.js @@ -108,6 +108,7 @@ function renderHazardEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(hazardData); if (!hazardData || !hazardID) return; renderHazardEditor(hazardID, hazardData); } diff --git a/internal/admin/static/itemeditor.js b/internal/admin/static/itemeditor.js index 5a2b371..5ffb0cb 100644 --- a/internal/admin/static/itemeditor.js +++ b/internal/admin/static/itemeditor.js @@ -224,6 +224,7 @@ function renderItemEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(itemData); if (!itemData || !itemID) return; renderItemEditor(itemID, itemData); } @@ -349,6 +350,8 @@ function equipFieldID(key) { function renderEquipField(key, label, type, hint, val, wide, searchEntity) { var fid = equipFieldID(key); val = val === undefined || val === null ? '' : val; + var dp = 'equipment.' + key; + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; function wrap(cls, inner) { var w = (wide === 'wide') ? ' obj-wide' : ''; @@ -358,7 +361,7 @@ function renderEquipField(key, label, type, hint, val, wide, searchEntity) { if (type === 'select') { var opts = hint ? hint.split('|') : []; - var inner = '<span>' + esc(label) + '</span><select id="' + fid + '">'; + var inner = '<span>' + esc(label) + '</span><select id="' + fid + '"' + dpAttr + '>'; opts.forEach(function(o) { inner += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + esc(o) + '</option>'; }); @@ -368,10 +371,10 @@ function renderEquipField(key, label, type, hint, val, wide, searchEntity) { if (type === 'search') { var entity = searchEntity || 'items'; var ph = hint ? ' placeholder="' + esc(hint) + '"' : ' placeholder="Search ' + entity + '..."'; - return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); + return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-field-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); } var ph = hint ? ' placeholder="' + esc(hint) + '"' : ''; - return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + '>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + dpAttr + '>'); } function renderEquipmentCard(data) { @@ -491,6 +494,17 @@ function renderField(sec, f, data, idx, subPath, optStyle) { if (!showIf(sectionData)) hiddenStyle = ' style="display:none"'; } + var pfx = cardPath(sec); + var dp; + if (idx >= 0) { + dp = pfx + '[' + idx + '].' + key; + } else if (subPath) { + dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; + } else { + dp = (pfx ? pfx + '.' : '') + key; + } + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; + function wrap(cls, inner) { var w = (wide === 'wide') ? ' obj-wide' : ''; if (wide === 'narrow') w = ' obj-narrow'; @@ -499,19 +513,19 @@ function renderField(sec, f, data, idx, subPath, optStyle) { } if (type === 'checkbox') { - return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(label) + '</span>'); + return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + dpAttr + '> <span>' + esc(label) + '</span>'); } if (type === 'json') { var jstr = typeof val === 'object' ? JSON.stringify(val) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}">' + esc(jstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}"' + dpAttr + '>' + esc(jstr) + '</textarea>'); } if (type === 'textarea') { var dstr = typeof val === 'object' ? (Array.isArray(val) ? val.map(function(v){return v.text||'';}).join('\n') : JSON.stringify(val)) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3">' + esc(dstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3"' + dpAttr + '>' + esc(dstr) + '</textarea>'); } if (type === 'select') { var opts = hint ? hint.split('|') : []; - var inner = '<span>' + esc(label) + '</span><select id="' + fid + '">'; + var inner = '<span>' + esc(label) + '</span><select id="' + fid + '"' + dpAttr + '>'; opts.forEach(function(o) { inner += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + esc(o) + '</option>'; }); @@ -520,24 +534,15 @@ function renderField(sec, f, data, idx, subPath, optStyle) { } if (type === 'color') { var swatch = '<span class="color-swatch" style="background:#' + escAttr(String(val) || '808080') + '"></span>'; - return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"></div>'); + return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"' + dpAttr + '></div>'); } if (type === 'search') { var entity = searchEntity || 'items'; var ph = hint ? ' placeholder="' + esc(hint) + '"' : ' placeholder="Search ' + entity + '..."'; - var pfx = cardPath(sec); - var dp; - if (idx >= 0) { - dp = pfx + '[' + idx + '].' + key; - } else if (subPath) { - dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; - } else { - dp = (pfx ? pfx + '.' : '') + key; - } - return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); + return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-field-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); } var ph = hint ? ' placeholder="' + esc(hint) + '"' : ''; - return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + '>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + dpAttr + '>'); } // ========================================================================= @@ -575,25 +580,26 @@ function renderSubFieldHTML(sec, stKey, idx, f, val) { if (f[4] === 'wide') extraStyle = ' style="flex:1 1 100%"'; var defaultPh = ''; if (f[3]) defaultPh = ' placeholder="' + escAttr(f[3]) + '"'; + var dp = cardPath(sec) + '.' + stKey + '[' + idx + '].' + f[0]; + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; if (f[2] === 'search') { var entity = f[6] || 'items'; var ph = f[3] ? ' placeholder="' + escAttr(f[3]) + '"' : ' placeholder="Search ' + entity + '..."'; - var dp = cardPath(sec) + '.' + stKey + '[' + idx + '].' + f[0]; - return '<label class="obj-field obj-search' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; + return '<label class="obj-field obj-search' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-field-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; } if (f[2] === 'checkbox') { - return '<label class="obj-field obj-check' + extraCls + '"' + extraStyle + '><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(f[1]) + '</span></label>'; + return '<label class="obj-field obj-check' + extraCls + '"' + extraStyle + '><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + dpAttr + '> <span>' + esc(f[1]) + '</span></label>'; } if (f[2] === 'select') { var opts = f[3] ? f[3].split('|') : []; - var sel = '<label class="obj-field' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><select id="' + fid + '">'; + var sel = '<label class="obj-field' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><select id="' + fid + '"' + dpAttr + '>'; opts.forEach(function(o) { sel += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + esc(o || '(none)') + '</option>'; }); sel += '</select></label>'; return sel; } - return '<label class="obj-field' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + defaultPh + '></label>'; + return '<label class="obj-field' + extraCls + '"' + extraStyle + '><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + defaultPh + dpAttr + '></label>'; } function renderSubtable(sec, st, data) { diff --git a/internal/admin/static/mobeditor.js b/internal/admin/static/mobeditor.js index c564088..73dd16b 100644 --- a/internal/admin/static/mobeditor.js +++ b/internal/admin/static/mobeditor.js @@ -202,6 +202,7 @@ function renderMobEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(mobData); if (!mobData || !mobID) return; renderMobEditor(mobID, mobData); } @@ -425,16 +426,17 @@ function renderMobSubField(sec, stKey, idx, f, val) { else if (f[4] === 'grow') extraCls = ' obj-grow'; var defaultPh = ''; if (f[3]) defaultPh = ' placeholder="' + escAttr(f[3]) + '"'; + var dp = mobCardPath(sec) + '.' + stKey + '[' + idx + '].' + f[0]; + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; if (f[2] === 'search') { var entity = f[6] || 'items'; var ph = f[3] ? ' placeholder="' + escAttr(f[3]) + '"' : ' placeholder="Search ' + entity + '..."'; - var dp = mobCardPath(sec) + '.' + stKey + '[' + idx + '].' + f[0]; - return '<label class="obj-field obj-search' + extraCls + '"><span>' + esc(f[1]) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; + return '<label class="obj-field obj-search' + extraCls + '"><span>' + esc(f[1]) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-field-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; } if (f[2] === 'checkbox') { - return '<label class="obj-field obj-check' + extraCls + '"><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(f[1]) + '</span></label>'; + return '<label class="obj-field obj-check' + extraCls + '"><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + dpAttr + '> <span>' + esc(f[1]) + '</span></label>'; } - return '<label class="obj-field' + extraCls + '"><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + defaultPh + '></label>'; + return '<label class="obj-field' + extraCls + '"><span>' + esc(f[1]) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + defaultPh + dpAttr + '></label>'; } function renderMobSubtable(sec, st, data) { @@ -529,11 +531,12 @@ function renderMobTableSubtable(sec, st, arr) { st.fields.forEach(function(f) { var fid = mobFieldIDSub(sec, st.key, idx, f[0]); var val = item[f[0]]; + var dp = mobCardPath(sec) + '.' + st.key + '[' + idx + '].' + f[0]; if (f[2] === 'search') { var entity = f[6] || 'items'; var sval = (val === undefined || val === null) ? '' : String(val); h += '<div class="ingredients-col ingredients-col-items"><div class="obj-field obj-search" style="width:100%"><div style="position:relative;width:100%">' + - '<input id="' + fid + '" value="' + escAttr(sval) + '" class="search-input" data-search-type="' + entity + '" placeholder="Search ' + entity + '...">' + + '<input id="' + fid + '" value="' + escAttr(sval) + '" class="search-input" data-search-type="' + entity + '" data-field-path="' + escAttr(dp) + '" placeholder="Search ' + entity + '...">' + '<div class="search-results" style="display:none"></div></div></div></div>'; } else { var hasPh = !!f[3]; @@ -543,7 +546,7 @@ function renderMobTableSubtable(sec, st, arr) { else nval = String(val); var ph = hasPh ? ' placeholder="' + escAttr(f[3]) + '"' : ''; h += '<div class="ingredients-col" style="width:' + mobTableColWidth(f) + 'px">' + - '<label class="obj-field"><input id="' + fid + '" value="' + escAttr(nval) + '"' + ph + '></label>' + + '<label class="obj-field"><input id="' + fid + '" value="' + escAttr(nval) + '"' + ph + ' data-field-path="' + escAttr(dp) + '"></label>' + '</div>'; } }); diff --git a/internal/admin/static/moduleeditor.js b/internal/admin/static/moduleeditor.js index 6d6a3e0..f1fb0fd 100644 --- a/internal/admin/static/moduleeditor.js +++ b/internal/admin/static/moduleeditor.js @@ -109,6 +109,7 @@ function renderModuleEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(moduleData); if (!moduleData || !moduleID) return; renderModuleEditor(moduleID, moduleData); } diff --git a/internal/admin/static/objecteditor.js b/internal/admin/static/objecteditor.js index c80cb57..f8ef18a 100644 --- a/internal/admin/static/objecteditor.js +++ b/internal/admin/static/objecteditor.js @@ -18,14 +18,14 @@ var SECTIONS = [ ] }, { - id: 'steal', label: 'Steal', - detect: function(d) { return d.steal_table !== undefined || d.steal_level !== undefined || d.steal_speed !== undefined || d.steal_xp !== undefined || d.guard_mob !== undefined; }, + id: 'steal', label: 'Steal', path: 'steal', + detect: function(d) { return d.steal !== undefined; }, fields: [ - ['steal_table', 'Table ID', 'text'], - ['steal_level', 'Level', 'number', '', 'narrow'], - ['steal_xp', 'XP', 'number', '', 'narrow'], - ['steal_speed', 'Speed', 'number', '', 'narrow'], - ['guard_mob', 'Guard Mob', 'text'], + ['table', 'Table ID', 'search', '', '', null, 'drops'], + ['level', 'Level', 'number', '1', 'narrow'], + ['xp', 'XP', 'number', '5', 'narrow'], + ['speed', 'Speed', 'number', '4', 'narrow'], + ['guard_mob', 'Guard Mob', 'search', '', '', null, 'mobs'], ] }, { @@ -148,6 +148,8 @@ function loadObject(id) { window._editorData = objectData; window._useInterVis = {}; window._useInterHelpCollapsed = undefined; + window._coreVis = {}; + window._stealVis = {}; renderObjectEditor(id, data); }).catch(function(e) { $('#editorMain').innerHTML = '<p style="color:var(--danger)">Failed to load: ' + esc(e.message) + '</p>'; @@ -250,6 +252,8 @@ function renderCard(sec, data) { h += renderArraySection(sec, data); } else if (sec.id === 'core') { h += renderCoreCard(data); + } else if (sec.id === 'steal') { + h += renderStealCard(data); } else { h += '<div class="obj-card-grid">'; sec.fields.forEach(function(f) { @@ -351,6 +355,17 @@ function renderField(sec, f, data, idx, subPath, optStyle) { if (!showIf(sectionData)) hiddenStyle = ' style="display:none"'; } + var pfx = cardPath(sec); + var dp; + if (idx >= 0) { + dp = pfx + '[' + idx + '].' + key; + } else if (subPath) { + dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; + } else { + dp = (pfx ? pfx + '.' : '') + key; + } + var dpAttr = ' data-field-path="' + escAttr(dp) + '"'; + function wrap(cls, inner) { var w = (wide === 'wide') ? ' obj-wide' : ''; if (wide === 'narrow') w = ' obj-narrow'; @@ -359,19 +374,19 @@ function renderField(sec, f, data, idx, subPath, optStyle) { } if (type === 'checkbox') { - return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(label) + '</span>'); + return wrap('obj-field obj-check', '<input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + dpAttr + '> <span>' + esc(label) + '</span>'); } if (type === 'json') { var jstr = typeof val === 'object' ? JSON.stringify(val) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}">' + esc(jstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="2" placeholder="{}"' + dpAttr + '>' + esc(jstr) + '</textarea>'); } if (type === 'textarea') { var dstr = typeof val === 'object' ? (Array.isArray(val) ? val.map(function(v){return v.text||'';}).join('\n') : JSON.stringify(val)) : String(val); - return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3">' + esc(dstr) + '</textarea>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><textarea id="' + fid + '" rows="3"' + dpAttr + '>' + esc(dstr) + '</textarea>'); } if (type === 'select') { var opts = hint ? hint.split('|') : []; - var inner = '<span>' + esc(label) + '</span><select id="' + fid + '" onchange="toggleGatherConditionals()">'; + var inner = '<span>' + esc(label) + '</span><select id="' + fid + '" onchange="toggleGatherConditionals()"' + dpAttr + '>'; opts.forEach(function(o) { inner += '<option value="' + escAttr(o) + '"' + (String(val) === o ? ' selected' : '') + '>' + (o === '' ? 'All' : esc(o)) + '</option>'; }); @@ -380,39 +395,93 @@ function renderField(sec, f, data, idx, subPath, optStyle) { } if (type === 'color') { var swatch = '<span class="color-swatch" style="background:#' + escAttr(String(val) || '808080') + '"></span>'; - return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"></div>'); + return wrap('obj-field color-field', '<span>' + esc(label) + '</span><div class="obj-color-row">' + swatch + '<input id="' + fid + '" value="' + escAttr(String(val)) + '" class="color-input"' + dpAttr + '></div>'); } if (type === 'search') { var entity = searchEntity || 'items'; var ph = hint ? ' placeholder="' + esc(hint) + '"' : ' placeholder="Search ' + entity + '..."'; - var pfx = cardPath(sec); - var dp; - if (idx >= 0) { - dp = pfx + '[' + idx + '].' + key; - } else if (subPath) { - dp = (pfx ? pfx + '.' : '') + subPath + '.' + key; - } else { - dp = (pfx ? pfx + '.' : '') + key; - } - return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); + return wrap('obj-field obj-search', '<span>' + esc(label) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-field-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div>'); } var ph = hint ? ' placeholder="' + esc(hint) + '"' : ''; - return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + '>'); + return wrap('obj-field', '<span>' + esc(label) + '</span><input id="' + fid + '" value="' + escAttr(String(val)) + '"' + ph + dpAttr + '>'); } function renderCoreCard(data) { var sec = SECTIONS[0]; + window._coreVis = window._coreVis || {}; + var vis = window._coreVis; + var showAliases = vis.aliases || (data.aliases && data.aliases.length > 0); + var showInRoom = vis.inroom_description || (data.inroom_description && data.inroom_description !== ''); + var showRemoval = vis.removal_item || (data.removal_item && data.removal_item !== ''); + var h = '<div class="obj-core-row">'; h += renderField(sec, sec.fields[0], data, -1, null, 'flex:1'); // name - h += renderField(sec, sec.fields[2], data, -1, null, 'flex:2'); // aliases h += renderField(sec, sec.fields[1], data, -1); // color h += renderField(sec, sec.fields[3], data, -1); // hidden h += '</div>'; h += '<div style="display:flex;flex-direction:column;gap:8px">'; h += renderField(sec, sec.fields[5], data, -1); // description - h += renderField(sec, sec.fields[4], data, -1); // in-room description - h += renderField(sec, sec.fields[6], data, -1); // removal item + + if (showAliases) { + h += '<div style="display:flex;gap:6px;align-items:flex-start">'; + h += '<div style="flex:1">' + renderField(sec, sec.fields[2], data, -1, null, 'flex:2') + '</div>'; + h += '<button class="btn btn-sm btn-danger" onclick="hideCoreField(\'aliases\')" style="margin-top:2px">X</button>'; + h += '</div>'; + } + if (showInRoom) { + h += '<div style="display:flex;gap:6px;align-items:flex-start">'; + h += '<div style="flex:1">' + renderField(sec, sec.fields[4], data, -1) + '</div>'; + h += '<button class="btn btn-sm btn-danger" onclick="hideCoreField(\'inroom_description\')" style="margin-top:2px">X</button>'; + h += '</div>'; + } + if (showRemoval) { + h += '<div style="display:flex;gap:6px;align-items:flex-start">'; + h += '<div style="flex:1">' + renderField(sec, sec.fields[6], data, -1) + '</div>'; + h += '<button class="btn btn-sm btn-danger" onclick="hideCoreField(\'removal_item\')" style="margin-top:2px">X</button>'; + h += '</div>'; + } h += '</div>'; + + h += '<div style="display:flex;gap:8px;margin-top:4px">'; + if (!showAliases) { + h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;white-space:nowrap" onclick="showCoreField(\'aliases\')">+ Add Aliases</button>'; + } + if (!showInRoom) { + h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;white-space:nowrap" onclick="showCoreField(\'inroom_description\')">+ Add In-Room Desc</button>'; + } + if (!showRemoval) { + h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;white-space:nowrap" onclick="showCoreField(\'removal_item\')">+ Add Removal Item</button>'; + } + h += '</div>'; + + return h; +} + +function renderStealCard(data) { + var sec = SECTIONS[1]; + window._stealVis = window._stealVis || {}; + var vis = window._stealVis; + var stealData = data.steal || {}; + var showGuard = vis.guard_mob || (stealData.guard_mob && stealData.guard_mob !== ''); + + var h = '<div style="display:flex;gap:10px">'; + h += '<div style="flex:1">' + renderField(sec, sec.fields[0], data, -1) + '</div>'; + h += renderField(sec, sec.fields[1], data, -1); + h += renderField(sec, sec.fields[2], data, -1); + h += renderField(sec, sec.fields[3], data, -1); + h += '</div>'; + + if (showGuard) { + h += '<div style="display:flex;gap:6px;align-items:flex-start;margin-top:8px">'; + h += '<div style="flex:1">' + renderField(sec, sec.fields[4], data, -1) + '</div>'; + h += '<button class="btn btn-sm btn-danger" onclick="hideGuardMob()" style="margin-top:2px">X</button>'; + h += '</div>'; + } else { + h += '<div style="margin-top:8px">'; + h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;white-space:nowrap;width:100%" onclick="showGuardMob()">+ Guard Mob</button>'; + h += '</div>'; + } + return h; } @@ -433,7 +502,9 @@ function renderArraySection(sec, data) { h += '<div class="obj-sub-row" data-idx="' + idx + '" style="flex-direction:column;align-items:stretch;position:relative;gap:6px">'; - h += '<div style="display:flex;gap:6px;align-items:flex-end;flex-wrap:wrap">'; + h += '<button class="btn btn-sm btn-danger" style="position:absolute;top:0;right:0;white-space:nowrap;z-index:1" onclick="removeArrayItem(\'' + sec.id + '\',' + idx + ')">Remove Interaction</button>'; + + h += '<div style="display:flex;gap:4px;align-items:flex-end;flex-wrap:wrap;padding-right:125px">'; h += renderField(sec, sec.fields[0], data, idx, null, 'max-width:260px'); h += '<div style="width:1px;background:var(--border);align-self:stretch;margin:2px 0;flex-shrink:0"></div>'; if (!showCond) { @@ -445,7 +516,6 @@ function renderArraySection(sec, data) { if (!showMsg) { h += '<button class="btn btn-sm" style="background:var(--accent);color:var(--text);border:1px solid #1a5a8e;align-self:flex-end;margin-bottom:3px;white-space:nowrap" onclick="showUseInterField(' + idx + ',\'message\')">+ Add Message</button>'; } - h += '<button class="btn btn-sm btn-danger" style="margin-left:auto;align-self:flex-end;margin-bottom:3px;white-space:nowrap" onclick="removeArrayItem(\'' + sec.id + '\',' + idx + ')">Remove Interaction</button>'; h += '</div>'; h += '<div style="display:' + (showCond ? 'flex' : 'none') + ';gap:6px;align-items:flex-start">'; @@ -541,7 +611,7 @@ function renderSubtable(sec, st, data) { var entity = f[5] || 'items'; var ph = f[3] ? ' placeholder="' + escAttr(f[3]) + '"' : ' placeholder="Search ' + entity + '..."'; var dp = cardPath(sec) + '.' + st.key + '[' + idx + '].' + f[0]; - h += '<label class="obj-field obj-search' + extraCls + '"><span>' + esc(f[1]) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; + h += '<label class="obj-field obj-search' + extraCls + '"><span>' + esc(f[1]) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-field-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; } else if (f[2] === 'checkbox') { h += '<label class="obj-field obj-check' + extraCls + '"><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(f[1]) + '</span></label>'; } else { @@ -567,7 +637,7 @@ function renderSubtable(sec, st, data) { var entity = f[5] || 'items'; var ph = f[3] ? ' placeholder="' + escAttr(f[3]) + '"' : ' placeholder="Search ' + entity + '..."'; var dp = cardPath(sec) + '.' + st.key + '[' + idx + '].' + f[0]; - h += '<label class="obj-field obj-search' + extraCls + '"><span>' + esc(f[1]) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-data-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; + h += '<label class="obj-field obj-search' + extraCls + '"><span>' + esc(f[1]) + '</span><div style="position:relative;width:100%"><input id="' + fid + '" value="' + escAttr(String(val)) + '" class="search-input" data-search-type="' + entity + '" data-field-path="' + escAttr(dp) + '"' + ph + '><div class="search-results" style="display:none"></div></div></label>'; } else if (f[2] === 'checkbox') { h += '<label class="obj-field obj-check' + extraCls + '"><input type="checkbox" id="' + fid + '"' + (val ? ' checked' : '') + '> <span>' + esc(f[1]) + '</span></label>'; } else { @@ -693,11 +763,7 @@ function removeSection(id) { if (sec.path) { delete objectData[sec.path]; } else if (id === 'steal') { - delete objectData.steal_table; - delete objectData.steal_level; - delete objectData.steal_xp; - delete objectData.steal_speed; - delete objectData.guard_mob; + delete objectData.steal; } renderCurrent(); } @@ -724,11 +790,7 @@ function addSection() { objectData[sec.path] = {}; } } else if (id === 'steal') { - objectData.steal_table = ''; - objectData.steal_level = 0; - objectData.steal_xp = 0; - objectData.steal_speed = 0; - objectData.guard_mob = ''; + objectData.steal = {table: ''}; } expandedCards[id] = true; renderCurrent(); @@ -806,6 +868,7 @@ function validateDropRow(idx) { } function renderCurrent() { + ced_syncDOMToData(objectData); if (!objectData || !objectID) return; renderObjectEditor(objectID, objectData); } @@ -827,6 +890,38 @@ function hideUseInterField(idx, key) { renderCurrent(); } +function showCoreField(key) { + window._coreVis = window._coreVis || {}; + window._coreVis[key] = true; + renderCurrent(); +} + +function hideCoreField(key) { + window._coreVis = window._coreVis || {}; + window._coreVis[key] = false; + if (key === 'aliases') { + objectData.aliases = []; + } else if (key === 'inroom_description') { + objectData.inroom_description = ''; + } else if (key === 'removal_item') { + objectData.removal_item = ''; + } + renderCurrent(); +} + +function showGuardMob() { + window._stealVis = window._stealVis || {}; + window._stealVis.guard_mob = true; + renderCurrent(); +} + +function hideGuardMob() { + window._stealVis = window._stealVis || {}; + window._stealVis.guard_mob = false; + if (objectData.steal) objectData.steal.guard_mob = ''; + renderCurrent(); +} + function toggleUseInterHelp() { if (window._useInterHelpCollapsed === undefined) { window._useInterHelpCollapsed = false; @@ -967,6 +1062,8 @@ function saveObject() { } } else if (hasValues && sec.path) { out[sec.path] = sectionObj; + } else if (hasValues) { + Object.keys(sectionObj).forEach(function(k) { out[k] = sectionObj[k]; }); } }); diff --git a/internal/admin/static/techeditor.js b/internal/admin/static/techeditor.js index cd4f50f..70f881e 100644 --- a/internal/admin/static/techeditor.js +++ b/internal/admin/static/techeditor.js @@ -98,6 +98,7 @@ function renderTechEditor(id, data) { } function renderCurrent() { + ced_syncDOMToData(techData); if (!techData || !techID) return; renderTechEditor(techID, techData); } |
