@builder.io/sdk-svelte 5.2.11 → 5.2.13

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 (52) hide show
  1. package/lib/browser/blocks/image/image.svelte +3 -1
  2. package/lib/browser/blocks/personalization-container/helpers/inlined-fns.d.ts +8 -2
  3. package/lib/browser/blocks/personalization-container/helpers/inlined-fns.js +22 -2
  4. package/lib/browser/blocks/personalization-container/helpers.js +5 -1
  5. package/lib/browser/components/block/components/block-styles.svelte +2 -2
  6. package/lib/browser/components/block/components/live-edit-block-styles.svelte +2 -2
  7. package/lib/browser/constants/sdk-version.d.ts +1 -1
  8. package/lib/browser/constants/sdk-version.js +1 -1
  9. package/lib/browser/helpers/css.d.ts +2 -0
  10. package/lib/browser/helpers/css.js +14 -0
  11. package/lib/browser/helpers/studio-user-attributes.d.ts +6 -0
  12. package/lib/browser/helpers/studio-user-attributes.js +31 -0
  13. package/lib/browser/helpers/user-attributes.d.ts +6 -2
  14. package/lib/browser/helpers/user-attributes.js +20 -6
  15. package/lib/edge/blocks/image/image.svelte +3 -1
  16. package/lib/edge/blocks/personalization-container/helpers/inlined-fns.d.ts +8 -2
  17. package/lib/edge/blocks/personalization-container/helpers/inlined-fns.js +22 -2
  18. package/lib/edge/blocks/personalization-container/helpers.js +5 -1
  19. package/lib/edge/components/block/components/block-styles.svelte +2 -2
  20. package/lib/edge/components/block/components/live-edit-block-styles.svelte +2 -2
  21. package/lib/edge/constants/sdk-version.d.ts +1 -1
  22. package/lib/edge/constants/sdk-version.js +1 -1
  23. package/lib/edge/helpers/css.d.ts +2 -0
  24. package/lib/edge/helpers/css.js +14 -0
  25. package/lib/edge/helpers/studio-user-attributes.d.ts +6 -0
  26. package/lib/edge/helpers/studio-user-attributes.js +31 -0
  27. package/lib/edge/helpers/user-attributes.d.ts +6 -2
  28. package/lib/edge/helpers/user-attributes.js +20 -6
  29. package/lib/node/blocks/image/image.svelte +3 -1
  30. package/lib/node/blocks/personalization-container/helpers/inlined-fns.d.ts +8 -2
  31. package/lib/node/blocks/personalization-container/helpers/inlined-fns.js +22 -2
  32. package/lib/node/blocks/personalization-container/helpers.js +5 -1
  33. package/lib/node/components/block/components/block-styles.svelte +2 -2
  34. package/lib/node/components/block/components/live-edit-block-styles.svelte +2 -2
  35. package/lib/node/constants/sdk-version.d.ts +1 -1
  36. package/lib/node/constants/sdk-version.js +1 -1
  37. package/lib/node/helpers/css.d.ts +2 -0
  38. package/lib/node/helpers/css.js +14 -0
  39. package/lib/node/helpers/studio-user-attributes.d.ts +6 -0
  40. package/lib/node/helpers/studio-user-attributes.js +31 -0
  41. package/lib/node/helpers/user-attributes.d.ts +6 -2
  42. package/lib/node/helpers/user-attributes.js +20 -6
  43. package/package.json +1 -1
  44. package/src/blocks/image/image.svelte +3 -1
  45. package/src/blocks/personalization-container/helpers/inlined-fns.ts +23 -2
  46. package/src/blocks/personalization-container/helpers.ts +5 -1
  47. package/src/components/block/components/block-styles.svelte +8 -2
  48. package/src/components/block/components/live-edit-block-styles.svelte +8 -2
  49. package/src/constants/sdk-version.ts +1 -1
  50. package/src/helpers/css.ts +15 -0
  51. package/src/helpers/studio-user-attributes.ts +31 -0
  52. package/src/helpers/user-attributes.ts +20 -6
