@movable/studio-framework 3.0.0-canary.0 → 3.0.0-esmodules.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/dist/index.js CHANGED
@@ -2,11 +2,15 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
5
+ var React = require('react');
6
+ var ReactDOM = require('react-dom');
7
+ var CD = require('cropduster');
6
8
 
7
- var React = _interopDefault(require('react'));
8
- var ReactDOM = _interopDefault(require('react-dom'));
9
- var CD = _interopDefault(require('cropduster'));
9
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
+
11
+ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
12
+ var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
13
+ var CD__default = /*#__PURE__*/_interopDefaultLegacy(CD);
10
14
 
11
15
  // Capturama needs a polyfill for Object.entries
12
16
  if (typeof Object.entries === 'undefined') {
@@ -50,13 +54,50 @@ function _extends() {
50
54
  }
51
55
 
52
56
  const {
53
- entries
57
+ entries: entries$2
54
58
  } = Object;
55
- const STYLE_WHITELIST = new Set(['top', 'left', 'color', 'width', 'border', 'borderRadius', 'borderColor', 'borderStyle', 'borderWidth', 'boxSizing', 'boxShadow', 'height', 'marginTop', 'marginLeft', 'marginBottom', 'marginRight', 'zIndex', 'fontSize', 'position', 'textAlign', 'transform', 'textShadow', 'lineHeight', 'fontFamily', 'fontWeight', 'textTransform', 'letterSpacing', 'backgroundColor', 'backgroundImage', 'transformOrigin', 'opacity', 'overflow', 'display', 'flexDirection', 'justifyContent', 'whiteSpace']);
59
+ const STYLE_WHITELIST = {
60
+ top: true,
61
+ left: true,
62
+ color: true,
63
+ width: true,
64
+ border: true,
65
+ borderRadius: true,
66
+ borderColor: true,
67
+ borderStyle: true,
68
+ borderWidth: true,
69
+ boxSizing: true,
70
+ boxShadow: true,
71
+ height: true,
72
+ marginTop: true,
73
+ marginLeft: true,
74
+ marginBottom: true,
75
+ marginRight: true,
76
+ zIndex: true,
77
+ fontSize: true,
78
+ position: true,
79
+ textAlign: true,
80
+ transform: true,
81
+ textShadow: true,
82
+ lineHeight: true,
83
+ fontFamily: true,
84
+ fontWeight: true,
85
+ textTransform: true,
86
+ letterSpacing: true,
87
+ backgroundColor: true,
88
+ backgroundImage: true,
89
+ transformOrigin: true,
90
+ opacity: true,
91
+ overflow: true,
92
+ display: true,
93
+ flexDirection: true,
94
+ justifyContent: true,
95
+ whiteSpace: true
96
+ };
56
97
  const PROPERTY_TRANSFORMS = {
57
98
  transform: value => `rotate(${value}deg)`
58
99
  };
59
- const TRANSFORMS_MAP = new Map(entries(PROPERTY_TRANSFORMS));
100
+ const TRANSFORMS_MAP = new Map(entries$2(PROPERTY_TRANSFORMS));
60
101
 
61
102
  function transformStyle(key, value) {
62
103
  const transform = TRANSFORMS_MAP.get(key);
@@ -64,9 +105,13 @@ function transformStyle(key, value) {
64
105
  }
65
106
 
66
107
  function styleFromAttributes(tag) {
67
- return entries(tag).filter(([key]) => STYLE_WHITELIST.has(key)).map(([key, value]) => [key, transformStyle(key, value)]).reduce((result, [key, value]) => ({ ...result,
68
- [key]: value
69
- }), {});
108
+ const result = {};
109
+
110
+ for (const key of Object.keys(tag)) {
111
+ if (STYLE_WHITELIST[key]) result[key] = transformStyle(key, tag[key]);
112
+ }
113
+
114
+ return result;
70
115
  }
71
116
 
72
117
  function normalizeRichTextStyles(attributes = {}) {
@@ -118,7 +163,7 @@ function runModifiers(element, fns) {
118
163
  }
119
164
  }
120
165
 
121
- class StyledElement extends React.Component {
166
+ class StyledElement extends React__default['default'].Component {
122
167
  updateStyleElement() {
123
168
  const domNode = this.styleElementRef;
124
169
 
@@ -127,7 +172,7 @@ class StyledElement extends React.Component {
127
172
  tag,
128
173
  registryResolver
129
174
  } = this.props;
130
- const elementModifierFunctions = resolveElementModifiers((tag === null || tag === void 0 ? void 0 : tag.appliedElementModifiers) || [], registryResolver);
175
+ const elementModifierFunctions = resolveElementModifiers(tag?.appliedElementModifiers || [], registryResolver);
131
176
  runModifiers(domNode, elementModifierFunctions);
132
177
  }
133
178
  }
@@ -155,7 +200,7 @@ class StyledElement extends React.Component {
155
200
  } = this.props;
156
201
  const style = styleFromAttributes(tag);
157
202
  const className = classNameFromAttributes(tag);
158
- return /*#__PURE__*/React.createElement("div", {
203
+ return /*#__PURE__*/React__default['default'].createElement("div", {
159
204
  className: className,
160
205
  style: style,
161
206
  ref: this.setStyleElementRef
@@ -370,6 +415,13 @@ function StudioErrorWrapper(error, message) {
370
415
 
371
416
  return error.clone(wrappedMessage);
372
417
  }
418
+ class StudioError extends BaseStudioError {
419
+ constructor(dynamicProperty, message) {
420
+ super(message);
421
+ this.setSourceDynamicProperty(dynamicProperty);
422
+ }
423
+
424
+ }
373
425
 
374
426
  function getStateTuple(prop, resolver) {
375
427
  if (!prop) {
@@ -388,7 +440,7 @@ function getStateTuple(prop, resolver) {
388
440
  return resolver.getPropertyStateTuple(propertyGroupKey, propertyPath, context);
389
441
  }
390
442
 
391
- class TagComponent extends React.Component {
443
+ class TagComponent extends React__default['default'].Component {
392
444
  static getDerivedStateFromProps(props) {
393
445
  const {
394
446
  tag,
@@ -433,6 +485,7 @@ class TagComponent extends React.Component {
433
485
  getPropertyWithFallback(prop, fallback) {
434
486
  const {
435
487
  tag,
488
+ canvas,
436
489
  propertyResolver
437
490
  } = this.props;
438
491
  const [result, error] = getStateTuple(prop, propertyResolver);
@@ -442,7 +495,7 @@ class TagComponent extends React.Component {
442
495
 
443
496
  if (propertyFallback) {
444
497
  const warning = error ? StudioErrorWrapper(error, 'Fallback Warning') : new BaseStudioError('Fallback Warning');
445
- const consoleMessage = warning.setErrorType('FallbackWarning').setTag(tag).setSourceDynamicProperty(prop).setEntryDynamicProperty(prop);
498
+ const consoleMessage = warning.setErrorType('FallbackWarning').setTag(tag).setCanvas(canvas).setSourceType('tag').setSourceDynamicProperty(prop).setEntryDynamicProperty(prop);
446
499
  console.warn(consoleMessage);
447
500
  return propertyFallback;
448
501
  }
@@ -450,10 +503,10 @@ class TagComponent extends React.Component {
450
503
  if (passedFallback) return fallback;
451
504
 
452
505
  if (error) {
453
- throw StudioErrorWrapper(error).setTag(tag);
506
+ throw StudioErrorWrapper(error).setTag(tag).setCanvas(canvas).setSourceType('tag');
454
507
  }
455
508
 
456
- throw new BaseStudioError('Missing Data').setErrorType('MissingData').setTag(tag).setSourceDynamicProperty(prop).setEntryDynamicProperty(prop);
509
+ throw new BaseStudioError('Missing Data').setErrorType('MissingData').setTag(tag).setCanvas(canvas).setSourceType('tag').setSourceDynamicProperty(prop).setEntryDynamicProperty(prop);
457
510
  }
458
511
 
459
512
  constructor(props) {
@@ -486,7 +539,7 @@ class TagComponent extends React.Component {
486
539
  return null;
487
540
  }
488
541
 
489
- return /*#__PURE__*/React.createElement(StudioTool, {
542
+ return /*#__PURE__*/React__default['default'].createElement(StudioTool, {
490
543
  tag: tag,
491
544
  getPropertyState: this.getPropertyState,
492
545
  getPropertyWithFallback: this.getPropertyWithFallback
@@ -495,29 +548,32 @@ class TagComponent extends React.Component {
495
548
 
496
549
  }
497
550
 
498
- class Tags extends React.Component {
551
+ class Tags extends React__default['default'].Component {
499
552
  render() {
500
553
  const {
501
554
  registryResolver,
502
555
  propertyResolver,
503
556
  onPendingPromise,
504
- tags
557
+ tags,
558
+ canvas
505
559
  } = this.props;
506
560
  return tags.map((tag, index) => {
507
561
  const {
508
562
  subtags
509
563
  } = tag;
510
- return /*#__PURE__*/React.createElement(StyledElement, {
564
+ return /*#__PURE__*/React__default['default'].createElement(StyledElement, {
511
565
  key: index,
512
566
  tag: tag,
513
567
  registryResolver: registryResolver
514
- }, subtags ? /*#__PURE__*/React.createElement(Tags, {
568
+ }, subtags ? /*#__PURE__*/React__default['default'].createElement(Tags, {
515
569
  tags: subtags,
570
+ canvas: canvas,
516
571
  registryResolver: registryResolver,
517
572
  propertyResolver: propertyResolver,
518
573
  onPendingPromise: onPendingPromise
519
- }) : /*#__PURE__*/React.createElement(TagComponent, {
574
+ }) : /*#__PURE__*/React__default['default'].createElement(TagComponent, {
520
575
  tag: tag,
576
+ canvas: canvas,
521
577
  registryResolver: registryResolver,
522
578
  propertyResolver: propertyResolver,
523
579
  onPendingPromise: onPendingPromise
@@ -527,7 +583,7 @@ class Tags extends React.Component {
527
583
 
528
584
  }
529
585
 
530
- class SizeContainer extends React.Component {
586
+ class SizeContainer extends React__default['default'].Component {
531
587
  updateContainer() {
532
588
  const domNode = this.containerElementRef;
533
589
 
@@ -536,7 +592,7 @@ class SizeContainer extends React.Component {
536
592
  canvas,
537
593
  registryResolver
538
594
  } = this.props;
539
- const elementModifierFunctions = resolveElementModifiers((canvas === null || canvas === void 0 ? void 0 : canvas.appliedElementModifiers) || [], registryResolver);
595
+ const elementModifierFunctions = resolveElementModifiers(canvas?.appliedElementModifiers || [], registryResolver);
540
596
  runModifiers(domNode, elementModifierFunctions);
541
597
  }
542
598
  }
@@ -565,7 +621,7 @@ class SizeContainer extends React.Component {
565
621
  },
566
622
  children
567
623
  } = this.props;
568
- return /*#__PURE__*/React.createElement("div", {
624
+ return /*#__PURE__*/React__default['default'].createElement("div", {
569
625
  id: "mi_size_container",
570
626
  style: {
571
627
  width,
@@ -652,7 +708,6 @@ function contextAccessor(context, cb) {
652
708
  let _Symbol$iterator;
653
709
 
654
710
  const ERROR_KEY = Symbol('error');
655
- const TEMP_STORAGE = Symbol('temporary-key');
656
711
 
657
712
  function isError(t) {
658
713
  return t && typeof t === 'object' && ERROR_KEY in t;
@@ -672,6 +727,8 @@ class CacheMap {
672
727
  _defineProperty(this, "acceptingKeys", true);
673
728
 
674
729
  _defineProperty(this, "tempContext", void 0);
730
+
731
+ _defineProperty(this, "tempStorageKey", context => JSON.stringify(context));
675
732
  }
676
733
 
677
734
  get(context) {
@@ -709,11 +766,12 @@ class CacheMap {
709
766
 
710
767
  freezeContext() {
711
768
  this.acceptingKeys = false;
769
+ const temp = this.tempStorageKey(this.tempContext);
712
770
 
713
- if (this.cache.has(TEMP_STORAGE)) {
714
- const value = this.cache.get(TEMP_STORAGE);
771
+ if (this.cache.has(temp)) {
772
+ const value = this.cache.get(temp);
715
773
  const context = this.tempContext;
716
- this.cache.delete(TEMP_STORAGE);
774
+ this.cache.delete(temp);
717
775
  this.cache.set(this.cacheKeyFor(context), value);
718
776
  }
719
777
  }
@@ -731,7 +789,7 @@ class CacheMap {
731
789
  cacheKeyFor(context) {
732
790
  if (this.acceptingKeys) {
733
791
  this.tempContext = context;
734
- return TEMP_STORAGE;
792
+ return this.tempStorageKey(context);
735
793
  }
736
794
 
737
795
  if (this.keyCache.has(context)) {
@@ -790,6 +848,7 @@ class ComputeThrottler {
790
848
  result = compute();
791
849
  } catch (e) {
792
850
  this.isPending = false;
851
+ this.tryNextInQueue();
793
852
  throw e;
794
853
  }
795
854
 
@@ -880,6 +939,31 @@ function parseGetPropertyArgs(args) {
880
939
  return [false, '', arg3];
881
940
  }
882
941
  }
942
+ function parseGetPropertyProxyArgs(args) {
943
+ let callback = null;
944
+
945
+ if (typeof args[args.length - 1] === 'function') {
946
+ callback = args.pop();
947
+ }
948
+
949
+ if (!callback) {
950
+ callback = (_, error) => {
951
+ if (error) {
952
+ throw error;
953
+ }
954
+ };
955
+ }
956
+
957
+ const [arg1, arg2, arg3] = args;
958
+
959
+ if (typeof arg2 === 'string' && typeof arg3 !== 'function') {
960
+ return [arg1, arg2, arg3, callback];
961
+ } else if (typeof arg1 === 'string' && typeof arg2 !== 'function' && typeof arg2 !== 'string') {
962
+ return [false, arg1, arg2, callback];
963
+ } else if (typeof arg3 !== 'function') {
964
+ return [false, '', arg3, callback];
965
+ }
966
+ }
883
967
  function parseSetPropertyArgs(args) {
884
968
  if (args.length === 2) {
885
969
  const [arg1, arg2] = args;
@@ -889,18 +973,42 @@ function parseSetPropertyArgs(args) {
889
973
  }
890
974
  }
891
975
 
892
- let DebugType;
976
+ /** This implementation is adapted from uuidjs rather than requiring the full library
977
+ * see: https://github.com/movableink/studio-framework/pull/762#discussion_r555175040
978
+ */
979
+ const byteToHex = [];
980
+
981
+ for (let i = 0; i < 256; ++i) {
982
+ byteToHex.push((i + 0x100).toString(16).substr(1));
983
+ }
984
+
985
+ function generateV4Uuid() {
986
+ // generate cryptographically strong random numbers
987
+ // see: github.com/uuidjs/uuid/blob/master/src/rng-browser.js
988
+ const unsigned8BitArray = new Uint8Array(16);
989
+ const getRandomValues = crypto.getRandomValues.bind(crypto);
990
+ const randomValues = getRandomValues(unsigned8BitArray); // per 4.4, set bits for version and `clock_seq_hi_and_reserved`
991
+ // see: github.com/uuidjs/uuid/blob/master/src/v4.js
893
992
 
894
- (function (DebugType) {
895
- DebugType["cacheRead"] = "cache-read";
896
- DebugType["propertyRequest"] = "property-request";
897
- DebugType["resolve"] = "resolve";
898
- DebugType["await"] = "await";
899
- DebugType["error"] = "error";
900
- DebugType["override"] = "override";
901
- })(DebugType || (DebugType = {}));
993
+ randomValues[6] = randomValues[6] & 0x0f | 0x40;
994
+ randomValues[8] = randomValues[8] & 0x3f | 0x80; // strigify v4 format
995
+ // see: github.com/uuidjs/uuid/blob/master/src/stringify.js
902
996
 
903
- class InertDebugEvent {
997
+ return (byteToHex[randomValues[0]] + byteToHex[randomValues[1]] + byteToHex[randomValues[2]] + byteToHex[randomValues[3]] + '-' + byteToHex[randomValues[4]] + byteToHex[randomValues[5]] + '-' + byteToHex[randomValues[6]] + byteToHex[randomValues[7]] + '-' + byteToHex[randomValues[8]] + byteToHex[randomValues[9]] + '-' + byteToHex[randomValues[10]] + byteToHex[randomValues[11]] + byteToHex[randomValues[12]] + byteToHex[randomValues[13]] + byteToHex[randomValues[14]] + byteToHex[randomValues[15]]).toLowerCase();
998
+ }
999
+
1000
+ let EventType;
1001
+
1002
+ (function (EventType) {
1003
+ EventType["cacheRead"] = "cache-read";
1004
+ EventType["propertyRequest"] = "property-request";
1005
+ EventType["resolve"] = "resolve";
1006
+ EventType["await"] = "await";
1007
+ EventType["error"] = "error";
1008
+ EventType["override"] = "override";
1009
+ })(EventType || (EventType = {}));
1010
+
1011
+ class InertEvent {
904
1012
  makeChildEvent() {
905
1013
  return this;
906
1014
  } // eslint-disable-next-line @typescript-eslint/no-empty-function
@@ -913,8 +1021,12 @@ class InertDebugEvent {
913
1021
 
914
1022
  }
915
1023
 
916
- class ActiveDebugEvent {
1024
+ class ActiveEvent {
917
1025
  constructor(type, dynamicProperty) {
1026
+ _defineProperty(this, "id", void 0);
1027
+
1028
+ _defineProperty(this, "createdAt", void 0);
1029
+
918
1030
  _defineProperty(this, "type", void 0);
919
1031
 
920
1032
  _defineProperty(this, "dynamicProperty", void 0);
@@ -927,10 +1039,12 @@ class ActiveDebugEvent {
927
1039
 
928
1040
  this.type = type;
929
1041
  this.dynamicProperty = dynamicProperty;
1042
+ this.id = generateV4Uuid();
1043
+ this.createdAt = Date.now();
930
1044
  }
931
1045
 
932
1046
  makeChildEvent(type, prop) {
933
- const event = new ActiveDebugEvent(type, prop || this.dynamicProperty);
1047
+ const event = new ActiveEvent(type, prop || this.dynamicProperty);
934
1048
  this.childEvents.push(event);
935
1049
  return event;
936
1050
  }
@@ -945,10 +1059,10 @@ class ActiveDebugEvent {
945
1059
 
946
1060
  }
947
1061
  function createEvent(type, prop) {
948
- return new ActiveDebugEvent(type, prop);
1062
+ return new ActiveEvent(type, prop);
949
1063
  }
950
1064
  function createInertEvent() {
951
- return new InertDebugEvent();
1065
+ return new InertEvent();
952
1066
  }
953
1067
 
954
1068
  function deprecateGetContext(key) {
@@ -1010,6 +1124,10 @@ class Property {
1010
1124
  return this.overrideFn;
1011
1125
  }
1012
1126
 
1127
+ get dependencies() {
1128
+ return this.dependentProperties;
1129
+ }
1130
+
1013
1131
  getCacheFor(context) {
1014
1132
  if (this.memoizeCache) {
1015
1133
  return this.memoizeCache.get(context);
@@ -1078,8 +1196,9 @@ class Property {
1078
1196
 
1079
1197
  getPropertyProxy(context, requestEvent) {
1080
1198
  return (...args) => {
1081
- const [grouping, key, extraContext = {}] = parseGetPropertyArgs(args);
1082
- const property = this.computed.getPropertyInstance(grouping || this.grouping, key);
1199
+ const [grouping, key, extraContext = {}, callback] = parseGetPropertyProxyArgs(args);
1200
+ const propertyGroupKey = grouping || this.grouping;
1201
+ const property = this.computed.getPropertyInstance(propertyGroupKey, key);
1083
1202
 
1084
1203
  if (!property) {
1085
1204
  return null;
@@ -1090,21 +1209,89 @@ class Property {
1090
1209
  ...extraContext
1091
1210
  };
1092
1211
  const prop = {
1093
- propertyGroupKey: grouping,
1212
+ propertyGroupKey: propertyGroupKey,
1094
1213
  propertyPath: key,
1095
1214
  context: fullContext
1096
1215
  };
1097
- const childEvent = requestEvent.makeChildEvent(DebugType.propertyRequest, prop);
1098
- const result = property.compute(fullContext, childEvent);
1216
+ const childEvent = requestEvent.makeChildEvent(EventType.propertyRequest, prop);
1217
+ let result;
1218
+ let error;
1219
+ if (window.MICapture?.isPreview) this.throwIfDependencyCycleDetected(property);
1220
+
1221
+ try {
1222
+ result = property.compute(fullContext, childEvent);
1223
+ } catch (e) {
1224
+ error = e;
1225
+ }
1099
1226
 
1100
1227
  if (isPromise(result)) {
1101
1228
  throw new PromiseException(result);
1102
1229
  }
1103
1230
 
1104
- return result;
1231
+ try {
1232
+ if (error) {
1233
+ callback(null, error);
1234
+ return;
1235
+ } else {
1236
+ callback(result, null);
1237
+ return result;
1238
+ }
1239
+ } catch (e) {
1240
+ const wrappedError = StudioErrorWrapper(e).setSourceDynamicProperty(prop);
1241
+ throw wrappedError;
1242
+ }
1105
1243
  };
1106
1244
  }
1107
1245
 
1246
+ throwIfDependencyCycleDetected(property) {
1247
+ performance.now();
1248
+ const hasDependentProperties = property.dependentProperties.size;
1249
+
1250
+ if (hasDependentProperties) {
1251
+ const dependencyCycleProperties = this.getDependencyCycle(property.dependentProperties);
1252
+ const isDependencyCycleDetected = dependencyCycleProperties.length;
1253
+
1254
+ if (isDependencyCycleDetected) {
1255
+ throw new Error(`Dependency cycle detected: ${dependencyCycleProperties.map(prop => {
1256
+ return prop.grouping ? prop.grouping + '::' + prop.propertyKey : prop.propertyKey;
1257
+ }).join(' > ')}`);
1258
+ }
1259
+ }
1260
+ }
1261
+
1262
+ getDependencyCycle(dependencies) {
1263
+ const propsToInspect = [...dependencies.values()];
1264
+ const propsInspected = new Set();
1265
+ const propsCompleted = new Set();
1266
+
1267
+ while (propsToInspect.length) {
1268
+ const currentProperty = propsToInspect.pop();
1269
+ const detectionResult = this.detectCycle(currentProperty, propsInspected, propsCompleted);
1270
+ if (detectionResult.length) return [currentProperty, ...detectionResult];
1271
+ }
1272
+
1273
+ return [];
1274
+ }
1275
+
1276
+ detectCycle(currentProperty, propsInspected, propsCompleted) {
1277
+ if (!propsCompleted.has(currentProperty)) {
1278
+ propsInspected.add(currentProperty);
1279
+ propsCompleted.add(currentProperty);
1280
+
1281
+ for (const prop of currentProperty.dependencies) {
1282
+ if (!propsCompleted.has(prop)) {
1283
+ const detectionResult = this.detectCycle(prop, propsInspected, propsCompleted);
1284
+ if (detectionResult.length) return [prop, ...detectionResult];
1285
+ }
1286
+
1287
+ if (propsInspected.has(prop)) return [prop];
1288
+ }
1289
+ }
1290
+
1291
+ propsInspected.delete(currentProperty);
1292
+ return [];
1293
+ }
1294
+
1108
1295
  generateComputeArgs(passedContext = {}, requestEvent) {
1109
1296
  const context = contextAccessor(passedContext, key => this.memoizeCache.addContextKey(key));
1110
1297
  const getProperty = this.getPropertyProxy(passedContext, requestEvent);
@@ -1134,7 +1321,7 @@ class Property {
1134
1321
  const overwrittenResult = overrideFn(result, context);
1135
1322
 
1136
1323
  if (overrideFn !== DEFAULT_OVERRIDE) {
1137
- const overrideEvent = event.makeChildEvent(DebugType.override);
1324
+ const overrideEvent = event.makeChildEvent(EventType.override);
1138
1325
  overrideEvent.setValue(result);
1139
1326
  }
1140
1327
 
@@ -1153,7 +1340,7 @@ class Property {
1153
1340
  };
1154
1341
  const wrappedError = StudioErrorWrapper(error).setSourceDynamicProperty(dynamicProperty).setEntryDynamicProperty(dynamicProperty);
1155
1342
  this.memoizeCache.setError(context, wrappedError);
1156
- event.makeChildEvent(DebugType.error);
1343
+ event.makeChildEvent(EventType.error);
1157
1344
  event.setError(wrappedError);
1158
1345
  throw wrappedError;
1159
1346
  }
@@ -1161,7 +1348,7 @@ class Property {
1161
1348
  async resolvePromiseResult(promise, context, event) {
1162
1349
  try {
1163
1350
  const result = await promise;
1164
- event.makeChildEvent(DebugType.resolve);
1351
+ event.makeChildEvent(EventType.resolve);
1165
1352
  const overwrittenResult = this.overrideResult(result, context, event);
1166
1353
  this.memoizeCache.set(context, overwrittenResult);
1167
1354
  event.setValue(overwrittenResult);
@@ -1173,7 +1360,7 @@ class Property {
1173
1360
 
1174
1361
  async yieldPromiseException(exception, context, event) {
1175
1362
  try {
1176
- event.makeChildEvent(DebugType.await);
1363
+ event.makeChildEvent(EventType.await);
1177
1364
  await exception.promise;
1178
1365
  return this.trySyncCompute(context, event);
1179
1366
  } catch (error) {
@@ -1187,10 +1374,10 @@ class Property {
1187
1374
  const result = this.computeFn(computeArgs);
1188
1375
 
1189
1376
  if (isPromise(result)) {
1190
- event.makeChildEvent(DebugType.await);
1377
+ event.makeChildEvent(EventType.await);
1191
1378
  return this.resolvePromiseResult(result, context, event);
1192
1379
  } else {
1193
- event.makeChildEvent(DebugType.resolve);
1380
+ event.makeChildEvent(EventType.resolve);
1194
1381
  const overwrittenResult = this.overrideResult(result, context, event);
1195
1382
  this.memoizeCache.set(context, overwrittenResult);
1196
1383
  event.setValue(overwrittenResult);
@@ -1204,7 +1391,7 @@ class Property {
1204
1391
  cachedCompute(context, event) {
1205
1392
  // If memoize cache is not set up, compute compute and set up the cache.
1206
1393
  if (this.memoizeCache.has(context)) {
1207
- event.makeChildEvent(DebugType.cacheRead);
1394
+ event.makeChildEvent(EventType.cacheRead);
1208
1395
  return this.memoizeCache.get(context);
1209
1396
  } // Immediately cache the response (it wil be re-written when finalized if it's a promise).
1210
1397
 
@@ -1297,7 +1484,7 @@ class GroupedRegistry {
1297
1484
  }
1298
1485
 
1299
1486
  const {
1300
- entries: entries$2
1487
+ entries
1301
1488
  } = Object;
1302
1489
 
1303
1490
  function isComputeFn(fn) {
@@ -1312,13 +1499,13 @@ class AsyncComputed {
1312
1499
 
1313
1500
  _defineProperty(this, "globProperties", new GroupedRegistry());
1314
1501
 
1315
- _defineProperty(this, "debugEnabled", false);
1502
+ _defineProperty(this, "eventsEnabled", false);
1316
1503
 
1317
- _defineProperty(this, "propertyDebugEventsTree", []);
1504
+ _defineProperty(this, "eventsTree", []);
1318
1505
  }
1319
1506
 
1320
- enableDebugMode() {
1321
- this.debugEnabled = true;
1507
+ enableEvents() {
1508
+ this.eventsEnabled = true;
1322
1509
  }
1323
1510
 
1324
1511
  setProperty(...args) {
@@ -1337,7 +1524,7 @@ class AsyncComputed {
1337
1524
  }
1338
1525
 
1339
1526
  setProperties(object, prefix) {
1340
- entries$2(object).forEach(([path, value]) => {
1527
+ entries(object).forEach(([path, value]) => {
1341
1528
  const fullPath = prefix ? `${prefix}.${path}` : path;
1342
1529
  this.setProperty(fullPath, value);
1343
1530
  });
@@ -1353,8 +1540,8 @@ class AsyncComputed {
1353
1540
  return result;
1354
1541
  }
1355
1542
 
1356
- get propertyDebugEvents() {
1357
- return this.propertyDebugEventsTree;
1543
+ get propertyEvents() {
1544
+ return this.eventsTree;
1358
1545
  }
1359
1546
 
1360
1547
  getPropertyInstance(grouping, path) {
@@ -1425,14 +1612,14 @@ class AsyncComputed {
1425
1612
 
1426
1613
  let requestEvent;
1427
1614
 
1428
- if (this.debugEnabled) {
1429
- requestEvent = createEvent(DebugType.propertyRequest, {
1615
+ if (this.eventsEnabled) {
1616
+ requestEvent = createEvent(EventType.propertyRequest, {
1430
1617
  propertyGroupKey: property.grouping,
1431
1618
  // make sure we're using the property's actual grouping
1432
1619
  propertyPath: path,
1433
1620
  context: context
1434
1621
  });
1435
- this.propertyDebugEventsTree.push(requestEvent);
1622
+ this.eventsTree.push(requestEvent);
1436
1623
  } else {
1437
1624
  requestEvent = createInertEvent();
1438
1625
  }
@@ -1521,9 +1708,9 @@ class AsyncComputed {
1521
1708
 
1522
1709
  }
1523
1710
 
1524
- const ShapeTag = () => /*#__PURE__*/React.createElement("div", null);
1711
+ const ShapeTag = () => /*#__PURE__*/React__default['default'].createElement("div", null);
1525
1712
 
1526
- class StudioTool extends React.Component {
1713
+ class StudioTool extends React__default['default'].Component {
1527
1714
  // TODO: We need to be able to extend StudioTool and create a new call signature for dynamicProperties
1528
1715
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
1529
1716
  static dynamicProperties(tag) {
@@ -1536,7 +1723,7 @@ function trimTextOverflow(element) {
1536
1723
  const parentElement = element.parentElement;
1537
1724
  const parentBox = parentElement.getBoundingClientRect(); // It fits already; no need to resize.
1538
1725
 
1539
- if (!overflowsParent(element, parentBox)) {
1726
+ if (!overflowsParent$1(element, parentBox)) {
1540
1727
  return;
1541
1728
  }
1542
1729
 
@@ -1561,7 +1748,7 @@ function truncateText(element, parentBox) {
1561
1748
  element.parentElement.nextElementSibling.remove();
1562
1749
  }
1563
1750
 
1564
- while (endPos > 0 && overflowsParent(element, parentBox)) {
1751
+ while (endPos > 0 && overflowsParent$1(element, parentBox)) {
1565
1752
  endPos = currentText.lastIndexOf(' ');
1566
1753
  currentText = currentText.substr(0, endPos);
1567
1754
 
@@ -1592,7 +1779,7 @@ function findOverlappingElement(element, parentBox) {
1592
1779
  const children = Array.from(element.children);
1593
1780
 
1594
1781
  for (const child of children) {
1595
- if (overflowsParent(child, parentBox)) {
1782
+ if (overflowsParent$1(child, parentBox)) {
1596
1783
  return findOverlappingElement(child, parentBox);
1597
1784
  }
1598
1785
  } // if no child is found to overlap, return self.
@@ -1601,14 +1788,14 @@ function findOverlappingElement(element, parentBox) {
1601
1788
  return element;
1602
1789
  }
1603
1790
 
1604
- function overflowsParent(element, parentBox) {
1791
+ function overflowsParent$1(element, parentBox) {
1605
1792
  const selfBox = element.getBoundingClientRect();
1606
1793
  const isOutside = !(selfBox.left >= parentBox.left && selfBox.right <= parentBox.right && selfBox.bottom <= parentBox.bottom && element.scrollHeight <= parentBox.height && element.scrollWidth <= parentBox.width);
1607
1794
  return isOutside;
1608
1795
  }
1609
1796
 
1610
1797
  const {
1611
- min
1798
+ min: min$1
1612
1799
  } = Math;
1613
1800
  function convertToEms(insertProperties, baseFontSize) {
1614
1801
  return insertProperties.map(prop => {
@@ -1629,16 +1816,16 @@ function shrinkToFit(element, richTextStructure, baseFontSize, minFontSize) {
1629
1816
  } = element;
1630
1817
  const parentBox = parentElement.getBoundingClientRect(); // It fits already; no need to resize.
1631
1818
 
1632
- if (!overflowsParent$1(parentBox, element)) {
1819
+ if (!overflowsParent(parentBox, element)) {
1633
1820
  return true;
1634
1821
  }
1635
1822
 
1636
1823
  const smallestFontRatio = richTextStructure.reduce((smallest, op) => {
1637
- smallest = min(smallest, getFontRatio(op));
1824
+ smallest = min$1(smallest, getFontRatio(op));
1638
1825
 
1639
1826
  if (op.spans) {
1640
1827
  for (const span of op.spans) {
1641
- smallest = min(smallest, getFontRatio(span));
1828
+ smallest = min$1(smallest, getFontRatio(span));
1642
1829
  }
1643
1830
  }
1644
1831
 
@@ -1664,7 +1851,7 @@ function resizeToFit(currentFontSize, selfElement, parentBox, minimumFontSize, s
1664
1851
 
1665
1852
  selfElement.style.fontSize = `${currentFontSize}px`; // Re-run if still outside of parent container
1666
1853
 
1667
- if (overflowsParent$1(parentBox, selfElement)) {
1854
+ if (overflowsParent(parentBox, selfElement)) {
1668
1855
  // truncate if min font size reached
1669
1856
  const nextFontSize = currentFontSize - 1;
1670
1857
  return resizeToFit(nextFontSize, selfElement, parentBox, minimumFontSize, smallestRatio);
@@ -1673,7 +1860,7 @@ function resizeToFit(currentFontSize, selfElement, parentBox, minimumFontSize, s
1673
1860
  return true;
1674
1861
  }
1675
1862
 
1676
- function overflowsParent$1(parentBox, selfElement) {
1863
+ function overflowsParent(parentBox, selfElement) {
1677
1864
  const range = document.createRange();
1678
1865
  range.selectNode(selfElement);
1679
1866
  const selfBox = range.getBoundingClientRect();
@@ -1713,7 +1900,7 @@ const DEFAULT_P_STYLES = {
1713
1900
  padding: 0
1714
1901
  };
1715
1902
  const {
1716
- min: min$1
1903
+ min
1717
1904
  } = Math;
1718
1905
  function makeRenderStructure(ops, fitToOneLine) {
1719
1906
  const paragraphs = [];
@@ -1800,7 +1987,7 @@ function getMinFontSize(ops, defaultSize) {
1800
1987
  const fontSize = op.attributes && op.attributes.fontSize;
1801
1988
 
1802
1989
  if (fontSize) {
1803
- return min$1(curMin, parseFloat(fontSize.toString()));
1990
+ return min(curMin, parseFloat(fontSize.toString()));
1804
1991
  }
1805
1992
 
1806
1993
  return curMin;
@@ -1813,7 +2000,7 @@ const RichTextSpan = ({
1813
2000
  fitToOneLine
1814
2001
  }) => {
1815
2002
  const whiteSpace = fitToOneLine ? 'nowrap' : 'pre-wrap';
1816
- return /*#__PURE__*/React.createElement("span", {
2003
+ return /*#__PURE__*/React__default['default'].createElement("span", {
1817
2004
  style: { ...normalizeRichTextStyles(attributes),
1818
2005
  whiteSpace
1819
2006
  }
@@ -1826,9 +2013,9 @@ const isNewLine = ({
1826
2013
 
1827
2014
  const haveSpans = spans => spans.length;
1828
2015
 
1829
- const mapSpans = (spans, fitToOneLine) => spans.map((span, n) => isNewLine(span) ? /*#__PURE__*/React.createElement("br", {
2016
+ const mapSpans = (spans, fitToOneLine) => spans.map((span, n) => isNewLine(span) ? /*#__PURE__*/React__default['default'].createElement("br", {
1830
2017
  key: n
1831
- }) : /*#__PURE__*/React.createElement(RichTextSpan, _extends({
2018
+ }) : /*#__PURE__*/React__default['default'].createElement(RichTextSpan, _extends({
1832
2019
  key: n
1833
2020
  }, span, {
1834
2021
  fitToOneLine: fitToOneLine
@@ -1844,7 +2031,7 @@ class RichTextElement extends StudioTool {
1844
2031
  didOverflow: false
1845
2032
  });
1846
2033
 
1847
- _defineProperty(this, "elementRef", /*#__PURE__*/React.createRef());
2034
+ _defineProperty(this, "elementRef", /*#__PURE__*/React__default['default'].createRef());
1848
2035
  }
1849
2036
 
1850
2037
  static dynamicProperties(tag) {
@@ -1936,18 +2123,18 @@ class RichTextElement extends StudioTool {
1936
2123
  const style = {
1937
2124
  fontSize: baseFontSize
1938
2125
  };
1939
- return /*#__PURE__*/React.createElement("div", {
2126
+ return /*#__PURE__*/React__default['default'].createElement("div", {
1940
2127
  className: "rich-text-element",
1941
2128
  ref: this.elementRef,
1942
2129
  style: style
1943
2130
  }, ops.map(({
1944
2131
  attributes,
1945
2132
  spans
1946
- }, i) => /*#__PURE__*/React.createElement("p", {
2133
+ }, i) => /*#__PURE__*/React__default['default'].createElement("p", {
1947
2134
  style: { ...attributes
1948
2135
  },
1949
2136
  key: i
1950
- }, haveSpans(spans) ? mapSpans(spans, fitToOneLine) : /*#__PURE__*/React.createElement("br", null))));
2137
+ }, haveSpans(spans) ? mapSpans(spans, fitToOneLine) : /*#__PURE__*/React__default['default'].createElement("br", null))));
1951
2138
  }
1952
2139
 
1953
2140
  }
@@ -1965,7 +2152,12 @@ function ImageTag({
1965
2152
  throw new Error('Could not render image as no URL was provided');
1966
2153
  }
1967
2154
 
1968
- CD.waitForAsset(imageUrl);
2155
+ const isDataURL = imageUrl.match(/^data:/);
2156
+
2157
+ if (!isDataURL) {
2158
+ CD__default['default'].waitForAsset(imageUrl);
2159
+ }
2160
+
1969
2161
  const styles = {
1970
2162
  height: '100%',
1971
2163
  width: '100%',
@@ -1973,7 +2165,7 @@ function ImageTag({
1973
2165
  left: '0',
1974
2166
  top: '0'
1975
2167
  };
1976
- return /*#__PURE__*/React.createElement("img", {
2168
+ return /*#__PURE__*/React__default['default'].createElement("img", {
1977
2169
  src: imageUrl,
1978
2170
  style: styles,
1979
2171
  alt: altText,
@@ -2007,13 +2199,22 @@ const HIDDEN_IMAGE_STYLE = {
2007
2199
  position: 'absolute',
2008
2200
  opacity: 0
2009
2201
  };
2010
- function stylesForImage(imageUrl, cropStyle) {
2202
+ function stylesForImage(imageUrl, cropStyle, imageAnchor = {
2203
+ horizontalPosition: 'left',
2204
+ verticalPosition: 'top'
2205
+ }) {
2011
2206
  const cropStyles = CROPPING_OPTIONS[cropStyle];
2012
- return {
2207
+ const {
2208
+ horizontalPosition,
2209
+ verticalPosition
2210
+ } = imageAnchor;
2211
+ const imageStyles = {
2013
2212
  background: `url("${imageUrl}")`,
2014
2213
  ...BASE_STYLES,
2015
2214
  ...cropStyles
2016
2215
  };
2216
+ imageStyles.backgroundPosition = `${horizontalPosition} ${verticalPosition}`;
2217
+ return imageStyles;
2017
2218
  }
2018
2219
 
2019
2220
  class DynamicImageTag extends StudioTool {
@@ -2030,21 +2231,27 @@ class DynamicImageTag extends StudioTool {
2030
2231
  tag: {
2031
2232
  dynamicProperty,
2032
2233
  isCropped,
2033
- dynamicAltText
2234
+ dynamicAltText,
2235
+ imageAnchor
2034
2236
  },
2035
2237
  getPropertyWithFallback
2036
2238
  } = this.props;
2037
2239
  const src = getPropertyWithFallback(dynamicProperty);
2038
2240
  const altText = getPropertyWithFallback(dynamicAltText, '');
2039
- CD.waitForAsset(src);
2241
+ const isDataURL = src.match(/^data:/);
2242
+
2243
+ if (!isDataURL) {
2244
+ CD__default['default'].waitForAsset(src);
2245
+ }
2246
+
2040
2247
  const styleType = isCropped ? 'cropped' : 'contained';
2041
- const styles = stylesForImage(src, styleType);
2042
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
2248
+ const styles = stylesForImage(src, styleType, imageAnchor);
2249
+ return /*#__PURE__*/React__default['default'].createElement(React__default['default'].Fragment, null, /*#__PURE__*/React__default['default'].createElement("div", {
2043
2250
  className: "studio-dynamic-image",
2044
2251
  role: "image",
2045
2252
  "aria-label": altText,
2046
2253
  style: styles
2047
- }), /*#__PURE__*/React.createElement("img", {
2254
+ }), /*#__PURE__*/React__default['default'].createElement("img", {
2048
2255
  alt: "hidden",
2049
2256
  "aria-hidden": "true",
2050
2257
  className: "studio-hidden-dynamic-image",
@@ -2063,7 +2270,7 @@ var bootstrapApp = (resolver => {
2063
2270
  resolver.registerComponent('image', DynamicImageTag);
2064
2271
  });
2065
2272
 
2066
- class App extends React.Component {
2273
+ class App extends React__default['default'].Component {
2067
2274
  constructor(props) {
2068
2275
  super(props);
2069
2276
 
@@ -2140,19 +2347,20 @@ class App extends React.Component {
2140
2347
  canvas
2141
2348
  }
2142
2349
  } = this;
2143
- const container = /*#__PURE__*/React.createElement(SizeContainer, {
2350
+ const container = /*#__PURE__*/React__default['default'].createElement(SizeContainer, {
2144
2351
  dimensions: dimensions || this.dimensionsDefault,
2145
2352
  registryResolver: registryResolver,
2146
2353
  canvas: canvas
2147
- }, /*#__PURE__*/React.createElement(Tags, {
2354
+ }, /*#__PURE__*/React__default['default'].createElement(Tags, {
2148
2355
  tags: tags || [],
2356
+ canvas: canvas,
2149
2357
  registryResolver: registryResolver,
2150
2358
  propertyResolver: propertyResolver,
2151
2359
  onPendingPromise: this.onPendingPromise
2152
2360
  }));
2153
2361
 
2154
2362
  if (clickthroughURL) {
2155
- return /*#__PURE__*/React.createElement("a", {
2363
+ return /*#__PURE__*/React__default['default'].createElement("a", {
2156
2364
  id: "mi_dynamic_link",
2157
2365
  href: clickthroughURL,
2158
2366
  rel: "noopener noreferrer",
@@ -2218,7 +2426,7 @@ function setupAppDefaults() {
2218
2426
  },
2219
2427
  namespacedOptions = []
2220
2428
  } = MI;
2221
- const queryParams = CD.params();
2429
+ const queryParams = CD__default['default'].params();
2222
2430
  const attributes = document.querySelector('.mi-attributes');
2223
2431
  const {
2224
2432
  canvases,
@@ -2296,7 +2504,7 @@ function animation(app) {
2296
2504
 
2297
2505
  var n=function(t,s,r,e){var u;s[0]=0;for(var h=1;h<s.length;h++){var p=s[h++],a=s[h]?(s[0]|=p?1:2,r[s[h++]]):s[++h];3===p?e[0]=a:4===p?e[1]=Object.assign(e[1]||{},a):5===p?(e[1]=e[1]||{})[s[++h]]=a:6===p?e[1][s[++h]]+=a+"":p?(u=t.apply(a,n(t,a,r,["",null])),e.push(u),a[0]?s[0]|=2:(s[h-2]=0,s[h]=u)):e.push(a);}return e},t=new Map;function htm(s){var r=t.get(this);return r||(r=new Map,t.set(this,r)),(r=n(this,r.get(s)||(r.set(s,r=function(n){for(var t,s,r=1,e="",u="",h=[0],p=function(n){1===r&&(n||(e=e.replace(/^\s*\n\s*|\s*\n\s*$/g,"")))?h.push(0,n,e):3===r&&(n||e)?(h.push(3,n,e),r=2):2===r&&"..."===e&&n?h.push(4,n,0):2===r&&e&&!n?h.push(5,0,!0,e):r>=5&&((e||!n&&5===r)&&(h.push(r,0,e,s),r=6),n&&(h.push(r,n,0,s),r=6)),e="";},a=0;a<n.length;a++){a&&(1===r&&p(),p(a));for(var l=0;l<n[a].length;l++)t=n[a][l],1===r?"<"===t?(p(),h=[h],r=3):e+=t:4===r?"--"===e&&">"===t?(r=1,e=""):e=t+e[0]:u?t===u?u="":e+=t:'"'===t||"'"===t?u=t:">"===t?(p(),r=1):r&&("="===t?(r=5,s=e,e=""):"/"===t&&(r<5||">"===n[a][l+1])?(p(),3===r&&(h=h[0]),r=h,(h=h[0]).push(2,0,r),r=0):" "===t||"\t"===t||"\n"===t||"\r"===t?(p(),r=2):e+=t),3===r&&"!--"===e&&(r=4,h=h[0]);}return p(),h}(s)),r),arguments,[])).length>1?r:r[0]}
2298
2506
 
2299
- const html = htm.bind(React.createElement);
2507
+ const html = htm.bind(React__default['default'].createElement);
2300
2508
  function makeCustomTool(toolData) {
2301
2509
  return class CustomTool extends StudioTool {
2302
2510
  static dynamicProperties() {
@@ -2310,12 +2518,15 @@ function makeCustomTool(toolData) {
2310
2518
  } = this.props;
2311
2519
  const {
2312
2520
  code,
2313
- propertyInputs = []
2521
+ propertyInputs = [],
2522
+ context_options: contextOptionsInputs = []
2314
2523
  } = toolData;
2315
2524
  const propertyNames = propertyInputs.map(input => input.variableName);
2316
- const Tool = new Function(...propertyNames, 'html', 'CD', 'tag', code);
2317
- const values = propertyInputs.map(prop => getPropertyWithFallback(prop));
2318
- return Tool(...values, html, CD, tag);
2525
+ const contextOptionsNames = contextOptionsInputs.map(input => input.name);
2526
+ const Tool = new Function(...propertyNames, ...contextOptionsNames, 'html', 'CD', 'tag', code);
2527
+ const propertyValues = propertyInputs.map(prop => getPropertyWithFallback(prop));
2528
+ const contextValues = contextOptionsInputs.map(input => tag.toolProperties[input.name]);
2529
+ return Tool(...propertyValues, ...contextValues, html, CD__default['default'], tag);
2319
2530
  }
2320
2531
 
2321
2532
  };
@@ -2349,9 +2560,9 @@ function cleanHTML(element) {
2349
2560
  newBody.style.cssText = BASE_STYLE;
2350
2561
  newBody.appendChild(element);
2351
2562
 
2352
- if (analyticsLength > 250) {
2563
+ if (analyticsLength > 500) {
2353
2564
  analytics.setAttribute('data-mi-data', '{}');
2354
- console.log('extraData trying to be set exceeds 250 characters, setting extraData to be empty to avoid breaking crops');
2565
+ console.log('extraData trying to be set exceeds 500 characters, setting extraData to be empty to avoid breaking crops');
2355
2566
  }
2356
2567
 
2357
2568
  if (analytics) {
@@ -2388,6 +2599,85 @@ function addBrowserContent(baseElement) {
2388
2599
  rootNode.body.appendChild(script);
2389
2600
  }
2390
2601
 
2602
+ const COUNT_OF_SCRIPT_TAGS_COMPILED_BY_STUDIO = 5;
2603
+
2604
+ const stringMatch = (original, comparable, ignoreRegExp) => {
2605
+ return original.replace(ignoreRegExp, '') === comparable;
2606
+ };
2607
+
2608
+ const studioOverwritableJs = 'var app = new studio[a-zA-Z0-9]*.A\\(\\);';
2609
+ const jsIgnoreRE = new RegExp(`\\s|${studioOverwritableJs}`, 'g');
2610
+ const jsCompareString = `(function(){app.render(document.getElementById('react-root')).then(function(){window.APP_SUCCESSFULLY_RENDERED=true;});})();`;
2611
+ const isPristinePicJsTag = scriptTag => {
2612
+ const script = scriptTag.innerHTML;
2613
+ return stringMatch(script, jsCompareString, jsIgnoreRE);
2614
+ };
2615
+ const isDocumentEligibleForStaticHandling = rootNode => {
2616
+ const document = getDocumentNode(rootNode);
2617
+ const scriptTags = document.getElementsByTagName('script');
2618
+ const hasTagContainingPristinePicJs = Array.from(scriptTags).some(isPristinePicJsTag);
2619
+
2620
+ if (!hasTagContainingPristinePicJs) {
2621
+ console.log('static status is false - user-altered js detected');
2622
+ return false;
2623
+ }
2624
+
2625
+ if (scriptTags.length > COUNT_OF_SCRIPT_TAGS_COMPILED_BY_STUDIO) {
2626
+ console.log('static status is false - user-added script tags detected');
2627
+ return false;
2628
+ }
2629
+
2630
+ return true;
2631
+ };
2632
+
2633
+ const basicTypes = new Set(['group', 'image', 'rectangle', 'richText', 'staticPic', 'text']);
2634
+
2635
+ const isTagStatic = tag => {
2636
+ const {
2637
+ appliedElementModifiers,
2638
+ conditionalDynamicProperty,
2639
+ dynamicAltText,
2640
+ dynamicProperty,
2641
+ richText,
2642
+ subtags,
2643
+ type
2644
+ } = tag;
2645
+ if (subtags?.length) return subtags.some(isTagStatic);
2646
+ const isCustomTool = type.startsWith('customTool.');
2647
+ const hasDynamicRichText = richText?.some(({
2648
+ insert
2649
+ }) => typeof insert === 'object' && insert.dynamicProperty);
2650
+ if (appliedElementModifiers || !basicTypes.has(type) || conditionalDynamicProperty || dynamicAltText || dynamicProperty || hasDynamicRichText || isCustomTool) return false;
2651
+ return true;
2652
+ };
2653
+
2654
+ const areAttributesStatic = attributes => {
2655
+ const {
2656
+ canvases
2657
+ } = attributes;
2658
+
2659
+ for (const {
2660
+ appliedElementModifiers,
2661
+ tags = [],
2662
+ clickthroughDynamicProperty,
2663
+ conditionalDynamicProperty
2664
+ } of canvases) {
2665
+ if (appliedElementModifiers || clickthroughDynamicProperty || conditionalDynamicProperty) {
2666
+ console.log('static status is false - dynamic properties on canvas detected');
2667
+ return false;
2668
+ }
2669
+
2670
+ for (const tag of tags) {
2671
+ if (!isTagStatic(tag)) {
2672
+ console.log('static status is false - dynamic properties on tag detected');
2673
+ return false;
2674
+ }
2675
+ }
2676
+ }
2677
+
2678
+ return true;
2679
+ };
2680
+
2391
2681
  class StudioFramework {
2392
2682
  constructor(opts = setupAppDefaults()) {
2393
2683
  _defineProperty(this, "currentGrouping", false);
@@ -2411,23 +2701,41 @@ class StudioFramework {
2411
2701
  _defineProperty(this, "namespacedOptions", void 0);
2412
2702
 
2413
2703
  _defineProperty(this, "debug", {
2414
- CD,
2704
+ CD: CD__default['default'],
2705
+ enableDebugMode: () => {
2706
+ this.asyncComputed.enableEvents();
2707
+ },
2415
2708
  getAllProperties: () => this.asyncComputed.dumpState(),
2416
- getPropertyEvents: () => this.asyncComputed.propertyDebugEvents,
2709
+ getPropertyEvents: () => {
2710
+ console.warn('`app.debug.getPropertyEvents()` is deprecated. Please use `app.meta.enableEvents()` and `app.meta.getEvents([consoleLog])`.');
2711
+ return this.asyncComputed.propertyEvents;
2712
+ },
2417
2713
  logProperties: () => {
2418
2714
  const state = this.asyncComputed.dumpState();
2419
- console.warn('`app.debug.logProperties()` is deprecated. Please use `app.debug.logPropertyTree()`');
2715
+ console.warn('`app.debug.logProperties()` is deprecated. Please use `app.meta.enableEvents` and `app.meta.getEvents([consoleLog])`.');
2420
2716
  console.log(JSON.stringify(state, null, 2));
2421
2717
  },
2422
- enableDebugMode: () => {
2423
- this.asyncComputed.enableDebugMode();
2424
- },
2425
2718
  logPropertyTree: () => {
2426
- const events = this.asyncComputed.propertyDebugEvents;
2719
+ const events = this.asyncComputed.propertyEvents;
2427
2720
  console.log(JSON.stringify(events, null, 2));
2428
2721
  }
2429
2722
  });
2430
2723
 
2724
+ _defineProperty(this, "meta", {
2725
+ eventsEnabled: false,
2726
+ enableEvents: () => {
2727
+ // tbd: options for pruning which events are captured
2728
+ this.meta.eventsEnabled = true;
2729
+ this.asyncComputed.enableEvents();
2730
+ },
2731
+ getEvents: consoleLog => {
2732
+ if (!this.meta.eventsEnabled) console.warn('`app.metadata.getEvents()` was called, however `app.metadata.enableEvents([...options])` was not set first. No events will be returned.');
2733
+ const events = this.asyncComputed.propertyEvents;
2734
+ if (consoleLog) console.log(JSON.stringify(events, null, 2));
2735
+ return events;
2736
+ }
2737
+ });
2738
+
2431
2739
  const {
2432
2740
  canvases = [],
2433
2741
  customProps = [],
@@ -2440,6 +2748,12 @@ class StudioFramework {
2440
2748
  namespacedOptions = [],
2441
2749
  queryParams = {}
2442
2750
  } = opts;
2751
+
2752
+ if (window.MICapture?.isPreview) {
2753
+ console.log('static status is true - static check has started');
2754
+ window.RENDERED_STATICALLY = true;
2755
+ }
2756
+
2443
2757
  this.options = options;
2444
2758
  this.namespacedOptions = namespacedOptions;
2445
2759
  this.customProps = customProps;
@@ -2457,7 +2771,25 @@ class StudioFramework {
2457
2771
  return queryParams;
2458
2772
  });
2459
2773
  animation(this);
2460
- this.setup();
2774
+
2775
+ if (this.shouldPerformStaticCheck) {
2776
+ const proxy = new Proxy(this, {
2777
+ set(target, key, value) {
2778
+ if (key in target && key !== 'currentGrouping') {
2779
+ window.RENDERED_STATICALLY = false;
2780
+ console.log(`static status is false - overridden StudioFramework class method `, key);
2781
+ }
2782
+
2783
+ return Reflect.set(target, key, value);
2784
+ }
2785
+
2786
+ });
2787
+ this.setup.call(proxy);
2788
+ if (this.shouldPerformStaticCheck) window.RENDERED_STATICALLY = areAttributesStatic(opts);
2789
+ } else {
2790
+ this.setup();
2791
+ }
2792
+
2461
2793
  this.setupOptions();
2462
2794
  this.setupNamespacedOptions();
2463
2795
  this.setupCustomTools();
@@ -2465,16 +2797,20 @@ class StudioFramework {
2465
2797
  this.setupModifiedProps();
2466
2798
  }
2467
2799
 
2800
+ get shouldPerformStaticCheck() {
2801
+ return window.MICapture?.isPreview && window.RENDERED_STATICALLY;
2802
+ }
2803
+
2468
2804
  triggerRerender() {
2469
2805
  if (this.appComponent) {
2470
2806
  this.appComponent.forceUpdate();
2471
2807
  }
2472
2808
  }
2473
2809
 
2474
- async isCurrentCanvas(canvas) {
2810
+ async resolveConditionalDynamicProperty(model) {
2475
2811
  const {
2476
2812
  conditionalDynamicProperty
2477
- } = canvas;
2813
+ } = model;
2478
2814
  if (!conditionalDynamicProperty) return [true, null];
2479
2815
  const {
2480
2816
  propertyGroupKey = '',
@@ -2491,7 +2827,7 @@ class StudioFramework {
2491
2827
  } = this;
2492
2828
 
2493
2829
  for (const canvas of canvases) {
2494
- const [result, error] = await this.isCurrentCanvas(canvas);
2830
+ const [result, error] = await this.resolveConditionalDynamicProperty(canvas);
2495
2831
  if (result) return canvas;
2496
2832
 
2497
2833
  if (error) {
@@ -2561,28 +2897,41 @@ class StudioFramework {
2561
2897
  setup() {} // eslint-disable-next-line
2562
2898
 
2563
2899
 
2564
- setupTags(tags) {
2565
- return tags;
2566
- } // eslint-disable-next-line
2900
+ async getCurrentTags(tags, canvas) {
2901
+ const filteredTags = [];
2567
2902
 
2903
+ for (const tag of tags) {
2904
+ const [result, error] = await this.resolveConditionalDynamicProperty(tag);
2568
2905
 
2569
- async clickthrough(currentCanvas) {
2570
- let {
2571
- clickthrough_dynamic_property
2572
- } = this.options;
2906
+ if (result) {
2907
+ filteredTags.push(tag);
2908
+ }
2573
2909
 
2574
- if (currentCanvas === null || currentCanvas === void 0 ? void 0 : currentCanvas.clickthroughDynamicProperty) {
2575
- clickthrough_dynamic_property = currentCanvas.clickthroughDynamicProperty;
2910
+ if (error) {
2911
+ const wrappedError = StudioErrorWrapper(error, `Skipped ${tag.label || tag.displayName} because conditional dynamic property threw an error`).setSourceType('tag-condition').setSourceDynamicProperty(tag.conditionalDynamicProperty).setEntryDynamicProperty(tag.conditionalDynamicProperty).setTag(tag).setCanvas(canvas);
2912
+ console.warn(wrappedError);
2913
+ }
2576
2914
  }
2577
2915
 
2578
- if (clickthrough_dynamic_property) {
2916
+ return this.setupTags(filteredTags);
2917
+ }
2918
+
2919
+ setupTags(tags) {
2920
+ return tags;
2921
+ }
2922
+
2923
+ async clickthrough(currentCanvas) {
2924
+ const canvasCDP = currentCanvas?.clickthroughDynamicProperty;
2925
+
2926
+ if (canvasCDP) {
2579
2927
  const {
2580
2928
  context,
2581
2929
  propertyPath,
2582
- propertyValue
2583
- } = clickthrough_dynamic_property;
2930
+ propertyValue,
2931
+ propertyGroupKey
2932
+ } = canvasCDP;
2584
2933
  if (propertyValue) return propertyValue;
2585
- const [result, error] = await this.getPropertyTuple(propertyPath, context);
2934
+ const [result, error] = await this.getPropertyTuple(propertyGroupKey, propertyPath, context);
2586
2935
 
2587
2936
  if (error) {
2588
2937
  throw StudioErrorWrapper(error).setSourceType('dynamic-clickthrough').setCanvas(currentCanvas);
@@ -2617,12 +2966,12 @@ class StudioFramework {
2617
2966
  throw new Error('The result of `analyticsData` must be an object');
2618
2967
  }
2619
2968
 
2620
- CD.setExtraData(analyticsData);
2969
+ CD__default['default'].setExtraData(analyticsData);
2621
2970
  }
2622
2971
 
2623
2972
  renderApp(targetElement, clickthroughURL, tags, dimensions, canvas) {
2624
2973
  return new Promise((resolve, reject) => {
2625
- ReactDOM.render( /*#__PURE__*/React.createElement(App, {
2974
+ ReactDOM__default['default'].render( /*#__PURE__*/React__default['default'].createElement(App, {
2626
2975
  asyncComputed: this.asyncComputed,
2627
2976
  clickthroughURL: clickthroughURL,
2628
2977
  resolver: this.resolver,
@@ -2664,18 +3013,25 @@ class StudioFramework {
2664
3013
  const {
2665
3014
  propertyGroupKey,
2666
3015
  propertyPath,
3016
+ propertyValue,
2667
3017
  context
2668
3018
  } = value;
2669
- this.setProperty(`options.${key}`, ({
2670
- getProperty
2671
- }) => {
2672
- return getProperty(propertyGroupKey, propertyPath, context);
2673
- });
2674
- this.setProperty(key, ({
2675
- getProperty
2676
- }) => {
2677
- return getProperty(propertyGroupKey, propertyPath, context);
2678
- });
3019
+
3020
+ if (propertyGroupKey === 'static') {
3021
+ this.setProperty(`options.${key}`, propertyValue);
3022
+ this.setProperty(key, propertyValue);
3023
+ } else {
3024
+ this.setProperty(`options.${key}`, ({
3025
+ getProperty
3026
+ }) => {
3027
+ return getProperty(propertyGroupKey, propertyPath, context);
3028
+ });
3029
+ this.setProperty(key, ({
3030
+ getProperty
3031
+ }) => {
3032
+ return getProperty(propertyGroupKey, propertyPath, context);
3033
+ });
3034
+ }
2679
3035
  } else {
2680
3036
  this.setProperty(`options.${key}`, value);
2681
3037
  this.setProperty(key, value);
@@ -2701,13 +3057,19 @@ class StudioFramework {
2701
3057
  const {
2702
3058
  propertyGroupKey,
2703
3059
  propertyPath,
3060
+ propertyValue,
2704
3061
  context
2705
3062
  } = value;
2706
- this.setProperty(`options.${fieldName}`, ({
2707
- getProperty
2708
- }) => {
2709
- return getProperty(propertyGroupKey, propertyPath, context);
2710
- });
3063
+
3064
+ if (propertyGroupKey === 'static') {
3065
+ this.setProperty(`options.${fieldName}`, propertyValue);
3066
+ } else {
3067
+ this.setProperty(`options.${fieldName}`, ({
3068
+ getProperty
3069
+ }) => {
3070
+ return getProperty(propertyGroupKey, propertyPath, context);
3071
+ });
3072
+ }
2711
3073
  } else {
2712
3074
  this.setProperty(`options.${fieldName}`, value);
2713
3075
  }
@@ -2797,23 +3159,51 @@ class StudioFramework {
2797
3159
 
2798
3160
  handleRenderError(e) {
2799
3161
  console.error(e);
2800
- CD.throwError(e);
3162
+ CD__default['default'].throwError(e);
2801
3163
  throw e;
2802
3164
  }
2803
3165
 
3166
+ handleAndPreventFutureAnalyticsCalls(data) {
3167
+ if (typeof data !== 'object') {
3168
+ throw new Error('The result of `analyticsData` must be an object');
3169
+ }
3170
+
3171
+ const format = window.MICapture?.format;
3172
+ const analytics = document.querySelector('#mi-data');
3173
+
3174
+ if (format === 'html') {
3175
+ const analyticsLength = analytics ? (analytics.getAttribute('data-mi-data') || '').length : 0;
3176
+
3177
+ if (analyticsLength > 500) {
3178
+ analytics.setAttribute('data-mi-data', '{}');
3179
+ console.log('extraData trying to be set exceeds 500 characters, setting extraData to be empty to avoid breaking crops');
3180
+ }
3181
+ } // Force a full overwrite
3182
+
3183
+
3184
+ if (analytics) document.querySelector('#mi-data').remove();
3185
+ CD__default['default'].setExtraData(data);
3186
+
3187
+ CD__default['default'].setExtraData = () => {
3188
+ console.log('setExtraData call was prevented via handleAndPreventFutureAnalyticsCalls');
3189
+ };
3190
+
3191
+ CD__default['default'].resume();
3192
+ }
3193
+
2804
3194
  async render(targetElement) {
3195
+ if (window.MICapture?.isPreview) console.log('Preview Render');
2805
3196
  let currentCanvas;
2806
3197
 
2807
3198
  try {
2808
- var _currentCanvas;
2809
-
2810
- CD.pause();
3199
+ CD__default['default'].pause();
2811
3200
  currentCanvas = await this.getCurrentCanvas();
2812
3201
  const clickthroughURL = await this.handleClickthrough(currentCanvas);
2813
- const canvasTags = ((_currentCanvas = currentCanvas) === null || _currentCanvas === void 0 ? void 0 : _currentCanvas.tags) || [];
2814
- const tags = this.setupTags(canvasTags);
3202
+ const canvasTags = currentCanvas?.tags || [];
3203
+ const tags = await this.getCurrentTags(canvasTags, currentCanvas);
2815
3204
  const height = currentCanvas.height || this.options.height;
2816
3205
  const width = currentCanvas.width || this.options.width;
3206
+ const format = window.MICapture?.format;
2817
3207
  const renderPromise = this.renderApp(targetElement, clickthroughURL, tags, {
2818
3208
  height,
2819
3209
  width
@@ -2825,12 +3215,23 @@ class StudioFramework {
2825
3215
  this.setupAnimation();
2826
3216
  }
2827
3217
 
2828
- cleanHTML(targetElement);
2829
- addBrowserContent(targetElement);
2830
- CD.resume();
3218
+ if (this.shouldPerformStaticCheck) {
3219
+ if (window.RENDERED_STATICALLY = isDocumentEligibleForStaticHandling(targetElement)) {
3220
+ console.log('static status is true - static check has finished');
3221
+ } else {
3222
+ console.log('static status is false - static check has finished');
3223
+ }
3224
+ }
3225
+
3226
+ if (format === 'html') {
3227
+ cleanHTML(targetElement);
3228
+ addBrowserContent(targetElement);
3229
+ }
2831
3230
  } catch (e) {
2832
3231
  const wrappedError = StudioErrorWrapper(e).setCanvas(currentCanvas);
2833
3232
  this.handleRenderError(wrappedError);
3233
+ } finally {
3234
+ CD__default['default'].resume();
2834
3235
  }
2835
3236
  }
2836
3237
 
@@ -3122,6 +3523,7 @@ if (window.MICapture && window.MICapture.captureAppLog) {
3122
3523
  }
3123
3524
 
3124
3525
  exports.App = App;
3526
+ exports.StudioError = StudioError;
3125
3527
  exports.StudioTool = StudioTool;
3126
3528
  exports.computedPath = computedPath;
3127
3529
  exports.default = StudioFramework;