UNPKG

2.76 kBPlain TextView Raw
1/**
2 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS-IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import { createMap, truncateArray } from "./util";
18import { flush, queueChange } from "./changes";
19
20/**
21 * Used to keep track of the previous values when a 2-way diff is necessary.
22 * This object is cleared out and reused.
23 */
24const prevValuesMap = createMap();
25
26/**
27 * Calculates the diff between previous and next values, calling the update
28 * function when an item has changed value. If an item from the previous values
29 * is not present in the the next values, the update function is called with a
30 * value of `undefined`.
31 * @param prev The previous values, alternating name, value pairs.
32 * @param next The next values, alternating name, value pairs.
33 * @param updateCtx The context for the updateFn.
34 * @param updateFn A function to call when a value has changed.
35 */
36function calculateDiff<T>(
37 prev: Array<string>,
38 next: Array<string>,
39 updateCtx: T,
40 updateFn: (ctx: T, x: string, y: {} | undefined) => void
41) {
42 const isNew = !prev.length;
43 let i = 0;
44
45 for (; i < next.length; i += 2) {
46 const name = next[i];
47 if (isNew) {
48 prev[i] = name;
49 } else if (prev[i] !== name) {
50 break;
51 }
52
53 const value = next[i + 1];
54 if (isNew || prev[i + 1] !== value) {
55 prev[i + 1] = value;
56 queueChange(updateFn, updateCtx, name, value);
57 }
58 }
59
60 // Items did not line up exactly as before, need to make sure old items are
61 // removed. This should be a rare case.
62 if (i < next.length || i < prev.length) {
63 const startIndex = i;
64
65 for (i = startIndex; i < prev.length; i += 2) {
66 prevValuesMap[prev[i]] = prev[i + 1];
67 }
68
69 for (i = startIndex; i < next.length; i += 2) {
70 const name = next[i] as string;
71 const value = next[i + 1];
72
73 if (prevValuesMap[name] !== value) {
74 queueChange(updateFn, updateCtx, name, value);
75 }
76
77 prev[i] = name;
78 prev[i + 1] = value;
79
80 delete prevValuesMap[name];
81 }
82
83 truncateArray(prev, next.length);
84
85 for (const name in prevValuesMap) {
86 queueChange(updateFn, updateCtx, name, undefined);
87 delete prevValuesMap[name];
88 }
89 }
90
91 flush();
92}
93
94export { calculateDiff };