|
|
|
@@ -1,11 +1,36 @@
|
|
|
|
|
|
|
|
<!-- @component
|
|
|
|
|
|
|
|
MetaPixel integrates the Meta (Facebook) Pixel into your Svelte application,
|
|
|
|
|
|
|
|
allowing you to track page views and custom events while respecting user consent
|
|
|
|
|
|
|
|
for tracking. The component manages the lifecycle of the Meta Pixel script and
|
|
|
|
|
|
|
|
PixelControl interface.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The PixelControl class also allows you to directly manage multiple Pixel
|
|
|
|
|
|
|
|
instances and handle event tracking with optional test event codes without
|
|
|
|
|
|
|
|
using the MetaPixel component.
|
|
|
|
|
|
|
|
-->
|
|
|
|
|
|
|
|
|
|
|
|
<script lang="ts" module>
|
|
|
|
<script lang="ts" module>
|
|
|
|
|
|
|
|
export type PixelControlOptions = {
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* if provided, events fired will always have this code attached
|
|
|
|
|
|
|
|
* to prevent them from polluting real analytics data.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
testEventCode?: string;
|
|
|
|
|
|
|
|
/** Advanced matching data */
|
|
|
|
|
|
|
|
advancedMatching?: AdvancedMatching;
|
|
|
|
|
|
|
|
/** Initialization options */
|
|
|
|
|
|
|
|
initOptions?: InitOptions;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export class PixelControl {
|
|
|
|
export class PixelControl {
|
|
|
|
private _pixelID: string;
|
|
|
|
private _pixelID: string;
|
|
|
|
private _testEventCode?: string = undefined;
|
|
|
|
private _testEventCode?: string = undefined;
|
|
|
|
private _trackingManager: MaybeGetter<TrackingManager | undefined>;
|
|
|
|
private _trackingManager: MaybeGetter<TrackingManager | undefined>;
|
|
|
|
|
|
|
|
|
|
|
|
private static _baseLoaded: boolean = false;
|
|
|
|
private static _baseLoaded: boolean = false;
|
|
|
|
|
|
|
|
private static _registeredPixels: Record<string, PixelControl> = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Indicates whether the Meta Pixel base script has been loaded. */
|
|
|
|
static get baseLoaded(): boolean {
|
|
|
|
static get baseLoaded(): boolean {
|
|
|
|
return this._baseLoaded;
|
|
|
|
return this._baseLoaded;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -24,11 +49,11 @@
|
|
|
|
private constructor(
|
|
|
|
private constructor(
|
|
|
|
trackingManager: MaybeGetter<TrackingManager | undefined>,
|
|
|
|
trackingManager: MaybeGetter<TrackingManager | undefined>,
|
|
|
|
pixelID: string,
|
|
|
|
pixelID: string,
|
|
|
|
testEventCode?: string
|
|
|
|
options?: PixelControlOptions
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
this._trackingManager = trackingManager;
|
|
|
|
this._trackingManager = trackingManager;
|
|
|
|
this._pixelID = pixelID;
|
|
|
|
this._pixelID = pixelID;
|
|
|
|
this._testEventCode = testEventCode;
|
|
|
|
this._testEventCode = options?.testEventCode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Loads the Meta Pixel base script. */
|
|
|
|
/** Loads the Meta Pixel base script. */
|
|
|
|
@@ -60,43 +85,55 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Returns a PixelControl instance for the given Meta Pixel ID. If
|
|
|
|
* Registers a PixelControl instance for the given Meta Pixel ID. If
|
|
|
|
* the base Meta Pixel script has not been loaded yet, it will be
|
|
|
|
* the base Meta Pixel script has not been loaded yet, it will be
|
|
|
|
* loaded automatically. Optionally sets a test event code for the Pixel.
|
|
|
|
* loaded automatically. Optionally sets a test event code for the Pixel.
|
|
|
|
* Does NOT initialize the Pixel; call `fireInit()` on the returned instance
|
|
|
|
* Should only be called once for each Pixel ID, use PixelControl.get()
|
|
|
|
* before tracking events.
|
|
|
|
* to retrieve existing instances.
|
|
|
|
* @param trackingManager Tracking manager to handle user consent for tracking
|
|
|
|
* @param trackingManager Tracking manager to handle user consent for tracking
|
|
|
|
* @param pixelID Meta Pixel ID
|
|
|
|
* @param pixelID Meta Pixel ID
|
|
|
|
* @param options Optional settings
|
|
|
|
* @param options Optional settings
|
|
|
|
* @returns PixelControl instance
|
|
|
|
* @returns PixelControl instance
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static for(
|
|
|
|
static initialize(
|
|
|
|
trackingManager: MaybeGetter<TrackingManager | undefined>,
|
|
|
|
trackingManager: MaybeGetter<TrackingManager | undefined>,
|
|
|
|
pixelID: string,
|
|
|
|
pixelID: string,
|
|
|
|
options?: {
|
|
|
|
options?: PixelControlOptions
|
|
|
|
/**
|
|
|
|
|
|
|
|
* if provided, events fired will always have this code attached
|
|
|
|
|
|
|
|
* to prevent them from polluting real analytics data.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
testEventCode?: string;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
): PixelControl {
|
|
|
|
): PixelControl {
|
|
|
|
|
|
|
|
// Load the base script if not already loaded
|
|
|
|
PixelControl.load();
|
|
|
|
PixelControl.load();
|
|
|
|
return new PixelControl(trackingManager, pixelID, options?.testEventCode);
|
|
|
|
|
|
|
|
|
|
|
|
// Check for existing PixelControl instance
|
|
|
|
|
|
|
|
if (this._registeredPixels[pixelID]) {
|
|
|
|
|
|
|
|
log.warn(
|
|
|
|
|
|
|
|
`PixelControl instance for Meta Pixel ID: ${pixelID} already exists. Returning existing instance.`
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
return this._registeredPixels[pixelID];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create and register the PixelControl instance
|
|
|
|
|
|
|
|
const pixel = new PixelControl(trackingManager, pixelID, options);
|
|
|
|
|
|
|
|
this._registeredPixels[pixelID] = pixel;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Fire initialization
|
|
|
|
|
|
|
|
window.fbq('init', pixel._pixelID, options?.advancedMatching, options?.initOptions);
|
|
|
|
|
|
|
|
log.debug(`Meta Pixel [${pixel._pixelID}] initialized.`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Initializes this pixel with the Meta Pixel API including any advanced
|
|
|
|
* Returns an existing PixelControl instance for the given Meta Pixel ID.
|
|
|
|
* matching data and options.
|
|
|
|
* @param pixelID Meta Pixel ID
|
|
|
|
* @param advancedMatching Advanced matching data
|
|
|
|
* @returns PixelControl instance
|
|
|
|
* @param initOptions Initialization options
|
|
|
|
* @throws Error if no PixelControl instance is found for the given ID.
|
|
|
|
* @returns this PixelControl instance
|
|
|
|
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
fireInit(advancedMatching?: AdvancedMatching, initOptions?: InitOptions): PixelControl {
|
|
|
|
static get(pixelID: string): PixelControl {
|
|
|
|
PixelControl.loadGuard();
|
|
|
|
const pixel = this._registeredPixels[pixelID];
|
|
|
|
window.fbq('init', this._pixelID, advancedMatching, initOptions);
|
|
|
|
if (!pixel) {
|
|
|
|
log.debug(`Meta Pixel [${this._pixelID}] initialized.`);
|
|
|
|
throw new Error(`No PixelControl instance found for Meta Pixel ID: ${pixelID}`);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
@@ -117,10 +154,17 @@
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
pageView() {
|
|
|
|
pageView() {
|
|
|
|
if (!this.consentGuard()) return;
|
|
|
|
if (!this.consentGuard()) return;
|
|
|
|
window.fbq('track', 'PageView', undefined, { test_event_code: this._testEventCode });
|
|
|
|
// Send the PageView event
|
|
|
|
log.debug(
|
|
|
|
if (!dev || this._testEventCode) {
|
|
|
|
`Meta Pixel [${this._pixelID}] PageView event sent (test code: ${this._testEventCode}).`
|
|
|
|
window.fbq('track', 'PageView', undefined, { test_event_code: this._testEventCode });
|
|
|
|
);
|
|
|
|
log.debug(
|
|
|
|
|
|
|
|
`Meta Pixel [${this._pixelID}] PageView event sent${dev && ` (test code: ${this._testEventCode})`}.`
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
log.info(
|
|
|
|
|
|
|
|
`Meta Pixel [${this._pixelID}] PageView event not sent in development mode without a test event code.`
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
@@ -129,13 +173,19 @@
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
track<K extends StandardEventName>(event: K, params?: EventParamsByName[K], eventID?: string) {
|
|
|
|
track<K extends StandardEventName>(event: K, params?: EventParamsByName[K], eventID?: string) {
|
|
|
|
if (!this.consentGuard()) return;
|
|
|
|
if (!this.consentGuard()) return;
|
|
|
|
window.fbq('trackSingle', this._pixelID, event, params, {
|
|
|
|
if (!dev || this._testEventCode) {
|
|
|
|
eventID,
|
|
|
|
window.fbq('trackSingle', this._pixelID, event, params, {
|
|
|
|
test_event_code: this._testEventCode
|
|
|
|
eventID,
|
|
|
|
});
|
|
|
|
test_event_code: this._testEventCode
|
|
|
|
log.debug(
|
|
|
|
});
|
|
|
|
`Meta Pixel [${this._pixelID}] ${event} event sent (test code: ${this._testEventCode}).`
|
|
|
|
log.debug(
|
|
|
|
);
|
|
|
|
`Meta Pixel [${this._pixelID}] ${event} event sent${dev && ` (test code: ${this._testEventCode})`}.`
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
log.info(
|
|
|
|
|
|
|
|
`Meta Pixel [${this._pixelID}] ${event} event not sent in development mode without a test event code.`
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
@@ -144,13 +194,19 @@
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
trackCustom(event: string, params?: CommonParams & CustomParams, eventID?: string) {
|
|
|
|
trackCustom(event: string, params?: CommonParams & CustomParams, eventID?: string) {
|
|
|
|
if (!this.consentGuard()) return;
|
|
|
|
if (!this.consentGuard()) return;
|
|
|
|
window.fbq('trackSingleCustom', this._pixelID, event, params, {
|
|
|
|
if (!dev || this._testEventCode) {
|
|
|
|
eventID,
|
|
|
|
window.fbq('trackSingleCustom', this._pixelID, event, params, {
|
|
|
|
test_event_code: this._testEventCode
|
|
|
|
eventID,
|
|
|
|
});
|
|
|
|
test_event_code: this._testEventCode
|
|
|
|
log.debug(
|
|
|
|
});
|
|
|
|
`Meta Pixel [${this._pixelID}] ${event} custom event sent (test code: ${this._testEventCode}).`
|
|
|
|
log.debug(
|
|
|
|
);
|
|
|
|
`Meta Pixel [${this._pixelID}] ${event} custom event sent (test code: ${this._testEventCode}).`
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
log.info(
|
|
|
|
|
|
|
|
`Meta Pixel [${this._pixelID}] ${event} custom event not sent in development mode without a test event code.`
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
@@ -171,32 +227,30 @@
|
|
|
|
import { onNavigate } from '$app/navigation';
|
|
|
|
import { onNavigate } from '$app/navigation';
|
|
|
|
import { resolveGetter, type MaybeGetter } from './util/getter.ts';
|
|
|
|
import { resolveGetter, type MaybeGetter } from './util/getter.ts';
|
|
|
|
import log from 'loglevel';
|
|
|
|
import log from 'loglevel';
|
|
|
|
|
|
|
|
import { dev } from '$app/environment';
|
|
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
interface Props {
|
|
|
|
/** Meta Pixel ID */
|
|
|
|
|
|
|
|
pixelID: string;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* If a test event code is available, events fired will always have this
|
|
|
|
|
|
|
|
* code attached to prevent them from polluting real analytics data.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
testEventCode?: string;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Controls whether page views are automatically tracked by this
|
|
|
|
|
|
|
|
* component (default: true).
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
autoPageView?: boolean;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Tracking manager to handle user consent for tracking. If omitted
|
|
|
|
* Tracking manager to handle user consent for tracking. If omitted
|
|
|
|
* tracking is disabled by default until consent is granted via
|
|
|
|
* tracking is disabled by default until consent is granted via
|
|
|
|
* PixelControl.grantConsent().
|
|
|
|
* PixelControl.grantConsent().
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
trackingManager?: TrackingManager;
|
|
|
|
trackingManager?: TrackingManager;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Meta Pixel ID */
|
|
|
|
|
|
|
|
pixelID: string;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Meta Pixel Options */
|
|
|
|
|
|
|
|
pixelOptions?: PixelControlOptions;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Controls whether page views are automatically tracked by this
|
|
|
|
|
|
|
|
* component (default: true).
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
autoPageView?: boolean;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let { pixelID, testEventCode, autoPageView = true, trackingManager }: Props = $props();
|
|
|
|
let { pixelID, pixelOptions, autoPageView = true, trackingManager }: Props = $props();
|
|
|
|
|
|
|
|
|
|
|
|
let pixel = $state<PixelControl | null>(null);
|
|
|
|
let pixel = $state<PixelControl | null>(null);
|
|
|
|
|
|
|
|
|
|
|
|
@@ -205,7 +259,7 @@
|
|
|
|
throw new Error('MetaPixel component requires a TrackingManager to manage consent.');
|
|
|
|
throw new Error('MetaPixel component requires a TrackingManager to manage consent.');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PixelControl.load();
|
|
|
|
PixelControl.load();
|
|
|
|
pixel = PixelControl.for(trackingManager, pixelID, { testEventCode }).fireInit();
|
|
|
|
pixel = PixelControl.initialize(trackingManager, pixelID, pixelOptions);
|
|
|
|
|
|
|
|
|
|
|
|
trackingManager.runWithConsent(() => {
|
|
|
|
trackingManager.runWithConsent(() => {
|
|
|
|
if (autoPageView && pixel) {
|
|
|
|
if (autoPageView && pixel) {
|
|
|
|
@@ -221,4 +275,11 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const getPixelControl = (): PixelControl => {
|
|
|
|
|
|
|
|
if (!pixel) {
|
|
|
|
|
|
|
|
throw new Error('MetaPixel component has not been initialized yet, wait for onMount.');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return pixel;
|
|
|
|
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
|