import { createApp } from 'petite-vue' import { domain, nopoll } from '@params' type Config = { apiurl: string pollurl: string domain: string itemid: string sid: string iid: string nopoll: boolean } export function config (api, polling): Config { const pu = polling ? `${api.path}/${polling.msgbase.join('/')}` : '' const urlparams = new URL(location.href).searchParams return { apiurl: api.path, pollurl: pu, domain: domain, itemid: urlparams.get('id'), sid: getSid(), iid: createRandString(1), nopoll: nopoll } } export const pvapp = { run(conf: Config) { appdata.conf = conf createApp(appdata).mount() if (appdata.conf.pollurl && !appdata.conf.nopoll) { appdata.poll() } } } const appdata = { $delimiters: ['{|', '|}'], conf: {} as Config, components: {}, output: '', handle, poll, mounted(name: string) { console.log('app mounted: ', name) }, Data } // components // (move to separate module(s)) function Data(name, action, domain: string): object { domain = domain || this.conf.domain const comp = { name: name, // -> class of incoming/outgoing message action: action, // (default) action of outgoing message domain: domain, // domain of incoming/outgoing message data: {}, // model (2-way data store) data_bak: {}, // backup copy when leaving view or edit mode meta: {}, // metadata (params) for each data field mode: 'view', mounted, chmode, copynew, exec // default function to execute upon button click } this.components[name] = comp return comp } // appdata and component method definitions function mounted(name: string, meta: any) { if (meta.defexpr) { meta.default = eval(meta.defexpr) } this.data[name] = meta.default || "" this.meta[name] = meta } function chmode(action: string) { if (this.mode == action) { return } if (this.mode == 'view') { Object.assign(this.data_bak, this.data) } switch(action) { case 'query': setQuery(this) break case 'new': setNew(this) break default: Object.assign(this.data, this.data_bak) } this.mode = action } function copynew(action: string) { Object.assign(this.data, this.data_bak) this.data.id = '' this.mode = 'new' } function exec(action: string) { action = action || this.action const data = this.data const conf = this.conf let value = '' for (const k of Object.keys(data)) { value += `${k}: ${data[k]}, ` } this.output += '\n' + value console.log('exec:', value) sendMsg(conf, [this.domain, action, this.name, data.id], data) } function handle(msg) { const data = JSON.parse(msg.payload) const [ domain, action, class_, item ] = msg.path.split('/') // TODO: check action => select handler fct const comp = this.components[class_ || 'data'] if (domain && domain != comp.domain) { return } if (comp) { Object.assign(comp.data, data) } } function poll() { dopoll(this) } // helper functions for data manipulation function setQuery(obj) { for (const key in obj.data) { obj.data[key] = '' } } function setNew(obj) { for (const key in obj.data) { obj.data[key] = '' const meta = obj.meta[key] if (meta) { obj.data[key] = meta.default || '' } } obj.data.id = '' } // basic functions - move to api.ts async function sendMsg(conf: Config, basemsg: string[], data: any) { const url = `${conf.apiurl}/${basemsg.join('/')}` await send(url, conf, data) } async function send(url: string, conf: Config, data: any) { data._interaction = conf.iid const body = JSON.stringify(data) const headers = {} headers['X-Integrator-Session'] = conf.sid return fetch(url, { method: 'POST', headers: headers, body: body }) } function createRandString(size: number): string { const arr = new Uint32Array(size) crypto.getRandomValues(arr) const result: string[] = [] arr.forEach((x) => result.push(x.toString(36))) return result.join('') } function getSid(): string { const sid_key = 'api.sessionid' let sid = localStorage.getItem(sid_key) if (!sid) { sid = createRandString(2) localStorage.setItem(sid_key, sid) } return sid } //console.log("sid: ", getSid()) // TODO: clear sid - when? //localStorage.setItem('api.sessionid', '') async function dopoll(app: typeof appdata) { const wait_time = 10000 while (true) { try { const res = await(send(app.conf.pollurl, app.conf, {})) const msg = await res.json() console.log(msg) switch (msg.status) { case 'idle': break case 'data': app.handle(msg) break default: console.log('poll response: ', msg) await sleep(wait_time) } } catch (error) { console.log(error) await sleep(wait_time) } } } const sleep = (delay: number) => new Promise(r => setTimeout(r, delay))