@temple-wallet/extension-ads 6.1.0 → 6.3.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.
Files changed (93) hide show
  1. package/dist/ads-actions/helpers.d.ts +13 -0
  2. package/dist/ads-actions/helpers.js +83 -0
  3. package/dist/ads-actions/helpers.js.map +1 -0
  4. package/dist/ads-actions/index.d.ts +3 -0
  5. package/dist/ads-actions/index.js +45 -0
  6. package/dist/ads-actions/index.js.map +1 -0
  7. package/dist/ads-actions/process-permanent-rule.d.ts +3 -0
  8. package/dist/ads-actions/process-permanent-rule.js +132 -0
  9. package/dist/ads-actions/process-permanent-rule.js.map +1 -0
  10. package/dist/ads-actions/process-providers-ads.d.ts +3 -0
  11. package/dist/ads-actions/process-providers-ads.js +113 -0
  12. package/dist/ads-actions/process-providers-ads.js.map +1 -0
  13. package/dist/ads-actions/process-rule.d.ts +3 -0
  14. package/dist/ads-actions/process-rule.js +97 -0
  15. package/dist/ads-actions/process-rule.js.map +1 -0
  16. package/dist/ads-configuration.d.ts +33 -0
  17. package/dist/ads-configuration.js +29 -0
  18. package/dist/ads-configuration.js.map +1 -0
  19. package/dist/ads-meta.d.ts +17 -0
  20. package/dist/ads-meta.js +135 -0
  21. package/dist/ads-meta.js.map +1 -0
  22. package/dist/constants.d.ts +8 -0
  23. package/dist/constants.js +9 -0
  24. package/dist/constants.js.map +1 -0
  25. package/dist/execute-ads-actions/ads-views/index.d.ts +3 -0
  26. package/dist/execute-ads-actions/ads-views/index.js +4 -0
  27. package/dist/execute-ads-actions/ads-views/index.js.map +1 -0
  28. package/dist/execute-ads-actions/ads-views/make-hypelab-ad.d.ts +3 -0
  29. package/dist/execute-ads-actions/ads-views/make-hypelab-ad.js +60 -0
  30. package/dist/execute-ads-actions/ads-views/make-hypelab-ad.js.map +1 -0
  31. package/dist/execute-ads-actions/ads-views/make-persona-ad.d.ts +3 -0
  32. package/dist/execute-ads-actions/ads-views/make-persona-ad.js +11 -0
  33. package/dist/execute-ads-actions/ads-views/make-persona-ad.js.map +1 -0
  34. package/dist/execute-ads-actions/ads-views/make-tkey-ad.d.ts +2 -0
  35. package/dist/execute-ads-actions/ads-views/make-tkey-ad.js +24 -0
  36. package/dist/execute-ads-actions/ads-views/make-tkey-ad.js.map +1 -0
  37. package/dist/execute-ads-actions/index.d.ts +2 -0
  38. package/dist/execute-ads-actions/index.js +23 -0
  39. package/dist/execute-ads-actions/index.js.map +1 -0
  40. package/dist/execute-ads-actions/observing.d.ts +1 -0
  41. package/dist/execute-ads-actions/observing.js +123 -0
  42. package/dist/execute-ads-actions/observing.js.map +1 -0
  43. package/dist/execute-ads-actions/override-element-styles.d.ts +2 -0
  44. package/dist/execute-ads-actions/override-element-styles.js +6 -0
  45. package/dist/execute-ads-actions/override-element-styles.js.map +1 -0
  46. package/dist/execute-ads-actions/process-insert-ad-action.d.ts +2 -0
  47. package/dist/execute-ads-actions/process-insert-ad-action.js +120 -0
  48. package/dist/execute-ads-actions/process-insert-ad-action.js.map +1 -0
  49. package/dist/index.d.ts +8 -3
  50. package/dist/index.js +1363 -13
  51. package/dist/index.js.map +1 -0
  52. package/dist/render-ads-stack.d.ts +7 -0
  53. package/dist/render-ads-stack.js +101 -0
  54. package/dist/render-ads-stack.js.map +1 -0
  55. package/dist/temple-wallet-api.d.ts +17 -0
  56. package/dist/temple-wallet-api.js +56 -0
  57. package/dist/temple-wallet-api.js.map +1 -0
  58. package/dist/transform-raw-rules.d.ts +3 -0
  59. package/dist/transform-raw-rules.js +117 -0
  60. package/dist/transform-raw-rules.js.map +1 -0
  61. package/dist/types/ad-view.d.ts +3 -0
  62. package/dist/types/ad-view.js +2 -0
  63. package/dist/types/ad-view.js.map +1 -0
  64. package/dist/types/ads-actions.d.ts +54 -0
  65. package/dist/types/ads-actions.js +15 -0
  66. package/dist/types/ads-actions.js.map +1 -0
  67. package/dist/types/ads-meta.d.ts +46 -0
  68. package/dist/types/ads-meta.js +2 -0
  69. package/dist/types/ads-meta.js.map +1 -0
  70. package/dist/types/ads-provider.d.ts +7 -0
  71. package/dist/types/ads-provider.js +8 -0
  72. package/dist/types/ads-provider.js.map +1 -0
  73. package/dist/types/ads-rules.d.ts +17 -0
  74. package/dist/types/ads-rules.js +2 -0
  75. package/dist/types/ads-rules.js.map +1 -0
  76. package/dist/types/temple-wallet-api.d.ts +61 -0
  77. package/dist/types/temple-wallet-api.js +2 -0
  78. package/dist/types/temple-wallet-api.js.map +1 -0
  79. package/dist/utils.d.ts +6 -0
  80. package/dist/utils.js +4 -0
  81. package/dist/utils.js.map +1 -0
  82. package/package.json +1 -1
  83. package/src/ads-actions/helpers.ts +11 -3
  84. package/src/ads-actions/process-permanent-rule.ts +26 -10
  85. package/src/ads-actions/process-providers-ads.ts +12 -7
  86. package/src/ads-actions/process-rule.ts +6 -5
  87. package/src/constants.ts +2 -0
  88. package/src/execute-ads-actions/ads-views/make-hypelab-ad.ts +6 -2
  89. package/src/execute-ads-actions/observing.ts +15 -11
  90. package/src/execute-ads-actions/process-insert-ad-action.ts +91 -14
  91. package/src/render-ads-stack.ts +9 -6
  92. package/src/types/ads-actions.ts +3 -3
  93. package/src/types/temple-wallet-api.ts +7 -1
