 glb.DEBUG_INFO_OBJ = {};
// var CONNECTION_ATTEMPTS = 0;
glb.APP_INFO = {};
// var vars = {};
// var USERS_CACHE = {};
var SYSTEM_READY = null;
// var CPI_WEBSOCKET;
var GLB_JS_SCRIPTS_LOADED = [];

var STUDIO_WEBSOCKET = null;
var STUDIO_WEBSOCKET_CONNECTION_ID = null;
var STUDIO_PEER = null;
var STUDIO_PEER_CONN_SEND_METHOD = null;
var STUDIO_PEER_CONN_ID = null;
var SUPPORT_PEER = null;
var SUPPORT_PEER_CONN = null;
var STUDIO_PEER_CONN_MSG_QUEUE = [];

var CLIENT_ACTIVITY_TS;
var IS_ONLINE;

glb.REFERENCE_LESS_FUNCTIONS = ['update', 'raise_event', 'call_library', 'invoke_action', 'loader_on', 'loader_off', 'emit_event', 'delay', 'execute_evaluate_javascript', 'execute_native_javascript'];

var CACHE_PROG_UI = {};

var ALERT_IS_ACTIVE = false;
glb.WORKER_ATTEMPTS_NOT_RESPONDING = 200000;

glb.WORKER_TIMEOUT = 600000;
glb.WORKER_PAUSE = false;
// glb.EMAIL_SEND_RECEIVE_STATUS = {};
var DATASOURCE_INTERVALS = {};
var APP_MODAL_OBJ = {};
var CURRENT_APP_POPOVER = null;
var ELEMENT_CLICK_EVENT = null;
var posX = 0; //cursor x
var posY = 0; //cursor x
var LOADER_ACTIVE = false;
var LOADER_TEXT = '';
var REFRESHER_IN_PROGRESS = false;

glb.screen_num = 0;
var RESPONSE_FROM_STUDIO_QUEUE = {};

var SCREEN_BLOCKER_OBJ = {};
var IS_PROGRESS_SCREEN_OPEN = null;
// var UI_ENGINE_OBJ = null;

var UI_WORKER_OBJ = {
  jobs: [],
  num: 9000,
  cache: {},
  viewport_height_set_ids: [],
  xu_render_cache: {},
  //  pending_for_viewport_render: {}
};

glb.html5_events_handler = [
  'onabort',
  'onafterprint',
  'onautocomplete',
  'onautocompleteerror',
  'onbeforeprint',
  'onbeforeunload',
  'onblur',
  'oncancel',
  'oncanplay',
  'oncanplaythrough',
  'onchange',
  'onclick',
  'onclose',
  'oncontextmenu',
  'oncopy',
  'oncuechange',
  'oncut',
  'ondblclick',
  'ondrag',
  'ondragend',
  'ondragenter',
  'ondragexit',
  'ondragleave',
  'ondragover',
  'ondragstart',
  'ondrop',
  'ondurationchange',
  'onemptied',
  'onended',
  'onerror',
  'onfocus',
  'onhashchange',
  'oninput',
  'oninvalid',
  'onkeydown',
  'onkeypress',
  'onkeyup',
  'onload',
  'onloadeddata',
  'onloadedmetadata',
  'onloadstart',
  'onmessage',
  'onmousedown',
  'onmouseenter',
  'onmouseleave',
  'onmousemove',
  'onmouseout',
  'onmouseover',
  'onmouseup',
  'onmousewheel',
  'onoffline',
  'ononline',
  'onpagehide',
  'onpageshow',
  'onpaste',
  'onpause',
  'onplay',
  'onplaying',
  'onpopstate',
  'onprogress',
  'onratechange',
  'onreset',
  'onresize',
  'onscroll',
  'onsearch',
  'onseeked',
  'onseeking',
  'onselect',
  'onshow',
  'onsort',
  'onstalled',
  'onstorage',
  'onsubmit',
  'onsuspend',
  'ontimeupdate',
  'ontoggle',
  'onunload',
  'onvolumechange',
  'onwaiting',
];

glb.lifecycle = {
  plugins: {},
  // queue: [],
  fn_arr: ['beforeInit', 'initialized', 'systemReady', 'beforeMounted', 'mounted'],
  // add(type, fn, params) {
  //   this.queue.push({ type, fn, params });
  // },
  execute: async function (SESSION_ID, event) {
    const _session = SESSION_OBJ[SESSION_ID];

    const xu_api = await func.common.get_module(SESSION_ID, 'xuda-api-library.mjs', {
      func,
      glb,
      SESSION_OBJ,
      SESSION_ID,
      APP_OBJ,
      dsSession: func.utils.get_last_datasource_no(SESSION_ID),
    });

    var params = {
      SESSION_ID,
      session_data: _session,
      app_obj: APP_OBJ[_session.app_id],
      xu_api,
    };

    for await (const [plugin_name, val] of Object.entries(glb.lifecycle.plugins)) {
      if (val?.plugin_script?.[event]) {
        params.setup_data = val.setup_data;
        await val.plugin_script[event](params);
      }
    }
  },
};

glb.run_xu_before = [
  'xu-cdn',
  'xu-style',
  'xu-render',
  'xu-for-key',
  'xu-for-val',
  // "xu-ui-plugin",
  // "programParameters",
];
glb.run_xu_after = ['xu-bind', 'xu-class', 'xu-script', 'xu-ui-plugin', 'xu-ref'];
glb.attr_abbreviations_arr = ['xu-click', 'xu-dblclick', 'xu-contextmenu', 'xu-focus', 'xu-keyup', 'xu-change', 'xu-blur', 'xu-init'];
glb.solid_attributes = ['disabled'];
 func.utils = {};

func.utils.debug = {};
func.utils.debug.watch = async function (SESSION_ID, key, type, info, result, condition, not_executed) {
  if (!glb.DEBUG_MODE) return;

  const debug_utils = await func.common.get_module(SESSION_ID, 'xuda-debug-utils-module.mjs');

  debug_utils.watch(SESSION_ID, key, type, info, result, condition, not_executed);
};
func.utils.debug.log = async function (SESSION_ID, node_idP, jsonP) {
  if (typeof IS_PROCESS_SERVER !== 'undefined') return;

  if (!glb.DEBUG_MODE && !glb.TRACE_ON) return;

  const debug_utils = await func.common.get_module(SESSION_ID, 'xuda-debug-utils-module.mjs');

  debug_utils.log(SESSION_ID, node_idP, jsonP);
  // console.error(jsonP);
};
func.utils.debug.write = async function (SESSION_ID, logP, callbackP) {
  if (!glb.DEBUG_MODE) return;

  const debug_utils = await func.common.get_module(SESSION_ID, 'xuda-debug-utils-module.mjs');

  debug_utils.write(SESSION_ID, logP, callbackP);
};

func.utils.debug.read_command = async function (data) {
  if (!glb.DEBUG_MODE) return;

  const debug_utils = await func.common.get_module(SESSION_ID, 'xuda-debug-utils-module.mjs');

  debug_utils.read_command(data);
};
func.utils.DOCS_OBJ = {};
func.utils.DOCS_OBJ.get = async function (SESSION_ID, idP) {
  if (!idP || idP === '0') return;

  var _session = SESSION_OBJ[SESSION_ID];
  const _app_id = _session.app_id;
  if (!DOCS_OBJ[_app_id]) {
    DOCS_OBJ[_app_id] = {};
  }

  if (DOCS_OBJ[_app_id][idP]) {
    return DOCS_OBJ[_app_id][idP];
  }

  if (_session.project_data) {
    if (idP === 'system') {
      if (_session.project_data.globals) {
        DOCS_OBJ[_app_id][idP] = _session.project_data.globals;
      } else {
        DOCS_OBJ[_app_id][idP] = {};
      }
      return DOCS_OBJ[_app_id][idP];
    }
    let val = _session.project_data?.programs?.[idP];
    if (val) {
      return (DOCS_OBJ[_app_id][idP] = val);
    }
  }

  if (typeof _session.SLIM_BUNDLE === 'undefined' || !_session.SLIM_BUNDLE) {
    const module = await func.common.get_module(SESSION_ID, `xuda-progs-loader-module.mjs`);

    if (idP !== 'system') {
      DOCS_OBJ[_app_id][idP] = await module.DOCS_OBJ_get(SESSION_ID, idP);
      if (DOCS_OBJ[_app_id][idP] && _.isEmpty(DOCS_OBJ[_app_id][idP])) {
        await func.utils.remove_cached_objects(SESSION_ID);

        delete DOCS_OBJ[_app_id][idP];
      }
      return DOCS_OBJ[_app_id][idP];
    }

    DOCS_OBJ[_app_id][idP] = await module.DOCS_OBJ_get(SESSION_ID, 'global_' + (APP_OBJ[_app_id].app_replicate || _app_id));
    if (APP_OBJ[_app_id].app_imported_projects) {
      for await (const imported_app_id of APP_OBJ[_app_id].app_imported_projects) {
        var view_ret = await module.DOCS_OBJ_get(SESSION_ID, 'global_' + imported_app_id);
        DOCS_OBJ[_app_id][idP] = _.merge(DOCS_OBJ[_app_id][idP], view_ret);
      }
    }
    return DOCS_OBJ[_app_id][idP];
  }

  console.error(`${idP} not found`);
};

