'use strict';'require ui';'require uci';'require rpc';'require dom';'require baseclass';const scope=this;uci.loadPackage('luci').catch();const callSessionAccess=rpc.declare({object:'session',method:'access',params:['scope','object','function'],expect:{'access':false}});const CBIJSONConfig=baseclass.extend({__init__(data){data=Object.assign({},data);this.data={};let num_sections=0;const section_ids=[];for(const sectiontype in data){if(!data.hasOwnProperty(sectiontype))
continue;if(Array.isArray(data[sectiontype])){for(let i=0,index=0;i<data[sectiontype].length;i++){const item=data[sectiontype][i];let anonymous;let name;if(!L.isObject(item))
continue;if(typeof(item['.name'])=='string'){name=item['.name'];anonymous=false;}
else{name=sectiontype+num_sections;anonymous=true;}
if(!this.data.hasOwnProperty(name))
section_ids.push(name);this.data[name]=Object.assign(item,{'.index':num_sections++,'.anonymous':anonymous,'.name':name,'.type':sectiontype});}}
else if(L.isObject(data[sectiontype])){this.data[sectiontype]=Object.assign(data[sectiontype],{'.anonymous':false,'.name':sectiontype,'.type':sectiontype});section_ids.push(sectiontype);num_sections++;}}
section_ids.sort(L.bind((a,b)=>{const indexA=(this.data[a]['.index']!=null)?+this.data[a]['.index']:9999;const indexB=(this.data[b]['.index']!=null)?+this.data[b]['.index']:9999;if(indexA!=indexB)
return(indexA-indexB);return L.naturalCompare(a,b);},this));for(let i=0;i<section_ids.length;i++)
this.data[section_ids[i]]['.index']=i;},load(){return Promise.resolve(this.data);},save(){return Promise.resolve();},get(config,section,option){if(section==null)
return null;if(option==null)
return this.data[section];if(!this.data.hasOwnProperty(section))
return null;const value=this.data[section][option];if(Array.isArray(value))
return value;if(L.isObject(value))
return value;if(value!=null)
return String(value);return null;},set(config,section,option,value){if(section==null||option==null||option.charAt(0)=='.')
return;if(!this.data.hasOwnProperty(section))
return;if(value==null)
delete this.data[section][option];else if(Array.isArray(value))
this.data[section][option]=value;else if(L.isObject(value))
this.data[section][option]=value;else
this.data[section][option]=String(value);},unset(config,section,option){return this.set(config,section,option,null);},sections(config,sectiontype,callback){const rv=[];for(const section_id in this.data)
if(sectiontype==null||this.data[section_id]['.type']==sectiontype)
rv.push(this.data[section_id]);rv.sort((a,b)=>{return a['.index']-b['.index']});if(typeof(callback)=='function')
for(let i=0;i<rv.length;i++)
callback.call(this,rv[i],rv[i]['.name']);return rv;},add(config,sectiontype,sectionname){let num_sections_type=0;let next_index=0;for(const name in this.data){num_sections_type+=(this.data[name]['.type']==sectiontype);next_index=Math.max(next_index,this.data[name]['.index']);}
const section_id=sectionname??(sectiontype+num_sections_type);if(!this.data.hasOwnProperty(section_id)){this.data[section_id]={'.name':section_id,'.type':sectiontype,'.anonymous':(sectionname==null),'.index':next_index+1};}
return section_id;},remove(config,section){if(this.data.hasOwnProperty(section))
delete this.data[section];},resolveSID(config,section_id){return section_id;},move(config,section_id1,section_id2,after){return uci.move.apply(this,[config,section_id1,section_id2,after]);}});const CBIAbstractElement=baseclass.extend({__init__(title,description){this.title=title??'';this.description=description??'';this.children=[];},append(obj){this.children.push(obj);},parse(){const args=arguments;this.children.forEach((child)=>{child.parse(...args);});},render(){L.error('InternalError','Not implemented');},loadChildren(...args){const tasks=[];if(Array.isArray(this.children))
for(let i=0;i<this.children.length;i++)
if(!this.children[i].disable)
tasks.push(this.children[i].load(...args));return Promise.all(tasks);},renderChildren(tab_name,...args){const tasks=[];let index=0;if(Array.isArray(this.children))
for(let i=0;i<this.children.length;i++)
if(tab_name===null||this.children[i].tab===tab_name)
if(!this.children[i].disable)
tasks.push(this.children[i].render(index++,...args));return Promise.all(tasks);},stripTags(s){if(typeof(s)=='string'&&!s.match(/[<>\&]/))
return s;const x=dom.elem(s)?s:dom.parse(`<div>${s}</div>`);x.querySelectorAll('br').forEach((br)=>{x.replaceChild(document.createTextNode('\n'),br);});return(x.textContent??x.innerText??'').replace(/([ \t]*\n)+/g,'\n');},titleFn(attr,...args){let s=null;if(typeof(this[attr])=='function')
s=this[attr](...args);else if(typeof(this[attr])=='string')
s=args.length?this[attr].format(...args):this[attr];if(s!=null)
s=this.stripTags(String(s)).trim();if(s==null||s=='')
return null;return s;}});const CBIMap=CBIAbstractElement.extend({__init__(config,...args){this.super('__init__',args);uci.load('luci');this.config=config;this.parsechain=[config];this.data=uci;},findElements(...args){let q=null;if(args.length==1)
q=args[0];else if(args.length==2)
q='[%s="%s"]'.format(args[0],args[1]);else
L.error('InternalError','Expecting one or two arguments to findElements()');return this.root.querySelectorAll(q);},findElement(...args){const res=this.findElements(...args);return res.length?res[0]:null;},chain(config){if(this.parsechain.indexOf(config)==-1)
this.parsechain.push(config);},section(cbiClass,...args){if(!CBIAbstractSection.isSubclass(cbiClass))
L.error('TypeError','Class must be a descendent of CBIAbstractSection');const obj=cbiClass.instantiate([this,...args]);this.append(obj);return obj;},load(){const doCheckACL=(!(this instanceof CBIJSONMap)&&this.readonly==null);const loadTasks=[doCheckACL?callSessionAccess('uci',this.config,'write'):true];const configs=this.parsechain??[this.config];loadTasks.push(...configs.map(L.bind((config,i)=>{return i?L.resolveDefault(this.data.load(config)):this.data.load(config);},this)));return Promise.all(loadTasks).then(L.bind((res)=>{if(res[0]===false)
this.readonly=true;return this.loadChildren();},this));},parse(){const tasks=[];if(Array.isArray(this.children))
for(let i=0;i<this.children.length;i++)
tasks.push(this.children[i].parse());return Promise.all(tasks);},save(cb,silent){this.checkDepends();return this.parse().then(cb).then(this.data.save.bind(this.data)).then(this.load.bind(this)).catch((e)=>{if(!silent){ui.showModal(_('Save error'),[E('p',{},[_('An error occurred while saving the form:')]),E('p',{},[E('em',{'style':'white-space:pre-wrap'},[e.message])]),E('div',{'class':'right'},[E('button',{'class':'cbi-button','click':ui.hideModal},[_('Dismiss')])])]);}
return Promise.reject(e);}).then(this.renderContents.bind(this));},reset(){return this.renderContents();},render(){return this.load().then(this.renderContents.bind(this));},renderContents(){const mapEl=(this.root??=E('div',{'id':'cbi-%s'.format(this.config),'class':'cbi-map','cbi-dependency-check':L.bind(this.checkDepends,this)}));dom.bindClassInstance(mapEl,this);return this.renderChildren(null).then(L.bind((nodes)=>{const initialRender=!mapEl.firstChild;dom.content(mapEl,null);if(this.title!=null&&this.title!='')
mapEl.appendChild(E('h2',{'name':'content'},this.title));if(this.description!=null&&this.description!='')
mapEl.appendChild(E('div',{'class':'cbi-map-descr'},this.description));if(this.tabbed)
dom.append(mapEl,E('div',{'class':'cbi-map-tabbed'},nodes));else
dom.append(mapEl,nodes);if(!initialRender){mapEl.classList.remove('flash');window.setTimeout(()=>{mapEl.classList.add('flash');},1);}
this.checkDepends();const tabGroups=mapEl.querySelectorAll('.cbi-map-tabbed, .cbi-section-node-tabbed');for(let i=0;i<tabGroups.length;i++)
ui.tabs.initTabGroup(tabGroups[i].childNodes);return mapEl;},this));},lookupOption(name,section_id,config_name){let id;let elem;let sid;let inst;if(name.indexOf('.')>-1)
id='cbid.%s'.format(name);else
id='cbid.%s.%s.%s'.format(config_name??this.config,section_id,name);elem=this.findElement('data-field',id);sid=elem?id.split(/\./)[2]:null;inst=elem?dom.findClassInstance(elem):null;return(inst instanceof CBIAbstractValue)?[inst,sid]:null;},checkDepends(ev,n){let changed=false;for(let i=0,s=this.children[0];(s=this.children[i])!=null;i++)
if(s.checkDepends(ev,n))
changed=true;if(changed&&(n??0)<10)
this.checkDepends(ev,(n??10)+1);ui.tabs.updateTabs(ev,this.root);},isDependencySatisfied(depends,config_name,section_id){let def=false;if(!Array.isArray(depends)||!depends.length)
return true;for(let i=0;i<depends.length;i++){let istat=true;const reverse=depends[i]['!reverse'];const contains=depends[i]['!contains'];for(const dep in depends[i]){if(dep=='!reverse'||dep=='!contains'){continue;}
else if(dep=='!default'){def=true;istat=false;}
else{const res=this.lookupOption(dep,section_id,config_name);const val=(res&&res[0].isActive(res[1]))?res[0].formvalue(res[1]):null;const equal=contains?isContained(val,depends[i][dep]):isEqual(val,depends[i][dep]);istat=(istat&&equal);}}
if(istat^reverse)
return true;}
return def;}});const CBIJSONMap=CBIMap.extend({__init__(data,...args){this.super('__init__',['json',...args]);this.config='json';this.parsechain=['json'];this.data=new CBIJSONConfig(data);}});const CBIAbstractSection=CBIAbstractElement.extend({__init__(map,sectionType,...args){this.super('__init__',args);this.sectiontype=sectionType;this.map=map;this.config=map.config;this.optional=true;this.addremove=false;this.dynamic=false;},cfgsections(){L.error('InternalError','Not implemented');},filter(section_id){return true;},load(){const section_ids=this.cfgsections();const tasks=[];if(Array.isArray(this.children))
for(let i=0;i<section_ids.length;i++)
tasks.push(this.loadChildren(section_ids[i]).then(Function.prototype.bind.call((section_id,set_values)=>{for(let i=0;i<set_values.length;i++)
this.children[i].cfgvalue(section_id,set_values[i]);},this,section_ids[i])));return Promise.all(tasks);},parse(){const section_ids=this.cfgsections();const tasks=[];if(Array.isArray(this.children))
for(let i=0;i<section_ids.length;i++)
for(let j=0;j<this.children.length;j++)
tasks.push(this.children[j].parse(section_ids[i]));return Promise.all(tasks);},tab(name,title,description){if(this.tabs&&this.tabs[name])
throw'Tab already declared';const entry={name,title,description,children:[]};this.tabs??=[];this.tabs.push(entry);this.tabs[name]=entry;this.tab_names??=[];this.tab_names.push(name);},option(cbiClass,...args){if(!CBIAbstractValue.isSubclass(cbiClass))
throw L.error('TypeError','Class must be a descendant of CBIAbstractValue');const obj=cbiClass.instantiate([this.map,this,...args]);this.append(obj);return obj;},taboption(tabName,...args){if(!this.tabs?.[tabName])
throw L.error('ReferenceError','Associated tab not declared');const obj=this.option(...args);obj.tab=tabName;this.tabs[tabName].children.push(obj);return obj;},cfgvalue(section_id,option){const rv=(arguments.length==1)?{}:null;for(let i=0,o;(o=this.children[i])!=null;i++)
if(rv)
rv[o.option]=o.cfgvalue(section_id);else if(o.option==option)
return o.cfgvalue(section_id);return rv;},formvalue(section_id,option){const rv=(arguments.length==1)?{}:null;for(let i=0,o;(o=this.children[i])!=null;i++){const func=this.map.root?this.children[i].formvalue:this.children[i].cfgvalue;if(rv)
rv[o.option]=func.call(o,section_id);else if(o.option==option)
return func.call(o,section_id);}
return rv;},getUIElement(section_id,option){const rv=(arguments.length==1)?{}:null;for(let i=0,o;(o=this.children[i])!=null;i++)
if(rv)
rv[o.option]=o.getUIElement(section_id);else if(o.option==option)
return o.getUIElement(section_id);return rv;},getOption(option){const rv=(arguments.length==0)?{}:null;for(let i=0,o;(o=this.children[i])!=null;i++)
if(rv)
rv[o.option]=o;else if(o.option==option)
return o;return rv;},renderUCISection(section_id){const renderTasks=[];if(!this.tabs)
return this.renderOptions(null,section_id);for(let i=0;i<this.tab_names.length;i++)
renderTasks.push(this.renderOptions(this.tab_names[i],section_id));return Promise.all(renderTasks).then(this.renderTabContainers.bind(this,section_id));},renderTabContainers(section_id,nodes){const config_name=this.uciconfig??this.map.config;const containerEls=E([]);for(let i=0;i<nodes.length;i++){const tab_name=this.tab_names[i];const tab_data=this.tabs[tab_name];const containerEl=E('div',{'id':'container.%s.%s.%s'.format(config_name,section_id,tab_name),'data-tab':tab_name,'data-tab-title':tab_data.title,'data-tab-active':tab_name===this.selected_tab});if(tab_data.description!=null&&tab_data.description!='')
containerEl.appendChild(E('div',{'class':'cbi-tab-descr'},tab_data.description));containerEl.appendChild(nodes[i]);containerEls.appendChild(containerEl);}
return containerEls;},renderOptions(tab_name,section_id){const in_table=(this instanceof CBITableSection);return this.renderChildren(tab_name,section_id,in_table).then((nodes)=>{const optionEls=E([]);for(let i=0;i<nodes.length;i++)
optionEls.appendChild(nodes[i]);return optionEls;});},checkDepends(ev,n){let changed=false;const sids=this.cfgsections();for(let i=0,sid=sids[0];(sid=sids[i])!=null;i++){for(let j=0,o=this.children[0];(o=this.children[j])!=null;j++){let isActive=o.isActive(sid);const isSatisfied=o.checkDepends(sid);if(isActive!=isSatisfied){o.setActive(sid,!isActive);isActive=!isActive;changed=true;}
if(!n&&isActive)
o.triggerValidation(sid);}}
return changed;}});function isEqual(x,y){if(typeof(y)=='object'&&y instanceof RegExp)
return(x==null)?false:y.test(x);if(x!=null&&y!=null&&typeof(x)!=typeof(y))
return false;if((x==null&&y!=null)||(x!=null&&y==null))
return false;if(Array.isArray(x)){if(x.length!=y.length)
return false;for(let i=0;i<x.length;i++)
if(!isEqual(x[i],y[i]))
return false;}
else if(typeof(x)=='object'){for(const k in x){if(x.hasOwnProperty(k)&&!y.hasOwnProperty(k))
return false;if(!isEqual(x[k],y[k]))
return false;}
for(const k in y)
if(y.hasOwnProperty(k)&&!x.hasOwnProperty(k))
return false;}
else if(x!=y){return false;}
return true;};function isContained(x,y){if(Array.isArray(x)){for(let i=0;i<x.length;i++)
if(x[i]==y)
return true;}
else if(L.isObject(x)){if(x.hasOwnProperty(y)&&x[y]!=null)
return true;}
else if(typeof(x)=='string'){return(x.indexOf(y)>-1);}
return false;};const CBIAbstractValue=CBIAbstractElement.extend({__init__(map,section,option,...args){this.super('__init__',args);this.section=section;this.option=option;this.map=map;this.config=map.config;this.deps=[];this.initial={};this.rmempty=true;this.default=null;this.size=null;this.optional=false;this.retain=false;},depends(field,value){let deps;if(typeof(field)==='string')
deps={},deps[field]=value;else
deps=field;this.deps.push(deps);},transformDepList(section_id,deplist){const list=deplist??this.deps;const deps=[];if(Array.isArray(list)){for(let i=0;i<list.length;i++){const dep={};for(const k in list[i]){if(list[i].hasOwnProperty(k)){if(k.charAt(0)==='!')
dep[k]=list[i][k];else if(k.indexOf('.')!==-1)
dep['cbid.%s'.format(k)]=list[i][k];else
dep['cbid.%s.%s.%s'.format(this.uciconfig??this.section.uciconfig??this.map.config,this.ucisection??section_id,k)]=list[i][k];}}
for(const k in dep){if(dep.hasOwnProperty(k)){deps.push(dep);break;}}}}
return deps;},transformChoices(){if(!Array.isArray(this.keylist)||this.keylist.length==0)
return null;const choices={};for(let i=0;i<this.keylist.length;i++)
choices[this.keylist[i]]=this.vallist[i];return choices;},checkDepends(section_id){const config_name=this.uciconfig??this.section.uciconfig??this.map.config;const active=this.map.isDependencySatisfied(this.deps,config_name,section_id);if(active)
this.updateDefaultValue(section_id);return active;},updateDefaultValue(section_id){if(!L.isObject(this.defaults))
return;const config_name=this.uciconfig??this.section.uciconfig??this.map.config;const cfgvalue=L.toArray(this.cfgvalue(section_id))[0];let default_defval=null;let satisfied_defval=null;for(const value in this.defaults){if(!this.defaults[value]||this.defaults[value].length==0){default_defval=value;continue;}
else if(this.map.isDependencySatisfied(this.defaults[value],config_name,section_id)){satisfied_defval=value;break;}}
if(satisfied_defval==null)
satisfied_defval=default_defval;const node=this.map.findElement('id',this.cbid(section_id));if(node&&node.getAttribute('data-changed')!='true'&&satisfied_defval!=null&&cfgvalue==null)
dom.callClassMethod(node,'setValue',satisfied_defval);this.default=satisfied_defval;},cbid(section_id){if(section_id==null)
L.error('TypeError','Section ID required');return'cbid.%s.%s.%s'.format(this.uciconfig??this.section.uciconfig??this.map.config,section_id,this.option);},load(section_id){if(section_id==null)
L.error('TypeError','Section ID required');return this.map.data.get(this.uciconfig??this.section.uciconfig??this.map.config,this.ucisection??section_id,this.ucioption??this.option);},getUIElement(section_id){const node=this.map.findElement('id',this.cbid(section_id));const inst=node?dom.findClassInstance(node):null;return(inst instanceof ui.AbstractElement)?inst:null;},cfgvalue(section_id,set_value){if(section_id==null)
L.error('TypeError','Section ID required');if(arguments.length==2){this.data??={};this.data[section_id]=set_value;}
return this.data?.[section_id];},formvalue(section_id){const elem=this.getUIElement(section_id);return elem?elem.getValue():null;},textvalue(section_id){let cval=this.cfgvalue(section_id);if(cval==null)
cval=this.default;if(Array.isArray(cval))
cval=cval.join(' ');return(cval!=null)?'%h'.format(cval):null;},validate(section_id,value){return true;},getValidator(section_id){if(Array.isArray(this.validate)){const validators=this.validate;const element=this;return(value)=>{for(let val of validators){if(typeof(val)==='function'){const result=val.call(element,section_id,value);if(result!==true)
return result;}}
return true;};}
return L.bind(this.validate,this,section_id);},isValid(section_id){const elem=this.getUIElement(section_id);return elem?elem.isValid():true;},getValidationError(section_id){const elem=this.getUIElement(section_id);return elem?elem.getValidationError():'';},isActive(section_id){const field=this.map.findElement('data-field',this.cbid(section_id));return(field!=null&&!field.classList.contains('hidden'));},setActive(section_id,active){const field=this.map.findElement('data-field',this.cbid(section_id));if(field&&field.classList.contains('hidden')==active){field.classList[active?'remove':'add']('hidden');if(dom.matches(field.parentNode,'.td.cbi-value-field'))
field.parentNode.classList[active?'remove':'add']('inactive');return true;}
return false;},triggerValidation(section_id){const elem=this.getUIElement(section_id);return elem?elem.triggerValidation():true;},parse(section_id){const active=this.isActive(section_id);if(active&&!this.isValid(section_id)){const title=this.stripTags(this.title).trim();const error=this.getValidationError(section_id);return Promise.reject(new TypeError(`${_('Option "%s" contains an invalid input value.').format(title || this.option)} ${error}`));}
if(active){const cval=this.cfgvalue(section_id);const fval=this.formvalue(section_id);if(fval==null||fval==''){if(this.rmempty||this.optional){return Promise.resolve(this.remove(section_id));}
else{const title=this.stripTags(this.title).trim();return Promise.reject(new TypeError(_('Option "%s" must not be empty.').format(title||this.option)));}}
else if(this.forcewrite||!isEqual(cval,fval)){return Promise.resolve(this.write(section_id,fval));}}
else if(!this.retain){return Promise.resolve(this.remove(section_id));}
return Promise.resolve();},write(section_id,formvalue){return this.map.data.set(this.uciconfig??this.section.uciconfig??this.map.config,this.ucisection??section_id,this.ucioption??this.option,formvalue);},remove(section_id){const this_cfg=this.uciconfig??this.section.uciconfig??this.map.config;const this_sid=this.ucisection??section_id;const this_opt=this.ucioption??this.option;for(let i=0;i<this.section.children.length;i++){const sibling=this.section.children[i];if(sibling===this||sibling.ucioption==null)
continue;const sibling_cfg=sibling.uciconfig??sibling.section.uciconfig??sibling.map.config;const sibling_sid=sibling.ucisection??section_id;const sibling_opt=sibling.ucioption??sibling.option;if(this_cfg!=sibling_cfg||this_sid!=sibling_sid||this_opt!=sibling_opt)
continue;if(!sibling.isActive(section_id))
continue;return;}
this.map.data.unset(this_cfg,this_sid,this_opt);}});const CBITypedSection=CBIAbstractSection.extend({__name__:'CBI.TypedSection',cfgsections(){return this.map.data.sections(this.uciconfig??this.map.config,this.sectiontype).map((s)=>{return s['.name']}).filter(L.bind(this.filter,this));},handleAdd(ev,name){const config_name=this.uciconfig??this.map.config;this.map.data.add(config_name,this.sectiontype,name);return this.map.save(null,true);},handleRemove(section_id,ev){const config_name=this.uciconfig??this.map.config;this.map.data.remove(config_name,section_id);return this.map.save(null,true);},renderSectionAdd(extra_class){if(!this.addremove)
return E([]);const createEl=E('div',{'class':'cbi-section-create'});const config_name=this.uciconfig??this.map.config;const btn_title=this.titleFn('addbtntitle');if(extra_class!=null)
createEl.classList.add(extra_class);if(this.anonymous){createEl.appendChild(E('button',{'class':'cbi-button cbi-button-add','title':btn_title??_('Add'),'click':ui.createHandlerFn(this,'handleAdd'),'disabled':this.map.readonly||null},[btn_title??_('Add')]));}
else{const nameEl=E('input',{'type':'text','class':'cbi-section-create-name','disabled':this.map.readonly||null});dom.append(createEl,[E('div',{},nameEl),E('button',{'class':'cbi-button cbi-button-add','title':btn_title??_('Add'),'click':ui.createHandlerFn(this,(ev)=>{if(nameEl.classList.contains('cbi-input-invalid'))
return;return this.handleAdd(ev,nameEl.value);}),'disabled':this.map.readonly||true},[btn_title??_('Add')])]);if(this.map.readonly!==true){ui.addValidator(nameEl,'uciname',true,(v)=>{const button=createEl.querySelector('.cbi-section-create > .cbi-button-add');if(v!==''){button.disabled=null;return true;}
else{button.disabled=true;return _('Expecting: %s').format(_('non-empty value'));}},'blur','keyup');}}
return createEl;},renderSectionPlaceholder(){return E('em',_('This section contains no values yet'));},renderContents(cfgsections,nodes){const section_id=null;const config_name=this.uciconfig??this.map.config;const sectionEl=E('div',{'id':'cbi-%s-%s'.format(config_name,this.sectiontype),'class':'cbi-section','data-tab':(this.map.tabbed&&!this.parentoption)?this.sectiontype:null,'data-tab-title':(this.map.tabbed&&!this.parentoption)?this.title||this.sectiontype:null});if(this.title!=null&&this.title!=''&&!this.hidetitle)
sectionEl.appendChild(E('h3',{},this.title));if(this.description!=null&&this.description!='')
sectionEl.appendChild(E('div',{'class':'cbi-section-descr'},this.description));for(let i=0;i<nodes.length;i++){if(this.addremove){const rem_btn_title=this.titleFn('delbtntitle',section_id);sectionEl.appendChild(E('div',{'class':'cbi-section-remove right'},E('button',{'class':'cbi-button','name':'cbi.rts.%s.%s'.format(config_name,cfgsections[i]),'data-section-id':cfgsections[i],'click':ui.createHandlerFn(this,'handleRemove',cfgsections[i]),'disabled':this.map.readonly||null},[rem_btn_title??_('Delete')])));}
if(!this.anonymous)
sectionEl.appendChild(E('h3',cfgsections[i].toUpperCase()));sectionEl.appendChild(E('div',{'id':'cbi-%s-%s'.format(config_name,cfgsections[i]),'class':this.tabs?'cbi-section-node cbi-section-node-tabbed':'cbi-section-node','data-section-id':cfgsections[i]},nodes[i]));}
if(nodes.length==0)
sectionEl.appendChild(this.renderSectionPlaceholder());sectionEl.appendChild(this.renderSectionAdd());dom.bindClassInstance(sectionEl,this);return sectionEl;},render(){const cfgsections=this.cfgsections();const renderTasks=[];for(let i=0;i<cfgsections.length;i++)
renderTasks.push(this.renderUCISection(cfgsections[i]));return Promise.all(renderTasks).then(this.renderContents.bind(this,cfgsections));}});const CBITableSection=CBITypedSection.extend({__name__:'CBI.TableSection',tab(){throw'Tabs are not supported by TableSection';},handleClone(section_id,put_next,name){let config_name=this.uciconfig||this.map.config;this.map.data.clone(config_name,this.sectiontype,section_id,put_next,name);return this.map.save(null,true);},renderContents(cfgsections,nodes){const section_id=null;const config_name=this.uciconfig??this.map.config;const max_cols=this.max_cols??this.children.length;const cloneable=this.cloneable;const has_more=max_cols<this.children.length;const drag_sort=this.sortable&&!('ontouchstart'in window);const touch_sort=this.sortable&&('ontouchstart'in window);const sectionEl=E('div',{'id':'cbi-%s-%s'.format(config_name,this.sectiontype),'class':'cbi-section cbi-tblsection','data-tab':(this.map.tabbed&&!this.parentoption)?this.sectiontype:null,'data-tab-title':(this.map.tabbed&&!this.parentoption)?this.title||this.sectiontype:null});const tableEl=E('table',{'class':'table cbi-section-table'});const theadEl=E('thead',{'class':'thead cbi-section-thead'});const tbodyEl=E('tbody',{'class':'tbody cbi-section-tbody'});const tfootEl=E('tfoot',{'class':'tfoot cbi-section-tfoot'});if(this.title!=null&&this.title!=''&&!this.hidetitle)
sectionEl.appendChild(E('h3',{},this.title));if(this.description!=null&&this.description!='')
sectionEl.appendChild(E('div',{'class':'cbi-section-descr'},this.description));theadEl.appendChild(this.renderHeaderRows(false));if(theadEl.hasChildNodes())
tableEl.appendChild(theadEl);for(let i=0;i<nodes.length;i++){let sectionname=this.titleFn('sectiontitle',cfgsections[i]);if(sectionname==null)
sectionname=cfgsections[i];const trEl=E('tr',{'id':'cbi-%s-%s'.format(config_name,cfgsections[i]),'class':'tr cbi-section-table-row','data-sid':cfgsections[i],'dragover':drag_sort?L.bind(this.handleDragOver,this):null,'dragenter':drag_sort?L.bind(this.handleDragEnter,this):null,'dragleave':drag_sort?L.bind(this.handleDragLeave,this):null,'dragend':drag_sort?L.bind(this.handleDragEnd,this):null,'drop':drag_sort?L.bind(this.handleDrop,this):null,'touchend':touch_sort?L.bind(this.handleTouchEnd,this):null,'data-title':(sectionname&&(!this.anonymous||this.sectiontitle))?sectionname:null,'data-section-id':cfgsections[i]});if(this.extedit||this.rowcolors)
trEl.classList.add(!(tbodyEl.childNodes.length%2)?'cbi-rowstyle-1':'cbi-rowstyle-2');for(let j=0;j<max_cols&&nodes[i].firstChild;j++)
trEl.appendChild(nodes[i].firstChild);trEl.appendChild(this.renderRowActions(cfgsections[i],has_more?_('More…'):null));tbodyEl.appendChild(trEl);}
if(nodes.length==0)
tbodyEl.appendChild(E('tr',{'class':'tr cbi-section-table-row placeholder'},E('td',{'class':'td'},this.renderSectionPlaceholder())));tableEl.appendChild(tbodyEl);tfootEl.appendChild(this.renderFooterRows(false));if(tfootEl.hasChildNodes())
tableEl.appendChild(tfootEl);sectionEl.appendChild(tableEl);setTimeout(()=>{try{this.stabilizeActionColumnWidth(tableEl);}catch(e){}},0);sectionEl.appendChild(this.renderSectionAdd('cbi-tblsection-create'));dom.bindClassInstance(sectionEl,this);return sectionEl;},renderHeaderRows(has_action){let has_titles=false;let has_descriptions=false;const max_cols=this.max_cols??this.children.length;const has_more=max_cols<this.children.length;const anon_class=(!this.anonymous||this.sectiontitle)?'named':'anonymous';const tableFilter=uci.get('luci','main','tablefilters')||false;const trEls=E([]);for(let i=0,opt;i<max_cols&&(opt=this.children[i])!=null;i++){if(opt.modalonly)
continue;has_titles=has_titles||!!opt.title;has_descriptions=has_descriptions||!!opt.description;}
if(has_titles){const trEl=E('tr',{'class':`tr cbi-section-table-titles ${anon_class}`,'data-title':(!this.anonymous||this.sectiontitle)?_('Name'):null,'click':this.sortable?ui.createHandlerFn(this,'handleSort'):null});for(let i=0,opt;i<max_cols&&(opt=this.children[i])!=null;i++){if(opt.modalonly)
continue;trEl.appendChild(E('th',{'class':'th cbi-section-table-cell','data-widget':opt.__name__,'data-sortable-row':this.sortable?'':null}));if(opt.width!=null)
trEl.lastElementChild.style.width=(typeof(opt.width)=='number')?`${opt.width}px`:opt.width;if(opt.titleref)
trEl.lastElementChild.appendChild(E('a',{'href':opt.titleref,'class':'cbi-title-ref','title':this.titledesc??_('Go to relevant configuration page')},opt.title));else
dom.content(trEl.lastElementChild,opt.title);}
if(this.sortable||this.extedit||this.addremove||has_more||has_action||this.cloneable){const rawTitle=(this.actionstitle!==undefined)?this.actionstitle:null;const actionsTitle=(typeof rawTitle==='function')?rawTitle.call(this,has_action):rawTitle;trEl.appendChild(E('th',{'class':'th cbi-section-table-cell cbi-section-actions'},(actionsTitle!==undefined)?actionsTitle:null));}
trEls.appendChild(trEl);}
if(this.filterrow&&tableFilter){const filterTr=E('tr',{'class':`tr cbi-section-table-filter ${anon_class}`});if(!this.anonymous||this.sectiontitle){filterTr.appendChild(E('th',{'class':'th cbi-section-table-cell'},[E('input',{'type':'text','class':'cbi-input cbi-section-filter','placeholder':_('Filter'),})]));}
for(let i=0,opt;i<max_cols&&(opt=this.children[i])!=null;i++){if(opt.modalonly)continue;const f=/flag/i.test(opt.__name__);const th=E('th',{'class':'th cbi-section-table-cell'},[E('input',{'type':'text','class':'cbi-input cbi-section-filter','placeholder':f?_('0/1'):_('Filter')+' '+opt.title,'maxlength':f?1:'','style':f?'width: 30px;':'',})]);if(opt.width!=null)th.style.width=(typeof(opt.width)=='number')?`${opt.width}px`:opt.width;filterTr.appendChild(th);}
if(this.sortable||this.extedit||this.addremove||has_more||has_action||this.cloneable){filterTr.appendChild(E('th',{'class':'th cbi-section-table-cell cbi-section-actions'},[E('button',{'class':'btn cbi-button cbi-button-neutral','type':'button','title':_('Reset filters'),'click':()=>{const inputs=filterTr.querySelectorAll('input.cbi-section-filter');inputs.forEach(i=>{i.value='';i.dispatchEvent(new Event('input',{bubbles:true}));});const tbl=filterTr.closest('table');try{this.stabilizeActionColumnWidth(tbl);}catch(e){}}},[_('Reset')])]));}
const attachFn=(input)=>{input.addEventListener('input',(ev)=>{const tbl=ev.target.closest('table');if(!tbl)return;const inputs=tbl.querySelectorAll('tr.cbi-section-table-filter input');const col_filts=Array.from(inputs).map(i=>i.value.trim());const rows=tbl.querySelectorAll('tr.tr.cbi-section-table-row');rows.forEach(row=>{const cells=Array.from(row.children).filter(c=>c.classList&&c.classList.contains('td'));let hide=false;for(let k=0;k<col_filts.length;k++){if(!col_filts[k])continue;let txt;const cell=cells[k];const checked=cell?.querySelector('input[type="checkbox"]')?.checked;const select=cell?.querySelector('select');const checkbox=checked!==undefined;if(checkbox)
txt=checked?'1':'0';else if(select)
txt=Array.from(select.selectedOptions).map(opt=>opt.textContent||opt.value.toLowerCase()).join(' ');else
txt=cell.textContent||'';if(!txt.includes(col_filts[k])){hide=true;break;}}
row.style.display=hide?'none':'';});try{this.stabilizeActionColumnWidth(tbl);}catch(e){}});};filterTr.querySelectorAll('input').forEach(attachFn);trEls.appendChild(filterTr);}
if(has_descriptions&&!this.nodescriptions){const trEl=E('tr',{'class':`tr cbi-section-table-descr ${anon_class}`});for(let i=0,opt;i<max_cols&&(opt=this.children[i])!=null;i++){if(opt.modalonly)
continue;trEl.appendChild(E('th',{'class':'th cbi-section-table-cell','data-widget':opt.__name__},opt.description));if(opt.width!=null)
trEl.lastElementChild.style.width=(typeof(opt.width)=='number')?`${opt.width}px`:opt.width;}
if(this.sortable||this.extedit||this.addremove||has_more||has_action||this.cloneable){const rawTitle=(this.actionstitle!==undefined)?this.actionstitle:null;const actionsTitle=(typeof rawTitle==='function')?rawTitle.call(this,has_action):rawTitle;trEl.appendChild(E('th',{'class':'th cbi-section-table-cell cbi-section-actions'},(actionsTitle!==undefined)?actionsTitle:null));}
trEls.appendChild(trEl);}
return trEls;},renderFooterRows(has_action){if(this.footer==null)
return E([]);const max_cols=this.max_cols??this.children.length;const has_more=max_cols<this.children.length;const anon_class=(!this.anonymous||this.sectiontitle)?'named':'anonymous';if(typeof this.footer==='function'){const node=this.footer.call(this,has_action);return node||E([]);}
const values=Array.isArray(this.footer)?this.footer:[];let idx=0;const trEl=E('tr',{'class':`tr cbi-section-table-footer ${anon_class}`});if(!this.anonymous||this.sectiontitle){trEl.appendChild(E('td',{'class':'td cbi-value-field cbi-section-table-titles'},values[idx++]??null));}
for(let i=0,opt;i<max_cols&&(opt=this.children[i])!=null;i++){if(opt.modalonly)
continue;trEl.appendChild(E('td',{'class':'td','data-widget':opt.__name__},values[idx++]??null));}
if(this.sortable||this.extedit||this.addremove||has_more||has_action||this.cloneable){trEl.appendChild(E('td',{'class':'td cbi-section-actions'},values[idx++]??null));}
return trEl;},stabilizeActionColumnWidth(tableEl){if(!tableEl||!tableEl.querySelector)return;const actionDivs=Array.from(tableEl.querySelectorAll('td.cbi-section-actions > div'));let max=0;actionDivs.forEach(div=>{if(div&&div.offsetWidth)max=Math.max(max,div.offsetWidth);});const saved=parseInt(tableEl.dataset.actionColWidth||'0',10)||0;if(max<=0&&saved>0)max=saved;if(max<=0)return;tableEl.dataset.actionColWidth=String(max);const px=`${max}px`;const setStyles=(el)=>{if(!el)return;el.style.minWidth=px;el.style.width=px;};setStyles(tableEl.querySelector('th.cbi-section-actions'));setStyles(tableEl.querySelector('tr.cbi-section-table-filter th.cbi-section-actions'));setStyles(tableEl.querySelector('tr.cbi-section-table-footer td.cbi-section-actions'));actionDivs.forEach(div=>setStyles(div.parentNode));if(!tableEl.__actionColResizeAttached){tableEl.__actionColResizeAttached=true;window.addEventListener('resize',()=>{delete tableEl.dataset.actionColWidth;this.stabilizeActionColumnWidth(tableEl);});}},renderRowActions(section_id,more_label,trEl){const config_name=this.uciconfig??this.map.config;if(!this.sortable&&!this.extedit&&!this.addremove&&!more_label&&!this.cloneable)
return E([]);const tdEl=E('td',{'class':'td cbi-section-table-cell nowrap cbi-section-actions'},E('div'));if(this.sortable){const touch_sort=('ontouchstart'in window);const dragHandleProps={'title':_('Drag to reorder'),'class':'cbi-button drag-handle center','style':'cursor:move; user-select:none; -webkit-user-select:none; display:inline-block;','draggable':!touch_sort,'dragstart':!touch_sort?L.bind(function(ev){this.handleDragStart(ev,trEl);},this):null,'dragend':!touch_sort?L.bind(function(ev){this.handleDragEnd(ev,trEl);},this):null,'touchmove':touch_sort?L.bind(function(ev){this.handleTouchMove(ev);},this):null,'touchend':touch_sort?L.bind(function(ev){this.handleTouchEnd(ev);},this):null};const dragHandle=E('button',dragHandleProps,'☰');dom.append(tdEl.lastElementChild,[dragHandle]);}
if(this.extedit){let evFn=null;if(typeof(this.extedit)=='function')
evFn=L.bind(this.extedit,this);else if(typeof(this.extedit)=='string')
evFn=L.bind((sid,ev)=>{location.href=this.extedit.format(sid);},this,section_id);dom.append(tdEl.lastElementChild,E('button',{'title':_('Edit'),'class':'btn cbi-button cbi-button-edit','click':evFn},[_('Edit')]));}
if(more_label){dom.append(tdEl.lastElementChild,E('button',{'title':more_label,'class':'btn cbi-button cbi-button-edit','click':ui.createHandlerFn(this,'renderMoreOptionsModal',section_id)},[more_label]));}
if(this.cloneable){const btn_title=this.titleFn('clonebtntitle',section_id);dom.append(tdEl.lastElementChild,E('button',{'title':btn_title||_('Clone')+'⿻','class':'btn cbi-button cbi-button-neutral','click':ui.createHandlerFn(this,'handleClone',section_id,true),'disabled':this.map.readonly||null},[btn_title||_('Clone')+'⿻']));}
if(this.addremove){const btn_title=this.titleFn('delbtntitle',section_id);dom.append(tdEl.lastElementChild,E('button',{'title':btn_title??_('Delete'),'class':'btn cbi-button cbi-button-remove','click':ui.createHandlerFn(this,'handleRemove',section_id),'disabled':this.map.readonly||null},[btn_title??_('Delete')]));}
return tdEl;},handleDragInit(ev){scope.dragState={node:ev.target};},handleDragStart(ev,trEl){if(!ev.target||!ev.target.classList||!ev.target.classList.contains('drag-handle')){scope.dragState=null;return false;}
scope.dragState=scope.dragState||{};scope.dragState.node=trEl||dom.parent(ev.target,'.tr');ev.dataTransfer.setData('text','drag');ev.target.style.opacity=0.4;},handleDragOver(ev){if(scope.dragState===null)return;const n=scope.dragState.targetNode;const r=scope.dragState.rect;const t=r.top+r.height/2;if(ev.clientY<=t){n.classList.remove('drag-over-below');n.classList.add('drag-over-above');}
else{n.classList.remove('drag-over-above');n.classList.add('drag-over-below');}
ev.dataTransfer.dropEffect='move';ev.preventDefault();return false;},handleDragEnter(ev){if(scope.dragState===null)return;scope.dragState.rect=ev.currentTarget.getBoundingClientRect();scope.dragState.targetNode=ev.currentTarget;},handleDragLeave(ev){ev.currentTarget.classList.remove('drag-over-above');ev.currentTarget.classList.remove('drag-over-below');},handleDragEnd(ev,trEl){let n;if(trEl){n=trEl;}else if(ev.target&&typeof ev.target.closest==='function'){n=ev.target.closest('tr');}else{return;}
if(!n)return;n.querySelector('.drag-handle').style.opacity='';n.style.opacity='';n.classList.add('flash');n.parentNode.querySelectorAll('.drag-over-above, .drag-over-below').forEach((tr)=>{tr.classList.remove('drag-over-above');tr.classList.remove('drag-over-below');});},handleDrop(ev){const s=scope.dragState;if(!s)return;if(s.node&&s.targetNode){const config_name=this.uciconfig??this.map.config;let ref_node=s.targetNode;let after=false;if(ref_node.classList.contains('drag-over-below')){ref_node=ref_node.nextElementSibling;after=true;}
const sid1=s.node.getAttribute('data-sid');const sid2=s.targetNode.getAttribute('data-sid');s.node.parentNode.insertBefore(s.node,ref_node);this.map.data.move(config_name,sid1,sid2,after);}
scope.dragState=null;ev.target.style.opacity='';ev.stopPropagation();ev.preventDefault();return false;},determineBackgroundColor(node){let r=255;let g=255;let b=255;while(node){const s=window.getComputedStyle(node);const c=(s.getPropertyValue('background-color')??'').replace(/ /g,'');if(c!=''&&c!='transparent'&&c!='rgba(0,0,0,0)'){if(/^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i.test(c)){r=parseInt(RegExp.$1,16);g=parseInt(RegExp.$2,16);b=parseInt(RegExp.$3,16);}
else if(/^rgba?\(([0-9]+),([0-9]+),([0-9]+)[,)]$/.test(c)){r=+RegExp.$1;g=+RegExp.$2;b=+RegExp.$3;}
break;}
node=node.parentNode;}
return[r,g,b];},handleTouchMove(ev){if(!ev.target.classList.contains('drag-handle'))
return;const touchLoc=ev.targetTouches[0];const rowBtn=ev.target;const rowElem=dom.parent(rowBtn,'.tr');const htmlElem=document.querySelector('html');let dragHandle=document.querySelector('.touchsort-element');const viewportHeight=Math.max(document.documentElement.clientHeight,window.innerHeight??0);if(!dragHandle){const rowRect=rowElem.getBoundingClientRect();const btnRect=rowBtn.getBoundingClientRect();const paddingLeft=btnRect.left-rowRect.left;const paddingRight=rowRect.right-btnRect.right;const colorBg=this.determineBackgroundColor(rowElem);const colorFg=(colorBg[0]*0.299+colorBg[1]*0.587+colorBg[2]*0.114)>186?[0,0,0]:[255,255,255];dragHandle=E('div',{'class':'touchsort-element'},[E('strong',[rowElem.getAttribute('data-title')]),rowBtn.cloneNode(true)]);Object.assign(dragHandle.style,{position:'absolute',boxShadow:'0 0 3px rgba(%d, %d, %d, 1)'.format(colorFg[0],colorFg[1],colorFg[2]),background:'rgba(%d, %d, %d, 0.8)'.format(colorBg[0],colorBg[1],colorBg[2]),top:`${rowRect.top}px`,left:`${rowRect.left}px`,width:`${rowRect.width}px`,height:`${rowBtn.offsetHeight + 4}px`});Object.assign(dragHandle.firstElementChild.style,{position:'absolute',lineHeight:dragHandle.style.height,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis',left:(paddingRight>paddingLeft)?'':'5px',right:(paddingRight>paddingLeft)?'5px':'',width:`${Math.max(paddingLeft, paddingRight) - 10}px`});Object.assign(dragHandle.lastElementChild.style,{position:'absolute',top:'2px',left:`${paddingLeft}px`,width:`${rowBtn.offsetWidth}px`});document.body.appendChild(dragHandle);rowElem.classList.remove('flash');rowBtn.blur();}
dragHandle.style.top=`${touchLoc.pageY - (parseInt(dragHandle.style.height) / 2)}px`;rowElem.parentNode.querySelectorAll('.cbi-section-table-row').forEach((tr,i,trs)=>{const trRect=tr.getBoundingClientRect();const yTop=trRect.top+window.scrollY;const yBottom=trRect.bottom+window.scrollY;const yMiddle=yTop+((yBottom-yTop)/2);tr.classList.remove('drag-over-above','drag-over-below');if((i==0||touchLoc.pageY>=yTop)&&touchLoc.pageY<=yMiddle)
tr.classList.add('drag-over-above');else if((i==(trs.length-1)||touchLoc.pageY<=yBottom)&&touchLoc.pageY>yMiddle)
tr.classList.add('drag-over-below');});ev.preventDefault();if(touchLoc.clientY<30)
window.requestAnimationFrame(()=>{htmlElem.scrollTop-=30});else if(touchLoc.clientY>viewportHeight-30)
window.requestAnimationFrame(()=>{htmlElem.scrollTop+=30});},handleTouchEnd(ev){const rowElem=dom.parent(ev.target,'.tr');const htmlElem=document.querySelector('html');const dragHandle=document.querySelector('.touchsort-element');const targetElem=rowElem.parentNode.querySelector('.drag-over-above, .drag-over-below');const viewportHeight=Math.max(document.documentElement.clientHeight,window.innerHeight??0);if(!dragHandle)
return;dragHandle.style.opacity='';if(targetElem){const isBelow=targetElem.classList.contains('drag-over-below');rowElem.parentNode.insertBefore(rowElem,isBelow?targetElem.nextElementSibling:targetElem);this.map.data.move(this.uciconfig??this.map.config,rowElem.getAttribute('data-sid'),targetElem.getAttribute('data-sid'),isBelow);window.requestAnimationFrame(()=>{const rowRect=rowElem.getBoundingClientRect();if(rowRect.top<50)
htmlElem.scrollTop=(htmlElem.scrollTop+rowRect.top-50);else if(rowRect.bottom>viewportHeight-50)
htmlElem.scrollTop=(htmlElem.scrollTop+viewportHeight-50-rowRect.height);rowElem.classList.add('flash');});targetElem.classList.remove('drag-over-above','drag-over-below');}
document.body.removeChild(dragHandle);},handleModalCancel(modalMap,ev){const prevNode=this.getPreviousModalMap();let resetTasks=Promise.resolve();if(prevNode){const heading=prevNode.parentNode.querySelector('h4');let prevMap=dom.findClassInstance(prevNode);while(prevMap){resetTasks=resetTasks.then(L.bind(prevMap.load,prevMap)).then(L.bind(prevMap.reset,prevMap));prevMap=prevMap.parent;}
prevNode.classList.add('flash');prevNode.classList.remove('hidden');prevNode.parentNode.removeChild(prevNode.nextElementSibling);heading.removeChild(heading.lastElementChild);if(!this.getPreviousModalMap())
prevNode.parentNode.querySelector('div.button-row > button').firstChild.data=_('Dismiss');}
else{ui.hideModal();}
return resetTasks;},handleModalSave(modalMap,ev){const mapNode=this.getActiveModalMap();let activeMap=dom.findClassInstance(mapNode);let saveTasks=activeMap.save(null,true);while(activeMap.parent){activeMap=activeMap.parent;saveTasks=saveTasks.then(L.bind(activeMap.load,activeMap)).then(L.bind(activeMap.reset,activeMap));}
return saveTasks.then(L.bind(this.handleModalCancel,this,modalMap,ev,true)).catch(()=>{});},handleSort(ev){if(!ev.target.matches('th[data-sortable-row]'))
return;const th=ev.target;const descending=(th.getAttribute('data-sort-direction')=='desc');const config_name=this.uciconfig??this.map.config;let index=0;const list=[];ev.currentTarget.querySelectorAll('th').forEach((other_th,i)=>{if(other_th!==th)
other_th.removeAttribute('data-sort-direction');else
index=i;});ev.currentTarget.parentNode.querySelectorAll('tr.cbi-section-table-row').forEach(L.bind((tr,i)=>{const sid=tr.getAttribute('data-sid');const opt=tr.childNodes[index].getAttribute('data-name');let val=this.cfgvalue(sid,opt);tr.querySelectorAll('.flash').forEach((n)=>{n.classList.remove('flash')});val=Array.isArray(val)?val.join(' '):val;val=`${val}`;list.push([ui.Table.prototype.deriveSortKey((val!=null&&typeof val.trim==='function')?val.trim():''),tr]);},this));list.sort((a,b)=>{return descending?-L.naturalCompare(a[0],b[0]):L.naturalCompare(a[0],b[0]);});window.requestAnimationFrame(L.bind(()=>{let ref_sid;let cur_sid;for(let i=0;i<list.length;i++){list[i][1].childNodes[index].classList.add('flash');th.parentNode.parentNode.appendChild(list[i][1]);cur_sid=list[i][1].getAttribute('data-sid');if(ref_sid)
this.map.data.move(config_name,cur_sid,ref_sid,true);ref_sid=cur_sid;}
th.setAttribute('data-sort-direction',descending?'asc':'desc');},this));},addModalOptions(modalSection,section_id,ev){},getActiveModalMap(){return document.querySelector('body.modal-overlay-active > #modal_overlay > .modal.cbi-modal > .cbi-map:not(.hidden)');},getPreviousModalMap(){const mapNode=this.getActiveModalMap();const prevNode=mapNode?mapNode.previousElementSibling:null;return(prevNode&&prevNode.matches('.cbi-map.hidden'))?prevNode:null;},cloneOptions(src_section,dest_section){for(let i=0;i<src_section.children.length;i++){const o1=src_section.children[i];if(o1.modalonly===false&&src_section===this)
continue;let o2;if(o1.subsection){o2=dest_section.option(o1.constructor,o1.option,o1.subsection.constructor,o1.subsection.sectiontype,o1.subsection.title,o1.subsection.description);for(const k in o1.subsection){if(!o1.subsection.hasOwnProperty(k))
continue;switch(k){case'map':case'children':case'parentoption':continue;default:o2.subsection[k]=o1.subsection[k];}}
this.cloneOptions(o1.subsection,o2.subsection);}
else{o2=dest_section.option(o1.constructor,o1.option,o1.title,o1.description);}
for(const k in o1){if(!o1.hasOwnProperty(k))
continue;switch(k){case'map':case'section':case'option':case'title':case'description':case'subsection':continue;default:o2[k]=o1[k];}}}},renderMoreOptionsModal(section_id,ev){const parent=this.map;const sref=parent.data.get(parent.config,section_id);const mapNode=this.getActiveModalMap();const activeMap=mapNode?dom.findClassInstance(mapNode):null;const stackedMap=activeMap&&(activeMap.parent!==parent||activeMap.section!==section_id);return(stackedMap?activeMap.save(null,true):Promise.resolve()).then(L.bind(()=>{section_id=sref['.name'];let m;if(parent instanceof CBIJSONMap){m=new CBIJSONMap(null,null,null);m.data=parent.data;}
else{m=new CBIMap(parent.config,null,null);}
const s=m.section(CBINamedSection,section_id,this.sectiontype);m.parent=parent;m.section=section_id;m.readonly=parent.readonly;if(this.tabs){s.tabs=Array.from(this.tabs);for(const key in this.tabs){if(Object.prototype.hasOwnProperty.call(this.tabs,key)&&isNaN(Number(key))){s.tabs[key]=this.tabs[key];}}}else{s.tabs=undefined;}
if(this.tab_names){s.tab_names=Array.isArray(this.tab_names)?this.tab_names.slice():Object.assign({},this.tab_names);}else{s.tab_names=undefined;}
this.cloneOptions(this,s);return Promise.resolve(this.addModalOptions(s,section_id,ev)).then(()=>{return m.render();}).then(L.bind((nodes)=>{let title=parent.title;let name=null;if((name=this.titleFn('modaltitle',section_id))!=null)
title=name;else if((name=this.titleFn('sectiontitle',section_id))!=null)
title='%s - %s'.format(parent.title,name);else if(!this.anonymous)
title='%s - %s'.format(parent.title,section_id);if(stackedMap){mapNode.parentNode.querySelector('h4').appendChild(E('span',title?` » ${title}`:''));mapNode.parentNode.querySelector('div.button-row > button').firstChild.data=_('Dismiss');mapNode.classList.add('hidden');mapNode.parentNode.insertBefore(nodes,mapNode.nextElementSibling);nodes.classList.add('flash');}
else{ui.showModal(title,[nodes,E('div',{'class':'button-row'},[E('button',{'class':'btn cbi-button','click':ui.createHandlerFn(this,'handleModalCancel',m)},[_('Dismiss')]),' ',E('button',{'class':'btn cbi-button cbi-button-positive important','click':ui.createHandlerFn(this,'handleModalSave',m),'disabled':m.readonly||null},[_('Save')])])],'cbi-modal');}},this));},this)).catch(L.error);}});const CBIGridSection=CBITableSection.extend({tab(name,title,description){CBIAbstractSection.prototype.tab.call(this,name,title,description);},handleAdd(ev,name){const config_name=this.uciconfig??this.map.config;const section_id=this.map.data.add(config_name,this.sectiontype,name);const mapNode=this.getPreviousModalMap();const prevMap=mapNode?dom.findClassInstance(mapNode):this.map;prevMap.addedSection=section_id;return this.renderMoreOptionsModal(section_id);},handleModalSave(...args){const mapNode=this.getPreviousModalMap();const prevMap=mapNode?dom.findClassInstance(mapNode):this.map;return this.super('handleModalSave',args);},handleModalCancel(modalMap,ev,isSaving){const config_name=this.uciconfig??this.map.config;const mapNode=this.getPreviousModalMap();const prevMap=mapNode?dom.findClassInstance(mapNode):this.map;if(prevMap.addedSection!=null&&!isSaving)
this.map.data.remove(config_name,prevMap.addedSection);delete prevMap.addedSection;return this.super('handleModalCancel',arguments);},renderUCISection(section_id){return this.renderOptions(null,section_id);},renderChildren(tab_name,section_id,in_table){const tasks=[];let index=0;for(let i=0,opt;(opt=this.children[i])!=null;i++){if(opt.disable||opt.modalonly)
continue;if(opt.editable)
tasks.push(opt.render(index++,section_id,in_table));else
tasks.push(this.renderTextValue(section_id,opt));}
return Promise.all(tasks);},renderTextValue(section_id,opt){const title=this.stripTags(opt.title).trim();const descr=this.stripTags(opt.description).trim();const value=opt.textvalue(section_id);return E('td',{'class':'td cbi-value-field','data-title':(title!='')?title:null,'data-description':(descr!='')?descr:null,'data-name':opt.option,'data-widget':'CBI.DummyValue'},(value!=null)?value:E('em',_('none')));},renderHeaderRows(section_id){return this.super('renderHeaderRows',[true]);},renderRowActions(section_id){return this.super('renderRowActions',[section_id,_('Edit')]);},parse(){const section_ids=this.cfgsections();const tasks=[];if(Array.isArray(this.children)){for(let i=0;i<section_ids.length;i++){for(let j=0;j<this.children.length;j++){if(!this.children[j].editable||this.children[j].modalonly)
continue;tasks.push(this.children[j].parse(section_ids[i]));}}}
return Promise.all(tasks);}});const CBINamedSection=CBIAbstractSection.extend({__name__:'CBI.NamedSection',__init__(map,section_id,...args){this.super('__init__',[map,...args]);this.section=section_id;},cfgsections(){return[this.section];},handleAdd(ev){const section_id=this.section;const config_name=this.uciconfig??this.map.config;this.map.data.add(config_name,this.sectiontype,section_id);return this.map.save(null,true);},handleRemove(ev){const section_id=this.section;const config_name=this.uciconfig??this.map.config;this.map.data.remove(config_name,section_id);return this.map.save(null,true);},renderContents(data){const ucidata=data[0];const nodes=data[1];const section_id=this.section;const config_name=this.uciconfig??this.map.config;const sectionEl=E('div',{'id':ucidata?null:'cbi-%s-%s'.format(config_name,section_id),'class':'cbi-section','data-tab':(this.map.tabbed&&!this.parentoption)?this.sectiontype:null,'data-tab-title':(this.map.tabbed&&!this.parentoption)?this.title||this.sectiontype:null});if(typeof(this.title)==='string'&&this.title!==''&&!this.hidetitle)
sectionEl.appendChild(E('h3',{},this.title));if(typeof(this.description)==='string'&&this.description!=='')
sectionEl.appendChild(E('div',{'class':'cbi-section-descr'},this.description));if(ucidata){if(this.addremove){const rem_btn_title=this.titleFn('delbtntitle',section_id);sectionEl.appendChild(E('div',{'class':'cbi-section-remove right'},E('button',{'class':'cbi-button','click':ui.createHandlerFn(this,'handleRemove'),'disabled':this.map.readonly||null},[rem_btn_title??_('Delete')])));}
sectionEl.appendChild(E('div',{'id':'cbi-%s-%s'.format(config_name,section_id),'class':this.tabs?'cbi-section-node cbi-section-node-tabbed':'cbi-section-node','data-section-id':section_id},nodes));}
else if(this.addremove){const add_btn_title=this.titleFn('addbtntitle',section_id);sectionEl.appendChild(E('button',{'class':'cbi-button cbi-button-add','click':ui.createHandlerFn(this,'handleAdd'),'disabled':this.map.readonly||null},[add_btn_title??_('Add')]));}
dom.bindClassInstance(sectionEl,this);return sectionEl;},render(){const config_name=this.uciconfig??this.map.config;const section_id=this.section;return Promise.all([this.map.data.get(config_name,section_id),this.renderUCISection(section_id)]).then(this.renderContents.bind(this));}});const CBIValue=CBIAbstractValue.extend({__name__:'CBI.Value',value(key,val){this.keylist??=[];this.keylist.push(String(key));this.vallist??=[];this.vallist.push(dom.elem(val)?val:String(val!=null?val:key));},render(option_index,section_id,in_table){return Promise.resolve(this.cfgvalue(section_id)).then(this.renderWidget.bind(this,section_id,option_index)).then(this.renderFrame.bind(this,section_id,in_table,option_index));},handleValueChange(section_id,state,ev){if(typeof(this.onchange)!='function')
return;const value=this.formvalue(section_id);if(isEqual(value,state.previousValue))
return;state.previousValue=value;this.onchange.call(this,ev,section_id,value);},renderFrame(section_id,in_table,option_index,nodes){const config_name=this.uciconfig??this.section.uciconfig??this.map.config;const depend_list=this.transformDepList(section_id);let optionEl;if(in_table){const title=this.stripTags(this.title).trim();optionEl=E('td',{'class':'td cbi-value-field','data-title':(title!='')?title:null,'data-description':this.stripTags(this.description).trim(),'data-name':this.option,'data-widget':this.typename||(this.template?this.template.replace(/^.+\//,''):null)||this.__name__},E('div',{'id':'cbi-%s-%s-%s'.format(config_name,section_id,this.option),'data-index':option_index,'data-depends':depend_list,'data-field':this.cbid(section_id)}));}
else{optionEl=E('div',{'class':'cbi-value','id':'cbi-%s-%s-%s'.format(config_name,section_id,this.option),'data-index':option_index,'data-depends':depend_list,'data-field':this.cbid(section_id),'data-name':this.option,'data-widget':this.typename||(this.template?this.template.replace(/^.+\//,''):null)||this.__name__});if(this.last_child)
optionEl.classList.add('cbi-value-last');if(typeof(this.title)==='string'&&this.title!==''){optionEl.appendChild(E('label',{'class':'cbi-value-title','for':'widget.cbid.%s.%s.%s'.format(config_name,section_id,this.option),'click':(ev)=>{const node=ev.currentTarget;const elem=node.nextElementSibling.querySelector(`#${node.getAttribute('for')}`)??node.nextElementSibling.querySelector(`[data-widget-id="${node.getAttribute('for')}"]`);if(elem){elem.click();elem.focus();}}},this.titleref?E('a',{'class':'cbi-title-ref','href':this.titleref,'title':this.titledesc??_('Go to relevant configuration page')},this.title):this.title));optionEl.appendChild(E('div',{'class':'cbi-value-field'}));}}
if(nodes)
(optionEl.lastChild??optionEl).appendChild(nodes);if(!in_table&&typeof(this.description)==='string'&&this.description!=='')
dom.append(optionEl.lastChild??optionEl,E('div',{'class':'cbi-value-description'},this.description.trim()));if(depend_list&&depend_list.length)
if(in_table)
optionEl.firstChild.classList.add('hidden');else
optionEl.classList.add('hidden');optionEl.addEventListener('widget-change',L.bind(this.map.checkDepends,this.map));optionEl.addEventListener('widget-change',L.bind(this.handleValueChange,this,section_id,{}));dom.bindClassInstance(optionEl,this);return optionEl;},renderWidget(section_id,option_index,cfgvalue){const value=(cfgvalue!=null)?cfgvalue:this.default;const choices=this.transformChoices();let widget;if(choices){const placeholder=(this.optional||this.rmempty)?E('em',_('unspecified')):_('-- Please choose --');widget=new ui.Combobox(Array.isArray(value)?value.join(' '):value,choices,{id:this.cbid(section_id),sort:this.keylist,optional:this.optional||this.rmempty,datatype:this.datatype,select_placeholder:this.placeholder??placeholder,validate:this.getValidator(section_id),disabled:(this.readonly!=null)?this.readonly:this.map.readonly});}
else{widget=new ui.Textfield(Array.isArray(value)?value.join(' '):value,{id:this.cbid(section_id),password:this.password,optional:this.optional||this.rmempty,datatype:this.datatype,placeholder:this.placeholder,validate:this.getValidator(section_id),disabled:(this.readonly!=null)?this.readonly:this.map.readonly});}
return widget.render();}});const CBIDynamicList=CBIValue.extend({__name__:'CBI.DynamicList',renderWidget(section_id,option_index,cfgvalue){const value=(cfgvalue!=null)?cfgvalue:this.default;const choices=this.transformChoices();const items=L.toArray(value);const widget=new ui.DynamicList(items,choices,{id:this.cbid(section_id),sort:this.keylist,optional:this.optional||this.rmempty,datatype:this.datatype,placeholder:this.placeholder,validate:this.getValidator(section_id),disabled:(this.readonly!=null)?this.readonly:this.map.readonly});return widget.render();},});const CBIListValue=CBIValue.extend({__name__:'CBI.ListValue',__init__(...args){this.super('__init__',args);this.widget='select';this.orientation='horizontal';this.deplist=[];},renderWidget(section_id,option_index,cfgvalue){const choices=this.transformChoices();const widget=new ui.Select((cfgvalue!=null)?cfgvalue:this.default,choices,{id:this.cbid(section_id),size:this.size,sort:this.keylist,widget:this.widget,optional:this.optional,orientation:this.orientation,placeholder:this.placeholder,validate:this.getValidator(section_id),disabled:(this.readonly!=null)?this.readonly:this.map.readonly});return widget.render();},});const CBIRichListValue=CBIListValue.extend({__name__:'CBI.RichListValue',__init__(){this.super('__init__',arguments);this.widget='select';this.orientation='horizontal';this.deplist=[];},renderWidget(section_id,option_index,cfgvalue){const choices=this.transformChoices();const widget=new ui.Dropdown((cfgvalue!=null)?cfgvalue:this.default,choices,{id:this.cbid(section_id),size:this.size,sort:this.keylist,widget:this.widget,multiple:this.multiple,optional:this.optional,orientation:this.orientation,select_placeholder:this.select_placeholder||this.placeholder,custom_placeholder:this.custom_placeholder||this.placeholder,validate:this.getValidator(section_id),disabled:(this.readonly!=null)?this.readonly:this.map.readonly});return widget.render();},value(value,title,description){if(description){CBIListValue.prototype.value.call(this,value,E([],[E('span',{'class':'hide-open'},[title]),E('div',{'class':'hide-close','style':'min-width:25vw'},[E('strong',[title]),E('br'),E('span',{'style':'white-space:normal'},description)])]));}
else{CBIListValue.prototype.value.call(this,value,title);}}});const CBIRangeSliderValue=CBIValue.extend({__name__:'CBI.RangeSliderValue',renderWidget(section_id,option_index,cfgvalue){const slider=new ui.RangeSlider((cfgvalue!=null)?cfgvalue:this.default,{id:this.cbid(section_id),name:this.cbid(section_id),min:this.min,max:this.max,step:this.step,calculate:this.calculate,calcunits:this.calcunits,disabled:this.readonly||this.disabled,datatype:this.datatype,validate:this.getValidator(section_id),});this.widget=slider;return slider.render();},formvalue(section_id){const elem=this.getUIElement(section_id);if(!elem)return null;const val=elem.getValue().toString();return(val===this.default?.toString())?null:val;}});const CBIFlagValue=CBIValue.extend({__name__:'CBI.FlagValue',__init__(...args){this.super('__init__',args);this.enabled='1';this.disabled='0';this.default=this.disabled;},renderWidget(section_id,option_index,cfgvalue){let tooltip=null;if(typeof(this.tooltip)=='function')
tooltip=this.tooltip(section_id);else if(typeof(this.tooltip)=='string')
tooltip=this.tooltip.format(section_id);const widget=new ui.Checkbox((cfgvalue!=null)?cfgvalue:this.default,{id:this.cbid(section_id),value_enabled:this.enabled,value_disabled:this.disabled,validate:this.getValidator(section_id),tooltip,tooltipicon:this.tooltipicon,disabled:(this.readonly!=null)?this.readonly:this.map.readonly});return widget.render();},formvalue(section_id){const elem=this.getUIElement(section_id);const checked=elem?elem.isChecked():false;return checked?this.enabled:this.disabled;},textvalue(section_id){let cval=this.cfgvalue(section_id);if(cval==null)
cval=this.default;return(cval==this.enabled)?_('Yes'):_('No');},parse(section_id){if(this.isActive(section_id)){const fval=this.formvalue(section_id);if(!this.isValid(section_id)){const title=this.stripTags(this.title).trim();const error=this.getValidationError(section_id);return Promise.reject(new TypeError(`${_('Option "%s" contains an invalid input value.').format(title || this.option)} ${error}`));}
if(fval==this.default&&(this.optional||this.rmempty))
return Promise.resolve(this.remove(section_id));else
return Promise.resolve(this.write(section_id,fval));}
else if(!this.retain){return Promise.resolve(this.remove(section_id));}},});const CBIMultiValue=CBIDynamicList.extend({__name__:'CBI.MultiValue',__init__(...args){this.super('__init__',args);this.placeholder=_('-- Please choose --');},renderWidget(section_id,option_index,cfgvalue){const value=(cfgvalue!=null)?cfgvalue:this.default;const choices=this.transformChoices();const widget=new ui.Dropdown(L.toArray(value),choices,{id:this.cbid(section_id),sort:this.keylist,multiple:true,optional:this.optional||this.rmempty,select_placeholder:this.placeholder,create:this.create,display_items:this.display_size??this.size??3,dropdown_items:this.dropdown_size??this.size??-1,validate:this.getValidator(section_id),disabled:(this.readonly!=null)?this.readonly:this.map.readonly});return widget.render();},});const CBITextValue=CBIValue.extend({__name__:'CBI.TextValue',value:null,renderWidget(section_id,option_index,cfgvalue){const value=(cfgvalue!=null)?cfgvalue:this.default;const widget=new ui.Textarea(value,{id:this.cbid(section_id),optional:this.optional||this.rmempty,placeholder:this.placeholder,monospace:this.monospace,cols:this.cols,rows:this.rows,wrap:this.wrap,validate:this.getValidator(section_id),readonly:(this.readonly!=null)?this.readonly:this.map.readonly,disabled:(this.disabled!=null)?this.disabled:null,});return widget.render();}});const CBIDummyValue=CBIValue.extend({__name__:'CBI.DummyValue',renderWidget(section_id,option_index,cfgvalue){const value=(cfgvalue!=null)?cfgvalue:this.default;const hiddenEl=new ui.Hiddenfield(value,{id:this.cbid(section_id)});const outputEl=E('output',{'style':this.hidden?'display:none':null,'for':this.cbid(section_id)});if(this.href&&!((this.readonly!=null)?this.readonly:this.map.readonly))
outputEl.appendChild(E('a',{'href':this.href}));dom.append(outputEl.lastChild??outputEl,this.rawhtml?value:[value]);return E([outputEl,hiddenEl.render()]);},remove(){},write(){}});const CBIButtonValue=CBIValue.extend({__name__:'CBI.ButtonValue',renderWidget(section_id,option_index,cfgvalue){const value=(cfgvalue!=null)?cfgvalue:this.default;const hiddenEl=new ui.Hiddenfield(value,{id:this.cbid(section_id)});const outputEl=E('output',{'for':this.cbid(section_id)});const btn_title=this.titleFn('inputtitle',section_id)??this.titleFn('title',section_id);if(value!==false)
dom.content(outputEl,[E('button',{'class':'cbi-button cbi-button-%s'.format(this.inputstyle??'button'),'click':ui.createHandlerFn(this,(section_id,ev)=>{if(this.onclick)
return this.onclick(ev,section_id);ev.currentTarget.parentNode.nextElementSibling.value=value;return this.map.save();},section_id),'disabled':(this.readonly??this.map.readonly)||null},[btn_title])]);else
dom.content(outputEl,' - ');return E([outputEl,hiddenEl.render()]);}});const CBIHiddenValue=CBIValue.extend({__name__:'CBI.HiddenValue',renderWidget(section_id,option_index,cfgvalue){const widget=new ui.Hiddenfield((cfgvalue!=null)?cfgvalue:this.default,{id:this.cbid(section_id)});return widget.render();}});const CBIFileUpload=CBIValue.extend({__name__:'CBI.FileSelect',__init__(...args){this.super('__init__',args);this.browser=false;this.directory_create=false;this.directory_select=false;this.show_hidden=false;this.enable_upload=true;this.enable_remove=true;this.enable_download=false;this.root_directory='/etc/luci-uploads';},renderWidget(section_id,option_index,cfgvalue){const browserEl=new ui.FileUpload((cfgvalue!=null)?cfgvalue:this.default,{id:this.cbid(section_id),name:this.cbid(section_id),browser:this.browser,show_hidden:this.show_hidden,directory_create:this.directory_create,directory_select:this.directory_select,enable_upload:this.enable_upload,enable_remove:this.enable_remove,enable_download:this.enable_download,root_directory:this.root_directory,disabled:(this.readonly!=null)?this.readonly:this.map.readonly});return browserEl.render();}});const CBIDirectoryPicker=CBIValue.extend({__name__:'CBI.DirectoryPicker',__init__(...args){this.super('__init__',args);this.browser=false;this.directory_create=false;this.enable_download=false;this.enable_remove=false;this.enable_upload=false;this.root_directory='/tmp';this.show_hidden=true;},renderWidget(section_id,option_index,cfgvalue){const browserEl=new ui.FileUpload((cfgvalue!=null)?cfgvalue:this.default,{id:this.cbid(section_id),name:this.cbid(section_id),browser:this.browser,directory_create:this.directory_create,directory_select:true,enable_download:this.enable_download,enable_remove:this.enable_remove,enable_upload:this.enable_upload,root_directory:this.root_directory,show_hidden:this.show_hidden,disabled:(this.readonly!=null)?this.readonly:this.map.readonly});return browserEl.render();}});const CBISectionValue=CBIValue.extend({__name__:'CBI.ContainerValue',__init__(map,section,option,cbiClass,...args){this.super('__init__',[map,section,option]);if(!CBIAbstractSection.isSubclass(cbiClass))
throw'Sub section must be a descendent of CBIAbstractSection';this.subsection=cbiClass.instantiate([this.map,...args]);this.subsection.parentoption=this;},load(section_id){return this.subsection.load(section_id);},parse(section_id){return this.subsection.parse(section_id);},renderWidget(section_id,option_index,cfgvalue){return this.subsection.render(section_id);},checkDepends(section_id){this.subsection.checkDepends(section_id);return CBIValue.prototype.checkDepends.apply(this,[section_id]);},value(){},write(){},remove(){},cfgvalue(){return null},formvalue(){return null}});return baseclass.extend({Map:CBIMap,JSONMap:CBIJSONMap,AbstractSection:CBIAbstractSection,AbstractValue:CBIAbstractValue,TypedSection:CBITypedSection,TableSection:CBITableSection,GridSection:CBIGridSection,NamedSection:CBINamedSection,Value:CBIValue,DynamicList:CBIDynamicList,ListValue:CBIListValue,RichListValue:CBIRichListValue,RangeSliderValue:CBIRangeSliderValue,Flag:CBIFlagValue,MultiValue:CBIMultiValue,TextValue:CBITextValue,DummyValue:CBIDummyValue,Button:CBIButtonValue,HiddenValue:CBIHiddenValue,FileUpload:CBIFileUpload,DirectoryPicker:CBIDirectoryPicker,SectionValue:CBISectionValue});