• 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      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