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