summaryrefslogtreecommitdiff
path: root/frontend-old/node_modules/web-vitals/src/lib/polyfills
diff options
context:
space:
mode:
Diffstat (limited to 'frontend-old/node_modules/web-vitals/src/lib/polyfills')
-rw-r--r--frontend-old/node_modules/web-vitals/src/lib/polyfills/firstInputPolyfill.ts174
-rw-r--r--frontend-old/node_modules/web-vitals/src/lib/polyfills/getFirstHiddenTimePolyfill.ts29
-rw-r--r--frontend-old/node_modules/web-vitals/src/lib/polyfills/interactionCountPolyfill.ts63
3 files changed, 266 insertions, 0 deletions
diff --git a/frontend-old/node_modules/web-vitals/src/lib/polyfills/firstInputPolyfill.ts b/frontend-old/node_modules/web-vitals/src/lib/polyfills/firstInputPolyfill.ts
new file mode 100644
index 0000000..804c423
--- /dev/null
+++ b/frontend-old/node_modules/web-vitals/src/lib/polyfills/firstInputPolyfill.ts
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+ FirstInputPolyfillEntry,
+ FirstInputPolyfillCallback,
+} from '../../types.js';
+
+type addOrRemoveEventListener =
+ | typeof addEventListener
+ | typeof removeEventListener;
+
+let firstInputEvent: Event | null;
+let firstInputDelay: number;
+let firstInputTimeStamp: Date;
+let callbacks: FirstInputPolyfillCallback[];
+
+const listenerOpts: AddEventListenerOptions = {passive: true, capture: true};
+const startTimeStamp: Date = new Date();
+
+/**
+ * Accepts a callback to be invoked once the first input delay and event
+ * are known.
+ */
+export const firstInputPolyfill = (
+ onFirstInput: FirstInputPolyfillCallback,
+) => {
+ callbacks.push(onFirstInput);
+ reportFirstInputDelayIfRecordedAndValid();
+};
+
+export const resetFirstInputPolyfill = () => {
+ callbacks = [];
+ firstInputDelay = -1;
+ firstInputEvent = null;
+ eachEventType(addEventListener);
+};
+
+/**
+ * Records the first input delay and event, so subsequent events can be
+ * ignored. All added event listeners are then removed.
+ */
+const recordFirstInputDelay = (delay: number, event: Event) => {
+ if (!firstInputEvent) {
+ firstInputEvent = event;
+ firstInputDelay = delay;
+ firstInputTimeStamp = new Date();
+
+ eachEventType(removeEventListener);
+ reportFirstInputDelayIfRecordedAndValid();
+ }
+};
+
+/**
+ * Reports the first input delay and event (if they're recorded and valid)
+ * by running the array of callback functions.
+ */
+const reportFirstInputDelayIfRecordedAndValid = () => {
+ // In some cases the recorded delay is clearly wrong, e.g. it's negative
+ // or it's larger than the delta between now and initialization.
+ // - https://github.com/GoogleChromeLabs/first-input-delay/issues/4
+ // - https://github.com/GoogleChromeLabs/first-input-delay/issues/6
+ // - https://github.com/GoogleChromeLabs/first-input-delay/issues/7
+ if (
+ firstInputDelay >= 0 &&
+ // @ts-ignore (subtracting two dates always returns a number)
+ firstInputDelay < firstInputTimeStamp - startTimeStamp
+ ) {
+ const entry = {
+ entryType: 'first-input',
+ name: firstInputEvent!.type,
+ target: firstInputEvent!.target,
+ cancelable: firstInputEvent!.cancelable,
+ startTime: firstInputEvent!.timeStamp,
+ processingStart: firstInputEvent!.timeStamp + firstInputDelay,
+ } as FirstInputPolyfillEntry;
+ callbacks.forEach(function (callback) {
+ callback(entry);
+ });
+ callbacks = [];
+ }
+};
+
+/**
+ * Handles pointer down events, which are a special case.
+ * Pointer events can trigger main or compositor thread behavior.
+ * We differentiate these cases based on whether or not we see a
+ * 'pointercancel' event, which are fired when we scroll. If we're scrolling
+ * we don't need to report input delay since FID excludes scrolling and
+ * pinch/zooming.
+ */
+const onPointerDown = (delay: number, event: Event) => {
+ /**
+ * Responds to 'pointerup' events and records a delay. If a pointer up event
+ * is the next event after a pointerdown event, then it's not a scroll or
+ * a pinch/zoom.
+ */
+ const onPointerUp = () => {
+ recordFirstInputDelay(delay, event);
+ removePointerEventListeners();
+ };
+
+ /**
+ * Responds to 'pointercancel' events and removes pointer listeners.
+ * If a 'pointercancel' is the next event to fire after a pointerdown event,
+ * it means this is a scroll or pinch/zoom interaction.
+ */
+ const onPointerCancel = () => {
+ removePointerEventListeners();
+ };
+
+ /**
+ * Removes added pointer event listeners.
+ */
+ const removePointerEventListeners = () => {
+ removeEventListener('pointerup', onPointerUp, listenerOpts);
+ removeEventListener('pointercancel', onPointerCancel, listenerOpts);
+ };
+
+ addEventListener('pointerup', onPointerUp, listenerOpts);
+ addEventListener('pointercancel', onPointerCancel, listenerOpts);
+};
+
+/**
+ * Handles all input events and records the time between when the event
+ * was received by the operating system and when it's JavaScript listeners
+ * were able to run.
+ */
+const onInput = (event: Event) => {
+ // Only count cancelable events, which should trigger behavior
+ // important to the user.
+ if (event.cancelable) {
+ // In some browsers `event.timeStamp` returns a `DOMTimeStamp` value
+ // (epoch time) instead of the newer `DOMHighResTimeStamp`
+ // (document-origin time). To check for that we assume any timestamp
+ // greater than 1 trillion is a `DOMTimeStamp`, and compare it using
+ // the `Date` object rather than `performance.now()`.
+ // - https://github.com/GoogleChromeLabs/first-input-delay/issues/4
+ const isEpochTime = event.timeStamp > 1e12;
+ const now = isEpochTime ? new Date() : performance.now();
+
+ // Input delay is the delta between when the system received the event
+ // (e.g. event.timeStamp) and when it could run the callback (e.g. `now`).
+ const delay = (now as number) - event.timeStamp;
+
+ if (event.type == 'pointerdown') {
+ onPointerDown(delay, event);
+ } else {
+ recordFirstInputDelay(delay, event);
+ }
+ }
+};
+
+/**
+ * Invokes the passed callback const for = each event type with t =>he
+ * `onInput` const and = `listenerOpts =>`.
+ */
+const eachEventType = (callback: addOrRemoveEventListener) => {
+ const eventTypes = ['mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach((type) => callback(type, onInput, listenerOpts));
+};
diff --git a/frontend-old/node_modules/web-vitals/src/lib/polyfills/getFirstHiddenTimePolyfill.ts b/frontend-old/node_modules/web-vitals/src/lib/polyfills/getFirstHiddenTimePolyfill.ts
new file mode 100644
index 0000000..9d7f26a
--- /dev/null
+++ b/frontend-old/node_modules/web-vitals/src/lib/polyfills/getFirstHiddenTimePolyfill.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+let firstHiddenTime = document.visibilityState === 'hidden' ? 0 : Infinity;
+
+const onVisibilityChange = (event: Event) => {
+ if (document.visibilityState === 'hidden') {
+ firstHiddenTime = event.timeStamp;
+ removeEventListener('visibilitychange', onVisibilityChange, true);
+ }
+};
+
+// Note: do not add event listeners unconditionally (outside of polyfills).
+addEventListener('visibilitychange', onVisibilityChange, true);
+
+export const getFirstHiddenTime = () => firstHiddenTime;
diff --git a/frontend-old/node_modules/web-vitals/src/lib/polyfills/interactionCountPolyfill.ts b/frontend-old/node_modules/web-vitals/src/lib/polyfills/interactionCountPolyfill.ts
new file mode 100644
index 0000000..e363376
--- /dev/null
+++ b/frontend-old/node_modules/web-vitals/src/lib/polyfills/interactionCountPolyfill.ts
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {observe} from '../observe.js';
+
+declare global {
+ interface Performance {
+ interactionCount: number;
+ }
+}
+
+let interactionCountEstimate = 0;
+let minKnownInteractionId = Infinity;
+let maxKnownInteractionId = 0;
+
+const updateEstimate = (entries: PerformanceEventTiming[]) => {
+ entries.forEach((e) => {
+ if (e.interactionId) {
+ minKnownInteractionId = Math.min(minKnownInteractionId, e.interactionId);
+ maxKnownInteractionId = Math.max(maxKnownInteractionId, e.interactionId);
+
+ interactionCountEstimate = maxKnownInteractionId
+ ? (maxKnownInteractionId - minKnownInteractionId) / 7 + 1
+ : 0;
+ }
+ });
+};
+
+let po: PerformanceObserver | undefined;
+
+/**
+ * Returns the `interactionCount` value using the native API (if available)
+ * or the polyfill estimate in this module.
+ */
+export const getInteractionCount = () => {
+ return po ? interactionCountEstimate : performance.interactionCount || 0;
+};
+
+/**
+ * Feature detects native support or initializes the polyfill if needed.
+ */
+export const initInteractionCountPolyfill = () => {
+ if ('interactionCount' in performance || po) return;
+
+ po = observe('event', updateEstimate, {
+ type: 'event',
+ buffered: true,
+ durationThreshold: 0,
+ } as PerformanceObserverInit);
+};