@@ -4,7 +4,9 @@ export interface UserAttributes {
4
4
  export declare const USER_ATTRIBUTES_COOKIE_NAME = "builder.userAttributes";
5
5
  export declare function createUserAttributesService(): {
6
6
  setUserAttributes(newAttrs: UserAttributes): void;
7
- getUserAttributes(): any;
7
+ getUserAttributes(): {
8
+ [x: string]: any;
9
+ };
8
10
  subscribeOnUserAttributesChange(callback: (attrs: UserAttributes) => void, { fireImmediately }?: {
9
11
  fireImmediately?: boolean | undefined;
10
12
  }): () => void;
@@ -12,7 +14,9 @@ export declare function createUserAttributesService(): {
12
14
  };
13
15
  export declare const userAttributesService: {
14
16
  setUserAttributes(newAttrs: UserAttributes): void;
15
- getUserAttributes(): any;
17
+ getUserAttributes(): {
18
+ [x: string]: any;
19
+ };
16
20
  subscribeOnUserAttributesChange(callback: (attrs: UserAttributes) => void, { fireImmediately }?: {
17
21
  fireImmediately?: boolean | undefined;
18
22
  }): () => void;
@@ -2,17 +2,25 @@ import { TARGET } from '../constants/target';
2
2
  import { isBrowser } from '../functions/is-browser';
3
3
  import { getCookieSync, setCookie } from './cookie';
4
4
  import { noSerializeWrapper } from './no-serialize-wrapper';
5
+ import { getStudioUserAttributes } from './studio-user-attributes';
5
6
  export const USER_ATTRIBUTES_COOKIE_NAME = 'builder.userAttributes';
6
7
  export function createUserAttributesService() {
7
8
  let canTrack = true;
8
9
  const subscribers = new Set();
10
+ const getCookieUserAttributes = () => {
11
+ const cookie = getCookieSync({
12
+ name: USER_ATTRIBUTES_COOKIE_NAME,
13
+ canTrack
14
+ });
15
+ return cookie ? JSON.parse(cookie) : {};
16
+ };
9
17
  return {
10
18
  setUserAttributes(newAttrs) {
11
19
  if (!isBrowser()) {
12
20
  return;
13
21
  }
14
22
  const userAttributes = {
15
- ...this.getUserAttributes(),
23
+ ...getCookieUserAttributes(),
16
24
  ...newAttrs
17
25
  };
18
26
  setCookie({
@@ -20,16 +28,22 @@ export function createUserAttributesService() {
20
28
  value: JSON.stringify(userAttributes),
21
29
  canTrack
22
30
  });
23
- subscribers.forEach(callback => callback(userAttributes));
31
+ // Kept out of the cookie above so previewing never persists into a real visitor's
32
+ // session, but re-applied here so the site's own attributes cannot clobber it.
33
+ const studioAttributes = getStudioUserAttributes();
34
+ subscribers.forEach(callback => callback({
35
+ ...userAttributes,
36
+ ...studioAttributes
37
+ }));
24
38
  },
25
39
  getUserAttributes() {
26
40
  if (!isBrowser()) {
27
41
  return {};
28
42
  }
29
- return JSON.parse(getCookieSync({
30
- name: USER_ATTRIBUTES_COOKIE_NAME,
31
- canTrack
32
- }) || '{}');
43
+ return {
44
+ ...getCookieUserAttributes(),
45
+ ...getStudioUserAttributes()
46
+ };
33
47
  },
34
48
  subscribeOnUserAttributesChange(callback, { fireImmediately } = {}) {
35
49
  subscribers.add(callback);
@@ -78,7 +78,9 @@ onMount(() => { });
78
78
  <source type="image/webp" srcset={webpSrcSet()} sizes={sizesToUse()} />
79
79
  {/if}<img
80
80
  style={stringifyStyles({
81
- objectPosition: backgroundPosition || "center",
81
+ objectPosition: `var(--builder-image-position, ${
82
+ backgroundPosition || "center"
83
+ })`,
82
84
  objectFit: backgroundSize || "cover",
83
85
  ...aspectRatioCss(),
84
86
  })}
@@ -4,8 +4,14 @@
4
4
  */
5
5
  import type { Query, UserAttributes } from '../helpers';
6
6
  import { type PersonalizationContainerProps } from '../personalization-container.types';
7
+ /**
8
+ * Mirrors helpers/studio-user-attributes.ts; stringification rules out sharing the code.
9
+ * Anything referenced from module scope becomes an undefined global at runtime.
10
+ */
11
+ export declare function getStudioUserAttributes(): Record<string, unknown>;
7
12
  export declare function filterWithCustomTargeting(userAttributes: UserAttributes, query: Query[], startDate?: string, endDate?: string): boolean;
8
13
  export declare function updateVisibilityStylesScript(variants: PersonalizationContainerProps['variants'], blockId: string, isHydrationTarget: boolean, locale?: string): void;
9
- export declare const PERSONALIZATION_SCRIPT = "function getPersonalizedVariant(variants, blockId, isHydrationTarget, locale) {\n if (!navigator.cookieEnabled) {\n return;\n }\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n const parentDiv = document.currentScript?.parentElement;\n const variantId = parentDiv?.getAttribute('data-variant-id');\n const isDefaultVariant = variantId === `${blockId}-default`;\n const isWinningVariant = winningVariantIndex !== -1 && variantId === `${blockId}-${winningVariantIndex}` || winningVariantIndex === -1 && isDefaultVariant;\n if (isWinningVariant && !isDefaultVariant) {\n parentDiv?.removeAttribute('hidden');\n parentDiv?.removeAttribute('aria-hidden');\n } else if (!isWinningVariant && isDefaultVariant) {\n parentDiv?.setAttribute('hidden', 'true');\n parentDiv?.setAttribute('aria-hidden', 'true');\n }\n if (isHydrationTarget) {\n if (!isWinningVariant) {\n const itsStyleEl = parentDiv?.previousElementSibling;\n if (itsStyleEl) {\n itsStyleEl.remove();\n }\n parentDiv?.remove();\n }\n const thisScript = document.currentScript;\n if (thisScript) {\n thisScript.remove();\n }\n }\n}";
14
+ export declare const STUDIO_USER_ATTRIBUTES_SCRIPT = "function getStudioUserAttributes() {\n const params = new URLSearchParams(window.location.search);\n if (params.get('builder.preview') !== 'BUILDER_STUDIO') {\n return {};\n }\n const prefix = 'builder.userAttributes.';\n const attributes = {};\n params.forEach(function (value, key) {\n if (key.indexOf(prefix) === 0) {\n attributes[key.slice(prefix.length)] = value === 'true' ? true : value === 'false' ? false : value;\n }\n });\n return attributes;\n}";
15
+ export declare const PERSONALIZATION_SCRIPT = "function getPersonalizedVariant(variants, blockId, isHydrationTarget, locale) {\n if (!navigator.cookieEnabled) {\n return;\n }\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n Object.assign(attributes, window.builderIoStudioUserAttributes());\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n const parentDiv = document.currentScript?.parentElement;\n const variantId = parentDiv?.getAttribute('data-variant-id');\n const isDefaultVariant = variantId === `${blockId}-default`;\n const isWinningVariant = winningVariantIndex !== -1 && variantId === `${blockId}-${winningVariantIndex}` || winningVariantIndex === -1 && isDefaultVariant;\n if (isWinningVariant && !isDefaultVariant) {\n parentDiv?.removeAttribute('hidden');\n parentDiv?.removeAttribute('aria-hidden');\n } else if (!isWinningVariant && isDefaultVariant) {\n parentDiv?.setAttribute('hidden', 'true');\n parentDiv?.setAttribute('aria-hidden', 'true');\n }\n if (isHydrationTarget) {\n if (!isWinningVariant) {\n const itsStyleEl = parentDiv?.previousElementSibling;\n if (itsStyleEl) {\n itsStyleEl.remove();\n }\n parentDiv?.remove();\n }\n const thisScript = document.currentScript;\n if (thisScript) {\n thisScript.remove();\n }\n }\n}";
10
16
  export declare const FILTER_WITH_CUSTOM_TARGETING_SCRIPT = "function filterWithCustomTargeting(userAttributes, query, startDate, endDate) {\n function isString(val) {\n return typeof val === 'string';\n }\n function isNumber(val) {\n return typeof val === 'number';\n }\n function objectMatchesQuery(userattr, query) {\n const result = (() => {\n const property = query.property;\n const operator = query.operator;\n let testValue = query.value;\n if (query && query.property === 'urlPath' && query.value && typeof query.value === 'string' && query.value !== '/' && query.value.endsWith('/')) {\n testValue = query.value.slice(0, -1);\n }\n if (!(property && operator)) {\n return true;\n }\n if (Array.isArray(testValue)) {\n if (operator === 'isNot') {\n return testValue.every(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n return !!testValue.find(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n const value = userattr[property];\n if (Array.isArray(value)) {\n return value.includes(testValue);\n }\n switch (operator) {\n case 'is':\n return value === testValue;\n case 'isNot':\n return value !== testValue;\n case 'contains':\n return (isString(value) || Array.isArray(value)) && value.includes(String(testValue));\n case 'startsWith':\n return isString(value) && value.startsWith(String(testValue));\n case 'endsWith':\n return isString(value) && value.endsWith(String(testValue));\n case 'greaterThan':\n return isNumber(value) && isNumber(testValue) && value > testValue;\n case 'lessThan':\n return isNumber(value) && isNumber(testValue) && value < testValue;\n case 'greaterThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value >= testValue;\n case 'lessThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value <= testValue;\n default:\n return false;\n }\n })();\n return result;\n }\n const item = {\n query,\n startDate,\n endDate\n };\n const now = userAttributes.date && new Date(userAttributes.date) || new Date();\n if (item.startDate && new Date(item.startDate) > now) {\n return false;\n } else if (item.endDate && new Date(item.endDate) < now) {\n return false;\n }\n if (!item.query || !item.query.length) {\n return true;\n }\n return item.query.every(filter => {\n return objectMatchesQuery(userAttributes, filter);\n });\n}";
11
- export declare const UPDATE_VISIBILITY_STYLES_SCRIPT = "function updateVisibilityStylesScript(variants, blockId, isHydrationTarget, locale) {\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const visibilityStylesEl = document.currentScript?.previousElementSibling;\n if (!visibilityStylesEl) {\n return;\n }\n if (isHydrationTarget) {\n visibilityStylesEl.remove();\n const currentScript = document.currentScript;\n if (currentScript) {\n currentScript.remove();\n }\n } else {\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n if (winningVariantIndex !== -1) {\n let newStyleStr = variants?.map((_, index) => {\n if (index === winningVariantIndex) return '';\n return `div[data-variant-id=\"${blockId}-${index}\"] { display: none !important; } `;\n }).join('') || '';\n newStyleStr += `div[data-variant-id=\"${blockId}-default\"] { display: none !important; } `;\n visibilityStylesEl.innerHTML = newStyleStr;\n }\n }\n}";
17
+ export declare const UPDATE_VISIBILITY_STYLES_SCRIPT = "function updateVisibilityStylesScript(variants, blockId, isHydrationTarget, locale) {\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const visibilityStylesEl = document.currentScript?.previousElementSibling;\n if (!visibilityStylesEl) {\n return;\n }\n if (isHydrationTarget) {\n visibilityStylesEl.remove();\n const currentScript = document.currentScript;\n if (currentScript) {\n currentScript.remove();\n }\n } else {\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n Object.assign(attributes, window.builderIoStudioUserAttributes());\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n if (winningVariantIndex !== -1) {\n let newStyleStr = variants?.map((_, index) => {\n if (index === winningVariantIndex) return '';\n return `div[data-variant-id=\"${blockId}-${index}\"] { display: none !important; } `;\n }).join('') || '';\n newStyleStr += `div[data-variant-id=\"${blockId}-default\"] { display: none !important; } `;\n visibilityStylesEl.innerHTML = newStyleStr;\n }\n }\n}";
@@ -3,6 +3,24 @@
3
3
  * They cannot import anything.
4
4
  */
5
5
  import '../personalization-container.types';
6
+ /**
7
+ * Mirrors helpers/studio-user-attributes.ts; stringification rules out sharing the code.
8
+ * Anything referenced from module scope becomes an undefined global at runtime.
9
+ */
10
+ export function getStudioUserAttributes() {
11
+ const params = new URLSearchParams(window.location.search);
12
+ if (params.get('builder.preview') !== 'BUILDER_STUDIO') {
13
+ return {};
14
+ }
15
+ const prefix = 'builder.userAttributes.';
16
+ const attributes = {};
17
+ params.forEach(function (value, key) {
18
+ if (key.indexOf(prefix) === 0) {
19
+ attributes[key.slice(prefix.length)] = value === 'true' ? true : value === 'false' ? false : value;
20
+ }
21
+ });
22
+ return attributes;
23
+ }
6
24
  export function filterWithCustomTargeting(userAttributes, query, startDate, endDate) {
7
25
  function isString(val) {
8
26
  return typeof val === 'string';
@@ -112,6 +130,7 @@ export function updateVisibilityStylesScript(variants, blockId, isHydrationTarge
112
130
  if (locale) {
113
131
  attributes.locale = locale;
114
132
  }
133
+ Object.assign(attributes, window.builderIoStudioUserAttributes());
115
134
  const winningVariantIndex = variants?.findIndex(function (variant) {
116
135
  return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);
117
136
  });
@@ -126,6 +145,7 @@ export function updateVisibilityStylesScript(variants, blockId, isHydrationTarge
126
145
  }
127
146
  }
128
147
  }
129
- export const PERSONALIZATION_SCRIPT = "function getPersonalizedVariant(variants, blockId, isHydrationTarget, locale) {\n if (!navigator.cookieEnabled) {\n return;\n }\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n const parentDiv = document.currentScript?.parentElement;\n const variantId = parentDiv?.getAttribute('data-variant-id');\n const isDefaultVariant = variantId === `${blockId}-default`;\n const isWinningVariant = winningVariantIndex !== -1 && variantId === `${blockId}-${winningVariantIndex}` || winningVariantIndex === -1 && isDefaultVariant;\n if (isWinningVariant && !isDefaultVariant) {\n parentDiv?.removeAttribute('hidden');\n parentDiv?.removeAttribute('aria-hidden');\n } else if (!isWinningVariant && isDefaultVariant) {\n parentDiv?.setAttribute('hidden', 'true');\n parentDiv?.setAttribute('aria-hidden', 'true');\n }\n if (isHydrationTarget) {\n if (!isWinningVariant) {\n const itsStyleEl = parentDiv?.previousElementSibling;\n if (itsStyleEl) {\n itsStyleEl.remove();\n }\n parentDiv?.remove();\n }\n const thisScript = document.currentScript;\n if (thisScript) {\n thisScript.remove();\n }\n }\n}";
148
+ export const STUDIO_USER_ATTRIBUTES_SCRIPT = "function getStudioUserAttributes() {\n const params = new URLSearchParams(window.location.search);\n if (params.get('builder.preview') !== 'BUILDER_STUDIO') {\n return {};\n }\n const prefix = 'builder.userAttributes.';\n const attributes = {};\n params.forEach(function (value, key) {\n if (key.indexOf(prefix) === 0) {\n attributes[key.slice(prefix.length)] = value === 'true' ? true : value === 'false' ? false : value;\n }\n });\n return attributes;\n}";
149
+ export const PERSONALIZATION_SCRIPT = "function getPersonalizedVariant(variants, blockId, isHydrationTarget, locale) {\n if (!navigator.cookieEnabled) {\n return;\n }\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n Object.assign(attributes, window.builderIoStudioUserAttributes());\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n const parentDiv = document.currentScript?.parentElement;\n const variantId = parentDiv?.getAttribute('data-variant-id');\n const isDefaultVariant = variantId === `${blockId}-default`;\n const isWinningVariant = winningVariantIndex !== -1 && variantId === `${blockId}-${winningVariantIndex}` || winningVariantIndex === -1 && isDefaultVariant;\n if (isWinningVariant && !isDefaultVariant) {\n parentDiv?.removeAttribute('hidden');\n parentDiv?.removeAttribute('aria-hidden');\n } else if (!isWinningVariant && isDefaultVariant) {\n parentDiv?.setAttribute('hidden', 'true');\n parentDiv?.setAttribute('aria-hidden', 'true');\n }\n if (isHydrationTarget) {\n if (!isWinningVariant) {\n const itsStyleEl = parentDiv?.previousElementSibling;\n if (itsStyleEl) {\n itsStyleEl.remove();\n }\n parentDiv?.remove();\n }\n const thisScript = document.currentScript;\n if (thisScript) {\n thisScript.remove();\n }\n }\n}";
130
150
  export const FILTER_WITH_CUSTOM_TARGETING_SCRIPT = "function filterWithCustomTargeting(userAttributes, query, startDate, endDate) {\n function isString(val) {\n return typeof val === 'string';\n }\n function isNumber(val) {\n return typeof val === 'number';\n }\n function objectMatchesQuery(userattr, query) {\n const result = (() => {\n const property = query.property;\n const operator = query.operator;\n let testValue = query.value;\n if (query && query.property === 'urlPath' && query.value && typeof query.value === 'string' && query.value !== '/' && query.value.endsWith('/')) {\n testValue = query.value.slice(0, -1);\n }\n if (!(property && operator)) {\n return true;\n }\n if (Array.isArray(testValue)) {\n if (operator === 'isNot') {\n return testValue.every(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n return !!testValue.find(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n const value = userattr[property];\n if (Array.isArray(value)) {\n return value.includes(testValue);\n }\n switch (operator) {\n case 'is':\n return value === testValue;\n case 'isNot':\n return value !== testValue;\n case 'contains':\n return (isString(value) || Array.isArray(value)) && value.includes(String(testValue));\n case 'startsWith':\n return isString(value) && value.startsWith(String(testValue));\n case 'endsWith':\n return isString(value) && value.endsWith(String(testValue));\n case 'greaterThan':\n return isNumber(value) && isNumber(testValue) && value > testValue;\n case 'lessThan':\n return isNumber(value) && isNumber(testValue) && value < testValue;\n case 'greaterThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value >= testValue;\n case 'lessThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value <= testValue;\n default:\n return false;\n }\n })();\n return result;\n }\n const item = {\n query,\n startDate,\n endDate\n };\n const now = userAttributes.date && new Date(userAttributes.date) || new Date();\n if (item.startDate && new Date(item.startDate) > now) {\n return false;\n } else if (item.endDate && new Date(item.endDate) < now) {\n return false;\n }\n if (!item.query || !item.query.length) {\n return true;\n }\n return item.query.every(filter => {\n return objectMatchesQuery(userAttributes, filter);\n });\n}";
131
- export const UPDATE_VISIBILITY_STYLES_SCRIPT = "function updateVisibilityStylesScript(variants, blockId, isHydrationTarget, locale) {\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const visibilityStylesEl = document.currentScript?.previousElementSibling;\n if (!visibilityStylesEl) {\n return;\n }\n if (isHydrationTarget) {\n visibilityStylesEl.remove();\n const currentScript = document.currentScript;\n if (currentScript) {\n currentScript.remove();\n }\n } else {\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n if (winningVariantIndex !== -1) {\n let newStyleStr = variants?.map((_, index) => {\n if (index === winningVariantIndex) return '';\n return `div[data-variant-id=\"${blockId}-${index}\"] { display: none !important; } `;\n }).join('') || '';\n newStyleStr += `div[data-variant-id=\"${blockId}-default\"] { display: none !important; } `;\n visibilityStylesEl.innerHTML = newStyleStr;\n }\n }\n}";
151
+ export const UPDATE_VISIBILITY_STYLES_SCRIPT = "function updateVisibilityStylesScript(variants, blockId, isHydrationTarget, locale) {\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const visibilityStylesEl = document.currentScript?.previousElementSibling;\n if (!visibilityStylesEl) {\n return;\n }\n if (isHydrationTarget) {\n visibilityStylesEl.remove();\n const currentScript = document.currentScript;\n if (currentScript) {\n currentScript.remove();\n }\n } else {\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n Object.assign(attributes, window.builderIoStudioUserAttributes());\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n if (winningVariantIndex !== -1) {\n let newStyleStr = variants?.map((_, index) => {\n if (index === winningVariantIndex) return '';\n return `div[data-variant-id=\"${blockId}-${index}\"] { display: none !important; } `;\n }).join('') || '';\n newStyleStr += `div[data-variant-id=\"${blockId}-default\"] { display: none !important; } `;\n visibilityStylesEl.innerHTML = newStyleStr;\n }\n }\n}";
@@ -1,11 +1,12 @@
1
1
  import { TARGET } from '../../constants/target';
2
2
  import { isBrowser } from '../../functions/is-browser';
3
3
  import { isEditing } from '../../functions/is-editing';
4
- import { FILTER_WITH_CUSTOM_TARGETING_SCRIPT, PERSONALIZATION_SCRIPT, UPDATE_VISIBILITY_STYLES_SCRIPT } from './helpers/inlined-fns';
4
+ import { FILTER_WITH_CUSTOM_TARGETING_SCRIPT, PERSONALIZATION_SCRIPT, STUDIO_USER_ATTRIBUTES_SCRIPT, UPDATE_VISIBILITY_STYLES_SCRIPT } from './helpers/inlined-fns';
5
5
  export const DEFAULT_INDEX = 'default';
6
6
  const FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME = 'filterWithCustomTargeting';
7
7
  const BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME = 'builderIoPersonalization';
8
8
  const UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME = 'updateVisibilityStylesScript';
9
+ const STUDIO_USER_ATTRIBUTES_SCRIPT_FN_NAME = 'builderIoStudioUserAttributes';
9
10
  const PERSONALIZATION_CONTAINER_COMPONENT_NAME = 'PersonalizationContainer';
10
11
  /**
11
12
  * SDKs that support Variant Containers
@@ -94,6 +95,9 @@ export const hasPersonalizationContainer = (content) => containsPersonalizationC
94
95
  export const getInitPersonalizationVariantsFnsScriptString = () => {
95
96
  return `
96
97
  (function() {
98
+ if (!window.${STUDIO_USER_ATTRIBUTES_SCRIPT_FN_NAME}) {
99
+ window.${STUDIO_USER_ATTRIBUTES_SCRIPT_FN_NAME} = ${STUDIO_USER_ATTRIBUTES_SCRIPT};
100
+ }
97
101
  if (!window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME}) {
98
102
  window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME} = ${FILTER_WITH_CUSTOM_TARGETING_SCRIPT};
99
103
  }
@@ -3,7 +3,7 @@
3
3
  <script>import { getMaxWidthQueryForSize, getSizesForBreakpoints, } from "../../../constants/device-sizes";
4
4
  import { TARGET } from "../../../constants/target";
5
5
  import { camelToKebabCase } from "../../../functions/camel-to-kebab-case";
6
- import { createCssClass } from "../../../helpers/css";
6
+ import { createCssClass, getResponsiveStylesWithImagePosition, } from "../../../helpers/css";
7
7
  import { checkIsDefined } from "../../../helpers/nullable";
8
8
  import InlinedStyles from "../../inlined-styles.svelte";
9
9
  export let block;
@@ -21,7 +21,7 @@ $: canShowBlock = () => {
21
21
  };
22
22
  $: css = () => {
23
23
  const processedBlock = block;
24
- const styles = processedBlock.responsiveStyles;
24
+ const styles = getResponsiveStylesWithImagePosition(processedBlock.responsiveStyles, processedBlock.component?.name === "Image");
25
25
  const content = context.content;
26
26
  const sizesWithUpdatedBreakpoints = getSizesForBreakpoints(content?.meta?.breakpoints || {});
27
27
  const contentHasXSmallBreakpoint = Boolean(content?.meta?.breakpoints?.xsmall);
@@ -4,7 +4,7 @@
4
4
  import { TARGET } from "../../../constants/target";
5
5
  import { camelToKebabCase } from "../../../functions/camel-to-kebab-case";
6
6
  import { getProcessedBlock } from "../../../functions/get-processed-block";
7
- import { createCssClass } from "../../../helpers/css";
7
+ import { createCssClass, getResponsiveStylesWithImagePosition, } from "../../../helpers/css";
8
8
  import { findBlockById } from "../../../helpers/find-block";
9
9
  import { checkIsDefined } from "../../../helpers/nullable";
10
10
  import InlinedStyles from "../../inlined-styles.svelte";
@@ -39,7 +39,7 @@ $: canShowBlock = () => {
39
39
  return true;
40
40
  };
41
41
  $: css = () => {
42
- const styles = processedBlock?.()?.responsiveStyles;
42
+ const styles = getResponsiveStylesWithImagePosition(processedBlock?.()?.responsiveStyles, processedBlock?.()?.component?.name === "Image");
43
43
  const content = contextProvider.content;
44
44
  const sizesWithUpdatedBreakpoints = getSizesForBreakpoints(content?.meta?.breakpoints || {});
45
45
  const contentHasXSmallBreakpoint = Boolean(content?.meta?.breakpoints?.xsmall);
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "5.2.11";
1
+ export declare const SDK_VERSION = "5.2.13";
@@ -1 +1 @@
1
- export const SDK_VERSION = "5.2.11";
1
+ export const SDK_VERSION = "5.2.13";
@@ -1,3 +1,5 @@
1
+ import type { BuilderBlock } from '../types/builder-block';
2
+ export declare const getResponsiveStylesWithImagePosition: (styles: BuilderBlock['responsiveStyles'], isBuilderImage: boolean) => BuilderBlock['responsiveStyles'];
1
3
  export declare const convertStyleMapToCSSArray: (style: Partial<CSSStyleDeclaration>) => string[];
2
4
  export declare const convertStyleMapToCSS: (style: Partial<CSSStyleDeclaration>) => string;
3
5
  export declare const createCssClass: ({ mediaQuery, className, styles }: {
@@ -1,5 +1,19 @@
1
1
  import { camelToKebabCase } from '../functions/camel-to-kebab-case';
2
2
  import { checkIsDefined } from './nullable';
3
+ const withBuilderImagePosition = (style, fallback) => {
4
+ const imagePosition = style?.objectPosition || fallback;
5
+ return imagePosition ? {
6
+ ...style,
7
+ '--builder-image-position': imagePosition
8
+ } : style;
9
+ };
10
+ export const getResponsiveStylesWithImagePosition = (styles, isBuilderImage) => isBuilderImage ? {
11
+ ...styles,
12
+ large: withBuilderImagePosition(styles?.large, 'initial'),
13
+ medium: withBuilderImagePosition(styles?.medium),
14
+ small: withBuilderImagePosition(styles?.small),
15
+ xsmall: withBuilderImagePosition(styles?.xsmall)
16
+ } : styles;
3
17
  export const convertStyleMapToCSSArray = (style) => {
4
18
  const cssProps = Object.entries(style).map(([key, value]) => {
5
19
  if (typeof value === 'string') {
@@ -0,0 +1,6 @@
1
+ import type { UserAttributes } from './user-attributes';
2
+ /**
3
+ * Studio passes its targeting overrides as query params because it cannot write the
4
+ * builder.userAttributes cookie on the previewed site's origin.
5
+ */
6
+ export declare const getStudioUserAttributes: () => UserAttributes;
@@ -0,0 +1,31 @@
1
+ import { isBrowser } from '../functions/is-browser';
2
+ const STUDIO_PREVIEW_PARAM = 'builder.preview';
3
+ const STUDIO_PREVIEW_VALUE = 'BUILDER_STUDIO';
4
+ const USER_ATTRIBUTE_PARAM_PREFIX = 'builder.userAttributes.';
5
+ const parseStudioValue = (value) => {
6
+ if (value === 'true')
7
+ return true;
8
+ if (value === 'false')
9
+ return false;
10
+ return value;
11
+ };
12
+ /**
13
+ * Studio passes its targeting overrides as query params because it cannot write the
14
+ * builder.userAttributes cookie on the previewed site's origin.
15
+ */
16
+ export const getStudioUserAttributes = () => {
17
+ if (!isBrowser()) {
18
+ return {};
19
+ }
20
+ const params = new URLSearchParams(window.location.search);
21
+ if (params.get(STUDIO_PREVIEW_PARAM) !== STUDIO_PREVIEW_VALUE) {
22
+ return {};
23
+ }
24
+ const attributes = {};
25
+ params.forEach((value, key) => {
26
+ if (key.startsWith(USER_ATTRIBUTE_PARAM_PREFIX)) {
27
+ attributes[key.slice(USER_ATTRIBUTE_PARAM_PREFIX.length)] = parseStudioValue(value);
28
+ }
29
+ });
30
+ return attributes;
31
+ };
@@ -4,7 +4,9 @@ export interface UserAttributes {
4
4
  export declare const USER_ATTRIBUTES_COOKIE_NAME = "builder.userAttributes";
5
5
  export declare function createUserAttributesService(): {
6
6
  setUserAttributes(newAttrs: UserAttributes): void;
7
- getUserAttributes(): any;
7
+ getUserAttributes(): {
8
+ [x: string]: any;
9
+ };
8
10
  subscribeOnUserAttributesChange(callback: (attrs: UserAttributes) => void, { fireImmediately }?: {
9
11
  fireImmediately?: boolean | undefined;
10
12
  }): () => void;
@@ -12,7 +14,9 @@ export declare function createUserAttributesService(): {
12
14
  };
13
15
  export declare const userAttributesService: {
14
16
  setUserAttributes(newAttrs: UserAttributes): void;
15
- getUserAttributes(): any;
17
+ getUserAttributes(): {
18
+ [x: string]: any;
19
+ };
16
20
  subscribeOnUserAttributesChange(callback: (attrs: UserAttributes) => void, { fireImmediately }?: {
17
21
  fireImmediately?: boolean | undefined;
18
22
  }): () => void;
@@ -2,17 +2,25 @@ import { TARGET } from '../constants/target';
2
2
  import { isBrowser } from '../functions/is-browser';
3
3
  import { getCookieSync, setCookie } from './cookie';
4
4
  import { noSerializeWrapper } from './no-serialize-wrapper';
5
+ import { getStudioUserAttributes } from './studio-user-attributes';
5
6
  export const USER_ATTRIBUTES_COOKIE_NAME = 'builder.userAttributes';
6
7
  export function createUserAttributesService() {
7
8
  let canTrack = true;
8
9
  const subscribers = new Set();
10
+ const getCookieUserAttributes = () => {
11
+ const cookie = getCookieSync({
12
+ name: USER_ATTRIBUTES_COOKIE_NAME,
13
+ canTrack
14
+ });
15
+ return cookie ? JSON.parse(cookie) : {};
16
+ };
9
17
  return {
10
18
  setUserAttributes(newAttrs) {
11
19
  if (!isBrowser()) {
12
20
  return;
13
21
  }
14
22
  const userAttributes = {
15
- ...this.getUserAttributes(),
23
+ ...getCookieUserAttributes(),
16
24
  ...newAttrs
17
25
  };
18
26
  setCookie({
@@ -20,16 +28,22 @@ export function createUserAttributesService() {
20
28
  value: JSON.stringify(userAttributes),
21
29
  canTrack
22
30
  });
23
- subscribers.forEach(callback => callback(userAttributes));
31
+ // Kept out of the cookie above so previewing never persists into a real visitor's
32
+ // session, but re-applied here so the site's own attributes cannot clobber it.
33
+ const studioAttributes = getStudioUserAttributes();
34
+ subscribers.forEach(callback => callback({
35
+ ...userAttributes,
36
+ ...studioAttributes
37
+ }));
24
38
  },
25
39
  getUserAttributes() {
26
40
  if (!isBrowser()) {
27
41
  return {};
28
42
  }
29
- return JSON.parse(getCookieSync({
30
- name: USER_ATTRIBUTES_COOKIE_NAME,
31
- canTrack
32
- }) || '{}');
43
+ return {
44
+ ...getCookieUserAttributes(),
45
+ ...getStudioUserAttributes()
46
+ };
33
47
  },
34
48
  subscribeOnUserAttributesChange(callback, { fireImmediately } = {}) {
35
49
  subscribers.add(callback);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@builder.io/sdk-svelte",
3
3
  "description": "Builder.io SDK for Svelte",
4
- "version": "5.2.11",
4
+ "version": "5.2.13",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "lib",
@@ -87,7 +87,9 @@
87
87
  <source type="image/webp" srcset={webpSrcSet()} sizes={sizesToUse()} />
88
88
  {/if}<img
89
89
  style={stringifyStyles({
90
- objectPosition: backgroundPosition || "center",
90
+ objectPosition: `var(--builder-image-position, ${
91
+ backgroundPosition || "center"
92
+ })`,
91
93
  objectFit: backgroundSize || "cover",
92
94
  ...aspectRatioCss(),
93
95
  })}
@@ -5,6 +5,25 @@
5
5
 
6
6
  import type { Query, UserAttributes } from '../helpers';
7
7
  import { type PersonalizationContainerProps } from '../personalization-container.types';
8
+
9
+ /**
10
+ * Mirrors helpers/studio-user-attributes.ts; stringification rules out sharing the code.
11
+ * Anything referenced from module scope becomes an undefined global at runtime.
12
+ */
13
+ export function getStudioUserAttributes() {
14
+ const params = new URLSearchParams(window.location.search);
15
+ if (params.get('builder.preview') !== 'BUILDER_STUDIO') {
16
+ return {};
17
+ }
18
+ const prefix = 'builder.userAttributes.';
19
+ const attributes: Record<string, unknown> = {};
20
+ params.forEach(function (value, key) {
21
+ if (key.indexOf(prefix) === 0) {
22
+ attributes[key.slice(prefix.length)] = value === 'true' ? true : value === 'false' ? false : value;
23
+ }
24
+ });
25
+ return attributes;
26
+ }
8
27
  export function filterWithCustomTargeting(userAttributes: UserAttributes, query: Query[], startDate?: string, endDate?: string) {
9
28
  function isString(val: unknown): val is string {
10
29
  return typeof val === 'string';
@@ -110,6 +129,7 @@ export function updateVisibilityStylesScript(variants: PersonalizationContainerP
110
129
  if (locale) {
111
130
  attributes.locale = locale;
112
131
  }
132
+ Object.assign(attributes, (window as any).builderIoStudioUserAttributes());
113
133
  const winningVariantIndex = variants?.findIndex(function (variant) {
114
134
  return (window as any).filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);
115
135
  });
@@ -123,6 +143,7 @@ export function updateVisibilityStylesScript(variants: PersonalizationContainerP
123
143
  }
124
144
  }
125
145
  }
126
- export const PERSONALIZATION_SCRIPT = "function getPersonalizedVariant(variants, blockId, isHydrationTarget, locale) {\n if (!navigator.cookieEnabled) {\n return;\n }\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n const parentDiv = document.currentScript?.parentElement;\n const variantId = parentDiv?.getAttribute('data-variant-id');\n const isDefaultVariant = variantId === `${blockId}-default`;\n const isWinningVariant = winningVariantIndex !== -1 && variantId === `${blockId}-${winningVariantIndex}` || winningVariantIndex === -1 && isDefaultVariant;\n if (isWinningVariant && !isDefaultVariant) {\n parentDiv?.removeAttribute('hidden');\n parentDiv?.removeAttribute('aria-hidden');\n } else if (!isWinningVariant && isDefaultVariant) {\n parentDiv?.setAttribute('hidden', 'true');\n parentDiv?.setAttribute('aria-hidden', 'true');\n }\n if (isHydrationTarget) {\n if (!isWinningVariant) {\n const itsStyleEl = parentDiv?.previousElementSibling;\n if (itsStyleEl) {\n itsStyleEl.remove();\n }\n parentDiv?.remove();\n }\n const thisScript = document.currentScript;\n if (thisScript) {\n thisScript.remove();\n }\n }\n}";
146
+ export const STUDIO_USER_ATTRIBUTES_SCRIPT = "function getStudioUserAttributes() {\n const params = new URLSearchParams(window.location.search);\n if (params.get('builder.preview') !== 'BUILDER_STUDIO') {\n return {};\n }\n const prefix = 'builder.userAttributes.';\n const attributes = {};\n params.forEach(function (value, key) {\n if (key.indexOf(prefix) === 0) {\n attributes[key.slice(prefix.length)] = value === 'true' ? true : value === 'false' ? false : value;\n }\n });\n return attributes;\n}";
147
+ export const PERSONALIZATION_SCRIPT = "function getPersonalizedVariant(variants, blockId, isHydrationTarget, locale) {\n if (!navigator.cookieEnabled) {\n return;\n }\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n Object.assign(attributes, window.builderIoStudioUserAttributes());\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n const parentDiv = document.currentScript?.parentElement;\n const variantId = parentDiv?.getAttribute('data-variant-id');\n const isDefaultVariant = variantId === `${blockId}-default`;\n const isWinningVariant = winningVariantIndex !== -1 && variantId === `${blockId}-${winningVariantIndex}` || winningVariantIndex === -1 && isDefaultVariant;\n if (isWinningVariant && !isDefaultVariant) {\n parentDiv?.removeAttribute('hidden');\n parentDiv?.removeAttribute('aria-hidden');\n } else if (!isWinningVariant && isDefaultVariant) {\n parentDiv?.setAttribute('hidden', 'true');\n parentDiv?.setAttribute('aria-hidden', 'true');\n }\n if (isHydrationTarget) {\n if (!isWinningVariant) {\n const itsStyleEl = parentDiv?.previousElementSibling;\n if (itsStyleEl) {\n itsStyleEl.remove();\n }\n parentDiv?.remove();\n }\n const thisScript = document.currentScript;\n if (thisScript) {\n thisScript.remove();\n }\n }\n}";
127
148
  export const FILTER_WITH_CUSTOM_TARGETING_SCRIPT = "function filterWithCustomTargeting(userAttributes, query, startDate, endDate) {\n function isString(val) {\n return typeof val === 'string';\n }\n function isNumber(val) {\n return typeof val === 'number';\n }\n function objectMatchesQuery(userattr, query) {\n const result = (() => {\n const property = query.property;\n const operator = query.operator;\n let testValue = query.value;\n if (query && query.property === 'urlPath' && query.value && typeof query.value === 'string' && query.value !== '/' && query.value.endsWith('/')) {\n testValue = query.value.slice(0, -1);\n }\n if (!(property && operator)) {\n return true;\n }\n if (Array.isArray(testValue)) {\n if (operator === 'isNot') {\n return testValue.every(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n return !!testValue.find(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n const value = userattr[property];\n if (Array.isArray(value)) {\n return value.includes(testValue);\n }\n switch (operator) {\n case 'is':\n return value === testValue;\n case 'isNot':\n return value !== testValue;\n case 'contains':\n return (isString(value) || Array.isArray(value)) && value.includes(String(testValue));\n case 'startsWith':\n return isString(value) && value.startsWith(String(testValue));\n case 'endsWith':\n return isString(value) && value.endsWith(String(testValue));\n case 'greaterThan':\n return isNumber(value) && isNumber(testValue) && value > testValue;\n case 'lessThan':\n return isNumber(value) && isNumber(testValue) && value < testValue;\n case 'greaterThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value >= testValue;\n case 'lessThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value <= testValue;\n default:\n return false;\n }\n })();\n return result;\n }\n const item = {\n query,\n startDate,\n endDate\n };\n const now = userAttributes.date && new Date(userAttributes.date) || new Date();\n if (item.startDate && new Date(item.startDate) > now) {\n return false;\n } else if (item.endDate && new Date(item.endDate) < now) {\n return false;\n }\n if (!item.query || !item.query.length) {\n return true;\n }\n return item.query.every(filter => {\n return objectMatchesQuery(userAttributes, filter);\n });\n}";
128
- export const UPDATE_VISIBILITY_STYLES_SCRIPT = "function updateVisibilityStylesScript(variants, blockId, isHydrationTarget, locale) {\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const visibilityStylesEl = document.currentScript?.previousElementSibling;\n if (!visibilityStylesEl) {\n return;\n }\n if (isHydrationTarget) {\n visibilityStylesEl.remove();\n const currentScript = document.currentScript;\n if (currentScript) {\n currentScript.remove();\n }\n } else {\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n if (winningVariantIndex !== -1) {\n let newStyleStr = variants?.map((_, index) => {\n if (index === winningVariantIndex) return '';\n return `div[data-variant-id=\"${blockId}-${index}\"] { display: none !important; } `;\n }).join('') || '';\n newStyleStr += `div[data-variant-id=\"${blockId}-default\"] { display: none !important; } `;\n visibilityStylesEl.innerHTML = newStyleStr;\n }\n }\n}";
149
+ export const UPDATE_VISIBILITY_STYLES_SCRIPT = "function updateVisibilityStylesScript(variants, blockId, isHydrationTarget, locale) {\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const visibilityStylesEl = document.currentScript?.previousElementSibling;\n if (!visibilityStylesEl) {\n return;\n }\n if (isHydrationTarget) {\n visibilityStylesEl.remove();\n const currentScript = document.currentScript;\n if (currentScript) {\n currentScript.remove();\n }\n } else {\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n Object.assign(attributes, window.builderIoStudioUserAttributes());\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n if (winningVariantIndex !== -1) {\n let newStyleStr = variants?.map((_, index) => {\n if (index === winningVariantIndex) return '';\n return `div[data-variant-id=\"${blockId}-${index}\"] { display: none !important; } `;\n }).join('') || '';\n newStyleStr += `div[data-variant-id=\"${blockId}-default\"] { display: none !important; } `;\n visibilityStylesEl.innerHTML = newStyleStr;\n }\n }\n}";
@@ -4,12 +4,13 @@ import { isEditing } from '../../functions/is-editing';
4
4
  import type { BuilderBlock } from '../../types/builder-block';
5
5
  import type { BuilderContent } from '../../types/builder-content';
6
6
  import type { Target } from '../../types/targets';
7
- import { FILTER_WITH_CUSTOM_TARGETING_SCRIPT, PERSONALIZATION_SCRIPT, UPDATE_VISIBILITY_STYLES_SCRIPT } from './helpers/inlined-fns';
7
+ import { FILTER_WITH_CUSTOM_TARGETING_SCRIPT, PERSONALIZATION_SCRIPT, STUDIO_USER_ATTRIBUTES_SCRIPT, UPDATE_VISIBILITY_STYLES_SCRIPT } from './helpers/inlined-fns';
8
8
  import type { PersonalizationContainerProps } from './personalization-container.types';
9
9
  export const DEFAULT_INDEX = 'default';
10
10
  const FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME = 'filterWithCustomTargeting';
11
11
  const BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME = 'builderIoPersonalization';
12
12
  const UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME = 'updateVisibilityStylesScript';
13
+ const STUDIO_USER_ATTRIBUTES_SCRIPT_FN_NAME = 'builderIoStudioUserAttributes';
13
14
  const PERSONALIZATION_CONTAINER_COMPONENT_NAME = 'PersonalizationContainer';
14
15
  export type UserAttributes = {
15
16
  date?: string | Date;
@@ -126,6 +127,9 @@ export const hasPersonalizationContainer = (content: BuilderContent | null | und
126
127
  export const getInitPersonalizationVariantsFnsScriptString = () => {
127
128
  return `
128
129
  (function() {
130
+ if (!window.${STUDIO_USER_ATTRIBUTES_SCRIPT_FN_NAME}) {
131
+ window.${STUDIO_USER_ATTRIBUTES_SCRIPT_FN_NAME} = ${STUDIO_USER_ATTRIBUTES_SCRIPT};
132
+ }
129
133
  if (!window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME}) {
130
134
  window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME} = ${FILTER_WITH_CUSTOM_TARGETING_SCRIPT};
131
135
  }
@@ -13,7 +13,10 @@
13
13
  import { TARGET } from "../../../constants/target";
14
14
  import type { BuilderContextInterface } from "../../../context/types";
15
15
  import { camelToKebabCase } from "../../../functions/camel-to-kebab-case";
16
- import { createCssClass } from "../../../helpers/css";
16
+ import {
17
+ createCssClass,
18
+ getResponsiveStylesWithImagePosition,
19
+ } from "../../../helpers/css";
17
20
  import { checkIsDefined } from "../../../helpers/nullable";
18
21
  import type { BuilderBlock } from "../../../types/builder-block";
19
22
  import InlinedStyles from "../../inlined-styles.svelte";
@@ -34,7 +37,10 @@
34
37
  };
35
38
  $: css = () => {
36
39
  const processedBlock = block;
37
- const styles = processedBlock.responsiveStyles;
40
+ const styles = getResponsiveStylesWithImagePosition(
41
+ processedBlock.responsiveStyles,
42
+ processedBlock.component?.name === "Image"
43
+ );
38
44
  const content = context.content;
39
45
  const sizesWithUpdatedBreakpoints = getSizesForBreakpoints(
40
46
  content?.meta?.breakpoints || {}
@@ -13,7 +13,10 @@
13
13
  import { TARGET } from "../../../constants/target";
14
14
  import { camelToKebabCase } from "../../../functions/camel-to-kebab-case";
15
15
  import { getProcessedBlock } from "../../../functions/get-processed-block";
16
- import { createCssClass } from "../../../helpers/css";
16
+ import {
17
+ createCssClass,
18
+ getResponsiveStylesWithImagePosition,
19
+ } from "../../../helpers/css";
17
20
  import { findBlockById } from "../../../helpers/find-block";
18
21
  import { checkIsDefined } from "../../../helpers/nullable";
19
22
  import type {
@@ -54,7 +57,10 @@
54
57
  return true;
55
58
  };
56
59
  $: css = () => {
57
- const styles = processedBlock?.()?.responsiveStyles;
60
+ const styles = getResponsiveStylesWithImagePosition(
61
+ processedBlock?.()?.responsiveStyles,
62
+ processedBlock?.()?.component?.name === "Image"
63
+ );
58
64
  const content = contextProvider.content;
59
65
  const sizesWithUpdatedBreakpoints = getSizesForBreakpoints(
60
66
  content?.meta?.breakpoints || {}
@@ -1 +1 @@
1
- export const SDK_VERSION = "5.2.11"
1
+ export const SDK_VERSION = "5.2.13"
@@ -1,5 +1,20 @@
1
1
  import { camelToKebabCase } from '../functions/camel-to-kebab-case';
2
+ import type { BuilderBlock } from '../types/builder-block';
2
3
  import { checkIsDefined } from './nullable';
4
+ const withBuilderImagePosition = (style: Partial<CSSStyleDeclaration> | undefined, fallback?: string): Partial<CSSStyleDeclaration> | undefined => {
5
+ const imagePosition = style?.objectPosition || fallback;
6
+ return imagePosition ? ({
7
+ ...style,
8
+ '--builder-image-position': imagePosition
9
+ } as Partial<CSSStyleDeclaration>) : style;
10
+ };
11
+ export const getResponsiveStylesWithImagePosition = (styles: BuilderBlock['responsiveStyles'], isBuilderImage: boolean): BuilderBlock['responsiveStyles'] => isBuilderImage ? {
12
+ ...styles,
13
+ large: withBuilderImagePosition(styles?.large, 'initial'),
14
+ medium: withBuilderImagePosition(styles?.medium),
15
+ small: withBuilderImagePosition(styles?.small),
16
+ xsmall: withBuilderImagePosition(styles?.xsmall)
17
+ } : styles;
3
18
  export const convertStyleMapToCSSArray = (style: Partial<CSSStyleDeclaration>): string[] => {
4
19
  const cssProps = Object.entries(style).map(([key, value]) => {
5
20
  if (typeof value === 'string') {