import { onUnmounted, Ref, ref, watch } from 'vue'; import { useCykStore } from './cykStore'; import { DBModule, Expression, loadLanguageModule, ModuleInstruction, ObjectData, parseXML, PrimitiveData, Scope, Tag, Variable, Variables, XmlError, } from '@cyklang/core'; import { WindowManager, } from './WindowManager'; import { ComponentModel, // FormDataValidation, PushModalDialogFunction, ValidationFunction, } from './cykLang' import loglevel from 'loglevel'; // import { // getDBManager, // structure, // verifyAccessRight, // verifyDBSession, // } from '../services/cyklang'; import { Router } from 'vue-router'; import { useCykLang } from './cykLang'; import { useI18n } from 'vue-i18n' const logger = loglevel.getLogger('cykRun.ts'); logger.setLevel('debug'); type HideFunction = () => Promise; export type ShowMessageFunction = (title?: string, message?: string, ok?: string, cancel?: string) => Promise let t_source_file = 'Source File' let t_line = 'Line Number' let last_router: Router | undefined /** * function AlertException * @param err */ export const AlertException = (err: any) => { const store = useCykStore() let goto = '' console.log(err) let message = String(err) if (err instanceof XmlError) { const xmlError = err as XmlError message = t_source_file + ' : ' + xmlError.tag.filename + ', ' + t_line + ' : ' + (Number(xmlError.tag.line) + 1) + '\n' + xmlError.message if (xmlError.tag.filename === '') { goto = '/edit' } } if (goto === '' && !store.user.logged) { goto = '/login' } alert(message) // logger.debug('AlertException last_router', last_router) if (goto !== '' && last_router) { // logger.debug('store.loginRoute set to', store.loginRoute) // last_router.replace(goto) window.location.hash = goto } }; //------------------------------------------------------------------------------------------------ // function useCykRun //------------------------------------------------------------------------------------------------ export function useCykRun( props: { xmlSource: string | undefined, moduleName: string | undefined, }, showMessageFunction: ShowMessageFunction, router: Router, browserLanguage: string, showLoading: ( msg: string ) => void, hideLoading: () => void ) { last_router = router // try { const { t } = useI18n({ useScope: 'global' }); t_source_file = t('source_file') t_line = t('line') // } // catch (err) { // console.log(err) // logger.error(err) // } onUnmounted(() => { if (store.variableReact === refFormComponent.value?.variableReact) { store.variableReact = undefined; if (store.toolbarData !== undefined) { store.toolbarData = undefined; store.keyToolbarData = 0; } } }); const { getDBManager, structure, verifyAccessRight, } = useCykLang() const isLoading = ref(true); const store = useCykStore(); store.loginRoute = router.currentRoute.value.path // const router = useRouter(); // const route = useRoute(); let moduleInstruction: ModuleInstruction; // moduleExist const moduleExist = async (module: string): Promise => { let result: DBModule | undefined try { // await verifyDBSession(router); const dbManager = await getDBManager(); result = await dbManager.dbModuleExist(module); } catch (err) { // logger.debug('moduleExist exception', err) logger.error(err) } return result } // fetchModule const fetchModule = async (module: string): Promise => { let result; try { const dbModule = await moduleExist(module) if (dbModule === undefined) { logger.error('--- fetchModule module ' + module + ' not found') throw '---- fetchModule module ' + module + ' not found'; } if (dbModule.auth) { const store = useCykStore() if (!store.user.logged) { window.location.hash = '/login' throw t('authentication_required') } } verifyAccessRight( String(store.user.user_access), dbModule.access || '', 'Exec Permission on Module ' + module, 'x' ); result = dbModule.source; // logger.debug('fetchModule.complete '+module) } catch (err) { // logger.debug('fetchModule exception', err) logger.error(err); throw err; } finally { // $q.loading.hide(); } return result; }; //--------------------------------------------------------------------------------- // Form API //--------------------------------------------------------------------------------- const refFormComponent = ref(); const keyFormComponent = ref(0); const setFormComponentValue = (formComponent: ComponentModel | undefined) => { if (formComponent === refFormComponent.value) return if (refFormComponent.value !== undefined && refFormComponent.value.resolve !== undefined) { refFormComponent.value.resolve() } refFormComponent.value = formComponent store.variableReact = formComponent && formComponent.variableReact keyFormComponent.value++ } const validateForm: ValidationFunction = async (): Promise => { let result = true; if (refFormComponent.value === undefined) { logger.error('validateForm when form undefined'); } else if (refFormComponent.value.validationFunction === undefined) { logger.error('validationFunction is undefined'); } else { result = await refFormComponent.value.validationFunction(); } return result; }; const browserHistoryPush = (url: string) => { router.push(url); }; //--------------------------------------------------------------------------------- // Modal Dialog API //--------------------------------------------------------------------------------- const dialogStack: ComponentModel[] = []; const dialogHidefn: HideFunction[] = []; const key0 = ref(0); const key1 = ref(0); const key2 = ref(0); const key3 = ref(0); const key4 = ref(0); const key5 = ref(0); const dialogKey: Ref[] = [key0, key1, key2, key3, key4, key5]; // let dialogIndex = -1; const dialogCounter = ref(0); const dumpStack = () => { logger.debug('dumpStack dialogCounter ' + dialogCounter.value); for (let ind = 0; ind < dialogCounter.value; ind++) { const formDataValidation = dialogStack[ind]; logger.debug( 'ind=' + ind + ', key=' + dialogKey[ind].value + ', name=' + formDataValidation.objectData?.tag.attributes.NAME ); } }; const pushModalDialog: PushModalDialogFunction = (dialogComponent: ComponentModel): Promise => { if (dialogCounter.value >= dialogKey.length) { throw Error( 'La pile des boites de dialogue a une taille de ' + dialogKey.length + ' qui a été atteinte' ); } // const formData = new FormDataValidation(dialogData); dialogStack.push(dialogComponent); dialogKey[dialogCounter.value].value = 0; dialogCounter.value++; // dumpStack(); if (dialogComponent.promise === undefined) { throw 'dialogComponent.promise is undefined' } return dialogComponent.promise; }; const popModalDialog = async () => { await dialogHidefn[dialogCounter.value - 1](); // dialogIndex--; const formData = dialogStack.pop(); //dialogKey.pop(); if (formData !== undefined) { if (formData.resolve !== undefined) formData.resolve(); dialogCounter.value--; // dumpStack(); } // logger.debug('//////// dialogCounter ' + dialogCounter.value); }; const refreshModalDialog = () => { if (dialogStack.length > 0) { logger.debug( '=== refreshModalDialog ' + dialogStack[dialogStack.length - 1].objectData?.tag.attributes.NAME ); dialogKey[dialogStack.length - 1].value = dialogKey[dialogStack.length - 1].value + 1; // dumpStack(); } }; const resetModalDialogDirty = () => { if (dialogStack.length > 0) { dialogStack[dialogStack.length - 1].dirtyManager?.resetDirty(); } }; const escape = () => { popModalDialog(); }; const sendEventModalDialog = (componentName: string, event: string) => { if (dialogStack.length > 0) { dialogStack[dialogStack.length - 1].variableReact?.sendEvent( componentName, event ); } else if (refFormComponent.value !== undefined) { refFormComponent.value.variableReact?.sendEvent(componentName, event); } }; const validateModalDialog: ValidationFunction = async (): Promise => { let result = true; logger.debug('validateModalDialog DEBUT'); if (dialogStack.length === 0) { logger.error('validateModalDialog when no dialog shown'); } else { const formDataValidation = dialogStack[dialogStack.length - 1]; if (formDataValidation.validationFunction === undefined) { logger.error('validationFunction undefined'); } else { result = await formDataValidation.validationFunction(); } } return result; }; //--------------------------------------------------------------------------------- // Console Output API //--------------------------------------------------------------------------------- // const output = ref(''); const appendOutput = (line: string) => { store.loggerOutput(line); }; //--------------------------------------------------------------------------------- // Debug API //--------------------------------------------------------------------------------- const debugTitle = ref(''); const debugSubtitle = ref(''); const debugModel = ref(false); const debugExpression = ref(''); const debugValue = ref(''); let debugScope: Scope; let debugPromise: Promise; let debugResolve: () => void; const debugEvaluate = async () => { const express = new Expression(debugScope); try { if (debugExpression.value.trim() === '') { debugValue.value = ''; } else { const data = await express.evaluate(debugExpression.value); const str = data?.toString(); if (str !== undefined) debugValue.value = str; } } catch (err) { debugValue.value = String(err); } }; const debugClose = () => { debugModel.value = false; debugResolve(); }; const debugShowDialog = (scope: Scope, tag: Tag): Promise => { debugTitle.value = 'Debug'; debugSubtitle.value = 'File : ' + tag.filename + ', Line : ' + tag.line; debugScope = scope; watch(debugExpression, async () => { await debugEvaluate(); }); debugExpression.value = tag.getText(); debugModel.value = true; debugPromise = new Promise((resolve) => { debugResolve = resolve; }); watch(debugModel, () => { if (debugModel.value === false) debugResolve(); }); return debugPromise; }; // // function callCommandFunction // let callCommandFunction: () => Promise const preview = async () => { try { await store.currentuser() // if (store.script === undefined) return; // const typeInput = structure.scope.getDataType('input'); let source = props.xmlSource; if (source === undefined && props.moduleName !== undefined) { source = await fetchModule(props.moduleName); } if (source === undefined && store.module.source_edit !== undefined) { source = store.module.source_edit; } // addConfirmFunction(scopeRun); const tagSource = parseXML( props.moduleName || '', source || 'empty source module' ); const dbManager = await getDBManager(); // i18n // set localization file for cykLang module props.moduleName // priority: store.user.lang : browserLanguage parameter : prefix of browserLanguage: en-US: en if (props.moduleName) { await loadLanguageModule(structure.scope, dbManager, tagSource.attributes.PACKAGE || props.moduleName, [store.user.user_lang, browserLanguage, 'en-US']) } const windowManager = new WindowManager( props.moduleName || '', setFormComponentValue, validateForm, browserHistoryPush, pushModalDialog, popModalDialog, validateModalDialog, appendOutput, refreshModalDialog, resetModalDialogDirty, sendEventModalDialog, debugShowDialog, showMessageFunction, showLoading, hideLoading ); await windowManager.addDataTypes(); moduleInstruction = new ModuleInstruction(tagSource, dbManager); await moduleInstruction.parseInstructions(tagSource, windowManager.scopeRun); store.moduleInstruction = moduleInstruction; isLoading.value = false; // logger.debug('== after parseInstructions isLoading ' + isLoading.value); await moduleInstruction.execute(windowManager.scopeRun); const initFunction = moduleInstruction.objectData?.variables.getFunction('__init__'); if (initFunction !== undefined) { const params = new Variables(); const varThis = params.addVariable('this', structure.objectDataType); varThis.data = moduleInstruction.objectData; // $q.loading.show(); await initFunction.callFunction(params, windowManager.scopeRun); // $q.loading.hide(); } callCommandFunction = async () => { const commandFunction = moduleInstruction.objectData?.variables.getFunction('__command__'); if (commandFunction !== undefined) { const params = new Variables(); const varThis = params.addVariable('this', structure.objectDataType); varThis.data = moduleInstruction.objectData; const varCommand = params.addVariable( 'command', structure.stringDataType ); varCommand.data = new PrimitiveData( structure.stringDataType, router.currentRoute.value.params.command as string ); const objQuery = new ObjectData(structure.objectDataType, new Tag(''), structure.scope) const varQuery = new Variable(structure.objectDataType, objQuery) for (const key in router.currentRoute.value.query) { if (Object.prototype.hasOwnProperty.call(router.currentRoute.value.query, key)) { const value = String(router.currentRoute.value.query[key]) objQuery.variables.push(key, new Variable(structure.stringDataType, new PrimitiveData(structure.stringDataType, value))) } } params.push('search', varQuery) await commandFunction.callFunction(params, windowManager.scopeRun); } }; await callCommandFunction(); } catch (err) { AlertException(err) } finally { // $q.loading.hide(); } }; (async () => { await preview(); })(); watch( () => ({ params: router.currentRoute.value.params, query: router.currentRoute.value.query }), async () => { moduleInstruction && await callCommandFunction(); } ); store.loggerInit(); return { isLoading, store, formComponent: refFormComponent, keyFormComponent, dialogStack, dialogCounter, dialogHidefn, dialogKey, escape, debugTitle, debugSubtitle, debugModel, debugExpression, debugValue, debugEvaluate, debugClose, key0, key1, key2, key3, key4, key5, }; }