aboutsummaryrefslogtreecommitdiff
path: root/internal/admin
diff options
context:
space:
mode:
Diffstat (limited to 'internal/admin')
-rw-r--r--internal/admin/static/cardeditor.js78
-rw-r--r--internal/admin/static/dropeditor.js1
-rw-r--r--internal/admin/static/hazardeditor.js1
-rw-r--r--internal/admin/static/itemeditor.js54
-rw-r--r--internal/admin/static/mobeditor.js15
-rw-r--r--internal/admin/static/moduleeditor.js1
-rw-r--r--internal/admin/static/objecteditor.js177
-rw-r--r--internal/admin/static/techeditor.js1
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);
}