@@ -1,9 +1,17 @@
1
1
  import { BANNER_ADS_META, buildHypeLabNativeMeta } from 'src/ads-meta';
2
- import { SIBLING_REPLACEMENT_ATTRIBUTE_NAME, TEMPLE_WALLET_AD_ATTRIBUTE_NAME } from 'src/constants';
2
+ import {
3
+ SIBLING_REPLACEMENT_ATTRIBUTE_NAME,
4
+ TEMPLE_WALLET_AD_ATTRIBUTE_NAME,
5
+ TEMPLE_WALLET_NATIVE_AD_ATTRIBUTE_NAME
6
+ } from 'src/constants';
3
7
  import { AdType } from 'src/types/ads-actions';
4
8
  import { AdMetadata } from 'src/types/ads-meta';
5
9
 
6
- export const ourAdQuerySelector = `iframe[${TEMPLE_WALLET_AD_ATTRIBUTE_NAME}], div[${TEMPLE_WALLET_AD_ATTRIBUTE_NAME}]`;
10
+ export const PX_VALUE_REGEX = /[\d.]+(px)?$/;
11
+
12
+ export const ourAdQuerySelector = `[${TEMPLE_WALLET_AD_ATTRIBUTE_NAME}]`;
13
+ export const ourBannerAdQuerySelector = `[${TEMPLE_WALLET_AD_ATTRIBUTE_NAME}]:not([${TEMPLE_WALLET_NATIVE_AD_ATTRIBUTE_NAME}])`;
14
+ export const ourNativeAdQuerySelector = `[${TEMPLE_WALLET_NATIVE_AD_ATTRIBUTE_NAME}]`;
7
15
 
8
16
  export const elementIsOurAd = (element: Element) => Boolean(element.closest(ourAdQuerySelector));
9
17
 