func.utils.FILES_OBJ = {};
func.utils.FILES_OBJ.get = async function (SESSION_ID, idP) {
  if (!idP) return;
  return await func.utils.DOCS_OBJ.get(SESSION_ID, idP);
};
func.utils.VIEWS_OBJ = {};
func.utils.VIEWS_OBJ.get = async function (SESSION_ID, idP) {
  if (!idP) return;

  return await func.utils.DOCS_OBJ.get(SESSION_ID, idP);
};

func.utils.TREE_OBJ = {};
func.utils.TREE_OBJ.get = async function (SESSION_ID, idP) {
  if (!idP) return;
  var ret = await func.utils.DOCS_OBJ.get(SESSION_ID, idP);
  if (ret?.properties) {
    ret.properties.id = idP;
  }

  return ret.properties;
};

func.utils.get_dateTime = async function (SESSION_ID, typeP, dateP) {
  // const getUTC = function (ts) {
  //   var date = new Date(ts);
  //   var utc_date = new Date(
  //     date.getUTCFullYear(),
  //     date.getUTCMonth(),
  //     date.getUTCDate(),
  //     date.getUTCHours(),
  //     date.getUTCMinutes(),
  //     date.getUTCSeconds(),
  //     date.getUTCMilliseconds()
  //   );
  //   return utc_date;
  // };
  const get_server_ts = async function () {
    var _session = SESSION_OBJ[SESSION_ID];

    const response = await fetch(`https://${_session.domain}/cpi/get_utc_ts`, {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({}),
    });
    const json = await response.json();
    return json.data;
    // }
  };

  function getWeekNumber(d) {
    // Copy date so don't modify original
    d = new Date(+d);
    d.setHours(0, 0, 0);
    // Set to nearest Thursday: current date + 4 - current day number
    // Make Sunday's day number 7
    d.setDate(d.getDate() + 4 - (d.getDay() || 7));
    // Get first day of year
    var yearStart = new Date(d.getFullYear(), 0, 1);
    // Calculate full weeks to nearest Thursday
    var weekNo = Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
    // Return array of year and week number
    return weekNo;
  }
  var sysDate = new Date(dateP); // getUTC(dateP); //new Date(dateP);

  if (!dateP) {
    let ts = await get_server_ts();
    sysDate = new Date(ts);
  }
  var day = _.padStart(sysDate.getDate(), 2, '0');
  var month = _.padStart(sysDate.getMonth() + 1, 2, '0');
  var year = sysDate.getFullYear();
  var week = _.padStart(getWeekNumber(sysDate), 2, '0');
  var hour = _.padStart(sysDate.getHours(), 2, '0');
  var minute = _.padStart(sysDate.getMinutes(), 2, '0');
  var second = _.padStart(sysDate.getSeconds(), 2, '0');
  if (typeP === 'SYS_DATE') return year + '-' + month + '-' + day;
  if (typeP === 'SYS_DATE_TIME') return year + '-' + month + '-' + day + 'T' + hour + ':' + minute;
  if (typeP === 'SYS_DATE_VALUE') return sysDate.valueOf();
  if (typeP === 'SYS_DATE_WEEK_YEAR') return year + 'W' + week;
  if (typeP === 'SYS_DATE_MONTH_YEAR') return year + '-' + month;
  if (typeP === 'SYS_TIME') return hour + ':' + minute + ':' + second;
  if (typeP === 'SYS_TIME_SHORT') return hour + ':' + minute;
};
func.utils.is_onscreen_event = function (functionP) {
  const arr = ['invoke_action', 'cache_refresh', 'call_popover', 'call_modal', 'call_page', 'loader_on', 'loader_off', 'emit_event'];

  return arr.includes(functionP);
};

func.utils.get_screen_obj = async function (SESSION_ID, id) {
  const prog_obj = await func.utils.VIEWS_OBJ.get(SESSION_ID, id);
  if (!prog_obj) return console.error('prog not found: ' + id);

  // if (prog_obj.properties.menuType === 'screen' || prog_obj.properties.menuType === 'api' || glb.FUNCTION_NODES_ARR.includes(prog_obj.properties.menuType) || glb.MOBILE_ARR.includes(prog_obj.properties.menuType)) {
  if (['component', ...glb.FUNCTION_NODES_ARR].includes(prog_obj.properties.menuType)) {
    return prog_obj;
  }
  return;
};

func.utils.clean_returned_datasource = function (SESSION_ID, DS) {
  const clean_object_functions = function (obj) {
    for (const [key, val] of Object.entries(obj)) {
      if (typeof val === 'function') {
        delete obj[key];
      }
    }
  };

  var _session = SESSION_OBJ[SESSION_ID];
  if (!_session.DS_GLB[DS]) return;

  var obj = _.clone(_session.DS_GLB[DS]);

  delete obj.screen_params;

  // try {
  //   clean_object_functions(obj);

  //   obj = JSON.parse(JSON.stringify(obj, func.utils.clean_stringify_null, '\t'));
  // } catch (e) {
  //   console.error(e);
  // }

  delete obj.pre_init_fields;
  delete obj.oninit_triggers_to_run;
  // delete obj.raw_data;

  delete obj.debug;

  const clean_empty_objects = function () {
    for (const [key, val] of Object.entries(obj)) {
      if (typeof val === 'object' && !Array.isArray(val) && _.isEmpty(val)) {
        delete obj[key];
      }
    }

    for (const [key, val] of Object.entries(obj)) {
      if (typeof val === 'object' && Array.isArray(val) && !val.length) {
        delete obj[key];
      }
    }
  };

  delete obj.screenInfo;

  delete obj.viewEventsProp;
  delete obj.viewSourceDesc;
  delete obj.viewSourceProp;

  delete obj.v;
  clean_empty_objects();

  try {
    clean_object_functions(obj);

    obj = JSON.parse(JSON.stringify(obj, func.utils.clean_stringify_null, '\t'));
  } catch (e) {
    console.error(e);
  }
  // clean_dataset();
  return obj;
};
func.utils.post_back_to_client = function (SESSION_ID, service, id, data) {
  if (typeof IS_PROCESS_SERVER !== 'undefined') return;
  worker_post_message({
    fx_to_execute: service,
    params: data,
    session_id: SESSION_ID,
    worker_id: id,
  });
};
func.utils.job_worker = {};

