@24i/bigscreen-sdk 1.0.8-alpha.2171 → 1.0.8-alpha.2178
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/package.json +1 -1
- package/packages/analytics/src/clients/GoogleAnalytics/GoogleAnalytics.ts +34 -135
- package/packages/analytics/src/clients/GoogleAnalytics/constants.ts +9 -22
- package/packages/analytics/src/clients/GoogleAnalytics/index.ts +0 -1
- package/packages/analytics/src/clients/GoogleAnalytics/interface.ts +0 -47
- package/packages/analytics/src/clients/GoogleAnalytics/mapPayload.ts +65 -18
- package/packages/keyboard/src/Backdrop/Backdrop.scss +0 -2
- package/packages/utils/src/textUtils.ts +47 -13
- package/packages/analytics/src/clients/GoogleAnalytics/generateSessionId.ts +0 -8
- package/packages/analytics/src/clients/GoogleAnalytics/getUrl.ts +0 -17
- package/packages/analytics/src/clients/GoogleAnalytics/prepareBody.ts +0 -14
package/package.json
CHANGED
|
@@ -1,143 +1,59 @@
|
|
|
1
1
|
import { Storage } from '@24i/bigscreen-sdk/storage';
|
|
2
|
-
import { device } from '@24i/bigscreen-sdk/device';
|
|
3
2
|
import { debounce } from '@24i/bigscreen-sdk/utils/debounce';
|
|
4
|
-
import { createTimeout } from '@24i/bigscreen-sdk/utils/timers';
|
|
5
|
-
import { MINUTE_IN_MS } from '@24i/bigscreen-sdk/utils/timeConstants';
|
|
6
3
|
import { generateUuid } from '@24i/bigscreen-sdk/utils/generateUuid';
|
|
7
|
-
import type { AnalyticsClient, AnalyticsEventsMap
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { generateSessionId } from './generateSessionId';
|
|
12
|
-
import { GOOGLE_ANALYTICS_TRIGGERS, TRIGGER_NAME_TO_GA_EVENTS } from './constants';
|
|
13
|
-
import {
|
|
14
|
-
GAEventsNames,
|
|
15
|
-
QueuedEvent,
|
|
16
|
-
ConstantUrlParams,
|
|
17
|
-
SessionInfo,
|
|
18
|
-
EventUrlParams,
|
|
19
|
-
} from './interface';
|
|
4
|
+
import type { AnalyticsClient, AnalyticsEventsMap } from '../../interface';
|
|
5
|
+
import { mapPayload } from './mapPayload';
|
|
6
|
+
import { TRIGGER_NAME_TO_GA_EVENTS } from './constants';
|
|
7
|
+
import { GAEventsNames } from './interface';
|
|
20
8
|
|
|
21
9
|
const SEND_DEBOUNCE_MS = 5000;
|
|
22
10
|
const MAX_QUEUE_LENGTH = 10;
|
|
23
|
-
// eslint-disable-next-line no-magic-numbers
|
|
24
|
-
const SESSION_TIMEOUT = 30 * MINUTE_IN_MS;
|
|
25
11
|
|
|
26
12
|
const GA_CLIENT_ID = 'GA_client_id';
|
|
27
|
-
|
|
28
|
-
const
|
|
13
|
+
|
|
14
|
+
const getGAUrl = (id: string, secret: string) => (
|
|
15
|
+
// You can use the following URL for debugging if the events are not getting collected.
|
|
16
|
+
// `https://www.google-analytics.com/debug/mp/collect?measurement_id=${id}&api_secret=${secret}`
|
|
17
|
+
`https://www.google-analytics.com/mp/collect?measurement_id=${id}&api_secret=${secret}`
|
|
18
|
+
);
|
|
29
19
|
|
|
30
20
|
export class GoogleAnalytics implements AnalyticsClient {
|
|
31
21
|
name = 'GoogleAnalytics';
|
|
32
22
|
|
|
33
|
-
|
|
34
|
-
cid: '',
|
|
35
|
-
sid: 0,
|
|
36
|
-
uamb: 0,
|
|
37
|
-
uap: '',
|
|
38
|
-
uapv: '',
|
|
39
|
-
uam: '',
|
|
40
|
-
seg: 0,
|
|
41
|
-
sct: 0,
|
|
42
|
-
_p: generateUuid(),
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
a24iOperational = false;
|
|
46
|
-
|
|
47
|
-
lastOnEvent: number | undefined = undefined;
|
|
48
|
-
|
|
49
|
-
eventsQueue: QueuedEvent[] = [];
|
|
23
|
+
clientId = '';
|
|
50
24
|
|
|
51
|
-
|
|
25
|
+
sessionId = generateUuid();
|
|
52
26
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
sessionStartSent = false;
|
|
56
|
-
|
|
57
|
-
firstEvent = true;
|
|
27
|
+
a24iOperational = false;
|
|
58
28
|
|
|
59
|
-
|
|
29
|
+
eventsQueue: { name: GAEventsNames, params: Record<string, any> }[] = [];
|
|
60
30
|
|
|
61
|
-
constructor(private id: string) {
|
|
31
|
+
constructor(private id: string, private secret: string) {
|
|
62
32
|
this.init();
|
|
63
33
|
}
|
|
64
34
|
|
|
65
35
|
async init() {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
await Storage.setItem(GA_CLIENT_ID, params.cid);
|
|
36
|
+
this.clientId = await Storage.getItem(GA_CLIENT_ID) ?? '';
|
|
37
|
+
if (!this.clientId) {
|
|
38
|
+
this.clientId = generateUuid();
|
|
39
|
+
await Storage.setItem(GA_CLIENT_ID, this.clientId);
|
|
71
40
|
}
|
|
72
|
-
this.firstVisit = !(await Storage.getItem(GA_FIRST_VISIT)) ?? true;
|
|
73
|
-
if (this.firstVisit) {
|
|
74
|
-
await Storage.setItem(GA_FIRST_VISIT, '0');
|
|
75
|
-
}
|
|
76
|
-
await this.startSession();
|
|
77
|
-
params.uap = await device.getPlatformName();
|
|
78
|
-
params.uapv = await device.getPlatformVersion();
|
|
79
|
-
params.uam = await device.getModelName();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async startSession() {
|
|
83
|
-
const sessionNumber = parseInt(await Storage.getItem(GA_SESSION_COUNTER) ?? '0') + 1;
|
|
84
|
-
this.constantUrlParams.sct = sessionNumber;
|
|
85
|
-
this.constantUrlParams.sid = generateSessionId();
|
|
86
|
-
await Storage.setItem(GA_SESSION_COUNTER, sessionNumber.toString());
|
|
87
|
-
this.isSessionActive = true;
|
|
88
|
-
this.setSessionTimeout();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
setSessionTimeout() {
|
|
92
|
-
this.sessionTimeout.set(() => {
|
|
93
|
-
this.isSessionActive = false;
|
|
94
|
-
this.sessionStartSent = false;
|
|
95
|
-
this.constantUrlParams.seg = 0;
|
|
96
|
-
}, SESSION_TIMEOUT);
|
|
97
41
|
}
|
|
98
42
|
|
|
99
|
-
|
|
100
|
-
async onEvent<Event extends keyof AnalyticsEventsMap>(
|
|
43
|
+
onEvent<Event extends keyof AnalyticsEventsMap>(
|
|
101
44
|
triggerName: Event,
|
|
102
|
-
payload: Parameters<AnalyticsEventsMap[Event]
|
|
103
|
-
) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} else {
|
|
107
|
-
await this.startSession();
|
|
108
|
-
}
|
|
109
|
-
const events = TRIGGER_NAME_TO_GA_EVENTS[
|
|
110
|
-
triggerName as typeof GOOGLE_ANALYTICS_TRIGGERS[number]
|
|
111
|
-
];
|
|
112
|
-
const now = Date.now();
|
|
113
|
-
const engagementTime = this.lastOnEvent ? now - this.lastOnEvent : undefined;
|
|
114
|
-
this.lastOnEvent = now;
|
|
115
|
-
await Promise.all(events.map(async (event, index) => {
|
|
45
|
+
payload: Parameters<AnalyticsEventsMap[Event]>,
|
|
46
|
+
): void {
|
|
47
|
+
const events = TRIGGER_NAME_TO_GA_EVENTS[triggerName];
|
|
48
|
+
events.forEach(async (event) => {
|
|
116
49
|
if (event === 'a24i_operational' && !this.shouldContinueWithA24iOperational()) return;
|
|
117
|
-
const gtagPayload = mapPayload(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
),
|
|
121
|
-
);
|
|
122
|
-
const urlParams = mapEventParamsToUrlParams(payload as SceneInfo);
|
|
123
|
-
this.send(event, gtagPayload, urlParams);
|
|
124
|
-
}));
|
|
50
|
+
const gtagPayload = mapPayload(event, triggerName, this.sessionId, payload);
|
|
51
|
+
this.send(event, gtagPayload);
|
|
52
|
+
});
|
|
125
53
|
}
|
|
126
54
|
|
|
127
|
-
private send(name: GAEventsNames, params: Record<string, any
|
|
128
|
-
|
|
129
|
-
this.eventsQueue[this.eventsQueue.length - 1]?.urlParams ?? {},
|
|
130
|
-
);
|
|
131
|
-
if (lastUrlParams !== '{}' && lastUrlParams !== JSON.stringify(urlParams)) {
|
|
132
|
-
this.sendQueue();
|
|
133
|
-
}
|
|
134
|
-
if (this.constantUrlParams.seg === 0) {
|
|
135
|
-
this.eventsQueue.push({ name, params, urlParams });
|
|
136
|
-
this.sendQueue();
|
|
137
|
-
this.constantUrlParams.seg = 1;
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
this.eventsQueue.push({ name, params, urlParams });
|
|
55
|
+
private send(name: GAEventsNames, params: Record<string, any>) {
|
|
56
|
+
this.eventsQueue.push({ name, params });
|
|
141
57
|
if (this.eventsQueue.length >= MAX_QUEUE_LENGTH) {
|
|
142
58
|
this.sendQueue();
|
|
143
59
|
} else {
|
|
@@ -146,15 +62,13 @@ export class GoogleAnalytics implements AnalyticsClient {
|
|
|
146
62
|
}
|
|
147
63
|
|
|
148
64
|
private sendQueue = () => {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
65
|
+
const body = JSON.stringify({
|
|
66
|
+
client_id: this.clientId,
|
|
67
|
+
events: this.eventsQueue,
|
|
68
|
+
});
|
|
152
69
|
this.eventsQueue.length = 0;
|
|
153
70
|
try {
|
|
154
|
-
fetch(
|
|
155
|
-
...this.constantUrlParams,
|
|
156
|
-
...urlParams,
|
|
157
|
-
}), { method: 'POST', body });
|
|
71
|
+
fetch(getGAUrl(this.id, this.secret), { method: 'POST', body });
|
|
158
72
|
} catch (e) {
|
|
159
73
|
// Effort to send was made.
|
|
160
74
|
}
|
|
@@ -167,19 +81,4 @@ export class GoogleAnalytics implements AnalyticsClient {
|
|
|
167
81
|
if (!a24iOperational) this.a24iOperational = true;
|
|
168
82
|
return !a24iOperational;
|
|
169
83
|
}
|
|
170
|
-
|
|
171
|
-
private async getSessionInfo(engagementTime: number | undefined): Promise<SessionInfo> {
|
|
172
|
-
const sessionStarted = !this.sessionStartSent;
|
|
173
|
-
this.sessionStartSent = true;
|
|
174
|
-
const { firstEvent } = this;
|
|
175
|
-
this.firstEvent = false;
|
|
176
|
-
const { firstVisit } = this;
|
|
177
|
-
this.firstVisit = false;
|
|
178
|
-
return {
|
|
179
|
-
sessionStarted,
|
|
180
|
-
engagementTime,
|
|
181
|
-
firstEvent,
|
|
182
|
-
firstVisit: firstVisit as boolean,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
84
|
}
|
|
@@ -1,30 +1,17 @@
|
|
|
1
|
+
import { AnalyticsEventsNames } from '../../interface';
|
|
1
2
|
import { GAEventsNames } from './interface';
|
|
2
3
|
|
|
3
|
-
export const
|
|
4
|
-
'app_close',
|
|
5
|
-
'playback_10',
|
|
6
|
-
'playback_25',
|
|
7
|
-
'playback_50',
|
|
8
|
-
'playback_75',
|
|
9
|
-
'playback_90',
|
|
10
|
-
'playback_pause',
|
|
11
|
-
'playback_start',
|
|
12
|
-
'playback_stop',
|
|
13
|
-
'player_close',
|
|
14
|
-
'player_open',
|
|
15
|
-
'screen_view',
|
|
16
|
-
'scroll_25',
|
|
17
|
-
'scroll_50',
|
|
18
|
-
'scroll_75',
|
|
19
|
-
'scroll_90',
|
|
20
|
-
] as const;
|
|
21
|
-
|
|
22
|
-
export const TRIGGER_NAME_TO_GA_EVENTS: Record<
|
|
23
|
-
typeof GOOGLE_ANALYTICS_TRIGGERS[number], GAEventsNames[]
|
|
24
|
-
> = {
|
|
4
|
+
export const TRIGGER_NAME_TO_GA_EVENTS: Record<AnalyticsEventsNames, GAEventsNames[]> = {
|
|
25
5
|
// App
|
|
26
6
|
'app_close': ['app_close', 'video_progress'],
|
|
27
7
|
|
|
8
|
+
// Data loading / Player buffering
|
|
9
|
+
'buffering_start': ['buffering_start'],
|
|
10
|
+
'buffering_stop': ['buffering_start'],
|
|
11
|
+
|
|
12
|
+
// Heartbeat
|
|
13
|
+
'heartbeat_5': ['a24i_content'],
|
|
14
|
+
|
|
28
15
|
// Playback
|
|
29
16
|
'playback_10': ['video_progress'],
|
|
30
17
|
'playback_25': ['video_progress'],
|
|
@@ -8,50 +8,3 @@ export type GAEventsNames = (
|
|
|
8
8
|
| 'video_progress'
|
|
9
9
|
| 'video_start'
|
|
10
10
|
);
|
|
11
|
-
|
|
12
|
-
export type QueuedEvent = {
|
|
13
|
-
name: string,
|
|
14
|
-
params: Record<string, any>,
|
|
15
|
-
urlParams: EventUrlParams,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type SessionInfo = {
|
|
19
|
-
engagementTime: number | undefined,
|
|
20
|
-
sessionStarted: boolean,
|
|
21
|
-
firstEvent: boolean,
|
|
22
|
-
firstVisit: boolean,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export type ConstantUrlParams = {
|
|
26
|
-
/** Client ID (UUIv4 string) */
|
|
27
|
-
cid: string,
|
|
28
|
-
/** Session ID (random number) */
|
|
29
|
-
sid: number,
|
|
30
|
-
/** 1 for mobile devices, 0 otherwise */
|
|
31
|
-
uamb: number,
|
|
32
|
-
/** Platform name */
|
|
33
|
-
uap: string,
|
|
34
|
-
/** Platform version */
|
|
35
|
-
uapv: string,
|
|
36
|
-
/** Device model name */
|
|
37
|
-
uam: string,
|
|
38
|
-
/** Session engagement 0 for first event of session, 1 otherwise */
|
|
39
|
-
seg: number,
|
|
40
|
-
/** Session count */
|
|
41
|
-
sct: number,
|
|
42
|
-
/** Random page hash */
|
|
43
|
-
_p: string,
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export type EventUrlParams = {
|
|
47
|
-
/** Language iso639-1 */
|
|
48
|
-
ul: string,
|
|
49
|
-
/** Device resolution as WIDTHxHEIGHT */
|
|
50
|
-
sr: string,
|
|
51
|
-
/** Page hash route */
|
|
52
|
-
dl: string,
|
|
53
|
-
/** Page title */
|
|
54
|
-
dt: string,
|
|
55
|
-
/** Page referrer hash route */
|
|
56
|
-
dr: string,
|
|
57
|
-
};
|
|
@@ -1,29 +1,76 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import type {
|
|
2
|
+
AnalyticsEventsNames,
|
|
3
|
+
GeneralAppData,
|
|
4
|
+
SceneInfo,
|
|
5
|
+
AppInfo,
|
|
6
|
+
UserInfo,
|
|
7
|
+
} from '../../interface';
|
|
8
|
+
import type { GAEventsNames } from './interface';
|
|
9
|
+
|
|
10
|
+
const MILI_TO_MICRO = 10;
|
|
11
|
+
|
|
12
|
+
const camelToSnake = (name: string) => (
|
|
13
|
+
name.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`)
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const addKeyIfExists = <T extends string>(
|
|
17
|
+
keyName: T,
|
|
18
|
+
payload: { [key in typeof keyName]?: any },
|
|
19
|
+
gtagPayload: Record<string, any>,
|
|
20
|
+
) => {
|
|
21
|
+
if (payload[keyName]) gtagPayload[camelToSnake(keyName)] = payload[keyName];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const mapGeneralData = (payload: Partial<GeneralAppData>) => {
|
|
25
|
+
const gtagPayloadGeneralData: Partial<GeneralAppData> = {};
|
|
26
|
+
addKeyIfExists('language', payload, gtagPayloadGeneralData);
|
|
27
|
+
addKeyIfExists('resolution', payload, gtagPayloadGeneralData);
|
|
28
|
+
return gtagPayloadGeneralData;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const mapSceneInfo = (payload: Partial<SceneInfo>) => {
|
|
32
|
+
const gtagPayloadSceneInfo: Partial<SceneInfo> = {};
|
|
33
|
+
addKeyIfExists('sceneId', payload, gtagPayloadSceneInfo);
|
|
34
|
+
addKeyIfExists('sceneType', payload, gtagPayloadSceneInfo);
|
|
35
|
+
addKeyIfExists('sceneName', payload, gtagPayloadSceneInfo);
|
|
36
|
+
return gtagPayloadSceneInfo;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const mapAppData = (payload: Partial<AppInfo>) => {
|
|
40
|
+
const gtagPayloadAppInfo: Partial<AppInfo> = {};
|
|
41
|
+
addKeyIfExists('applicationId', payload, gtagPayloadAppInfo);
|
|
42
|
+
addKeyIfExists('serviceId', payload, gtagPayloadAppInfo);
|
|
43
|
+
return gtagPayloadAppInfo;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const mapUserData = (payload: Partial<UserInfo>) => {
|
|
47
|
+
const gtagPayloadUserInfo: Partial<UserInfo> = {};
|
|
48
|
+
addKeyIfExists('userId', payload, gtagPayloadUserInfo);
|
|
49
|
+
return gtagPayloadUserInfo;
|
|
11
50
|
};
|
|
12
51
|
|
|
13
52
|
export const mapPayload = (
|
|
14
53
|
eventName: GAEventsNames,
|
|
15
54
|
triggerName: AnalyticsEventsNames,
|
|
16
|
-
|
|
55
|
+
sessionId: string,
|
|
56
|
+
payload: Record<string, any>,
|
|
17
57
|
) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
58
|
+
let gtagPayload: Record<string, any> = {
|
|
59
|
+
session_id: sessionId,
|
|
60
|
+
event_trigger: triggerName,
|
|
61
|
+
timestamp_micros: Date.now() * MILI_TO_MICRO,
|
|
62
|
+
...mapGeneralData(payload),
|
|
63
|
+
...mapAppData(payload),
|
|
64
|
+
...mapUserData(payload),
|
|
24
65
|
};
|
|
66
|
+
if (eventName === 'page_view' || eventName === 'buffering_start') {
|
|
67
|
+
gtagPayload = {
|
|
68
|
+
...gtagPayload,
|
|
69
|
+
...mapSceneInfo(payload),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
25
72
|
if (eventName === 'scroll') {
|
|
26
|
-
gtagPayload
|
|
73
|
+
gtagPayload.percent_scroll = parseInt(triggerName.split('_')[1], 10);
|
|
27
74
|
}
|
|
28
75
|
return gtagPayload;
|
|
29
76
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
.keyboard-backdrop {
|
|
2
2
|
position: fixed;
|
|
3
3
|
left: 0;
|
|
4
|
-
right: 0;
|
|
5
4
|
top: 0;
|
|
6
5
|
width: $backdrop-width;
|
|
7
6
|
height: $backdrop-height;
|
|
@@ -10,7 +9,6 @@
|
|
|
10
9
|
.keyboard-backdrop-disabled {
|
|
11
10
|
position: absolute;
|
|
12
11
|
left: 0;
|
|
13
|
-
right: 0;
|
|
14
12
|
top: 0;
|
|
15
13
|
width: 100%;
|
|
16
14
|
height: 100%;
|
|
@@ -1,25 +1,59 @@
|
|
|
1
1
|
const ELLIPSIS = '...';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
const trimWithEllipsis = (
|
|
4
|
+
row: HTMLSpanElement | HTMLDivElement,
|
|
5
|
+
requestedNumberOfLines: number,
|
|
6
|
+
ellipsis = ELLIPSIS,
|
|
7
|
+
ending = false
|
|
8
|
+
) : void => {
|
|
9
|
+
if (!row.textContent) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
row.style.visibility = 'hidden';
|
|
13
|
+
const lineHeightString = window.getComputedStyle(row, null).getPropertyValue('line-height');
|
|
14
|
+
const lineHeight = parseInt(lineHeightString, 10);
|
|
15
|
+
let numberOfLines = row.offsetHeight / lineHeight;
|
|
16
|
+
if (numberOfLines > requestedNumberOfLines) {
|
|
17
|
+
const originalTextContent = row.textContent!;
|
|
18
|
+
row.textContent = ellipsis;
|
|
19
|
+
let stringBuffer = '';
|
|
20
|
+
if (ending) {
|
|
21
|
+
let currentPosition = 0;
|
|
22
|
+
do {
|
|
23
|
+
stringBuffer += originalTextContent.charAt(currentPosition);
|
|
24
|
+
row.textContent = stringBuffer + ellipsis;
|
|
25
|
+
currentPosition += 1;
|
|
26
|
+
numberOfLines = row.offsetHeight / lineHeight;
|
|
27
|
+
} while (numberOfLines <= requestedNumberOfLines
|
|
28
|
+
&& currentPosition < originalTextContent.length);
|
|
29
|
+
row.textContent = stringBuffer.substring(0, stringBuffer.length - 1) + ellipsis;
|
|
30
|
+
} else {
|
|
13
31
|
let currentPosition = originalTextContent.length - 1;
|
|
14
32
|
do {
|
|
15
33
|
const charToAdd = originalTextContent.at(currentPosition);
|
|
16
34
|
stringBuffer = `${charToAdd}${stringBuffer}`;
|
|
17
|
-
row.textContent = `${
|
|
35
|
+
row.textContent = `${ellipsis}${stringBuffer}`;
|
|
18
36
|
currentPosition -= 1;
|
|
19
37
|
numberOfLines = Math.floor(row.offsetHeight / lineHeight);
|
|
20
38
|
} while (numberOfLines <= requestedNumberOfLines && currentPosition >= 0);
|
|
21
|
-
row.textContent = `${
|
|
39
|
+
row.textContent = `${ellipsis}${stringBuffer.substring(1)}`;
|
|
22
40
|
}
|
|
23
|
-
row.style.visibility = '';
|
|
24
41
|
}
|
|
42
|
+
row.style.visibility = '';
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const trimWithLeadingEllipsis = (
|
|
46
|
+
row: HTMLSpanElement | HTMLDivElement,
|
|
47
|
+
requestedNumberOfLines: number,
|
|
48
|
+
ellipsis = ELLIPSIS,
|
|
49
|
+
) => {
|
|
50
|
+
return trimWithEllipsis(row, requestedNumberOfLines, ellipsis, false);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const trimWithEndingEllipsis = (
|
|
54
|
+
row: HTMLSpanElement | HTMLDivElement,
|
|
55
|
+
requestedNumberOfLines: number,
|
|
56
|
+
ellipsis = ELLIPSIS,
|
|
57
|
+
) => {
|
|
58
|
+
return trimWithEllipsis(row, requestedNumberOfLines, ellipsis, true);
|
|
25
59
|
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ConstantUrlParams, EventUrlParams } from './interface';
|
|
2
|
-
|
|
3
|
-
const TRANSPORT_URL = 'https://www.google-analytics.com/g/collect?';
|
|
4
|
-
// You can use the following URL for debugging if the events are not getting collected.
|
|
5
|
-
// const TRANSPORT_URL = 'https://www.google-analytics.com/debug/g/collec?';
|
|
6
|
-
|
|
7
|
-
export const getUrl = (id: string, params: ConstantUrlParams & EventUrlParams) => {
|
|
8
|
-
const urlParts = [
|
|
9
|
-
TRANSPORT_URL,
|
|
10
|
-
`v=2&tid=${id}`,
|
|
11
|
-
];
|
|
12
|
-
(Object.keys(params) as (keyof (ConstantUrlParams & EventUrlParams))[])
|
|
13
|
-
.forEach((paramName: keyof (ConstantUrlParams & EventUrlParams)) => {
|
|
14
|
-
urlParts.push(`&${paramName}=${encodeURIComponent(params[paramName])}`);
|
|
15
|
-
});
|
|
16
|
-
return urlParts.join('');
|
|
17
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { QueuedEvent } from './interface';
|
|
2
|
-
|
|
3
|
-
export const prepareBody = (events: QueuedEvent[]) => {
|
|
4
|
-
const eventLines = events.map((event: QueuedEvent) => {
|
|
5
|
-
const eventParams = [
|
|
6
|
-
`en=${event.name}`,
|
|
7
|
-
];
|
|
8
|
-
Object.keys(event.params).forEach((paramName) => {
|
|
9
|
-
eventParams.push(`${paramName}=${encodeURIComponent(event.params[paramName])}`);
|
|
10
|
-
});
|
|
11
|
-
return eventParams.join('&');
|
|
12
|
-
});
|
|
13
|
-
return eventLines.join('\r\n');
|
|
14
|
-
};
|