/
array.ts
104 lines (87 loc) · 2.67 KB
/
array.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import { EmberArray } from '@ember/-internals/utils';
import { arrayContentDidChange, arrayContentWillChange } from './array_events';
import { addListener, removeListener } from './events';
const EMPTY_ARRAY = Object.freeze([]);
interface ObservedObject<T> extends EmberArray<T> {
_revalidate?: () => void;
}
export function objectAt<T>(array: T[] | EmberArray<T>, index: number): T | undefined {
if (Array.isArray(array)) {
return array[index];
} else {
return array.objectAt(index);
}
}
export function replace<T>(
array: T[] | EmberArray<T>,
start: number,
deleteCount: number,
items = EMPTY_ARRAY
): void {
if (Array.isArray(array)) {
replaceInNativeArray(array, start, deleteCount, items);
} else {
array.replace(start, deleteCount, items as any);
}
}
const CHUNK_SIZE = 60000;
// To avoid overflowing the stack, we splice up to CHUNK_SIZE items at a time.
// See https://code.google.com/p/chromium/issues/detail?id=56588 for more details.
export function replaceInNativeArray<T>(
array: T[] | EmberArray<T>,
start: number,
deleteCount: number,
items: ReadonlyArray<T>
): void {
arrayContentWillChange(array, start, deleteCount, items.length);
if (items.length <= CHUNK_SIZE) {
array.splice(start, deleteCount, ...items);
} else {
array.splice(start, deleteCount);
for (let i = 0; i < items.length; i += CHUNK_SIZE) {
let chunk = items.slice(i, i + CHUNK_SIZE);
array.splice(start + i, 0, ...chunk);
}
}
arrayContentDidChange(array, start, deleteCount, items.length);
}
interface ArrayObserverOptions {
willChange: string;
didChange: string;
}
type Operation<T> = (
obj: ObservedObject<T>,
eventName: string,
target: object | Function | null,
callbackName: string
) => void;
function arrayObserversHelper<T>(
obj: ObservedObject<T>,
target: object | Function | null,
opts: ArrayObserverOptions,
operation: Operation<T>
): ObservedObject<T> {
let { willChange, didChange } = opts;
operation(obj, '@array:before', target, willChange);
operation(obj, '@array:change', target, didChange);
/*
* Array proxies have a `_revalidate` method which must be called to set
* up their internal array observation systems.
*/
obj._revalidate?.();
return obj;
}
export function addArrayObserver<T>(
array: EmberArray<T>,
target: object | Function | null,
opts: ArrayObserverOptions
): ObservedObject<T> {
return arrayObserversHelper(array, target, opts, addListener);
}
export function removeArrayObserver<T>(
array: EmberArray<T>,
target: object | Function | null,
opts: ArrayObserverOptions
): ObservedObject<T> {
return arrayObserversHelper(array, target, opts, removeListener);
}