• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      // avoid invoking setTimeout after appDestroy
67      if (typeof setTimeout === "function") {
68        setTimeout(() => {
69          this._hasTimer = false;
70          this.flush();
71        }, 0);
72      }
73    }
74    const map: object[] = this._map;
75    if (!map[defaultDepth]) {
76      map[defaultDepth] = {};
77    }
78    const group: object = map[defaultDepth];
79    if (!group[type]) {
80      group[type] = {};
81    }
82    if (type === 'element') {
83      if (!group[type][ref]) {
84        group[type][ref] = [];
85      }
86      group[type][ref].push(handler);
87    } else {
88      group[type][ref] = handler;
89    }
90  }
91
92  /**
93   * Execute actions of differ.
94   */
95  public flush(): void {
96    const map: object[] = this._map.slice();
97    this._map.length = 0;
98    map.forEach((group) => {
99      callTypeList(group, 'element');
100      callTypeMap(group, 'repeat');
101      callTypeMap(group, 'shown');
102    });
103
104    const hooks: Function[] = this._hooks.slice();
105    this._hooks.length = 0;
106    hooks.forEach((fn) => {
107      fn();
108    });
109
110    if (!this.isEmpty()) {
111      this.flush();
112    }
113  }
114  then(fn) {
115    this._hooks.push(fn);
116  }
117}
118
119function callTypeMap(group: any, type: string): void {
120  const map: Function[] = group[type];
121  for (const ref in map) {
122    map[ref]();
123  }
124}
125
126function callTypeList(group: any, type: string): void {
127  const map: any = group[type];
128  for (const ref in map) {
129    if (hasOwn(map, ref)) {
130      const list: Function[] = map[ref];
131      list.forEach((handler) => {
132        handler();
133      });
134    }
135  }
136}
137