Google Tag Manager
- Access
- GA4 Tag and ID Configuration
- Slate Form Submission Tracking
- Application Submission Tracking
- Install Deanon-ID Tracking & UTM Persistance Script
Access
Section titled “Access”A minimum of Edit permissions are required. Publish permissions are preferred.

GA4 Tag and ID Configuration
Section titled “GA4 Tag and ID Configuration”

Slate Form Submission Tracking
Section titled “Slate Form Submission Tracking”

![]()
Application Submission Tracking
Section titled “Application Submission Tracking”![]()
![]()
Install Deanon-ID Tracking & UTM Persistance Script
Section titled “Install Deanon-ID Tracking & UTM Persistance Script”Install the _ft tracking & utm_* persistance script in Google Tag Manager as a custom HTML tag.
<script>(function() { const COOKIE_NAME = '_ft'; const COOKIE_DOMAIN = '.miamioh.edu'; // set to '' for testing on localhost const COOKIE_DAYS = 365; const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
function getCookie(name) { const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); return match ? decodeURIComponent(match[2]) : null; }
function setCookie(name, value, days, domain) { const d = new Date(); d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000)); let cookieStr = name + "=" + encodeURIComponent(value) + ";expires=" + d.toUTCString() + ";path=/"; if (domain) { cookieStr += ";domain=" + domain; } document.cookie = cookieStr; }
function generateUUID() { if (typeof crypto !== 'undefined' && crypto.randomUUID) { return crypto.randomUUID(); } if (typeof crypto !== 'undefined' && crypto.getRandomValues) { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = crypto.getRandomValues(new Uint8Array(1))[0] % 16; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); }
// --- Load existing cookie state (visitor ID + first-touch UTMs), or initialize --- let ftData = {}; const existingRaw = getCookie(COOKIE_NAME); if (existingRaw) { try { ftData = JSON.parse(existingRaw); } catch (e) { ftData = {}; } }
let visitorId = ftData.visitor_id; if (!visitorId) { visitorId = generateUUID(); ftData.visitor_id = visitorId; }
// --- Capture first-touch UTMs into the same object, only if none stored yet --- let cookieNeedsUpdate = !existingRaw; // true if we just created visitor_id if (!ftData.utms) { const urlParams = new URLSearchParams(window.location.search); let captured = {}; let found = false; utmKeys.forEach((key) => { if (urlParams.has(key)) { captured[key] = urlParams.get(key); found = true; } }); if (found) { captured._captured_at = new Date().toISOString(); ftData.utms = captured; cookieNeedsUpdate = true; } }
if (cookieNeedsUpdate) { setCookie(COOKIE_NAME, JSON.stringify(ftData), COOKIE_DAYS, COOKIE_DOMAIN); }
function getStoredUTMs() { if (!ftData.utms) return null; const copy = Object.assign({}, ftData.utms); delete copy._captured_at; return copy; }
// --- Hidden referrer field tagging (visitor ID only) --- function tagReferrerField(field) { if (field.dataset.ftTagged) return; try { const url = new URL(field.value, window.location.origin); url.searchParams.set('ft_id', visitorId); field.value = url.toString(); } catch (e) { const separator = field.value.includes('?') ? '&' : '?'; field.value = field.value + separator + 'ft_id=' + encodeURIComponent(visitorId); } field.dataset.ftTagged = 'true'; }
// --- Form action rewriting: backfill UTMs without overwriting any already present --- function tagFormAction(form) { if (form.dataset.ftActionTagged) return; const utms = getStoredUTMs(); if (!utms || Object.keys(utms).length === 0) { form.dataset.ftActionTagged = 'true'; return; }
let actionUrl; try { actionUrl = new URL(form.action, window.location.origin); } catch (e) { form.dataset.ftActionTagged = 'true'; return; }
Object.keys(utms).forEach((key) => { if (!utms[key]) return; if (!actionUrl.searchParams.has(key)) { actionUrl.searchParams.set(key, utms[key]); } });
form.action = actionUrl.toString(); form.dataset.ftActionTagged = 'true'; }
function tagAll() { document.querySelectorAll('input[type="hidden"][name="referrer"]:not([data-ft-tagged])').forEach(tagReferrerField); document.querySelectorAll('[id^="form_"] form:not([data-ft-action-tagged])').forEach(tagFormAction); }
// --- Start tagging once the DOM body exists, then watch for async Slate forms --- function startTagging() { tagAll(); const observer = new MutationObserver(tagAll); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => observer.disconnect(), 15000); }
if (document.body) { startTagging(); } else { document.addEventListener('DOMContentLoaded', startTagging); }})();</script>