func.utils.job_worker = function (session_id) {
  var SESSION_ID = session_id;
  var _session = SESSION_OBJ[SESSION_ID];
  var is_progressScreen_on;
  var is_not_responding;
  var attempt = 0;

  const lock = function (dsP) {
    if (!_session.WORKER_OBJ.jobs[_session.WORKER_OBJ.stat] || _session.WORKER_OBJ.jobs[_session.WORKER_OBJ.stat].typeP === 'system_interval' || _session.WORKER_OBJ.jobs[_session.WORKER_OBJ.stat].typeP === 'system event') {
      return;
    }
    if (glb.IS_WORKER) {
      func.utils.post_back_to_client(SESSION_ID, 'screen_blocker_on', _session.worker_id, null);
    } else {
      func.UI.utils.screen_blocker(true, 'Worker', dsP);
    }
  };
  const unlock = function () {
    if (glb.IS_WORKER) {
    } else {
      func.UI.utils.screen_blocker(false, 'Worker');
    }
  };
  const not_responding = function () {
    is_not_responding = true;
    func.UI.utils.progressScreen.hide('Working, Please wait..');
    setTimeout(function () {
      if (!is_not_responding) return;

      reset();
    }, 500);
  };
  const idle = function () {
    if (is_progressScreen_on) {
      setTimeout(function () {
        if (!attempt && is_progressScreen_on) {
          is_progressScreen_on = false;
          is_not_responding = false;

          func.UI.utils.progressScreen.hide('Working, Please wait..');
        } else if (attempt > 300 && is_not_responding) {
          is_not_responding = false;
          busy();
        }
      }, 310);
    } else {
      if (!glb.IS_WORKER) {
        // func.UI.screen.garbage_collector(SESSION_ID);
      }
    }
  };
  const busy = function () {
    if (glb.IS_WORKER) return;
    func.utils.debug_report(SESSION_ID, 'utils.worker.busy', 'worker processing more then 10 second', 'W', '', _session.WORKER_OBJ.jobs);

    is_progressScreen_on = true;
  };
  const reset = function () {
    func.utils.debug_report(SESSION_ID, 'utils.worker.reset', 'worker not responding', 'E', '', _session.WORKER_OBJ.jobs);
    _session.WORKER_OBJ.jobs = [];
    _session.WORKER_OBJ.stat = null;
    $('.screen_blocker').remove();
  };
  return {
    _interval: null,
    _was_busy: null,
    init: async function () {
      var _this = this;
      this._interval = setInterval(async function () {
        var _session = SESSION_OBJ[SESSION_ID];
        if (!_session?.WORKER_OBJ) return;
        if (typeof _session.WORKER_OBJ.stat === 'undefined' || _session.WORKER_OBJ.stat === 'undefined' || _session.WORKER_OBJ.stat === null) {
          // idle

          // if (_this._was_busy) {
          //   if (glb.IS_WORKER) {
          //     func.utils.post_back_to_client(
          //       SESSION_ID,
          //       "worker_busy_off",
          //       _session.worker_id,
          //       null
          //     );
          //   } else {
          //     func.UI.utils.indicator.worker.normal();
          //   }
          //   _this._was_busy = false;
          // }
          unlock();
          if (_session.WORKER_OBJ.jobs.length) {
            for await (const [key, val] of Object.entries(_session.WORKER_OBJ.jobs)) {
              if (val.stat) {
                break;
              }
              // if (!_session.WORKER_OBJ.stat) {
              if (!_session.WORKER_OBJ.jobs[Number(key)] || val.job_num === 9999999) {
                continue;
              }
              if (val.dsSessionP && !_session.DS_GLB[val.dsSessionP]) {
                func.events.delete_job(SESSION_ID, val.job_num);
                break;
              }
              await func.events.execute(
                SESSION_ID,
                val.job_num,
                val.eventIdP,
                val.triggerP,
                val.functionP,
                val.refIdP,
                val.containerP,
                val.elementP,
                val.rowP,
                val.evt,
                val.descP,
                val.rootScreenIdP,
                val.dsSessionP,
                null,
                val.typeP,
                null,
                val.event_propertiesP,
                val.calling_triggerP,
                null,
                val.paramsP,
                val.target_frame_idP,
                val.calling_trigger_prop,
                val.calling_program,
                val.argumentsP,
                val.prog_id,
                val.nodeId,
                val.parentDataSourceNo,
                val.$container,
              );
            }
            _this._was_busy = true;
          } else {
            // idle
            if (_this._was_busy) {
              if (glb.IS_WORKER) {
                func.utils.post_back_to_client(SESSION_ID, 'worker_busy_off', _session.worker_id, null);
              } else {
                func.UI.utils.indicator.worker.normal();
              }
            }
            _this._was_busy = false;
          }
          attempt = 0;
          is_not_responding = false;
          idle();
        } else {
          //busy
          _this._was_busy = true;
          if (glb.IS_WORKER) {
            func.utils.post_back_to_client(SESSION_ID, 'worker_busy_on', _session.worker_id, null);
          } else {
            func.UI.utils.indicator.worker.busy();
          }
          if (glb.WORKER_PAUSE) return;
          attempt++;
          if (!is_progressScreen_on && attempt > glb.WORKER_TIMEOUT) busy();
          if (!is_not_responding && attempt >= glb.WORKER_ATTEMPTS_NOT_RESPONDING) {
            not_responding();
          }
          var ds = null;
          if (_session.WORKER_OBJ.jobs[0]) ds = _session.WORKER_OBJ.jobs[0].dsSessionP;
          lock(ds);
        }
      }, 1);
    },
    stop: function () {
      clearInterval(this._interval);
    },
  };
};

func.utils.base64MimeType = function (encoded) {
  var result = null;

  if (typeof encoded !== 'string') {
    return result;
  }

  var mime = encoded.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/);

  if (mime && mime.length) {
    result = mime[1];
  }

  return result;
};

func.utils.makeid = function (length) {
  var result = '';
  var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  var charactersLength = characters.length;
  for (var i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }
  return result;
};

func.utils.get_device = function () {
  var device;
  try {
    if (window.cordova) {
      device = window.cordova.platformId;
    }
  } catch (e) {
    console.error('error using ui element in server side request');
  }
  return device;
};

func.utils.ws_worker = {};

