1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19/* 20 * 2021.01.08 - Add init global data and hook life cycle of App. 21 * Copyright (c) 2021 Huawei Device Co., Ltd. 22 */ 23 24import { 25 isModule, 26 removePrefix, 27 isApplication, 28 removeApplicationPrefix, 29 Log 30} from '../../utils/index'; 31import { 32 registerCustomComponent, 33 requireModule 34} from '../page/register'; 35import { pageMap, appMap } from './map'; 36import { updateLocale, updateDpi } from './index'; 37import Page from '../page/index'; 38import { App } from './App'; 39 40const APP_LIFE_CYCLE_TYPES: string[] = ['onCreate', 'onError', 'onDestroy', 'onShow', 'onHide']; 41 42export let CSS_INHERITANCE: string[]; 43 44/** 45 * Parse app page code. 46 * @param {Page} page 47 * @param {string} packageName - PackageName of App. 48 * @param {string} name - Name of page. 49 * @param {*[]} args 50 */ 51export const defineFn = function(page: Page, packageName: string, name?: string, ...args: any[] | null): void { 52 Log.debug(`Define a page ${name}.`); 53 const parseContent: Function = args[1]; 54 let bundleContent: any = null; 55 56 // Function to obtain bundle content. 57 if (parseContent) { 58 const pageRequire = (name: string) : any => { 59 if (isModule(name)) { 60 const appFunction = (): Page => { 61 return pageMap.getTop(packageName) || page; 62 }; 63 return requireModule(appFunction, removePrefix(name)); 64 } 65 }; 66 const moduleContent = { exports: {} }; 67 parseContent(pageRequire, moduleContent.exports, moduleContent); 68 bundleContent = moduleContent.exports; 69 70 let minPlatformVersion: number = 5; 71 if (bundleContent.manifest) { 72 minPlatformVersion = bundleContent.manifest.minPlatformVersion; 73 } 74 CSS_INHERITANCE = minPlatformVersion > 5 ? 75 ['fontFamily', 'fontWeight', 'fontSize', 'fontStyle', 'textAlign', 'lineHeight', 'letterSpacing', 'color', 'visibility'] : 76 []; 77 } 78 79 // Apply bundleContent. 80 if (isApplication(name)) { 81 const componetName: string = removeApplicationPrefix(name); 82 registerCustomComponent(page, componetName, bundleContent); 83 } 84}; 85 86/** 87 * Set i18n and dpi data, hook life cycle of App. 88 * @param {Page} page 89 * @param {string} packageName - PackageName of App. 90 * @param {string} name - Name of page. 91 * @param {*} config 92 * @param {*} data 93 * @return {*} 94 */ 95export function bootstrap(page: Page, packageName: string, name: string, config: any, data: any): any { 96 Log.debug(`Bootstrap for ${name}.`); 97 Log.debug(`${config} ${data}`); 98 99 // Check component name. 100 let componentName: string; 101 if (isApplication(name)) { 102 componentName = removeApplicationPrefix(name); 103 } else { 104 return new Error(`Wrong component name: ${name}.`); 105 } 106 107 // Init global data when page first load, 108 // global.aceapp.$data means config.data in manifest.json, can add new data by this.$app.$data api. 109 if (page.options && page.options.appCreate) { 110 const getApp = function() { 111 return global.aceapp; 112 }; 113 global.getApp = getApp; 114 global.aceapp = {}; 115 global.aceapp.$data = page.options.appGlobalData || {}; 116 117 // Set i18n and dpi data. 118 if (page.options.i18n) { 119 updateLocale(page.options.i18n); 120 } 121 if (page.options.resourcesConfiguration) { 122 updateDpi(page.options.resourcesConfiguration); 123 } 124 } 125 if (page.customComponentMap) { 126 const app: App = appMap[packageName]; 127 if (app) { 128 const options: object = page.customComponentMap[componentName] || {}; 129 const aceapp: any = global.aceapp || {}; 130 for (const key in options) { 131 if (!isReserved(key)) { 132 app[key] = options[key]; 133 aceapp[key] = options[key]; 134 } 135 } 136 aceapp.$def = aceapp; 137 aceapp._def = aceapp.$def; 138 aceapp._data = aceapp.$data; 139 140 // Exit api to $app. 141 aceapp.exit = function(): void { 142 }; 143 APP_LIFE_CYCLE_TYPES.forEach((type) => { 144 app.onEvent(`hook:${type}`, options[type]); 145 }); 146 147 // Last fire on Create. 148 Log.debug(`Page "onCreate" lifecycle in app(${app.packageName}).`); 149 app.emitEvent('hook:onCreate'); 150 } 151 } 152} 153 154/** 155 * Check input param is onCreate or onDestroy. 156 * @param {string} key 157 * @return {boolean} 158 */ 159function isReserved(key: string): boolean { 160 if (key === 'onCreate' || key === 'onDestroy') { 161 return true; 162 } 163 return false; 164} 165 166/** 167 * Return timerAPIs. 168 * @param {Page} page - Page 169 * @return {Object} 170 */ 171export function genTimerAPI(page: Page): object { 172 const timerAPIs: object = {}; 173 174 // Timer APIs polyfill in native 175 const timer: any = page.requireModule('timer'); 176 const animation = page.requireModule('animation'); 177 Object.assign(timerAPIs, { 178 setTimeout: (...args) => { 179 const handler = () => { 180 args[0](...args.slice(2)); 181 }; 182 timer.setTimeout(handler, args[1]); 183 return page.doc.taskCenter.callbackManager.currCallbackId.toString(); 184 }, 185 setInterval: (...args) => { 186 const handler = () => { 187 args[0](...args.slice(2)); 188 }; 189 timer.setInterval(handler, args[1]); 190 return page.doc.taskCenter.callbackManager.currCallbackId.toString(); 191 }, 192 clearTimeout: (n) => { 193 timer.clearTimeout(n); 194 page.doc.taskCenter.callbackManager.remove(n); 195 }, 196 clearInterval: (n) => { 197 timer.clearInterval(n); 198 page.doc.taskCenter.callbackManager.remove(n); 199 }, 200 requestAnimationFrame: (...args) => { 201 const handler = function(timestamp) { 202 args[0](timestamp, ...args.slice(1)); 203 }; 204 animation.requestAnimationFrame(handler); 205 return page.doc.taskCenter.callbackManager.currCallbackId.toString(); 206 }, 207 cancelAnimationFrame: (n) => { 208 animation.cancelAnimationFrame(n); 209 } 210 }); 211 return timerAPIs; 212} 213