1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { Log } from '../../utils/index'; 17import { 18 defineFn, 19 bootstrap, 20 genTimerAPI 21} from './bundle'; 22import { appMap } from './map'; 23import { getPageGlobal } from './helper'; 24import { App } from './App'; 25import { PageLinkedMap } from './map'; 26import Page from '../page/index'; 27import { destroy } from '../page/api/index'; 28import { mockSystemPlugin } from '../extend/systemplugin/index'; 29import { compileBundle } from '../page/entry/init'; 30import { removePrefix } from '../util/index'; 31import { requireModule } from '../page/register'; 32 33/** 34 * Device information for mediaQuery. 35 */ 36export interface MediaQueryInfo { 37 'orientation': string; 38 'device-type': string; 39 'device-width': string; 40 'device-height': string; 41 'round-screen': boolean; 42 'width': string; 43 'height': string; 44 'isInit': boolean; 45 'resolution': string; 46 'aspect-ratio': string; 47 'dark-mode': string; 48} 49 50/** 51 * Information of App. 52 */ 53export interface Options extends MediaQueryInfo { 54 'appInstanceId': string; 55 'packageName': string; 56 'appCreate': boolean; 57 'appCode': string | Function; 58 'pcPreview': string; 59 'resourcesConfiguration': object; 60 'i18n': object; 61 'language': string; 62 'appGlobalData'?: object; 63 'bundleUrl': string; 64} 65 66/** 67 * Framework Services. 68 */ 69export interface Services { 70 service: object; 71 I18n?: Function; 72 dpi?: Function; 73} 74 75interface ParseOptions { 76 $app_define$(...args: any[]): void; // eslint-disable-line camelcase 77 $app_bootstrap$(name: string, config: any, _data: any): void; // eslint-disable-line camelcase 78 $app_require$(name: string): void; // eslint-disable-line camelcase 79} 80 81const pageMap: PageLinkedMap = App.pageMap; 82 83/** 84 * Create app page, run jsbundle code. 85 * @param {Page} page 86 * @param {Options} options 87 * @param {Object} data 88 * @param {Services} services 89 */ 90export function appCreate(page: Page, options: Options, data: object, services: Services): void { 91 if (!options || !options.appCreate || !options.appCode) { 92 return; 93 } 94 if (options.pcPreview && options.pcPreview === 'enable') { 95 global.pcPreview = true; 96 mockSystemPlugin(); 97 } 98 const packageName: string = page.packageName; 99 const appPage: Page = new Page(options.appInstanceId, options, packageName, data); 100 pageMap.unshift(appPage); 101 Log.debug(`Create a page with: ${packageName}.`); 102 appMap[packageName] = new App(packageName, options.appInstanceId); 103 const timerAPIs: object = genTimerAPI(appPage); 104 appMap[packageName].setTimer(timerAPIs); 105 const code = options.appCode; 106 global.__appProto__ = getPageGlobal(packageName); 107 108 // prepare page env methods 109 const appDefine = (...args: any[]): void => defineFn(page, packageName, ...args); 110 const appBootstrap = (name: string, config: any, _data: any): void => { 111 bootstrap(page, packageName, name, config, _data || data); 112 Log.debug(`After create a page(${page.id}).`); 113 }; 114 115 const appFunction = () => pageMap.getTop(packageName) || page; 116 117 // require in top app(instance) 118 const appRequireModule = name => requireModule(appFunction, removePrefix(name)); 119 const parseOptions: ParseOptions = { 120 $app_define$: appDefine, 121 $app_bootstrap$: appBootstrap, 122 $app_require$: appRequireModule 123 }; 124 global.$app_require$ = appRequireModule; 125 126 // Compile js bundle code and get result. 127 if (typeof code === 'function') { 128 Log.info('call Function directly when appCreate'); 129 code.call(global, parseOptions); 130 } else { 131 // Function with code and use strict mode. 132 const functionCode: string = `(function(global){\n\n"use strict";\n\n ${code} \n\n})(this.__appProto__)`; 133 compileBundle(functionCode, 'app.js', parseOptions, timerAPIs, services); 134 } 135} 136 137/** 138 * Emit onError event. 139 * @param {string} packageName 140 * @param {*} errors 141 */ 142export function appError(packageName: string, errors: any): void { 143 Log.debug(`AppError an app with: ${packageName}.`); 144 const app: App = appMap[packageName]; 145 if (!app) { 146 Log.debug(`AppError an app error ${packageName}.`); 147 return; 148 } 149 Log.debug(`AppError an app error ${packageName}.`); 150 app.emitEvent('hook:onError', errors); 151} 152 153/** 154 * Emit onShow event. 155 * @param {string} packageName - Package name. 156 */ 157export function appShow(packageName: string): void { 158 Log.debug(`Show an app with: ${packageName}.`); 159 const app: App = appMap[packageName]; 160 if (!app) { 161 Log.debug(`Show an app error ${packageName}.`); 162 return; 163 } 164 app.emitEvent('hook:onShow'); 165} 166 167/** 168 * Emit onHide event. 169 * @param {string} packageName - Package name. 170 */ 171export function appHide(packageName: string): void { 172 Log.debug(`Hide an app with: ${packageName}.`); 173 const app: App = appMap[packageName]; 174 if (!app) { 175 Log.debug(`Hide an app error ${packageName}.`); 176 return; 177 } 178 app.emitEvent('hook:onHide'); 179} 180 181/** 182 * Emit onDestroy event. 183 * @param {string} packageName - Package name. 184 */ 185export function appDestroy(packageName: string): void { 186 Log.debug(`Destroy an app with: ${packageName}.`); 187 const app: App = appMap[packageName]; 188 if (!app) { 189 Log.error(`Destroy an app error ${packageName}.`); 190 return; 191 } 192 app.emitEvent('hook:onDestroy'); 193 app.deleteGlobalKeys(); 194 delete appMap[packageName]; 195 const appPage: Page = pageMap[app.appInstanceId]; 196 if (appPage) { 197 if (appPage.doc.taskCenter.callbackIsEmpty()) { 198 appPage.callTasks([{ 199 module: 'internal.jsResult', 200 method: 'appDestroyFinish', 201 args: [] 202 }]); 203 destroy(appPage); 204 pageMap.remove(appPage); 205 } else { 206 appPage.destroyed = true; 207 } 208 } 209} 210 211/** 212 * Init language resource. 213 * @param {*} i18nData 214 */ 215export function updateLocale(i18nData: any): void { 216 if (i18nData) { 217 global.aceapp._i18n_data_ = { messages: i18nData.resources }; 218 } else { 219 global.aceapp._i18n_data_ = null; 220 } 221} 222 223/** 224 * Init image dpi. 225 * @param {Object} dpiData 226 */ 227export function updateDpi(dpiData: object): void { 228 if (dpiData) { 229 global.aceapp._dpi_data_ = { images: dpiData }; 230 } else { 231 global.aceapp._dpi_data_ = null; 232 } 233} 234