@brandlab-digital/site-next 3.3.0 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # `@brandlab-digital/site-next`
2
+
3
+ Proprietary software © Brandlab Digital Marketing. All rights reserved. Published for Brandlab client sites.
@@ -1 +1 @@
1
- {"version":3,"file":"google-tag-plan.d.ts","sourceRoot":"","sources":["../src/google-tag-plan.ts"],"names":[],"mappings":"AAEA,4DAA4D;AAC5D,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,mCAAmC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACnD;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/B,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAA;CACtC;AAiBD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,uBAAuB,EAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAC1B,qBAAqB,GAAG,IAAI,CA+C9B"}
1
+ {"version":3,"file":"google-tag-plan.d.ts","sourceRoot":"","sources":["../src/google-tag-plan.ts"],"names":[],"mappings":"AAGA,4DAA4D;AAC5D,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,mCAAmC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACnD;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/B,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAA;CACtC;AAiBD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,uBAAuB,EAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAC1B,qBAAqB,GAAG,IAAI,CA6C9B"}
@@ -1,4 +1,5 @@
1
1
  import { getGoogleAdsWebsiteCallSendTo } from '@brandlab-digital/site-core/analytics';
2
+ import { websiteCallCallbackSource } from './website-call-callback.js';
2
3
  /**
3
4
  * Where Google's EU user consent policy needs a person's own consent for
4
5
  * ad_user_data: the EEA, the United Kingdom and Switzerland. A disclosed
@@ -33,9 +34,7 @@ export function createBrandlabGoogleTagPlan(config, phoneNumber) {
33
34
  : null;
34
35
  const configCommands = destinations.map((destination) => `window.gtag('config', ${safeJson(destination)});`);
35
36
  if (websiteCallDestination && displayPhone) {
36
- configCommands.push(`window.gtag('config', ${safeJson(websiteCallDestination)}, ${safeJson({
37
- phone_conversion_number: displayPhone,
38
- })});`);
37
+ configCommands.push(`window.gtag('config', ${safeJson(websiteCallDestination)}, {"phone_conversion_number":${safeJson(displayPhone)},"phone_conversion_callback":${websiteCallCallbackSource(displayPhone)}});`);
39
38
  }
40
39
  return {
41
40
  loaderId,
@@ -1 +1 @@
1
- {"version":3,"file":"google-tag-plan.js","sourceRoot":"","sources":["../src/google-tag-plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,uCAAuC,CAAA;AAuBrF;;;;GAIG;AACH,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7C,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;IAC1G,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;IAClF,iFAAiF;IACjF,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;CAChC,CAAC,CAAA;AAEX,MAAM,0BAA0B,GAAG,qBAAqB,CAAA;AACxD,MAAM,gCAAgC,GAAG,oBAAoB,CAAA;AAE7D,MAAM,UAAU,2BAA2B,CACzC,MAA+B,EAC/B,WAA2B;IAE3B,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAChC,MAAM,aAAa,GAAG,kBAAkB,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;IAC9D,MAAM,qBAAqB,GAAG,0BAA0B,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAA;IACtF,MAAM,QAAQ,GAAG,aAAa,IAAI,qBAAqB,CAAA;IACvD,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IAE1B,MAAM,YAAY,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC;YAC/B,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1D,CAAC,CAAC,CAAA;IACH,MAAM,YAAY,GAAG,0BAA0B,CAAC,WAAW,CAAC,CAAA;IAC5D,MAAM,sBAAsB,GAAG,YAAY;QACzC,CAAC,CAAC,6BAA6B,CAAC;YAC5B,YAAY,EAAE,qBAAqB;YACnC,eAAe,EAAE,MAAM,CAAC,mCAAmC;SAC5D,CAAC,IAAI,IAAI;QACZ,CAAC,CAAC,IAAI,CAAA;IACR,MAAM,cAAc,GAAG,YAAY,CAAC,GAAG,CACrC,CAAC,WAAW,EAAE,EAAE,CAAC,yBAAyB,QAAQ,CAAC,WAAW,CAAC,IAAI,CACpE,CAAA;IACD,IAAI,sBAAsB,IAAI,YAAY,EAAE,CAAC;QAC3C,cAAc,CAAC,IAAI,CACjB,yBAAyB,QAAQ,CAAC,sBAAsB,CAAC,KAAK,QAAQ,CAAC;YACrE,uBAAuB,EAAE,YAAY;SACtC,CAAC,IAAI,CACP,CAAA;IACH,CAAC;IAED,OAAO;QACL,QAAQ;QACR,MAAM,EAAE,+CAA+C,kBAAkB,CAAC,QAAQ,CAAC,EAAE;QACrF,YAAY,EAAE;YACZ,4CAA4C;YAC5C,6EAA6E;YAC7E,gGAAgG;YAChG,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS;gBACjC,CAAC,CAAC,CAAC,qCAAqC,QAAQ,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC,IAAI,CAAC;gBACjJ,CAAC,CAAC,EAAE,CAAC;YACP,oDAAoD,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,kCAAkC;YAC5I,sCAAsC;YACtC,gCAAgC;YAChC,GAAG,cAAc;SAClB,CAAC,IAAI,CAAC,IAAI,CAAC;QACZ,YAAY;QACZ,sBAAsB;KACvB,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAgC;IAC1D,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,CAAA;IAChC,OAAO,UAAU,IAAI,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC;QAC9D,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE;QAC1B,CAAC,CAAC,SAAS,CAAA;AACf,CAAC;AAED,SAAS,0BAA0B,CAAC,KAAgC;IAClE,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,CAAA;IAChC,IAAI,CAAC,UAAU,IAAI,CAAC,gCAAgC,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,OAAO,SAAS,CAAA;IACvF,OAAO,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,UAAU,EAAE,CAAA;AACvE,CAAC;AAED,SAAS,0BAA0B,CAAC,KAAgC;IAClE,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,CAAA;IAChC,MAAM,MAAM,GAAG,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE,CAAA;IACnD,OAAO,UAAU;QACf,UAAU,CAAC,MAAM,IAAI,EAAE;QACvB,MAAM,CAAC,MAAM,IAAI,CAAC;QAClB,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC;QAClC,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,SAAS,CAAA;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;SACzB,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC;SACxB,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC;SACxB,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC;SACxB,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC;SAC7B,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;AAClC,CAAC","sourcesContent":["import { getGoogleAdsWebsiteCallSendTo } from '@brandlab-digital/site-core/analytics'\n\n/** Supplied by `defineBrandlabAnalytics(...).googleTag`. */\nexport interface BrandlabGoogleTagConfig {\n enabled: boolean\n measurementId?: string | null\n googleAdsConversionId?: string | null\n googleAdsWebsiteCallConversionLabel?: string | null\n /**\n * `granted` only for a disclosed Enhanced Conversions contract. Everything\n * else keeps the ad_user_data default denied. ad_personalization stays denied.\n */\n adUserData?: 'granted' | 'denied'\n}\n\nexport interface BrandlabGoogleTagPlan {\n loaderId: string\n source: string\n inlineScript: string\n destinations: readonly string[]\n websiteCallDestination: string | null\n}\n\n/**\n * Where Google's EU user consent policy needs a person's own consent for\n * ad_user_data: the EEA, the United Kingdom and Switzerland. A disclosed\n * contract's granted default never applies there.\n */\nconst CONSENT_REQUIRED_REGIONS = Object.freeze([\n 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU',\n 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'IS', 'LI', 'NO', 'GB', 'CH',\n // EU outermost regions and Åland, which Google geolocates under their own codes.\n 'GF', 'GP', 'MQ', 'RE', 'YT', 'MF', 'AX',\n] as const)\n\nconst GA4_MEASUREMENT_ID_PATTERN = /^G-[A-Z0-9]{4,20}$/i\nconst GOOGLE_ADS_CONVERSION_ID_PATTERN = /^(?:AW-)?\\d{6,20}$/\n\nexport function createBrandlabGoogleTagPlan(\n config: BrandlabGoogleTagConfig,\n phoneNumber?: string | null,\n): BrandlabGoogleTagPlan | null {\n if (!config.enabled) return null\n const measurementId = validMeasurementId(config.measurementId)\n const googleAdsConversionId = validGoogleAdsConversionId(config.googleAdsConversionId)\n const loaderId = measurementId ?? googleAdsConversionId\n if (!loaderId) return null\n\n const destinations = [...new Set([\n ...(measurementId ? [measurementId] : []),\n ...(googleAdsConversionId ? [googleAdsConversionId] : []),\n ])]\n const displayPhone = safeDisplayedBusinessPhone(phoneNumber)\n const websiteCallDestination = displayPhone\n ? getGoogleAdsWebsiteCallSendTo({\n conversionId: googleAdsConversionId,\n conversionLabel: config.googleAdsWebsiteCallConversionLabel,\n }) ?? null\n : null\n const configCommands = destinations.map(\n (destination) => `window.gtag('config', ${safeJson(destination)});`,\n )\n if (websiteCallDestination && displayPhone) {\n configCommands.push(\n `window.gtag('config', ${safeJson(websiteCallDestination)}, ${safeJson({\n phone_conversion_number: displayPhone,\n })});`,\n )\n }\n\n return {\n loaderId,\n source: `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(loaderId)}`,\n inlineScript: [\n 'window.dataLayer = window.dataLayer || [];',\n 'window.gtag = window.gtag || function(){window.dataLayer.push(arguments);};',\n // A region-specific default takes precedence over the global one for visitors in those regions.\n ...(config.adUserData === 'granted'\n ? [`window.gtag('consent', 'default', ${safeJson({ ad_user_data: 'denied', ad_personalization: 'denied', region: CONSENT_REQUIRED_REGIONS })});`]\n : []),\n `window.gtag('consent', 'default', {ad_user_data:'${config.adUserData === 'granted' ? 'granted' : 'denied'}',ad_personalization:'denied'});`,\n \"window.gtag('set', 'user_data', {});\",\n \"window.gtag('js', new Date());\",\n ...configCommands,\n ].join('\\n'),\n destinations,\n websiteCallDestination,\n }\n}\n\nfunction validMeasurementId(value: string | null | undefined) {\n const normalized = value?.trim()\n return normalized && GA4_MEASUREMENT_ID_PATTERN.test(normalized)\n ? normalized.toUpperCase()\n : undefined\n}\n\nfunction validGoogleAdsConversionId(value: string | null | undefined) {\n const normalized = value?.trim()\n if (!normalized || !GOOGLE_ADS_CONVERSION_ID_PATTERN.test(normalized)) return undefined\n return normalized.startsWith('AW-') ? normalized : `AW-${normalized}`\n}\n\nfunction safeDisplayedBusinessPhone(value: string | null | undefined) {\n const normalized = value?.trim()\n const digits = normalized?.replace(/\\D/g, '') ?? ''\n return normalized &&\n normalized.length <= 32 &&\n digits.length >= 6 &&\n /^[0-9+().\\s-]+$/.test(normalized)\n ? normalized\n : undefined\n}\n\nfunction safeJson(value: unknown) {\n return JSON.stringify(value)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029')\n}\n"]}
1
+ {"version":3,"file":"google-tag-plan.js","sourceRoot":"","sources":["../src/google-tag-plan.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGoogleAdsWebsiteCallSendTo } from '@brandlab-digital/site-core/analytics'\nimport { websiteCallCallbackSource } from './website-call-callback.js'\n\n/** Supplied by `defineBrandlabAnalytics(...).googleTag`. */\nexport interface BrandlabGoogleTagConfig {\n enabled: boolean\n measurementId?: string | null\n googleAdsConversionId?: string | null\n googleAdsWebsiteCallConversionLabel?: string | null\n /**\n * `granted` only for a disclosed Enhanced Conversions contract. Everything\n * else keeps the ad_user_data default denied. ad_personalization stays denied.\n */\n adUserData?: 'granted' | 'denied'\n}\n\nexport interface BrandlabGoogleTagPlan {\n loaderId: string\n source: string\n inlineScript: string\n destinations: readonly string[]\n websiteCallDestination: string | null\n}\n\n/**\n * Where Google's EU user consent policy needs a person's own consent for\n * ad_user_data: the EEA, the United Kingdom and Switzerland. A disclosed\n * contract's granted default never applies there.\n */\nconst CONSENT_REQUIRED_REGIONS = Object.freeze([\n 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU',\n 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'IS', 'LI', 'NO', 'GB', 'CH',\n // EU outermost regions and Åland, which Google geolocates under their own codes.\n 'GF', 'GP', 'MQ', 'RE', 'YT', 'MF', 'AX',\n] as const)\n\nconst GA4_MEASUREMENT_ID_PATTERN = /^G-[A-Z0-9]{4,20}$/i\nconst GOOGLE_ADS_CONVERSION_ID_PATTERN = /^(?:AW-)?\\d{6,20}$/\n\nexport function createBrandlabGoogleTagPlan(\n config: BrandlabGoogleTagConfig,\n phoneNumber?: string | null,\n): BrandlabGoogleTagPlan | null {\n if (!config.enabled) return null\n const measurementId = validMeasurementId(config.measurementId)\n const googleAdsConversionId = validGoogleAdsConversionId(config.googleAdsConversionId)\n const loaderId = measurementId ?? googleAdsConversionId\n if (!loaderId) return null\n\n const destinations = [...new Set([\n ...(measurementId ? [measurementId] : []),\n ...(googleAdsConversionId ? [googleAdsConversionId] : []),\n ])]\n const displayPhone = safeDisplayedBusinessPhone(phoneNumber)\n const websiteCallDestination = displayPhone\n ? getGoogleAdsWebsiteCallSendTo({\n conversionId: googleAdsConversionId,\n conversionLabel: config.googleAdsWebsiteCallConversionLabel,\n }) ?? null\n : null\n const configCommands = destinations.map(\n (destination) => `window.gtag('config', ${safeJson(destination)});`,\n )\n if (websiteCallDestination && displayPhone) {\n configCommands.push(\n `window.gtag('config', ${safeJson(websiteCallDestination)}, {\"phone_conversion_number\":${safeJson(displayPhone)},\"phone_conversion_callback\":${websiteCallCallbackSource(displayPhone)}});`,\n )\n }\n\n return {\n loaderId,\n source: `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(loaderId)}`,\n inlineScript: [\n 'window.dataLayer = window.dataLayer || [];',\n 'window.gtag = window.gtag || function(){window.dataLayer.push(arguments);};',\n // A region-specific default takes precedence over the global one for visitors in those regions.\n ...(config.adUserData === 'granted'\n ? [`window.gtag('consent', 'default', ${safeJson({ ad_user_data: 'denied', ad_personalization: 'denied', region: CONSENT_REQUIRED_REGIONS })});`]\n : []),\n `window.gtag('consent', 'default', {ad_user_data:'${config.adUserData === 'granted' ? 'granted' : 'denied'}',ad_personalization:'denied'});`,\n \"window.gtag('set', 'user_data', {});\",\n \"window.gtag('js', new Date());\",\n ...configCommands,\n ].join('\\n'),\n destinations,\n websiteCallDestination,\n }\n}\n\nfunction validMeasurementId(value: string | null | undefined) {\n const normalized = value?.trim()\n return normalized && GA4_MEASUREMENT_ID_PATTERN.test(normalized)\n ? normalized.toUpperCase()\n : undefined\n}\n\nfunction validGoogleAdsConversionId(value: string | null | undefined) {\n const normalized = value?.trim()\n if (!normalized || !GOOGLE_ADS_CONVERSION_ID_PATTERN.test(normalized)) return undefined\n return normalized.startsWith('AW-') ? normalized : `AW-${normalized}`\n}\n\nfunction safeDisplayedBusinessPhone(value: string | null | undefined) {\n const normalized = value?.trim()\n const digits = normalized?.replace(/\\D/g, '') ?? ''\n return normalized &&\n normalized.length <= 32 &&\n digits.length >= 6 &&\n /^[0-9+().\\s-]+$/.test(normalized)\n ? normalized\n : undefined\n}\n\nfunction safeJson(value: unknown) {\n return JSON.stringify(value)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029')\n}\n"]}
@@ -0,0 +1,5 @@
1
+ /** Browser-only callback source for Google's website-call configuration.
2
+ * Keep it self-contained: it is embedded in the existing shared tag, not a second loader.
3
+ */
4
+ export declare function websiteCallCallbackSource(displayPhone: string): string;
5
+ //# sourceMappingURL=website-call-callback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"website-call-callback.d.ts","sourceRoot":"","sources":["../src/website-call-callback.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAsDtE"}
@@ -0,0 +1,59 @@
1
+ /** Browser-only callback source for Google's website-call configuration.
2
+ * Keep it self-contained: it is embedded in the existing shared tag, not a second loader.
3
+ */
4
+ export function websiteCallCallbackSource(displayPhone) {
5
+ return `(function(original) {
6
+ var formatted = original;
7
+ var mobile = '';
8
+ var previous = original;
9
+ var observer;
10
+ function digits(value) { return value.replace(/[^0-9]/g, ''); }
11
+ function sameNumber(value) {
12
+ var number = digits(value);
13
+ var expected = digits(original);
14
+ if (number === expected || (mobile && number === digits(mobile))) return true;
15
+ // Australian sites commonly render national numbers but use +61 telephone links.
16
+ return expected.length === 10 && expected[0] === '0' && number === '61' + expected.slice(1);
17
+ }
18
+ function observe() {
19
+ if (observer && document.body) observer.observe(document.body, {
20
+ subtree: true, childList: true, characterData: true,
21
+ attributes: true, attributeFilter: ['href']
22
+ });
23
+ }
24
+ function replace() {
25
+ if (typeof document === 'undefined' || !document.body || !mobile) return;
26
+ if (observer) observer.disconnect();
27
+ document.body.querySelectorAll('a[href^="tel:"]').forEach(function(link) {
28
+ if (link.closest('[contenteditable]')) return;
29
+ if (sameNumber(link.getAttribute('href').slice(4))) link.setAttribute('href', 'tel:' + mobile);
30
+ });
31
+ var walker = document.createTreeWalker(document.body, 4);
32
+ var node;
33
+ while ((node = walker.nextNode())) {
34
+ if (node.parentElement && node.parentElement.closest('script,style,noscript,textarea,select,pre,code,[contenteditable]')) continue;
35
+ var value = node.nodeValue;
36
+ var next = value.split(original).join(formatted);
37
+ if (previous !== original) next = next.split(previous).join(formatted);
38
+ if (next !== value) node.nodeValue = next;
39
+ }
40
+ observe();
41
+ }
42
+ return function(nextFormatted, nextMobile) {
43
+ if (typeof nextFormatted !== 'string' || typeof nextMobile !== 'string') return;
44
+ if (!/^[0-9+().\\s-]{6,32}$/.test(nextFormatted) || !/^\\+?[0-9]{6,15}$/.test(nextMobile)) return;
45
+ if (digits(nextFormatted).length < 6) return;
46
+ previous = formatted;
47
+ // Update links already holding the prior forwarding number before replacing the state.
48
+ if (mobile && document.body) document.body.querySelectorAll('a[href^="tel:"]').forEach(function(link) {
49
+ if (!link.closest('[contenteditable]') && digits(link.getAttribute('href').slice(4)) === digits(mobile)) link.setAttribute('href', 'tel:' + nextMobile);
50
+ });
51
+ formatted = nextFormatted;
52
+ mobile = nextMobile;
53
+ if (!observer && typeof MutationObserver !== 'undefined') observer = new MutationObserver(replace);
54
+ replace();
55
+ if (!document.body) document.addEventListener('DOMContentLoaded', replace, { once: true });
56
+ };
57
+ })(${JSON.stringify(displayPhone)})`;
58
+ }
59
+ //# sourceMappingURL=website-call-callback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"website-call-callback.js","sourceRoot":"","sources":["../src/website-call-callback.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,YAAoB;IAC5D,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoDF,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,GAAG,CAAA;AACtC,CAAC","sourcesContent":["/** Browser-only callback source for Google's website-call configuration.\n * Keep it self-contained: it is embedded in the existing shared tag, not a second loader.\n */\nexport function websiteCallCallbackSource(displayPhone: string): string {\n return `(function(original) {\n var formatted = original;\n var mobile = '';\n var previous = original;\n var observer;\n function digits(value) { return value.replace(/[^0-9]/g, ''); }\n function sameNumber(value) {\n var number = digits(value);\n var expected = digits(original);\n if (number === expected || (mobile && number === digits(mobile))) return true;\n // Australian sites commonly render national numbers but use +61 telephone links.\n return expected.length === 10 && expected[0] === '0' && number === '61' + expected.slice(1);\n }\n function observe() {\n if (observer && document.body) observer.observe(document.body, {\n subtree: true, childList: true, characterData: true,\n attributes: true, attributeFilter: ['href']\n });\n }\n function replace() {\n if (typeof document === 'undefined' || !document.body || !mobile) return;\n if (observer) observer.disconnect();\n document.body.querySelectorAll('a[href^=\"tel:\"]').forEach(function(link) {\n if (link.closest('[contenteditable]')) return;\n if (sameNumber(link.getAttribute('href').slice(4))) link.setAttribute('href', 'tel:' + mobile);\n });\n var walker = document.createTreeWalker(document.body, 4);\n var node;\n while ((node = walker.nextNode())) {\n if (node.parentElement && node.parentElement.closest('script,style,noscript,textarea,select,pre,code,[contenteditable]')) continue;\n var value = node.nodeValue;\n var next = value.split(original).join(formatted);\n if (previous !== original) next = next.split(previous).join(formatted);\n if (next !== value) node.nodeValue = next;\n }\n observe();\n }\n return function(nextFormatted, nextMobile) {\n if (typeof nextFormatted !== 'string' || typeof nextMobile !== 'string') return;\n if (!/^[0-9+().\\\\s-]{6,32}$/.test(nextFormatted) || !/^\\\\+?[0-9]{6,15}$/.test(nextMobile)) return;\n if (digits(nextFormatted).length < 6) return;\n previous = formatted;\n // Update links already holding the prior forwarding number before replacing the state.\n if (mobile && document.body) document.body.querySelectorAll('a[href^=\"tel:\"]').forEach(function(link) {\n if (!link.closest('[contenteditable]') && digits(link.getAttribute('href').slice(4)) === digits(mobile)) link.setAttribute('href', 'tel:' + nextMobile);\n });\n formatted = nextFormatted;\n mobile = nextMobile;\n if (!observer && typeof MutationObserver !== 'undefined') observer = new MutationObserver(replace);\n replace();\n if (!document.body) document.addEventListener('DOMContentLoaded', replace, { once: true });\n };\n })(${JSON.stringify(displayPhone)})`\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@brandlab-digital/site-next",
3
- "version": "3.3.0",
3
+ "version": "3.3.2",
4
+ "license": "UNLICENSED",
4
5
  "repository": {
5
6
  "type": "git",
6
7
  "url": "git+https://github.com/RomieDandan/brandlab.git",
@@ -14,17 +15,20 @@
14
15
  "files": [
15
16
  "dist"
16
17
  ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
17
21
  "scripts": {
18
22
  "build": "rm -rf dist && tsc -p tsconfig.json",
19
23
  "test": "vitest run",
20
24
  "typecheck": "tsc --noEmit -p tsconfig.json"
21
25
  },
22
26
  "dependencies": {
23
- "@brandlab-digital/site-core": "3.3.0",
27
+ "@brandlab-digital/site-core": "3.3.2",
24
28
  "browser-image-compression": "2.0.2",
25
29
  "pdf-lib": "1.17.1",
26
30
  "server-only": "0.0.1",
27
- "sharp": "0.35.4"
31
+ "sharp": "0.35.5"
28
32
  },
29
33
  "peerDependencies": {
30
34
  "next": ">=16",