func.utils.ws_worker.functions = {
  init: async function (data) {
    var SESSION_ID = data.SESSION_ID;

    APP_OBJ[data.app_id] = data.APP_OBJ;
    PROJECT_OBJ[data.app_id] = data.PROJECT_OBJ;

    if (['live_preview', 'miniapp'].includes(data.SESSION_INFO.engine_mode)) {
      DOCS_OBJ[data.app_id] = data.DOCS_OBJ;
    } else if (typeof IS_DOCKER === 'undefined' && typeof IS_PROCESS_SERVER === 'undefined') {
      if (!DOCS_OBJ[data.app_id]) {
        DOCS_OBJ[data.app_id] = {};
      }
    }

    glb.APP_INFO[data.app_id] = data.APP_INFO;
    glb.DEBUG_MODE = data.DEBUG_MODE;
    glb.DEBUG_INFO_OBJ = data.DEBUG_INFO_OBJ;

    glb.WINDOW_LOCATION_SEARCH = data.WINDOW_LOCATION_SEARCH;
    glb.ROOT_ELEMENT_ATTRIBUTES = data.ROOT_ELEMENT_ATTRIBUTES;

    DATASOURCE_INTERVALS[SESSION_ID] = {};

    SESSION_OBJ[SESSION_ID] = data.SESSION_INFO;

    var _session = SESSION_OBJ[SESSION_ID];
    glb.SESSION_INFO = data.SESSION_INFO;

    _session.engine_mode = data.engine_mode;
    STUDIO_WEBSOCKET_CONNECTION_ID = data.STUDIO_WEBSOCKET_CONNECTION_ID;

    for (let [key, val] of Object.entries(_session.DS_GLB)) {
      if (Number(key) > _session.dataSourceSessionGlobal) {
        _session.dataSourceSessionGlobal = Number(key);
      }
    }
    if (typeof _session.SLIM_BUNDLE === 'undefined' || !_session.SLIM_BUNDLE) {
      const db_adapter = await func.common.get_module(SESSION_ID, 'xuda-db-adapter-module.mjs');

      func.db = db_adapter._db;
    }

    _session.WORKER_OBJ.fx = new func.utils.job_worker(SESSION_ID);
    _session.WORKER_OBJ.fx.init();

    if (_session.app_id === 'unknown') {
      worker_post_message({
        fx_to_execute: 'init_done',
        worker_id: ws_worker_id,
        session_id: SESSION_ID,
      });
    } else {
      const module = await func.common.get_module(SESSION_ID, `xuda-progs-loader-module.mjs`);
      await module.load_objects_cache(SESSION_ID);
      worker_post_message({
        fx_to_execute: 'init_done',
        worker_id: ws_worker_id,
        session_id: SESSION_ID,
      });
    }

    WEB_WORKER_CALLBACK_QUEUE[SESSION_ID] = {};
  },
  datasource_create: async function (params, promise_queue_id) {
    var SESSION_ID = params.session_id;
    var _session = SESSION_OBJ[SESSION_ID];
    _session.ts = new Date().getTime();
    var args = params;
    args.SESSION_ID = SESSION_ID;

    if (show_log) {
      console.log('DATASOURCE EXECUTING SESSION_ID: ' + SESSION_ID, APP_OBJ[_session.app_id].app_name);
    }
    if (Number(params.dataSourceSessionGlobal) > _session.dataSourceSessionGlobal) {
      _session.dataSourceSessionGlobal = Number(params.dataSourceSessionGlobal);
    }

    const ret = await func.datasource.prepare(
      args.SESSION_ID,
      args.prog_id,
      args.dataSourceNoP,
      args.parentDataSourceNoP,
      args.containerIdP,
      args.rowIdP,
      args.jobNoP,
      args.calling_trigger_prop,
      args.parameters_raw_obj,
      null,
      args.callingSourceP,
      args.calling_jobP,
      args.screen_dsP,
      args.is_panelP,
      args.parameters_obj_inP,
      args.static_refreshP,
      args.run_atP,
      args.worker_id,
    );
    try {
      let _ds = _session.DS_GLB[ret.dsSessionP];
      if (show_log) console.log('DATASOURCE EXECUTION DONE ' + ret.dsSessionP + ' ' + _ds?.tree_obj?.menuName || '' + ' SESSION_ID: ' + SESSION_ID, APP_OBJ[_session.app_id].app_name);
      var obj = func.utils.clean_returned_datasource(SESSION_ID, ret?.dsSessionP);
      obj.dataSourceSessionGlobal = _session.dataSourceSessionGlobal;

      worker_post_message({
        promise_queue_id,
        params: obj,
        worker_id: ws_worker_id,
        session_id: SESSION_ID,
        process_pid: params.process_pid,
        service: params.service,
      });
      _ds.stat = 'idle';
    } catch (error) {
      debugger;
    }
  },
  datasource_delete: function (params, promise_queue_id) {
    var SESSION_ID = params.session_id;
    var _session = SESSION_OBJ[SESSION_ID];
    if (DATASOURCE_INTERVALS[SESSION_ID] && DATASOURCE_INTERVALS[SESSION_ID][params.dssession]) {
      DATASOURCE_INTERVALS[SESSION_ID][params.dssession].clear();
    }
    delete _session.DS_GLB[params.dssession];
    if (show_log) console.log('DATASOURCE DELETE SESSION_ID: ' + SESSION_ID, APP_OBJ[_session.app_id].app_name, params.dssession);
    // debugger;
    worker_post_message({
      promise_queue_id,
      worker_id: ws_worker_id,
      session_id: SESSION_ID,
      process_pid: params.process_pid,
      service: params.service,
    });
  },
  update_datasource_changes_from_client: async function (params, promise_queue_id) {
    if (_.isEmpty(SESSION_OBJ)) return;
    var SESSION_ID = params.session_id;
    var _session = SESSION_OBJ[SESSION_ID];
    if (!_session) {
      _session = {};
      _session.app_id = params.app_id;
      _session.dataSourceSessionGlobal = -1;
      _session.DS_GLB = {};
    }
    if (show_log) console.log('DATASOURCE UPDATE SESSION_ID: ' + SESSION_ID, APP_OBJ[_session.app_id].app_name, params.dssession);

    await func.datasource.update(SESSION_ID, params.datasource_changes, true);

    worker_post_message({
      promise_queue_id,
      params: params.dssession,
      worker_id: ws_worker_id,
      session_id: SESSION_ID,
      process_pid: params.process_pid,
      service: params.service,
    });
  },

  return_to_data_source: function (params, promise_queue_id) {
    var SESSION_ID = params.session_id;
    var _session = SESSION_OBJ[SESSION_ID];
    var ds = _session.DS_GLB[params.dssession];
    var type = params.return_to_data_source_type;
    var args = ds.args;
    if (show_log) console.log('DATASOURCE RETURN TO DATASOURCE ' + params.dssession + ' SESSION_ID: ' + SESSION_ID, APP_OBJ[_session.app_id].app_name);
    _session.DS_GLB[params.dssession].v.onscreen_events_active = params.onscreen_events_active;
    if (params.viewEventExec_arr) _session.DS_GLB[params.dssession].viewEventExec_arr = JSON.parse(params.viewEventExec_arr);
    var done = function (SESSION_ID, DS) {
      if (show_log) console.log('DATASOURCE RETURN TO DATASOURCE DONE ' + DS + ' SESSION_ID: ' + SESSION_ID, APP_OBJ[_session.app_id].app_name);
      var obj = func.utils.clean_returned_datasource(SESSION_ID, DS);
      obj.dataSourceSessionGlobal = _session.dataSourceSessionGlobal;
      worker_post_message({
        fx_to_execute: 'post_datasource',
        params: {
          ds_obj: obj,
          dsSessionP: params.dssession,
        },
        worker_id: ws_worker_id,
        session_id: SESSION_ID,
        process_pid: params.process_pid,
        service: params.service,
      });
    };

    done(SESSION_ID, params.dssession);
  },
  acknowledged_worker_with_eventChangesResults_done: function (params) {
    var SESSION_ID = params.session_id;
    var _session = SESSION_OBJ[SESSION_ID];
    var ds = _session.DS_GLB[params.dssession];

    if (show_log) console.log('UPDATE CHANGE EVENT DONE TO DATASOURCE ' + params.dssession + ' SESSION_ID: ' + SESSION_ID, APP_OBJ[_session.app_id].app_name);
    ds.eventChangesResults_done = true;
  },
  return_from_db_query: function (params) {
    var SESSION_ID = params.session_id;
    var _session = SESSION_OBJ[SESSION_ID];
    var id = params.id;
    if (show_log) console.log('RETURN FROM DB_QUERY ' + params.dssession + ' SESSION_ID: ' + SESSION_ID, APP_OBJ[_session.app_id].app_name);
    debugger;
    var callback = func.utils.get_callback_queue(SESSION_ID, params.callback_id);
    if (callback) callback(params.data);
  },
  return_from_sava_data: function (params) {
    var SESSION_ID = params.session_id;
    var _session = SESSION_OBJ[SESSION_ID];
    var id = params.id;
    if (show_log) console.log('RETURN FROM SAVE_DATA ' + params.dssession + ' SESSION_ID: ' + SESSION_ID, APP_OBJ[_session.app_id].app_name);
    func.utils.get_callback_queue(SESSION_ID, params.callback_id)();
  },
  update_debug_info: function (params) {
    glb.DEBUG_INFO_OBJ = params;
  },
  // update_VIEWS_OBJ: function (params) {
  //   delete VIEWS_OBJ[APP_ID][params.id];
  // },
  // update_TREE_OBJ: function (params) {
  //   delete TREE_OBJ[APP_ID][params.id];
  // },
  // send_object_to_worker: function (params) {
  //   if (RESPONSE_FROM_STUDIO_QUEUE[params.req_id]) {
  //     RESPONSE_FROM_STUDIO_QUEUE[params.req_id].data = params;
  //   }
  // },
  get_dataSourceSessionGlobal: function (params) {
    var SESSION_ID = params.session_id;
    SESSION_OBJ[SESSION_ID].dataSourceSessionGlobal++;
    let new_dataSourceSessionGlobal = SESSION_OBJ[SESSION_ID].dataSourceSessionGlobal;
    return { new_dataSourceSessionGlobal };
  },
  create_webworker_globals: function (params) {
    var SESSION_ID = params.session_id;
    SESSION_OBJ[SESSION_ID].DS_GLB[0] = params.ds_data;
  },
  return_doc_from_studio: function (params) {
    var SESSION_ID = params.session_id;
    function emitCustomEvent(eventName, detail) {
      const event = new CustomEvent(eventName, { detail });
      self.dispatchEvent(event);
    }
    emitCustomEvent('live_preview_get_obj_response_worker_' + params._id, {
      data: params,
    });
  },
  return_doc_from_websocket: function (params) {
    var SESSION_ID = params.session_id;
    function emitCustomEvent(eventName, detail) {
      const event = new CustomEvent(eventName, { detail });
      self.dispatchEvent(event);
    }
    emitCustomEvent('get_doc_obj_from_build_worker_' + params._id, {
      data: params,
    });
  },
  return_dbs_data_from_websocket: function (params) {
    var SESSION_ID = params.session_id;
    function emitCustomEvent(eventName, detail) {
      const event = new CustomEvent(eventName, { detail });
      self.dispatchEvent(event);
    }
    emitCustomEvent('get_ws_data_worker_' + params.websocket_queue_num, {
      data: params.data,
    });
  },
  heartbeat: async function (params) {
    var SESSION_ID = params.session_id;

    try {
      const do_heartbeat = async function (app_replicate, app_id, token_id, fingerprint, device_name, stat) {
        try {
          module.exports.close_expired_device_log_sessions(app_id);

          return await update_device(app_replicate, app_id, token_id, fingerprint, device_name, stat);
        } catch (err) {
          return { code: -400, data: err.message };
        }
      };
      let ret = await do_heartbeat(params.app_replicate, params.app_id, params.gtp_token || req.body.app_token, params.fingerprint, params.device_name, params.stat);

      // session_status: ret_session.data.stat
      if (params.token) {
        try {
          const couch = await __.rpi.get_app_couch(req.body.app_id);
          const session_doc = await couch.get(req.body.app_token);
          ret.session_stat = session_doc.stat;
        } catch (error) {}
      }
    } catch (error) {
      debugger;
    }
  },
  return_rpi_request_from_studio: function (params) {
    var SESSION_ID = params.session_id;
    function emitCustomEvent(eventName, detail) {
      const event = new CustomEvent(eventName, { detail });
      self.dispatchEvent(event);
    }
    emitCustomEvent('rpi_request_response_worker_' + params.table_id, {
      data: params.data,
    });
  },
};

