@temple-wallet/extension-ads 6.0.0 → 6.2.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 (95) hide show
  1. package/.editorconfig +9 -0
  2. package/dist/ads-actions/helpers.d.ts +13 -0
  3. package/dist/ads-actions/helpers.js +83 -0
  4. package/dist/ads-actions/helpers.js.map +1 -0
  5. package/dist/ads-actions/index.d.ts +3 -0
  6. package/dist/ads-actions/index.js +45 -0
  7. package/dist/ads-actions/index.js.map +1 -0
  8. package/dist/ads-actions/process-permanent-rule.d.ts +3 -0
  9. package/dist/ads-actions/process-permanent-rule.js +132 -0
  10. package/dist/ads-actions/process-permanent-rule.js.map +1 -0
  11. package/dist/ads-actions/process-providers-ads.d.ts +3 -0
  12. package/dist/ads-actions/process-providers-ads.js +113 -0
  13. package/dist/ads-actions/process-providers-ads.js.map +1 -0
  14. package/dist/ads-actions/process-rule.d.ts +3 -0
  15. package/dist/ads-actions/process-rule.js +97 -0
  16. package/dist/ads-actions/process-rule.js.map +1 -0
  17. package/dist/ads-configuration.d.ts +33 -0
  18. package/dist/ads-configuration.js +29 -0
  19. package/dist/ads-configuration.js.map +1 -0
  20. package/dist/ads-meta.d.ts +17 -0
  21. package/dist/ads-meta.js +135 -0
  22. package/dist/ads-meta.js.map +1 -0
  23. package/dist/constants.d.ts +8 -0
  24. package/dist/constants.js +9 -0
  25. package/dist/constants.js.map +1 -0
  26. package/dist/execute-ads-actions/ads-views/index.d.ts +3 -0
  27. package/dist/execute-ads-actions/ads-views/index.js +4 -0
  28. package/dist/execute-ads-actions/ads-views/index.js.map +1 -0
  29. package/dist/execute-ads-actions/ads-views/make-hypelab-ad.d.ts +3 -0
  30. package/dist/execute-ads-actions/ads-views/make-hypelab-ad.js +60 -0
  31. package/dist/execute-ads-actions/ads-views/make-hypelab-ad.js.map +1 -0
  32. package/dist/execute-ads-actions/ads-views/make-persona-ad.d.ts +3 -0
  33. package/dist/execute-ads-actions/ads-views/make-persona-ad.js +11 -0
  34. package/dist/execute-ads-actions/ads-views/make-persona-ad.js.map +1 -0
  35. package/dist/execute-ads-actions/ads-views/make-tkey-ad.d.ts +2 -0
  36. package/dist/execute-ads-actions/ads-views/make-tkey-ad.js +24 -0
  37. package/dist/execute-ads-actions/ads-views/make-tkey-ad.js.map +1 -0
  38. package/dist/execute-ads-actions/index.d.ts +2 -0
  39. package/dist/execute-ads-actions/index.js +23 -0
  40. package/dist/execute-ads-actions/index.js.map +1 -0
  41. package/dist/execute-ads-actions/observing.d.ts +1 -0
  42. package/dist/execute-ads-actions/observing.js +123 -0
  43. package/dist/execute-ads-actions/observing.js.map +1 -0
  44. package/dist/execute-ads-actions/override-element-styles.d.ts +2 -0
  45. package/dist/execute-ads-actions/override-element-styles.js +6 -0
  46. package/dist/execute-ads-actions/override-element-styles.js.map +1 -0
  47. package/dist/execute-ads-actions/process-insert-ad-action.d.ts +2 -0
  48. package/dist/execute-ads-actions/process-insert-ad-action.js +120 -0
  49. package/dist/execute-ads-actions/process-insert-ad-action.js.map +1 -0
  50. package/dist/index.d.ts +7 -3
  51. package/dist/index.js +13 -13
  52. package/dist/index.js.map +1 -0
  53. package/dist/render-ads-stack.d.ts +7 -0
  54. package/dist/render-ads-stack.js +101 -0
  55. package/dist/render-ads-stack.js.map +1 -0
  56. package/dist/temple-wallet-api.d.ts +17 -0
  57. package/dist/temple-wallet-api.js +56 -0
  58. package/dist/temple-wallet-api.js.map +1 -0
  59. package/dist/transform-raw-rules.d.ts +3 -0
  60. package/dist/transform-raw-rules.js +117 -0
  61. package/dist/transform-raw-rules.js.map +1 -0
  62. package/dist/types/ad-view.d.ts +3 -0
  63. package/dist/types/ad-view.js +2 -0
  64. package/dist/types/ad-view.js.map +1 -0
  65. package/dist/types/ads-actions.d.ts +54 -0
  66. package/dist/types/ads-actions.js +15 -0
  67. package/dist/types/ads-actions.js.map +1 -0
  68. package/dist/types/ads-meta.d.ts +46 -0
  69. package/dist/types/ads-meta.js +2 -0
  70. package/dist/types/ads-meta.js.map +1 -0
  71. package/dist/types/ads-provider.d.ts +7 -0
  72. package/dist/types/ads-provider.js +8 -0
  73. package/dist/types/ads-provider.js.map +1 -0
  74. package/dist/types/ads-rules.d.ts +17 -0
  75. package/dist/types/ads-rules.js +2 -0
  76. package/dist/types/ads-rules.js.map +1 -0
  77. package/dist/types/temple-wallet-api.d.ts +61 -0
  78. package/dist/types/temple-wallet-api.js +2 -0
  79. package/dist/types/temple-wallet-api.js.map +1 -0
  80. package/dist/utils.d.ts +6 -0
  81. package/dist/utils.js +4 -0
  82. package/dist/utils.js.map +1 -0
  83. package/package.json +1 -1
  84. package/src/ads-actions/helpers.ts +11 -3
  85. package/src/ads-actions/process-permanent-rule.ts +26 -10
  86. package/src/ads-actions/process-providers-ads.ts +12 -7
  87. package/src/ads-actions/process-rule.ts +3 -3
  88. package/src/constants.ts +2 -0
  89. package/src/execute-ads-actions/ads-views/make-hypelab-ad.ts +6 -2
  90. package/src/execute-ads-actions/observing.ts +15 -11
  91. package/src/execute-ads-actions/process-insert-ad-action.ts +91 -14
  92. package/src/render-ads-stack.ts +18 -7
  93. package/src/types/ads-actions.ts +3 -3
  94. package/src/types/temple-wallet-api.ts +6 -1
  95. package/.vscode/settings.json +0 -3
