mixpanel-browser 2.52.0 → 2.54.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/.vscode/launch.json +23 -0
- package/CHANGELOG.md +22 -0
- package/README.md +12 -0
- package/build.sh +2 -0
- package/dist/mixpanel-core.cjs.js +6365 -0
- package/dist/mixpanel-js-wrapper.js +2 -0
- package/dist/mixpanel-js-wrapper.min.js +1 -1
- package/dist/mixpanel-recorder.js +980 -112
- package/dist/mixpanel-recorder.min.js +9 -9
- package/dist/mixpanel-with-async-recorder.cjs.js +6367 -0
- package/dist/mixpanel.amd.js +5310 -518
- package/dist/mixpanel.cjs.js +5310 -518
- package/dist/mixpanel.globals.js +161 -113
- package/dist/mixpanel.min.js +106 -106
- package/dist/mixpanel.umd.js +5310 -518
- package/package.json +1 -2
- package/rollup.config.js +3 -3
- package/src/config.js +1 -1
- package/src/loaders/bundle-loaders.js +22 -0
- package/src/loaders/loader-globals.js +2 -1
- package/src/loaders/loader-module-core.js +7 -0
- package/src/loaders/loader-module-with-async-recorder.js +7 -0
- package/src/loaders/loader-module.js +4 -1
- package/src/loaders/mixpanel-js-wrapper.js +2 -0
- package/src/mixpanel-core.js +18 -11
- package/src/recorder/index.js +133 -42
- package/src/request-batcher.js +19 -12
- package/src/request-queue.js +112 -88
package/dist/mixpanel.globals.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
var Config = {
|
|
5
5
|
DEBUG: false,
|
|
6
|
-
LIB_VERSION: '2.
|
|
6
|
+
LIB_VERSION: '2.54.0'
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
@@ -2052,6 +2052,7 @@
|
|
|
2052
2052
|
this.reportError = options.errorReporter || _.bind(logger$1.error, logger$1);
|
|
2053
2053
|
this.lock = new SharedLock(storageKey, {storage: this.storage});
|
|
2054
2054
|
|
|
2055
|
+
this.usePersistence = options.usePersistence;
|
|
2055
2056
|
this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
|
|
2056
2057
|
|
|
2057
2058
|
this.memQueue = [];
|
|
@@ -2076,29 +2077,36 @@
|
|
|
2076
2077
|
'payload': item
|
|
2077
2078
|
};
|
|
2078
2079
|
|
|
2079
|
-
this.
|
|
2080
|
-
|
|
2081
|
-
try {
|
|
2082
|
-
var storedQueue = this.readFromStorage();
|
|
2083
|
-
storedQueue.push(queueEntry);
|
|
2084
|
-
succeeded = this.saveToStorage(storedQueue);
|
|
2085
|
-
if (succeeded) {
|
|
2086
|
-
// only add to in-memory queue when storage succeeds
|
|
2087
|
-
this.memQueue.push(queueEntry);
|
|
2088
|
-
}
|
|
2089
|
-
} catch(err) {
|
|
2090
|
-
this.reportError('Error enqueueing item', item);
|
|
2091
|
-
succeeded = false;
|
|
2092
|
-
}
|
|
2093
|
-
if (cb) {
|
|
2094
|
-
cb(succeeded);
|
|
2095
|
-
}
|
|
2096
|
-
}, this), _.bind(function lockFailure(err) {
|
|
2097
|
-
this.reportError('Error acquiring storage lock', err);
|
|
2080
|
+
if (!this.usePersistence) {
|
|
2081
|
+
this.memQueue.push(queueEntry);
|
|
2098
2082
|
if (cb) {
|
|
2099
|
-
cb(
|
|
2083
|
+
cb(true);
|
|
2100
2084
|
}
|
|
2101
|
-
}
|
|
2085
|
+
} else {
|
|
2086
|
+
this.lock.withLock(_.bind(function lockAcquired() {
|
|
2087
|
+
var succeeded;
|
|
2088
|
+
try {
|
|
2089
|
+
var storedQueue = this.readFromStorage();
|
|
2090
|
+
storedQueue.push(queueEntry);
|
|
2091
|
+
succeeded = this.saveToStorage(storedQueue);
|
|
2092
|
+
if (succeeded) {
|
|
2093
|
+
// only add to in-memory queue when storage succeeds
|
|
2094
|
+
this.memQueue.push(queueEntry);
|
|
2095
|
+
}
|
|
2096
|
+
} catch(err) {
|
|
2097
|
+
this.reportError('Error enqueueing item', item);
|
|
2098
|
+
succeeded = false;
|
|
2099
|
+
}
|
|
2100
|
+
if (cb) {
|
|
2101
|
+
cb(succeeded);
|
|
2102
|
+
}
|
|
2103
|
+
}, this), _.bind(function lockFailure(err) {
|
|
2104
|
+
this.reportError('Error acquiring storage lock', err);
|
|
2105
|
+
if (cb) {
|
|
2106
|
+
cb(false);
|
|
2107
|
+
}
|
|
2108
|
+
}, this), this.pid);
|
|
2109
|
+
}
|
|
2102
2110
|
};
|
|
2103
2111
|
|
|
2104
2112
|
/**
|
|
@@ -2109,7 +2117,7 @@
|
|
|
2109
2117
|
*/
|
|
2110
2118
|
RequestQueue.prototype.fillBatch = function(batchSize) {
|
|
2111
2119
|
var batch = this.memQueue.slice(0, batchSize);
|
|
2112
|
-
if (batch.length < batchSize) {
|
|
2120
|
+
if (this.usePersistence && batch.length < batchSize) {
|
|
2113
2121
|
// don't need lock just to read events; localStorage is thread-safe
|
|
2114
2122
|
// and the worst that could happen is a duplicate send of some
|
|
2115
2123
|
// orphaned events, which will be deduplicated on the server side
|
|
@@ -2158,61 +2166,67 @@
|
|
|
2158
2166
|
_.each(ids, function(id) { idSet[id] = true; });
|
|
2159
2167
|
|
|
2160
2168
|
this.memQueue = filterOutIDsAndInvalid(this.memQueue, idSet);
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
succeeded
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2169
|
+
if (!this.usePersistence) {
|
|
2170
|
+
if (cb) {
|
|
2171
|
+
cb(true);
|
|
2172
|
+
}
|
|
2173
|
+
} else {
|
|
2174
|
+
var removeFromStorage = _.bind(function() {
|
|
2175
|
+
var succeeded;
|
|
2176
|
+
try {
|
|
2177
|
+
var storedQueue = this.readFromStorage();
|
|
2178
|
+
storedQueue = filterOutIDsAndInvalid(storedQueue, idSet);
|
|
2179
|
+
succeeded = this.saveToStorage(storedQueue);
|
|
2180
|
+
|
|
2181
|
+
// an extra check: did storage report success but somehow
|
|
2182
|
+
// the items are still there?
|
|
2183
|
+
if (succeeded) {
|
|
2184
|
+
storedQueue = this.readFromStorage();
|
|
2185
|
+
for (var i = 0; i < storedQueue.length; i++) {
|
|
2186
|
+
var item = storedQueue[i];
|
|
2187
|
+
if (item['id'] && !!idSet[item['id']]) {
|
|
2188
|
+
this.reportError('Item not removed from storage');
|
|
2189
|
+
return false;
|
|
2190
|
+
}
|
|
2178
2191
|
}
|
|
2179
2192
|
}
|
|
2193
|
+
} catch(err) {
|
|
2194
|
+
this.reportError('Error removing items', ids);
|
|
2195
|
+
succeeded = false;
|
|
2180
2196
|
}
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
succeeded = false;
|
|
2184
|
-
}
|
|
2185
|
-
return succeeded;
|
|
2186
|
-
}, this);
|
|
2197
|
+
return succeeded;
|
|
2198
|
+
}, this);
|
|
2187
2199
|
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2200
|
+
this.lock.withLock(function lockAcquired() {
|
|
2201
|
+
var succeeded = removeFromStorage();
|
|
2202
|
+
if (cb) {
|
|
2203
|
+
cb(succeeded);
|
|
2204
|
+
}
|
|
2205
|
+
}, _.bind(function lockFailure(err) {
|
|
2206
|
+
var succeeded = false;
|
|
2207
|
+
this.reportError('Error acquiring storage lock', err);
|
|
2208
|
+
if (!localStorageSupported(this.storage, true)) {
|
|
2209
|
+
// Looks like localStorage writes have stopped working sometime after
|
|
2210
|
+
// initialization (probably full), and so nobody can acquire locks
|
|
2211
|
+
// anymore. Consider it temporarily safe to remove items without the
|
|
2212
|
+
// lock, since nobody's writing successfully anyway.
|
|
2213
|
+
succeeded = removeFromStorage();
|
|
2214
|
+
if (!succeeded) {
|
|
2215
|
+
// OK, we couldn't even write out the smaller queue. Try clearing it
|
|
2216
|
+
// entirely.
|
|
2217
|
+
try {
|
|
2218
|
+
this.storage.removeItem(this.storageKey);
|
|
2219
|
+
} catch(err) {
|
|
2220
|
+
this.reportError('Error clearing queue', err);
|
|
2221
|
+
}
|
|
2209
2222
|
}
|
|
2210
2223
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
}
|
|
2215
|
-
}
|
|
2224
|
+
if (cb) {
|
|
2225
|
+
cb(succeeded);
|
|
2226
|
+
}
|
|
2227
|
+
}, this), this.pid);
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2216
2230
|
};
|
|
2217
2231
|
|
|
2218
2232
|
// internal helper for RequestQueue.updatePayloads
|
|
@@ -2240,25 +2254,32 @@
|
|
|
2240
2254
|
*/
|
|
2241
2255
|
RequestQueue.prototype.updatePayloads = function(itemsToUpdate, cb) {
|
|
2242
2256
|
this.memQueue = updatePayloads(this.memQueue, itemsToUpdate);
|
|
2243
|
-
this.
|
|
2244
|
-
var succeeded;
|
|
2245
|
-
try {
|
|
2246
|
-
var storedQueue = this.readFromStorage();
|
|
2247
|
-
storedQueue = updatePayloads(storedQueue, itemsToUpdate);
|
|
2248
|
-
succeeded = this.saveToStorage(storedQueue);
|
|
2249
|
-
} catch(err) {
|
|
2250
|
-
this.reportError('Error updating items', itemsToUpdate);
|
|
2251
|
-
succeeded = false;
|
|
2252
|
-
}
|
|
2253
|
-
if (cb) {
|
|
2254
|
-
cb(succeeded);
|
|
2255
|
-
}
|
|
2256
|
-
}, this), _.bind(function lockFailure(err) {
|
|
2257
|
-
this.reportError('Error acquiring storage lock', err);
|
|
2257
|
+
if (!this.usePersistence) {
|
|
2258
2258
|
if (cb) {
|
|
2259
|
-
cb(
|
|
2259
|
+
cb(true);
|
|
2260
2260
|
}
|
|
2261
|
-
}
|
|
2261
|
+
} else {
|
|
2262
|
+
this.lock.withLock(_.bind(function lockAcquired() {
|
|
2263
|
+
var succeeded;
|
|
2264
|
+
try {
|
|
2265
|
+
var storedQueue = this.readFromStorage();
|
|
2266
|
+
storedQueue = updatePayloads(storedQueue, itemsToUpdate);
|
|
2267
|
+
succeeded = this.saveToStorage(storedQueue);
|
|
2268
|
+
} catch(err) {
|
|
2269
|
+
this.reportError('Error updating items', itemsToUpdate);
|
|
2270
|
+
succeeded = false;
|
|
2271
|
+
}
|
|
2272
|
+
if (cb) {
|
|
2273
|
+
cb(succeeded);
|
|
2274
|
+
}
|
|
2275
|
+
}, this), _.bind(function lockFailure(err) {
|
|
2276
|
+
this.reportError('Error acquiring storage lock', err);
|
|
2277
|
+
if (cb) {
|
|
2278
|
+
cb(false);
|
|
2279
|
+
}
|
|
2280
|
+
}, this), this.pid);
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2262
2283
|
};
|
|
2263
2284
|
|
|
2264
2285
|
/**
|
|
@@ -2301,7 +2322,10 @@
|
|
|
2301
2322
|
*/
|
|
2302
2323
|
RequestQueue.prototype.clear = function() {
|
|
2303
2324
|
this.memQueue = [];
|
|
2304
|
-
|
|
2325
|
+
|
|
2326
|
+
if (this.usePersistence) {
|
|
2327
|
+
this.storage.removeItem(this.storageKey);
|
|
2328
|
+
}
|
|
2305
2329
|
};
|
|
2306
2330
|
|
|
2307
2331
|
// maximum interval between request retries after exponential backoff
|
|
@@ -2319,7 +2343,8 @@
|
|
|
2319
2343
|
this.errorReporter = options.errorReporter;
|
|
2320
2344
|
this.queue = new RequestQueue(storageKey, {
|
|
2321
2345
|
errorReporter: _.bind(this.reportError, this),
|
|
2322
|
-
storage: options.storage
|
|
2346
|
+
storage: options.storage,
|
|
2347
|
+
usePersistence: options.usePersistence
|
|
2323
2348
|
});
|
|
2324
2349
|
|
|
2325
2350
|
this.libConfig = options.libConfig;
|
|
@@ -2336,6 +2361,11 @@
|
|
|
2336
2361
|
|
|
2337
2362
|
// extra client-side dedupe
|
|
2338
2363
|
this.itemIdsSentSuccessfully = {};
|
|
2364
|
+
|
|
2365
|
+
// Make the flush occur at the interval specified by flushIntervalMs, default behavior will attempt consecutive flushes
|
|
2366
|
+
// as long as the queue is not empty. This is useful for high-frequency events like Session Replay where we might end up
|
|
2367
|
+
// in a request loop and get ratelimited by the server.
|
|
2368
|
+
this.flushOnlyOnInterval = options.flushOnlyOnInterval || false;
|
|
2339
2369
|
};
|
|
2340
2370
|
|
|
2341
2371
|
/**
|
|
@@ -2420,6 +2450,9 @@
|
|
|
2420
2450
|
var startTime = new Date().getTime();
|
|
2421
2451
|
var currentBatchSize = this.batchSize;
|
|
2422
2452
|
var batch = this.queue.fillBatch(currentBatchSize);
|
|
2453
|
+
// if there's more items in the queue than the batch size, attempt
|
|
2454
|
+
// to flush again after the current batch is done.
|
|
2455
|
+
var attemptSecondaryFlush = batch.length === currentBatchSize;
|
|
2423
2456
|
var dataForRequest = [];
|
|
2424
2457
|
var transformedItems = {};
|
|
2425
2458
|
_.each(batch, function(item) {
|
|
@@ -2487,22 +2520,17 @@
|
|
|
2487
2520
|
this.flush();
|
|
2488
2521
|
} else if (
|
|
2489
2522
|
_.isObject(res) &&
|
|
2490
|
-
res.
|
|
2491
|
-
(res.xhr_req['status'] >= 500 || res.xhr_req['status'] === 429 || res.error === 'timeout')
|
|
2523
|
+
(res.httpStatusCode >= 500 || res.httpStatusCode === 429 || res.error === 'timeout')
|
|
2492
2524
|
) {
|
|
2493
2525
|
// network or API error, or 429 Too Many Requests, retry
|
|
2494
2526
|
var retryMS = this.flushInterval * 2;
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
var retryAfter = headers['Retry-After'];
|
|
2498
|
-
if (retryAfter) {
|
|
2499
|
-
retryMS = (parseInt(retryAfter, 10) * 1000) || retryMS;
|
|
2500
|
-
}
|
|
2527
|
+
if (res.retryAfter) {
|
|
2528
|
+
retryMS = (parseInt(res.retryAfter, 10) * 1000) || retryMS;
|
|
2501
2529
|
}
|
|
2502
2530
|
retryMS = Math.min(MAX_RETRY_INTERVAL_MS, retryMS);
|
|
2503
2531
|
this.reportError('Error; retry in ' + retryMS + ' ms');
|
|
2504
2532
|
this.scheduleFlush(retryMS);
|
|
2505
|
-
} else if (_.isObject(res) && res.
|
|
2533
|
+
} else if (_.isObject(res) && res.httpStatusCode === 413) {
|
|
2506
2534
|
// 413 Payload Too Large
|
|
2507
2535
|
if (batch.length > 1) {
|
|
2508
2536
|
var halvedBatchSize = Math.max(1, Math.floor(currentBatchSize / 2));
|
|
@@ -2526,7 +2554,11 @@
|
|
|
2526
2554
|
_.bind(function(succeeded) {
|
|
2527
2555
|
if (succeeded) {
|
|
2528
2556
|
this.consecutiveRemovalFailures = 0;
|
|
2529
|
-
this.
|
|
2557
|
+
if (this.flushOnlyOnInterval && !attemptSecondaryFlush) {
|
|
2558
|
+
this.resetFlush(); // schedule next batch with a delay
|
|
2559
|
+
} else {
|
|
2560
|
+
this.flush(); // handle next batch if the queue isn't empty
|
|
2561
|
+
}
|
|
2530
2562
|
} else {
|
|
2531
2563
|
this.reportError('Failed to remove items from queue');
|
|
2532
2564
|
if (++this.consecutiveRemovalFailures > 5) {
|
|
@@ -2574,7 +2606,6 @@
|
|
|
2574
2606
|
}
|
|
2575
2607
|
logger.log('MIXPANEL REQUEST:', dataForRequest);
|
|
2576
2608
|
this.sendRequest(dataForRequest, requestOptions, batchSendCallback);
|
|
2577
|
-
|
|
2578
2609
|
} catch(err) {
|
|
2579
2610
|
this.reportError('Error flushing request queue', err);
|
|
2580
2611
|
this.resetFlush();
|
|
@@ -4108,6 +4139,12 @@
|
|
|
4108
4139
|
*/
|
|
4109
4140
|
|
|
4110
4141
|
var init_type; // MODULE or SNIPPET loader
|
|
4142
|
+
// allow bundlers to specify how extra code (recorder bundle) should be loaded
|
|
4143
|
+
// eslint-disable-next-line no-unused-vars
|
|
4144
|
+
var load_extra_bundle = function(src, _onload) {
|
|
4145
|
+
throw new Error(src + ' not available in this build.');
|
|
4146
|
+
};
|
|
4147
|
+
|
|
4111
4148
|
var mixpanel_master; // main mixpanel instance / object
|
|
4112
4149
|
var INIT_MODULE = 0;
|
|
4113
4150
|
var INIT_SNIPPET = 1;
|
|
@@ -4201,7 +4238,9 @@
|
|
|
4201
4238
|
'hooks': {},
|
|
4202
4239
|
'record_block_class': new RegExp('^(mp-block|fs-exclude|amp-block|rr-block|ph-no-capture)$'),
|
|
4203
4240
|
'record_block_selector': 'img, video',
|
|
4241
|
+
'record_collect_fonts': false,
|
|
4204
4242
|
'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
|
|
4243
|
+
'record_inline_images': false,
|
|
4205
4244
|
'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
|
|
4206
4245
|
'record_mask_text_selector': '*',
|
|
4207
4246
|
'record_max_ms': MAX_RECORDING_MS,
|
|
@@ -4437,12 +4476,7 @@
|
|
|
4437
4476
|
}, this);
|
|
4438
4477
|
|
|
4439
4478
|
if (_.isUndefined(win['__mp_recorder'])) {
|
|
4440
|
-
|
|
4441
|
-
scriptEl.type = 'text/javascript';
|
|
4442
|
-
scriptEl.async = true;
|
|
4443
|
-
scriptEl.onload = handleLoadedRecorder;
|
|
4444
|
-
scriptEl.src = this.get_config('recorder_src');
|
|
4445
|
-
document$1.head.appendChild(scriptEl);
|
|
4479
|
+
load_extra_bundle(this.get_config('recorder_src'), handleLoadedRecorder);
|
|
4446
4480
|
} else {
|
|
4447
4481
|
handleLoadedRecorder();
|
|
4448
4482
|
}
|
|
@@ -4741,7 +4775,8 @@
|
|
|
4741
4775
|
lib.report_error(error);
|
|
4742
4776
|
if (callback) {
|
|
4743
4777
|
if (verbose_mode) {
|
|
4744
|
-
|
|
4778
|
+
var response_headers = req['responseHeaders'] || {};
|
|
4779
|
+
callback({status: 0, httpStatusCode: req['status'], error: error, retryAfter: response_headers['Retry-After']});
|
|
4745
4780
|
} else {
|
|
4746
4781
|
callback(0);
|
|
4747
4782
|
}
|
|
@@ -4841,6 +4876,7 @@
|
|
|
4841
4876
|
attrs.queue_key,
|
|
4842
4877
|
{
|
|
4843
4878
|
libConfig: this['config'],
|
|
4879
|
+
errorReporter: this.get_config('error_reporter'),
|
|
4844
4880
|
sendRequestFunc: _.bind(function(data, options, cb) {
|
|
4845
4881
|
this._send_request(
|
|
4846
4882
|
this.get_config('api_host') + attrs.endpoint,
|
|
@@ -4852,8 +4888,8 @@
|
|
|
4852
4888
|
beforeSendHook: _.bind(function(item) {
|
|
4853
4889
|
return this._run_hook('before_send_' + attrs.type, item);
|
|
4854
4890
|
}, this),
|
|
4855
|
-
|
|
4856
|
-
|
|
4891
|
+
stopAllBatchingFunc: _.bind(this.stop_batch_senders, this),
|
|
4892
|
+
usePersistence: true
|
|
4857
4893
|
}
|
|
4858
4894
|
);
|
|
4859
4895
|
}, this);
|
|
@@ -6302,7 +6338,8 @@
|
|
|
6302
6338
|
_.register_event(win, 'load', dom_loaded_handler, true);
|
|
6303
6339
|
};
|
|
6304
6340
|
|
|
6305
|
-
function init_from_snippet() {
|
|
6341
|
+
function init_from_snippet(bundle_loader) {
|
|
6342
|
+
load_extra_bundle = bundle_loader;
|
|
6306
6343
|
init_type = INIT_SNIPPET;
|
|
6307
6344
|
mixpanel_master = win[PRIMARY_INSTANCE_NAME];
|
|
6308
6345
|
|
|
@@ -6342,8 +6379,19 @@
|
|
|
6342
6379
|
add_dom_loaded_handler();
|
|
6343
6380
|
}
|
|
6344
6381
|
|
|
6382
|
+
// For loading separate bundles asynchronously via script tag
|
|
6383
|
+
// so that we don't load them until they are needed at runtime.
|
|
6384
|
+
function loadAsync (src, onload) {
|
|
6385
|
+
var scriptEl = document.createElement('script');
|
|
6386
|
+
scriptEl.type = 'text/javascript';
|
|
6387
|
+
scriptEl.async = true;
|
|
6388
|
+
scriptEl.onload = onload;
|
|
6389
|
+
scriptEl.src = src;
|
|
6390
|
+
document.head.appendChild(scriptEl);
|
|
6391
|
+
}
|
|
6392
|
+
|
|
6345
6393
|
/* eslint camelcase: "off" */
|
|
6346
6394
|
|
|
6347
|
-
init_from_snippet();
|
|
6395
|
+
init_from_snippet(loadAsync);
|
|
6348
6396
|
|
|
6349
6397
|
})();
|