func.utils.set_callback_queue = function (SESSION_ID, func) {
  var t = new Date().valueOf().toString() + Math.random().toString();
  try {
    WEB_WORKER_CALLBACK_QUEUE[SESSION_ID][t] = func;
  } catch (e) {
    console.error(id);
    func.utils.remove_cached_objects(SESSION_ID);
  }
  return t;
};
func.utils.get_callback_queue = function (SESSION_ID, t) {
  var func = WEB_WORKER_CALLBACK_QUEUE[SESSION_ID][t];
  setTimeout(function () {
    if (WEB_WORKER_CALLBACK_QUEUE[SESSION_ID][t]) delete WEB_WORKER_CALLBACK_QUEUE[SESSION_ID][t];
  }, 1000);
  return func;
};

func.utils.clean_stringify_null = function (key, value) {
  // Filtering out properties
  if (value === null) {
    return undefined;
  }
  return value;
};

func.utils.load_js_on_demand = async function (js_src, type) {
  const get_script = function (callback) {
    function getScript(scriptUrl, callback) {
      const script = document.createElement('script');
      script.src = scriptUrl;
      if (type) script.type = type;
      script.onload = callback;

      document.head.appendChild(script);
    }
    if (glb.IS_WORKER) {
      callback(script);
      return;
    }
    function isScriptLoaded(src) {
      return GLB_JS_SCRIPTS_LOADED.includes(src);
    }

    if (isScriptLoaded(js_src)) {
      callback(false);
    } else {
      getScript(js_src, function (response, status) {
        // console.log(response,status)
        callback(true);
        GLB_JS_SCRIPTS_LOADED.push(js_src);
      });
    }
  };

  return new Promise((resolve) => {
    get_script(resolve);
  });
};

func.utils.load_css_on_demand = function (css_href) {
  function isCssLoaded(src) {
    try {
      return document.querySelector('link[href="' + src + '"]') ? true : false;
    } catch (err) {
      console.warn(`load css ${css_href} failed`);
      return true;
    }
  }

  if (isCssLoaded(css_href)) {
    return;
  }
  return $('<link/>', {
    rel: 'stylesheet',
    type: 'text/css',
    href: css_href,
  }).prependTo('head');
};

func.utils.remove_js_css_file = function (filename, filetype) {
  var targetelement = filetype == 'js' ? 'script' : filetype == 'css' ? 'link' : 'none'; //determine element type to create nodelist from
  var targetattr = filetype == 'js' ? 'src' : filetype == 'css' ? 'href' : 'none'; //determine corresponding attribute to test for
  var allsuspects = document.getElementsByTagName(targetelement);
  for (var i = allsuspects.length; i >= 0; i--) {
    //search backwards within node list for matching elements to remove
    if (allsuspects[i] && allsuspects[i].getAttribute(targetattr) != null && allsuspects[i].getAttribute(targetattr).indexOf(filename) != -1) allsuspects[i].parentNode.removeChild(allsuspects[i]); //remove element by calling parentNode.removeChild()
  }
};

func.utils.replace_studio_drive_url = function (SESSION_ID, val) {
  var _session = SESSION_OBJ[SESSION_ID];
  if (!_session.is_deployment) return val;
  try {
    return val.replaceAll(`https://xuda.ai/studio-drive/${APP_OBJ[_session.app_id].app_replicate}`, `https://${_session.domain}/studio-drive/${APP_OBJ[_session.app_id].app_replicate}`);
  } catch (err) {
    return val;
  }
};

func.utils.get_drive_url = function (SESSION_ID, val, wrap) {
  // wrap = false; // tbd bypass

  var _session = SESSION_OBJ[SESSION_ID];
  function replaceFiletoURL(fileString) {
    const _app = APP_OBJ[_session.app_id];

    let url = `https://${_session.domain}/workspace-drive/${_app.is_deployment ? _app.app_datacenter_id : _app.app_id_reference}/`;

    let FILE_REPLACE_URL = `${url}${val}`;

    if (!_app.is_deployment) {
      // the deployment server will handle the app_token
      FILE_REPLACE_URL += `?app_token=${_session.app_token}&ts=${Date.now()}`;
    } else {
      FILE_REPLACE_URL += `?ts=${_session?.opt?.app_build_id || 0}`;
    }

    let match = `drv_${_app.app_replicate || _session.app_id}_[0-9a-f\\-]+\\.[a-zA-Z0-9]+`;

    let pat = new RegExp(match, 'g');
    let URLString = fileString.replace(pat, function (match, idx) {
      // debugger;
      const hasURLbefore = fileString.substring(idx - url.length, idx) === url;

      if (hasURLbefore) {
        return match;
      }

      return FILE_REPLACE_URL.replace('{val}', match);
    });

    return URLString;
  }

  if (typeof val === 'string' || typeof val === 'object') {
    if (typeof val === 'string') {
      if (val.includes('.') && val.includes('drv_') && val.length > 30) {
        var ret = replaceFiletoURL(val);
        if (wrap) {
          return { value: '"' + ret + '"', changed: true };
        } else {
          return { value: ret, changed: true };
        }
      } else {
        return { value: val, changed: false };
      }
    }

    if (typeof val === 'object') {
      try {
        let str = JSON.stringify(val);
        if (str.includes('.') && str.includes('drv_') && str.length > 30) {
          let new_val = replaceFiletoURL(str);

          return { value: new_val, changed: true };
        } else {
          return { value: val, changed: false };
        }
      } catch (err) {
        return { value: val, changed: false };
      }
    }
  } else {
    return { value: val, changed: false };
  }
};

func.utils.debug_report = async function (SESSION_ID, sourceP, msgP, typeP, errP, objP) {
  var _session = SESSION_OBJ[SESSION_ID];
  var details = [sourceP];
  if (errP) details.push(errP);
  if (objP) details.push(objP);
  if (!typeP || typeP === 'E') {
    setTimeout(() => {
      // if (
      //   typeof IS_DOCKER === "undefined" &&
      //   typeof IS_API_SERVER === "undefined" &&
      //   typeof IS_PROCESS_SERVER === "undefined" &&
      //   !glb?.IS_WORKER
      // ) {
      //   func.index.delete_pouch(SESSION_ID);
      // }
    }, 1000);

    // // temp inactive cause to loop

    // func.utils.alerts.invoke(
    //   SESSION_ID,
    //   "system_msg",
    //   "SYS_MSG_1240",
    //   "debug",
    //   null,
    //   msgP,
    //   sourceP //arguments.callee.caller.toString()- deprecated es6
    // );
    console.error('XUDA', 'ERROR', sourceP, msgP, details);

    if (typeof IS_API_SERVER !== 'undefined' || typeof IS_DOCKER !== 'undefined' || typeof IS_PROCESS_SERVER !== 'undefined') {
      return __.rpi.write_log(SESSION_OBJ[SESSION_ID].app_id, 'error', 'worker', 'runtime', msgP, null, objP, sourceP);
    }

    if (glb.IS_WORKER) {
      let obj = {
        service: 'write_log',
        data: msgP,
        log_type: 'error',
        id: STUDIO_WEBSOCKET_CONNECTION_ID,
        uid: _session.USR_OBJ._id,
        source: 'runtime',
        app_id: _session.app_id,
        gtp_token: _session.gtp_token,
        app_token: _session.app_token,
      };

      return func.utils.post_back_to_client(SESSION_ID, 'write_log', _session.worker_id, obj);
    }

    // // temp inactive cause to loop
    // if (!glb.IS_WORKER) {
    //   await func.common.db(SESSION_ID, "write_log", {
    //     client_id: _session?.SYS_GLOBAL_OBJ_CLIENT_INFO?.fingerprint,
    //     source: sourceP,
    //     log_type: "error",
    //     msg: msgP,
    //   });
    // }
  }
  if (typeP === 'W') console.warn('XUDA', 'WARNING', msgP, details);
  if (typeP === 'I') console.log('XUDA', 'INFO', msgP, details);
};

func.utils.request_error = function (SESSION_ID, type, e) {
  var _session = SESSION_OBJ[SESSION_ID];
  console.error(type, e);
  if (typeof IS_PROCESS_SERVER !== 'undefined') return;
  if (!glb.IS_WORKER) {
    func.utils.debug_report(SESSION_ID, type, e, 'E');
    setTimeout(function () {
      if (!glb.debug_js) {
        // location.reload();
        console.warn('** reload request');
      }
    }, 2000);
  } else {
    func.utils.post_back_to_client(SESSION_ID, 'ajax_error', _session.worker_id, null);
  }
};

