diff --git a/extra_script.py b/extra_script.py index cb0f57d..8d58014 100644 --- a/extra_script.py +++ b/extra_script.py @@ -10,10 +10,18 @@ Import("env") GEN_DIR='generated' CFG_FILE='web/config.json' XDR_FILE='web/xdrconfig.json' -FILES=['web/index.html',CFG_FILE,XDR_FILE,'web/index.js','web/index.css'] CFG_INCLUDE='GwConfigDefinitions.h' XDR_INCLUDE='GwXdrTypeMappings.h' +EMBEDDED_INCLUDE="GwEmbeddedFiles.h" +def getEmbeddedFiles(env): + rt=[] + efiles=env.GetProjectOption("board_build.embed_files") + for f in efiles.split("\n"): + if f == '': + continue + rt.append(f) + return rt def basePath(): #see: https://stackoverflow.com/questions/16771894/python-nameerror-global-name-file-is-not-defined @@ -38,126 +46,165 @@ def isCurrent(infile,outfile): print("%s is newer then %s, no need to recreate"%(outfile,infile)) return True return False -def compressFile(inFile): - outfile=os.path.basename(inFile)+".gz" - inFile=os.path.join(basePath(),inFile) - outfile=os.path.join(outPath(),outfile) +def compressFile(inFile,outfile): if isCurrent(inFile,outfile): return with open(inFile, 'rb') as f_in: with gzip.open(outfile, 'wb') as f_out: shutil.copyfileobj(f_in, f_out) - -def generateCfg(): - outfile=os.path.join(outPath(),CFG_INCLUDE) - infile=os.path.join(basePath(),CFG_FILE) +def generateFile(infile,outfile,callback,inMode='rb',outMode='w'): if isCurrent(infile,outfile): return - print("creating %s"%CFG_INCLUDE) + print("creating %s"%outfile) oh=None - with open(CFG_FILE,'rb') as ch: - config=json.load(ch) - try: - with open(outfile,'w') as oh: - oh.write("//generated from %s\n"%CFG_FILE) - oh.write('#include "GwConfigItem.h"\n') - l=len(config) - oh.write('class GwConfigDefinitions{\n') - oh.write(' public:\n') - oh.write(' int getNumConfig() const{return %d;}\n'%(l)) - for item in config: - n=item.get('name') - if n is None: - continue - if len(n) > 15: - raise Exception("%s: config names must be max 15 caracters"%n) - oh.write(' const String %s=F("%s");\n'%(n,n)) - oh.write(' protected:\n') - oh.write(' GwConfigItem *configs[%d]={\n'%(l)) - first=True - for item in config: - if not first: - oh.write(',\n') - first=False - oh.write(" new GwConfigItem(%s,\"%s\")"%(item.get('name'),item.get('default'))) - oh.write('};\n') - oh.write('};\n') - oh.close() - except Exception as e: - if oh is not None: + with open(infile,inMode) as ch: + with open(outfile,'w') as oh: + try: + callback(ch,oh,inFile=infile) + oh.close() + except Exception as e: try: oh.close() except: pass - os.unlink(outfile) - raise + os.unlink(outfile) + raise -def generateXdrMappings(): - outfile=os.path.join(outPath(),XDR_INCLUDE) - infile=os.path.join(basePath(),XDR_FILE) - if isCurrent(infile,outfile): - return - print("creating %s"%XDR_INCLUDE) - oh=None +def writeFileIfChanged(fileName,data): + if os.path.exists(fileName): + with open(fileName,"r") as ih: + old=ih.read() + ih.close() + if old == data: + return + print("#generating %s"%fileName) + with open(fileName,"w") as oh: + oh.write(data) - with open(infile,"rb") as fp: - jdoc=json.load(fp) - try: - with open(outfile,"w") as oh: - oh.write("static GwXDRTypeMapping* typeMappings[]={\n") - first=True - for cat in jdoc: - item=jdoc[cat] - cid=item.get('id') - if cid is None: - continue - tc=item.get('type') - if tc is not None: - if first: - first=False - else: - oh.write(",\n") - oh.write(" new GwXDRTypeMapping(%d,%d,0) /*%s*/"%(cid,tc,cat)) - fields=item.get('fields') - if fields is None: - continue - idx=0 - for fe in fields: - if not isinstance(fe,dict): - continue - tc=fe.get('t') - id=fe.get('v') - if id is None: - id=idx - idx+=1 - l=fe.get('l') or '' - if tc is None or id is None: - continue - if first: - first=False - else: - oh.write(",\n") - oh.write(" new GwXDRTypeMapping(%d,%d,%d) /*%s:%s*/"%(cid,tc,id,cat,l)) - oh.write("\n") - oh.write("};\n") - except Exception as e: - if oh: - try: - oh.close() - except: - pass - os.unlink(outfile) - raise +def generateCfg(ch,oh,inFile=''): + config=json.load(ch) + oh.write("//generated from %s\n"%inFile) + oh.write('#include "GwConfigItem.h"\n') + l=len(config) + oh.write('class GwConfigDefinitions{\n') + oh.write(' public:\n') + oh.write(' int getNumConfig() const{return %d;}\n'%(l)) + for item in config: + n=item.get('name') + if n is None: + continue + if len(n) > 15: + raise Exception("%s: config names must be max 15 caracters"%n) + oh.write(' const String %s=F("%s");\n'%(n,n)) + oh.write(' protected:\n') + oh.write(' GwConfigItem *configs[%d]={\n'%(l)) + first=True + for item in config: + if not first: + oh.write(',\n') + first=False + oh.write(" new GwConfigItem(%s,\"%s\")"%(item.get('name'),item.get('default'))) + oh.write('};\n') + oh.write('};\n') + +def generateXdrMappings(fp,oh,inFile=''): + jdoc=json.load(fp) + oh.write("static GwXDRTypeMapping* typeMappings[]={\n") + first=True + for cat in jdoc: + item=jdoc[cat] + cid=item.get('id') + if cid is None: + continue + tc=item.get('type') + if tc is not None: + if first: + first=False + else: + oh.write(",\n") + oh.write(" new GwXDRTypeMapping(%d,0,%d) /*%s*/"%(cid,tc,cat)) + fields=item.get('fields') + if fields is None: + continue + idx=0 + for fe in fields: + if not isinstance(fe,dict): + continue + tc=fe.get('t') + id=fe.get('v') + if id is None: + id=idx + idx+=1 + l=fe.get('l') or '' + if tc is None or id is None: + continue + if first: + first=False + else: + oh.write(",\n") + oh.write(" new GwXDRTypeMapping(%d,%d,%d) /*%s:%s*/"%(cid,id,tc,cat,l)) + oh.write("\n") + oh.write("};\n") +def generateEmbedded(elist,outFile): + content="" + for entry in elist: + content+="EMBED_GZ_FILE(\"%s\",%s,\"%s\");\n"%entry + writeFileIfChanged(outFile,content) -if not checkDir(): - sys.exit(1) -for f in FILES: - print("compressing %s"%f) - compressFile(f) -generateCfg() -generateXdrMappings() -version="dev"+datetime.now().strftime("%Y%m%d") -env.Append(CPPDEFINES=[('GWDEVVERSION',version)]) +def getContentType(fn): + if (fn.endswith('.gz')): + fn=fn[0:-3] + if (fn.endswith('html')): + return "text/html" + if (fn.endswith('json')): + return "application/json" + if (fn.endswith('js')): + return "text/javascript" + if (fn.endswith('css')): + return "text/css" + return "application/octet-stream" + +def prebuild(env): + print("#prebuild running") + if not checkDir(): + sys.exit(1) + embedded=getEmbeddedFiles(env) + filedefs=[] + for ef in embedded: + print("#checking embedded file %s"%ef) + (dn,fn)=os.path.split(ef) + pureName=fn + if pureName.endswith('.gz'): + pureName=pureName[0:-3] + ct=getContentType(pureName) + usname=ef.replace('/','_').replace('.','_') + filedefs.append((pureName,usname,ct)) + inFile=os.path.join(basePath(),"web",pureName) + if os.path.exists(inFile): + print("compressing %s"%inFile) + compressFile(inFile,ef) + else: + print("#WARNING: infile %s for %s not found"%(inFile,ef)) + generateEmbedded(filedefs,os.path.join(outPath(),EMBEDDED_INCLUDE)) + generateFile(os.path.join(basePath(),CFG_FILE),os.path.join(outPath(),CFG_INCLUDE),generateCfg) + generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings) + version="dev"+datetime.now().strftime("%Y%m%d") + env.Append(CPPDEFINES=[('GWDEVVERSION',version)]) + +def cleangenerated(source, target, env): + od=outPath() + if os.path.isdir(od): + print("#cleaning up %s"%od) + for f in os.listdir(od): + if f == "." or f == "..": + continue + fn=os.path.join(od,f) + os.unlink(f) + +print("#prescript...") +prebuild(env) +#script does not run on clean yet - maybe in the future +env.AddPostAction("clean",cleangenerated) diff --git a/lib/webserver/GwWebServer.cpp b/lib/webserver/GwWebServer.cpp index 0dafd09..a09c711 100644 --- a/lib/webserver/GwWebServer.cpp +++ b/lib/webserver/GwWebServer.cpp @@ -16,15 +16,12 @@ class EmbeddedFile { embeddedFiles[name]=this; } } ; -#define EMBED_GZ_FILE(fileName, fileExt, contentType) \ - extern const uint8_t fileName##_##fileExt##_File[] asm("_binary_generated_" #fileName "_" #fileExt "_gz_start"); \ - extern const uint8_t fileName##_##fileExt##_FileLen[] asm("_binary_generated_" #fileName "_" #fileExt "_gz_size"); \ - const EmbeddedFile fileName##_##fileExt##_Config(#fileName "." #fileExt,contentType,(const uint8_t*)fileName##_##fileExt##_File,(int)fileName##_##fileExt##_FileLen); +#define EMBED_GZ_FILE(fileName, binName, contentType) \ + extern const uint8_t binName##_File[] asm("_binary_" #binName "_start"); \ + extern const uint8_t binName##_FileLen[] asm("_binary_" #binName "_size"); \ + const EmbeddedFile binName##_Config(fileName,contentType,(const uint8_t*)binName##_File,(int)binName##_FileLen); -EMBED_GZ_FILE(index,html,"text/html") -EMBED_GZ_FILE(config,json,"application/json") -EMBED_GZ_FILE(index,js,"text/javascript") -EMBED_GZ_FILE(index,css,"text/css") +#include "GwEmbeddedFiles.h" void sendEmbeddedFile(String name,String contentType,AsyncWebServerRequest *request){ std::map::iterator it=embeddedFiles.find(name); diff --git a/platformio.ini b/platformio.ini index 4bf75a3..f3f6732 100644 --- a/platformio.ini +++ b/platformio.ini @@ -23,6 +23,7 @@ board_build.embed_files = generated/index.js.gz generated/index.css.gz generated/config.json.gz + generated/xdrconfig.json.gz board_build.partitions = partitions_custom.csv extra_scripts = pre:extra_script.py diff --git a/web/index.css b/web/index.css index fce2825..af37f2d 100644 --- a/web/index.css +++ b/web/index.css @@ -1,3 +1,6 @@ +*{ + box-sizing: border-box; +} body{ font-family: Arial, Helvetica, sans-serif; } @@ -89,6 +92,58 @@ body{ .hidden{ display: none !important; } + #xdrPage .content.hidden { + display: unset !important; + } + #xdrPage .category .title{ + display: none; + } + #xdrPage span.label{ + width: 4em; + } + #xdrPage .value{ + width: 24em; + } + + .xdrline { + padding-top: 0.2em; + padding-bottom: 0.2em; + display: flex; + flex-direction: row; + flex-wrap: wrap; + } + .xdrunused{ + opacity: 0; + pointer-events: none; + } + span.xdrlabel { + width: 8em; + display: inline-block; + } + .xdrinput .xdrdir { + width: 12em; + } + .xdrinput .xdrdir:before { + content: 'Direction'; + } + .xdrinput .xdrcat { + width: 12em; + } + .xdrinput .xdrsel { + width: 12em; + } + .xdrinput .xdrfield { + width: 12em; + } + .xdrinput .xdrimode { + width: 8em; + } + .xdrinput .xdrinstance { + width: 4em; + } + .xdrinput .xdrname { + width: 16em; + } .msgDetails .value { width: 5em; text-align: right; diff --git a/web/index.html b/web/index.html index 0768854..abe2a4c 100644 --- a/web/index.html +++ b/web/index.html @@ -17,6 +17,7 @@
Status
Config
+
XDR
Data
@@ -56,6 +57,16 @@
+ diff --git a/web/index.js b/web/index.js index d83230c..f4cc6e6 100644 --- a/web/index.js +++ b/web/index.js @@ -3,12 +3,14 @@ let lastUpdate = (new Date()).getTime(); let reloadConfig = false; function addEl(type, clazz, parent, text) { let el = document.createElement(type); - if ( ! (clazz instanceof Array)){ - clazz=clazz.split(/ */); + if (clazz) { + if (!(clazz instanceof Array)) { + clazz = clazz.split(/ */); + } + clazz.forEach(function (ce) { + el.classList.add(ce); + }); } - clazz.forEach(function(ce){ - el.classList.add(ce); - }); if (text) el.textContent = text; if (parent) parent.appendChild(el); return el; @@ -230,15 +232,22 @@ function checkChange(el, row) { } } } -function createInput(configItem, frame) { +let configDefinitions; +let xdrConfig; +function createInput(configItem, frame,clazz) { let el; if (configItem.type === 'boolean' || configItem.type === 'list') { - el = document.createElement('select') + el=addEl('select',clazz,frame); el.setAttribute('name', configItem.name) let slist = []; if (configItem.list) { configItem.list.forEach(function (v) { - slist.push({ l: v, v: v }); + if (v instanceof Object){ + slist.push({l:v.l,v:v.v}); + } + else{ + slist.push({ l: v, v: v }); + } }) } else { @@ -246,64 +255,19 @@ function createInput(configItem, frame) { slist.push({ l: 'off', v: 'false' }) } slist.forEach(function (sitem) { - let sitemEl = document.createElement('option'); + let sitemEl = addEl('option','',el,sitem.l); sitemEl.setAttribute('value', sitem.v); - sitemEl.textContent = sitem.l; - el.appendChild(sitemEl); }) - frame.appendChild(el); return el; } if (configItem.type === 'filter') { - el = document.createElement('div'); - el.classList.add('filter'); - let ais = createInput({ - type: 'list', - name: configItem.name + "_ais", - list: ['aison', 'aisoff'] - }, el); - let mode = createInput({ - type: 'list', - name: configItem.name + "_mode", - list: ['whitelist', 'blacklist'] - }, el); - let sentences = createInput({ - type: 'text', - name: configItem.name + "_sentences", - }, el); - let data = document.createElement('input'); - data.setAttribute('type', 'hidden'); - el.appendChild(data); - let changeFunction = function () { - let cv = data.value || ""; - let parts = cv.split(":"); - ais.value = (parts[0] == '0') ? "aisoff" : "aison"; - mode.value = (parts[1] == '0') ? "whitelist" : "blacklist"; - sentences.value = parts[2] || ""; - } - let updateFunction = function () { - let nv = (ais.value == 'aison') ? "1" : "0"; - nv += ":"; - nv += (mode.value == 'blacklist') ? "1" : "0"; - nv += ":"; - nv += sentences.value; - data.value = nv; - let chev = new Event('change'); - data.dispatchEvent(chev); - } - mode.addEventListener('change', updateFunction); - ais.addEventListener("change", updateFunction); - sentences.addEventListener("change", updateFunction); - data.addEventListener('change', function (ev) { - changeFunction(); - }); - data.setAttribute('name', configItem.name); - frame.appendChild(el); - return data; + return createFilterInput(configItem,frame,clazz); } - el = document.createElement('input'); + if (configItem.type === 'xdr'){ + return createXdrInput(configItem,frame,clazz); + } + el = addEl('input',clazz,frame); el.setAttribute('name', configItem.name) - frame.appendChild(el); if (configItem.type === 'password') { el.setAttribute('type', 'password'); let vis = addEl('span', 'icon-eye icon', frame); @@ -324,78 +288,281 @@ function createInput(configItem, frame) { } return el; } -let configDefinitions; + +function updateSelectList(item,slist){ + item.innerHTML=''; + slist.forEach(function (sitem) { + let sitemEl = addEl('option','',item,sitem.l); + sitemEl.setAttribute('value', sitem.v); + }) +} +function getXdrCategories(){ + let rt=[]; + for (let c in xdrConfig){ + if (xdrConfig[c].enabled !== false){ + rt.push({l:c,v:xdrConfig[c].id}); + } + } + return rt; +} +function getXdrSelectors(category){ + category=parseInt(category); + for (let c in xdrConfig){ + let base=xdrConfig[c]; + if (parseInt(base.id) == category){ + return base.selector || []; + } + } + return []; +} +function getXdrFields(category){ + category=parseInt(category); + for (let c in xdrConfig){ + let base=xdrConfig[c]; + if (parseInt(base.id) == category){ + return base.fields || []; + } + } + return []; +} + +function createXdrLine(parent,label){ + let d=addEl('div','xdrline',parent); + addEl('span','xdrlabel',d,label); + return d; +} +function showHideXdr(el,show,useParent){ + if (useParent) el=el.parentElement; + if (show) el.classList.remove('xdrunused'); + else el.classList.add('xdrunused'); +} + +function createXdrInput(configItem,frame){ + let el = addEl('div','xdrinput',frame); + let d=createXdrLine(el,'Direction'); + let direction=createInput({ + type:'list', + name: configItem.name+"_dir", + list: [ + //GwXDRMappingDef::Direction + {l:'off',v:0}, + {l:'bidir',v:1}, + {l:'to2K',v:2}, + {l:'from2K',v:3} + ] + },d,'xdrdir'); + d=createXdrLine(el,'Category'); + let category=createInput({ + type: 'list', + name: configItem.name+"_cat", + list:getXdrCategories() + },d,'xdrcat'); + d=createXdrLine(el,'Source'); + let selector=createInput({ + type: 'list', + name: configItem.name+"_sel", + list:[] + },d,'xdrsel'); + d=createXdrLine(el,'Field'); + let field=createInput({ + type:'list', + name: configItem.name+'_field', + list: [] + },d,'xdrfield'); + d=createXdrLine(el,'Instance'); + let imode=createInput({ + type:'list', + name: configItem.name+"_imode", + list:[ + //GwXDRMappingDef::InstanceMode + {l:'single',v:0}, + {l:'ignore',v:1}, + {l:'auto',v:2} + ] + },d,'xdrimode'); + let instance=createInput({ + type:'number', + name: configItem.name+"_instance", + },d,'xdrinstance'); + d=createXdrLine(el,'Transducer'); + let xdrName=createInput({ + type:'text', + name: configItem.name+"_xdr" + },d,'xdrname'); + let data = addEl('input',undefined,el); + data.setAttribute('type', 'hidden'); + data.setAttribute('name', configItem.name); + let changeFunction = function () { + let parts=data.value.split(','); + direction.value=parts[1] || 0; + category.value=parts[0] || 0; + let selectors=getXdrSelectors(category.value); + updateSelectList(selector,selectors); + showHideXdr(selector,selectors.length>0); + let fields=getXdrFields(category.value); + updateSelectList(field,fields); + showHideXdr(field,fields.length>0); + selector.value=parts[2]||0; + field.value=parts[3]||0; + imode.value=parts[4]||0; + instance.value=parts[5]||0; + showHideXdr(instance,imode.value == 0); + xdrName.value=parts[6]||''; + } + let updateFunction = function () { + let txt=category.value+","+direction.value+","+ + selector.value+","+field.value+","+imode.value; + let instanceValue=parseInt(instance.value||0); + if (isNaN(instanceValue)) instanceValue=0; + if (instanceValue<0) instanceValue=0; + if (instanceValue>255) instanceValue=255; + txt+=","+instanceValue; + let xdr=xdrName.value.replace(/[^a-zA-Z0-9]/g,''); + xdr=xdr.substr(0,12); + txt+=","+xdr; + data.value=txt; + let ev=new Event('change'); + data.dispatchEvent(ev); + } + category.addEventListener('change',updateFunction); + direction.addEventListener('change',updateFunction); + selector.addEventListener('change',updateFunction); + field.addEventListener('change',updateFunction); + imode.addEventListener('change',updateFunction); + instance.addEventListener('change',updateFunction); + xdrName.addEventListener('change',updateFunction); + data.addEventListener('change',changeFunction); + return data; +} + +function createFilterInput(configItem, frame) { + let el = addEl('div','filter',frame); + let ais = createInput({ + type: 'list', + name: configItem.name + "_ais", + list: ['aison', 'aisoff'] + }, el); + let mode = createInput({ + type: 'list', + name: configItem.name + "_mode", + list: ['whitelist', 'blacklist'] + }, el); + let sentences = createInput({ + type: 'text', + name: configItem.name + "_sentences", + }, el); + let data = addEl('input',undefined,el); + data.setAttribute('type', 'hidden'); + let changeFunction = function () { + let cv = data.value || ""; + let parts = cv.split(":"); + ais.value = (parts[0] == '0') ? "aisoff" : "aison"; + mode.value = (parts[1] == '0') ? "whitelist" : "blacklist"; + sentences.value = parts[2] || ""; + } + let updateFunction = function () { + let nv = (ais.value == 'aison') ? "1" : "0"; + nv += ":"; + nv += (mode.value == 'blacklist') ? "1" : "0"; + nv += ":"; + nv += sentences.value; + data.value = nv; + let chev = new Event('change'); + data.dispatchEvent(chev); + } + mode.addEventListener('change', updateFunction); + ais.addEventListener("change", updateFunction); + sentences.addEventListener("change", updateFunction); + data.addEventListener('change', function (ev) { + changeFunction(); + }); + data.setAttribute('name', configItem.name); + return data; +} + +function createConfigDefinitions(parent, capabilities, defs,includeXdr) { + let category; + let categoryEl; + let frame = parent.querySelector('.configFormRows'); + if (!frame) throw Error("no config form"); + frame.innerHTML = ''; + configDefinitions = defs; + defs.forEach(function (item) { + if (!item.type) return; + if ((item.category === 'xdr') !== includeXdr) return; + if (item.category != category || !categoryEl) { + let categoryFrame = addEl('div', 'category', frame); + let categoryTitle = addEl('div', 'title', categoryFrame); + let categoryButton = addEl('span', 'icon icon-more', categoryTitle); + addEl('span', 'label', categoryTitle, item.category); + categoryEl = addEl('div', 'content', categoryFrame); + categoryEl.classList.add('hidden'); + let currentEl = categoryEl; + categoryTitle.addEventListener('click', function (ev) { + let rs = currentEl.classList.toggle('hidden'); + if (rs) { + categoryButton.classList.add('icon-more'); + categoryButton.classList.remove('icon-less'); + } + else { + categoryButton.classList.remove('icon-more'); + categoryButton.classList.add('icon-less'); + } + }) + category = item.category; + } + if (item.capabilities !== undefined) { + for (let capability in item.capabilities) { + let values = item.capabilities[capability]; + if (!capabilities[capability]) return; + let found = false; + values.forEach(function (v) { + if (capabilities[capability] == v) found = true; + }); + if (!found) return; + } + } + let row = addEl('div', 'row', categoryEl); + let label = item.label || item.name; + addEl('span', 'label', row, label); + let valueFrame = addEl('div', 'value', row); + let valueEl = createInput(item, valueFrame); + if (!valueEl) return; + valueEl.setAttribute('data-default', item.default); + valueEl.addEventListener('change', function (ev) { + let el = ev.target; + checkChange(el, row); + }) + if (item.check) valueEl.setAttribute('data-check', item.check); + let btContainer = addEl('div', 'buttonContainer', row); + let bt = addEl('button', 'defaultButton', btContainer, 'X'); + bt.setAttribute('data-default', item.default); + bt.addEventListener('click', function (ev) { + valueEl.value = valueEl.getAttribute('data-default'); + let changeEvent = new Event('change'); + valueEl.dispatchEvent(changeEvent); + }) + bt = addEl('button', 'infoButton', btContainer, '?'); + bt.addEventListener('click', function (ev) { + showOverlay(item.description); + }); + }) +} function loadConfigDefinitions() { getJson("api/capabilities") .then(function (capabilities) { getJson("config.json") .then(function (defs) { - let category; - let categoryEl; - let frame = document.querySelector('.configFormRows'); - if (!frame) throw Error("no config form"); - frame.innerHTML = ''; - configDefinitions = defs; - defs.forEach(function (item) { - if (!item.type) return; - if (item.category != category || !categoryEl) { - let categoryFrame = addEl('div', 'category', frame); - let categoryTitle = addEl('div', 'title', categoryFrame); - let categoryButton = addEl('span', 'icon icon-more', categoryTitle); - addEl('span', 'label', categoryTitle, item.category); - categoryEl = addEl('div', 'content', categoryFrame); - categoryEl.classList.add('hidden'); - let currentEl = categoryEl; - categoryTitle.addEventListener('click', function (ev) { - let rs = currentEl.classList.toggle('hidden'); - if (rs) { - categoryButton.classList.add('icon-more'); - categoryButton.classList.remove('icon-less'); - } - else { - categoryButton.classList.remove('icon-more'); - categoryButton.classList.add('icon-less'); - } - }) - category = item.category; - } - if (item.capabilities !== undefined) { - for (let capability in item.capabilities) { - let values = item.capabilities[capability]; - if (!capabilities[capability]) return; - let found = false; - values.forEach(function (v) { - if (capabilities[capability] == v) found = true; - }); - if (!found) return; - } - } - let row = addEl('div', 'row', categoryEl); - let label = item.label || item.name; - addEl('span', 'label', row, label); - let valueFrame = addEl('div', 'value', row); - let valueEl = createInput(item, valueFrame); - if (!valueEl) return; - valueEl.setAttribute('data-default', item.default); - valueEl.addEventListener('change', function (ev) { - let el = ev.target; - checkChange(el, row); + getJson("xdrconfig.json") + .then(function(xdr){ + xdrConfig=xdr; + configDefinitions=defs; + let normalConfig=document.getElementById('configPage'); + let xdrParent=document.getElementById('xdrPage'); + if (normalConfig) createConfigDefinitions(normalConfig,capabilities,defs,false); + if (xdrParent) createConfigDefinitions(xdrParent,capabilities,defs,true); + resetForm(); }) - if (item.check) valueEl.setAttribute('data-check', item.check); - let btContainer = addEl('div', 'buttonContainer', row); - let bt = addEl('button', 'defaultButton', btContainer, 'X'); - bt.setAttribute('data-default', item.default); - bt.addEventListener('click', function (ev) { - valueEl.value = valueEl.getAttribute('data-default'); - let changeEvent = new Event('change'); - valueEl.dispatchEvent(changeEvent); - }) - bt = addEl('button', 'infoButton', btContainer, '?'); - bt.addEventListener('click', function (ev) { - showOverlay(item.description); - }); - }) - resetForm(); }) }) .catch(function (err) { alert("unable to load config: " + err) })