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 20import { Log } from '../../utils/index'; 21import Watcher from '../reactivity/watcher'; 22import Element from '../../vdom/Element'; 23import Vm from './index'; 24 25/** 26 * Enum for Page lifecycle hooks. 27 * @enum {string} 28 * @readonly 29 */ 30/* eslint-disable no-unused-vars */ 31export const enum PageLifecycleHooks { 32 /** 33 * ONINIT Type 34 */ 35 ONINIT = 'onInit', 36 /** 37 * ONREADY Type 38 */ 39 ONREADY = 'onReady', 40 /** 41 * ONSHOW Type 42 */ 43 ONSHOW = 'onShow', 44 /** 45 * ONHIDE Type 46 */ 47 ONHIDE = 'onHide', 48 /** 49 * ONBACKPRESS Type 50 */ 51 ONBACKPRESS = 'onBackPress', 52 /** 53 * ONMENUPRESS Type 54 */ 55 ONMENUPRESS = 'onMenuPress', 56 /** 57 * ONMENUBUTTONPRESS Type 58 */ 59 ONMENUBUTTONPRESS = 'onMenuButtonPress', 60 /** 61 * ONSUSPENDED Type 62 */ 63 ONSUSPENDED = 'onSuspended', 64 /** 65 * ONSTARTCONTINUATUIN Type 66 */ 67 ONSTARTCONTINUATUIN = 'onStartContinuation', 68 /** 69 * ONCOMPLETECONTINUATION Type 70 */ 71 ONCOMPLETECONTINUATION = 'onCompleteContinuation', 72 /** 73 * ONSAVEDATA Type 74 */ 75 ONSAVEDATA = 'onSaveData', 76 /** 77 * ONRESTOREDATA Type 78 */ 79 ONRESTOREDATA = 'onRestoreData', 80 /** 81 * ONNEWREQUEST Type 82 */ 83 ONNEWREQUEST = 'onNewRequest', 84 /** 85 * ONCONFIGURATIONUPDATED Type 86 */ 87 ONCONFIGURATIONUPDATED = 'onConfigurationUpdated', 88 /** 89 * ONLAYOUTREADY Type 90 */ 91 ONLAYOUTREADY = 'onLayoutReady', 92 /** 93 * ONACTIVE Type 94 */ 95 ONACTIVE = 'onActive', 96 /** 97 * ONLAYOUTREADY Type 98 */ 99 ONINACTIVE = 'onInactive' 100} 101/* eslint-enable no-unused-vars */ 102 103const PAGE_LIFE_CYCLE_TYPES: Array<PageLifecycleHooks> = [ 104 PageLifecycleHooks.ONSHOW, 105 PageLifecycleHooks.ONHIDE, 106 PageLifecycleHooks.ONBACKPRESS, 107 PageLifecycleHooks.ONMENUPRESS, 108 PageLifecycleHooks.ONMENUBUTTONPRESS, 109 PageLifecycleHooks.ONSTARTCONTINUATUIN, 110 PageLifecycleHooks.ONCOMPLETECONTINUATION, 111 PageLifecycleHooks.ONSAVEDATA, 112 PageLifecycleHooks.ONRESTOREDATA, 113 PageLifecycleHooks.ONNEWREQUEST, 114 PageLifecycleHooks.ONCONFIGURATIONUPDATED, 115 PageLifecycleHooks.ONLAYOUTREADY, 116 PageLifecycleHooks.ONACTIVE, 117 PageLifecycleHooks.ONINACTIVE 118]; 119 120/** 121 * Bind page lifeCycle. 122 * @param {Vm} vm - Vm object. 123 * @param {Element} element - Element object. 124 */ 125export function bindPageLifeCycle(vm: Vm, element: Element): void { 126 const options = vm.vmOptions || {}; 127 PAGE_LIFE_CYCLE_TYPES.forEach(type => { 128 let eventType; 129 if (type === PageLifecycleHooks.ONSHOW) { 130 eventType = 'viewappear'; 131 } else if (type === PageLifecycleHooks.ONHIDE) { 132 eventType = 'viewdisappear'; 133 } else if (type === PageLifecycleHooks.ONBACKPRESS) { 134 eventType = 'clickbackitem'; 135 } else if (type === PageLifecycleHooks.ONSUSPENDED) { 136 eventType = 'viewsuspended'; 137 } else if (type === 'onConfigurationUpdated') { 138 eventType = 'onConfigurationUpdated'; 139 } else if (type === 'onLayoutReady') { 140 eventType = 'layoutReady'; 141 } else if (type === 'onActive') { 142 eventType = 'viewactive'; 143 } else if (type === 'onInactive') { 144 eventType = 'viewinactive'; 145 } else { 146 eventType = type; 147 } 148 149 const handle = options[type]; 150 let isEmitEvent = false; 151 if (handle) { 152 isEmitEvent = true; 153 element.addEvent(eventType, eventHandle); 154 } else { 155 if (type === PageLifecycleHooks.ONSHOW || type === PageLifecycleHooks.ONHIDE) { 156 element.addEvent(eventType, eventHandle); 157 } 158 } 159 160 /** 161 * Hadle event methods. 162 * @param {*} event - Event methods. 163 * @param {*} args - Arg list. 164 * @return {*} 165 */ 166 function eventHandle(event, ...args: any[]): any { 167 if (type === PageLifecycleHooks.ONSHOW) { 168 emitSubVmLife(vm, 'onPageShow'); 169 vm.visible = true; 170 } else if (type === PageLifecycleHooks.ONHIDE) { 171 emitSubVmLife(vm, 'onPageHide'); 172 vm.visible = false; 173 } else if (type === PageLifecycleHooks.ONCONFIGURATIONUPDATED) { 174 return vm.$emitDirect(`hook:${type}`, ...args); 175 } 176 177 Log.debug(`EventHandle: isEmitEvent = ${isEmitEvent}, event = ${event}, args = ${JSON.stringify(args)}.`); 178 if (isEmitEvent) { 179 if (type === PageLifecycleHooks.ONNEWREQUEST) { 180 return handleNewRequest(args[0]); 181 } else if (type === PageLifecycleHooks.ONSAVEDATA) { 182 return handleSaveData(); 183 } else if (type === PageLifecycleHooks.ONRESTOREDATA) { 184 return handleRestoreData(args[0]); 185 } else if (type === PageLifecycleHooks.ONCOMPLETECONTINUATION) { 186 return vm.$emitDirect(`hook:${type}`, ...args); 187 } else { 188 return vm.$emit(`hook:${type}`, {}, ...args); 189 } 190 } 191 } 192 193 /** 194 * Handle saveData. 195 * @return {string | boolean} If no hook, return false. Otherwise return vm.shareData. 196 */ 197 function handleSaveData(): string | boolean { 198 const allData = { 199 saveData: {}, 200 shareData: {} 201 }; 202 const result = vm.$emitDirect(`hook:${type}`, allData.saveData); 203 if (!result) { 204 return false; 205 } 206 const shareResult = vm.shareData || {}; 207 if (shareResult instanceof Object && !(shareResult instanceof Array)) { 208 allData.shareData = shareResult; 209 } 210 return JSON.stringify(allData); 211 } 212 213 /** 214 * Handle restore Data. 215 * @param {*} restoreData - Restore data. 216 * @return {*} 217 */ 218 function handleRestoreData(restoreData: any) { 219 const saveData = restoreData.saveData || {}; 220 const shareData = restoreData.shareData || {}; 221 222 Object.assign(vm.shareData, shareData); 223 return vm.$emitDirect(`hook:${type}`, saveData); 224 } 225 function handleNewRequest(data: any) { 226 Object.assign(vm.__data, data); 227 return vm.$emitDirect(`hook:${type}`); 228 } 229 }); 230} 231 232/** 233 * Watch a calc function and callback if the calc value changes. 234 * @param {Vm} vm - Vm object. 235 * @param {string} data - Data that needed. 236 * @param {Function | string} callback - Callback function. 237 * @return {*} 238 */ 239export function watch(vm: Vm, data: string, callback: ((...args: any) => any) | string): any { 240 function calc() { 241 let arr = []; 242 arr = data.split('.'); 243 let retData = this; 244 arr.forEach(type => { 245 if (retData) { 246 retData = retData[type]; 247 } 248 }); 249 return retData; 250 } 251 const watcher = new Watcher(vm, calc, function(value, oldValue) { 252 if (typeof value !== 'object' && value === oldValue) { 253 return; 254 } 255 if (typeof callback === 'function') { 256 callback(value, oldValue); 257 } else { 258 if (vm.methods[callback] && typeof vm.methods[callback] === 'function') { 259 vm.methods[callback](value, oldValue); 260 } 261 } 262 }, null); 263 return watcher.value; 264} 265 266/** 267 * Prop is assigned to data to observe prop. 268 * @param {Vm} vm - Vm object. 269 */ 270export function initPropsToData(vm: Vm): void { 271 vm.props.forEach(prop => { 272 if (vm.__data) { 273 vm.__data[prop] = vm[prop]; 274 } 275 }); 276} 277 278/** 279 * Emit subVm lifecycle 280 * @param {Vm} vm - Vm object. 281 * @param {String} type - event type 282 */ 283export function emitSubVmLife(vm: Vm, type:string) { 284 if (vm.childrenVms) { 285 vm.childrenVms.forEach((subVm) => { 286 subVm.$emit(`hook:${type}`); 287 emitSubVmLife(subVm, type); 288 }); 289 } 290} 291