@searchspring/snap-controller 0.64.0 → 0.65.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.
@@ -81,6 +81,8 @@ var AbstractController_1 = require("../Abstract/AbstractController");
81
81
  var snap_store_mobx_1 = require("@searchspring/snap-store-mobx");
82
82
  var getParams_1 = require("../utils/getParams");
83
83
  var types_1 = require("../types");
84
+ var BACKGROUND_FILTER_FIELD_MATCHES = ['collection', 'category', 'categories', 'hierarchy'];
85
+ var BACKGROUND_FILTERS_VALUE_FLAGS = [1, 0, '1', '0', 'true', 'false', true, false];
84
86
  var defaultConfig = {
85
87
  id: 'search',
86
88
  globals: {},
@@ -105,19 +107,24 @@ var SearchController = /** @class */ (function (_super) {
105
107
  var _this = _super.call(this, config, { client: client, store: store, urlManager: urlManager, eventManager: eventManager, profiler: profiler, logger: logger, tracker: tracker }, context) || this;
106
108
  _this.type = types_1.ControllerTypes.search;
107
109
  _this.previousResults = [];
110
+ _this.pageType = 'search';
111
+ _this.events = { product: {} };
108
112
  _this.track = {
109
113
  product: {
110
- click: function (e, result) {
111
- var _a, _b, _c, _d;
114
+ clickThrough: function (e, result) {
115
+ var _a, _b, _c, _d, _e, _f;
116
+ if ((_a = _this.events.product[result.id]) === null || _a === void 0 ? void 0 : _a.clickThrough) {
117
+ return;
118
+ }
112
119
  var target = e.target;
113
- var resultHref = ((_b = (_a = result.display) === null || _a === void 0 ? void 0 : _a.mappings.core) === null || _b === void 0 ? void 0 : _b.url) || ((_c = result.mappings.core) === null || _c === void 0 ? void 0 : _c.url);
120
+ var resultHref = ((_c = (_b = result.display) === null || _b === void 0 ? void 0 : _b.mappings.core) === null || _c === void 0 ? void 0 : _c.url) || ((_d = result.mappings.core) === null || _d === void 0 ? void 0 : _d.url) || '';
114
121
  var elemHref = target === null || target === void 0 ? void 0 : target.getAttribute('href');
115
122
  // the href that should be used for restoration - if the elemHref contains the resultHref - use resultHref
116
123
  var storedHref = (elemHref === null || elemHref === void 0 ? void 0 : elemHref.indexOf(resultHref)) != -1 ? resultHref : elemHref || resultHref;
117
124
  var scrollMap = {};
118
125
  // generate the selector using element class and parent classes
119
126
  var selector = generateHrefSelector(target, storedHref);
120
- var domRect = selector ? (_d = document === null || document === void 0 ? void 0 : document.querySelector(selector)) === null || _d === void 0 ? void 0 : _d.getBoundingClientRect() : undefined;
127
+ var domRect = selector ? (_e = document === null || document === void 0 ? void 0 : document.querySelector(selector)) === null || _e === void 0 ? void 0 : _e.getBoundingClientRect() : undefined;
121
128
  // store element position data to scrollMap
122
129
  if (selector || storedHref || domRect) {
123
130
  try {
@@ -135,17 +142,64 @@ var SearchController = /** @class */ (function (_super) {
135
142
  }
136
143
  // store position data or empty object
137
144
  _this.storage.set('scrollMap', scrollMap);
138
- // track
139
- var _e = result.attributes, intellisuggestData = _e.intellisuggestData, intellisuggestSignature = _e.intellisuggestSignature;
140
- var event = _this.tracker.track.product.click({
141
- intellisuggestData: intellisuggestData,
142
- intellisuggestSignature: intellisuggestSignature,
143
- href: elemHref || resultHref,
145
+ var data = getSearchSchemaData({ params: _this.params, store: _this.store, results: [result] });
146
+ _this.tracker.events[_this.pageType].clickThrough({ data: data, siteId: (_f = _this.config.globals) === null || _f === void 0 ? void 0 : _f.siteId });
147
+ _this.events.product[result.id] = _this.events.product[result.id] || {};
148
+ _this.events.product[result.id].clickThrough = true;
149
+ _this.eventManager.fire('track.product.clickThrough', { controller: _this, event: e, products: [result], trackEvent: data });
150
+ },
151
+ click: function (e, result) {
152
+ var _a, _b, _c;
153
+ if (result.type === 'banner') {
154
+ return;
155
+ }
156
+ // TODO: closest might be going too far - write own function to only go n levels up - additionally check that href includes result.url
157
+ var href = ((_a = e.target) === null || _a === void 0 ? void 0 : _a.getAttribute('href')) || ((_c = (_b = e.target) === null || _b === void 0 ? void 0 : _b.closest('a')) === null || _c === void 0 ? void 0 : _c.getAttribute('href'));
158
+ if (href) {
159
+ _this.track.product.clickThrough(e, result);
160
+ }
161
+ else {
162
+ // TODO: in future, send as an interaction event
163
+ }
164
+ },
165
+ render: function (result) {
166
+ var _a, _b;
167
+ if ((_a = _this.events.product[result.id]) === null || _a === void 0 ? void 0 : _a.render) {
168
+ return;
169
+ }
170
+ var data = getSearchSchemaData({ params: _this.params, store: _this.store, results: [result] });
171
+ _this.tracker.events[_this.pageType].render({ data: data, siteId: (_b = _this.config.globals) === null || _b === void 0 ? void 0 : _b.siteId });
172
+ _this.events.product[result.id] = _this.events.product[result.id] || {};
173
+ _this.events.product[result.id].render = true;
174
+ _this.eventManager.fire('track.product.render', { controller: _this, products: [result], trackEvent: data });
175
+ },
176
+ impression: function (result) {
177
+ var _a, _b;
178
+ if ((_a = _this.events.product[result.id]) === null || _a === void 0 ? void 0 : _a.impression) {
179
+ return;
180
+ }
181
+ var data = getSearchSchemaData({ params: _this.params, store: _this.store, results: [result] });
182
+ _this.tracker.events[_this.pageType].impression({ data: data, siteId: (_b = _this.config.globals) === null || _b === void 0 ? void 0 : _b.siteId });
183
+ _this.events.product[result.id] = _this.events.product[result.id] || {};
184
+ _this.events.product[result.id].impression = true;
185
+ _this.eventManager.fire('track.product.impression', { controller: _this, products: [result], trackEvent: data });
186
+ },
187
+ addToCart: function (result) {
188
+ var _a;
189
+ var data = getSearchSchemaData({ params: _this.params, store: _this.store, results: [result] });
190
+ _this.tracker.events[_this.pageType].addToCart({
191
+ data: data,
192
+ siteId: (_a = _this.config.globals) === null || _a === void 0 ? void 0 : _a.siteId,
144
193
  });
145
- _this.eventManager.fire('track.product.click', { controller: _this, event: e, result: result, trackEvent: event });
146
- return event;
194
+ _this.eventManager.fire('track.product.addToCart', { controller: _this, products: [result], trackEvent: data });
147
195
  },
148
196
  },
197
+ redirect: function (redirectURL) {
198
+ var _a;
199
+ var data = getSearchRedirectSchemaData({ redirectURL: redirectURL });
200
+ _this.tracker.events.search.redirect({ data: data, siteId: (_a = _this.config.globals) === null || _a === void 0 ? void 0 : _a.siteId });
201
+ _this.eventManager.fire('track.product.redirect', { controller: _this, redirectURL: redirectURL, trackEvent: data });
202
+ },
149
203
  };
150
204
  _this.search = function () { return __awaiter(_this, void 0, void 0, function () {
151
205
  var params_1, err_1, stringyParams, prevStringyParams, searchProfile, meta, response, preventBackfill, dontBackfill, backfillRequests, backfillResponses, backfillResults, afterSearchProfile, err_2, afterStoreProfile, err_3, err_4;
@@ -163,9 +217,9 @@ var SearchController = /** @class */ (function (_super) {
163
217
  _o.label = 2;
164
218
  case 2:
165
219
  params_1 = this.params;
166
- if (((_d = (_c = this.params.search) === null || _c === void 0 ? void 0 : _c.query) === null || _d === void 0 ? void 0 : _d.string) && ((_f = (_e = this.params.search) === null || _e === void 0 ? void 0 : _e.query) === null || _f === void 0 ? void 0 : _f.string.length)) {
220
+ if (((_d = (_c = params_1.search) === null || _c === void 0 ? void 0 : _c.query) === null || _d === void 0 ? void 0 : _d.string) && ((_f = (_e = params_1.search) === null || _e === void 0 ? void 0 : _e.query) === null || _f === void 0 ? void 0 : _f.string.length)) {
167
221
  // save it to the history store
168
- this.store.history.save(this.params.search.query.string);
222
+ this.store.history.save(params_1.search.query.string);
169
223
  }
170
224
  this.store.loading = true;
171
225
  _o.label = 3;
@@ -251,7 +305,9 @@ var SearchController = /** @class */ (function (_super) {
251
305
  _o.label = 10;
252
306
  case 10: return [3 /*break*/, 13];
253
307
  case 11:
254
- // normal request for next page
308
+ // normal request
309
+ // reset events for new search
310
+ this.events = { product: {} };
255
311
  // clear previousResults to prevent infinite scroll from using them
256
312
  this.previousResults = [];
257
313
  return [4 /*yield*/, this.client.search(params_1)];
@@ -373,6 +429,13 @@ var SearchController = /** @class */ (function (_super) {
373
429
  }
374
430
  });
375
431
  }); };
432
+ _this.addToCart = function (product) { return __awaiter(_this, void 0, void 0, function () {
433
+ return __generator(this, function (_a) {
434
+ this.track.product.addToCart(product);
435
+ this.eventManager.fire('addToCart', { controller: this, products: [product] });
436
+ return [2 /*return*/];
437
+ });
438
+ }); };
376
439
  // deep merge config with defaults
377
440
  _this.config = (0, deepmerge_1.default)(defaultConfig, _this.config);
378
441
  // set restorePosition to be enabled by default when using infinite (if not provided)
@@ -386,6 +449,53 @@ var SearchController = /** @class */ (function (_super) {
386
449
  });
387
450
  // set last params to undefined for compare in search
388
451
  _this.storage.set('lastStringyParams', undefined);
452
+ _this.eventManager.on('beforeSearch', function (_a, next) {
453
+ var request = _a.request;
454
+ return __awaiter(_this, void 0, void 0, function () {
455
+ var req, query, hasCategoryBackgroundFilters;
456
+ var _b, _c, _d;
457
+ return __generator(this, function (_e) {
458
+ switch (_e.label) {
459
+ case 0:
460
+ // wait for other middleware to resolve
461
+ return [4 /*yield*/, next()];
462
+ case 1:
463
+ // wait for other middleware to resolve
464
+ _e.sent();
465
+ if (((_b = this.context) === null || _b === void 0 ? void 0 : _b.pageType) === 'category') {
466
+ this.pageType = 'category';
467
+ return [2 /*return*/];
468
+ }
469
+ req = request;
470
+ query = (_c = req.search) === null || _c === void 0 ? void 0 : _c.query;
471
+ if (!query) {
472
+ hasCategoryBackgroundFilters = (_d = req.filters) === null || _d === void 0 ? void 0 : _d.filter(function (filter) { return filter.background; }).filter(function (filter) {
473
+ return BACKGROUND_FILTER_FIELD_MATCHES.find(function (bgFilter) {
474
+ var _a;
475
+ return (_a = filter.field) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(bgFilter);
476
+ });
477
+ }).filter(function (filter) {
478
+ return BACKGROUND_FILTERS_VALUE_FLAGS.every(function (flag) {
479
+ switch (filter.type) {
480
+ case 'range':
481
+ var rangeFilter = filter;
482
+ return rangeFilter.value !== flag;
483
+ case 'value':
484
+ default:
485
+ var valueFilter = filter;
486
+ return valueFilter.value !== flag;
487
+ }
488
+ });
489
+ });
490
+ if (hasCategoryBackgroundFilters === null || hasCategoryBackgroundFilters === void 0 ? void 0 : hasCategoryBackgroundFilters.length) {
491
+ this.pageType = 'category';
492
+ }
493
+ }
494
+ return [2 /*return*/];
495
+ }
496
+ });
497
+ });
498
+ });
389
499
  // add 'afterSearch' middleware
390
500
  _this.eventManager.on('afterSearch', function (search, next) { return __awaiter(_this, void 0, void 0, function () {
391
501
  var config, redirectURL, searchStore, nonBackgroundFilters;
@@ -399,6 +509,7 @@ var SearchController = /** @class */ (function (_super) {
399
509
  if (redirectURL && ((_d = (_c = config === null || config === void 0 ? void 0 : config.settings) === null || _c === void 0 ? void 0 : _c.redirects) === null || _d === void 0 ? void 0 : _d.merchandising) && !((_f = (_e = search === null || search === void 0 ? void 0 : search.response) === null || _e === void 0 ? void 0 : _e.filters) === null || _f === void 0 ? void 0 : _f.length) && !searchStore.loaded) {
400
510
  //set loaded to true to prevent infinite search/reloading from happening
401
511
  searchStore.loaded = true;
512
+ this.track.redirect(redirectURL);
402
513
  window.location.replace(redirectURL);
403
514
  return [2 /*return*/, false];
404
515
  }
@@ -558,17 +669,15 @@ var SearchController = /** @class */ (function (_super) {
558
669
  }
559
670
  params.tracking = params.tracking || {};
560
671
  params.tracking.domain = window.location.href;
561
- var userId = this.tracker.getUserId();
672
+ var _e = this.tracker.getContext(), userId = _e.userId, sessionId = _e.sessionId, pageLoadId = _e.pageLoadId, shopperId = _e.shopperId;
562
673
  if (userId) {
563
674
  params.tracking.userId = userId;
564
675
  }
565
- var sessionId = this.tracker.getContext().sessionId;
566
676
  if (sessionId) {
567
677
  params.tracking.sessionId = sessionId;
568
678
  }
569
- var pageId = this.tracker.getContext().pageLoadId;
570
- if (pageId) {
571
- params.tracking.pageLoadId = pageId;
679
+ if (pageLoadId) {
680
+ params.tracking.pageLoadId = pageLoadId;
572
681
  }
573
682
  if (!((_d = (_c = this.config.globals) === null || _c === void 0 ? void 0 : _c.personalization) === null || _d === void 0 ? void 0 : _d.disabled)) {
574
683
  var cartItems = this.tracker.cookies.cart.get();
@@ -581,7 +690,6 @@ var SearchController = /** @class */ (function (_super) {
581
690
  params.personalization = params.personalization || {};
582
691
  params.personalization.lastViewed = lastViewedItems.join(',');
583
692
  }
584
- var shopperId = this.tracker.getShopperId();
585
693
  if (shopperId) {
586
694
  params.personalization = params.personalization || {};
587
695
  params.personalization.shopper = shopperId;
@@ -641,3 +749,66 @@ function generateHrefSelector(element, href, levels) {
641
749
  return;
642
750
  }
643
751
  exports.generateHrefSelector = generateHrefSelector;
752
+ function getSearchRedirectSchemaData(_a) {
753
+ var redirectURL = _a.redirectURL;
754
+ return {
755
+ redirect: redirectURL,
756
+ };
757
+ }
758
+ function getSearchSchemaData(_a) {
759
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
760
+ var params = _a.params, store = _a.store, results = _a.results;
761
+ var filters = (_b = params.filters) === null || _b === void 0 ? void 0 : _b.reduce(function (acc, filter) {
762
+ var _a;
763
+ var _b, _c, _d, _e;
764
+ var key = filter.background ? 'bgfilter' : 'filter';
765
+ acc[key] = acc[key] || [];
766
+ var value = filter.type === 'range' &&
767
+ !isNaN((_b = filter.value) === null || _b === void 0 ? void 0 : _b.low) &&
768
+ !isNaN((_c = filter.value) === null || _c === void 0 ? void 0 : _c.low)
769
+ ? ["low=".concat((_d = filter.value) === null || _d === void 0 ? void 0 : _d.low), "high=".concat((_e = filter.value) === null || _e === void 0 ? void 0 : _e.high)]
770
+ : ["".concat(filter.value)];
771
+ var existing = acc[key].find(function (item) { return item.field === filter.field; });
772
+ if (existing && !existing.value.includes(value[0])) {
773
+ (_a = existing.value).push.apply(_a, value);
774
+ }
775
+ else {
776
+ acc[key].push({
777
+ field: filter.field,
778
+ value: value,
779
+ });
780
+ }
781
+ return acc;
782
+ }, {});
783
+ return __assign(__assign({ q: ((_d = (_c = params.search) === null || _c === void 0 ? void 0 : _c.query) === null || _d === void 0 ? void 0 : _d.string) || '', correctedQuery: ((_f = (_e = store.search) === null || _e === void 0 ? void 0 : _e.originalQuery) === null || _f === void 0 ? void 0 : _f.string) ? (_h = (_g = store.search) === null || _g === void 0 ? void 0 : _g.query) === null || _h === void 0 ? void 0 : _h.string : undefined }, filters), { sort: [
784
+ {
785
+ field: (_j = store.sorting.current) === null || _j === void 0 ? void 0 : _j.field,
786
+ dir: (_k = store.sorting.current) === null || _k === void 0 ? void 0 : _k.direction,
787
+ },
788
+ ], pagination: {
789
+ totalResults: store.pagination.totalResults,
790
+ page: store.pagination.page,
791
+ resultsPerPage: store.pagination.pageSize,
792
+ }, merchandising: {
793
+ personalized: store.merchandising.personalized,
794
+ redirect: store.merchandising.redirect,
795
+ triggeredCampaigns: (((_l = store.merchandising.campaigns) === null || _l === void 0 ? void 0 : _l.length) &&
796
+ ((_m = store.merchandising.campaigns) === null || _m === void 0 ? void 0 : _m.map(function (campaign) {
797
+ var experiement = store.merchandising.experiments.find(function (experiment) { return experiment.campaignId === campaign.id; });
798
+ return {
799
+ id: campaign.id,
800
+ experimentId: experiement === null || experiement === void 0 ? void 0 : experiement.experimentId,
801
+ variationId: experiement === null || experiement === void 0 ? void 0 : experiement.variationId,
802
+ };
803
+ }))) ||
804
+ undefined,
805
+ }, results: (results === null || results === void 0 ? void 0 : results.map(function (result) {
806
+ var core = result.mappings.core;
807
+ return {
808
+ uid: core.uid || '',
809
+ // childUid: core.uid,
810
+ sku: core.sku,
811
+ // childSku: core.sku,
812
+ };
813
+ })) || [] });
814
+ }
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { StorageStore } from '@searchspring/snap-store-mobx';
2
+ import { StorageStore, Product, Banner } from '@searchspring/snap-store-mobx';
3
3
  import { AbstractController } from '../Abstract/AbstractController';
4
4
  import { ControllerTypes } from '../types';
5
5
  import { AutocompleteStore } from '@searchspring/snap-store-mobx';
@@ -8,14 +8,26 @@ import type { AutocompleteRequestModel } from '@searchspring/snapi-types';
8
8
  export declare const INPUT_DELAY = 200;
9
9
  type AutocompleteTrackMethods = {
10
10
  product: {
11
- click: (e: MouseEvent, result: any) => void;
11
+ clickThrough: (e: MouseEvent, result: Product) => void;
12
+ click: (e: MouseEvent, result: Product | Banner) => void;
13
+ render: (result: Product) => void;
14
+ impression: (result: Product) => void;
15
+ addToCart: (results: Product) => void;
12
16
  };
17
+ redirect: (redirectURL: string) => void;
13
18
  };
14
19
  export declare class AutocompleteController extends AbstractController {
15
20
  type: ControllerTypes;
16
21
  store: AutocompleteStore;
17
22
  config: AutocompleteControllerConfig;
18
23
  storage: StorageStore;
24
+ events: {
25
+ product: Record<string, {
26
+ clickThrough?: boolean;
27
+ impression?: boolean;
28
+ render?: boolean;
29
+ }>;
30
+ };
19
31
  constructor(config: AutocompleteControllerConfig, { client, store, urlManager, eventManager, profiler, logger, tracker }: ControllerServices, context?: ContextVariables);
20
32
  track: AutocompleteTrackMethods;
21
33
  get params(): AutocompleteRequestModel;
@@ -39,6 +51,7 @@ export declare class AutocompleteController extends AbstractController {
39
51
  bind(): Promise<void>;
40
52
  searchTrending: () => Promise<void>;
41
53
  search: () => Promise<void>;
54
+ addToCart: (product: Product) => Promise<void>;
42
55
  }
43
56
  export {};
44
57
  //# sourceMappingURL=AutocompleteController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AutocompleteController.d.ts","sourceRoot":"","sources":["../../../src/Autocomplete/AutocompleteController.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAa,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,4BAA4B,EAAiC,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAElI,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAG1E,eAAO,MAAM,WAAW,MAAM,CAAC;AA+B/B,KAAK,wBAAwB,GAAG;IAC/B,OAAO,EAAE;QACR,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;KAC5C,CAAC;CACF,CAAC;AAEF,qBAAa,sBAAuB,SAAQ,kBAAkB;IACtD,IAAI,kBAAgC;IACnC,KAAK,EAAE,iBAAiB,CAAC;IACzB,MAAM,EAAE,4BAA4B,CAAC;IACtC,OAAO,EAAE,YAAY,CAAC;gBAG5B,MAAM,EAAE,4BAA4B,EACpC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IAyD3B,KAAK,EAAE,wBAAwB,CAO7B;IAEF,IAAI,MAAM,IAAI,wBAAwB,CA0CrC;IAEK,UAAU,CAAC,YAAY,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BhE,KAAK,IAAI,IAAI;IASb,QAAQ;;0BAEc,aAAa,KAAG,QAAQ,OAAO,GAAG,SAAS,CAAC;wBAkDpD,aAAa,KAAG,IAAI;uBAMrB,UAAU,KAAG,IAAI;4BAQN,MAAM,SAAS,CAAC,gBAAgB,CAAC,KAAG,QAAQ,IAAI,CAAC;mCAgDhD,MAAM,WAAW,CAAC,gBAAgB,CAAC,KAAG,IAAI;uBAqBtD,KAAK;;;;uBAyDL,UAAU,KAAG,IAAI;;MAS5B;IAEF,MAAM,IAAI,IAAI;IAgBR,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgF3B,cAAc,QAAa,QAAQ,IAAI,CAAC,CAwBtC;IAEF,MAAM,QAAa,QAAQ,IAAI,CAAC,CA0I9B;CACF"}
1
+ {"version":3,"file":"AutocompleteController.d.ts","sourceRoot":"","sources":["../../../src/Autocomplete/AutocompleteController.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAa,OAAO,EAAqB,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAC5G,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,4BAA4B,EAAiC,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAElI,OAAO,KAAK,EAAE,wBAAwB,EAAgE,MAAM,2BAA2B,CAAC;AAWxI,eAAO,MAAM,WAAW,MAAM,CAAC;AA+B/B,KAAK,wBAAwB,GAAG;IAC/B,OAAO,EAAE;QACR,YAAY,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QACvD,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;QACzD,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QAClC,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QACtC,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;KACtC,CAAC;IACF,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CAAC;AAEF,qBAAa,sBAAuB,SAAQ,kBAAkB;IACtD,IAAI,kBAAgC;IACnC,KAAK,EAAE,iBAAiB,CAAC;IACzB,MAAM,EAAE,4BAA4B,CAAC;IACtC,OAAO,EAAE,YAAY,CAAC;IAE7B,MAAM,EAAE;QACP,OAAO,EAAE,MAAM,CACd,MAAM,EACN;YACC,YAAY,CAAC,EAAE,OAAO,CAAC;YACvB,UAAU,CAAC,EAAE,OAAO,CAAC;YACrB,MAAM,CAAC,EAAE,OAAO,CAAC;SACjB,CACD,CAAC;KACF,CAEC;gBAGD,MAAM,EAAE,4BAA4B,EACpC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IA0D3B,KAAK,EAAE,wBAAwB,CAqD7B;IAEF,IAAI,MAAM,IAAI,wBAAwB,CAwCrC;IAEK,UAAU,CAAC,YAAY,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BhE,KAAK,IAAI,IAAI;IASb,QAAQ;;0BAEc,aAAa,KAAG,QAAQ,OAAO,GAAG,SAAS,CAAC;wBAkDpD,aAAa,KAAG,IAAI;uBAMrB,UAAU,KAAG,IAAI;4BAQN,MAAM,SAAS,CAAC,gBAAgB,CAAC,KAAG,QAAQ,IAAI,CAAC;mCAgDhD,MAAM,WAAW,CAAC,gBAAgB,CAAC,KAAG,IAAI;uBAqBtD,KAAK;;;;uBAyDL,UAAU,KAAG,IAAI;;MAS5B;IAEF,MAAM,IAAI,IAAI;IAgBR,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgF3B,cAAc,QAAa,QAAQ,IAAI,CAAC,CAwBtC;IAEF,MAAM,QAAa,QAAQ,IAAI,CAAC,CA6I9B;IAEF,SAAS,YAAmB,OAAO,KAAG,QAAQ,IAAI,CAAC,CAGjD;CACF"}
@@ -37,12 +37,62 @@ export class AutocompleteController extends AbstractController {
37
37
  constructor(config, { client, store, urlManager, eventManager, profiler, logger, tracker }, context) {
38
38
  super(config, { client, store, urlManager, eventManager, profiler, logger, tracker }, context);
39
39
  this.type = ControllerTypes.autocomplete;
40
+ this.events = {
41
+ product: {},
42
+ };
40
43
  this.track = {
41
- // TODO: add in future when autocomplete supports result click tracking
42
44
  product: {
43
- click: () => {
44
- this.log.warn('product.click tracking is not currently supported in this controller type');
45
+ clickThrough: (e, result) => {
46
+ if (this.events.product[result.id]?.clickThrough) {
47
+ return;
48
+ }
49
+ const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] });
50
+ this.tracker.events.autocomplete.clickThrough({ data, siteId: this.config.globals?.siteId });
51
+ this.events.product[result.id] = this.events.product[result.id] || {};
52
+ this.events.product[result.id].clickThrough = true;
53
+ this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, products: [result], trackEvent: data });
54
+ },
55
+ click: (e, result) => {
56
+ if (result.type === 'banner') {
57
+ return;
58
+ }
59
+ // TODO: closest might be going too far - write own function to only go n levels up - additionally check that href includes result.url
60
+ const href = e.target?.getAttribute('href') || e.target?.closest('a')?.getAttribute('href');
61
+ if (href) {
62
+ this.track.product.clickThrough(e, result);
63
+ }
64
+ else {
65
+ // TODO: in future, send as an interaction event
66
+ }
67
+ },
68
+ render: (result) => {
69
+ if (this.events.product[result.id]?.render)
70
+ return;
71
+ const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] });
72
+ this.tracker.events.autocomplete.render({ data, siteId: this.config.globals?.siteId });
73
+ this.events.product[result.id] = this.events.product[result.id] || {};
74
+ this.events.product[result.id].render = true;
75
+ this.eventManager.fire('track.product.render', { controller: this, products: [result], trackEvent: data });
76
+ },
77
+ impression: (result) => {
78
+ if (this.events.product[result.id]?.impression)
79
+ return;
80
+ const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] });
81
+ this.tracker.events.autocomplete.impression({ data, siteId: this.config.globals?.siteId });
82
+ this.events.product[result.id] = this.events.product[result.id] || {};
83
+ this.events.product[result.id].impression = true;
84
+ this.eventManager.fire('track.product.impression', { controller: this, products: [result], trackEvent: data });
45
85
  },
86
+ addToCart: (result) => {
87
+ const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] });
88
+ this.tracker.events.autocomplete.addToCart({ data, siteId: this.config.globals?.siteId });
89
+ this.eventManager.fire('track.product.addToCart', { controller: this, products: [result], trackEvent: data });
90
+ },
91
+ },
92
+ redirect: (redirectURL) => {
93
+ const data = getAutocompleteRedirectSchemaData({ redirectURL });
94
+ this.tracker.events.autocomplete.redirect({ data, siteId: this.config.globals?.siteId });
95
+ this.eventManager.fire('track.product.redirect', { controller: this, redirectURL, trackEvent: data });
46
96
  },
47
97
  };
48
98
  this.handlers = {
@@ -257,6 +307,8 @@ export class AutocompleteController extends AbstractController {
257
307
  if (!this.initialized) {
258
308
  await this.init();
259
309
  }
310
+ // reset events for new search
311
+ this.events = { product: {} };
260
312
  // if urlManager has no query, there will be no need to get params and no query
261
313
  if (!this.urlManager.state.query) {
262
314
  return;
@@ -382,6 +434,10 @@ export class AutocompleteController extends AbstractController {
382
434
  this.store.loading = false;
383
435
  }
384
436
  };
437
+ this.addToCart = async (product) => {
438
+ this.track.product.addToCart(product);
439
+ this.eventManager.fire('addToCart', { controller: this, products: [product] });
440
+ };
385
441
  // deep merge config with defaults
386
442
  this.config = deepmerge(defaultConfig, this.config);
387
443
  this.store.setConfig(this.config);
@@ -409,6 +465,7 @@ export class AutocompleteController extends AbstractController {
409
465
  await next();
410
466
  const redirectURL = ac.controller.store.merchandising?.redirect;
411
467
  if (redirectURL && this.config?.settings?.redirects?.merchandising) {
468
+ this.track.redirect(redirectURL);
412
469
  window.location.href = redirectURL;
413
470
  return false;
414
471
  }
@@ -429,9 +486,7 @@ export class AutocompleteController extends AbstractController {
429
486
  get params() {
430
487
  const urlState = this.urlManager.state;
431
488
  const params = deepmerge({ ...getSearchParams(urlState) }, this.config.globals);
432
- const userId = this.tracker.getUserId();
433
- const sessionId = this.tracker.getContext().sessionId;
434
- const pageLoadId = this.tracker.getContext().pageLoadId;
489
+ const { userId, sessionId, pageLoadId, shopperId } = this.tracker.getContext();
435
490
  params.tracking = params.tracking || {};
436
491
  params.tracking.domain = window.location.href;
437
492
  if (userId) {
@@ -454,7 +509,6 @@ export class AutocompleteController extends AbstractController {
454
509
  params.personalization = params.personalization || {};
455
510
  params.personalization.lastViewed = lastViewedItems.join(',');
456
511
  }
457
- const shopperId = this.tracker.getShopperId();
458
512
  if (shopperId) {
459
513
  params.personalization = params.personalization || {};
460
514
  params.personalization.shopper = shopperId;
@@ -633,3 +687,69 @@ function unbindFormParameters(form, fn) {
633
687
  }
634
688
  }
635
689
  }
690
+ function getAutocompleteRedirectSchemaData({ redirectURL }) {
691
+ return {
692
+ redirect: redirectURL,
693
+ };
694
+ }
695
+ function getAutocompleteSchemaData({ params, store, results, }) {
696
+ const filters = params.filters?.reduce((acc, filter) => {
697
+ const key = filter.background ? 'bgfilter' : 'filter';
698
+ acc[key] = acc[key] || [];
699
+ const value = filter.type === 'range' &&
700
+ !isNaN(filter.value?.low) &&
701
+ !isNaN(filter.value?.low)
702
+ ? [`low=${filter.value?.low}`, `high=${filter.value?.high}`]
703
+ : [`${filter.value}`];
704
+ const existing = acc[key].find((item) => item.field === filter.field);
705
+ if (existing && !existing.value.includes(value[0])) {
706
+ existing.value.push(...value);
707
+ }
708
+ else {
709
+ acc[key].push({
710
+ field: filter.field,
711
+ value,
712
+ });
713
+ }
714
+ return acc;
715
+ }, {});
716
+ return {
717
+ q: store.search?.originalQuery?.string || store.search?.query?.string || '',
718
+ correctedQuery: store.search?.originalQuery?.string ? store.search?.query?.string : undefined,
719
+ ...filters,
720
+ sort: [
721
+ {
722
+ field: store.sorting.current?.field,
723
+ dir: store.sorting.current?.direction,
724
+ },
725
+ ],
726
+ pagination: {
727
+ totalResults: store.pagination.totalResults,
728
+ page: store.pagination.page,
729
+ resultsPerPage: store.pagination.pageSize,
730
+ },
731
+ merchandising: {
732
+ personalized: store.merchandising.personalized,
733
+ redirect: store.merchandising.redirect,
734
+ triggeredCampaigns: (store.merchandising.campaigns?.length &&
735
+ store.merchandising.campaigns?.map((campaign) => {
736
+ const experiement = store.merchandising.experiments.find((experiment) => experiment.campaignId === campaign.id);
737
+ return {
738
+ id: campaign.id,
739
+ experimentId: experiement?.experimentId,
740
+ variationId: experiement?.variationId,
741
+ };
742
+ })) ||
743
+ undefined,
744
+ },
745
+ results: results?.map((result) => {
746
+ const core = result.mappings.core;
747
+ return {
748
+ uid: core.uid || '',
749
+ // childUid: core.uid,
750
+ sku: core.sku,
751
+ // childSku: core.sku,
752
+ };
753
+ }) || [],
754
+ };
755
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"FinderController.d.ts","sourceRoot":"","sources":["../../../src/Finder/FinderController.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,KAAK,EAAE,sBAAsB,EAAkB,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAiB7G,qBAAa,gBAAiB,SAAQ,kBAAkB;IAChD,IAAI,kBAA0B;IAC7B,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,sBAAsB,CAAC;gBAGtC,MAAM,EAAE,sBAAsB,EAC9B,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IA+B3B,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CA+BhC;IAED,IAAI,QAAa,QAAQ,IAAI,CAAC,CAkB5B;IAEF,KAAK,QAAO,IAAI,CAId;IAEF,MAAM,QAAa,QAAQ,IAAI,CAAC,CAoI9B;CACF"}
1
+ {"version":3,"file":"FinderController.d.ts","sourceRoot":"","sources":["../../../src/Finder/FinderController.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,KAAK,EAAE,sBAAsB,EAAkB,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAiB7G,qBAAa,gBAAiB,SAAQ,kBAAkB;IAChD,IAAI,kBAA0B;IAC7B,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,sBAAsB,CAAC;gBAGtC,MAAM,EAAE,sBAAsB,EAC9B,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IA+B3B,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CA6BhC;IAED,IAAI,QAAa,QAAQ,IAAI,CAAC,CAkB5B;IAEF,KAAK,QAAO,IAAI,CAId;IAEF,MAAM,QAAa,QAAQ,IAAI,CAAC,CAoI9B;CACF"}
@@ -194,9 +194,7 @@ export class FinderController extends AbstractController {
194
194
  }
195
195
  get params() {
196
196
  const urlState = this.urlManager.state;
197
- const userId = this.tracker.getUserId();
198
- const sessionId = this.tracker.getContext().sessionId;
199
- const pageLoadId = this.tracker.getContext().pageLoadId;
197
+ const { userId, sessionId, pageLoadId } = this.tracker.getContext();
200
198
  const tracking = {};
201
199
  if (userId) {
202
200
  tracking.userId = userId;
@@ -1,5 +1,4 @@
1
1
  import { Product } from '@searchspring/snap-store-mobx';
2
- import { BeaconEvent } from '@searchspring/snap-tracker';
3
2
  import { AbstractController } from '../Abstract/AbstractController';
4
3
  import { ControllerTypes } from '../types';
5
4
  import type { RecommendationStore } from '@searchspring/snap-store-mobx';
@@ -7,28 +6,25 @@ import type { RecommendRequestModel } from '@searchspring/snap-client';
7
6
  import type { RecommendationControllerConfig, ControllerServices, ContextVariables } from '../types';
8
7
  type RecommendationTrackMethods = {
9
8
  product: {
10
- click: (e: MouseEvent, result: Product) => BeaconEvent | undefined;
11
- render: (result: Product) => BeaconEvent | undefined;
12
- impression: (result: Product) => BeaconEvent | undefined;
13
- removedFromBundle: (result: Product) => BeaconEvent | undefined;
14
- addedToBundle: (result: Product) => BeaconEvent | undefined;
9
+ clickThrough: (e: MouseEvent, result: Product) => void;
10
+ click: (e: MouseEvent, result: Product) => void;
11
+ render: (result: Product) => void;
12
+ impression: (result: Product) => void;
13
+ addToCart: (result: Product) => void;
14
+ };
15
+ bundle: {
16
+ addToCart: (results: Product[]) => void;
15
17
  };
16
- click: (e: MouseEvent) => BeaconEvent | undefined;
17
- addBundle: (e: MouseEvent, results: Product[]) => BeaconEvent | undefined;
18
- impression: () => BeaconEvent | undefined;
19
- render: (results?: Product[]) => BeaconEvent | undefined;
20
18
  };
21
19
  export declare class RecommendationController extends AbstractController {
22
20
  type: ControllerTypes;
23
21
  store: RecommendationStore;
24
22
  config: RecommendationControllerConfig;
25
23
  events: {
26
- click?: BeaconEvent;
27
- impression?: BeaconEvent;
28
- render?: BeaconEvent;
29
- product?: Record<string, {
30
- impression?: BeaconEvent;
31
- render?: BeaconEvent;
24
+ product: Record<string, {
25
+ clickThrough?: boolean;
26
+ impression?: boolean;
27
+ render?: boolean;
32
28
  }>;
33
29
  };
34
30
  constructor(config: RecommendationControllerConfig, { client, store, urlManager, eventManager, profiler, logger, tracker }: ControllerServices, context?: ContextVariables);
@@ -1 +1 @@
1
- {"version":3,"file":"RecommendationController.d.ts","sourceRoot":"","sources":["../../../src/Recommendation/RecommendationController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,EAAE,8BAA8B,EAAiB,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEpH,KAAK,0BAA0B,GAAG;IACjC,OAAO,EAAE;QACR,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,CAAC;QACnE,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,CAAC;QACrD,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,CAAC;QACzD,iBAAiB,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,CAAC;QAChE,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,CAAC;KAC5D,CAAC;IACF,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,WAAW,GAAG,SAAS,CAAC;IAClD,SAAS,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,GAAG,SAAS,CAAC;IAC1E,UAAU,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC;IAC1C,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,WAAW,GAAG,SAAS,CAAC;CACzD,CAAC;AAUF,qBAAa,wBAAyB,SAAQ,kBAAkB;IACxD,IAAI,kBAAkC;IACrC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,EAAE,8BAA8B,CAAC;IAE/C,MAAM,EAAE;QACP,KAAK,CAAC,EAAE,WAAW,CAAC;QACpB,UAAU,CAAC,EAAE,WAAW,CAAC;QACzB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,UAAU,CAAC,EAAE,WAAW,CAAC;YAAC,MAAM,CAAC,EAAE,WAAW,CAAA;SAAE,CAAC,CAAC;KAC7E,CAKC;gBAGD,MAAM,EAAE,8BAA8B,EACtC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IA4C3B,KAAK,EAAE,0BAA0B,CAwS5B;IAEL,IAAI,MAAM,IAAI,qBAAqB,CA2BlC;IAED,MAAM,QAAa,QAAQ,IAAI,CAAC,CA0H9B;CACF"}
1
+ {"version":3,"file":"RecommendationController.d.ts","sourceRoot":"","sources":["../../../src/Recommendation/RecommendationController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,EAAE,8BAA8B,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAIrG,KAAK,0BAA0B,GAAG;IACjC,OAAO,EAAE;QACR,YAAY,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QACvD,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QAChD,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QAClC,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QACtC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;KACrC,CAAC;IACF,MAAM,EAAE;QACP,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;KACxC,CAAC;CACF,CAAC;AAUF,qBAAa,wBAAyB,SAAQ,kBAAkB;IACxD,IAAI,kBAAkC;IACrC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,EAAE,8BAA8B,CAAC;IAE/C,MAAM,EAAE;QACP,OAAO,EAAE,MAAM,CACd,MAAM,EACN;YACC,YAAY,CAAC,EAAE,OAAO,CAAC;YACvB,UAAU,CAAC,EAAE,OAAO,CAAC;YACrB,MAAM,CAAC,EAAE,OAAO,CAAC;SACjB,CACD,CAAC;KACF,CAEC;gBAGD,MAAM,EAAE,8BAA8B,EACtC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IAwC3B,KAAK,EAAE,0BAA0B,CAyD/B;IAEF,IAAI,MAAM,IAAI,qBAAqB,CA4BlC;IAED,MAAM,QAAa,QAAQ,IAAI,CAAC,CA6H9B;CACF"}