UNPKG

64.5 kBJavaScriptView Raw
1"use strict";
2var __assign = (this && this.__assign) || function () {
3 __assign = Object.assign || function(t) {
4 for (var s, i = 1, n = arguments.length; i < n; i++) {
5 s = arguments[i];
6 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 t[p] = s[p];
8 }
9 return t;
10 };
11 return __assign.apply(this, arguments);
12};
13Object.defineProperty(exports, "__esModule", { value: true });
14require("./polyfills/custom-event-polyfill");
15var next_tick_function_1 = require("./functions/next-tick.function");
16var query_string_class_1 = require("./classes/query-string.class");
17var package_json_1 = require("../package.json");
18var observable_class_1 = require("./classes/observable.class");
19var fetch_function_1 = require("./functions/fetch.function");
20var assign_function_1 = require("./functions/assign.function");
21var throttle_function_1 = require("./functions/throttle.function");
22var animator_class_1 = require("./classes/animator.class");
23var cookies_class_1 = require("./classes/cookies.class");
24var omit_function_1 = require("./functions/omit.function");
25var get_top_level_domain_1 = require("./functions/get-top-level-domain");
26var server_only_require_function_1 = require("./functions/server-only-require.function");
27var uuid_1 = require("./functions/uuid");
28function datePlusMinutes(minutes) {
29 if (minutes === void 0) { minutes = 30; }
30 return new Date(Date.now() + minutes * 60000);
31}
32var isPositiveNumber = function (thing) {
33 return typeof thing === 'number' && !isNaN(thing) && thing >= 0;
34};
35exports.isReactNative = typeof navigator === 'object' && navigator.product === 'ReactNative';
36exports.validEnvList = [
37 'production',
38 'qa',
39 'development',
40 'dev',
41 'cdn-qa',
42 'cloud',
43 'fast',
44 'cdn2',
45];
46function getQueryParam(url, variable) {
47 var query = url.split('?')[1] || '';
48 var vars = query.split('&');
49 for (var i = 0; i < vars.length; i++) {
50 var pair = vars[i].split('=');
51 if (decodeURIComponent(pair[0]) === variable) {
52 return decodeURIComponent(pair[1]);
53 }
54 }
55 return null;
56}
57var urlParser = {
58 parse: function (url) {
59 var parser = document.createElement('a');
60 parser.href = url;
61 var out = {};
62 var props = 'username password host hostname port protocol origin pathname search hash'.split(' ');
63 for (var i = props.length; i--;) {
64 out[props[i]] = parser[props[i]];
65 }
66 // IE 11 pathname handling workaround
67 // (IE omits preceeding '/', unlike other browsers)
68 if ((out.pathname || out.pathname === '') &&
69 typeof out.pathname === 'string' &&
70 out.pathname.indexOf('/') !== 0) {
71 out.pathname = '/' + out.pathname;
72 }
73 return out;
74 },
75};
76var parse = exports.isReactNative
77 ? function () { return ({}); }
78 : typeof window === 'object'
79 ? urlParser.parse
80 : server_only_require_function_1.default('url').parse;
81function setCookie(name, value, expires) {
82 try {
83 var expiresString = '';
84 // TODO: need to know if secure server side
85 if (expires) {
86 expiresString = '; expires=' + expires.toUTCString();
87 }
88 var secure = exports.isBrowser ? location.protocol === 'https:' : true;
89 document.cookie =
90 name +
91 '=' +
92 (value || '') +
93 expiresString +
94 '; path=/' +
95 ("; domain=" + get_top_level_domain_1.getTopLevelDomain(location.hostname)) +
96 (secure ? ';secure ; SameSite=None' : '');
97 }
98 catch (err) {
99 console.warn('Could not set cookie', err);
100 }
101}
102function getCookie(name) {
103 try {
104 return (decodeURIComponent(document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' +
105 encodeURIComponent(name).replace(/[\-\.\+\*]/g, '\\$&') +
106 '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')) || null);
107 }
108 catch (err) {
109 console.warn('Could not get cookie', err);
110 }
111}
112function size(object) {
113 return Object.keys(object).length;
114}
115function find(target, callback) {
116 var list = target;
117 // Makes sures is always has an positive integer as length.
118 var length = list.length >>> 0;
119 var thisArg = arguments[1];
120 for (var i = 0; i < length; i++) {
121 var element = list[i];
122 if (callback.call(thisArg, element, i, list)) {
123 return element;
124 }
125 }
126}
127var sessionStorageKey = 'builderSessionId';
128var localStorageKey = 'builderVisitorId';
129exports.isBrowser = typeof window !== 'undefined' && !exports.isReactNative;
130exports.isIframe = exports.isBrowser && window.top !== window.self;
131function BuilderComponent(info) {
132 if (info === void 0) { info = {}; }
133 return Builder.Component(info);
134}
135exports.BuilderComponent = BuilderComponent;
136var Builder = /** @class */ (function () {
137 function Builder(apiKey, request, response, forceNewInstance) {
138 var _this = this;
139 if (apiKey === void 0) { apiKey = null; }
140 if (forceNewInstance === void 0) { forceNewInstance = false; }
141 this.request = request;
142 this.response = response;
143 this.authToken = '';
144 this.eventsQueue = [];
145 this.throttledClearEventsQueue = throttle_function_1.throttle(function () {
146 _this.processEventsQueue();
147 // Extend the session cookie
148 _this.setCookie(sessionStorageKey, _this.sessionId, datePlusMinutes(30));
149 }, 100);
150 this.env = 'production';
151 this.sessionId = this.getSessionId();
152 this.targetContent = true;
153 this.cookies = null;
154 // TODO: api options object
155 this.cachebust = false;
156 this.overrideParams = '';
157 this.noCache = false;
158 this.preview = false;
159 this.canTrack$ = new observable_class_1.BehaviorSubject(!this.browserTrackingDisabled);
160 this.apiKey$ = new observable_class_1.BehaviorSubject(null);
161 this.userAttributesChanged = new observable_class_1.BehaviorSubject(null);
162 this.editingMode$ = new observable_class_1.BehaviorSubject(exports.isIframe);
163 // TODO: decorator to do this stuff with the get/set (how do with typing too? compiler?)
164 this.editingModel$ = new observable_class_1.BehaviorSubject(null);
165 this.userAgent = (typeof navigator === 'object' && navigator.userAgent) || '';
166 // Set this to control the userId
167 // TODO: allow changing it mid session and updating existing data to be associated
168 // e.g. for when a user navigates and then logs in
169 this.visitorId = this.getVisitorId();
170 this.autoTrack = !Builder.isBrowser
171 ? false
172 : !this.isDevelopmentEnv &&
173 !(Builder.isBrowser && location.search.indexOf('builder.preview=') !== -1);
174 this.useNewContentApi = false;
175 this.blockContentLoading = '';
176 this.observersByKey = {};
177 this.overrides = {};
178 this.getContentQueue = null;
179 this.priorContentQueue = null;
180 this.testCookiePrefix = 'builder.tests';
181 this.cookieQueue = [];
182 // TODO: use a window variable for this perhaps, e.g. bc webcomponents may be loading builder twice
183 // with it's and react (use rollup build to fix)
184 if (Builder.isBrowser && !forceNewInstance && Builder.singletonInstance) {
185 return Builder.singletonInstance;
186 }
187 if (this.request && this.response) {
188 this.setUserAgent(this.request.headers['user-agent'] || '');
189 this.cookies = new cookies_class_1.default(this.request, this.response);
190 }
191 if (apiKey) {
192 this.apiKey = apiKey;
193 }
194 if (exports.isBrowser) {
195 this.bindMessageListeners();
196 // TODO: postmessage to parent the builder info for every package
197 // type: 'builder.sdk', data: { name: '@builder.io/react', version: '0.1.23' }
198 // (window as any).BUILDER_VERSION = Builder.VERSION;
199 // Ensure always one Builder global singleton
200 // TODO: some people won't want this, e.g. rakuten
201 // Maybe hide this behind symbol or on document, etc
202 // if ((window as any).Builder) {
203 // Builder.components = (window as any).Builder.components;
204 // } else {
205 // (window as any).Builder = Builder;
206 // }
207 }
208 if (exports.isIframe) {
209 this.messageFrameLoaded();
210 }
211 // TODO: on destroy clear subscription
212 this.canTrack$.subscribe(function (value) {
213 if (value) {
214 if (typeof sessionStorage !== 'undefined') {
215 try {
216 if (!sessionStorage.getItem(sessionStorageKey)) {
217 sessionStorage.setItem(sessionStorageKey, _this.sessionId);
218 }
219 }
220 catch (err) {
221 console.debug('Session storage error', err);
222 }
223 }
224 if (_this.eventsQueue.length) {
225 _this.throttledClearEventsQueue();
226 }
227 if (_this.cookieQueue.length) {
228 _this.cookieQueue.forEach(function (item) {
229 _this.setCookie(item[0], item[1]);
230 });
231 _this.cookieQueue.length = 0;
232 }
233 }
234 });
235 if (exports.isBrowser) {
236 // TODO: defer so subclass constructor runs and injects location service
237 this.setTestsFromUrl();
238 // TODO: do this on every request send?
239 this.getOverridesFromQueryString();
240 }
241 }
242 Builder.register = function (type, info) {
243 // TODO: all must have name and can't conflict?
244 var typeList = this.registry[type];
245 if (!typeList) {
246 typeList = this.registry[type] = [];
247 }
248 typeList.push(info);
249 if (Builder.isBrowser) {
250 var message = {
251 type: 'builder.register',
252 data: {
253 type: type,
254 info: info,
255 },
256 };
257 try {
258 parent.postMessage(message, '*');
259 if (parent !== window) {
260 window.postMessage(message, '*');
261 }
262 }
263 catch (err) {
264 console.debug('Could not postmessage', err);
265 }
266 }
267 this.registryChange.next(this.registry);
268 };
269 Builder.registerEditor = function (info) {
270 if (Builder.isBrowser) {
271 window.postMessage({
272 type: 'builder.registerEditor',
273 data: omit_function_1.omit(info, 'component'),
274 }, '*');
275 var host = location.host;
276 if (!(host === 'localhost:1234' || host.indexOf('builder.io') !== -1)) {
277 console.error('Builder.registerEditor() called in the wrong environment! You cannot load custom editors from your app, they must be loaded through the Builder.io app itself. Follow the readme here for more details: https://github.com/builderio/builder/tree/master/plugins/cloudinary or contact chat us in our Spectrum community for help: https://spectrum.chat/builder');
278 }
279 }
280 this.editors.push(info);
281 };
282 Builder.registerPlugin = function (info) {
283 this.plugins.push(info);
284 };
285 Builder.registerAction = function (action) {
286 this.actions.push(action);
287 };
288 Builder.runAction = function (action) {
289 // TODO
290 var actionObject = typeof action === 'string' ? find(this.actions, function (item) { return item.name === action; }) : action;
291 if (!actionObject) {
292 throw new Error("Action not found: " + action);
293 }
294 };
295 Builder.fields = function (name, fields) {
296 var _a;
297 (_a = window.parent) === null || _a === void 0 ? void 0 : _a.postMessage({
298 type: 'builder.fields',
299 data: { name: name, fields: fields },
300 }, '*');
301 };
302 Builder.set = function (settings) {
303 if (Builder.isBrowser) {
304 // TODO: merge
305 Object.assign(this.settings, settings);
306 var message = {
307 type: 'builder.settingsChange',
308 data: this.settings,
309 };
310 parent.postMessage(message, '*');
311 }
312 this.settingsChange.next(this.settings);
313 };
314 Builder.import = function (packageName) {
315 if (!Builder.isBrowser) {
316 // TODO: server side support *maybe*
317 console.warn('Builder.import used on the server - this should only be used in the browser');
318 return;
319 }
320 var System = window.System;
321 if (!System) {
322 console.warn('System.js not available. Please include System.js when using Builder.import');
323 return;
324 }
325 return System.import("https://cdn.builder.io/systemjs/" + packageName);
326 };
327 Object.defineProperty(Builder, "editingPage", {
328 // useCdnApi = false;
329 get: function () {
330 return this._editingPage;
331 },
332 set: function (editingPage) {
333 this._editingPage = editingPage;
334 if (exports.isBrowser && exports.isIframe) {
335 if (editingPage) {
336 document.body.classList.add('builder-editing-page');
337 }
338 else {
339 document.body.classList.remove('builder-editing-page');
340 }
341 }
342 },
343 enumerable: true,
344 configurable: true
345 });
346 Builder.prepareComponentSpecToSend = function (spec) {
347 return __assign(__assign(__assign({}, spec), (spec.inputs && {
348 inputs: spec.inputs.map(function (input) {
349 var _a;
350 // TODO: do for nexted fields too
351 // TODO: probably just convert all functions, not just
352 // TODO: put this in input hooks: { onChange: ..., showIf: ... }
353 var keysToConvertFnToString = ['onChange', 'showIf'];
354 for (var _i = 0, keysToConvertFnToString_1 = keysToConvertFnToString; _i < keysToConvertFnToString_1.length; _i++) {
355 var key = keysToConvertFnToString_1[_i];
356 if (input[key] && typeof input[key] === 'function') {
357 var fn = input[key];
358 input = __assign(__assign({}, input), (_a = {}, _a[key] = "return (" + fn.toString() + ").apply(this, arguments)", _a));
359 }
360 }
361 return input;
362 }),
363 })), { hooks: Object.keys(spec.hooks || {}).reduce(function (memo, key) {
364 var value = spec.hooks && spec.hooks[key];
365 if (!value) {
366 return memo;
367 }
368 if (typeof value === 'string') {
369 memo[key] = value;
370 }
371 else {
372 memo[key] = "return (" + value.toString() + ").apply(this, arguments)";
373 }
374 return memo;
375 }, {}), class: undefined });
376 };
377 // static registerComponent(...) { .. }
378 Builder.registerComponent = function (component, options) {
379 var _a;
380 var spec = __assign(__assign({ class: component }, component.builderOptions), options);
381 this.addComponent(spec);
382 if (exports.isBrowser) {
383 var sendSpec = this.prepareComponentSpecToSend(spec);
384 (_a = window.parent) === null || _a === void 0 ? void 0 : _a.postMessage({
385 type: 'builder.registerComponent',
386 data: sendSpec,
387 }, '*');
388 }
389 };
390 Builder.addComponent = function (component) {
391 var current = find(this.components, function (item) { return item.name === component.name; });
392 if (current) {
393 // FIXME: why does sometimes we get an extra post without class - probably
394 // from postMessage handler wrong in some place
395 if (current.class && !component.class) {
396 return;
397 }
398 this.components.splice(this.components.indexOf(current), 1, component);
399 }
400 else {
401 this.components.push(component);
402 }
403 };
404 // TODO: style guide, etc off this system as well?
405 Builder.component = function (info) {
406 var _this = this;
407 if (info === void 0) { info = {}; }
408 return function (component) {
409 var _a;
410 var spec = __assign(__assign({}, info), { class: component });
411 if (!spec.name) {
412 spec.name = component.name;
413 }
414 _this.addComponent(spec);
415 var sendSpec = _this.prepareComponentSpecToSend(spec);
416 // TODO: serialize component name and inputs
417 if (exports.isBrowser) {
418 (_a = window.parent) === null || _a === void 0 ? void 0 : _a.postMessage({
419 type: 'builder.registerComponent',
420 data: sendSpec,
421 }, '*');
422 }
423 return component;
424 };
425 };
426 Object.defineProperty(Builder, "Component", {
427 get: function () {
428 return this.component;
429 },
430 enumerable: true,
431 configurable: true
432 });
433 Builder.prototype.processEventsQueue = function () {
434 if (!this.eventsQueue.length) {
435 return;
436 }
437 var events = this.eventsQueue;
438 this.eventsQueue = [];
439 var host = this.host;
440 fetch_function_1.fetch(host + "/api/v1/track", {
441 method: 'POST',
442 body: JSON.stringify({ events: events }),
443 headers: {
444 'content-type': 'application/json',
445 },
446 mode: 'cors',
447 }).catch(function () {
448 // Not the end of the world
449 });
450 };
451 Object.defineProperty(Builder.prototype, "browserTrackingDisabled", {
452 get: function () {
453 return Boolean(Builder.isBrowser && (navigator.doNotTrack === '1' || window.builderNoTrack));
454 },
455 enumerable: true,
456 configurable: true
457 });
458 Object.defineProperty(Builder.prototype, "canTrack", {
459 get: function () {
460 return this.canTrack$.value;
461 },
462 set: function (canTrack) {
463 if (this.canTrack !== canTrack) {
464 this.canTrack$.next(canTrack);
465 }
466 },
467 enumerable: true,
468 configurable: true
469 });
470 Object.defineProperty(Builder.prototype, "editingMode", {
471 get: function () {
472 return this.editingMode$.value;
473 },
474 set: function (value) {
475 if (value !== this.editingMode) {
476 this.editingMode$.next(value);
477 }
478 },
479 enumerable: true,
480 configurable: true
481 });
482 Object.defineProperty(Builder.prototype, "editingModel", {
483 get: function () {
484 return this.editingModel$.value;
485 },
486 set: function (value) {
487 if (value !== this.editingModel) {
488 this.editingModel$.next(value);
489 }
490 },
491 enumerable: true,
492 configurable: true
493 });
494 Builder.prototype.findParentElement = function (target, callback, checkElement) {
495 if (checkElement === void 0) { checkElement = true; }
496 if (!(target instanceof HTMLElement)) {
497 return null;
498 }
499 var parent = checkElement ? target : target.parentElement;
500 do {
501 if (!parent) {
502 return null;
503 }
504 var matches = callback(parent);
505 if (matches) {
506 return parent;
507 }
508 } while ((parent = parent.parentElement));
509 return null;
510 };
511 Builder.prototype.findBuilderParent = function (target) {
512 return this.findParentElement(target, function (el) {
513 var id = el.getAttribute('builder-id') || el.id;
514 return Boolean(id && id.indexOf('builder-') === 0);
515 });
516 };
517 Builder.prototype.setUserAgent = function (userAgent) {
518 this.userAgent = userAgent || '';
519 };
520 Builder.prototype.track = function (eventName, properties) {
521 if (properties === void 0) { properties = {}; }
522 // TODO: queue up track requests and fire them off when canTrack set to true - otherwise may get lots of clicks with no impressions
523 if (exports.isIframe || !exports.isBrowser || Builder.isPreviewing) {
524 return;
525 }
526 // batch events
527 this.eventsQueue.push({
528 type: eventName,
529 data: __assign(__assign({}, omit_function_1.omit(properties, 'meta')), { metadata: __assign(__assign({ sdkVersion: Builder.VERSION, url: location.href }, properties.meta), properties.metadata), ownerId: this.apiKey, userAttributes: this.getUserAttributes(), sessionId: this.sessionId, visitorId: this.visitorId }),
530 });
531 if (this.canTrack) {
532 this.throttledClearEventsQueue();
533 }
534 };
535 Builder.prototype.getSessionId = function () {
536 var sessionId = null;
537 try {
538 if (Builder.isBrowser && typeof sessionStorage !== 'undefined') {
539 sessionId = this.getCookie(sessionStorageKey);
540 }
541 }
542 catch (err) {
543 console.debug('Session storage error', err);
544 // It's ok
545 }
546 if (!sessionId) {
547 sessionId = uuid_1.uuid();
548 }
549 // Give the app a second to start up and set canTrack to false if needed
550 if (Builder.isBrowser) {
551 this.setCookie(sessionStorageKey, sessionId, datePlusMinutes(30));
552 }
553 return sessionId;
554 };
555 Builder.prototype.getVisitorId = function () {
556 var _this = this;
557 if (this.visitorId) {
558 return this.visitorId;
559 }
560 var visitorId = null;
561 try {
562 if (Builder.isBrowser && typeof localStorage !== 'undefined') {
563 // TODO: cookie instead?
564 visitorId = localStorage.getItem(localStorageKey);
565 }
566 }
567 catch (err) {
568 console.debug('Local storage error', err);
569 // It's ok
570 }
571 if (!visitorId) {
572 visitorId = uuid_1.uuid();
573 }
574 this.visitorId = visitorId;
575 // Give the app a second to start up and set canTrack to false if needed
576 if (Builder.isBrowser) {
577 setTimeout(function () {
578 try {
579 if (_this.canTrack && typeof localStorage !== 'undefined' && visitorId) {
580 localStorage.setItem(localStorageKey, visitorId);
581 }
582 }
583 catch (err) {
584 console.debug('Session storage error', err);
585 }
586 });
587 }
588 return visitorId;
589 };
590 Builder.prototype.trackImpression = function (contentId, variationId) {
591 if (exports.isIframe || !exports.isBrowser || Builder.isPreviewing) {
592 return;
593 }
594 // TODO: use this.track method
595 this.eventsQueue.push({
596 type: 'impression',
597 data: {
598 contentId: contentId,
599 variationId: variationId !== contentId ? variationId : undefined,
600 ownerId: this.apiKey,
601 userAttributes: this.getUserAttributes(),
602 sessionId: this.sessionId,
603 visitorId: this.visitorId,
604 },
605 });
606 this.throttledClearEventsQueue();
607 };
608 Builder.prototype.trackConversion = function (amount, contentId, variationId, customProperties) {
609 if (exports.isIframe || !exports.isBrowser || Builder.isPreviewing) {
610 return;
611 }
612 var meta = typeof contentId === 'object' ? contentId : customProperties;
613 var useContentId = typeof contentId === 'string' ? contentId : undefined;
614 // TODO: use this.track method
615 this.eventsQueue.push({
616 type: 'conversion',
617 data: {
618 amount: amount,
619 variationId: variationId,
620 meta: meta,
621 contentId: useContentId,
622 ownerId: this.apiKey,
623 userAttributes: this.getUserAttributes(),
624 sessionId: this.sessionId,
625 visitorId: this.visitorId,
626 },
627 });
628 this.throttledClearEventsQueue();
629 };
630 Object.defineProperty(Builder.prototype, "isDevelopmentEnv", {
631 // TODO: set this for QA
632 get: function () {
633 // Automatic determining of development environment
634 return (Builder.isIframe ||
635 (Builder.isBrowser && (location.hostname === 'localhost' || location.port !== '')) ||
636 this.env !== 'production');
637 },
638 enumerable: true,
639 configurable: true
640 });
641 Builder.prototype.trackInteraction = function (contentId, variationId, alreadyTrackedOne, event) {
642 if (alreadyTrackedOne === void 0) { alreadyTrackedOne = false; }
643 if (exports.isIframe || !exports.isBrowser || Builder.isPreviewing) {
644 return;
645 }
646 var target = event && event.target;
647 var targetBuilderElement = target && this.findBuilderParent(target);
648 function round(num) {
649 return Math.round(num * 1000) / 1000;
650 }
651 var metadata = {};
652 if (event) {
653 var clientX = event.clientX, clientY = event.clientY;
654 if (target) {
655 var targetRect = target.getBoundingClientRect();
656 var xOffset = clientX - targetRect.left;
657 var yOffset = clientY - targetRect.top;
658 var xRatio = round(xOffset / targetRect.width);
659 var yRatio = round(yOffset / targetRect.height);
660 metadata.targetOffset = {
661 x: xRatio,
662 y: yRatio,
663 };
664 }
665 if (targetBuilderElement) {
666 var targetRect = targetBuilderElement.getBoundingClientRect();
667 var xOffset = clientX - targetRect.left;
668 var yOffset = clientY - targetRect.top;
669 var xRatio = round(xOffset / targetRect.width);
670 var yRatio = round(yOffset / targetRect.height);
671 metadata.builderTargetOffset = {
672 x: xRatio,
673 y: yRatio,
674 };
675 }
676 }
677 // let selector: string | undefined = undefined;
678 // if (target) {
679 // try {
680 // selector = finder(target);
681 // } catch (err) {
682 // // nbd
683 // }
684 // }
685 var builderId = targetBuilderElement &&
686 (targetBuilderElement.getAttribute('builder-id') || targetBuilderElement.id);
687 if (builderId && targetBuilderElement) {
688 metadata.builderElementIndex = [].slice
689 .call(document.getElementsByClassName(builderId))
690 .indexOf(targetBuilderElement);
691 }
692 // TODO: use this.track method
693 this.eventsQueue.push({
694 type: 'click',
695 data: {
696 contentId: contentId,
697 metadata: metadata,
698 variationId: variationId !== contentId ? variationId : undefined,
699 ownerId: this.apiKey,
700 unique: !alreadyTrackedOne,
701 // targetSelector: selector,
702 targetBuilderElement: builderId || undefined,
703 userAttributes: this.getUserAttributes(),
704 sessionId: this.sessionId,
705 visitorId: this.visitorId,
706 },
707 });
708 this.throttledClearEventsQueue();
709 };
710 Builder.prototype.component = function (info) {
711 if (info === void 0) { info = {}; }
712 return Builder.component(info);
713 };
714 Object.defineProperty(Builder.prototype, "apiKey", {
715 get: function () {
716 return this.apiKey$.value;
717 },
718 set: function (key) {
719 this.apiKey$.next(key);
720 },
721 enumerable: true,
722 configurable: true
723 });
724 Builder.prototype.modifySearch = function (search) {
725 return search.replace(/(^|&|\?)(builder_.*?)=/gi, function (_match, group1, group2) { return group1 + group2.replace(/_/g, '.') + '='; });
726 };
727 Builder.prototype.setTestsFromUrl = function () {
728 var search = this.getLocation().search;
729 var params = query_string_class_1.QueryString.parseDeep(this.modifySearch(search || '').substr(1));
730 var tests = params.builder && params.builder.tests;
731 if (tests && typeof tests === 'object') {
732 for (var key in tests) {
733 if (tests.hasOwnProperty(key)) {
734 this.setTestCookie(key, tests[key]);
735 }
736 }
737 }
738 };
739 Builder.prototype.resetOverrides = function () {
740 // Ugly - pass down instances per request instead using react context
741 // or use builder.get('foo', { req, res }) in react...........
742 Builder.overrideUserAttributes = {};
743 this.cachebust = false;
744 this.noCache = false;
745 this.preview = false;
746 this.editingModel = null;
747 this.overrides = {};
748 this.env = 'production';
749 this.userAgent = '';
750 this.request = undefined;
751 this.response = undefined;
752 };
753 Builder.prototype.getOverridesFromQueryString = function () {
754 var location = this.getLocation();
755 var params = query_string_class_1.QueryString.parseDeep(this.modifySearch(location.search || '').substr(1));
756 var builder = params.builder;
757 if (builder) {
758 var userAttributes = builder.userAttributes, overrides = builder.overrides, env = builder.env, host = builder.host, api = builder.api, cachebust = builder.cachebust, noCache = builder.noCache, preview = builder.preview, editing = builder.editing, frameEditing = builder.frameEditing, overrideParams = builder.params;
759 if (userAttributes) {
760 this.setUserAttributes(userAttributes);
761 }
762 if (overrides) {
763 this.overrides = overrides;
764 }
765 if (exports.validEnvList.indexOf(env || api) > -1) {
766 this.env = env || api;
767 }
768 if (Builder.isEditing) {
769 var editingModel = frameEditing || editing || preview;
770 if (editingModel && editingModel !== 'true') {
771 this.editingModel = editingModel;
772 }
773 }
774 if (cachebust) {
775 this.cachebust = true;
776 }
777 if (noCache) {
778 this.noCache = true;
779 }
780 if (preview) {
781 this.preview = true;
782 }
783 if (params) {
784 this.overrideParams = overrideParams;
785 }
786 }
787 };
788 Builder.prototype.messageFrameLoaded = function () {
789 var _a;
790 (_a = window.parent) === null || _a === void 0 ? void 0 : _a.postMessage({
791 type: 'builder.loaded',
792 data: {
793 value: true,
794 },
795 }, '*');
796 };
797 Builder.prototype.bindMessageListeners = function () {
798 var _this = this;
799 if (exports.isBrowser) {
800 addEventListener('message', function (event) {
801 var _a, _b, _c, _d, _e;
802 var url = parse(event.origin);
803 var isRestricted = ['builder.register', 'builder.registerComponent'].indexOf((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) === -1;
804 if (isRestricted &&
805 !(url.hostname &&
806 (url.hostname === 'builder.io' ||
807 url.hostname.endsWith('.builder.io') ||
808 url.hostname === 'localhost'))) {
809 return;
810 }
811 var data = event.data;
812 if (data) {
813 switch (data.type) {
814 case 'builder.ping': {
815 (_b = window.parent) === null || _b === void 0 ? void 0 : _b.postMessage({
816 type: 'builder.pong',
817 data: {},
818 }, '*');
819 break;
820 }
821 case 'builder.register': {
822 // TODO: possibly do this for all...
823 if (event.source === window) {
824 break;
825 }
826 var options = data.data;
827 if (!options) {
828 break;
829 }
830 var type = options.type, info = options.info;
831 // TODO: all must have name and can't conflict?
832 var typeList = Builder.registry[type];
833 if (!typeList) {
834 typeList = Builder.registry[type] = [];
835 }
836 typeList.push(info);
837 Builder.registryChange.next(Builder.registry);
838 break;
839 }
840 case 'builder.settingsChange': {
841 // TODO: possibly do this for all...
842 if (event.source === window) {
843 break;
844 }
845 var settings = data.data;
846 if (!settings) {
847 break;
848 }
849 Object.assign(Builder.settings, settings);
850 Builder.settingsChange.next(Builder.settings);
851 break;
852 }
853 case 'builder.registerEditor': {
854 // TODO: possibly do this for all...
855 if (event.source === window) {
856 break;
857 }
858 var info_1 = data.data;
859 if (!info_1) {
860 break;
861 }
862 var hasComponent_1 = !!info_1.component;
863 Builder.editors.every(function (thisInfo, index) {
864 if (info_1.name === thisInfo.name) {
865 if (thisInfo.component && !hasComponent_1) {
866 return false;
867 }
868 else {
869 Builder.editors[index] = thisInfo;
870 }
871 return false;
872 }
873 return true;
874 });
875 break;
876 }
877 case 'builder.triggerAnimation': {
878 Builder.animator.triggerAnimation(data.data);
879 break;
880 }
881 case 'builder.contentUpdate':
882 var key = data.data.key || data.data.alias || data.data.entry || data.data.modelName;
883 var contentData = data.data.data; // hmmm...
884 var observer = _this.observersByKey[key];
885 if (observer) {
886 observer.next([contentData]);
887 }
888 break;
889 case 'builder.getComponents':
890 (_c = window.parent) === null || _c === void 0 ? void 0 : _c.postMessage({
891 type: 'builder.components',
892 data: Builder.components.map(function (item) { return Builder.prepareComponentSpecToSend(item); }),
893 }, '*');
894 break;
895 case 'builder.editingModel':
896 _this.editingModel = data.data.model;
897 break;
898 case 'builder.registerComponent':
899 var componentData = data.data;
900 Builder.addComponent(componentData);
901 break;
902 case 'builder.blockContentLoading':
903 if (typeof data.data.model === 'string') {
904 _this.blockContentLoading = data.data.model;
905 }
906 break;
907 case 'builder.editingMode':
908 var editingMode = data.data;
909 if (editingMode) {
910 _this.editingMode = true;
911 document.body.classList.add('builder-editing');
912 }
913 else {
914 _this.editingMode = false;
915 document.body.classList.remove('builder-editing');
916 }
917 break;
918 case 'builder.editingPageMode':
919 var editingPageMode = data.data;
920 Builder.editingPage = editingPageMode;
921 break;
922 case 'builder.overrideUserAttributes':
923 var userAttributes = data.data;
924 assign_function_1.assign(Builder.overrideUserAttributes, userAttributes);
925 _this.flushGetContentQueue(true);
926 // TODO: refetch too
927 break;
928 case 'builder.overrideTestGroup':
929 var _f = data.data, variationId = _f.variationId, contentId = _f.contentId;
930 if (variationId && contentId) {
931 _this.setTestCookie(contentId, variationId);
932 _this.flushGetContentQueue(true);
933 }
934 break;
935 case 'builder.evaluate': {
936 var text = data.data.text;
937 var args = data.data.arguments || [];
938 var id_1 = data.data.id;
939 // tslint:disable-next-line:no-function-constructor-with-string-args
940 var fn = new Function(text);
941 var result = void 0;
942 var error = null;
943 try {
944 result = fn.apply(_this, args);
945 }
946 catch (err) {
947 error = err;
948 }
949 if (error) {
950 (_d = window.parent) === null || _d === void 0 ? void 0 : _d.postMessage({
951 type: 'builder.evaluateError',
952 data: { id: id_1, error: error.message },
953 }, '*');
954 }
955 else {
956 if (result && typeof result.then === 'function') {
957 result
958 .then(function (finalResult) {
959 var _a;
960 (_a = window.parent) === null || _a === void 0 ? void 0 : _a.postMessage({
961 type: 'builder.evaluateResult',
962 data: { id: id_1, result: finalResult },
963 }, '*');
964 })
965 .catch(console.error);
966 }
967 else {
968 (_e = window.parent) === null || _e === void 0 ? void 0 : _e.postMessage({
969 type: 'builder.evaluateResult',
970 data: { result: result, id: id_1 },
971 }, '*');
972 }
973 }
974 break;
975 }
976 }
977 }
978 });
979 }
980 };
981 Object.defineProperty(Builder.prototype, "defaultCanTrack", {
982 get: function () {
983 return Boolean(Builder.isBrowser &&
984 navigator.userAgent.trim() &&
985 !navigator.userAgent.match(/bot|crawler|spider|robot|crawling|prerender|google|baidu|bing|msn|duckduckbot|teoma|slurp|yandex|phantom|headless|selenium|puppeteer/i) &&
986 !this.browserTrackingDisabled);
987 },
988 enumerable: true,
989 configurable: true
990 });
991 Builder.prototype.init = function (apiKey, canTrack, req, res) {
992 if (canTrack === void 0) { canTrack = this.defaultCanTrack; }
993 if (req) {
994 this.request = req;
995 }
996 if (res) {
997 this.response = res;
998 }
999 this.canTrack = canTrack;
1000 this.apiKey = apiKey;
1001 return this;
1002 };
1003 // TODO: allow adding location object as property and/or in constructor
1004 Builder.prototype.getLocation = function () {
1005 var parsedLocation = {};
1006 // in ssr mode
1007 if (this.request) {
1008 parsedLocation = parse(this.request.url);
1009 }
1010 // in the browser
1011 if (typeof location === 'object') {
1012 parsedLocation = parse(location.href);
1013 }
1014 // IE11 bug with parsed path being empty string
1015 // causes issues with our user targeting
1016 if (parsedLocation.pathname === '') {
1017 parsedLocation.pathname = '/';
1018 }
1019 return parsedLocation;
1020 };
1021 Builder.prototype.getUserAttributes = function (userAgent) {
1022 if (userAgent === void 0) { userAgent = this.userAgent || ''; }
1023 var isMobile = {
1024 Android: function () {
1025 return userAgent.match(/Android/i);
1026 },
1027 BlackBerry: function () {
1028 return userAgent.match(/BlackBerry/i);
1029 },
1030 iOS: function () {
1031 return userAgent.match(/iPhone|iPod/i);
1032 },
1033 Opera: function () {
1034 return userAgent.match(/Opera Mini/i);
1035 },
1036 Windows: function () {
1037 return userAgent.match(/IEMobile/i) || userAgent.match(/WPDesktop/i);
1038 },
1039 any: function () {
1040 return (isMobile.Android() ||
1041 isMobile.BlackBerry() ||
1042 isMobile.iOS() ||
1043 isMobile.Opera() ||
1044 isMobile.Windows());
1045 },
1046 };
1047 var isTablet = userAgent.match(/Tablet|iPad/i);
1048 var url = this.getLocation();
1049 return __assign({ urlPath: url.pathname, host: url.host || url.hostname,
1050 // TODO: maybe an option to choose to target off of mobile/tablet/desktop or just mobile/desktop
1051 device: isTablet ? 'tablet' : isMobile.any() ? 'mobile' : 'desktop' }, Builder.overrideUserAttributes);
1052 };
1053 Builder.prototype.setUserAttributes = function (options) {
1054 assign_function_1.assign(Builder.overrideUserAttributes, options);
1055 this.userAttributesChanged.next(options);
1056 };
1057 Builder.prototype.get = function (modelName, options) {
1058 if (options === void 0) { options = {}; }
1059 var instance = this;
1060 if (!Builder.isBrowser && (options.req || options.res)) {
1061 instance = new Builder(options.apiKey || this.apiKey, options.req, options.res);
1062 }
1063 else {
1064 if (options.apiKey && !this.apiKey) {
1065 this.apiKey = options.apiKey;
1066 }
1067 }
1068 return instance.queueGetContent(modelName, options).map(
1069 /* map( */ function (matches) {
1070 var match = matches && matches[0];
1071 if (Builder.isStatic) {
1072 return match;
1073 }
1074 var matchData = match && match.data;
1075 if (!matchData) {
1076 return null;
1077 }
1078 if (typeof matchData.blocksString !== 'undefined') {
1079 matchData.blocks = JSON.parse(matchData.blocksString);
1080 delete matchData.blocksString;
1081 }
1082 return {
1083 // TODO: add ab test info here and other high level stuff
1084 data: matchData,
1085 id: match.id,
1086 variationId: match.testVariationId || match.variationId,
1087 testVariationId: match.testVariationId || match.variationId,
1088 testVariationName: match.testVariationName,
1089 };
1090 });
1091 // );
1092 };
1093 // TODO: entry id in options
1094 Builder.prototype.queueGetContent = function (modelName, options) {
1095 var _this = this;
1096 if (options === void 0) { options = {}; }
1097 // TODO: if query do modelName + query
1098 var key = options.key ||
1099 options.alias ||
1100 // TODO: SDKs only pass entry key when given to them, and never when editing...
1101 // options.entry ||
1102 // TODO: this is ugly - instead of multiple of same model with different options are sent
1103 // say requires key/alias. Or if not perhaps make a reliable hash of the options and use that.
1104 // TODO: store last user state on last request and if user attributes different now
1105 // give a warning that need to use keys to request new contente
1106 // (options &&
1107 // Object.keys(options).filter(key => key !== 'model').length &&
1108 // JSON.stringify({ model: modelName, ...options, initialContent: undefined })) ||
1109 modelName;
1110 var isEditingThisModel = this.editingModel === modelName;
1111 // TODO: include params in this key........
1112 var currentObservable = this.observersByKey[key];
1113 // if (options.query && options.query._id) {
1114 // this.flushGetContentQueue([options])
1115 // }
1116 if (this.apiKey === 'DEMO' && !this.overrides[key] && !options.initialContent) {
1117 options.initialContent = [];
1118 }
1119 var initialContent = options.initialContent;
1120 // TODO: refresh option in options
1121 if (currentObservable && (!currentObservable.value || options.cache)) {
1122 // TODO: test if this ran, otherwise on 404 some observers may never be called...
1123 if (currentObservable.value) {
1124 next_tick_function_1.nextTick(function () {
1125 // TODO: return a new observable and only that one fires subscribers, don't refire for existing ones
1126 currentObservable.next(currentObservable.value);
1127 });
1128 }
1129 return currentObservable;
1130 }
1131 if (isEditingThisModel) {
1132 if (Builder.isBrowser) {
1133 parent.postMessage({ type: 'builder.updateContent' }, '*');
1134 }
1135 }
1136 if (!initialContent /* || isEditingThisModel */) {
1137 if (!this.getContentQueue) {
1138 this.getContentQueue = [];
1139 }
1140 this.getContentQueue.push(__assign(__assign({}, options), { model: modelName, key: key }));
1141 if (this.getContentQueue && this.getContentQueue.length >= 5) {
1142 this.flushGetContentQueue();
1143 }
1144 else {
1145 next_tick_function_1.nextTick(function () {
1146 _this.flushGetContentQueue();
1147 });
1148 }
1149 }
1150 var observable = new observable_class_1.BehaviorSubject(null);
1151 this.observersByKey[key] = observable;
1152 if (initialContent) {
1153 next_tick_function_1.nextTick(function () {
1154 // TODO: need to testModify this I think...?
1155 observable.next(initialContent);
1156 });
1157 }
1158 return observable;
1159 };
1160 Builder.prototype.requestUrl = function (url) {
1161 if (Builder.isBrowser) {
1162 // TODO: send auth header if builder.authToken
1163 return fetch_function_1.fetch(url, this.authToken
1164 ? {
1165 headers: {
1166 Authorization: "Bearer " + this.authToken,
1167 },
1168 }
1169 : undefined).then(function (res) { return res.json(); });
1170 }
1171 return new Promise(function (resolve, reject) {
1172 var module = url.indexOf('http:') === 0 ? server_only_require_function_1.default('http') : server_only_require_function_1.default('https');
1173 module
1174 .get(url, function (resp) {
1175 var data = '';
1176 // A chunk of data has been recieved.
1177 resp.on('data', function (chunk) {
1178 data += chunk;
1179 });
1180 // The whole response has been received. Print out the result.
1181 resp.on('end', function () {
1182 resolve(JSON.parse(data));
1183 });
1184 })
1185 .on('error', function (error) {
1186 reject(error);
1187 });
1188 });
1189 };
1190 Object.defineProperty(Builder.prototype, "host", {
1191 get: function () {
1192 if (this.env.includes('//')) {
1193 return this.env;
1194 }
1195 if (this.env.includes('.')) {
1196 return 'http://' + this.env;
1197 }
1198 switch (this.env) {
1199 case 'qa':
1200 return 'https://qa.builder.io';
1201 case 'fast':
1202 return 'https://fast.builder.io';
1203 case 'cloud':
1204 return 'https://cloud.builder.io';
1205 case 'cdn2':
1206 return 'https://cdn2.builder.io';
1207 case 'cdn-qa':
1208 return 'https://cdn-qa.builder.io';
1209 case 'development':
1210 case 'dev':
1211 return 'http://localhost:5000';
1212 default:
1213 return 'https://cdn.builder.io';
1214 }
1215 },
1216 enumerable: true,
1217 configurable: true
1218 });
1219 Builder.prototype.flushGetContentQueue = function (usePastQueue, useQueue) {
1220 var _this = this;
1221 if (usePastQueue === void 0) { usePastQueue = false; }
1222 if (!this.apiKey) {
1223 throw new Error('Builder needs to be initialized with an API key!');
1224 }
1225 if (!usePastQueue && !this.getContentQueue) {
1226 return;
1227 }
1228 var queue = useQueue || (usePastQueue ? this.priorContentQueue : this.getContentQueue) || [];
1229 // TODO: do this on every request send?
1230 this.getOverridesFromQueryString();
1231 var queryParams = {
1232 omit: 'meta.componentsUsed',
1233 apiKey: this.apiKey,
1234 };
1235 var pageQueryParams = typeof location !== 'undefined'
1236 ? query_string_class_1.QueryString.parseDeep(location.search.substr(1))
1237 : undefined || {};
1238 var userAttributes =
1239 // FIXME: HACK: only checks first in queue for user attributes overrides, should check all
1240 // TODO: merge user attributes provided here with defaults and current user attiributes (?)
1241 queue && queue[0].userAttributes
1242 ? queue[0].userAttributes
1243 : this.targetContent
1244 ? this.getUserAttributes()
1245 : {
1246 urlPath: this.getLocation().pathname,
1247 };
1248 var fullUrlQueueItem = queue.find(function (item) { return !!item.includeUrl; });
1249 if (fullUrlQueueItem) {
1250 var location_1 = this.getLocation();
1251 if (location_1.origin) {
1252 queryParams.url = "" + location_1.origin + location_1.pathname + location_1.search;
1253 }
1254 }
1255 var urlQueueItem = useQueue === null || useQueue === void 0 ? void 0 : useQueue.find(function (item) { return item.url; });
1256 if (urlQueueItem === null || urlQueueItem === void 0 ? void 0 : urlQueueItem.url) {
1257 userAttributes.urlPath = urlQueueItem.url.split('?')[0];
1258 }
1259 // TODO: merge in the attribute from query string ones
1260 // TODO: make this an option per component/request
1261 queryParams.userAttributes = Builder.useNewApi
1262 ? userAttributes
1263 : JSON.stringify(userAttributes);
1264 if (!usePastQueue && !useQueue) {
1265 this.priorContentQueue = queue;
1266 this.getContentQueue = null;
1267 }
1268 var cachebust = this.cachebust ||
1269 exports.isIframe ||
1270 pageQueryParams.cachebust ||
1271 pageQueryParams['builder.cachebust'];
1272 if (cachebust || this.env !== 'production') {
1273 queryParams.cachebust = true;
1274 }
1275 if (Builder.isEditing) {
1276 queryParams.isEditing = true;
1277 }
1278 if (this.noCache || this.env !== 'production') {
1279 queryParams.noCache = true;
1280 }
1281 if (size(this.overrides)) {
1282 for (var key in this.overrides) {
1283 if (this.overrides.hasOwnProperty(key)) {
1284 queryParams["overrides." + key] = this.overrides[key];
1285 }
1286 }
1287 }
1288 if (Builder.useNewApi && !Builder.isReact) {
1289 // TODO: remove me once v1 page editors converted to v2
1290 // queryParams.extractCss = true;
1291 queryParams.prerender = true;
1292 }
1293 if (Builder.useNewApi) {
1294 for (var _i = 0, queue_1 = queue; _i < queue_1.length; _i++) {
1295 var options = queue_1[_i];
1296 if (options.format) {
1297 queryParams.format = options.format;
1298 }
1299 // TODO: remove me and make permodel
1300 if (options.static) {
1301 queryParams.static = options.static;
1302 }
1303 if (options.cachebust) {
1304 queryParams.cachebust = options.cachebust;
1305 }
1306 if (isPositiveNumber(options.cacheSeconds)) {
1307 queryParams.cacheSeconds = options.cacheSeconds;
1308 }
1309 if (isPositiveNumber(options.staleCacheSeconds)) {
1310 queryParams.staleCacheSeconds = options.staleCacheSeconds;
1311 }
1312 var properties = [
1313 'prerender',
1314 'extractCss',
1315 'limit',
1316 'offset',
1317 'query',
1318 'preview',
1319 'model',
1320 'entry',
1321 'rev',
1322 'static',
1323 ];
1324 for (var _a = 0, properties_1 = properties; _a < properties_1.length; _a++) {
1325 var key = properties_1[_a];
1326 var value = options[key];
1327 if (value !== undefined) {
1328 queryParams.options = queryParams.options || {};
1329 queryParams.options[options.key] = queryParams.options[options.key] || {};
1330 queryParams.options[options.key][key] = JSON.stringify(value);
1331 }
1332 }
1333 }
1334 }
1335 if (this.preview) {
1336 queryParams.preview = 'true';
1337 }
1338 var hasParams = Object.keys(queryParams).length > 0;
1339 // TODO: option to force dev or qa api here
1340 var host = this.useNewContentApi ? 'https://lambda.builder.codes' : this.host;
1341 var keyNames = queue.map(function (item) { return encodeURIComponent(item.key); }).join(',');
1342 if (this.overrideParams) {
1343 var params = omit_function_1.omit(query_string_class_1.QueryString.parse(this.overrideParams), 'apiKey');
1344 assign_function_1.assign(queryParams, params);
1345 }
1346 var queryStr = Builder.useNewApi
1347 ? query_string_class_1.QueryString.stringifyDeep(queryParams)
1348 : query_string_class_1.QueryString.stringify(queryParams);
1349 var promise = this.requestUrl(host + "/api/v1/" + (Builder.useNewApi ? 'query' : 'content') + "/" + this.apiKey + "/" + keyNames +
1350 (queryParams && hasParams ? "?" + queryStr : '')).then(function (result) {
1351 for (var _i = 0, queue_2 = queue; _i < queue_2.length; _i++) {
1352 var options = queue_2[_i];
1353 var keyName = options.key;
1354 if (options.model === _this.blockContentLoading) {
1355 continue;
1356 }
1357 var isEditingThisModel = _this.editingModel === options.model;
1358 if (isEditingThisModel && Builder.isEditing) {
1359 parent.postMessage({ type: 'builder.updateContent' }, '*');
1360 // return;
1361 }
1362 var observer = _this.observersByKey[keyName];
1363 if (!observer) {
1364 return;
1365 }
1366 var data = result[keyName];
1367 var sorted = data; // sortBy(data, item => item.priority);
1368 if (data) {
1369 var testModifiedResults = Builder.isStatic
1370 ? sorted
1371 : _this.processResultsForTests(sorted);
1372 observer.next(testModifiedResults);
1373 }
1374 else {
1375 var search = _this.getLocation().search;
1376 if ((search || '').includes('builder.preview=' + options.model)) {
1377 var previewData = {
1378 id: 'preview',
1379 name: 'Preview',
1380 data: {},
1381 };
1382 observer.next([previewData]);
1383 }
1384 observer.next([]);
1385 }
1386 }
1387 }, function (err) {
1388 for (var _i = 0, queue_3 = queue; _i < queue_3.length; _i++) {
1389 var options = queue_3[_i];
1390 var observer = _this.observersByKey[options.key];
1391 if (!observer) {
1392 return;
1393 }
1394 observer.error(err);
1395 }
1396 });
1397 return promise;
1398 };
1399 Builder.prototype.processResultsForTests = function (results) {
1400 var _this = this;
1401 var _a;
1402 var mappedResults = results.map(function (item) {
1403 if (!item.variations) {
1404 return item;
1405 }
1406 var cookieValue = _this.getTestCookie(item.id);
1407 var cookieVariation = cookieValue === item.id ? item : item.variations[cookieValue];
1408 if (cookieVariation) {
1409 return __assign(__assign({}, item), { data: cookieVariation.data, variationId: cookieValue, testVariationId: cookieValue, testVariationName: cookieVariation.name });
1410 }
1411 if (item.variations && size(item.variations)) {
1412 var n = 0;
1413 var random = Math.random();
1414 for (var id in item.variations) {
1415 var variation = item.variations[id];
1416 var testRatio = variation.testRatio;
1417 n += testRatio;
1418 if (random < n) {
1419 _this.setTestCookie(item.id, variation.id);
1420 var variationName = variation.name || (variation.id === item.id ? 'Default variation' : '');
1421 return __assign(__assign({}, item), { data: variation.data, variationId: variation.id, testVariationId: variation.id, variationName: variationName, testVariationName: variationName });
1422 }
1423 }
1424 _this.setTestCookie(item.id, item.id);
1425 }
1426 return __assign(__assign(__assign({}, item), { variationId: item.id }), (item.variations &&
1427 size(item.variations) && {
1428 testVariationId: item.id,
1429 testVariationName: 'Default variation',
1430 }));
1431 });
1432 if (exports.isIframe) {
1433 (_a = window.parent) === null || _a === void 0 ? void 0 : _a.postMessage({ type: 'builder.contentResults', data: { results: mappedResults } }, '*');
1434 }
1435 return mappedResults;
1436 };
1437 Builder.prototype.getTestCookie = function (contentId) {
1438 return this.getCookie(this.testCookiePrefix + "." + contentId);
1439 };
1440 Builder.prototype.setTestCookie = function (contentId, variationId) {
1441 if (!this.canTrack) {
1442 this.cookieQueue.push([contentId, variationId]);
1443 return;
1444 }
1445 // 30 days from now
1446 var future = new Date();
1447 future.setDate(future.getDate() + 30);
1448 return this.setCookie(this.testCookiePrefix + "." + contentId, variationId, future);
1449 };
1450 Builder.prototype.getCookie = function (name) {
1451 if (this.cookies) {
1452 return this.cookies.get(name);
1453 }
1454 return Builder.isBrowser && getCookie(name);
1455 };
1456 Builder.prototype.setCookie = function (name, value, expires) {
1457 if (this.cookies) {
1458 return this.cookies.set(name, value, {
1459 expires: expires,
1460 secure: this.getLocation().protocol === 'https:',
1461 });
1462 }
1463 return Builder.isBrowser && setCookie(name, value, expires);
1464 };
1465 Builder.prototype.getContent = function (modelName, options) {
1466 if (options === void 0) { options = {}; }
1467 if (!this.apiKey) {
1468 throw new Error('Builder needs to be initialized with an API key!');
1469 }
1470 return this.queueGetContent(modelName);
1471 };
1472 Builder.VERSION = package_json_1.version;
1473 Builder.components = [];
1474 Builder.useNewApi = true;
1475 // isStatic delegates all variants rendering and election to the variants provider
1476 // which in turn will always render default variation on server and winning variation on client
1477 Builder.isStatic = false;
1478 Builder.animator = new animator_class_1.Animator();
1479 Builder.nextTick = next_tick_function_1.nextTick;
1480 Builder.throttle = throttle_function_1.throttle;
1481 Builder.editors = [];
1482 Builder.plugins = [];
1483 Builder.actions = [];
1484 Builder.registry = {};
1485 Builder.registryChange = new observable_class_1.BehaviorSubject({});
1486 Builder._editingPage = false;
1487 Builder.isIframe = exports.isIframe;
1488 Builder.isBrowser = exports.isBrowser;
1489 Builder.isReactNative = exports.isReactNative;
1490 Builder.isServer = !exports.isBrowser && !exports.isReactNative;
1491 Builder.previewingModel = Builder.isBrowser && getQueryParam(location.href, 'builder.preview');
1492 Builder.settings = {};
1493 Builder.settingsChange = new observable_class_1.BehaviorSubject({});
1494 // TODO: this is quick and dirty, do better implementation later. Also can be unreliable
1495 // if page 301s etc. Use a query param instead? also could have issues with redirects. Injecting var could
1496 // work but is async...
1497 Builder.isEditing = Boolean(exports.isIframe &&
1498 ((document.referrer && document.referrer.match(/builder\.io|localhost:1234/)) ||
1499 location.search.indexOf('builder.frameEditing=') !== -1));
1500 Builder.isPreviewing = Boolean(exports.isBrowser &&
1501 (location.search.indexOf('builder.preview=') !== -1 ||
1502 location.search.indexOf('builder.frameEditing=') !== -1));
1503 Builder.isReact = false;
1504 Builder.overrideUserAttributes = {};
1505 return Builder;
1506}());
1507exports.Builder = Builder;
1508//# sourceMappingURL=builder.class.js.map
\No newline at end of file