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 { hasOwn } from '../../../utils/index'; 21 22/** 23 * This class provides action for page refresh. 24 */ 25export default class Differ { 26 private _id: string; 27 private _map: object[]; 28 private _hooks: Function[]; 29 private _hasTimer: boolean; 30 31 constructor(id: string) { 32 this._id = id; 33 this._map = []; 34 this._hooks = []; 35 } 36 37 /** 38 * Check whether the map is empty. 39 * @return {boolean} 40 */ 41 public isEmpty(): boolean { 42 return this._map.length === 0; 43 } 44 45 /** 46 * Id of the page. 47 * @type {string} 48 */ 49 public get id() { 50 return this._id; 51 } 52 53 /** 54 * Append action. 55 * @param {string} type 56 * @param {string} ref 57 * @param {Function} handler 58 */ 59 public append(type: string, ref: string, handler: Function): void { 60 // Ignore depth to speed up render. 61 const defaultDepth: number = 1; 62 if (!this._hasTimer) { 63 this._hasTimer = true; 64 65 // Use setTimeout instead of setTimeoutDiffer 66 setTimeout(() => { 67 this._hasTimer = false; 68 this.flush(); 69 }, 0); 70 } 71 const map: object[] = this._map; 72 if (!map[defaultDepth]) { 73 map[defaultDepth] = {}; 74 } 75 const group: object = map[defaultDepth]; 76 if (!group[type]) { 77 group[type] = {}; 78 } 79 if (type === 'element') { 80 if (!group[type][ref]) { 81 group[type][ref] = []; 82 } 83 group[type][ref].push(handler); 84 } else { 85 group[type][ref] = handler; 86 } 87 } 88 89 /** 90 * Execute actions of differ. 91 */ 92 public flush(): void { 93 const map: object[] = this._map.slice(); 94 this._map.length = 0; 95 map.forEach((group) => { 96 callTypeList(group, 'element'); 97 callTypeMap(group, 'repeat'); 98 callTypeMap(group, 'shown'); 99 }); 100 101 const hooks: Function[] = this._hooks.slice(); 102 this._hooks.length = 0; 103 hooks.forEach((fn) => { 104 fn(); 105 }); 106 107 if (!this.isEmpty()) { 108 this.flush(); 109 } 110 } 111 then(fn) { 112 this._hooks.push(fn); 113 } 114} 115 116function callTypeMap(group: any, type: string): void { 117 const map: Function[] = group[type]; 118 for (const ref in map) { 119 map[ref](); 120 } 121} 122 123function callTypeList(group: any, type: string): void { 124 const map: any = group[type]; 125 for (const ref in map) { 126 if (hasOwn(map, ref)) { 127 const list: Function[] = map[ref]; 128 list.forEach((handler) => { 129 handler(); 130 }); 131 } 132 } 133} 134