mixpanel-browser 2.53.0 → 2.54.1
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 +9 -0
- package/README.md +12 -0
- package/build.sh +2 -0
- package/dist/mixpanel-core.cjs.js +6369 -0
- package/dist/mixpanel-recorder.js +982 -109
- package/dist/mixpanel-recorder.min.js +9 -9
- package/dist/mixpanel-with-async-recorder.cjs.js +6371 -0
- package/dist/mixpanel.amd.js +5333 -519
- package/dist/mixpanel.cjs.js +5333 -519
- package/dist/mixpanel.globals.js +166 -114
- package/dist/mixpanel.min.js +108 -108
- package/dist/mixpanel.umd.js +5333 -519
- 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/mixpanel-core.js +18 -11
- package/src/recorder/index.js +129 -37
- package/src/request-batcher.js +24 -13
- 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.1'
|
|
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
|
-
}
|
|
2257
|
+
if (!this.usePersistence) {
|
|
2253
2258
|
if (cb) {
|
|
2254
|
-
cb(
|
|
2259
|
+
cb(true);
|
|
2255
2260
|
}
|
|
2256
|
-
}
|
|
2257
|
-
this.
|
|
2258
|
-
|
|
2259
|
-
|
|
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
|
/**
|
|
@@ -2393,7 +2423,11 @@
|
|
|
2393
2423
|
RequestBatcher.prototype.scheduleFlush = function(flushMS) {
|
|
2394
2424
|
this.flushInterval = flushMS;
|
|
2395
2425
|
if (!this.stopped) { // don't schedule anymore if batching has been stopped
|
|
2396
|
-
this.timeoutID = setTimeout(_.bind(
|
|
2426
|
+
this.timeoutID = setTimeout(_.bind(function() {
|
|
2427
|
+
if (!this.stopped) {
|
|
2428
|
+
this.flush();
|
|
2429
|
+
}
|
|
2430
|
+
}, this), this.flushInterval);
|
|
2397
2431
|
}
|
|
2398
2432
|
};
|
|
2399
2433
|
|
|
@@ -2420,6 +2454,9 @@
|
|
|
2420
2454
|
var startTime = new Date().getTime();
|
|
2421
2455
|
var currentBatchSize = this.batchSize;
|
|
2422
2456
|
var batch = this.queue.fillBatch(currentBatchSize);
|
|
2457
|
+
// if there's more items in the queue than the batch size, attempt
|
|
2458
|
+
// to flush again after the current batch is done.
|
|
2459
|
+
var attemptSecondaryFlush = batch.length === currentBatchSize;
|
|
2423
2460
|
var dataForRequest = [];
|
|
2424
2461
|
var transformedItems = {};
|
|
2425
2462
|
_.each(batch, function(item) {
|
|
@@ -2487,22 +2524,17 @@
|
|
|
2487
2524
|
this.flush();
|
|
2488
2525
|
} else if (
|
|
2489
2526
|
_.isObject(res) &&
|
|
2490
|
-
res.
|
|
2491
|
-
(res.xhr_req['status'] >= 500 || res.xhr_req['status'] === 429 || res.error === 'timeout')
|
|
2527
|
+
(res.httpStatusCode >= 500 || res.httpStatusCode === 429 || res.error === 'timeout')
|
|
2492
2528
|
) {
|
|
2493
2529
|
// network or API error, or 429 Too Many Requests, retry
|
|
2494
2530
|
var retryMS = this.flushInterval * 2;
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
var retryAfter = headers['Retry-After'];
|
|
2498
|
-
if (retryAfter) {
|
|
2499
|
-
retryMS = (parseInt(retryAfter, 10) * 1000) || retryMS;
|
|
2500
|
-
}
|
|
2531
|
+
if (res.retryAfter) {
|
|
2532
|
+
retryMS = (parseInt(res.retryAfter, 10) * 1000) || retryMS;
|
|
2501
2533
|
}
|
|
2502
2534
|
retryMS = Math.min(MAX_RETRY_INTERVAL_MS, retryMS);
|
|
2503
2535
|
this.reportError('Error; retry in ' + retryMS + ' ms');
|
|
2504
2536
|
this.scheduleFlush(retryMS);
|
|
2505
|
-
} else if (_.isObject(res) && res.
|
|
2537
|
+
} else if (_.isObject(res) && res.httpStatusCode === 413) {
|
|
2506
2538
|
// 413 Payload Too Large
|
|
2507
2539
|
if (batch.length > 1) {
|
|
2508
2540
|
var halvedBatchSize = Math.max(1, Math.floor(currentBatchSize / 2));
|
|
@@ -2526,7 +2558,11 @@
|
|
|
2526
2558
|
_.bind(function(succeeded) {
|
|
2527
2559
|
if (succeeded) {
|
|
2528
2560
|
this.consecutiveRemovalFailures = 0;
|
|
2529
|
-
this.
|
|
2561
|
+
if (this.flushOnlyOnInterval && !attemptSecondaryFlush) {
|
|
2562
|
+
this.resetFlush(); // schedule next batch with a delay
|
|
2563
|
+
} else {
|
|
2564
|
+
this.flush(); // handle next batch if the queue isn't empty
|
|
2565
|
+
}
|
|
2530
2566
|
} else {
|
|
2531
2567
|
this.reportError('Failed to remove items from queue');
|
|
2532
2568
|
if (++this.consecutiveRemovalFailures > 5) {
|
|
@@ -2574,7 +2610,6 @@
|
|
|
2574
2610
|
}
|
|
2575
2611
|
logger.log('MIXPANEL REQUEST:', dataForRequest);
|
|
2576
2612
|
this.sendRequest(dataForRequest, requestOptions, batchSendCallback);
|
|
2577
|
-
|
|
2578
2613
|
} catch(err) {
|
|
2579
2614
|
this.reportError('Error flushing request queue', err);
|
|
2580
2615
|
this.resetFlush();
|
|
@@ -4108,6 +4143,12 @@
|
|
|
4108
4143
|
*/
|
|
4109
4144
|
|
|
4110
4145
|
var init_type; // MODULE or SNIPPET loader
|
|
4146
|
+
// allow bundlers to specify how extra code (recorder bundle) should be loaded
|
|
4147
|
+
// eslint-disable-next-line no-unused-vars
|
|
4148
|
+
var load_extra_bundle = function(src, _onload) {
|
|
4149
|
+
throw new Error(src + ' not available in this build.');
|
|
4150
|
+
};
|
|
4151
|
+
|
|
4111
4152
|
var mixpanel_master; // main mixpanel instance / object
|
|
4112
4153
|
var INIT_MODULE = 0;
|
|
4113
4154
|
var INIT_SNIPPET = 1;
|
|
@@ -4201,7 +4242,9 @@
|
|
|
4201
4242
|
'hooks': {},
|
|
4202
4243
|
'record_block_class': new RegExp('^(mp-block|fs-exclude|amp-block|rr-block|ph-no-capture)$'),
|
|
4203
4244
|
'record_block_selector': 'img, video',
|
|
4245
|
+
'record_collect_fonts': false,
|
|
4204
4246
|
'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
|
|
4247
|
+
'record_inline_images': false,
|
|
4205
4248
|
'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
|
|
4206
4249
|
'record_mask_text_selector': '*',
|
|
4207
4250
|
'record_max_ms': MAX_RECORDING_MS,
|
|
@@ -4437,12 +4480,7 @@
|
|
|
4437
4480
|
}, this);
|
|
4438
4481
|
|
|
4439
4482
|
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);
|
|
4483
|
+
load_extra_bundle(this.get_config('recorder_src'), handleLoadedRecorder);
|
|
4446
4484
|
} else {
|
|
4447
4485
|
handleLoadedRecorder();
|
|
4448
4486
|
}
|
|
@@ -4741,7 +4779,8 @@
|
|
|
4741
4779
|
lib.report_error(error);
|
|
4742
4780
|
if (callback) {
|
|
4743
4781
|
if (verbose_mode) {
|
|
4744
|
-
|
|
4782
|
+
var response_headers = req['responseHeaders'] || {};
|
|
4783
|
+
callback({status: 0, httpStatusCode: req['status'], error: error, retryAfter: response_headers['Retry-After']});
|
|
4745
4784
|
} else {
|
|
4746
4785
|
callback(0);
|
|
4747
4786
|
}
|
|
@@ -4841,6 +4880,7 @@
|
|
|
4841
4880
|
attrs.queue_key,
|
|
4842
4881
|
{
|
|
4843
4882
|
libConfig: this['config'],
|
|
4883
|
+
errorReporter: this.get_config('error_reporter'),
|
|
4844
4884
|
sendRequestFunc: _.bind(function(data, options, cb) {
|
|
4845
4885
|
this._send_request(
|
|
4846
4886
|
this.get_config('api_host') + attrs.endpoint,
|
|
@@ -4852,8 +4892,8 @@
|
|
|
4852
4892
|
beforeSendHook: _.bind(function(item) {
|
|
4853
4893
|
return this._run_hook('before_send_' + attrs.type, item);
|
|
4854
4894
|
}, this),
|
|
4855
|
-
|
|
4856
|
-
|
|
4895
|
+
stopAllBatchingFunc: _.bind(this.stop_batch_senders, this),
|
|
4896
|
+
usePersistence: true
|
|
4857
4897
|
}
|
|
4858
4898
|
);
|
|
4859
4899
|
}, this);
|
|
@@ -6302,7 +6342,8 @@
|
|
|
6302
6342
|
_.register_event(win, 'load', dom_loaded_handler, true);
|
|
6303
6343
|
};
|
|
6304
6344
|
|
|
6305
|
-
function init_from_snippet() {
|
|
6345
|
+
function init_from_snippet(bundle_loader) {
|
|
6346
|
+
load_extra_bundle = bundle_loader;
|
|
6306
6347
|
init_type = INIT_SNIPPET;
|
|
6307
6348
|
mixpanel_master = win[PRIMARY_INSTANCE_NAME];
|
|
6308
6349
|
|
|
@@ -6342,8 +6383,19 @@
|
|
|
6342
6383
|
add_dom_loaded_handler();
|
|
6343
6384
|
}
|
|
6344
6385
|
|
|
6386
|
+
// For loading separate bundles asynchronously via script tag
|
|
6387
|
+
// so that we don't load them until they are needed at runtime.
|
|
6388
|
+
function loadAsync (src, onload) {
|
|
6389
|
+
var scriptEl = document.createElement('script');
|
|
6390
|
+
scriptEl.type = 'text/javascript';
|
|
6391
|
+
scriptEl.async = true;
|
|
6392
|
+
scriptEl.onload = onload;
|
|
6393
|
+
scriptEl.src = src;
|
|
6394
|
+
document.head.appendChild(scriptEl);
|
|
6395
|
+
}
|
|
6396
|
+
|
|
6345
6397
|
/* eslint camelcase: "off" */
|
|
6346
6398
|
|
|
6347
|
-
init_from_snippet();
|
|
6399
|
+
init_from_snippet(loadAsync);
|
|
6348
6400
|
|
|
6349
6401
|
})();
|