1 | import { NgZone, Injectable, ApplicationRef, APP_INITIALIZER, Injector } from '@angular/core';
|
2 | import { Subject } from 'rxjs/Subject';
|
3 | import { async } from 'rxjs/scheduler/async';
|
4 | import 'rxjs/add/operator/debounceTime';
|
5 | import 'rxjs/add/operator/take';
|
6 | import 'rxjs/add/operator/publish';
|
7 | import 'rxjs/add/operator/observeOn';
|
8 |
|
9 |
|
10 | export type Deadline = {didTimeout: boolean, timeRemaining: () => number};
|
11 | export type IdleCallback = (deadline: Deadline) => void;
|
12 | export type IdleOptions = {timeout: number};
|
13 |
|
14 | @Injectable()
|
15 | export class Idle {
|
16 | idleHandlers = new Map();
|
17 | stableObservable$;
|
18 | constructor(public ngZone: NgZone) {
|
19 | this.stableObservable$ = this.ngZone
|
20 | .onStable
|
21 | .observeOn(async)
|
22 | .publish()
|
23 | .refCount();
|
24 | }
|
25 | requestIdleCallback(callback) {
|
26 | if ('requestIdleCallback' in window) {
|
27 | window['requestIdleCallback'](callback);
|
28 | } else {
|
29 | this.polyfillRequestIdleCallback(callback);
|
30 | }
|
31 | }
|
32 |
|
33 | cancelIdleCallback(handler) {
|
34 | if ('cancelIdleCallback' in window) {
|
35 | window['cancelIdleCallback'](handler);
|
36 | } else {
|
37 | this.polyfillCancelIdleCallback(handler);
|
38 | }
|
39 | }
|
40 |
|
41 | polyfillCancelIdleCallback(handler) {
|
42 | let {unsubscribe, timerId}: any = this.idleHandlers.get(handler);
|
43 |
|
44 | if (unsubscribe) {
|
45 | unsubscribe();
|
46 | }
|
47 | if (timerId) {
|
48 | this.ngZone.runOutsideAngular(function() {
|
49 | clearTimeout(timerId);
|
50 | });
|
51 | }
|
52 |
|
53 | this.idleHandlers.delete(handler);
|
54 | }
|
55 |
|
56 | polyfillRequestIdleCallback(callback: IdleCallback, {timeout}: IdleOptions = {timeout: 50}) {
|
57 | let dispose = undefined;
|
58 |
|
59 | const _self = this;
|
60 | _self.ngZone.runOutsideAngular(() => {
|
61 | function cb(): void {
|
62 | const start: number = Date.now();
|
63 | const deadline: Deadline = {
|
64 | didTimeout: false,
|
65 | timeRemaining() {
|
66 | return Math.max(0, 50 - (Date.now() - start));
|
67 | }
|
68 | };
|
69 |
|
70 | setTimeout(() => {
|
71 | deadline.didTimeout = true;
|
72 | if (dispose) {
|
73 | dispose.unsubscribe();
|
74 | }
|
75 | }, timeout || 50);
|
76 |
|
77 | callback(deadline);
|
78 | }
|
79 |
|
80 | if (this.ngZone.isStable) {
|
81 | let timerId = setTimeout(cb, 10);
|
82 | this.idleHandlers.set(callback, {
|
83 | timerId
|
84 | });
|
85 | } else {
|
86 | dispose = this.stableObservable$
|
87 | .debounceTime(10)
|
88 | .take(1)
|
89 | .subscribe(
|
90 | () => {
|
91 | let timerId = setTimeout(cb, 10);
|
92 | this.idleHandlers.set(callback, {
|
93 | unsubscribe: dispose.unsubscribe,
|
94 | timerId
|
95 | });
|
96 | });
|
97 | this.idleHandlers.set(callback, {
|
98 | unsubscribe: dispose.unsubscribe
|
99 | });
|
100 | }
|
101 |
|
102 | });
|
103 |
|
104 | }
|
105 | }
|
106 |
|
107 | export const ANGULARCLASS_IDLE_PROVIDERS = [
|
108 | Idle
|
109 | ];
|
110 |
|
111 | export function setupPrefetchInitializer(injector: Injector, callbacks?: Array<Function>) {
|
112 | let defaultReturnValue = (): any => null;
|
113 | if (!callbacks || !callbacks.length) {
|
114 | return defaultReturnValue;
|
115 | }
|
116 |
|
117 |
|
118 | setTimeout(() => {
|
119 | const appRef = injector.get(ApplicationRef);
|
120 | if (appRef.componentTypes.length === 0) {
|
121 | appRef.registerBootstrapListener(() => {
|
122 | let idle = injector.get(Idle);
|
123 | callbacks.forEach((cb) => idle.requestIdleCallback(cb));
|
124 | });
|
125 | } else {
|
126 | let idle = injector.get(Idle);
|
127 | callbacks.forEach((cb) => idle.requestIdleCallback(cb));
|
128 | }
|
129 | }, 0);
|
130 |
|
131 | return defaultReturnValue;
|
132 | }
|
133 |
|
134 | export function providePrefetchIdleCallbacks(prefetchCallbacks = []) {
|
135 | return [
|
136 | ...ANGULARCLASS_IDLE_PROVIDERS,
|
137 |
|
138 | { provide: APP_INITIALIZER, multi: true, useFactory: (injector) => {
|
139 | return setupPrefetchInitializer(injector, prefetchCallbacks);
|
140 | }, deps: [Injector] }
|
141 | ];
|
142 | }
|