PageSourceSearch

https://www.bigissue.com/wp-content/client-mu-plugins/bigissue-plu…/assets/js/frontend/bridge.js?m=1781534671g

js bigissue.com collected 2026-09-24 15:22:00 UTC 6,860 bytes, 225 lines download raw bytes

1/**
2 * Bridge API utility for storing and retrieving attribution data
3 * across bigissue.com and subscription platform
4 */
5
6/**
7 * Store attribution data and get a bridge ID
8 * 
9 * @param {Object} payload - The attribution data to store
10 * @param {string} payload.content_group - Content category (e.g., 'features', 'news')
11 * @param {string} payload.cta_type - Type of CTA (e.g., 'button', 'link')
12 * @param {string} payload.cta_position - Position of CTA (e.g., 'body', 'header', 'footer')
13 * @param {string} payload.cta_text - Text displayed on the CTA
14 * @param {string} payload.source_page - Full URL of the source page
15 * @param {number} [payload.expires_in=7200] - Time in seconds until data expires (default 2 hours)
16 * @returns {Promise<Object>} Bridge data including bridge_id
17 */
18async function storeBridgeData(payload) {
19	try {
20		const response = await fetch('/wp-json/bigissue/v1/bridge/store', {
21			method: 'POST',
22			headers: {
23				'Content-Type': 'application/json',
24			},
25			body: JSON.stringify(payload),
26		});
27
28		if (!response.ok) {
29			const error = await response.json();
30			throw new Error(error.message || 'Failed to store bridge data');
31		}
32
33		const data = await response.json();
34		return data;
35	} catch (error) {
36		console.error('Bridge API Error:', error);
37		throw error;
38	}
39}
40
41/**
42 * Retrieve attribution data using a bridge ID
43 * 
44 * @param {string} bridgeId - The bridge ID to resolve
45 * @returns {Promise<Object>} Bridge data including payload
46 */
47async function resolveBridgeData(bridgeId) {
48	try {
49		const response = await fetch(
50			`/wp-json/bigissue/v1/bridge/resolve?bridge_id=${encodeURIComponent(bridgeId)}`,
51			{
52				method: 'GET',
53				headers: {
54					'Content-Type': 'application/json',
55				},
56			}
57		);
58
59		if (!response.ok) {
60			const error = await response.json();
61			throw new Error(error.message || 'Failed to resolve bridge data');
62		}
63
64		const data = await response.json();
65		return data;
66	} catch (error) {
67		console.error('Bridge API Error:', error);
68		throw error;
69	}
70}
71
72/**
73 * Push attribution to GTM dataLayer (bigissue.com) after a successful bridge store.
74 * Matches subs-side shape under `darwin` where possible for shared GTM variable patterns.
75 * Full-page redirect may drop hits unless GA4 tags use beacon / transport settings in GTM.
76 *
77 * @param {Object} payload - Same fields sent to /bridge/store
78 * @param {string} bridgeId - Returned bridge_id
79 */
80function pushBridgeDataLayerEvent(payload, bridgeId) {
81	window.dataLayer = window.dataLayer || [];
82	window.dataLayer.push({
83		event: 'bridge_attribution_stored',
84		darwin: {
85			bridge_id: bridgeId,
86			content_group: payload.content_group,
87			cta_type: payload.cta_type,
88			cta_position: payload.cta_position,
89			cta_text: payload.cta_text,
90			source_page: payload.source_page,
91		},
92	});
93}
94
95/**
96 * Store attribution data and redirect to target URL with bridge_id
97 *
98 * @param {string} targetUrl - The URL to redirect to
99 * @param {Object} payload - The attribution data to store (see storeBridgeData)
100 * @returns {Promise<void>}
101 */
102async function storeAndRedirect(targetUrl, payload) {
103	try {
104		// Store the data and get bridge_id
105		const bridgeData = await storeBridgeData(payload);
106
107		pushBridgeDataLayerEvent(payload, bridgeData.bridge_id);
108
109		// Parse target URL and add bridge_id parameter
110		const url = new URL(targetUrl, window.location.origin);
111		url.searchParams.set('bridge_id', bridgeData.bridge_id);
112
113		// Redirect to target with bridge_id
114		window.location.href = url.toString();
115	} catch (error) {
116		// If storage fails, redirect without bridge_id
117		console.error('Bridge storage failed, redirecting without bridge_id:', error);
118		window.location.href = targetUrl;
119	}
120}
121
122/**
123 * Helper to attach bridge tracking to a link element
124 * 
125 * @param {HTMLElement} element - The link element to attach tracking to
126 * @param {Object} payload - The attribution data to store
127 */
128function attachBridgeTracking(element, payload) {
129	if (!element || !element.href) {
130		console.warn('Invalid element provided to attachBridgeTracking');
131		return;
132	}
133
134	element.addEventListener('click', async (e) => {
135		e.preventDefault();
136		e.stopImmediatePropagation(); // Stop other handlers (like GA) from firing
137		const targetUrl = element.href;
138		await storeAndRedirect(targetUrl, payload);
139	}, true); // Use capture phase to run before GA
140}
141
142/**
143 * Get the content group from page data
144 * Reads from data-article-category attribute on body tag
145 * For single posts, this will be the primary category assigned to the post
146 * Otherwise defaults to 'Unknown'
147 * 
148 * @returns {string}
149 */
150function getContentGroup() {
151	// Read from body tag data attribute (set by WordPress/theme)
152	const body = document.querySelector('body');
153	const category = body?.getAttribute('data-article-category');
154	
155	return category || 'Unknown';
156}
157
158/**
159 * Initialize bridge tracking for subscription and shop links
160 * 
161 * Automatically detects links to tracked domains and adds attribution tracking
162 * 
163 * Tracked domains:
164 * - bigissue.secure.darwin.cx → cta_type defaults to 'Subscription'
165 * - shop.bigissue.dsb-fly.net → cta_type defaults to 'Support'
166 * 
167 * Usage in HTML:
168 * <a href="https://bigissue.secure.darwin.cx/subscribe"
169 *    data-cta-position="body">
170 *   Subscribe Now
171 * </a>
172 * 
173 * Optional attributes:
174 * - data-cta-type: Type of CTA (overrides auto-detection)
175 * - data-cta-position: Position of CTA (default: 'Body')
176 * 
177 * Auto-populated:
178 * - cta_text: From link text content
179 * - source_page: Current page URL
180 * - content_group: From WordPress category or 'Unknown'
181 */
182function initBridgeTracking() {
183	// Define tracked domains and their default CTA types
184	const trackedDomains = [
185		{ domain: 'bigissue.secure.darwin.cx', defaultCtaType: 'Subscription' },
186		{ domain: 'shop.bigissue.dsb-fly.net', defaultCtaType: 'Support' }
187	];
188	
189	// Find all links to tracked domains
190	trackedDomains.forEach(({ domain, defaultCtaType }) => {
191		const elements = document.querySelectorAll(`a[href*="${domain}"]`);
192		
193		elements.forEach((element) => {
194			// Build payload with auto-population and fallbacks
195			const payload = { 
196				content_group: getContentGroup(),
197				cta_type: element.dataset.ctaType || defaultCtaType,
198				cta_position: element.dataset.ctaPosition || 'Body',
199				cta_text: element.textContent.trim() || 'Click',
200				source_page: window.location.href,
201			};
202
203			attachBridgeTracking(element, payload);
204		});
205	});
206}
207
208// Auto-initialize on DOM ready
209if (document.readyState === 'loading') {
210	document.addEventListener('DOMContentLoaded', initBridgeTracking);
211} else {
212	// DOM is already loaded
213	initBridgeTracking();
214}
215
216// Make functions available globally for debugging
217if (typeof window !== 'undefined') {
218	window.bridgeAPI = {
219		init: initBridgeTracking,
220		store: storeBridgeData,
221		resolve: resolveBridgeData,
222		storeAndRedirect: storeAndRedirect
223	};
224}
225

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.