mixpanel-browser 2.74.0 → 2.75.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/.github/workflows/unit-tests.yml +1 -1
- package/CHANGELOG.md +5 -0
- package/README.md +2 -2
- package/dist/mixpanel-core.cjs.js +318 -20
- package/dist/mixpanel-recorder.js +127 -15
- package/dist/mixpanel-recorder.min.js +1 -1
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-targeting.js +2576 -0
- package/dist/mixpanel-targeting.min.js +2 -0
- package/dist/mixpanel-targeting.min.js.map +1 -0
- package/dist/mixpanel-with-async-modules.cjs.d.ts +522 -0
- package/dist/mixpanel-with-async-modules.cjs.js +9700 -0
- package/dist/mixpanel-with-async-recorder.cjs.js +318 -20
- package/dist/mixpanel-with-recorder.js +435 -26
- package/dist/mixpanel-with-recorder.min.js +1 -1
- package/dist/mixpanel.amd.js +1020 -28
- package/dist/mixpanel.cjs.js +1020 -28
- package/dist/mixpanel.globals.js +318 -20
- package/dist/mixpanel.min.js +179 -172
- package/dist/mixpanel.module.js +1020 -28
- package/dist/mixpanel.umd.js +1020 -28
- package/dist/rrweb-bundled.js +119 -5
- package/dist/rrweb-compiled.js +116 -5
- package/package.json +4 -3
- package/rollup.config.mjs +34 -2
- package/src/config.js +1 -1
- package/src/flags/index.js +269 -8
- package/src/globals.js +14 -0
- package/src/loaders/loader-module.js +1 -0
- package/src/mixpanel-core.js +12 -3
- package/src/recorder/index.js +2 -1
- package/src/targeting/event-matcher.js +97 -0
- package/src/targeting/index.js +11 -0
- package/src/targeting/loader.js +36 -0
- package/src/utils.js +1 -8
- package/.claude/settings.local.json +0 -12
- /package/src/loaders/{loader-module-with-async-recorder.js → loader-module-with-async-modules.js} +0 -0
package/dist/mixpanel.amd.js
CHANGED
|
@@ -25,6 +25,16 @@ define((function () { 'use strict';
|
|
|
25
25
|
win = window;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Shared global window property names used across modules
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
// Targeting library global (used by flags and targeting modules)
|
|
33
|
+
var TARGETING_GLOBAL_NAME = '__mp_targeting';
|
|
34
|
+
|
|
35
|
+
// Recorder library global (used by recorder and mixpanel-core)
|
|
36
|
+
var RECORDER_GLOBAL_NAME = '__mp_recorder';
|
|
37
|
+
|
|
28
38
|
function _array_like_to_array(arr, len) {
|
|
29
39
|
if (len == null || len > arr.length) len = arr.length;
|
|
30
40
|
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
@@ -639,14 +649,16 @@ define((function () { 'use strict';
|
|
|
639
649
|
return this.nodeMetaMap.get(n2) || null;
|
|
640
650
|
};
|
|
641
651
|
// removes the node from idNodeMap
|
|
642
|
-
//
|
|
643
|
-
_proto.removeNodeFromMap = function removeNodeFromMap(n2) {
|
|
652
|
+
// if permanent is true, also removes from nodeMetaMap
|
|
653
|
+
_proto.removeNodeFromMap = function removeNodeFromMap(n2, permanent) {
|
|
644
654
|
var _this = this;
|
|
655
|
+
if (permanent === void 0) permanent = false;
|
|
645
656
|
var id = this.getId(n2);
|
|
646
657
|
this.idNodeMap.delete(id);
|
|
658
|
+
if (permanent) this.nodeMetaMap.delete(n2);
|
|
647
659
|
if (n2.childNodes) {
|
|
648
660
|
n2.childNodes.forEach(function(childNode) {
|
|
649
|
-
return _this.removeNodeFromMap(childNode);
|
|
661
|
+
return _this.removeNodeFromMap(childNode, permanent);
|
|
650
662
|
});
|
|
651
663
|
}
|
|
652
664
|
};
|
|
@@ -10388,6 +10400,15 @@ define((function () { 'use strict';
|
|
|
10388
10400
|
_proto.generateId = function generateId() {
|
|
10389
10401
|
return this.id++;
|
|
10390
10402
|
};
|
|
10403
|
+
_proto.remove = function remove(stylesheet) {
|
|
10404
|
+
var id = this.styleIDMap.get(stylesheet);
|
|
10405
|
+
if (id !== void 0) {
|
|
10406
|
+
this.styleIDMap.delete(stylesheet);
|
|
10407
|
+
this.idStyleMap.delete(id);
|
|
10408
|
+
return true;
|
|
10409
|
+
}
|
|
10410
|
+
return false;
|
|
10411
|
+
};
|
|
10391
10412
|
return StyleSheetMirror;
|
|
10392
10413
|
}();
|
|
10393
10414
|
function getShadowHost(n2) {
|
|
@@ -10710,7 +10731,15 @@ define((function () { 'use strict';
|
|
|
10710
10731
|
}
|
|
10711
10732
|
};
|
|
10712
10733
|
while(_this.mapRemoves.length){
|
|
10713
|
-
_this.
|
|
10734
|
+
var removedNode = _this.mapRemoves.shift();
|
|
10735
|
+
if (removedNode.nodeName === "IFRAME") {
|
|
10736
|
+
try {
|
|
10737
|
+
_this.iframeManager.removeIframe(removedNode);
|
|
10738
|
+
} catch (e2) {}
|
|
10739
|
+
} else {
|
|
10740
|
+
_this.stylesheetManager.cleanupStylesheetsForRemovedNode(removedNode);
|
|
10741
|
+
}
|
|
10742
|
+
_this.mirror.removeNodeFromMap(removedNode);
|
|
10714
10743
|
}
|
|
10715
10744
|
for(var _iterator = _create_for_of_iterator_helper_loose(_this.movedSet), _step; !(_step = _iterator()).done;){
|
|
10716
10745
|
var n2 = _step.value;
|
|
@@ -11084,6 +11113,9 @@ define((function () { 'use strict';
|
|
|
11084
11113
|
this.shadowDomManager.reset();
|
|
11085
11114
|
this.canvasManager.reset();
|
|
11086
11115
|
};
|
|
11116
|
+
_proto.getDoc = function getDoc() {
|
|
11117
|
+
return this.doc;
|
|
11118
|
+
};
|
|
11087
11119
|
return MutationBuffer;
|
|
11088
11120
|
}();
|
|
11089
11121
|
function deepDelete(addsSet, n2) {
|
|
@@ -11184,6 +11216,14 @@ define((function () { 'use strict';
|
|
|
11184
11216
|
});
|
|
11185
11217
|
return observer;
|
|
11186
11218
|
}
|
|
11219
|
+
function removeMutationBufferForDoc(doc) {
|
|
11220
|
+
for(var i2 = mutationBuffers.length - 1; i2 >= 0; i2--){
|
|
11221
|
+
var buffer = mutationBuffers[i2];
|
|
11222
|
+
if (buffer.getDoc() === doc) {
|
|
11223
|
+
mutationBuffers.splice(i2, 1);
|
|
11224
|
+
}
|
|
11225
|
+
}
|
|
11226
|
+
}
|
|
11187
11227
|
function initMoveObserver(param) {
|
|
11188
11228
|
var mousemoveCb = param.mousemoveCb, sampling = param.sampling, doc = param.doc, mirror2 = param.mirror;
|
|
11189
11229
|
if (sampling.mousemove === false) {
|
|
@@ -12199,6 +12239,8 @@ define((function () { 'use strict';
|
|
|
12199
12239
|
__publicField$1(this, "crossOriginIframeMirror", new CrossOriginIframeMirror(genId));
|
|
12200
12240
|
__publicField$1(this, "crossOriginIframeStyleMirror");
|
|
12201
12241
|
__publicField$1(this, "crossOriginIframeRootIdMap", /* @__PURE__ */ new WeakMap());
|
|
12242
|
+
__publicField$1(this, "iframeContentDocumentMap", /* @__PURE__ */ new WeakMap());
|
|
12243
|
+
__publicField$1(this, "iframeObserverCleanupMap", /* @__PURE__ */ new WeakMap());
|
|
12202
12244
|
__publicField$1(this, "mirror");
|
|
12203
12245
|
__publicField$1(this, "mutationCb");
|
|
12204
12246
|
__publicField$1(this, "wrappedEmit");
|
|
@@ -12220,6 +12262,31 @@ define((function () { 'use strict';
|
|
|
12220
12262
|
this.iframes.set(iframeEl, true);
|
|
12221
12263
|
if (iframeEl.contentWindow) this.crossOriginIframeMap.set(iframeEl.contentWindow, iframeEl);
|
|
12222
12264
|
};
|
|
12265
|
+
_proto.getIframeContentDocument = function getIframeContentDocument(iframeEl) {
|
|
12266
|
+
return this.iframeContentDocumentMap.get(iframeEl);
|
|
12267
|
+
};
|
|
12268
|
+
_proto.setObserverCleanup = function setObserverCleanup(iframeEl, cleanup) {
|
|
12269
|
+
this.iframeObserverCleanupMap.set(iframeEl, cleanup);
|
|
12270
|
+
};
|
|
12271
|
+
_proto.getObserverCleanup = function getObserverCleanup(iframeEl) {
|
|
12272
|
+
return this.iframeObserverCleanupMap.get(iframeEl);
|
|
12273
|
+
};
|
|
12274
|
+
_proto.removeIframe = function removeIframe(iframeEl) {
|
|
12275
|
+
var storedDoc = this.iframeContentDocumentMap.get(iframeEl);
|
|
12276
|
+
if (storedDoc) {
|
|
12277
|
+
this.stylesheetManager.cleanupStylesheetsForRemovedNode(storedDoc);
|
|
12278
|
+
this.mirror.removeNodeFromMap(storedDoc, true);
|
|
12279
|
+
}
|
|
12280
|
+
this.iframes.delete(iframeEl);
|
|
12281
|
+
this.iframeContentDocumentMap.delete(iframeEl);
|
|
12282
|
+
var observerCleanup = this.iframeObserverCleanupMap.get(iframeEl);
|
|
12283
|
+
if (observerCleanup) {
|
|
12284
|
+
try {
|
|
12285
|
+
observerCleanup();
|
|
12286
|
+
} catch (e2) {}
|
|
12287
|
+
this.iframeObserverCleanupMap.delete(iframeEl);
|
|
12288
|
+
}
|
|
12289
|
+
};
|
|
12223
12290
|
_proto.addLoadListener = function addLoadListener(cb) {
|
|
12224
12291
|
this.loadListener = cb;
|
|
12225
12292
|
};
|
|
@@ -12238,6 +12305,9 @@ define((function () { 'use strict';
|
|
|
12238
12305
|
attributes: [],
|
|
12239
12306
|
isAttachIframe: true
|
|
12240
12307
|
});
|
|
12308
|
+
if (iframeEl.contentDocument) {
|
|
12309
|
+
this.iframeContentDocumentMap.set(iframeEl, iframeEl.contentDocument);
|
|
12310
|
+
}
|
|
12241
12311
|
if (this.recordCrossOriginIframes) (_a2 = iframeEl.contentWindow) == null ? void 0 : _a2.addEventListener("message", this.handleMessage.bind(this));
|
|
12242
12312
|
(_b = this.loadListener) == null ? void 0 : _b.call(this, iframeEl);
|
|
12243
12313
|
if (iframeEl.contentDocument && iframeEl.contentDocument.adoptedStyleSheets && iframeEl.contentDocument.adoptedStyleSheets.length > 0) this.stylesheetManager.adoptStyleSheets(iframeEl.contentDocument.adoptedStyleSheets, this.mirror.getId(iframeEl.contentDocument));
|
|
@@ -13150,6 +13220,41 @@ define((function () { 'use strict';
|
|
|
13150
13220
|
this.styleMirror.reset();
|
|
13151
13221
|
this.trackedLinkElements = /* @__PURE__ */ new WeakSet();
|
|
13152
13222
|
};
|
|
13223
|
+
/**
|
|
13224
|
+
* Cleans up stylesheets associated with a removed node.
|
|
13225
|
+
*
|
|
13226
|
+
* @param removedNode - The node that was removed from the DOM.
|
|
13227
|
+
*/ _proto.cleanupStylesheetsForRemovedNode = function cleanupStylesheetsForRemovedNode(removedNode) {
|
|
13228
|
+
var _this = this;
|
|
13229
|
+
try {
|
|
13230
|
+
if (removedNode.nodeType === Node.DOCUMENT_NODE) {
|
|
13231
|
+
var doc = removedNode;
|
|
13232
|
+
if (doc.adoptedStyleSheets) {
|
|
13233
|
+
for(var _iterator = _create_for_of_iterator_helper_loose(doc.adoptedStyleSheets), _step; !(_step = _iterator()).done;){
|
|
13234
|
+
var sheet = _step.value;
|
|
13235
|
+
this.styleMirror.remove(sheet);
|
|
13236
|
+
}
|
|
13237
|
+
}
|
|
13238
|
+
}
|
|
13239
|
+
if (removedNode.nodeName === "STYLE") {
|
|
13240
|
+
var styleEl = removedNode;
|
|
13241
|
+
if (styleEl.sheet) {
|
|
13242
|
+
this.styleMirror.remove(styleEl.sheet);
|
|
13243
|
+
}
|
|
13244
|
+
}
|
|
13245
|
+
if (removedNode.nodeName === "LINK" && removedNode.rel === "stylesheet") {
|
|
13246
|
+
var linkEl = removedNode;
|
|
13247
|
+
if (linkEl.sheet) {
|
|
13248
|
+
this.styleMirror.remove(linkEl.sheet);
|
|
13249
|
+
}
|
|
13250
|
+
}
|
|
13251
|
+
if (removedNode.childNodes) {
|
|
13252
|
+
removedNode.childNodes.forEach(function(child) {
|
|
13253
|
+
_this.cleanupStylesheetsForRemovedNode(child);
|
|
13254
|
+
});
|
|
13255
|
+
}
|
|
13256
|
+
} catch (e2) {}
|
|
13257
|
+
};
|
|
13153
13258
|
// TODO: take snapshot on stylesheet reload by applying event listener
|
|
13154
13259
|
_proto.trackStylesheetInLinkElement = function trackStylesheetInLinkElement(_linkEl) {};
|
|
13155
13260
|
return StylesheetManager;
|
|
@@ -13604,7 +13709,23 @@ define((function () { 'use strict';
|
|
|
13604
13709
|
};
|
|
13605
13710
|
iframeManager.addLoadListener(function(iframeEl) {
|
|
13606
13711
|
try {
|
|
13607
|
-
|
|
13712
|
+
var iframeDoc = iframeEl.contentDocument;
|
|
13713
|
+
var iframeHandler = observe(iframeDoc);
|
|
13714
|
+
handlers.push(iframeHandler);
|
|
13715
|
+
var existingCleanup = iframeManager.getObserverCleanup(iframeEl);
|
|
13716
|
+
iframeManager.setObserverCleanup(iframeEl, function() {
|
|
13717
|
+
if (existingCleanup) {
|
|
13718
|
+
try {
|
|
13719
|
+
existingCleanup();
|
|
13720
|
+
} catch (e2) {}
|
|
13721
|
+
}
|
|
13722
|
+
try {
|
|
13723
|
+
iframeHandler();
|
|
13724
|
+
var idx = handlers.indexOf(iframeHandler);
|
|
13725
|
+
if (idx !== -1) handlers.splice(idx, 1);
|
|
13726
|
+
removeMutationBufferForDoc(iframeDoc);
|
|
13727
|
+
} catch (e2) {}
|
|
13728
|
+
});
|
|
13608
13729
|
} catch (error) {
|
|
13609
13730
|
console.warn(error);
|
|
13610
13731
|
}
|
|
@@ -13861,7 +13982,7 @@ define((function () { 'use strict';
|
|
|
13861
13982
|
}
|
|
13862
13983
|
return classMatchesRegex(index.parentNode(node2), regex);
|
|
13863
13984
|
}
|
|
13864
|
-
function getDefaultExportFromCjs(x2) {
|
|
13985
|
+
function getDefaultExportFromCjs$3(x2) {
|
|
13865
13986
|
return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
|
|
13866
13987
|
}
|
|
13867
13988
|
function getAugmentedNamespace(n) {
|
|
@@ -17976,7 +18097,7 @@ define((function () { 'use strict';
|
|
|
17976
18097
|
LazyResult2.registerPostcss(postcss);
|
|
17977
18098
|
var postcss_1 = postcss;
|
|
17978
18099
|
postcss.default = postcss;
|
|
17979
|
-
var postcss$1 = /* @__PURE__ */ getDefaultExportFromCjs(postcss_1);
|
|
18100
|
+
var postcss$1 = /* @__PURE__ */ getDefaultExportFromCjs$3(postcss_1);
|
|
17980
18101
|
postcss$1.stringify;
|
|
17981
18102
|
postcss$1.fromJSON;
|
|
17982
18103
|
postcss$1.plugin;
|
|
@@ -18853,7 +18974,7 @@ define((function () { 'use strict';
|
|
|
18853
18974
|
|
|
18854
18975
|
var Config = {
|
|
18855
18976
|
DEBUG: false,
|
|
18856
|
-
LIB_VERSION: '2.
|
|
18977
|
+
LIB_VERSION: '2.75.0'
|
|
18857
18978
|
};
|
|
18858
18979
|
|
|
18859
18980
|
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
@@ -19059,15 +19180,8 @@ define((function () { 'use strict';
|
|
|
19059
19180
|
return toString.call(obj) === '[object Array]';
|
|
19060
19181
|
};
|
|
19061
19182
|
|
|
19062
|
-
// from a comment on http://dbj.org/dbj/?p=286
|
|
19063
|
-
// fails on only one very rare and deliberate custom object:
|
|
19064
|
-
// var bomb = { toString : undefined, valueOf: function(o) { return "function BOMBA!"; }};
|
|
19065
19183
|
_.isFunction = function(f) {
|
|
19066
|
-
|
|
19067
|
-
return /^\s*\bfunction\b/.test(f);
|
|
19068
|
-
} catch (x) {
|
|
19069
|
-
return false;
|
|
19070
|
-
}
|
|
19184
|
+
return typeof f === 'function';
|
|
19071
19185
|
};
|
|
19072
19186
|
|
|
19073
19187
|
_.isArguments = function(obj) {
|
|
@@ -23761,7 +23875,590 @@ define((function () { 'use strict';
|
|
|
23761
23875
|
}
|
|
23762
23876
|
});
|
|
23763
23877
|
|
|
23764
|
-
win[
|
|
23878
|
+
win[RECORDER_GLOBAL_NAME] = MixpanelRecorder;
|
|
23879
|
+
|
|
23880
|
+
function getDefaultExportFromCjs (x) {
|
|
23881
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
23882
|
+
}
|
|
23883
|
+
|
|
23884
|
+
var logic$1 = {exports: {}};
|
|
23885
|
+
|
|
23886
|
+
/* globals define,module */
|
|
23887
|
+
var logic = logic$1.exports;
|
|
23888
|
+
|
|
23889
|
+
var hasRequiredLogic;
|
|
23890
|
+
|
|
23891
|
+
function requireLogic () {
|
|
23892
|
+
if (hasRequiredLogic) return logic$1.exports;
|
|
23893
|
+
hasRequiredLogic = 1;
|
|
23894
|
+
(function (module, exports) {
|
|
23895
|
+
(function(root, factory) {
|
|
23896
|
+
{
|
|
23897
|
+
module.exports = factory();
|
|
23898
|
+
}
|
|
23899
|
+
}(logic, function() {
|
|
23900
|
+
/* globals console:false */
|
|
23901
|
+
|
|
23902
|
+
if ( ! Array.isArray) {
|
|
23903
|
+
Array.isArray = function(arg) {
|
|
23904
|
+
return Object.prototype.toString.call(arg) === "[object Array]";
|
|
23905
|
+
};
|
|
23906
|
+
}
|
|
23907
|
+
|
|
23908
|
+
/**
|
|
23909
|
+
* Return an array that contains no duplicates (original not modified)
|
|
23910
|
+
* @param {array} array Original reference array
|
|
23911
|
+
* @return {array} New array with no duplicates
|
|
23912
|
+
*/
|
|
23913
|
+
function arrayUnique(array) {
|
|
23914
|
+
var a = [];
|
|
23915
|
+
for (var i=0, l=array.length; i<l; i++) {
|
|
23916
|
+
if (a.indexOf(array[i]) === -1) {
|
|
23917
|
+
a.push(array[i]);
|
|
23918
|
+
}
|
|
23919
|
+
}
|
|
23920
|
+
return a;
|
|
23921
|
+
}
|
|
23922
|
+
|
|
23923
|
+
var jsonLogic = {};
|
|
23924
|
+
var operations = {
|
|
23925
|
+
"==": function(a, b) {
|
|
23926
|
+
return a == b;
|
|
23927
|
+
},
|
|
23928
|
+
"===": function(a, b) {
|
|
23929
|
+
return a === b;
|
|
23930
|
+
},
|
|
23931
|
+
"!=": function(a, b) {
|
|
23932
|
+
return a != b;
|
|
23933
|
+
},
|
|
23934
|
+
"!==": function(a, b) {
|
|
23935
|
+
return a !== b;
|
|
23936
|
+
},
|
|
23937
|
+
">": function(a, b) {
|
|
23938
|
+
return a > b;
|
|
23939
|
+
},
|
|
23940
|
+
">=": function(a, b) {
|
|
23941
|
+
return a >= b;
|
|
23942
|
+
},
|
|
23943
|
+
"<": function(a, b, c) {
|
|
23944
|
+
return (c === undefined) ? a < b : (a < b) && (b < c);
|
|
23945
|
+
},
|
|
23946
|
+
"<=": function(a, b, c) {
|
|
23947
|
+
return (c === undefined) ? a <= b : (a <= b) && (b <= c);
|
|
23948
|
+
},
|
|
23949
|
+
"!!": function(a) {
|
|
23950
|
+
return jsonLogic.truthy(a);
|
|
23951
|
+
},
|
|
23952
|
+
"!": function(a) {
|
|
23953
|
+
return !jsonLogic.truthy(a);
|
|
23954
|
+
},
|
|
23955
|
+
"%": function(a, b) {
|
|
23956
|
+
return a % b;
|
|
23957
|
+
},
|
|
23958
|
+
"log": function(a) {
|
|
23959
|
+
console.log(a); return a;
|
|
23960
|
+
},
|
|
23961
|
+
"in": function(a, b) {
|
|
23962
|
+
if (!b || typeof b.indexOf === "undefined") return false;
|
|
23963
|
+
return (b.indexOf(a) !== -1);
|
|
23964
|
+
},
|
|
23965
|
+
"cat": function() {
|
|
23966
|
+
return Array.prototype.join.call(arguments, "");
|
|
23967
|
+
},
|
|
23968
|
+
"substr": function(source, start, end) {
|
|
23969
|
+
if (end < 0) {
|
|
23970
|
+
// JavaScript doesn't support negative end, this emulates PHP behavior
|
|
23971
|
+
var temp = String(source).substr(start);
|
|
23972
|
+
return temp.substr(0, temp.length + end);
|
|
23973
|
+
}
|
|
23974
|
+
return String(source).substr(start, end);
|
|
23975
|
+
},
|
|
23976
|
+
"+": function() {
|
|
23977
|
+
return Array.prototype.reduce.call(arguments, function(a, b) {
|
|
23978
|
+
return parseFloat(a, 10) + parseFloat(b, 10);
|
|
23979
|
+
}, 0);
|
|
23980
|
+
},
|
|
23981
|
+
"*": function() {
|
|
23982
|
+
return Array.prototype.reduce.call(arguments, function(a, b) {
|
|
23983
|
+
return parseFloat(a, 10) * parseFloat(b, 10);
|
|
23984
|
+
});
|
|
23985
|
+
},
|
|
23986
|
+
"-": function(a, b) {
|
|
23987
|
+
if (b === undefined) {
|
|
23988
|
+
return -a;
|
|
23989
|
+
} else {
|
|
23990
|
+
return a - b;
|
|
23991
|
+
}
|
|
23992
|
+
},
|
|
23993
|
+
"/": function(a, b) {
|
|
23994
|
+
return a / b;
|
|
23995
|
+
},
|
|
23996
|
+
"min": function() {
|
|
23997
|
+
return Math.min.apply(this, arguments);
|
|
23998
|
+
},
|
|
23999
|
+
"max": function() {
|
|
24000
|
+
return Math.max.apply(this, arguments);
|
|
24001
|
+
},
|
|
24002
|
+
"merge": function() {
|
|
24003
|
+
return Array.prototype.reduce.call(arguments, function(a, b) {
|
|
24004
|
+
return a.concat(b);
|
|
24005
|
+
}, []);
|
|
24006
|
+
},
|
|
24007
|
+
"var": function(a, b) {
|
|
24008
|
+
var not_found = (b === undefined) ? null : b;
|
|
24009
|
+
var data = this;
|
|
24010
|
+
if (typeof a === "undefined" || a==="" || a===null) {
|
|
24011
|
+
return data;
|
|
24012
|
+
}
|
|
24013
|
+
var sub_props = String(a).split(".");
|
|
24014
|
+
for (var i = 0; i < sub_props.length; i++) {
|
|
24015
|
+
if (data === null || data === undefined) {
|
|
24016
|
+
return not_found;
|
|
24017
|
+
}
|
|
24018
|
+
// Descending into data
|
|
24019
|
+
data = data[sub_props[i]];
|
|
24020
|
+
if (data === undefined) {
|
|
24021
|
+
return not_found;
|
|
24022
|
+
}
|
|
24023
|
+
}
|
|
24024
|
+
return data;
|
|
24025
|
+
},
|
|
24026
|
+
"missing": function() {
|
|
24027
|
+
/*
|
|
24028
|
+
Missing can receive many keys as many arguments, like {"missing:[1,2]}
|
|
24029
|
+
Missing can also receive *one* argument that is an array of keys,
|
|
24030
|
+
which typically happens if it's actually acting on the output of another command
|
|
24031
|
+
(like 'if' or 'merge')
|
|
24032
|
+
*/
|
|
24033
|
+
|
|
24034
|
+
var missing = [];
|
|
24035
|
+
var keys = Array.isArray(arguments[0]) ? arguments[0] : arguments;
|
|
24036
|
+
|
|
24037
|
+
for (var i = 0; i < keys.length; i++) {
|
|
24038
|
+
var key = keys[i];
|
|
24039
|
+
var value = jsonLogic.apply({"var": key}, this);
|
|
24040
|
+
if (value === null || value === "") {
|
|
24041
|
+
missing.push(key);
|
|
24042
|
+
}
|
|
24043
|
+
}
|
|
24044
|
+
|
|
24045
|
+
return missing;
|
|
24046
|
+
},
|
|
24047
|
+
"missing_some": function(need_count, options) {
|
|
24048
|
+
// missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence.
|
|
24049
|
+
var are_missing = jsonLogic.apply({"missing": options}, this);
|
|
24050
|
+
|
|
24051
|
+
if (options.length - are_missing.length >= need_count) {
|
|
24052
|
+
return [];
|
|
24053
|
+
} else {
|
|
24054
|
+
return are_missing;
|
|
24055
|
+
}
|
|
24056
|
+
},
|
|
24057
|
+
};
|
|
24058
|
+
|
|
24059
|
+
jsonLogic.is_logic = function(logic) {
|
|
24060
|
+
return (
|
|
24061
|
+
typeof logic === "object" && // An object
|
|
24062
|
+
logic !== null && // but not null
|
|
24063
|
+
! Array.isArray(logic) && // and not an array
|
|
24064
|
+
Object.keys(logic).length === 1 // with exactly one key
|
|
24065
|
+
);
|
|
24066
|
+
};
|
|
24067
|
+
|
|
24068
|
+
/*
|
|
24069
|
+
This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer.
|
|
24070
|
+
|
|
24071
|
+
Spec and rationale here: http://jsonlogic.com/truthy
|
|
24072
|
+
*/
|
|
24073
|
+
jsonLogic.truthy = function(value) {
|
|
24074
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
24075
|
+
return false;
|
|
24076
|
+
}
|
|
24077
|
+
return !! value;
|
|
24078
|
+
};
|
|
24079
|
+
|
|
24080
|
+
|
|
24081
|
+
jsonLogic.get_operator = function(logic) {
|
|
24082
|
+
return Object.keys(logic)[0];
|
|
24083
|
+
};
|
|
24084
|
+
|
|
24085
|
+
jsonLogic.get_values = function(logic) {
|
|
24086
|
+
return logic[jsonLogic.get_operator(logic)];
|
|
24087
|
+
};
|
|
24088
|
+
|
|
24089
|
+
jsonLogic.apply = function(logic, data) {
|
|
24090
|
+
// Does this array contain logic? Only one way to find out.
|
|
24091
|
+
if (Array.isArray(logic)) {
|
|
24092
|
+
return logic.map(function(l) {
|
|
24093
|
+
return jsonLogic.apply(l, data);
|
|
24094
|
+
});
|
|
24095
|
+
}
|
|
24096
|
+
// You've recursed to a primitive, stop!
|
|
24097
|
+
if ( ! jsonLogic.is_logic(logic) ) {
|
|
24098
|
+
return logic;
|
|
24099
|
+
}
|
|
24100
|
+
|
|
24101
|
+
var op = jsonLogic.get_operator(logic);
|
|
24102
|
+
var values = logic[op];
|
|
24103
|
+
var i;
|
|
24104
|
+
var current;
|
|
24105
|
+
var scopedLogic;
|
|
24106
|
+
var scopedData;
|
|
24107
|
+
var initial;
|
|
24108
|
+
|
|
24109
|
+
// easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]}
|
|
24110
|
+
if ( ! Array.isArray(values)) {
|
|
24111
|
+
values = [values];
|
|
24112
|
+
}
|
|
24113
|
+
|
|
24114
|
+
// 'if', 'and', and 'or' violate the normal rule of depth-first calculating consequents, let each manage recursion as needed.
|
|
24115
|
+
if (op === "if" || op == "?:") {
|
|
24116
|
+
/* 'if' should be called with a odd number of parameters, 3 or greater
|
|
24117
|
+
This works on the pattern:
|
|
24118
|
+
if( 0 ){ 1 }else{ 2 };
|
|
24119
|
+
if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };
|
|
24120
|
+
if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };
|
|
24121
|
+
|
|
24122
|
+
The implementation is:
|
|
24123
|
+
For pairs of values (0,1 then 2,3 then 4,5 etc)
|
|
24124
|
+
If the first evaluates truthy, evaluate and return the second
|
|
24125
|
+
If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)
|
|
24126
|
+
given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)
|
|
24127
|
+
given 0 parameters, return NULL (not great practice, but there was no Else)
|
|
24128
|
+
*/
|
|
24129
|
+
for (i = 0; i < values.length - 1; i += 2) {
|
|
24130
|
+
if ( jsonLogic.truthy( jsonLogic.apply(values[i], data) ) ) {
|
|
24131
|
+
return jsonLogic.apply(values[i+1], data);
|
|
24132
|
+
}
|
|
24133
|
+
}
|
|
24134
|
+
if (values.length === i+1) {
|
|
24135
|
+
return jsonLogic.apply(values[i], data);
|
|
24136
|
+
}
|
|
24137
|
+
return null;
|
|
24138
|
+
} else if (op === "and") { // Return first falsy, or last
|
|
24139
|
+
for (i=0; i < values.length; i+=1) {
|
|
24140
|
+
current = jsonLogic.apply(values[i], data);
|
|
24141
|
+
if ( ! jsonLogic.truthy(current)) {
|
|
24142
|
+
return current;
|
|
24143
|
+
}
|
|
24144
|
+
}
|
|
24145
|
+
return current; // Last
|
|
24146
|
+
} else if (op === "or") {// Return first truthy, or last
|
|
24147
|
+
for (i=0; i < values.length; i+=1) {
|
|
24148
|
+
current = jsonLogic.apply(values[i], data);
|
|
24149
|
+
if ( jsonLogic.truthy(current) ) {
|
|
24150
|
+
return current;
|
|
24151
|
+
}
|
|
24152
|
+
}
|
|
24153
|
+
return current; // Last
|
|
24154
|
+
} else if (op === "filter") {
|
|
24155
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24156
|
+
scopedLogic = values[1];
|
|
24157
|
+
|
|
24158
|
+
if ( ! Array.isArray(scopedData)) {
|
|
24159
|
+
return [];
|
|
24160
|
+
}
|
|
24161
|
+
// Return only the elements from the array in the first argument,
|
|
24162
|
+
// that return truthy when passed to the logic in the second argument.
|
|
24163
|
+
// For parity with JavaScript, reindex the returned array
|
|
24164
|
+
return scopedData.filter(function(datum) {
|
|
24165
|
+
return jsonLogic.truthy( jsonLogic.apply(scopedLogic, datum));
|
|
24166
|
+
});
|
|
24167
|
+
} else if (op === "map") {
|
|
24168
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24169
|
+
scopedLogic = values[1];
|
|
24170
|
+
|
|
24171
|
+
if ( ! Array.isArray(scopedData)) {
|
|
24172
|
+
return [];
|
|
24173
|
+
}
|
|
24174
|
+
|
|
24175
|
+
return scopedData.map(function(datum) {
|
|
24176
|
+
return jsonLogic.apply(scopedLogic, datum);
|
|
24177
|
+
});
|
|
24178
|
+
} else if (op === "reduce") {
|
|
24179
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24180
|
+
scopedLogic = values[1];
|
|
24181
|
+
initial = typeof values[2] !== "undefined" ? jsonLogic.apply(values[2], data) : null;
|
|
24182
|
+
|
|
24183
|
+
if ( ! Array.isArray(scopedData)) {
|
|
24184
|
+
return initial;
|
|
24185
|
+
}
|
|
24186
|
+
|
|
24187
|
+
return scopedData.reduce(
|
|
24188
|
+
function(accumulator, current) {
|
|
24189
|
+
return jsonLogic.apply(
|
|
24190
|
+
scopedLogic,
|
|
24191
|
+
{current: current, accumulator: accumulator}
|
|
24192
|
+
);
|
|
24193
|
+
},
|
|
24194
|
+
initial
|
|
24195
|
+
);
|
|
24196
|
+
} else if (op === "all") {
|
|
24197
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24198
|
+
scopedLogic = values[1];
|
|
24199
|
+
// All of an empty set is false. Note, some and none have correct fallback after the for loop
|
|
24200
|
+
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
|
|
24201
|
+
return false;
|
|
24202
|
+
}
|
|
24203
|
+
for (i=0; i < scopedData.length; i+=1) {
|
|
24204
|
+
if ( ! jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) {
|
|
24205
|
+
return false; // First falsy, short circuit
|
|
24206
|
+
}
|
|
24207
|
+
}
|
|
24208
|
+
return true; // All were truthy
|
|
24209
|
+
} else if (op === "none") {
|
|
24210
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24211
|
+
scopedLogic = values[1];
|
|
24212
|
+
|
|
24213
|
+
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
|
|
24214
|
+
return true;
|
|
24215
|
+
}
|
|
24216
|
+
for (i=0; i < scopedData.length; i+=1) {
|
|
24217
|
+
if ( jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) {
|
|
24218
|
+
return false; // First truthy, short circuit
|
|
24219
|
+
}
|
|
24220
|
+
}
|
|
24221
|
+
return true; // None were truthy
|
|
24222
|
+
} else if (op === "some") {
|
|
24223
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
24224
|
+
scopedLogic = values[1];
|
|
24225
|
+
|
|
24226
|
+
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
|
|
24227
|
+
return false;
|
|
24228
|
+
}
|
|
24229
|
+
for (i=0; i < scopedData.length; i+=1) {
|
|
24230
|
+
if ( jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) {
|
|
24231
|
+
return true; // First truthy, short circuit
|
|
24232
|
+
}
|
|
24233
|
+
}
|
|
24234
|
+
return false; // None were truthy
|
|
24235
|
+
}
|
|
24236
|
+
|
|
24237
|
+
// Everyone else gets immediate depth-first recursion
|
|
24238
|
+
values = values.map(function(val) {
|
|
24239
|
+
return jsonLogic.apply(val, data);
|
|
24240
|
+
});
|
|
24241
|
+
|
|
24242
|
+
|
|
24243
|
+
// The operation is called with "data" bound to its "this" and "values" passed as arguments.
|
|
24244
|
+
// Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments
|
|
24245
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
|
|
24246
|
+
if (operations.hasOwnProperty(op) && typeof operations[op] === "function") {
|
|
24247
|
+
return operations[op].apply(data, values);
|
|
24248
|
+
} else if (op.indexOf(".") > 0) { // Contains a dot, and not in the 0th position
|
|
24249
|
+
var sub_ops = String(op).split(".");
|
|
24250
|
+
var operation = operations;
|
|
24251
|
+
for (i = 0; i < sub_ops.length; i++) {
|
|
24252
|
+
if (!operation.hasOwnProperty(sub_ops[i])) {
|
|
24253
|
+
throw new Error("Unrecognized operation " + op +
|
|
24254
|
+
" (failed at " + sub_ops.slice(0, i+1).join(".") + ")");
|
|
24255
|
+
}
|
|
24256
|
+
// Descending into operations
|
|
24257
|
+
operation = operation[sub_ops[i]];
|
|
24258
|
+
}
|
|
24259
|
+
|
|
24260
|
+
return operation.apply(data, values);
|
|
24261
|
+
}
|
|
24262
|
+
|
|
24263
|
+
throw new Error("Unrecognized operation " + op );
|
|
24264
|
+
};
|
|
24265
|
+
|
|
24266
|
+
jsonLogic.uses_data = function(logic) {
|
|
24267
|
+
var collection = [];
|
|
24268
|
+
|
|
24269
|
+
if (jsonLogic.is_logic(logic)) {
|
|
24270
|
+
var op = jsonLogic.get_operator(logic);
|
|
24271
|
+
var values = logic[op];
|
|
24272
|
+
|
|
24273
|
+
if ( ! Array.isArray(values)) {
|
|
24274
|
+
values = [values];
|
|
24275
|
+
}
|
|
24276
|
+
|
|
24277
|
+
if (op === "var") {
|
|
24278
|
+
// This doesn't cover the case where the arg to var is itself a rule.
|
|
24279
|
+
collection.push(values[0]);
|
|
24280
|
+
} else {
|
|
24281
|
+
// Recursion!
|
|
24282
|
+
values.forEach(function(val) {
|
|
24283
|
+
collection.push.apply(collection, jsonLogic.uses_data(val) );
|
|
24284
|
+
});
|
|
24285
|
+
}
|
|
24286
|
+
}
|
|
24287
|
+
|
|
24288
|
+
return arrayUnique(collection);
|
|
24289
|
+
};
|
|
24290
|
+
|
|
24291
|
+
jsonLogic.add_operation = function(name, code) {
|
|
24292
|
+
operations[name] = code;
|
|
24293
|
+
};
|
|
24294
|
+
|
|
24295
|
+
jsonLogic.rm_operation = function(name) {
|
|
24296
|
+
delete operations[name];
|
|
24297
|
+
};
|
|
24298
|
+
|
|
24299
|
+
jsonLogic.rule_like = function(rule, pattern) {
|
|
24300
|
+
// console.log("Is ". JSON.stringify(rule) . " like " . JSON.stringify(pattern) . "?");
|
|
24301
|
+
if (pattern === rule) {
|
|
24302
|
+
return true;
|
|
24303
|
+
} // TODO : Deep object equivalency?
|
|
24304
|
+
if (pattern === "@") {
|
|
24305
|
+
return true;
|
|
24306
|
+
} // Wildcard!
|
|
24307
|
+
if (pattern === "number") {
|
|
24308
|
+
return (typeof rule === "number");
|
|
24309
|
+
}
|
|
24310
|
+
if (pattern === "string") {
|
|
24311
|
+
return (typeof rule === "string");
|
|
24312
|
+
}
|
|
24313
|
+
if (pattern === "array") {
|
|
24314
|
+
// !logic test might be superfluous in JavaScript
|
|
24315
|
+
return Array.isArray(rule) && ! jsonLogic.is_logic(rule);
|
|
24316
|
+
}
|
|
24317
|
+
|
|
24318
|
+
if (jsonLogic.is_logic(pattern)) {
|
|
24319
|
+
if (jsonLogic.is_logic(rule)) {
|
|
24320
|
+
var pattern_op = jsonLogic.get_operator(pattern);
|
|
24321
|
+
var rule_op = jsonLogic.get_operator(rule);
|
|
24322
|
+
|
|
24323
|
+
if (pattern_op === "@" || pattern_op === rule_op) {
|
|
24324
|
+
// echo "\nOperators match, go deeper\n";
|
|
24325
|
+
return jsonLogic.rule_like(
|
|
24326
|
+
jsonLogic.get_values(rule, false),
|
|
24327
|
+
jsonLogic.get_values(pattern, false)
|
|
24328
|
+
);
|
|
24329
|
+
}
|
|
24330
|
+
}
|
|
24331
|
+
return false; // pattern is logic, rule isn't, can't be eq
|
|
24332
|
+
}
|
|
24333
|
+
|
|
24334
|
+
if (Array.isArray(pattern)) {
|
|
24335
|
+
if (Array.isArray(rule)) {
|
|
24336
|
+
if (pattern.length !== rule.length) {
|
|
24337
|
+
return false;
|
|
24338
|
+
}
|
|
24339
|
+
/*
|
|
24340
|
+
Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT)
|
|
24341
|
+
*/
|
|
24342
|
+
for (var i = 0; i < pattern.length; i += 1) {
|
|
24343
|
+
// If any fail, we fail
|
|
24344
|
+
if ( ! jsonLogic.rule_like(rule[i], pattern[i])) {
|
|
24345
|
+
return false;
|
|
24346
|
+
}
|
|
24347
|
+
}
|
|
24348
|
+
return true; // If they *all* passed, we pass
|
|
24349
|
+
} else {
|
|
24350
|
+
return false; // Pattern is array, rule isn't
|
|
24351
|
+
}
|
|
24352
|
+
}
|
|
24353
|
+
|
|
24354
|
+
// Not logic, not array, not a === match for rule.
|
|
24355
|
+
return false;
|
|
24356
|
+
};
|
|
24357
|
+
|
|
24358
|
+
return jsonLogic;
|
|
24359
|
+
}));
|
|
24360
|
+
} (logic$1));
|
|
24361
|
+
return logic$1.exports;
|
|
24362
|
+
}
|
|
24363
|
+
|
|
24364
|
+
var logicExports = requireLogic();
|
|
24365
|
+
var jsonLogic = /*@__PURE__*/getDefaultExportFromCjs(logicExports);
|
|
24366
|
+
|
|
24367
|
+
/**
|
|
24368
|
+
* Shared helper to recursively lowercase strings in nested structures
|
|
24369
|
+
* @param {*} obj - Value to process
|
|
24370
|
+
* @param {boolean} lowercaseKeys - Whether to lowercase object keys
|
|
24371
|
+
* @returns {*} Processed value with lowercased strings
|
|
24372
|
+
*/
|
|
24373
|
+
var lowercaseJson = function(obj, lowercaseKeys) {
|
|
24374
|
+
if (obj === null || obj === undefined) {
|
|
24375
|
+
return obj;
|
|
24376
|
+
} else if (typeof obj === 'string') {
|
|
24377
|
+
return obj.toLowerCase();
|
|
24378
|
+
} else if (Array.isArray(obj)) {
|
|
24379
|
+
return obj.map(function(item) {
|
|
24380
|
+
return lowercaseJson(item, lowercaseKeys);
|
|
24381
|
+
});
|
|
24382
|
+
} else if (obj === Object(obj)) {
|
|
24383
|
+
var result = {};
|
|
24384
|
+
for (var key in obj) {
|
|
24385
|
+
if (obj.hasOwnProperty(key)) {
|
|
24386
|
+
var newKey = lowercaseKeys && typeof key === 'string' ? key.toLowerCase() : key;
|
|
24387
|
+
result[newKey] = lowercaseJson(obj[key], lowercaseKeys);
|
|
24388
|
+
}
|
|
24389
|
+
}
|
|
24390
|
+
return result;
|
|
24391
|
+
} else {
|
|
24392
|
+
return obj;
|
|
24393
|
+
}
|
|
24394
|
+
};
|
|
24395
|
+
|
|
24396
|
+
/**
|
|
24397
|
+
* Lowercase all string keys and values in a nested structure
|
|
24398
|
+
* @param {*} val - Value to process
|
|
24399
|
+
* @returns {*} Processed value with lowercased strings
|
|
24400
|
+
*/
|
|
24401
|
+
var lowercaseKeysAndValues = function(val) {
|
|
24402
|
+
return lowercaseJson(val, true);
|
|
24403
|
+
};
|
|
24404
|
+
|
|
24405
|
+
/**
|
|
24406
|
+
* Lowercase only leaf node string values in a nested structure (keys unchanged)
|
|
24407
|
+
* @param {*} val - Value to process
|
|
24408
|
+
* @returns {*} Processed value with lowercased leaf strings
|
|
24409
|
+
*/
|
|
24410
|
+
var lowercaseOnlyLeafNodes = function(val) {
|
|
24411
|
+
return lowercaseJson(val, false);
|
|
24412
|
+
};
|
|
24413
|
+
|
|
24414
|
+
/**
|
|
24415
|
+
* Check if an event matches the given criteria
|
|
24416
|
+
* @param {string} eventName - The name of the event being checked
|
|
24417
|
+
* @param {Object} properties - Event properties to evaluate against property filters
|
|
24418
|
+
* @param {Object} criteria - Criteria to match against, with:
|
|
24419
|
+
* - event_name: string - Required event name (case-sensitive match)
|
|
24420
|
+
* - property_filters: Object - Optional JsonLogic filters for properties
|
|
24421
|
+
* @returns {Object} Result object with:
|
|
24422
|
+
* - matches: boolean - Whether the event matches the criteria
|
|
24423
|
+
* - error: string|undefined - Error message if evaluation failed
|
|
24424
|
+
*/
|
|
24425
|
+
var eventMatchesCriteria = function(eventName, properties, criteria) {
|
|
24426
|
+
// Check exact event name match (case-sensitive)
|
|
24427
|
+
if (eventName !== criteria.event_name) {
|
|
24428
|
+
return { matches: false };
|
|
24429
|
+
}
|
|
24430
|
+
|
|
24431
|
+
// Evaluate property filters using JsonLogic
|
|
24432
|
+
var propertyFilters = criteria.property_filters;
|
|
24433
|
+
var filtersMatch = true; // default to true if no filters
|
|
24434
|
+
|
|
24435
|
+
if (propertyFilters && !_.isEmptyObject(propertyFilters)) {
|
|
24436
|
+
try {
|
|
24437
|
+
// Lowercase all keys and values in event properties for case-insensitive matching
|
|
24438
|
+
var lowercasedProperties = lowercaseKeysAndValues(properties || {});
|
|
24439
|
+
|
|
24440
|
+
// Lowercase only leaf nodes in JsonLogic filters (keep operators intact)
|
|
24441
|
+
var lowercasedFilters = lowercaseOnlyLeafNodes(propertyFilters);
|
|
24442
|
+
|
|
24443
|
+
filtersMatch = jsonLogic.apply(lowercasedFilters, lowercasedProperties);
|
|
24444
|
+
} catch (error) {
|
|
24445
|
+
return {
|
|
24446
|
+
matches: false,
|
|
24447
|
+
error: error.toString()
|
|
24448
|
+
};
|
|
24449
|
+
}
|
|
24450
|
+
}
|
|
24451
|
+
|
|
24452
|
+
return { matches: filtersMatch };
|
|
24453
|
+
};
|
|
24454
|
+
|
|
24455
|
+
// Create targeting library object
|
|
24456
|
+
var targetingLibrary = {};
|
|
24457
|
+
targetingLibrary['eventMatchesCriteria'] = eventMatchesCriteria;
|
|
24458
|
+
|
|
24459
|
+
// Set global Promise (use bracket notation to prevent minification)
|
|
24460
|
+
// This is the ONE AND ONLY global - matches recorder pattern
|
|
24461
|
+
win[TARGETING_GLOBAL_NAME] = Promise.resolve(targetingLibrary);
|
|
23765
24462
|
|
|
23766
24463
|
/** @const */ var DEFAULT_RAGE_CLICK_THRESHOLD_PX = 30;
|
|
23767
24464
|
/** @const */ var DEFAULT_RAGE_CLICK_TIMEOUT_MS = 1000;
|
|
@@ -24732,14 +25429,62 @@ define((function () { 'use strict';
|
|
|
24732
25429
|
// TODO integrate error_reporter from mixpanel instance
|
|
24733
25430
|
safewrapClass(Autocapture);
|
|
24734
25431
|
|
|
24735
|
-
|
|
25432
|
+
/**
|
|
25433
|
+
* Get the promise-based targeting loader
|
|
25434
|
+
* @param {Function} loadExtraBundle - Function to load external bundle (callback-based)
|
|
25435
|
+
* @param {string} targetingSrc - URL to targeting bundle
|
|
25436
|
+
* @returns {Promise} Promise that resolves with targeting library
|
|
25437
|
+
*/
|
|
25438
|
+
var getTargetingPromise = function(loadExtraBundle, targetingSrc) {
|
|
25439
|
+
// Return existing promise if already initialized or loading
|
|
25440
|
+
if (win[TARGETING_GLOBAL_NAME] && typeof win[TARGETING_GLOBAL_NAME].then === 'function') {
|
|
25441
|
+
return win[TARGETING_GLOBAL_NAME];
|
|
25442
|
+
}
|
|
25443
|
+
|
|
25444
|
+
// Create loading promise and set it as the global immediately
|
|
25445
|
+
// This makes minified build behavior consistent with dev/CJS builds
|
|
25446
|
+
win[TARGETING_GLOBAL_NAME] = new Promise(function (resolve) {
|
|
25447
|
+
loadExtraBundle(targetingSrc, resolve);
|
|
25448
|
+
}).then(function () {
|
|
25449
|
+
var p = win[TARGETING_GLOBAL_NAME];
|
|
25450
|
+
if (p && typeof p.then === 'function') {
|
|
25451
|
+
return p;
|
|
25452
|
+
}
|
|
25453
|
+
throw new Error('targeting failed to load');
|
|
25454
|
+
}).catch(function (err) {
|
|
25455
|
+
delete win[TARGETING_GLOBAL_NAME];
|
|
25456
|
+
throw err;
|
|
25457
|
+
});
|
|
24736
25458
|
|
|
25459
|
+
return win[TARGETING_GLOBAL_NAME];
|
|
25460
|
+
};
|
|
25461
|
+
|
|
25462
|
+
var logger = console_with_prefix('flags');
|
|
24737
25463
|
var FLAGS_CONFIG_KEY = 'flags';
|
|
24738
25464
|
|
|
24739
25465
|
var CONFIG_CONTEXT = 'context';
|
|
24740
25466
|
var CONFIG_DEFAULTS = {};
|
|
24741
25467
|
CONFIG_DEFAULTS[CONFIG_CONTEXT] = {};
|
|
24742
25468
|
|
|
25469
|
+
/**
|
|
25470
|
+
* Generate a unique key for a pending first-time event
|
|
25471
|
+
* @param {string} flagKey - The flag key
|
|
25472
|
+
* @param {string} firstTimeEventHash - The first_time_event_hash from the pending event definition
|
|
25473
|
+
* @returns {string} Composite key in format "flagKey:firstTimeEventHash"
|
|
25474
|
+
*/
|
|
25475
|
+
var getPendingEventKey = function(flagKey, firstTimeEventHash) {
|
|
25476
|
+
return flagKey + ':' + firstTimeEventHash;
|
|
25477
|
+
};
|
|
25478
|
+
|
|
25479
|
+
/**
|
|
25480
|
+
* Extract the flag key from a pending event key
|
|
25481
|
+
* @param {string} eventKey - The composite event key in format "flagKey:firstTimeEventHash"
|
|
25482
|
+
* @returns {string} The flag key portion
|
|
25483
|
+
*/
|
|
25484
|
+
var getFlagKeyFromPendingEventKey = function(eventKey) {
|
|
25485
|
+
return eventKey.split(':')[0];
|
|
25486
|
+
};
|
|
25487
|
+
|
|
24743
25488
|
/**
|
|
24744
25489
|
* FeatureFlagManager: support for Mixpanel's feature flagging product
|
|
24745
25490
|
* @constructor
|
|
@@ -24751,6 +25496,8 @@ define((function () { 'use strict';
|
|
|
24751
25496
|
this.setMpConfig = initOptions.setConfigFunc;
|
|
24752
25497
|
this.getMpProperty = initOptions.getPropertyFunc;
|
|
24753
25498
|
this.track = initOptions.trackingFunc;
|
|
25499
|
+
this.loadExtraBundle = initOptions.loadExtraBundle || function() {};
|
|
25500
|
+
this.targetingSrc = initOptions.targetingSrc || '';
|
|
24754
25501
|
};
|
|
24755
25502
|
|
|
24756
25503
|
FeatureFlagManager.prototype.init = function() {
|
|
@@ -24763,6 +25510,8 @@ define((function () { 'use strict';
|
|
|
24763
25510
|
this.fetchFlags();
|
|
24764
25511
|
|
|
24765
25512
|
this.trackedFeatures = new Set();
|
|
25513
|
+
this.pendingFirstTimeEvents = {};
|
|
25514
|
+
this.activatedFirstTimeEvents = {};
|
|
24766
25515
|
};
|
|
24767
25516
|
|
|
24768
25517
|
FeatureFlagManager.prototype.getFullConfig = function() {
|
|
@@ -24843,17 +25592,78 @@ define((function () { 'use strict';
|
|
|
24843
25592
|
throw new Error('No flags in API response');
|
|
24844
25593
|
}
|
|
24845
25594
|
var flags = new Map();
|
|
25595
|
+
var pendingFirstTimeEvents = {};
|
|
25596
|
+
|
|
25597
|
+
// Process flags from response
|
|
24846
25598
|
_.each(responseFlags, function(data, key) {
|
|
24847
|
-
|
|
24848
|
-
|
|
24849
|
-
|
|
24850
|
-
|
|
24851
|
-
|
|
24852
|
-
|
|
25599
|
+
// Check if this flag has any activated first-time events this session
|
|
25600
|
+
var hasActivatedEvent = false;
|
|
25601
|
+
var prefix = key + ':';
|
|
25602
|
+
_.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
|
|
25603
|
+
if (eventKey.startsWith(prefix)) {
|
|
25604
|
+
hasActivatedEvent = true;
|
|
25605
|
+
}
|
|
24853
25606
|
});
|
|
24854
|
-
|
|
25607
|
+
|
|
25608
|
+
if (hasActivatedEvent) {
|
|
25609
|
+
// Preserve the activated variant, don't overwrite with server's current variant
|
|
25610
|
+
var currentFlag = this.flags && this.flags.get(key);
|
|
25611
|
+
if (currentFlag) {
|
|
25612
|
+
flags.set(key, currentFlag);
|
|
25613
|
+
}
|
|
25614
|
+
} else {
|
|
25615
|
+
// Use server's current variant
|
|
25616
|
+
flags.set(key, {
|
|
25617
|
+
'key': data['variant_key'],
|
|
25618
|
+
'value': data['variant_value'],
|
|
25619
|
+
'experiment_id': data['experiment_id'],
|
|
25620
|
+
'is_experiment_active': data['is_experiment_active'],
|
|
25621
|
+
'is_qa_tester': data['is_qa_tester']
|
|
25622
|
+
});
|
|
25623
|
+
}
|
|
25624
|
+
}, this);
|
|
25625
|
+
|
|
25626
|
+
// Process top-level pending_first_time_events array
|
|
25627
|
+
var topLevelDefinitions = responseBody['pending_first_time_events'];
|
|
25628
|
+
if (topLevelDefinitions && topLevelDefinitions.length > 0) {
|
|
25629
|
+
_.each(topLevelDefinitions, function(def) {
|
|
25630
|
+
var flagKey = def['flag_key'];
|
|
25631
|
+
var eventKey = getPendingEventKey(flagKey, def['first_time_event_hash']);
|
|
25632
|
+
|
|
25633
|
+
// Skip if this specific event has already been activated this session
|
|
25634
|
+
if (this.activatedFirstTimeEvents[eventKey]) {
|
|
25635
|
+
return;
|
|
25636
|
+
}
|
|
25637
|
+
|
|
25638
|
+
// Store pending event definition using composite key
|
|
25639
|
+
pendingFirstTimeEvents[eventKey] = {
|
|
25640
|
+
'flag_key': flagKey,
|
|
25641
|
+
'flag_id': def['flag_id'],
|
|
25642
|
+
'project_id': def['project_id'],
|
|
25643
|
+
'first_time_event_hash': def['first_time_event_hash'],
|
|
25644
|
+
'event_name': def['event_name'],
|
|
25645
|
+
'property_filters': def['property_filters'],
|
|
25646
|
+
'pending_variant': def['pending_variant']
|
|
25647
|
+
};
|
|
25648
|
+
}, this);
|
|
25649
|
+
}
|
|
25650
|
+
|
|
25651
|
+
// Preserve any activated orphaned flags (flags that were activated but are no longer in response)
|
|
25652
|
+
if (this.activatedFirstTimeEvents) {
|
|
25653
|
+
_.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
|
|
25654
|
+
var flagKey = getFlagKeyFromPendingEventKey(eventKey);
|
|
25655
|
+
if (activated && !flags.has(flagKey) && this.flags && this.flags.has(flagKey)) {
|
|
25656
|
+
// Keep the activated flag even though it's not in the new response
|
|
25657
|
+
flags.set(flagKey, this.flags.get(flagKey));
|
|
25658
|
+
}
|
|
25659
|
+
}, this);
|
|
25660
|
+
}
|
|
25661
|
+
|
|
24855
25662
|
this.flags = flags;
|
|
25663
|
+
this.pendingFirstTimeEvents = pendingFirstTimeEvents;
|
|
24856
25664
|
this._traceparent = traceparent;
|
|
25665
|
+
|
|
25666
|
+
this._loadTargetingIfNeeded();
|
|
24857
25667
|
}.bind(this)).catch(function(error) {
|
|
24858
25668
|
this.markFetchComplete();
|
|
24859
25669
|
logger.error(error);
|
|
@@ -24877,6 +25687,177 @@ define((function () { 'use strict';
|
|
|
24877
25687
|
this._fetchInProgressStartTime = null;
|
|
24878
25688
|
};
|
|
24879
25689
|
|
|
25690
|
+
/**
|
|
25691
|
+
* Proactively load targeting bundle if any pending events have property filters
|
|
25692
|
+
*/
|
|
25693
|
+
FeatureFlagManager.prototype._loadTargetingIfNeeded = function() {
|
|
25694
|
+
var hasPropertyFilters = false;
|
|
25695
|
+
_.each(this.pendingFirstTimeEvents, function(evt) {
|
|
25696
|
+
if (evt['property_filters'] && !_.isEmptyObject(evt['property_filters'])) {
|
|
25697
|
+
hasPropertyFilters = true;
|
|
25698
|
+
}
|
|
25699
|
+
});
|
|
25700
|
+
|
|
25701
|
+
if (hasPropertyFilters) {
|
|
25702
|
+
this.getTargeting().then(function() {
|
|
25703
|
+
logger.log('targeting loaded for property filter evaluation');
|
|
25704
|
+
});
|
|
25705
|
+
}
|
|
25706
|
+
};
|
|
25707
|
+
|
|
25708
|
+
/**
|
|
25709
|
+
* Get the targeting library (initializes if not already loaded)
|
|
25710
|
+
* This method is primarily for testing - production code should rely on automatic loading
|
|
25711
|
+
* @returns {Promise} Promise that resolves with targeting library
|
|
25712
|
+
*/
|
|
25713
|
+
FeatureFlagManager.prototype.getTargeting = function() {
|
|
25714
|
+
return getTargetingPromise(
|
|
25715
|
+
this.loadExtraBundle.bind(this),
|
|
25716
|
+
this.targetingSrc
|
|
25717
|
+
).catch(function(error) {
|
|
25718
|
+
logger.error('Failed to load targeting: ' + error);
|
|
25719
|
+
}.bind(this));
|
|
25720
|
+
};
|
|
25721
|
+
|
|
25722
|
+
/**
|
|
25723
|
+
* Check if a tracked event matches any pending first-time events and activate the corresponding flag variant
|
|
25724
|
+
* @param {string} eventName - The name of the event being tracked
|
|
25725
|
+
* @param {Object} properties - Event properties to evaluate against property filters
|
|
25726
|
+
*
|
|
25727
|
+
* When a match is found (event name matches and property filters pass), this method:
|
|
25728
|
+
* - Switches the flag to the pending variant
|
|
25729
|
+
* - Marks the event as activated for this session
|
|
25730
|
+
* - Records the activation via the API (fire-and-forget)
|
|
25731
|
+
*/
|
|
25732
|
+
FeatureFlagManager.prototype.checkFirstTimeEvents = function(eventName, properties) {
|
|
25733
|
+
if (!this.pendingFirstTimeEvents || _.isEmptyObject(this.pendingFirstTimeEvents)) {
|
|
25734
|
+
return;
|
|
25735
|
+
}
|
|
25736
|
+
|
|
25737
|
+
// Check if targeting promise exists (either bundled or async loaded)
|
|
25738
|
+
if (win[TARGETING_GLOBAL_NAME] && _.isFunction(win[TARGETING_GLOBAL_NAME].then)) {
|
|
25739
|
+
win[TARGETING_GLOBAL_NAME].then(function(library) {
|
|
25740
|
+
this._processFirstTimeEventCheck(eventName, properties, library);
|
|
25741
|
+
}.bind(this)).catch(function() {
|
|
25742
|
+
// If targeting failed to load, process with null
|
|
25743
|
+
// Events without property filters will still match
|
|
25744
|
+
this._processFirstTimeEventCheck(eventName, properties, null);
|
|
25745
|
+
}.bind(this));
|
|
25746
|
+
} else {
|
|
25747
|
+
// No targeting available, process with null
|
|
25748
|
+
// Events without property filters will still match
|
|
25749
|
+
this._processFirstTimeEventCheck(eventName, properties, null);
|
|
25750
|
+
}
|
|
25751
|
+
};
|
|
25752
|
+
|
|
25753
|
+
/**
|
|
25754
|
+
* Internal method to process first-time event checks with loaded targeting library
|
|
25755
|
+
* @param {string} eventName - The name of the event being tracked
|
|
25756
|
+
* @param {Object} properties - Event properties to evaluate against property filters
|
|
25757
|
+
* @param {Object} targeting - The loaded targeting library
|
|
25758
|
+
*/
|
|
25759
|
+
FeatureFlagManager.prototype._processFirstTimeEventCheck = function(eventName, properties, targeting) {
|
|
25760
|
+
_.each(this.pendingFirstTimeEvents, function(pendingEvent, eventKey) {
|
|
25761
|
+
if (this.activatedFirstTimeEvents[eventKey]) {
|
|
25762
|
+
return;
|
|
25763
|
+
}
|
|
25764
|
+
|
|
25765
|
+
var flagKey = pendingEvent['flag_key'];
|
|
25766
|
+
|
|
25767
|
+
// Use targeting module to check if event matches
|
|
25768
|
+
var matchResult;
|
|
25769
|
+
|
|
25770
|
+
// If no targeting library and event has property filters, skip it
|
|
25771
|
+
if (!targeting && pendingEvent['property_filters'] && !_.isEmptyObject(pendingEvent['property_filters'])) {
|
|
25772
|
+
logger.warn('Skipping event check for "' + flagKey + '" - property filters require targeting library');
|
|
25773
|
+
return;
|
|
25774
|
+
}
|
|
25775
|
+
|
|
25776
|
+
// For simple events (no property filters), just check event name
|
|
25777
|
+
if (!targeting) {
|
|
25778
|
+
matchResult = {
|
|
25779
|
+
matches: eventName === pendingEvent['event_name'],
|
|
25780
|
+
error: null
|
|
25781
|
+
};
|
|
25782
|
+
} else {
|
|
25783
|
+
var criteria = {
|
|
25784
|
+
'event_name': pendingEvent['event_name'],
|
|
25785
|
+
'property_filters': pendingEvent['property_filters']
|
|
25786
|
+
};
|
|
25787
|
+
matchResult = targeting['eventMatchesCriteria'](
|
|
25788
|
+
eventName,
|
|
25789
|
+
properties,
|
|
25790
|
+
criteria
|
|
25791
|
+
);
|
|
25792
|
+
}
|
|
25793
|
+
|
|
25794
|
+
if (matchResult.error) {
|
|
25795
|
+
logger.error('Error checking first-time event for flag "' + flagKey + '": ' + matchResult.error);
|
|
25796
|
+
return;
|
|
25797
|
+
}
|
|
25798
|
+
|
|
25799
|
+
if (!matchResult.matches) {
|
|
25800
|
+
return;
|
|
25801
|
+
}
|
|
25802
|
+
|
|
25803
|
+
logger.log('First-time event matched for flag "' + flagKey + '": ' + eventName);
|
|
25804
|
+
|
|
25805
|
+
var newVariant = {
|
|
25806
|
+
'key': pendingEvent['pending_variant']['variant_key'],
|
|
25807
|
+
'value': pendingEvent['pending_variant']['variant_value'],
|
|
25808
|
+
'experiment_id': pendingEvent['pending_variant']['experiment_id'],
|
|
25809
|
+
'is_experiment_active': pendingEvent['pending_variant']['is_experiment_active']
|
|
25810
|
+
};
|
|
25811
|
+
|
|
25812
|
+
this.flags.set(flagKey, newVariant);
|
|
25813
|
+
this.activatedFirstTimeEvents[eventKey] = true;
|
|
25814
|
+
|
|
25815
|
+
this.recordFirstTimeEvent(
|
|
25816
|
+
pendingEvent['flag_id'],
|
|
25817
|
+
pendingEvent['project_id'],
|
|
25818
|
+
pendingEvent['first_time_event_hash']
|
|
25819
|
+
);
|
|
25820
|
+
}, this);
|
|
25821
|
+
};
|
|
25822
|
+
|
|
25823
|
+
FeatureFlagManager.prototype.getFirstTimeEventApiRoute = function(flagId) {
|
|
25824
|
+
// Construct URL: {api_host}/flags/{flagId}/first-time-events
|
|
25825
|
+
return this.getFullApiRoute() + '/' + flagId + '/first-time-events';
|
|
25826
|
+
};
|
|
25827
|
+
|
|
25828
|
+
FeatureFlagManager.prototype.recordFirstTimeEvent = function(flagId, projectId, firstTimeEventHash) {
|
|
25829
|
+
var distinctId = this.getMpProperty('distinct_id');
|
|
25830
|
+
var traceparent = generateTraceparent();
|
|
25831
|
+
|
|
25832
|
+
// Build URL with query string parameters
|
|
25833
|
+
var searchParams = new URLSearchParams();
|
|
25834
|
+
searchParams.set('mp_lib', 'web');
|
|
25835
|
+
searchParams.set('$lib_version', Config.LIB_VERSION);
|
|
25836
|
+
var url = this.getFirstTimeEventApiRoute(flagId) + '?' + searchParams.toString();
|
|
25837
|
+
|
|
25838
|
+
var payload = {
|
|
25839
|
+
'distinct_id': distinctId,
|
|
25840
|
+
'project_id': projectId,
|
|
25841
|
+
'first_time_event_hash': firstTimeEventHash
|
|
25842
|
+
};
|
|
25843
|
+
|
|
25844
|
+
logger.log('Recording first-time event for flag: ' + flagId);
|
|
25845
|
+
|
|
25846
|
+
// Fire-and-forget POST request
|
|
25847
|
+
this.fetch.call(win, url, {
|
|
25848
|
+
'method': 'POST',
|
|
25849
|
+
'headers': {
|
|
25850
|
+
'Content-Type': 'application/json',
|
|
25851
|
+
'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
|
|
25852
|
+
'traceparent': traceparent
|
|
25853
|
+
},
|
|
25854
|
+
'body': JSON.stringify(payload)
|
|
25855
|
+
}).catch(function(error) {
|
|
25856
|
+
// Silent failure - cohort sync will catch up
|
|
25857
|
+
logger.error('Failed to record first-time event for flag ' + flagId + ': ' + error);
|
|
25858
|
+
});
|
|
25859
|
+
};
|
|
25860
|
+
|
|
24880
25861
|
FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
|
|
24881
25862
|
if (!this.fetchPromise) {
|
|
24882
25863
|
return new Promise(function(resolve) {
|
|
@@ -24995,6 +25976,9 @@ define((function () { 'use strict';
|
|
|
24995
25976
|
// Deprecated method
|
|
24996
25977
|
FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
|
|
24997
25978
|
|
|
25979
|
+
// Exports intended only for testing
|
|
25980
|
+
FeatureFlagManager.prototype['getTargeting'] = FeatureFlagManager.prototype.getTargeting;
|
|
25981
|
+
|
|
24998
25982
|
/* eslint camelcase: "off" */
|
|
24999
25983
|
|
|
25000
25984
|
|
|
@@ -26464,6 +27448,7 @@ define((function () { 'use strict';
|
|
|
26464
27448
|
'record_min_ms': 0,
|
|
26465
27449
|
'record_sessions_percent': 0,
|
|
26466
27450
|
'recorder_src': 'https://cdn.mxpnl.com/libs/mixpanel-recorder.min.js',
|
|
27451
|
+
'targeting_src': 'https://cdn.mxpnl.com/libs/mixpanel-targeting.min.js',
|
|
26467
27452
|
'remote_settings_mode': SETTING_DISABLED // 'strict', 'fallback', 'disabled'
|
|
26468
27453
|
};
|
|
26469
27454
|
|
|
@@ -26693,7 +27678,9 @@ define((function () { 'use strict';
|
|
|
26693
27678
|
getConfigFunc: _.bind(this.get_config, this),
|
|
26694
27679
|
setConfigFunc: _.bind(this.set_config, this),
|
|
26695
27680
|
getPropertyFunc: _.bind(this.get_property, this),
|
|
26696
|
-
trackingFunc: _.bind(this.track, this)
|
|
27681
|
+
trackingFunc: _.bind(this.track, this),
|
|
27682
|
+
loadExtraBundle: load_extra_bundle,
|
|
27683
|
+
targetingSrc: this.get_config('targeting_src')
|
|
26697
27684
|
});
|
|
26698
27685
|
this.flags.init();
|
|
26699
27686
|
this['flags'] = this.flags;
|
|
@@ -26789,11 +27776,11 @@ define((function () { 'use strict';
|
|
|
26789
27776
|
|
|
26790
27777
|
var loadRecorder = _.bind(function(startNewIfInactive) {
|
|
26791
27778
|
var handleLoadedRecorder = _.bind(function() {
|
|
26792
|
-
this._recorder = this._recorder || new win[
|
|
27779
|
+
this._recorder = this._recorder || new win[RECORDER_GLOBAL_NAME](this);
|
|
26793
27780
|
this._recorder['resumeRecording'](startNewIfInactive);
|
|
26794
27781
|
}, this);
|
|
26795
27782
|
|
|
26796
|
-
if (_.isUndefined(win[
|
|
27783
|
+
if (_.isUndefined(win[RECORDER_GLOBAL_NAME])) {
|
|
26797
27784
|
load_extra_bundle(this.get_config('recorder_src'), handleLoadedRecorder);
|
|
26798
27785
|
} else {
|
|
26799
27786
|
handleLoadedRecorder();
|
|
@@ -27535,6 +28522,11 @@ define((function () { 'use strict';
|
|
|
27535
28522
|
send_request_options: options
|
|
27536
28523
|
}, callback);
|
|
27537
28524
|
|
|
28525
|
+
// Check for first-time event matches
|
|
28526
|
+
if (this.flags && this.flags.checkFirstTimeEvents) {
|
|
28527
|
+
this.flags.checkFirstTimeEvents(event_name, properties);
|
|
28528
|
+
}
|
|
28529
|
+
|
|
27538
28530
|
return ret;
|
|
27539
28531
|
});
|
|
27540
28532
|
|