func.utils.alerts = {};
func.utils.alerts.invoke = async function (SESSION_ID, typeP, paramsP, sourceP, dsSessionP, msgP) {
  try {
    var _session = SESSION_OBJ[SESSION_ID];
    if (ALERT_IS_ACTIVE) return;
    ALERT_IS_ACTIVE = true;
    var title;
    var message = '';
    var alert_type = 'console';
    var alertDisplay;
    var expRet = {};
    var _ds = _session.DS_GLB[dsSessionP];
    var type = '';
    var createLog;

    const get_alert_properties = async function (value, fx) {
      var ret = value || '';

      if (fx) {
        const exp_ret = await func.expression.get(SESSION_ID, fx, dsSessionP, 'alert');
        ret = exp_ret.result;
      }

      return ret;
    };

    switch (typeP) {
      case 'alert':
        type = 'User defined alert';

        title = await get_alert_properties(paramsP.alertTitle, paramsP.alertTitleFx);
        alert_type = await get_alert_properties(paramsP.alertType, paramsP.alertTypeFx);
        message = await get_alert_properties(paramsP.alertBody, paramsP.alertBodyFx);
        alertDisplay = await get_alert_properties(paramsP.alertDisplay, paramsP.alertDisplayFx);
        createLog = paramsP.createLog;
        break;

      case 'call_alert':
        type = 'User defined call alert';
        let prop = await func.utils.TREE_OBJ.get(SESSION_ID, paramsP.prog);
        if (!prop) {
          console.log('events.execute', 'Missing details for alert message object: ' + paramsP.prog, 'W');
        }
        // user defined

        let ret = await func.utils.VIEWS_OBJ.get(SESSION_ID, paramsP.prog);
        if (ret?.alertData) {
          title = await get_alert_properties(ret.alertData.alertTitle, ret.alertData.alertTitleFx);
          alert_type = await get_alert_properties(ret.alertData.alertType, ret.alertData.alertTypeFx);
          message = await get_alert_properties(ret.alertData.alertBody, ret.alertData.alertBodyFx);
          alertDisplay = await get_alert_properties(ret.alertData.alertDisplay, ret.alertData.alertDisplayFx);
          createLog = ret.alertData.createLog;
        }

        if (!title) {
          title = prop.menuTitle;
        }
        if (!alert_type) {
          alert_type = 'console';
        }

        if (!alertDisplay) {
          alertDisplay = 'modal';
        } //window alert by definition

        break;

      case 'system_msg': {
        type = 'System alert';
        const sys_alerts_obj = func.utils.get_system_error_msg();
        if (sys_alerts_obj[paramsP]) {
          title = sys_alerts_obj[paramsP].subject;
          alert_type = sys_alerts_obj[paramsP].alert_type;
          alertDisplay = sys_alerts_obj[paramsP].alertDisplay;

          expRet = await func.expression.get(SESSION_ID, sys_alerts_obj[paramsP].msg, dsSessionP, 'alert');
          message = func.expression.remove_quotes(expRet.result);

          if (msgP) message = msgP;

          if (alert_type === 'error') {
            if (_ds) _ds.error = title + ' ' + sourceP;
            func.utils.debug_report(SESSION_ID, sourceP, title + ' ' + sourceP, 'E', '', _ds);
          }
        }
        break;
      }
      default:
        message = msgP;
        break;
    }
  } catch (err) {
    console.error(err);
    ALERT_IS_ACTIVE = false;
    return;
  }

  if (glb.IS_WORKER) {
    if (_session.IS_API) {
      if (_ds) {
        _ds.api_rendered_output = message;
      } else {
        console.error(message);
      }
      return;
    }

    // if (typeof IS_PROCESS_SERVER !== "undefined") return;
    ALERT_IS_ACTIVE = false;
    return func.utils.post_back_to_client(SESSION_ID, 'alert', _session.worker_id, [SESSION_ID, alert_type, alertDisplay, message, title]);
  }
  // const sys_alerts_obj = func.utils.get_system_error_msg();
  // if (sys_alerts_obj[paramsP]) {
  //   var str = paramsP + " " + title + " " + message;
  //   if (sys_alerts_obj[paramsP].alert_type === "error") {
  //     console.error(str);
  //   } else if (sys_alerts_obj[paramsP].alert_type === "warning") {
  //     console.warn(str);
  //   } else {
  //     console.log(str);
  //   }
  // } else {
  //   console.log(str);
  // }

  ALERT_IS_ACTIVE = false;

  func.utils.alerts.execute(SESSION_ID, alert_type, alertDisplay, message, title, type);
  if (createLog) {
    func.utils.write_log(SESSION_ID, title, message, alert_type);
  }
};
func.utils.alerts.execute = function (SESSION_ID, alert_type, alertDisplay, message, title, type) {
  if (!UI_FRAMEWORK_INSTALLED) {
    ALERT_IS_ACTIVE = false;
    if (alertDisplay !== 'console') {
      return alert(title + '\n \n' + message);
    }
    return console[alert_type === 'error' ? 'error' : 'log'](alert_type, title, message);
  }

  switch (alertDisplay) {
    case 'console':
      console[alert_type === 'success' ? 'log' : alert_type === 'warning' ? 'warn' : alert_type](alert_type, title, message);
      ALERT_IS_ACTIVE = false;
      break;

    case 'modal':
      func.utils.alerts.popup(title, message, alert_type);
      break;

    case 'toast':
      func.utils.alerts.toast(SESSION_ID, title, message, alert_type);
      ALERT_IS_ACTIVE = false;
      break;

    case 'browser':
      alert(title + '\n \n' + message);
      ALERT_IS_ACTIVE = false;
    default:
      console.log(alert_type, title, message);
      ALERT_IS_ACTIVE = false;
  }
};

func.utils.alerts.toast = function (SESSION_ID, title, message, alert_type) {
  if (!UI_FRAMEWORK_PLUGIN.toast) return;
  const toast = new UI_FRAMEWORK_PLUGIN.toast();
  toast.create(alert_type, message, title, func.common.get_url(SESSION_ID, 'dist', `runtime/images/${alert_type}_alert_ico.svg`));
  ALERT_IS_ACTIVE = false;
};
func.utils.alerts.popup = function (title, message, alert_type) {
  const popup = new UI_FRAMEWORK_PLUGIN.popup();

  var buttons = [
    {
      text: 'Ok',
      role: 'cancel',
      // cssClass: "primary",
      handler: () => {
        ALERT_IS_ACTIVE = false;
      },
    },
  ];

  popup.create(_.upperFirst(alert_type), title, message, buttons);
};