@@ -0,0 +1,46 @@
1
+ interface AdSourceBase {
2
+ shouldNotUseStrictContainerLimits?: boolean;
3
+ }
4
+ interface HypeLabBannerAdSource extends AdSourceBase {
5
+ providerName: 'HypeLab';
6
+ native: false;
7
+ size: 'small' | 'high' | 'wide';
8
+ }
9
+ interface HypeLabNativeAdSource extends AdSourceBase {
10
+ providerName: 'HypeLab';
11
+ native: true;
12
+ slug: string;
13
+ }
14
+ /** Only covers TKEY ads for now */
15
+ interface TempleAdSource extends AdSourceBase {
16
+ providerName: 'Temple';
17
+ }
18
+ /** See: https://pub.persona3.io/docs
19
+ * `regular` - 321x101
20
+ * `medium` - 600x160
21
+ * `wide` - 970x90
22
+ * `squarish` - 300x250
23
+ */
24
+ export type PersonaAdShape = 'regular' | 'medium' | 'wide' | 'squarish';
25
+ interface PersonaAdSource extends AdSourceBase {
26
+ providerName: 'Persona';
27
+ shape: PersonaAdShape;
28
+ }
29
+ export type HypeLabAdSources = HypeLabBannerAdSource | HypeLabNativeAdSource;
30
+ type AdSource = HypeLabAdSources | TempleAdSource | PersonaAdSource;
31
+ export interface AdDimensions {
32
+ width: number;
33
+ height: number;
34
+ minContainerWidth: number;
35
+ minContainerHeight: number;
36
+ maxContainerWidth: number;
37
+ maxContainerHeight: number;
38
+ }
39
+ export interface AdMetadata {
40
+ source: AdSource;
41
+ dimensions: {
42
+ width?: number;
43
+ height: number;
44
+ };
45
+ }
46
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ads-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ads-meta.js","sourceRoot":"","sources":["../../src/types/ads-meta.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ export declare enum AdsProviderTitle {
2
+ Optimal = "Optimal",
3
+ HypeLab = "HypeLab",
4
+ Persona = "Persona",
5
+ Temple = "Temple Wallet"
6
+ }
7
+ export type AdsProviderName = keyof typeof AdsProviderTitle;
@@ -0,0 +1,8 @@
1
+ export var AdsProviderTitle;
2
+ (function (AdsProviderTitle) {
3
+ AdsProviderTitle["Optimal"] = "Optimal";
4
+ AdsProviderTitle["HypeLab"] = "HypeLab";
5
+ AdsProviderTitle["Persona"] = "Persona";
6
+ AdsProviderTitle["Temple"] = "Temple Wallet";
7
+ })(AdsProviderTitle || (AdsProviderTitle = {}));
8
+ //# sourceMappingURL=ads-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ads-provider.js","sourceRoot":"","sources":["../../src/types/ads-provider.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,uCAAmB,CAAA;IACnB,uCAAmB,CAAA;IACnB,uCAAmB,CAAA;IACnB,4CAAwB,CAAA;AAC1B,CAAC,EALW,gBAAgB,KAAhB,gBAAgB,QAK3B"}
@@ -0,0 +1,17 @@
1
+ import { RawAdPlacesRule, RawPermanentAdPlacesRule } from './temple-wallet-api';
2
+ export interface AdPlacesRule extends Omit<RawAdPlacesRule, 'urlRegexes'> {
3
+ urlRegexes: RegExp[];
4
+ }
5
+ export interface PermanentAdPlacesRule extends Omit<RawPermanentAdPlacesRule, 'urlRegexes'> {
6
+ urlRegexes: RegExp[];
7
+ isNative: boolean;
8
+ }
9
+ export interface AdsRules {
10
+ adPlacesRules: Array<Omit<AdPlacesRule, 'urlRegexes'>>;
11
+ permanentAdPlacesRules: PermanentAdPlacesRule[];
12
+ providersSelectors: Array<{
13
+ selector: string;
14
+ parentDepth: number;
15
+ }>;
16
+ timestamp: number;
17
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ads-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ads-rules.js","sourceRoot":"","sources":["../../src/types/ads-rules.ts"],"names":[],"mappings":""}
@@ -0,0 +1,61 @@
1
+ export interface AdStylesOverrides {
2
+ parentDepth: number;
3
+ style: Record<string, string>;
4
+ }
5
+ export interface RawAdPlacesRule {
6
+ urlRegexes: string[];
7
+ selector: {
8
+ isMultiple: boolean;
9
+ cssString: string;
10
+ parentDepth: number;
11
+ shouldUseDivWrapper: boolean;
12
+ divWrapperStyle?: Record<string, string>;
13
+ };
14
+ stylesOverrides?: AdStylesOverrides[];
15
+ shouldHideOriginal?: boolean;
16
+ }
17
+ export interface RawPermanentAdPlacesRule {
18
+ urlRegexes: string[];
19
+ adSelector: {
20
+ isMultiple: boolean;
21
+ cssString: string;
22
+ parentDepth: number;
23
+ };
24
+ parentSelector: {
25
+ isMultiple: boolean;
26
+ cssString: string;
27
+ parentDepth: number;
28
+ };
29
+ insertionIndex?: number;
30
+ insertBeforeSelector?: string;
31
+ insertAfterSelector?: string;
32
+ insertionsCount?: number;
33
+ shouldUseDivWrapper?: boolean;
34
+ wrapperType?: 'div' | 'tbody';
35
+ colsBefore?: number;
36
+ colspan?: number;
37
+ colsAfter?: number;
38
+ divWrapperStyle?: Record<string, string>;
39
+ wrapperStyle?: Record<string, string>;
40
+ elementStyle?: Record<string, string>;
41
+ elementToMeasureSelector?: string;
42
+ stylesOverrides?: AdStylesOverrides[];
43
+ shouldHideOriginal?: boolean;
44
+ }
45
+ export interface RawAdProvidersRule {
46
+ urlRegexes: string[];
47
+ providers: string[];
48
+ }
49
+ export interface RawAllAdsRules {
50
+ adPlacesRulesForAllDomains: Record<string, RawAdPlacesRule[]>;
51
+ providersRulesForAllDomains: Record<string, RawAdProvidersRule[]>;
52
+ providersSelectors: Record<string, (string | {
53
+ selector: string;
54
+ parentDepth: number;
55
+ })[]>;
56
+ providersToReplaceAtAllSites: string[];
57
+ permanentAdPlacesRulesForAllDomains: Record<string, RawPermanentAdPlacesRule[]>;
58
+ permanentNativeAdPlacesRulesForAllDomains: Record<string, RawPermanentAdPlacesRule[]>;
59
+ adsReplaceUrlsBlacklist: string[];
60
+ timestamp: number;
61
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=temple-wallet-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temple-wallet-api.js","sourceRoot":"","sources":["../../src/types/temple-wallet-api.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ /** From lodash */
2
+ type Truthy<T> = T extends null | undefined | void | false | '' | 0 | 0n ? never : T;
3
+ export declare const isTruthy: <T>(value: T) => value is Truthy<T>;
4
+ export declare const delay: (ms?: number) => Promise<unknown>;
5
+ export type StringRecord<T = string> = Record<string, T>;
6
+ export {};
package/dist/utils.js ADDED
@@ -0,0 +1,4 @@
1
+ export const isTruthy = (value) => Boolean(value);
2
+ const DEFAULT_DELAY = 300;
3
+ export const delay = (ms = DEFAULT_DELAY) => new Promise(res => setTimeout(res, ms));
4
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAI,KAAQ,EAAsB,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAE5E,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,GAAG,aAAa,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temple-wallet/extension-ads",
3
- "version": "6.0.0",
3
+ "version": "6.2.0",
4
4
  "main": "dist/index.js",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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
@@ -64,7 +64,7 @@ const processSelectedElement = (
64
64
 
65
65
  const actionBaseCommonProps = {
66
66
  shouldUseDivWrapper,
67
- divWrapperStyle
67
+ wrapperStyle: divWrapperStyle
68
68
  };
69
69
 
70
70
  let actionsBases: (InsertAdActionWithoutMeta | HideElementAction | RemoveElementAction)[];
@@ -108,7 +108,7 @@ const processSelectedElement = (
108
108
  insertionIndex: 0,
109
109
  stylesOverrides,
110
110
  isSiblingReplacement: true,
111
- divWrapperStyle: { display: 'contents' }
111
+ wrapperStyle: { display: 'contents' }
112
112
  };
113
113
 
114
114
  actionsBases = Array.from(banner.children)
@@ -122,7 +122,7 @@ const processSelectedElement = (
122
122
  type: AdActionType.ReplaceAllChildren,
123
123
  parent: banner,
124
124
  stylesOverrides,
125
- divWrapperStyle: { display: 'contents' }
125
+ wrapperStyle: { display: 'contents' }
126
126
  };
127
127
  actionsBases = [replaceAllChildrenActionBase];
128
128
  }
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
  });