mixpanel-browser 2.56.0 → 2.58.0
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/CHANGELOG.md +10 -0
- package/LICENSE +1 -15
- package/build.sh +3 -3
- package/dist/mixpanel-core.cjs.js +899 -410
- package/dist/mixpanel-recorder.js +909 -410
- package/dist/mixpanel-recorder.min.js +10 -10
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-with-async-recorder.cjs.js +899 -410
- package/dist/mixpanel.amd.js +911 -411
- package/dist/mixpanel.cjs.js +911 -411
- package/dist/mixpanel.globals.js +899 -410
- package/dist/mixpanel.min.js +122 -112
- package/dist/mixpanel.module.js +911 -411
- package/dist/mixpanel.umd.js +911 -411
- package/package.json +1 -1
- package/src/config.js +1 -1
- package/src/externs.js +14 -0
- package/src/gdpr-utils.js +2 -1
- package/src/mixpanel-core.js +4 -2
- package/src/promise-polyfill.js +379 -0
- package/src/recorder/index.js +2 -1
- package/src/recorder/session-recording.js +14 -2
- package/src/request-batcher.js +185 -164
- package/src/request-queue.js +200 -147
- package/src/shared-lock.js +104 -98
- package/src/storage/local-storage.js +53 -0
- package/src/storage/wrapper.js +14 -0
- package/src/utils.js +16 -33
- package/src/window.js +20 -0
- package/dist/mixpanel.min.js.map +0 -8
package/package.json
CHANGED
package/src/config.js
CHANGED
package/src/externs.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Externs for Promise polyfill so that closure compiler does not try to rename methods.
|
|
3
|
+
* https://developers.google.com/closure/compiler/docs/externs-and-exports
|
|
4
|
+
* @externs
|
|
5
|
+
*/
|
|
6
|
+
var Promise = {};
|
|
7
|
+
|
|
8
|
+
Promise.resolve = function() {};
|
|
9
|
+
Promise.reject = function() {};
|
|
10
|
+
Promise.race = function() {};
|
|
11
|
+
Promise.all = function() {};
|
|
12
|
+
|
|
13
|
+
Promise.prototype.then = function() {};
|
|
14
|
+
Promise.prototype.catch = function() {};
|
package/src/gdpr-utils.js
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* These functions are used internally by the SDK and are not intended to be publicly exposed.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { _, console
|
|
14
|
+
import { _, console } from './utils';
|
|
15
|
+
import { window } from './window';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* A function used to track a Mixpanel event (e.g. MixpanelLib.track)
|
package/src/mixpanel-core.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint camelcase: "off" */
|
|
2
2
|
import Config from './config';
|
|
3
|
-
import { MAX_RECORDING_MS, _, console, userAgent,
|
|
3
|
+
import { MAX_RECORDING_MS, _, console, userAgent, document, navigator, slice } from './utils';
|
|
4
|
+
import { window } from './window';
|
|
4
5
|
import { FormTracker, LinkTracker } from './dom-trackers';
|
|
5
6
|
import { RequestBatcher } from './request-batcher';
|
|
6
7
|
import { MixpanelGroup } from './mixpanel-group';
|
|
@@ -146,6 +147,7 @@ var DEFAULT_CONFIG = {
|
|
|
146
147
|
'hooks': {},
|
|
147
148
|
'record_block_class': new RegExp('^(mp-block|fs-exclude|amp-block|rr-block|ph-no-capture)$'),
|
|
148
149
|
'record_block_selector': 'img, video',
|
|
150
|
+
'record_canvas': false,
|
|
149
151
|
'record_collect_fonts': false,
|
|
150
152
|
'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
|
|
151
153
|
'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
|
|
@@ -923,7 +925,7 @@ MixpanelLib.prototype._track_or_batch = function(options, callback) {
|
|
|
923
925
|
}, this);
|
|
924
926
|
|
|
925
927
|
if (this._batch_requests && !should_send_immediately) {
|
|
926
|
-
batcher.enqueue(truncated_data
|
|
928
|
+
batcher.enqueue(truncated_data).then(function(succeeded) {
|
|
927
929
|
if (succeeded) {
|
|
928
930
|
callback(1, truncated_data);
|
|
929
931
|
} else {
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { window } from './window';
|
|
2
|
+
/**
|
|
3
|
+
* Promise polyfill sourced from https://github.com/getify/native-promise-only.
|
|
4
|
+
* Modified to
|
|
5
|
+
* - remove UMD wrapper and export as an object, so that we don't globally polyfill Promise.
|
|
6
|
+
* - rename access notation for dynamically referenced props to avoid minification, e.g. this.__NPO__ -> this['__NPO__']
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/*! Native Promise Only
|
|
10
|
+
v0.8.1 (c) Kyle Simpson
|
|
11
|
+
MIT License: http://getify.mit-license.org
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/*jshint validthis:true */
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
var setImmediate = window['setImmediate'];
|
|
18
|
+
var builtInProp, cycle, schedulingQueue,
|
|
19
|
+
ToString = Object.prototype.toString,
|
|
20
|
+
timer = (typeof setImmediate !== 'undefined') ?
|
|
21
|
+
function timer(fn) { return setImmediate(fn); } :
|
|
22
|
+
setTimeout;
|
|
23
|
+
|
|
24
|
+
// dammit, IE8.
|
|
25
|
+
try {
|
|
26
|
+
Object.defineProperty({},'x',{});
|
|
27
|
+
builtInProp = function builtInProp(obj,name,val,config) {
|
|
28
|
+
return Object.defineProperty(obj,name,{
|
|
29
|
+
value: val,
|
|
30
|
+
writable: true,
|
|
31
|
+
configurable: config !== false
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
builtInProp = function builtInProp(obj,name,val) {
|
|
37
|
+
obj[name] = val;
|
|
38
|
+
return obj;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Note: using a queue instead of array for efficiency
|
|
43
|
+
schedulingQueue = (function Queue() {
|
|
44
|
+
var first, last, item;
|
|
45
|
+
|
|
46
|
+
function Item(fn,self) {
|
|
47
|
+
this.fn = fn;
|
|
48
|
+
this.self = self;
|
|
49
|
+
this.next = void 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
add: function add(fn,self) {
|
|
54
|
+
item = new Item(fn,self);
|
|
55
|
+
if (last) {
|
|
56
|
+
last.next = item;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
first = item;
|
|
60
|
+
}
|
|
61
|
+
last = item;
|
|
62
|
+
item = void 0;
|
|
63
|
+
},
|
|
64
|
+
drain: function drain() {
|
|
65
|
+
var f = first;
|
|
66
|
+
first = last = cycle = void 0;
|
|
67
|
+
|
|
68
|
+
while (f) {
|
|
69
|
+
f.fn.call(f.self);
|
|
70
|
+
f = f.next;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
})();
|
|
75
|
+
|
|
76
|
+
function schedule(fn,self) {
|
|
77
|
+
schedulingQueue.add(fn,self);
|
|
78
|
+
if (!cycle) {
|
|
79
|
+
cycle = timer(schedulingQueue.drain);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// promise duck typing
|
|
84
|
+
function isThenable(o) {
|
|
85
|
+
var _then, oType = typeof o;
|
|
86
|
+
|
|
87
|
+
if (o !== null && (oType === 'object' || oType === 'function')) {
|
|
88
|
+
_then = o.then;
|
|
89
|
+
}
|
|
90
|
+
return typeof _then === 'function' ? _then : false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function notify() {
|
|
94
|
+
for (var i=0; i<this.chain.length; i++) {
|
|
95
|
+
notifyIsolated(
|
|
96
|
+
this,
|
|
97
|
+
(this.state === 1) ? this.chain[i].success : this.chain[i].failure,
|
|
98
|
+
this.chain[i]
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
this.chain.length = 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// NOTE: This is a separate function to isolate
|
|
105
|
+
// the `try..catch` so that other code can be
|
|
106
|
+
// optimized better
|
|
107
|
+
function notifyIsolated(self,cb,chain) {
|
|
108
|
+
var ret, _then;
|
|
109
|
+
try {
|
|
110
|
+
if (cb === false) {
|
|
111
|
+
chain.reject(self.msg);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
if (cb === true) {
|
|
115
|
+
ret = self.msg;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
ret = cb.call(void 0,self.msg);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (ret === chain.promise) {
|
|
122
|
+
chain.reject(TypeError('Promise-chain cycle'));
|
|
123
|
+
}
|
|
124
|
+
// eslint-disable-next-line no-cond-assign
|
|
125
|
+
else if (_then = isThenable(ret)) {
|
|
126
|
+
_then.call(ret,chain.resolve,chain.reject);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
chain.resolve(ret);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
chain.reject(err);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function resolve(msg) {
|
|
139
|
+
var _then, self = this;
|
|
140
|
+
|
|
141
|
+
// already triggered?
|
|
142
|
+
if (self.triggered) { return; }
|
|
143
|
+
|
|
144
|
+
self.triggered = true;
|
|
145
|
+
|
|
146
|
+
// unwrap
|
|
147
|
+
if (self.def) {
|
|
148
|
+
self = self.def;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// eslint-disable-next-line no-cond-assign
|
|
153
|
+
if (_then = isThenable(msg)) {
|
|
154
|
+
schedule(function(){
|
|
155
|
+
var defWrapper = new MakeDefWrapper(self);
|
|
156
|
+
try {
|
|
157
|
+
_then.call(msg,
|
|
158
|
+
function $resolve$(){ resolve.apply(defWrapper,arguments); },
|
|
159
|
+
function $reject$(){ reject.apply(defWrapper,arguments); }
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
reject.call(defWrapper,err);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
self.msg = msg;
|
|
169
|
+
self.state = 1;
|
|
170
|
+
if (self.chain.length > 0) {
|
|
171
|
+
schedule(notify,self);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
reject.call(new MakeDefWrapper(self),err);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function reject(msg) {
|
|
181
|
+
var self = this;
|
|
182
|
+
|
|
183
|
+
// already triggered?
|
|
184
|
+
if (self.triggered) { return; }
|
|
185
|
+
|
|
186
|
+
self.triggered = true;
|
|
187
|
+
|
|
188
|
+
// unwrap
|
|
189
|
+
if (self.def) {
|
|
190
|
+
self = self.def;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
self.msg = msg;
|
|
194
|
+
self.state = 2;
|
|
195
|
+
if (self.chain.length > 0) {
|
|
196
|
+
schedule(notify,self);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function iteratePromises(Constructor,arr,resolver,rejecter) {
|
|
201
|
+
for (var idx=0; idx<arr.length; idx++) {
|
|
202
|
+
(function IIFE(idx){
|
|
203
|
+
Constructor.resolve(arr[idx])
|
|
204
|
+
.then(
|
|
205
|
+
function $resolver$(msg){
|
|
206
|
+
resolver(idx,msg);
|
|
207
|
+
},
|
|
208
|
+
rejecter
|
|
209
|
+
);
|
|
210
|
+
})(idx);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function MakeDefWrapper(self) {
|
|
215
|
+
this.def = self;
|
|
216
|
+
this.triggered = false;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function MakeDef(self) {
|
|
220
|
+
this.promise = self;
|
|
221
|
+
this.state = 0;
|
|
222
|
+
this.triggered = false;
|
|
223
|
+
this.chain = [];
|
|
224
|
+
this.msg = void 0;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function NpoPromise(executor) {
|
|
228
|
+
if (typeof executor !== 'function') {
|
|
229
|
+
throw TypeError('Not a function');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (this['__NPO__'] !== 0) {
|
|
233
|
+
throw TypeError('Not a promise');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// instance shadowing the inherited "brand"
|
|
237
|
+
// to signal an already "initialized" promise
|
|
238
|
+
this['__NPO__'] = 1;
|
|
239
|
+
|
|
240
|
+
var def = new MakeDef(this);
|
|
241
|
+
|
|
242
|
+
this['then'] = function then(success,failure) {
|
|
243
|
+
var o = {
|
|
244
|
+
success: typeof success === 'function' ? success : true,
|
|
245
|
+
failure: typeof failure === 'function' ? failure : false
|
|
246
|
+
};
|
|
247
|
+
// Note: `then(..)` itself can be borrowed to be used against
|
|
248
|
+
// a different promise constructor for making the chained promise,
|
|
249
|
+
// by substituting a different `this` binding.
|
|
250
|
+
o.promise = new this.constructor(function extractChain(resolve,reject) {
|
|
251
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
252
|
+
throw TypeError('Not a function');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
o.resolve = resolve;
|
|
256
|
+
o.reject = reject;
|
|
257
|
+
});
|
|
258
|
+
def.chain.push(o);
|
|
259
|
+
|
|
260
|
+
if (def.state !== 0) {
|
|
261
|
+
schedule(notify,def);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return o.promise;
|
|
265
|
+
};
|
|
266
|
+
this['catch'] = function $catch$(failure) {
|
|
267
|
+
return this.then(void 0,failure);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
executor.call(
|
|
272
|
+
void 0,
|
|
273
|
+
function publicResolve(msg){
|
|
274
|
+
resolve.call(def,msg);
|
|
275
|
+
},
|
|
276
|
+
function publicReject(msg) {
|
|
277
|
+
reject.call(def,msg);
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
reject.call(def,err);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
var PromisePrototype = builtInProp({},'constructor',NpoPromise,
|
|
287
|
+
/*configurable=*/false
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// Note: Android 4 cannot use `Object.defineProperty(..)` here
|
|
291
|
+
NpoPromise.prototype = PromisePrototype;
|
|
292
|
+
|
|
293
|
+
// built-in "brand" to signal an "uninitialized" promise
|
|
294
|
+
builtInProp(PromisePrototype,'__NPO__',0,
|
|
295
|
+
/*configurable=*/false
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
builtInProp(NpoPromise,'resolve',function Promise$resolve(msg) {
|
|
299
|
+
var Constructor = this;
|
|
300
|
+
|
|
301
|
+
// spec mandated checks
|
|
302
|
+
// note: best "isPromise" check that's practical for now
|
|
303
|
+
if (msg && typeof msg === 'object' && msg['__NPO__'] === 1) {
|
|
304
|
+
return msg;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return new Constructor(function executor(resolve,reject){
|
|
308
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
309
|
+
throw TypeError('Not a function');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
resolve(msg);
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
builtInProp(NpoPromise,'reject',function Promise$reject(msg) {
|
|
317
|
+
return new this(function executor(resolve,reject){
|
|
318
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
319
|
+
throw TypeError('Not a function');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
reject(msg);
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
builtInProp(NpoPromise,'all',function Promise$all(arr) {
|
|
327
|
+
var Constructor = this;
|
|
328
|
+
|
|
329
|
+
// spec mandated checks
|
|
330
|
+
if (ToString.call(arr) !== '[object Array]') {
|
|
331
|
+
return Constructor.reject(TypeError('Not an array'));
|
|
332
|
+
}
|
|
333
|
+
if (arr.length === 0) {
|
|
334
|
+
return Constructor.resolve([]);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return new Constructor(function executor(resolve,reject){
|
|
338
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
339
|
+
throw TypeError('Not a function');
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
var len = arr.length, msgs = Array(len), count = 0;
|
|
343
|
+
|
|
344
|
+
iteratePromises(Constructor,arr,function resolver(idx,msg) {
|
|
345
|
+
msgs[idx] = msg;
|
|
346
|
+
if (++count === len) {
|
|
347
|
+
resolve(msgs);
|
|
348
|
+
}
|
|
349
|
+
},reject);
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
builtInProp(NpoPromise,'race',function Promise$race(arr) {
|
|
354
|
+
var Constructor = this;
|
|
355
|
+
|
|
356
|
+
// spec mandated checks
|
|
357
|
+
if (ToString.call(arr) !== '[object Array]') {
|
|
358
|
+
return Constructor.reject(TypeError('Not an array'));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return new Constructor(function executor(resolve,reject){
|
|
362
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
363
|
+
throw TypeError('Not a function');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
iteratePromises(Constructor,arr,function resolver(idx,msg){
|
|
367
|
+
resolve(msg);
|
|
368
|
+
},reject);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
var PromisePolyfill;
|
|
373
|
+
if (typeof Promise !== 'undefined' && Promise.toString().indexOf('[native code]') !== -1) {
|
|
374
|
+
PromisePolyfill = Promise;
|
|
375
|
+
} else {
|
|
376
|
+
PromisePolyfill = NpoPromise;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export { PromisePolyfill as Promise, NpoPromise };
|
package/src/recorder/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {record} from 'rrweb';
|
|
2
2
|
|
|
3
3
|
import { SessionRecording } from './session-recording';
|
|
4
|
-
import { console_with_prefix, _
|
|
4
|
+
import { console_with_prefix, _ } from '../utils'; // eslint-disable-line camelcase
|
|
5
|
+
import { window } from '../window';
|
|
5
6
|
|
|
6
7
|
var logger = console_with_prefix('recorder');
|
|
7
8
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IncrementalSource, EventType } from '@rrweb/types';
|
|
2
2
|
|
|
3
|
-
import { MAX_RECORDING_MS, MAX_VALUE_FOR_MIN_RECORDING_MS, console_with_prefix, _
|
|
3
|
+
import { MAX_RECORDING_MS, MAX_VALUE_FOR_MIN_RECORDING_MS, console_with_prefix, _} from '../utils'; // eslint-disable-line camelcase
|
|
4
|
+
import { window } from '../window';
|
|
4
5
|
import { addOptOutCheckMixpanelLib } from '../gdpr-utils';
|
|
5
6
|
import { RequestBatcher } from '../request-batcher';
|
|
6
7
|
import Config from '../config';
|
|
@@ -52,6 +53,7 @@ var SessionRecording = function(options) {
|
|
|
52
53
|
|
|
53
54
|
this.seqNo = 0;
|
|
54
55
|
this.replayStartTime = null;
|
|
56
|
+
this.replayStartUrl = null;
|
|
55
57
|
this.batchStartUrl = null;
|
|
56
58
|
|
|
57
59
|
this.idleTimeoutId = null;
|
|
@@ -103,6 +105,7 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
|
|
|
103
105
|
|
|
104
106
|
this.replayStartTime = new Date().getTime();
|
|
105
107
|
this.batchStartUrl = _.info.currentUrl();
|
|
108
|
+
this.replayStartUrl = _.info.currentUrl();
|
|
106
109
|
|
|
107
110
|
if (shouldStopBatcher || this.recordMinMs > 0) {
|
|
108
111
|
// the primary case for shouldStopBatcher is when we're starting recording after a reset
|
|
@@ -139,9 +142,17 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
|
|
|
139
142
|
'blockClass': this.getConfig('record_block_class'),
|
|
140
143
|
'blockSelector': blockSelector,
|
|
141
144
|
'collectFonts': this.getConfig('record_collect_fonts'),
|
|
145
|
+
'dataURLOptions': { // canvas image options (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL)
|
|
146
|
+
'type': 'image/webp',
|
|
147
|
+
'quality': 0.6
|
|
148
|
+
},
|
|
142
149
|
'maskAllInputs': true,
|
|
143
150
|
'maskTextClass': this.getConfig('record_mask_text_class'),
|
|
144
|
-
'maskTextSelector': this.getConfig('record_mask_text_selector')
|
|
151
|
+
'maskTextSelector': this.getConfig('record_mask_text_selector'),
|
|
152
|
+
'recordCanvas': this.getConfig('record_canvas'),
|
|
153
|
+
'sampling': {
|
|
154
|
+
'canvas': 15
|
|
155
|
+
}
|
|
145
156
|
});
|
|
146
157
|
|
|
147
158
|
if (typeof this._stopRecording !== 'function') {
|
|
@@ -259,6 +270,7 @@ SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (da
|
|
|
259
270
|
'replay_id': replayId,
|
|
260
271
|
'replay_length_ms': replayLengthMs,
|
|
261
272
|
'replay_start_time': this.replayStartTime / 1000,
|
|
273
|
+
'replay_start_url': this.replayStartUrl,
|
|
262
274
|
'seq': this.seqNo
|
|
263
275
|
};
|
|
264
276
|
var eventsJson = _.JSONEncode(data);
|