func.utils.get_system_error_msg = function () {
  var m = {};
  m['SYS_MSG_0101'] = {
    alert_type: 'success',
    alertDisplay: 'toast',
    subject: 'Save Success',
    msg: 'Settings successfully saved',
  };
  m['SYS_MSG_0102'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Save Failed CouchDB',
    msg: 'Data fail save to database',
  };
  m['SYS_MSG_0103'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Save Failed Table Empty',
    msg: 'Table empty, no fields declared',
  };
  m['SYS_MSG_0104'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Save Failed Missing Primary Index',
    msg: 'Update failed, table missing Primary index',
  };
  m['SYS_MSG_0105'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Save Failed Table Missing',
    msg: 'Table repository missing',
  };
  m['SYS_MSG_0106'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Save Failed Record Not Exist',
    msg: 'Save update failed record not exist',
  };
  m['SYS_MSG_0107'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Save Failed Unique Key',
    msg: 'Save Failed, record already exist',
  };
  m['SYS_MSG_0108'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Error reading document',
    msg: 'Save Failed, record not found',
  };
  m['SYS_MSG_0110'] = {
    alert_type: 'warning',
    alertDisplay: 'toast',
    subject: 'Record Changed',
    msg: 'Record changed by other user, reload to get the latest changes',
  };
  m['SYS_MSG_0120'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Create Mode Denied',
    msg: 'Create mode not allowed for this program',
  };
  m['SYS_MSG_0122'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Modify Mode Denied',
    msg: 'Modify mode not allowed for this program',
  };
  m['SYS_MSG_0124'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Delete Mode Denied',
    msg: 'Delete mode not allowed for this program',
  };
  m['SYS_MSG_0126'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Program Read Only',
    msg: 'Program set to Read Only',
  };
  m['SYS_MSG_0130'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Error Reduce',
    msg: 'Select Index to Reduce',
  };
  m['SYS_MSG_0201'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Failed to change GUI Property',
    msg: 'Failed to change GUI element property, GUI element missing',
  };
  m['SYS_MSG_0310'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Missing Reference Parameters Out',
    msg: 'Parameter out not exist in dataset',
  };
  m['SYS_MSG_0400'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Delete Widget Folder Denied',
    msg: 'The selected folder contains data, Please clean or move content to another folder',
  };
  m['SYS_MSG_0410'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Recipient Error',
    msg: 'Check recipient data',
  };
  m['SYS_MSG_0412'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Recipient Empty',
    msg: 'No recipients entered or selected',
  };
  m['SYS_MSG_0414'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Data Save Error',
    msg: 'Widget has no content',
  };
  m['SYS_MSG_0416'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Required Field',
    msg: 'Edit url field is empty',
  };
  m['SYS_MSG_0418'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Required Field',
    msg: 'Publish url field is empty',
  };
  m['SYS_MSG_0420'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Connection Error',
    msg: 'Cannot connect to mailbox',
  };
  m['SYS_MSG_0422'] = {
    alert_type: 'success',
    alertDisplay: 'modal',
    subject: 'Connection Ok',
    msg: 'Connection Ok :)',
  };
  m['SYS_MSG_0424'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Connection Failed',
    msg: 'Connection to POP3 failed',
  };
  m['SYS_MSG_0426'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Connection Failed',
    msg: 'SMTP Connection error, Test Email was not sent',
  };
  m['SYS_MSG_0430'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Email Account Error',
    msg: 'No email account found, Right Click Tree -> Settings->Manage Accounts -> Right click for menu options',
  };
  m['SYS_MSG_0440'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Widget Initiation Error',
    msg: 'Missing information for Link Type or Link Name',
  };
  m['SYS_MSG_0442'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Error Init Widget',
    msg: 'Missing record Id on Create Mode',
  };
  m['SYS_MSG_0450'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Validation Failed',
    msg: 'Fix fields highlight in Red',
  };
  m['SYS_MSG_0501'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Mandatory Alert Save',
    msg: 'Save action failed, Mandatory fields missing',
  };

  m['SYS_MSG_0550'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Illegal input number',
    msg: "@SYS_GLOBAL_OBJ_ACTIVE_FIELD_INFO.nameform +' only allow numbers!'",
  };
  m['SYS_MSG_0610'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Form Field Conflict',
    msg: 'Field declared more than once for the form',
  };
  m['SYS_MSG_0612'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Mask Error - Missing Definition',
    msg: 'Missing mask definition',
  };
  m['SYS_MSG_0614'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Mask Error - Wrong Definition',
    msg: 'Wrong mask definition',
  };
  m['SYS_MSG_0616'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Mask Error - Size Parser',
    msg: 'Size parser error',
  };
  m['SYS_MSG_0618'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Mask Error - Illegal Z switch',
    msg: "Illegal 'Z' in string mask",
  };
  m['SYS_MSG_0620'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Mask Error - Illegal N switch',
    msg: "Illegal 'N' in string mask",
  };
  m['SYS_MSG_0622'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Mask Error - Illegal + switch',
    msg: "Illegal '+' in string mask",
  };
  m['SYS_MSG_0624'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Mask Error - Illegal - switch',
    msg: "Illegal '-' in string mask",
  };
  m['SYS_MSG_0626'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Mask Error - Illegal C switch',
    msg: "Illegal 'C' in string mask",
  };
  m['SYS_MSG_0628'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Mask Error - Invalid switch',
    msg: 'Invalid switch in string mask',
  };
  m['SYS_MSG_0630'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Mask Error - Missing DOM Element',
    msg: 'Missing DOM element',
  };
  m['SYS_MSG_0632'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Mask Error - Too Big',
    msg: 'Size to big, max: 15.5',
  };
  m['SYS_MSG_0700'] = {
    alert_type: 'warning',
    alertDisplay: 'console',
    subject: 'Table Warning - Empty',
    msg: 'Table has no content',
  };
  m['SYS_MSG_0702'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Table Error - No Fields',
    msg: 'Table missing fields content',
  };
  m['SYS_MSG_0704'] = {
    alert_type: 'warning',
    alertDisplay: 'console',
    subject: 'Table Warning - Not In Use',
    msg: 'Table not in use by any object',
  };
  m['SYS_MSG_0706'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Table Error - No Primary Index',
    msg: 'Table must have at least one index',
  };
  m['SYS_MSG_0708'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Table Error - Bad Index Name',
    msg: 'Index name is invalid or cannot contain any of non word characters',
  };
  m['SYS_MSG_0710'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Table Error - Empty Index',
    msg: 'Index has no keys',
  };
  m['SYS_MSG_0712'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Table Error - Key Not Exist',
    msg: 'Key not exist in the table fields repository',
  };
  m['SYS_MSG_0714'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Table Error - Duplicate Fields',
    msg: 'Duplicate fields in the table fields repository',
  };
  m['SYS_MSG_0716'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Table Error - Bad Field Name',
    msg: 'Field name is invalid or cannot contain any of non word characters',
  };
  m['SYS_MSG_0718'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Table Error - Integrity Broken',
    msg: 'Field broken from its properties, edit the field and save',
  };
  m['SYS_MSG_0720'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Table Error - Model Not Exist',
    msg: 'Model assigned to the field not exist',
  };
  m['SYS_MSG_0722'] = {
    alert_type: 'warning',
    alertDisplay: 'console',
    subject: 'Object Warning - Not In Use',
    msg: 'Object not in use or not call by any object',
  };
  m['SYS_MSG_0724'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Table Not Exist',
    msg: 'Table assigned in object datasource not exist',
  };
  m['SYS_MSG_0726'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Index Keys Mismatch',
    msg: 'Table index has different structure',
  };
  m['SYS_MSG_0728'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Index Key From Empty',
    msg: 'Index key From must have a value',
  };
  m['SYS_MSG_0730'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Index Key From Reference',
    msg: 'Field reference not exist in any dataset or parameters',
  };
  m['SYS_MSG_0732'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Index Key To Empty',
    msg: 'Index key To must have a value',
  };
  m['SYS_MSG_0734'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Index Key To Reference',
    msg: 'Field reference not exist in any dataset or parameters',
  };
  m['SYS_MSG_0736'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Index Locate From Reference',
    msg: 'Field reference not exist in any dataset or parameters',
  };
  m['SYS_MSG_0738'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Index Locate To Reference',
    msg: 'Field reference not exist in any dataset or parameters',
  };
  m['SYS_MSG_0740'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Index Empty',
    msg: 'Index empty - no keys defined',
  };
  m['SYS_MSG_0742'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Index Reference',
    msg: 'Table index reference error',
  };
  m['SYS_MSG_0744'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Duplicate Fields',
    msg: 'Duplicate fields in the dataset fields repository',
  };
  m['SYS_MSG_0746'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Field reference error',
    msg: 'Field not exist in datasource table fields repository',
  };
  m['SYS_MSG_0748'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Mismatch Reference Type',
    msg: 'Mismatch in calling reference type',
  };
  m['SYS_MSG_0750'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Reference Broken',
    msg: 'Reference broken calling object not exist',
  };
  m['SYS_MSG_0752'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Empty Reference',
    msg: 'Reference empty',
  };
  m['SYS_MSG_0754'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Action Not Exist',
    msg: 'Action not exist',
  };
  m['SYS_MSG_0756'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Empty Event Reference',
    msg: 'Empty event reference',
  };
  m['SYS_MSG_0758'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Bad Field Name',
    msg: 'Field name is invalid or cannot contain any of non word characters',
  };
  m['SYS_MSG_0760'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Integrity Broken',
    msg: 'Field broken from its properties, edit the object and save',
  };
  m['SYS_MSG_0762'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Model Not Exist',
    msg: 'Model assigned to the field not exist',
  };
  m['SYS_MSG_0764'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Empty Dataset',
    msg: 'Dataset empty from fields',
  };
  m['SYS_MSG_0766'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Field Type Mismatch',
    msg: 'Field type not match to the underlined table field definition',
  };
  m['SYS_MSG_0768'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Field Mask Mismatch',
    msg: 'Field masks not match to the underlined table field definition',
  };
  m['SYS_MSG_0770'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - Bad Event Name',
    msg: 'Event name is invalid or cannot contain any of non word characters',
  };
  m['SYS_MSG_0772'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - UI Field Not Exist',
    msg: 'UI Field not exist in the dataset repository',
  };
  m['SYS_MSG_0774'] = {
    alert_type: 'error',
    alertDisplay: 'console',
    subject: 'Object Error - UI Field Reference Broken',
    msg: 'UI Field reference broken',
  };
  m['SYS_MSG_0780'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'UI element error',
    msg: 'UI element not exist',
  };

  m['SYS_MSG_1210'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Program error',
    msg: 'Program not exist',
  };
  m['SYS_MSG_1220'] = {
    alert_type: 'error',
    alertDisplay: 'modal',
    subject: 'Program error',
    msg: 'Non grid output defined',
  };

  m['SYS_MSG_1240'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Debug error',
    msg: '',
  };
  m['SYS_MSG_1250'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Debug log error',
    msg: '',
  };
  m['SYS_MSG_1260'] = {
    alert_type: 'error',
    alertDisplay: 'toast',
    subject: 'Session Expired',
    msg: 'Renew token session in Studio',
  };
  return m;
};

