'+
@@ -315,6 +335,7 @@
div.querySelector('[data-addtag="'+id+'"]').addEventListener('click',function(){addTag(id)});
div.querySelector('[data-addnode="'+id+'"]').addEventListener('click',function(){addNode(id)});
+ div.querySelector('[data-removepreset="'+id+'"]').addEventListener('click',function(){removePreset(id)});
// Mode/days toggle
$(p+'_mode').addEventListener('change',function(){
@@ -328,9 +349,22 @@
});
$(p+'_id').addEventListener('input',function(){
- var v=val('p'+id+'_id');
- var el=$('p'+id+'_disp');
- if(el) el.textContent=v?v:'Untitled preset';
+ updatePresetHeaders();
+ });
+ }
+
+ function updatePresetHeaders(){
+ presets.forEach(function(id,index){
+ var title=$('p'+id+'_disp');
+ if(title){
+ var v=val('p'+id+'_id');
+ title.textContent=v?v:'Preset '+(index+1);
+ }
+ var rm=document.querySelector('[data-removepreset="'+id+'"]');
+ if(rm){
+ rm.disabled=presets.length===1;
+ rm.style.visibility=presets.length===1?'hidden':'visible';
+ }
});
}
@@ -376,18 +410,70 @@
function chk(id){var el=$(id);return el?el.checked:false}
function esc(s){var d=document.createElement('div');d.textContent=s;return d.innerHTML}
+ function clearValidation(){
+ document.querySelectorAll('.field-error').forEach(function(el){el.remove()});
+ document.querySelectorAll('.field-invalid').forEach(function(el){
+ el.classList.remove('field-invalid');
+ el.removeAttribute('aria-invalid');
+ });
+ $('errorPanel').style.display='none';
+ $('errorPanel').innerHTML='';
+ }
+
+ function markFieldError(id,msg){
+ var el=$(id);
+ if(!el)return;
+ el.classList.add('field-invalid');
+ el.setAttribute('aria-invalid','true');
+ var wrap=el.closest('.f');
+ if(!wrap)return;
+ var err=document.createElement('div');
+ err.className='field-error';
+ err.textContent=msg;
+ wrap.appendChild(err);
+ }
+
+ function showValidation(errors){
+ if(!errors.length)return;
+ var panel=$('errorPanel');
+ panel.innerHTML='
Fix the highlighted fields before generating JSON.
'+
+ errors.map(function(err){return '
'+esc(err.message)+'
'}).join('')+
+ '
';
+ panel.style.display='block';
+ errors.forEach(function(err){if(err.fieldId)markFieldError(err.fieldId,err.message)});
+ }
+
function generate(){
+ clearValidation();
var cfg={presets:[]};
+ var errors=[];
+ var seenIds={};
presets.forEach(function(id){
var k='p'+id,p={};
- var pidv=val(k+'_id');if(!pidv)return;
+ var pidv=val(k+'_id');
+ if(!pidv){
+ errors.push({fieldId:k+'_id',message:'Preset ID is required.'});
+ return;
+ }
+ if(seenIds[pidv]){
+ errors.push({fieldId:k+'_id',message:'Preset IDs must be unique.'});
+ return;
+ }
+ seenIds[pidv]=true;
p.id=pidv;
p.defaultMode=val(k+'_mode')||'timed';
p.defaultChannel=val(k+'_channel')||'stable';
var dv=val(k+'_days');
var mode=val(k+'_mode');
if(mode==='timed'){
- if(dv==='custom'){var cd=parseInt(val(k+'_cd'))||0;if(cd)p.defaultDays=cd}
+ if(dv==='custom'){
+ var cd=Number(val(k+'_cd'));
+ if(!Number.isInteger(cd)||cd<1||cd>30){
+ errors.push({fieldId:k+'_cd',message:'Custom days must be an integer between 1 and 30.'});
+ return;
+ }
+ p.defaultDays=cd;
+ }
else{var nd=parseInt(dv);if(nd)p.defaultDays=nd}
}
var d;d=val(k+'_desc');if(d)p.description=d;
@@ -414,6 +500,12 @@
p.cleanup.deviceDeleteEnabled=chk(k+'_cdd');
cfg.presets.push(p);
});
+ if(errors.length){
+ showValidation(errors);
+ $('output').textContent='';
+ $('outputWrap').style.display='none';
+ return null;
+ }
var json=JSON.stringify(cfg,null,2);
$('output').textContent=json;
$('outputWrap').style.display='';
@@ -422,7 +514,8 @@
$('genBtn').addEventListener('click',generate);
$('dlBtn').addEventListener('click',function(){
- generate();
+ var cfg=generate();
+ if(!cfg)return;
var json=$('output').textContent;
var blob=new Blob([json],{type:'application/json'});
var a=document.createElement('a');
@@ -431,6 +524,7 @@
a.click();
URL.revokeObjectURL(a.href);
});
+ $('addPresetBtn').addEventListener('click',addPreset);
function renderHelp(){var html='';Object.keys(FIELD_LABELS).forEach(function(key){html+='
'+esc(FIELD_LABELS[key])+'
'+esc(TIPS[key])+'
'});$('helpList').innerHTML=html}
$('toggleHelp').addEventListener('click',function(){var panel=$('helpPanel');var open=panel.classList.toggle('open');$('toggleHelp').textContent=open?'Hide field explanations':'Explain all fields'});