import { DeclareInstruction, Expression, Instruction, InstructionType, ObjectData, PrimitiveData, Scope, Tag, XmlError } from "@cyklang/core"; import loglevel from "loglevel"; import { WindowManager } from "./WindowManager"; import { Capacitor } from "@capacitor/core"; import { Browser } from "@capacitor/browser" import { useCykStore } from "./cykStore"; const logger = loglevel.getLogger('WindowTabManager.ts'); logger.setLevel('debug'); interface Tab { name: string url: string window: Window } export class WindowTabManager { openedTabs: Map = new Map() constructor(moduleName: string) { const currentWindowName = window.name const newName = window.location.origin + ' ' + moduleName if (currentWindowName === newName) { logger.debug('window.name ALREADY equals to ' + newName) } else { logger.debug('window.name changed from ' + currentWindowName + ' to ' + newName) window.name = newName } } openTab(tabname: string, url: string) { logger.debug('openTab tabname ' + tabname + ', url ' + url) const openedTab = this.openedTabs.get(tabname) if (openedTab && !openedTab.window.closed) { logger.debug('openTab found -> focus') openedTab.window.focus() } else { const newWindowName = window.location.origin + ' ' + tabname const newWindow = window.open(url, newWindowName) if (!newWindow) { throw 'WindowTabManager unable to open ' + url + ' to a window named ' + newWindowName } logger.debug('openTab window.open( ' + url + ', ' + newWindowName) this.openedTabs.set(tabname, { name: tabname, url, window: newWindow }) } } } export class OpenWindowTabInstructionType extends InstructionType { windowManager: WindowManager constructor(windowManager: WindowManager) { super('open_window_tab'); this.windowManager = windowManager } async parseInstruction(tag: Tag, scope: Scope): Promise { return new OpenWindowTabInstruction(tag, this) } } export class OpenWindowTabInstruction extends Instruction { instructionType: OpenWindowTabInstructionType; constructor(tag: Tag, instructionType: OpenWindowTabInstructionType) { super(tag); this.instructionType = instructionType } async execute(scope: Scope) { logger.debug('OpenWindowTabInstruction.execute BEGIN') const paramScope = new Scope(scope.structure, scope, undefined) let pname: string | undefined let purl: string | undefined for (let ind = 0; ind < this.tag.childs.length; ind++) { const child = this.tag.childs[ind]; const declareInstruction = new DeclareInstruction(child, undefined) await declareInstruction.execute(paramScope) if (!declareInstruction.variable || declareInstruction.variable.dataType.name !== 'string') { throw new XmlError(' invalid parameter type', child) } switch (declareInstruction.name) { case 'name': pname = declareInstruction.variable.getString() break; case 'url': purl = declareInstruction.variable.getString() break; default: throw new XmlError(' unknown parameter ' + declareInstruction.name, child) } } if (!pname) throw new XmlError(' name parameter is missing', this.tag) if (!purl) throw new XmlError(' url parameter is missing', this.tag) this.instructionType.windowManager.windowTabManager.openTab(pname, purl) } } export class SetPathInstructionType extends InstructionType { windowManager: WindowManager; constructor(windowManager: WindowManager) { super('setpath') this.windowManager = windowManager } async parseInstruction(tag: Tag, scope: Scope): Promise { const inst = new SetPathInstruction(tag, scope); return inst } } class SetPathInstruction extends Instruction { outerScope: Scope constructor(tag: Tag, outerScope: Scope) { super(tag) this.outerScope = outerScope } async execute(scope: Scope): Promise { try { const data = await this.outerScope.structure.stringDataType.parseData(this.tag, this.outerScope) if (data) { const path = String((data as PrimitiveData).value) window.location.hash = path } } catch (err) { if (err instanceof XmlError) throw err throw new XmlError(String(err), this.tag) } } } /** * */ export class SetWindowNameInstructionType extends InstructionType { windowManager: WindowManager constructor(windowManager: WindowManager) { super('set_window_name') this.windowManager = windowManager } async parseInstruction(tag: Tag, scope: Scope): Promise { const inst = new SetWindowNameInstruction(tag, scope, this.windowManager) return inst } } /** * */ class SetWindowNameInstruction extends Instruction { outerScope: Scope windowManager: WindowManager constructor(tag: Tag, outerScope: Scope, windowManager: WindowManager) { super(tag) this.outerScope = outerScope this.windowManager = windowManager } async execute(scope: Scope): Promise { try { const data = await this.outerScope.structure.stringDataType.parseData(this.tag, this.outerScope) if (data) { const name = String((data as PrimitiveData).value) window.name = name } } catch (err) { if (err instanceof XmlError) throw err throw new XmlError(String(err), this.tag) } } } /** * */ export class OpenWindowLocationInstructionType extends InstructionType { windowManager: WindowManager; constructor(windowManager: WindowManager) { super('open_window_location') this.windowManager = windowManager } async parseInstruction(tag: Tag, scope: Scope): Promise { const inst = new OpenWindowLocationInstruction(tag, scope, this.windowManager); return inst } } /** * */ class OpenWindowLocationInstruction extends Instruction { outerScope: Scope windowManager: WindowManager /** * * @param tag * @param outerScope * @param windowManager */ constructor(tag: Tag, outerScope: Scope, windowManager: WindowManager) { super(tag) this.outerScope = outerScope this.windowManager = windowManager } /** * * @param scope */ async execute(scope: Scope): Promise { try { const scopeParams = new Scope(scope.structure, scope, undefined) for (let ind = 0; ind < this.tag.childs.length; ind++) { const child = this.tag.childs[ind] const declareInstruction = new DeclareInstruction(child, undefined) await declareInstruction.execute(scopeParams) } const p_name = scopeParams.variables.getString('name') const p_protocol = scopeParams.variables.getString('protocol') const p_host = scopeParams.variables.getString('host') const p_pathname = scopeParams.variables.getString('pathname') const p_hash = scopeParams.variables.getString('hash') const p_url = scopeParams.variables.getString('url') const p_reload = scopeParams.variables.getData("reload") const p_search = scopeParams.variables.getData('search') const p_query = scopeParams.variables.getData('query') let reload = false if (p_reload !== undefined && p_reload !== null && p_reload.type.isPrimitive()) { reload = Boolean((p_reload as PrimitiveData).value) } let encodedParams = new URLSearchParams() const p_params = p_query || p_search if (p_params) { if (p_params.type.isPrimitive()) { encodedParams = new URLSearchParams(p_params.toString()) } else if (p_params.type.isObject()) { const objQuery = p_params as ObjectData objQuery.variables.forEach(({ name, variable }) => { if (name) { encodedParams.append(name, variable.getString() || '') } }) } } let query: string | undefined let url = '' const isNative = Capacitor.isNativePlatform(); if (p_url) { url = p_url } else if (p_host) { query = encodedParams.toString() url = `${p_protocol || window.location.protocol}//${p_host}${p_pathname}?${query}` } else { let origin = window.location.origin let pathname = p_pathname || window.location.pathname || '' if (isNative && p_name) { encodedParams.append('authorization', `Bearer ${useCykStore().user.accessJWT}`) origin = process.env.DBREMOTE_URL || '' } query = encodedParams.toString() url = `${origin}${pathname}?${query || ''}${p_hash || ''}` } if (p_name) { if (isNative) { logger.debug(` native url = ${url}`) await Browser.open({ url }) } else { logger.debug(` web url = ${url}`) const win = window.open(url, p_name) if (win) { if (reload) { win.location.reload() } win.focus() } } } else { logger.debug(` href = ${url}`) window.location.href = url } } catch (err) { logger.error(err) if (err instanceof XmlError) throw err throw new XmlError(String(err), this.tag) } } } /** * */ export class ReloadWindowLocationInstructionType extends InstructionType { windowManager: WindowManager; constructor(windowManager: WindowManager) { super('reload_window_location') this.windowManager = windowManager } async parseInstruction(tag: Tag, scope: Scope): Promise { const inst = new ReloadWindowLocationInstruction(tag, scope, this.windowManager); return inst } } /** * */ class ReloadWindowLocationInstruction extends Instruction { outerScope: Scope windowManager: WindowManager constructor(tag: Tag, outerScope: Scope, windowManager: WindowManager) { super(tag) this.outerScope = outerScope this.windowManager = windowManager } async execute(scope: Scope): Promise { try { window.location.reload() } catch (err) { if (err instanceof XmlError) throw err throw new XmlError(String(err), this.tag) } } }