@@ -21,7 +29,7 @@ export const getFinalSize = (element: Element) => {
21
29
  const rawDimensionFromAttribute = element.getAttribute(dimension);
22
30
  const rawDimension = rawDimensionFromAttribute || rawDimensionFromStyle;
23
31
 
24
- if (/\d+px/.test(rawDimension)) {
32
+ if (PX_VALUE_REGEX.test(rawDimension)) {
25
33
  size[dimension] = Number(rawDimension.replace('px', ''));
26
34
  }
27
35
  if (dimension === 'width' && x < 0) {
@@ -17,7 +17,8 @@ import {
17
17
  applyQuerySelector,
18
18
  elementIsOurAd,
19
19
  getParentOfDepth,
20
- ourAdQuerySelector,
20
+ ourBannerAdQuerySelector,
21
+ ourNativeAdQuerySelector,
21
22
  prevBannerSiblingIsReplacement
22
23
  } from './helpers';
23
24
 
@@ -56,6 +57,7 @@ const processPermanentAdsParent = (
56
57
  const {
57
58
  shouldUseDivWrapper,
58
59
  divWrapperStyle: originalDivWrapperStyle,
60
+ wrapperStyle: originalWrapperStyle,
59
61
  elementStyle,
60
62
  adSelector,
61
63
  insertionIndex,
@@ -65,9 +67,16 @@ const processPermanentAdsParent = (
65
67
  elementToMeasureSelector,
66
68
  stylesOverrides,
67
69
  shouldHideOriginal = false,
68
- isNative
70
+ isNative,
71
+ urlRegexes,
72
+ parentSelector,
73
+ ...restProps
69
74
  } = rule;
70
- const divWrapperStyle = shouldUseDivWrapper ? originalDivWrapperStyle : { display: 'contents' };
75
+ const wrapperStyle = restProps.wrapperType
76
+ ? originalWrapperStyle
77
+ : shouldUseDivWrapper
78
+ ? originalDivWrapperStyle
79
+ : { display: 'contents' };
71
80
 
72
81
  const {
73
82
  isMultiple: shouldSearchForManyBannersInParent,
@@ -75,7 +84,11 @@ const processPermanentAdsParent = (
75
84
  parentDepth: bannerParentDepth
76
85
  } = adSelector;
77
86
 
78
- const ourAds = applyQuerySelector(ourAdQuerySelector, true, parent).reduce<Element[]>((acc, element) => {
87
+ const ourAds = applyQuerySelector(
88
+ isNative ? ourNativeAdQuerySelector : ourBannerAdQuerySelector,
89
+ true,
90
+ parent
91
+ ).reduce<Element[]>((acc, element) => {
79
92
  if (acc.some(prevElement => prevElement.contains(element) || element.contains(prevElement))) {
80
93
  return acc;
81
94
  }
@@ -107,9 +120,10 @@ const processPermanentAdsParent = (
107
120
  const replaceActionBase: OmitAdInAction<ReplaceElementWithAdAction> = {
108
121
  type: AdActionType.ReplaceElement,
109
122
  element: banner,
110
- divWrapperStyle,
123
+ wrapperStyle,
111
124
  elementStyle,
112
- stylesOverrides
125
+ stylesOverrides,
126
+ ...restProps
113
127
  };
114
128
  const hideActionBase: HideElementAction = {
115
129
  type: AdActionType.HideElement,
@@ -117,12 +131,13 @@ const processPermanentAdsParent = (
117
131
  };
118
132
  const insertActionBase: OmitAdInAction<SimpleInsertAdAction> = {
119
133
  type: AdActionType.SimpleInsertAd,
120
- divWrapperStyle,
134
+ wrapperStyle,
121
135
  elementStyle,
122
136
  parent: banner.parentElement!,
123
137
  insertionIndex: Array.from(banner.parentElement!.children).indexOf(banner),
124
138
  isSiblingReplacement: true,
125
- stylesOverrides
139
+ stylesOverrides,
140
+ ...restProps
126
141
  };
127
142
 
128
143
  let actionsToInsert: (InsertAdActionWithoutMeta | HideElementAction | RemoveElementAction)[] = [];
@@ -179,12 +194,13 @@ const processPermanentAdsParent = (
179
194
  if (normalizedInsertionIndex !== -1) {
180
195
  const actionBase: OmitAdInAction<SimpleInsertAdAction> = {
181
196
  type: AdActionType.SimpleInsertAd,
182
- divWrapperStyle,
197
+ wrapperStyle,
183
198
  elementStyle,
184
199
  parent: insertionParentElement,
185
200
  insertionIndex: normalizedInsertionIndex,
186
201
  isSiblingReplacement: false,
187
- stylesOverrides
202
+ stylesOverrides,
203
+ ...restProps
188
204
  };
189
205
 
190
206
  addActionsIfAdResolutionAvailable(
@@ -16,7 +16,8 @@ import {
16
16
  prevBannerSiblingIsReplacement,
17
17
  getFinalSize,
18
18
  ourAdQuerySelector,
19
- getParentOfDepth
19
+ getParentOfDepth,
20
+ PX_VALUE_REGEX
20
21
  } from './helpers';
21
22
 
22
23
  const styleToAdjustProps = [
@@ -69,8 +70,9 @@ export const processProvidersAds = async (
69
70
  return action.element.parentElement?.contains(newBanner) || newBanner.contains(action.element);
70
71
  });
71
72
  const bannerIsHiddenByParent = parentStyles?.display === 'none' || parentStyles?.visibility === 'hidden';
73
+ const hasAnchorParent = parentElementIndex >= 0 && acc[parentElementIndex] instanceof HTMLAnchorElement;
72
74
 
73
- if (bannerIsTouchedByOtherActions || width === 0 || height === 0 || bannerIsHiddenByParent) {
75
+ if (bannerIsTouchedByOtherActions || width === 0 || height === 0 || bannerIsHiddenByParent || hasAnchorParent) {
74
76
  return acc;
75
77
  }
76
78
 
@@ -90,10 +92,13 @@ export const processProvidersAds = async (
90
92
  await delay(0);
91
93
 
92
94
  const parent = banner.parentElement;
95
+ const { width: bannerStyleWidth, height: bannerStyleHeight } = window.getComputedStyle(banner);
93
96
  const elementToMeasure =
94
- parent?.closest<HTMLElement>('div, article, aside, footer, header') ??
95
- (parent?.tagName.toLowerCase() === 'body' ? undefined : parent) ??
96
- banner;
97
+ PX_VALUE_REGEX.test(bannerStyleWidth) && PX_VALUE_REGEX.test(bannerStyleHeight)
98
+ ? banner
99
+ : parent?.closest<HTMLElement>('div, article, aside, footer, header') ??
100
+ (parent?.tagName.toLowerCase() === 'body' ? undefined : parent) ??
101
+ banner;
97
102
 
98
103
  if (
99
104
  !elementIsOurAd(banner) &&
@@ -120,7 +125,7 @@ export const processProvidersAds = async (
120
125
 
121
126
  const bannerStyle = window.getComputedStyle(banner as HTMLElement);
122
127
 
123
- const divWrapperStyle = {
128
+ const wrapperStyle = {
124
129
  ...Object.fromEntries(styleToAdjustProps.map(propName => [propName, bannerStyle.getPropertyValue(propName)])),
125
130
  ...bannerStyleFromAttribute
126
131
  };
@@ -130,7 +135,7 @@ export const processProvidersAds = async (
130
135
  };
131
136
  const insertAdAction: OmitAdInAction<SimpleInsertAdAction> = {
132
137
  type: AdActionType.SimpleInsertAd,
133
- divWrapperStyle,
138
+ wrapperStyle,
134
139
  parent: banner.parentElement ?? document.body,
135
140
  insertionIndex: Array.from(banner.parentElement?.children ?? []).indexOf(banner),
136
141
  isSiblingReplacement: true
@@ -48,7 +48,8 @@ const processSelectedElement = (
48
48
  const {
49
49
  selector: { shouldUseDivWrapper, parentDepth, divWrapperStyle },
50
50
  stylesOverrides,
51
- shouldHideOriginal
51
+ shouldHideOriginal,
52
+ isNative = false
52
53
  } = rule;
53
54
 
54
55
  const banner = getParentOfDepth(selectedElement, parentDepth);
@@ -64,7 +65,7 @@ const processSelectedElement = (
64
65
 
65
66
  const actionBaseCommonProps = {
66
67
  shouldUseDivWrapper,
67
- divWrapperStyle
68
+ wrapperStyle: divWrapperStyle
68
69
  };
69
70
 
70
71
  let actionsBases: (InsertAdActionWithoutMeta | HideElementAction | RemoveElementAction)[];
@@ -108,7 +109,7 @@ const processSelectedElement = (
108
109
  insertionIndex: 0,
109
110
  stylesOverrides,
110
111
  isSiblingReplacement: true,
111
- divWrapperStyle: { display: 'contents' }
112
+ wrapperStyle: { display: 'contents' }
112
113
  };
113
114
 
114
115
  actionsBases = Array.from(banner.children)
@@ -122,12 +123,12 @@ const processSelectedElement = (
122
123
  type: AdActionType.ReplaceAllChildren,
123
124
  parent: banner,
124
125
  stylesOverrides,
125
- divWrapperStyle: { display: 'contents' }
126
+ wrapperStyle: { display: 'contents' }
126
127
  };
127
128
  actionsBases = [replaceAllChildrenActionBase];
128
129
  }
129
130
 
130
131
  if (actionsBases.length > 0) {
131
- addActionsIfAdResolutionAvailable(banner, AdType.SlotReplacement, false, ...actionsBases);
132
+ addActionsIfAdResolutionAvailable(banner, AdType.SlotReplacement, isNative, ...actionsBases);
132
133
  }
133
134
  };
package/src/constants.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export const TEMPLE_WALLET_AD_ATTRIBUTE_NAME = 'twa';
2
+ export const TEMPLE_WALLET_NATIVE_AD_ATTRIBUTE_NAME = 'twa-native';
2
3
  export const SIBLING_REPLACEMENT_ATTRIBUTE_NAME = 'twa-sibling-replacement';
3
4
  export const AD_SEEN_THRESHOLD = 0.5;
4
5
  export const AD_RENDER_START_MESSAGE_TYPE = 'adRenderStart';
5
6
  export const AD_RESIZE_MESSAGE_TYPE = 'resize';
6
7
  export const AD_READY_MESSAGE_TYPE = 'ready';
7
8
  export const AD_ERROR_MESSAGE_TYPE = 'error';
9
+ export const AD_VARIANT_READY_TIMEOUT = 10_000;
@@ -9,7 +9,7 @@ export const makeHypelabAdView = (source: HypeLabAdSources, dimensions: AdDimens
9
9
 
10
10
  const iframe = document.createElement('iframe');
11
11
  iframe.src = getHypelabIframeUrl(source, origin, width, height, iframe.id);
12
- iframe.style.width = `${width}px`;
12
+ iframe.style.width = source.native ? '100%' : `${width}px`;
13
13
  iframe.style.height = `${height}px`;
14
14
  iframe.style.border = 'none';
15
15
 
@@ -56,7 +56,11 @@ const getHypelabIframeUrl = (
56
56
  }
57
57
 
58
58
  const url = new URL(AdsConfiguration.HYPELAB_ADS_WINDOW_URL);
59
- url.searchParams.set('w', String(width ?? defaultWidth));
59
+ if (source.native) {
60
+ url.searchParams.set('vh', String(height ?? defaultHeight));
61
+ } else {
62
+ url.searchParams.set('w', String(width ?? defaultWidth));
63
+ }
60
64
  url.searchParams.set('h', String(height ?? defaultHeight));
61
65
  url.searchParams.set('p', placementSlug);
62
66
  url.searchParams.set('o', encryptWithAES(origin));
@@ -15,8 +15,6 @@ const loadingAdsIds = new Set();
15
15
  const loadedAdsIds = new Set();
16
16
  const alreadySentAnalyticsAdsIds = new Set();
17
17
 
18
- const IFRAME_READY_TIMEOUT = 30_000;
19
-
20
18
  export const subscribeToAdsStackIframeLoadIfNecessary = async (element: HTMLIFrameElement) => {
21
19
  const adId = element.id;
22
20
 
@@ -27,11 +25,6 @@ export const subscribeToAdsStackIframeLoadIfNecessary = async (element: HTMLIFra
27
25
  loadingAdsIds.add(adId);
28
26
 
29
27
  return new Promise<AdMetadata['source']['providerName']>((resolve, reject) => {
30
- setTimeout(() => {
31
- window.removeEventListener('message', messageListener);
32
- reject(new Error(`Timeout exceeded for ${adId}`));
33
- }, IFRAME_READY_TIMEOUT);
34
-
35
28
  const messageListener = (event: MessageEvent<any>) => {
36
29
  if (event.source !== element.contentWindow) return;
37
30
 
@@ -40,6 +33,9 @@ export const subscribeToAdsStackIframeLoadIfNecessary = async (element: HTMLIFra
40
33
 
41
34
  if (data.id !== adId) return;
42
35
 
36
+ const horizontalBordersWidth = element.offsetWidth - element.clientWidth;
37
+ const verticalBordersWidth = element.offsetHeight - element.clientHeight;
38
+
43
39
  switch (data.type) {
44
40
  case AD_READY_MESSAGE_TYPE:
45
41
  resolve(data.adMetadata.source.providerName);
@@ -53,14 +49,18 @@ export const subscribeToAdsStackIframeLoadIfNecessary = async (element: HTMLIFra
53
49
  const { dimensions }: AdMetadata = data.adMetadata;
54
50
  const { width, height } = dimensions;
55
51
  if (width > 0 && height > 0) {
56
- element.style.width = `${dimensions.width}px`;
52
+ if (!element.style.width?.endsWith('%')) {
53
+ element.style.width = `${dimensions.width}px`;
54
+ }
57
55
  element.style.height = `${dimensions.height}px`;
58
56
  }
59
57
  break;
60
58
  case AD_RESIZE_MESSAGE_TYPE:
61
59
  const { width: newWidth, height: newHeight } = data;
62
- element.style.width = `${newWidth}px`;
63
- element.style.height = `${newHeight}px`;
60
+ if (!element.style.width?.endsWith('%')) {
61
+ element.style.width = `${newWidth + horizontalBordersWidth}px`;
62
+ }
63
+ element.style.height = `${newHeight + verticalBordersWidth}px`;
64
64
  }
65
65
  } catch (error) {
66
66
  console.error('Observing error:', error);
@@ -81,7 +81,11 @@ export const subscribeToAdsStackIframeLoadIfNecessary = async (element: HTMLIFra
81
81
  observeIntersection(element, providerName);
82
82
  }
83
83
  })
84
- .catch(console.error)
84
+ .catch(e => {
85
+ console.error(e);
86
+
87
+ throw e;
88
+ })
85
89
  .finally(() => void loadingAdsIds.delete(adId));
86
90
  };
87
91
 
@@ -1,8 +1,13 @@
1
1
  import { nanoid } from 'nanoid';
2
2
 
3
+ import { PX_VALUE_REGEX } from 'src/ads-actions/helpers';
3
4
  import { AdsConfiguration } from 'src/ads-configuration';
4
5
  import { BANNER_ADS_META } from 'src/ads-meta';
5
- import { SIBLING_REPLACEMENT_ATTRIBUTE_NAME, TEMPLE_WALLET_AD_ATTRIBUTE_NAME } from 'src/constants';
6
+ import {
7
+ SIBLING_REPLACEMENT_ATTRIBUTE_NAME,
8
+ TEMPLE_WALLET_AD_ATTRIBUTE_NAME,
9
+ TEMPLE_WALLET_NATIVE_AD_ATTRIBUTE_NAME
10
+ } from 'src/constants';
6
11
  import { AdActionType, InsertAdAction } from 'src/types/ads-actions';
7
12
  import { AdDimensions } from 'src/types/ads-meta';
8
13
 
@@ -13,7 +18,7 @@ const adjustToAd = (wrapperElement: HTMLDivElement, adDimensions: AdDimensions)
13
18
  const predefinedStylesKeyToOverride = ['width', 'height'] as const;
14
19
  predefinedStylesKeyToOverride.forEach(dimensionName => {
15
20
  const predefinedStyleValue = wrapperElement.style[dimensionName] ?? '';
16
- if (/^\d+(px)?$/.test(predefinedStyleValue)) {
21
+ if (PX_VALUE_REGEX.test(predefinedStyleValue)) {
17
22
  const predefinedSize = parseInt(predefinedStyleValue, 10);
18
23
  if (predefinedSize < adDimensions[dimensionName]) {
19
24
  wrapperElement.style.removeProperty(dimensionName);
@@ -22,8 +27,20 @@ const adjustToAd = (wrapperElement: HTMLDivElement, adDimensions: AdDimensions)
22
27
  });
23
28
  };
24
29
 
25
- const processInsertAdActionOnce = async (action: InsertAdAction, wrapperElement: HTMLDivElement) => {
26
- const { elementStyle = {}, stylesOverrides = [] } = action;
30
+ const sumPxValues = (values: string[]) =>
31
+ values.reduce((acc, value) => {
32
+ const parsedValue = parseInt(value, 10);
33
+
34
+ return isNaN(parsedValue) && PX_VALUE_REGEX.test(value) ? acc : acc + parsedValue;
35
+ }, 0);
36
+
37
+ const processInsertAdActionOnce = async (
38
+ action: InsertAdAction,
39
+ wrapperElement: HTMLDivElement,
40
+ insertionPoint: HTMLDivElement | HTMLTableCellElement,
41
+ onAdsStackError: (error: Error) => void
42
+ ) => {
43
+ const { elementStyle = {}, stylesOverrides = [], adsMetadata, wrapperType } = action;
27
44
 
28
45
  stylesOverrides.sort((a, b) => a.parentDepth - b.parentDepth);
29
46
 
@@ -36,7 +53,7 @@ const processInsertAdActionOnce = async (action: InsertAdAction, wrapperElement:
36
53
  const firstAdMetadataOrId = action.adsMetadata[0];
37
54
  const { dimensions } =
38
55
  typeof firstAdMetadataOrId === 'number' ? BANNER_ADS_META[firstAdMetadataOrId] : firstAdMetadataOrId;
39
- adElement.style.width = `${dimensions.width}px`;
56
+ adElement.style.width = wrapperType === 'tbody' ? '100%' : `${dimensions.width}px`;
40
57
  adElement.style.height = `${dimensions.height}px`;
41
58
  adElement.style.border = 'none';
42
59
  for (const styleProp in elementStyle) {
@@ -45,7 +62,7 @@ const processInsertAdActionOnce = async (action: InsertAdAction, wrapperElement:
45
62
  adElement.setAttribute(TEMPLE_WALLET_AD_ATTRIBUTE_NAME, 'true');
46
63
 
47
64
  adjustToAd(wrapperElement, dimensions);
48
- wrapperElement.appendChild(adElement);
65
+ insertionPoint.appendChild(adElement);
49
66
 
50
67
  switch (action.type) {
51
68
  case AdActionType.ReplaceAllChildren:
@@ -66,7 +83,36 @@ const processInsertAdActionOnce = async (action: InsertAdAction, wrapperElement:
66
83
  break;
67
84
  }
68
85
 
69
- subscribeToAdsStackIframeLoadIfNecessary(adElement);
86
+ if (wrapperType !== 'tbody') {
87
+ const {
88
+ borderLeftWidth: adElementBLWidth,
89
+ borderRightWidth: adElementBRWidth,
90
+ borderTopWidth: adElementBTWidth,
91
+ borderBottomWidth: adElementBBWidth
92
+ } = window.getComputedStyle(adElement);
93
+ const {
94
+ borderLeftWidth: wrapperElementBLWidth,
95
+ borderRightWidth: wrapperElementBRWidth,
96
+ borderTopWidth: wrapperElementBTWidth,
97
+ borderBottomWidth: wrapperElementBBWidth
98
+ } = window.getComputedStyle(wrapperElement);
99
+ const horizontalBordersWidth = sumPxValues([
100
+ adElementBLWidth,
101
+ adElementBRWidth,
102
+ wrapperElementBLWidth,
103
+ wrapperElementBRWidth
104
+ ]);
105
+ const verticalBordersWidth = sumPxValues([
106
+ adElementBTWidth,
107
+ adElementBBWidth,
108
+ wrapperElementBTWidth,
109
+ wrapperElementBBWidth
110
+ ]);
111
+ adElement.style.width = `${dimensions.width - horizontalBordersWidth}px`;
112
+ adElement.style.height = `${dimensions.height - verticalBordersWidth}px`;
113
+ }
114
+
115
+ subscribeToAdsStackIframeLoadIfNecessary(adElement).catch(onAdsStackError);
70
116
 
71
117
  let currentParentDepth = 0;
72
118
  stylesOverrides.forEach(({ parentDepth, style }) => {
@@ -81,20 +127,51 @@ const processInsertAdActionOnce = async (action: InsertAdAction, wrapperElement:
81
127
  };
82
128
 
83
129
  export const processInsertAdAction = async (action: InsertAdAction) => {
84
- const { divWrapperStyle = {} } = action;
85
-
86
- const wrapperElement = document.createElement('div');
130
+ const { wrapperStyle = {}, adsMetadata, wrapperType = 'div', colsAfter = 0, colsBefore = 0, colspan = 1 } = action;
131
+ const isNativeAd = adsMetadata.every(adMetadata => typeof adMetadata === 'object');
132
+
133
+ let wrapperElement: HTMLDivElement | HTMLTableSectionElement;
134
+ let insertionPoint: HTMLDivElement | HTMLTableCellElement;
135
+ if (wrapperType === 'div') {
136
+ wrapperElement = document.createElement('div');
137
+ insertionPoint = wrapperElement;
138
+ } else {
139
+ wrapperElement = document.createElement('tbody');
140
+ const row = document.createElement('tr');
141
+ wrapperElement.appendChild(row);
142
+ for (let i = 0; i < colsBefore; i++) {
143
+ row.appendChild(document.createElement('td'));
144
+ }
145
+ const cell = document.createElement('td');
146
+ cell.colSpan = colspan;
147
+ row.appendChild(cell);
148
+ insertionPoint = cell;
149
+ for (let i = 0; i < colsAfter; i++) {
150
+ row.appendChild(document.createElement('td'));
151
+ }
152
+ }
87
153
  wrapperElement.setAttribute(TEMPLE_WALLET_AD_ATTRIBUTE_NAME, 'true');
154
+ if (isNativeAd) {
155
+ wrapperElement.setAttribute(TEMPLE_WALLET_NATIVE_AD_ATTRIBUTE_NAME, 'true');
156
+ }
88
157
 
89
- overrideElementStyles(wrapperElement, divWrapperStyle);
90
-
91
- await processInsertAdActionOnce(action, wrapperElement).catch(error => {
92
- console.error('Inserting an ad attempt error:', error);
158
+ overrideElementStyles(wrapperElement, wrapperStyle);
93
159
 
160
+ const hideFailedAd = () => {
94
161
  const emptyAdElement = document.createElement('div');
95
162
  emptyAdElement.setAttribute(TEMPLE_WALLET_AD_ATTRIBUTE_NAME, 'true');
163
+ if (isNativeAd) {
164
+ emptyAdElement.setAttribute(TEMPLE_WALLET_NATIVE_AD_ATTRIBUTE_NAME, 'true');
165
+ }
96
166
  emptyAdElement.style.display = 'none';
97
167
  wrapperElement.replaceWith(emptyAdElement);
168
+ };
169
+ await processInsertAdActionOnce(action, wrapperElement, insertionPoint, error => {
170
+ console.error('Failed to render ads', error);
171
+ hideFailedAd();
172
+ }).catch(error => {
173
+ console.error('Inserting an ad attempt error:', error);
174
+ hideFailedAd();
98
175
 
99
176
  throw error;
100
177
  });
@@ -3,14 +3,13 @@ import {
3
3
  AD_ERROR_MESSAGE_TYPE,
4
4
  AD_READY_MESSAGE_TYPE,
5
5
  AD_RENDER_START_MESSAGE_TYPE,
6
- AD_RESIZE_MESSAGE_TYPE
6
+ AD_RESIZE_MESSAGE_TYPE,
7
+ AD_VARIANT_READY_TIMEOUT
7
8
  } from './constants';
8
9
  import { makeHypelabAdView, makePersonaAdView, makeTKeyAdView } from './execute-ads-actions/ads-views';
9
10
  import { AdView } from './types/ad-view';
10
11
  import { AdMetadata } from './types/ads-meta';
11
12
 
12
- const IFRAME_READY_TIMEOUT = 30_000;
13
-
14
13
  const broadcastMessage = (content: unknown) => window.parent.postMessage(JSON.stringify(content), '*');
15
14
 
16
15
  /**
@@ -58,10 +57,10 @@ export const renderAdsStack = async (
58
57
 
59
58
  if (element instanceof HTMLIFrameElement) {
60
59
  return new Promise<void>((resolve, reject) => {
61
- setTimeout(() => {
60
+ const responseTimeout = setTimeout(() => {
62
61
  window.removeEventListener('message', messageListener);
63
62
  reject(new Error(`Timeout exceeded for ${adId}, ad source: ${JSON.stringify(source)}`));
64
- }, IFRAME_READY_TIMEOUT);
63
+ }, AD_VARIANT_READY_TIMEOUT);
65
64
 
66
65
  let wasReady = false;
67
66
  const messageListener = (event: MessageEvent<any>) => {
@@ -77,17 +76,21 @@ export const renderAdsStack = async (
77
76
  if (!wasReady) {
78
77
  wasReady = true;
79
78
  broadcastMessage({ id: adId, type: AD_READY_MESSAGE_TYPE, adMetadata });
79
+ clearTimeout(responseTimeout);
80
80
  resolve();
81
81
  }
82
82
  break;
83
83
  case AD_ERROR_MESSAGE_TYPE:
84
84
  window.removeEventListener('message', messageListener);
85
+ clearTimeout(responseTimeout);
85
86
  reject(new Error(data.reason ?? 'Unknown error'));
86
87
  break;
87
88
  case AD_RESIZE_MESSAGE_TYPE:
88
89
  const { width, height } = data;
89
90
  if (width > 0 && height > 0) {
90
- element.style.width = `${width}px`;
91
+ if (!element.style.width?.endsWith('%')) {
92
+ element.style.width = `${width}px`;
93
+ }
91
94
  element.style.height = `${height}px`;
92
95
  broadcastMessage({ ...data, id: adId });
93
96
  }
@@ -1,7 +1,7 @@
1
1
  import { StringRecord } from 'src/utils';
2
2
 
3
3
  import { AdMetadata } from './ads-meta';
4
- import type { AdStylesOverrides } from './temple-wallet-api';
4
+ import type { AdStylesOverrides, RawPermanentAdPlacesRule } from './temple-wallet-api';
5
5
 
6
6
  export enum AdActionType {
7
7
  ReplaceAllChildren = 'replace-all-children',
@@ -21,12 +21,12 @@ interface AdActionBase {
21
21
  type: AdActionType;
22
22
  }
23
23
 
24
- interface InsertAdActionProps {
24
+ interface InsertAdActionProps
25
+ extends Pick<RawPermanentAdPlacesRule, 'wrapperStyle' | 'wrapperType' | 'colsAfter' | 'colsBefore' | 'colspan'> {
25
26
  /** A value of 'number' type stands for the value with respective index in `BANNER_ADS_META`. It is done to shorten
26
27
  * ads stack Iframe URL search parameters.
27
28
  */
28
29
  adsMetadata: (number | AdMetadata)[];
29
- divWrapperStyle?: StringRecord<string>;
30
30
  elementStyle?: StringRecord<string>;
31
31
  stylesOverrides?: AdStylesOverrides[];
32
32
  }
@@ -14,6 +14,7 @@ export interface RawAdPlacesRule {
14
14
  };
15
15
  stylesOverrides?: AdStylesOverrides[];
16
16
  shouldHideOriginal?: boolean;
17
+ isNative?: boolean;
17
18
  }
18
19
 
19
20
  export interface RawPermanentAdPlacesRule {
@@ -32,8 +33,13 @@ export interface RawPermanentAdPlacesRule {
32
33
  insertBeforeSelector?: string;
33
34
  insertAfterSelector?: string;
34
35
  insertionsCount?: number;
35
- shouldUseDivWrapper: boolean;
36
+ shouldUseDivWrapper?: boolean;
37
+ wrapperType?: 'div' | 'tbody';
38
+ colsBefore?: number;
39
+ colspan?: number;
40
+ colsAfter?: number;
36
41
  divWrapperStyle?: Record<string, string>;
42
+ wrapperStyle?: Record<string, string>;
37
43
  elementStyle?: Record<string, string>;
38
44
  elementToMeasureSelector?: string;
39
45
  stylesOverrides?: AdStylesOverrides[];