@codecademy/tracking 1.0.32-alpha.dcd01566d8.0 → 1.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -7
- package/dist/integrations/gtm.d.ts +8 -0
- package/dist/integrations/gtm.js +27 -0
- package/dist/integrations/index.d.ts +3 -11
- package/dist/integrations/index.js +7 -36
- package/dist/integrations/onetrust.d.ts +2 -2
- package/dist/integrations/onetrust.js +2 -2
- package/dist/integrations/types.d.ts +0 -13
- package/package.json +2 -2
- package/dist/integrations/conditionallyLoadAnalytics.d.ts +0 -7
- package/dist/integrations/conditionallyLoadAnalytics.js +0 -11
- package/dist/integrations/fetchDestinationsForWriteKey.d.ts +0 -6
- package/dist/integrations/fetchDestinationsForWriteKey.js +0 -26
- package/dist/integrations/getConsentDecision.d.ts +0 -8
- package/dist/integrations/getConsentDecision.js +0 -27
- package/dist/integrations/mapDestinations.d.ts +0 -12
- package/dist/integrations/mapDestinations.js +0 -39
- package/dist/integrations/runSegmentSnippet.d.ts +0 -7
- package/dist/integrations/runSegmentSnippet.js +0 -69
package/README.md
CHANGED
|
@@ -80,20 +80,15 @@ Integrations are loaded in an intentionally layered manner for CCPA/GDPR complia
|
|
|
80
80
|
|
|
81
81
|
1. Wait 1000ms to allow any other post-hydration logic to run first
|
|
82
82
|
2. Load in OneTrust's banner and wait for its `OptanonWrapper` callback
|
|
83
|
-
3.
|
|
84
|
-
4. Destination integrations for Segment are fetched
|
|
85
|
-
5. Those integrations are compared against the user's consent decisions into a list of allowed destinations
|
|
86
|
-
6. We load only those allowed destinations using Segment's `analytics.load`
|
|
83
|
+
3. Load GTM
|
|
87
84
|
|
|
88
85
|
```ts
|
|
89
86
|
import { initializeTrackingIntegrations } from '@codecademy/tracking';
|
|
90
87
|
|
|
91
88
|
setTimeout(() => {
|
|
92
89
|
initializeTrackingIntegrations({
|
|
93
|
-
|
|
94
|
-
production: true,
|
|
90
|
+
environment: process.env.NODE_ENV,
|
|
95
91
|
scope: window,
|
|
96
|
-
writeKey: 'my-segment-write-key',
|
|
97
92
|
});
|
|
98
93
|
}, 1000);
|
|
99
94
|
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { TrackingWindow } from './types';
|
|
2
|
+
export type GTMSettings = {
|
|
3
|
+
environment: string;
|
|
4
|
+
scope: TrackingWindow;
|
|
5
|
+
optedOutExternalTracking?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare const OPT_OUT_DATALAYER_VAR = "user_opted_out_external_tracking";
|
|
8
|
+
export declare const initializeGTM: ({ scope, environment, optedOutExternalTracking, }: GTMSettings) => void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const OPT_OUT_DATALAYER_VAR = 'user_opted_out_external_tracking';
|
|
2
|
+
export const initializeGTM = _ref => {
|
|
3
|
+
var _scope$dataLayer;
|
|
4
|
+
let scope = _ref.scope,
|
|
5
|
+
environment = _ref.environment,
|
|
6
|
+
optedOutExternalTracking = _ref.optedOutExternalTracking;
|
|
7
|
+
(_scope$dataLayer = scope.dataLayer) !== null && _scope$dataLayer !== void 0 ? _scope$dataLayer : scope.dataLayer = [];
|
|
8
|
+
scope.dataLayer.push({
|
|
9
|
+
'gtm.start': new Date().getTime(),
|
|
10
|
+
event: 'gtm.js'
|
|
11
|
+
});
|
|
12
|
+
if (optedOutExternalTracking) {
|
|
13
|
+
scope.dataLayer.push({
|
|
14
|
+
[OPT_OUT_DATALAYER_VAR]: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
let preview_env = '';
|
|
18
|
+
if (environment === 'development') {
|
|
19
|
+
preview_env = '>m_auth=DoN0WSxjuUkImaph8PYXmA>m_preview=env-233';
|
|
20
|
+
} else if (environment === 'staging') {
|
|
21
|
+
preview_env = '>m_auth=VrQuCDuWXkLlTwNHJYEKTg>m_preview=env-232';
|
|
22
|
+
}
|
|
23
|
+
const gtm = document.createElement('script');
|
|
24
|
+
gtm.async = true;
|
|
25
|
+
gtm.src = "https://www.googletagmanager.com/gtm.js?id=GTM-KTLK85W".concat(preview_env);
|
|
26
|
+
document.getElementsByTagName('head')[0].appendChild(gtm);
|
|
27
|
+
};
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { TrackingWindow } from './types';
|
|
2
2
|
export type TrackingIntegrationsSettings = {
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Current environment.
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Whether this is running in a production environment.
|
|
9
|
-
*/
|
|
10
|
-
production: boolean;
|
|
6
|
+
environment: string;
|
|
11
7
|
/**
|
|
12
8
|
* Global scope (often the window) where globals such as analytics are stored.
|
|
13
9
|
*/
|
|
@@ -16,12 +12,8 @@ export type TrackingIntegrationsSettings = {
|
|
|
16
12
|
* Whether user has opted out or is excluded from external tracking
|
|
17
13
|
*/
|
|
18
14
|
optedOutExternalTracking?: boolean;
|
|
19
|
-
/**
|
|
20
|
-
* Segment write key.
|
|
21
|
-
*/
|
|
22
|
-
writeKey: string;
|
|
23
15
|
};
|
|
24
16
|
/**
|
|
25
17
|
* @see README.md for details and usage.
|
|
26
18
|
*/
|
|
27
|
-
export declare const initializeTrackingIntegrations: ({
|
|
19
|
+
export declare const initializeTrackingIntegrations: ({ environment, scope, optedOutExternalTracking, }: TrackingIntegrationsSettings) => Promise<void>;
|
|
@@ -1,54 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { fetchDestinationsForWriteKey } from './fetchDestinationsForWriteKey';
|
|
3
|
-
import { getConsentDecision } from './getConsentDecision';
|
|
4
|
-
import { mapDestinations } from './mapDestinations';
|
|
1
|
+
import { initializeGTM } from './gtm';
|
|
5
2
|
import { initializeOneTrust } from './onetrust';
|
|
6
|
-
import { runSegmentSnippet } from './runSegmentSnippet';
|
|
7
3
|
/**
|
|
8
4
|
* @see README.md for details and usage.
|
|
9
5
|
*/
|
|
10
6
|
export const initializeTrackingIntegrations = async _ref => {
|
|
11
|
-
let
|
|
12
|
-
production = _ref.production,
|
|
7
|
+
let environment = _ref.environment,
|
|
13
8
|
scope = _ref.scope,
|
|
14
|
-
optedOutExternalTracking = _ref.optedOutExternalTracking
|
|
15
|
-
writeKey = _ref.writeKey;
|
|
9
|
+
optedOutExternalTracking = _ref.optedOutExternalTracking;
|
|
16
10
|
// 1. Wait 1000ms to allow any other post-hydration logic to run first
|
|
17
11
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
18
12
|
|
|
19
13
|
// 2. Load in OneTrust's banner and wait for its `OptanonWrapper` callback
|
|
20
14
|
await initializeOneTrust({
|
|
21
15
|
scope,
|
|
22
|
-
|
|
16
|
+
environment
|
|
23
17
|
});
|
|
24
18
|
|
|
25
|
-
// 3.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// 4. Destination integrations for Segment are fetched
|
|
29
|
-
const destinations = await fetchDestinationsForWriteKey({
|
|
30
|
-
onError,
|
|
31
|
-
writeKey
|
|
32
|
-
});
|
|
33
|
-
if (!destinations) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
const consentDecision = getConsentDecision({
|
|
19
|
+
// 3. Load GTM
|
|
20
|
+
initializeGTM({
|
|
37
21
|
scope,
|
|
22
|
+
environment,
|
|
38
23
|
optedOutExternalTracking
|
|
39
24
|
});
|
|
40
|
-
|
|
41
|
-
// 5. Those integrations are compared against the user's consent decisions into a list of allowed destinations
|
|
42
|
-
const _mapDestinations = mapDestinations({
|
|
43
|
-
consentDecision,
|
|
44
|
-
destinations
|
|
45
|
-
}),
|
|
46
|
-
destinationPreferences = _mapDestinations.destinationPreferences;
|
|
47
|
-
|
|
48
|
-
// 6. We load only those allowed destinations using Segment's `analytics.load`
|
|
49
|
-
conditionallyLoadAnalytics({
|
|
50
|
-
analytics: scope.analytics,
|
|
51
|
-
destinationPreferences,
|
|
52
|
-
writeKey
|
|
53
|
-
});
|
|
54
25
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TrackingWindow } from './types';
|
|
2
2
|
export type OneTrustSettings = {
|
|
3
|
-
|
|
3
|
+
environment: string;
|
|
4
4
|
scope: TrackingWindow;
|
|
5
5
|
};
|
|
6
|
-
export declare const initializeOneTrust: ({
|
|
6
|
+
export declare const initializeOneTrust: ({ environment, scope, }: OneTrustSettings) => Promise<void>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export const initializeOneTrust = async _ref => {
|
|
2
|
-
let
|
|
2
|
+
let environment = _ref.environment,
|
|
3
3
|
scope = _ref.scope;
|
|
4
4
|
const script = document.createElement('script');
|
|
5
5
|
script.setAttribute('async', 'true');
|
|
6
6
|
script.setAttribute('src', 'https://cdn.cookielaw.org/scripttemplates/otSDKStub.js');
|
|
7
7
|
script.setAttribute('type', 'text/javascript');
|
|
8
|
-
script.setAttribute('data-domain-script', "cfa7b129-f37b-4f5a-9991-3f75ba7b85fb".concat(production ? '' : '-test'));
|
|
8
|
+
script.setAttribute('data-domain-script', "cfa7b129-f37b-4f5a-9991-3f75ba7b85fb".concat(environment === 'production' ? '' : '-test'));
|
|
9
9
|
document.body.appendChild(script);
|
|
10
10
|
const style = document.createElement('style');
|
|
11
11
|
style.textContent = rawStyles;
|
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
import { Consent } from './consent';
|
|
2
|
-
export interface SegmentAnalytics {
|
|
3
|
-
initialize?: boolean;
|
|
4
|
-
load(writeKey: string, options: SegmentAnalyticsOptions): void;
|
|
5
|
-
page(): void;
|
|
6
|
-
}
|
|
7
|
-
export interface SegmentDestination {
|
|
8
|
-
category: string;
|
|
9
|
-
id: string;
|
|
10
|
-
}
|
|
11
|
-
export interface SegmentAnalyticsOptions {
|
|
12
|
-
integrations: Record<string, boolean>;
|
|
13
|
-
}
|
|
14
2
|
export interface TrackingWindow {
|
|
15
|
-
analytics?: SegmentAnalytics;
|
|
16
3
|
dataLayer?: unknown[];
|
|
17
4
|
OnetrustActiveGroups?: Consent[] | string;
|
|
18
5
|
OptanonWrapper?: () => void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codecademy/tracking",
|
|
3
3
|
"description": "Tracking library for Codecademy",
|
|
4
|
-
"version": "1.0.32
|
|
4
|
+
"version": "1.0.32",
|
|
5
5
|
"author": "Codecademy Engineering <dev@codecademy.com>",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist/**"
|
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
15
|
"repository": "git@github.com:codecademy-engineering/mono.git",
|
|
16
|
-
"gitHead": "
|
|
16
|
+
"gitHead": "389b25c0b3df30f006b82c5151799d1747ea6778"
|
|
17
17
|
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { SegmentAnalytics } from './types';
|
|
2
|
-
export type AnalyticsLoadOptions = {
|
|
3
|
-
analytics: SegmentAnalytics;
|
|
4
|
-
destinationPreferences: Record<string, boolean>;
|
|
5
|
-
writeKey: string;
|
|
6
|
-
};
|
|
7
|
-
export declare const conditionallyLoadAnalytics: ({ analytics, destinationPreferences, writeKey, }: AnalyticsLoadOptions) => void;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export const conditionallyLoadAnalytics = _ref => {
|
|
2
|
-
let analytics = _ref.analytics,
|
|
3
|
-
destinationPreferences = _ref.destinationPreferences,
|
|
4
|
-
writeKey = _ref.writeKey;
|
|
5
|
-
if (analytics.initialize) {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
analytics.load(writeKey, {
|
|
9
|
-
integrations: destinationPreferences
|
|
10
|
-
});
|
|
11
|
-
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { SegmentDestination } from './types';
|
|
2
|
-
export type FetchDestinationsSettings = {
|
|
3
|
-
onError: (message: string) => void;
|
|
4
|
-
writeKey: string;
|
|
5
|
-
};
|
|
6
|
-
export declare const fetchDestinationsForWriteKey: ({ writeKey, onError, }: FetchDestinationsSettings) => Promise<SegmentDestination[] | undefined>;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const knownFetchFailures = ['Failed to fetch', 'Load failed', 'NetworkError when attempting to fetch resource', 'Resource blocked by content blocker'];
|
|
2
|
-
export const fetchDestinationsForWriteKey = async _ref => {
|
|
3
|
-
let writeKey = _ref.writeKey,
|
|
4
|
-
onError = _ref.onError;
|
|
5
|
-
const filteredOnError = error => {
|
|
6
|
-
if (!knownFetchFailures.some(failure => error.includes(failure))) {
|
|
7
|
-
onError(error);
|
|
8
|
-
}
|
|
9
|
-
};
|
|
10
|
-
try {
|
|
11
|
-
const response = await fetch("https://cdn.segment.com/v1/projects/".concat(writeKey, "/integrations"));
|
|
12
|
-
if (!response.ok) {
|
|
13
|
-
filteredOnError("Failed to fetch integrations for write key ".concat(writeKey, ": HTTP ").concat(response.status, " ").concat(response.statusText));
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
16
|
-
const destinations = await response.json();
|
|
17
|
-
for (const destination of destinations) {
|
|
18
|
-
destination.id = destination.creationName;
|
|
19
|
-
delete destination.creationName;
|
|
20
|
-
}
|
|
21
|
-
return destinations;
|
|
22
|
-
} catch (error) {
|
|
23
|
-
filteredOnError("Unknown error fetching Segment destinations for write key ".concat(writeKey, ": ").concat(error));
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
26
|
-
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Consent } from './consent';
|
|
2
|
-
import { TrackingWindow } from './types';
|
|
3
|
-
export interface ConsentDecisionOptions {
|
|
4
|
-
scope: TrackingWindow;
|
|
5
|
-
optedOutExternalTracking?: boolean;
|
|
6
|
-
}
|
|
7
|
-
export declare const OPT_OUT_DATALAYER_VAR = "user_opted_out_external_tracking";
|
|
8
|
-
export declare const getConsentDecision: ({ scope, optedOutExternalTracking, }: ConsentDecisionOptions) => Consent[];
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Consent } from './consent';
|
|
2
|
-
export const OPT_OUT_DATALAYER_VAR = 'user_opted_out_external_tracking';
|
|
3
|
-
export const getConsentDecision = _ref => {
|
|
4
|
-
let scope = _ref.scope,
|
|
5
|
-
optedOutExternalTracking = _ref.optedOutExternalTracking;
|
|
6
|
-
let consentDecision = [];
|
|
7
|
-
if (typeof scope.OnetrustActiveGroups === 'string') {
|
|
8
|
-
consentDecision = scope.OnetrustActiveGroups.split(',').filter(Boolean);
|
|
9
|
-
} else if (scope.OnetrustActiveGroups) {
|
|
10
|
-
consentDecision = scope.OnetrustActiveGroups;
|
|
11
|
-
}
|
|
12
|
-
if (optedOutExternalTracking) {
|
|
13
|
-
var _scope$dataLayer;
|
|
14
|
-
/**
|
|
15
|
-
* If user has already opted out of everything but the essentials
|
|
16
|
-
* don't force them to consent to Functional & Performance trackers
|
|
17
|
-
*/
|
|
18
|
-
if (consentDecision.length > 2) {
|
|
19
|
-
consentDecision = [Consent.StrictlyNecessary, Consent.Functional, Consent.Performance];
|
|
20
|
-
}
|
|
21
|
-
(_scope$dataLayer = scope.dataLayer) !== null && _scope$dataLayer !== void 0 ? _scope$dataLayer : scope.dataLayer = [];
|
|
22
|
-
scope.dataLayer.push({
|
|
23
|
-
[OPT_OUT_DATALAYER_VAR]: true
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
return consentDecision;
|
|
27
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Consent } from './consent';
|
|
2
|
-
import { SegmentDestination } from './types';
|
|
3
|
-
export type DestinationMapOptions = {
|
|
4
|
-
consentDecision?: Consent[];
|
|
5
|
-
destinations: SegmentDestination[];
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* @see https://www.notion.so/codecademy/GDPR-Compliance-141ebcc7ffa542daa0da56e35f482b41
|
|
9
|
-
*/
|
|
10
|
-
export declare const mapDestinations: ({ consentDecision, destinations, }: DestinationMapOptions) => {
|
|
11
|
-
destinationPreferences: Record<string, boolean>;
|
|
12
|
-
};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { Consent } from './consent';
|
|
2
|
-
// The Functional category may need to be added here in the future.
|
|
3
|
-
const targetingCategories = ['Advertising', 'Attribution', 'Email Marketing'];
|
|
4
|
-
const performanceCategories = ['Analytics', 'Customer Success', 'Surveys', 'Heatmaps & Recording'];
|
|
5
|
-
const functionalCategories = ['SMS & Push Notifications'];
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @see https://www.notion.so/codecademy/GDPR-Compliance-141ebcc7ffa542daa0da56e35f482b41
|
|
9
|
-
*/
|
|
10
|
-
export const mapDestinations = _ref => {
|
|
11
|
-
let _ref$consentDecision = _ref.consentDecision,
|
|
12
|
-
consentDecision = _ref$consentDecision === void 0 ? [Consent.StrictlyNecessary] : _ref$consentDecision,
|
|
13
|
-
destinations = _ref.destinations;
|
|
14
|
-
const destinationPreferences = Object.assign({
|
|
15
|
-
'Segment.io': consentDecision.includes(Consent.Functional)
|
|
16
|
-
}, ...destinations.map(dest => {
|
|
17
|
-
if (targetingCategories.includes(dest.category)) {
|
|
18
|
-
return {
|
|
19
|
-
[dest.id]: consentDecision.includes(Consent.Targeting)
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
if (performanceCategories.includes(dest.category)) {
|
|
23
|
-
return {
|
|
24
|
-
[dest.id]: consentDecision.includes(Consent.Performance)
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
if (functionalCategories.includes(dest.category)) {
|
|
28
|
-
return {
|
|
29
|
-
[dest.id]: consentDecision.includes(Consent.Functional)
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
[dest.id]: true
|
|
34
|
-
};
|
|
35
|
-
}));
|
|
36
|
-
return {
|
|
37
|
-
destinationPreferences
|
|
38
|
-
};
|
|
39
|
-
};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This code is copypasta from the Segment documentation.
|
|
3
|
-
* It creates the global analytics object and loads the Segment Analytics API that uses it.
|
|
4
|
-
*
|
|
5
|
-
* @see https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/quickstart/#step-2-copy-the-segment-snippet
|
|
6
|
-
*/
|
|
7
|
-
export declare const runSegmentSnippet: () => void;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* This code is copypasta from the Segment documentation.
|
|
6
|
-
* It creates the global analytics object and loads the Segment Analytics API that uses it.
|
|
7
|
-
*
|
|
8
|
-
* @see https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/quickstart/#step-2-copy-the-segment-snippet
|
|
9
|
-
*/
|
|
10
|
-
export const runSegmentSnippet = () => {
|
|
11
|
-
var _window;
|
|
12
|
-
// Create a queue, but don't obliterate an existing one!
|
|
13
|
-
(_window = window).analytics || (_window.analytics = []);
|
|
14
|
-
const _window2 = window,
|
|
15
|
-
analytics = _window2.analytics;
|
|
16
|
-
|
|
17
|
-
// If the real analytics.js is already on the page return.
|
|
18
|
-
if (analytics.initialize) return;
|
|
19
|
-
|
|
20
|
-
// If the snippet was invoked already show an error.
|
|
21
|
-
if (analytics.invoked) {
|
|
22
|
-
console.error('Segment snippet included twice.');
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Invoked flag, to make sure the snippet
|
|
27
|
-
// is never invoked twice.
|
|
28
|
-
analytics.invoked = true;
|
|
29
|
-
|
|
30
|
-
// A list of the methods in Analytics.js to stub.
|
|
31
|
-
analytics.methods = ['trackSubmit', 'trackClick', 'trackLink', 'trackForm', 'pageview', 'identify', 'reset', 'group', 'track', 'ready', 'alias', 'debug', 'page', 'once', 'off', 'on', 'addSourceMiddleware', 'addIntegrationMiddleware', 'setAnonymousId', 'addDestinationMiddleware'];
|
|
32
|
-
|
|
33
|
-
// Define a factory to create stubs. These are placeholders
|
|
34
|
-
// for methods in Analytics.js so that you never have to wait
|
|
35
|
-
// for it to load to actually record data. The `method` is
|
|
36
|
-
// stored as the first argument, so we can replay the data.
|
|
37
|
-
analytics.factory = function (method) {
|
|
38
|
-
return function () {
|
|
39
|
-
const args = Array.prototype.slice.call(arguments);
|
|
40
|
-
args.unshift(method);
|
|
41
|
-
analytics.push(args);
|
|
42
|
-
return analytics;
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// For each of our methods, generate a queueing stub.
|
|
47
|
-
for (let i = 0; i < analytics.methods.length; i += 1) {
|
|
48
|
-
const key = analytics.methods[i];
|
|
49
|
-
analytics[key] = analytics.factory(key);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Define a method to load Analytics.js from our CDN,
|
|
53
|
-
// and that will be sure to only ever load it once.
|
|
54
|
-
analytics.load = function (key, options) {
|
|
55
|
-
// Create an async script element based on your key.
|
|
56
|
-
const script = document.createElement('script');
|
|
57
|
-
script.type = 'text/javascript';
|
|
58
|
-
script.async = true;
|
|
59
|
-
script.src = 'https://cdn.segment.com/analytics.js/v1/' + key + '/analytics.min.js';
|
|
60
|
-
|
|
61
|
-
// Insert our script next to the first script element.
|
|
62
|
-
const first = document.getElementsByTagName('script')[0];
|
|
63
|
-
first.parentNode.insertBefore(script, first);
|
|
64
|
-
analytics._loadOptions = options;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// Add a version to keep track of what's in the wild.
|
|
68
|
-
analytics.SNIPPET_VERSION = '4.1.0';
|
|
69
|
-
};
|