func.utils.find_key_in_ViewUITreeObj = function (arr, key, val) {
  return arr.reduce((a, item) => {
    if (a) return a;
    if (item[key] === val) return item;
    if (item.children) return findId(val, item.children);
  }, null);
};

func.utils.get_plugin_setup = function (SESSION_ID, plugin_name) {
  const report_error = function (descP, warn) {
    func.utils.debug.log(SESSION_ID, plugin_name, {
      module: 'plugin',
      action: 'Init',
      source: 'get_plugin_setup',
      prop: descP,
      details: descP,
      result: null,
      error: warn ? false : true,
      fields: null,
      type: 'plugin',
    });
  };

  return new Promise(async (resolve, reject) => {
    try {
      const db = await func.utils.connect_pouchdb(SESSION_ID);

      try {
        let ret = await db.get(`cache_plugin_setup_${plugin_name}`);
        return resolve(ret.data);
      } catch (err) {
        // cache not found
        const json = await func.common.db(SESSION_ID, 'get_plugin_setup', {
          plugin_name,
        });
        if (json.code < 0) {
          report_error('Error: ' + json.data, json.error_type === 'W' ? true : false);
        }
        resolve(json);

        var doc = {
          _id: `cache_plugin_setup_${plugin_name}`,
          data: json,
          docType: 'cache_plugin',
        };
        db.put(doc);
      }
    } catch (e) {
      console.error(e);

      report_error('Error: ' + e.msg, e.error_type === 'W' ? true : false);
      resolve(e.msg);
    }
  });
};

func.utils.connect_pouchdb = async function (SESSION_ID) {
  const app_id = SESSION_OBJ[SESSION_ID].app_id;
  return new PouchDB('xuda_rt_' + app_id);
};

// func.utils.validate_pouchdb = async function (SESSION_ID) {
//   const app_id = SESSION_OBJ[SESSION_ID].app_id;
//   const db = new PouchDB("xuda_rt_" + app_id);
//   // db.info()
//   //   .then((info) => {
//   //     console.log("Database exists", info);
//   //   })
//   //   .catch((error) => {
//   //     if (error.status === 404) {
//   //       throw new Error("Database does not exist");
//   //     } else {
//   //       throw new Error("Error accessing database", error);
//   //     }
//   //   });

//   try {
//     let ret = await db.get(`cache_rt_info`);
//   } catch (error) {
//     throw new Error("new pouch detect");
//   }
// };

func.utils.call_plugin_api = function (SESSION_ID, plugin_nameP, dataP) {
  var _session = SESSION_OBJ[SESSION_ID];

  const report_error = function (descP, warn) {
    func.utils.debug.log(SESSION_ID, plugin_nameP, {
      module: 'plugin',
      action: 'Init',
      source: 'call_plugin_api',
      prop: descP,
      details: descP,
      result: null,
      error: warn ? false : true,
      fields: null,
      type: 'plugin',
    });
  };
  return new Promise(async (resolve) => {
    var data = {
      app_id: APP_OBJ[_session.app_id]._id,
      debug: glb.DEBUG_MODE,
      uid: _session.USR_OBJ._id,
      gtp_token: _session.gtp_token,
      app_token: _session.app_token,
    };

    data = _.assignIn(data, dataP);

    fetch(`https://xuda.io/ppi/${plugin_nameP}`, {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    })
      .then((response) => {
        if (!response.ok) {
          return response.text().then((text) => {
            throw new Error(text);
          });
        }
        return response.json();
      })
      .then((json) => {
        if (json.code < 0) {
          report_error('Error: ' + json.data, json.error_type === 'W' ? true : false);
        }
        resolve(json.data);

        // var doc = {
        //   _id: `cache_plugin_${plugin_nameP}`,
        //   data: json,
        //   docType: "cache_plugin",
        // };
        // db.put(doc);
      })
      .catch((err) => {
        report_error('Error: ' + err.message);
        resolve(err.message);
      });
    // }
  });
};

func.utils.get_plugin_resource = function (SESSION_ID, plugin_name, plugin_resource) {
  var _session = SESSION_OBJ[SESSION_ID];

  const get_path = function (resource) {
    if (_session.worker_type === 'Dev') {
      return `../../plugins/${plugin_name}/${resource}`;
    }
    if (typeof IS_PROCESS_SERVER !== 'undefined') {
      return `${_conf.plugins_drive_path}/${_session.app_id}/node_modules/${plugin_name}/${resource}`;
    } else {
      // return `./node_modules/${plugin_name}/${resource}?app_id=${_session.app_id}`;
      return `https://${_session.domain}/plugins/${plugin_name}/${resource}?app_id=${_session.app_id}&ts=${_session?.opt?.app_build_id || 0}`;
    }
  };

  return new Promise(async (resolve, reject) => {
    // const db = await func.utils.connect_pouchdb(SESSION_ID);

    // try {
    //   if (_session.worker_type === "Dev") throw "bypass cache";

    //   let ret = await db.get(`cache_plugin_${plugin_name}_${plugin_resource}`);
    //   return resolve(ret.data);
    // } catch (err) {
    //   try {
    //     const plugin_resource_res = await import(
    //       `${get_path(plugin_resource)}`
    //     );
    //     resolve(plugin_resource_res);

    //     // var doc = {
    //     //   _id: `cache_plugin_${plugin_name}_${plugin_resource}`,
    //     //   data: JSON.stringify(plugin_resource_res),
    //     //   docType: "cache_plugin",
    //     // };
    //     // db.put(doc);
    //   } catch (err) {
    //     console.error(err);
    //   }
    // }

    try {
      const plugin_resource_res = await import(`${get_path(plugin_resource)}`);
      resolve(plugin_resource_res);
    } catch (err) {
      console.error(err);
      reject();
    }
  });
};

func.utils.remove_cached_objects = async function (SESSION_ID) {
  if (typeof IS_DOCKER !== 'undefined' || typeof IS_PROCESS_SERVER !== 'undefined') return;
  try {
    const db = await func.utils.connect_pouchdb(SESSION_ID);

    let opt = {
      $or: [
        {
          docType: 'cache_objects',
        },
        {
          docType: 'cache_plugin',
        },
        {
          docType: 'cache_app',
        },
        {
          docType: 'cache_build_info',
        },
      ],
    };

    const res = await db.find({ selector: opt });
    for await (let val of res.docs) {
      await db.remove(val);
    }
  } catch (err) {
    return;
  }
};

func.utils.get_plugin_npm_cdn = async function (SESSION_ID, plugin_name, resource) {
  const _session = SESSION_OBJ[SESSION_ID];

  const get_path = function (resource) {
    if (_session.worker_type === 'Dev') {
      return `../../plugins/${plugin_name}/${resource}`;
    }

    return `https://${_session.domain}/plugins/${plugin_name}/${resource}?app_id=${_session.app_id}&ts=${_session?.opt?.app_build_id || 0}`;
  };

  return get_path(resource);
};

func.utils.write_log = async function (SESSION_ID, method = '', msg = '', log_type = 'error', source = 'runtime', details) {
  const _session = SESSION_OBJ[SESSION_ID];
  if (typeof IS_API_SERVER !== 'undefined' || typeof IS_DOCKER !== 'undefined' || typeof IS_PROCESS_SERVER !== 'undefined') {
    return __.rpi.write_log(_session.app_id, log_type, source, msg, details);
  }

  await func.common.db(SESSION_ID, 'write_log', {
    msg,
    log_type,
    source,
    details,
    method,
  });
};

func.utils.get_resource_filename = function (build, filename) {
  if (build) {
    return filename.replace(/(\.\w+)$/, `.${build}$1`);
  }
  return filename;
};

func.utils.set_SYS_GLOBAL_OBJ_WIDGET_INFO = async function (SESSION_ID, docP) {
  var obj = _.clone(docP);
  obj.date = await func.utils.get_dateTime(SESSION_ID, 'SYS_DATE', docP.date);
  obj.time = await func.utils.get_dateTime(SESSION_ID, 'SYS_TIME', docP.date);

  var datasource_changes = {
    [0]: {
      ['data_system']: {
        ['SYS_GLOBAL_OBJ_WIDGET_INFO']: obj,
      },
    },
  };
  await func.datasource.update(SESSION_ID, datasource_changes);
};

func.utils.get_last_datasource_no = function (SESSION_ID) {
  if (typeof IS_PROCESS_SERVER !== 'undefined') {
    return Object.keys(SESSION_OBJ[SESSION_ID].DS_GLB).at?.(-1);
  } else {
    const filtered = Object.values(SESSION_OBJ[SESSION_ID].DS_GLB).filter((e) => e.tree_obj.menuType !== 'api');
    return filtered?.at?.(-1)?.dsSession;
  }
};
