From d4525eb1dbc8ba0e6e7b9f5715226e83c6bf3dda Mon Sep 17 00:00:00 2001 From: "promptless[bot]" Date: Mon, 3 Aug 2026 12:32:47 +0000 Subject: [PATCH 1/2] Add Focus Item scripts developer page Document the split Focus Item embed scripts from mautic/mautic PR #16926: the /focus/{id}/display.js and /focus/{id}/tracking.js endpoints (legacy /focus/{id}.js preserved), the RUNTIME/DISPLAY/TRACKING scope model, and the client-side runtime globals (MauticFocusItems, MauticFocusTrackingQueue, MauticFocusUseMauticTrackingConsent), the enableMauticFocusTracking{id}() callback, and the mautic:tracking-enabled event. Add a matching event bullet and toctree entry. --- docs/index.rst | 1 + docs/mauticjs_api/focus_scripts.rst | 94 +++++++++++++++++++++++++++ docs/mauticjs_api/tracking_script.rst | 1 + 3 files changed, 96 insertions(+) create mode 100644 docs/mauticjs_api/focus_scripts.rst diff --git a/docs/index.rst b/docs/index.rst index df982275..c14c5268 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -264,6 +264,7 @@ There are several ways to support Mautic other than contributing with code. :hidden: mauticjs_api/tracking_script + mauticjs_api/focus_scripts .. toctree:: :maxdepth: 2 diff --git a/docs/mauticjs_api/focus_scripts.rst b/docs/mauticjs_api/focus_scripts.rst new file mode 100644 index 00000000..15644b95 --- /dev/null +++ b/docs/mauticjs_api/focus_scripts.rst @@ -0,0 +1,94 @@ +Focus Item scripts +################## + +Focus Items can be embedded on external sites. Mautic splits the embed JavaScript into consent-aware parts so a site can render a Focus Item before consent and add tracking only after consent. + +.. note:: + + For guidance on creating and configuring Focus Items in the Mautic UI, see the :xref:`Mautic User Documentation`. This page documents only the developer-facing split scripts. + +The Focus split scripts follow the same scope model described in :ref:`Script scopes and split scripts`. The scopes below apply that same model to Focus Items. + +.. vale off + +Split Focus scripts and scope model +*********************************** + +.. vale on + +Focus embedding is split into three scopes: + +* ``RUNTIME`` - the anonymous bootstrap that the other scopes depend on. It performs no tracking. +* ``DISPLAY`` - loads and renders the Focus Item. It adds no tracking pixel, no Contact tokens, and no ``mauticform[focusId]`` marker. +* ``TRACKING`` - resolves the tracked Contact, generates the trackable redirect, and installs the view pixel and the Form focus-id marker. + +These are Focus-specific scope labels. They mirror the ``BuildJsScope`` model documented on the tracking script page, but they aren't the same ``enum``. + +.. vale off + +Generated scripts and endpoints +******************************* + +.. vale on + +Mautic serves the scopes through separate endpoints so a site can load only what it needs before consent, then add tracking later. Each endpoint path resolves against your Mautic instance's base URL. + +.. list-table:: + :header-rows: 1 + + * - Endpoint + - Included scopes + - Purpose + * - ``/focus/{id}/display.js`` + - ``RUNTIME`` and ``DISPLAY`` + - Renders the Focus Item with no tracking. + * - ``/focus/{id}/tracking.js`` + - ``TRACKING`` + - Adds the tracking layer for a Focus Item that's already displayed. + * - ``/focus/{id}.js`` + - ``RUNTIME``, ``DISPLAY``, and ``TRACKING`` + - The legacy aggregate script. Unchanged and backward compatible; still returns the full combined script. + +All three endpoints send the ``Cache-Control: private, no-store`` response header. + +.. vale off + +Client-side runtime globals +*************************** + +.. vale on + +The split scripts expose a small set of client-side global variables, a callback, and an event so a site can control when tracking activates: + +* ``window.MauticFocusItems`` - a registry keyed by Focus Item id. Each entry is the runtime for that Focus Item and exposes methods to load and activate the tracking layer. The display script uses it to lazily inject and activate tracking. +* ``window.MauticFocusTrackingQueue`` - a queue that bridges consent. Activation requests made before the tracking layer is ready are queued and flushed once it loads. +* ``window.MauticFocusUseMauticTrackingConsent`` - when set to ``true``, the Focus display script auto-activates tracking as soon as Mautic website tracking becomes enabled, instead of waiting for a manual or CMP activation. The copied website-tracking snippet sets it to ``true`` when an administrator turns on the 'Use Mautic consent for Focus tracking' option. +* ``enableMauticFocusTracking{id}()`` - the per-Focus-Item activation callback a site wires into its consent-management platform (CMP). Calling it activates the tracking layer for that Focus Item. +* ``mautic:tracking-enabled`` - a document event the tracking layer dispatches once tracking initializes. The Focus runtime listens for it to bridge consent, mirroring how browser code can listen for the tracking layer's readiness signals described under :ref:`Client-side runtime globals`. + +.. vale off + +Activating tracking after consent +********************************* + +.. vale on + +Wire the per-Focus-Item callback into your CMP so it runs on the visitor's consent event. Use native JavaScript, since ``jQuery`` and other libraries aren't guaranteed to be available on third party sites: + +.. code-block:: js + + // called by your consent-management platform once the visitor consents + window.enableMauticFocusTracking123(); + +If the tracking layer hasn't loaded yet, the activation request is queued in ``window.MauticFocusTrackingQueue`` and flushed once the layer loads. + +Alternatively, set ``window.MauticFocusUseMauticTrackingConsent`` to ``true`` to let the Focus display script auto-activate tracking as soon as Mautic website tracking becomes enabled. On that path the Focus runtime activates in response to the ``mautic:tracking-enabled`` document event rather than waiting for a manual or CMP call. + +.. vale off + +Dynamic Web Content injection behavior +************************************** + +.. vale on + +The Dynamic Web Content build-JS subscriber now parses Dynamic Web Content through the DOM rather than treating it as opaque markup. It injects only same-origin Focus scripts. It always allows ``/focus/{id}/display.js`` so a Focus Item renders regardless of consent state. It injects the legacy ``/focus/{id}.js`` only when ``MauticJS.trackingEnabled`` is ``true``, so the combined tracking-capable script loads through Dynamic Web Content only once tracking is active. diff --git a/docs/mauticjs_api/tracking_script.rst b/docs/mauticjs_api/tracking_script.rst index c26121c6..41450a9f 100644 --- a/docs/mauticjs_api/tracking_script.rst +++ b/docs/mauticjs_api/tracking_script.rst @@ -223,6 +223,7 @@ The split scripts expose a small set of client-side global variables and an even * ``MauticJS.runtimeReady`` - set to ``true`` once the runtime bootstrap has loaded. Tracking code guards on it before running. * ``MauticJS.trackingEnabled`` - ``false`` in the essential or runtime build and ``true`` once the tracking layer loads. * ``MauticJS.requestWithCredentials`` - ``false`` by default in the essential or runtime build and ``true`` once tracking loads. +* ``mautic:tracking-enabled`` - a document event the tracking layer dispatches once tracking initializes. See :doc:`/mauticjs_api/focus_scripts` for how the Focus runtime consumes it to bridge consent. * ``mauticEssentialReady`` - a convention event, not something the generated runtime emits on its own. The authoritative readiness flag is ``MauticJS.runtimeReady``; the consent-managed essential loader snippet - the copy-paste snippet that loads ``/mautic-essential.js`` - dispatches ``mauticEssentialReady`` once ``MauticJS.runtimeReady === true`` by calling ``MauticJS.dispatchEvent('mauticEssentialReady')``. That helper builds a native ``CustomEvent`` and dispatches it on ``document``, so browser-side code following the split-script loader pattern can rely on it as a readiness hook and listen with ``document.addEventListener('mauticEssentialReady', ...)``. Because the essential script may have finished loading before your code runs - in which case the event has already fired and a late listener would never run - guard on ``MauticJS.runtimeReady`` first and run immediately when it's already ``true``, falling back to the listener only when the runtime isn't ready yet. The shipped tracking add-on snippet follows this same dual path. This handles both cases safely: From e82ccf2b0be9ac7da7e9b4abfec566258628417b Mon Sep 17 00:00:00 2001 From: "promptless[bot]" Date: Tue, 4 Aug 2026 13:45:35 +0000 Subject: [PATCH 2/2] Reconcile Focus Item scripts dev page to ready PR #16926 Update the developer docs for the split Focus scripts to match the now-ready state of mautic/mautic PR #16926 (source head ebe9fdfb): - Foreground the public window.MauticFocus.enableTracking() API and remove the no-longer-existent generated enableMauticFocusTracking{id}() callback. - Distinguish the Focus scope enum (FocusJsScope) from BuildJsScope, noting they share no code path and gate different script families. - Clarify that Dynamic Web Content injects the legacy /focus/{id}.js additively on top of the always-allowed display.js once tracking is enabled. - Add a runnable registry/queue inspection example and tighten the mautic:tracking-enabled cross-reference. --- docs/mauticjs_api/focus_scripts.rst | 31 ++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/docs/mauticjs_api/focus_scripts.rst b/docs/mauticjs_api/focus_scripts.rst index 15644b95..9391947d 100644 --- a/docs/mauticjs_api/focus_scripts.rst +++ b/docs/mauticjs_api/focus_scripts.rst @@ -7,7 +7,7 @@ Focus Items can be embedded on external sites. Mautic splits the embed JavaScrip For guidance on creating and configuring Focus Items in the Mautic UI, see the :xref:`Mautic User Documentation`. This page documents only the developer-facing split scripts. -The Focus split scripts follow the same scope model described in :ref:`Script scopes and split scripts`. The scopes below apply that same model to Focus Items. +The Focus split scripts follow an analogous but entirely separate scope model to the one described in :ref:`Script scopes and split scripts`. The scopes below apply that analogous but separate model to Focus Items. .. vale off @@ -22,7 +22,7 @@ Focus embedding is split into three scopes: * ``DISPLAY`` - loads and renders the Focus Item. It adds no tracking pixel, no Contact tokens, and no ``mauticform[focusId]`` marker. * ``TRACKING`` - resolves the tracked Contact, generates the trackable redirect, and installs the view pixel and the Form focus-id marker. -These are Focus-specific scope labels. They mirror the ``BuildJsScope`` model documented on the tracking script page, but they aren't the same ``enum``. +These are Focus-specific scope labels. They mirror the ``BuildJsScope`` model documented on the tracking script page, but they aren't the same ``enum`` and share no code path: ``BuildJsScope`` pairs ``RUNTIME`` and ``TRACKING`` with ``ESSENTIAL`` (not ``DISPLAY``) and gates the ``mtc.js`` / ``mautic-tracking.js`` / ``mautic-essential.js`` script family, whereas these Focus scopes gate only the Focus scripts. Don't gate Focus code on ``BuildJsScope`` cases or vice versa. .. vale off @@ -58,13 +58,24 @@ Client-side runtime globals .. vale on -The split scripts expose a small set of client-side global variables, a callback, and an event so a site can control when tracking activates: +The split scripts expose a small set of client-side global variables, a public method, and an event so a site can control when tracking activates: -* ``window.MauticFocusItems`` - a registry keyed by Focus Item id. Each entry is the runtime for that Focus Item and exposes methods to load and activate the tracking layer. The display script uses it to lazily inject and activate tracking. +* ``window.MauticFocus.enableTracking()`` - the public API for activating tracking on a displayed Focus Item after consent. If the Focus Item is registered in ``window.MauticFocusItems[id]``, it calls that item's ``loadTracking()``; otherwise it sets ``window.MauticFocusTrackingQueue[id] = true``, which the display script consumes once it registers the item. +* ``window.MauticFocusItems`` - a registry keyed by Focus Item ID. Each entry is the runtime for that Focus Item and exposes methods to load and activate the tracking layer. The display script uses it to lazily inject and activate tracking. * ``window.MauticFocusTrackingQueue`` - a queue that bridges consent. Activation requests made before the tracking layer is ready are queued and flushed once it loads. * ``window.MauticFocusUseMauticTrackingConsent`` - when set to ``true``, the Focus display script auto-activates tracking as soon as Mautic website tracking becomes enabled, instead of waiting for a manual or CMP activation. The copied website-tracking snippet sets it to ``true`` when an administrator turns on the 'Use Mautic consent for Focus tracking' option. -* ``enableMauticFocusTracking{id}()`` - the per-Focus-Item activation callback a site wires into its consent-management platform (CMP). Calling it activates the tracking layer for that Focus Item. -* ``mautic:tracking-enabled`` - a document event the tracking layer dispatches once tracking initializes. The Focus runtime listens for it to bridge consent, mirroring how browser code can listen for the tracking layer's readiness signals described under :ref:`Client-side runtime globals`. +* ``mautic:tracking-enabled`` - a document event the tracking layer dispatches once tracking initializes. The Focus runtime listens for it to bridge consent, mirroring the tracking layer's own dispatch of this event, documented in the ``mautic:tracking-enabled`` bullet under :ref:`Client-side runtime globals`. This is the event-dispatch signal, not the ``mauticEssentialReady`` / ``MauticJS.runtimeReady`` readiness-guard pattern. + +.. code-block:: js + + // check whether a Focus Item's runtime has registered yet + if (window.MauticFocusItems && window.MauticFocusItems[123]) { + window.MauticFocus.enableTracking(123); + } else { + // not ready yet — queue activation for when the display script loads + window.MauticFocusTrackingQueue = window.MauticFocusTrackingQueue || {}; + window.MauticFocusTrackingQueue[123] = true; + } .. vale off @@ -73,12 +84,14 @@ Activating tracking after consent .. vale on -Wire the per-Focus-Item callback into your CMP so it runs on the visitor's consent event. Use native JavaScript, since ``jQuery`` and other libraries aren't guaranteed to be available on third party sites: +Call the public ``window.MauticFocus.enableTracking()`` API from your CMP's consent event. Use native JavaScript, since ``jQuery`` and other libraries aren't guaranteed to be available on third party sites: .. code-block:: js // called by your consent-management platform once the visitor consents - window.enableMauticFocusTracking123(); + window.MauticFocus.enableTracking(123); + +The copied 'Consent-managed' snippet calls this same public API inline: it invokes ``window.MauticFocus.enableTracking()`` when available, and otherwise queues the request in ``window.MauticFocusTrackingQueue`` for the display script to flush once it loads. If the tracking layer hasn't loaded yet, the activation request is queued in ``window.MauticFocusTrackingQueue`` and flushed once the layer loads. @@ -91,4 +104,4 @@ Dynamic Web Content injection behavior .. vale on -The Dynamic Web Content build-JS subscriber now parses Dynamic Web Content through the DOM rather than treating it as opaque markup. It injects only same-origin Focus scripts. It always allows ``/focus/{id}/display.js`` so a Focus Item renders regardless of consent state. It injects the legacy ``/focus/{id}.js`` only when ``MauticJS.trackingEnabled`` is ``true``, so the combined tracking-capable script loads through Dynamic Web Content only once tracking is active. +The Dynamic Web Content build-JS subscriber now parses Dynamic Web Content through the DOM rather than treating it as opaque markup. It injects only same-origin Focus scripts through two independent allow-rules. It always allows ``/focus/{id}/display.js`` so a Focus Item renders regardless of consent state. When ``MauticJS.trackingEnabled`` is ``true``, it additionally allows the legacy ``/focus/{id}.js`` — this injection is added on top of the always-allowed display script, not in place of it — so the combined tracking-capable script loads through Dynamic Web Content only once tracking is active.