Skip to content

Google Tag Manager

  • Access
  • GA4 Tag and ID Configuration
  • Slate Form Submission Tracking
  • Application Submission Tracking
  • Install Deanon-ID Tracking & UTM Persistance Script

A minimum of Edit permissions are required. Publish permissions are preferred.

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>