BEFORE Google tags load (it * replaces the GetSiteControl line in headcode.inc, which satisfies * that) so consent defaults and gtag 'set' flags precede any config. * * Everything an editor should ever need to change lives in CONFIG below. */ (function () { "use strict"; // ===================================================================== // CONFIG — edit text/links/lists here; logic lives below. // ===================================================================== var CONFIG = { policyVersion: 1, // bump to re-show the notice (choices are KEPT) cookieName: "mcc_consent", cookieDays: 365, privacyPolicyUrl: "https://meridiancc.edu/about_mcc/policies_and_procedures/privacy_policy/", honorGPC: true, honorDNT: false, // legacy Do Not Track (off by default) floatingTab: false, // no floating reopener (it covered the YuJa // accessibility widget); reopen via a // [data-mccc-open] link on the privacy page // Youth-page force-off was considered and deliberately OMITTED: the // College for Kids pages are parent-directed (registration is // parent-completed — 91¸£ÀûÉç's documented COPPA posture), so the pages' // visitors are adults. See INSTALL.md for the analysis + verified // paths should compliance ever want the restriction added. google: { // Verified against the refreshed backup 2026-07-23: analytics.inc // configures exactly these two tags. analyticsIds: ["G-2JV2BY02ES"], advertisingIds: ["AW-16908016673"], }, // Cookie-name prefixes deleted (best effort) when enhanced // measurement is off. _gcl* are Google Ads click/conversion cookies. cleanupPrefixes: ["_gcl"], text: { bannerTitle: "Your privacy choices", // Banner copy simplified per Tony 2026-07-26: short notice + policy // link; the full disclosures and the switch live in the panel. bannerBody: "91¸£ÀûÉç uses web analytics to " + "understand how visitors use this website and improve its " + "content and performance.", gpcBannerBody: "Your browser sent a Global Privacy Control signal. " + "Advertising and enhanced measurement are off. Basic site " + "analytics and site features remain active.", policyPre: "View our ", policyLinkLabel: "privacy policy", policyPost: " for more details.", acceptLabel: "Accept & Continue", continueLabel: "Continue", reviewLabel: "Privacy Settings", choicesLabel: "Privacy choices", modalTitle: "Your privacy choices", modalIntro: "Basic site analytics and site features are always " + "active. You control optional advertising and enhanced " + "measurement below.", saveLabel: "Save choices", alwaysOn: "Always active", optionalTag: "Optional", gpcLockNote: "Global Privacy Control detected. Advertising and " + "enhanced measurement are off.", onLabel: "On", offLabel: "Off", closeLabel: "Close privacy choices", }, categories: [ { id: "features", label: "Site features", locked: true, description: "These services support website features such as " + "live chat, social feeds, call routing, and accessibility. " + "They are not controlled by this preference.", services: [ { name: "Tawk.to", purpose: "Live chat with 91¸£ÀûÉç staff (a notice also appears before you chat)." }, { name: "Curator.io", purpose: "Social media feed display on some pages." }, { name: "CallRail", purpose: "Call routing and attribution — measures which pages lead to phone calls." }, { name: "YuJa Panorama", purpose: "Website accessibility tooling." }, { name: "OU Alerts", purpose: "Campus emergency alert banner." }, ], }, { id: "basic", label: "Basic site analytics", locked: true, description: "91¸£ÀûÉç measures how visitors navigate and use the " + "website so we can improve its content and performance. These " + "tools process technical and usage information and provide 91¸£ÀûÉç " + "with aggregate reports. Basic analytics remains active when " + "optional measurement is off.", services: [ { name: "Google Analytics (GA4)", purpose: "Page and navigation measurement." }, { name: "Modern Campus Personalization / Matomo", purpose: "Site-usage statistics for our CMS." }, ], }, { id: "enhanced", label: "Advertising and enhanced measurement", locked: false, description: "When enabled, Google may use advertising " + "identifiers and signed-in Google account signals to help 91¸£ÀûÉç " + "understand which ads lead to visits or actions and to provide " + "cross-device, demographic, and interest reporting. Turn this " + "off to disable those features. Basic site analytics remains " + "active.", services: [ { name: "Google Ads", purpose: "Ad conversion measurement for 91¸£ÀûÉç campaigns." }, { name: "Google Signals", purpose: "Signed-in, cross-device, and demographic/interest reporting in Google Analytics." }, ], }, ], }; // ===================================================================== // Engine — no config below this line. // ===================================================================== var win = window, doc = document; // ---- state ---------------------------------------------------------- // Schema: { v, ts, ack, em } where em is true/false when the visitor // made an explicit choice, or null when they only acknowledged. function migrateLegacy(s) { // rev-2 schema had cats{analytics,advertising} + explicit[]. Migrate // conservatively: ANY legacy explicit opt-out -> enhanced OFF; a // legacy explicit all-on -> ON; otherwise acknowledgment only. if (!s.cats || typeof s.cats !== "object" || !Array.isArray(s.explicit)) { return null; } var em = null; var optedOut = s.explicit.some(function (id) { return s.cats[id] === false; }); if (optedOut) em = false; else if (s.explicit.length) em = true; return { v: typeof s.v === "number" ? s.v : 0, ts: typeof s.ts === "string" ? s.ts : "", ack: s.ack === true, em: em }; } function validState(s) { if (!s || typeof s !== "object" || Array.isArray(s)) return null; if (typeof s.em === "undefined" && s.cats) return migrateLegacy(s); if (typeof s.v !== "number" || typeof s.ack !== "boolean") return null; if (s.em !== null && typeof s.em !== "boolean") return null; return { v: s.v, ts: typeof s.ts === "string" ? s.ts : "", ack: s.ack, em: s.em }; } function readState() { var m = ("; " + doc.cookie).split("; " + CONFIG.cookieName + "="); var raw = m.length > 1 ? m[1].split(";")[0] : null; if (!raw) { try { raw = win.localStorage.getItem(CONFIG.cookieName); } catch (e) {} } if (!raw) return null; var parsed = null; try { parsed = JSON.parse(decodeURIComponent(raw)); } catch (e) { return null; } var s = validState(parsed); if (!s) return null; // Policy/schema-version bump: PRESERVE the explicit choice, drop only // the acknowledgment so the notice re-shows. if (s.v !== CONFIG.policyVersion) s.ack = false; return s; } function writeState(state) { var raw = encodeURIComponent(JSON.stringify(state)); var exp = new Date(Date.now() + CONFIG.cookieDays * 864e5).toUTCString(); doc.cookie = CONFIG.cookieName + "=" + raw + "; expires=" + exp + "; path=/; SameSite=Lax" + (win.location.protocol === "https:" ? "; Secure" : ""); try { win.localStorage.setItem(CONFIG.cookieName, raw); } catch (e) {} } var stored = null; try { stored = readState(); } catch (e) { stored = null; } var gpcSignal = !!(CONFIG.honorGPC && navigator.globalPrivacyControl === true) || !!(CONFIG.honorDNT && (navigator.doNotTrack === "1" || win.doNotTrack === "1")); // Effective enhanced-measurement state, recomputed per load. The GPC // lock wins over a saved ON but never overwrites it. function effectiveEM() { if (gpcSignal) return false; if (stored && typeof stored.em === "boolean") return stored.em; return true; // notice-model default } var em; try { em = effectiveEM(); } catch (e) { stored = null; em = !gpcSignal; } var emLocked = gpcSignal; // ---- Google enforcement (synchronous — before gtag.js loads) -------- function gtagPush() { win.dataLayer = win.dataLayer || []; win.dataLayer.push(arguments); } function consentModePayload() { var ads = em ? "granted" : "denied"; return { ad_storage: ads, ad_user_data: ads, ad_personalization: ads, analytics_storage: "granted", // basic analytics: always on (US posture) }; } // Tracks whether the restrictive 'set' flags were applied in THIS // document, so an explicit OFF->ON change can restore them. On a fresh // ON load nothing is pushed at all — Google property/account settings // stay authoritative (a client-side `true` cannot enable a feature an // administrator disabled at the property level; it only undoes OUR // earlier `false` from this same page). var restrictiveApplied = false; function pushSignalFlags() { if (!em) { // Both control sets, per Google's 2026-06-15 Signals change: // Consent Mode ad settings govern Ads; allow_google_signals governs // signed-in association for Analytics behavioral reporting. gtagPush("set", { allow_google_signals: false, allow_ad_personalization_signals: false, restricted_data_processing: true, }); gtagPush("set", "ads_data_redaction", true); restrictiveApplied = true; } // When ON at initial load: leave Signals/personalization/RDP at // property-configured defaults — nothing to push. } function cleanupCookies() { if (em) return; doc.cookie.split("; ").forEach(function (pair) { var name = pair.split("=")[0]; var hit = CONFIG.cleanupPrefixes.some(function (p) { return name.indexOf(p) === 0; }); if (!hit) return; [null, "." + win.location.hostname, "." + win.location.hostname.split(".").slice(-2).join(".")] .forEach(function (domain) { doc.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/" + (domain ? "; domain=" + domain : ""); }); }); } // Consent Mode defaults + Signals flags — MUST precede gtag.js. // NOTE deliberately absent: no ga-disable-* (GA4 basic analytics always // runs; the flag was also network-verified ineffective for AW- ids) // and no Matomo optUserOut (Matomo is basic analytics, always on). gtagPush("consent", "default", consentModePayload()); pushSignalFlags(); cleanupCookies(); // ---- gated-script activation (type="text/plain") -------------------- // The engine contract for the knowledge-core site and the optional // hardened analytics.inc (category "enhanced" gates AW config there). function activateGated() { var nodes = doc.querySelectorAll( 'script[type="text/plain"][data-mcc-consent]'); Array.prototype.forEach.call(nodes, function (node) { var cat = node.getAttribute("data-mcc-consent"); var granted = cat === "enhanced" ? em : true; // locked cats always on if (!granted || node.getAttribute("data-mcc-activated")) return; var s = doc.createElement("script"); Array.prototype.forEach.call(node.attributes, function (a) { if (a.name === "type" || a.name === "data-mcc-consent") return; s.setAttribute(a.name, a.value); }); s.text = node.text || ""; node.setAttribute("data-mcc-activated", "1"); node.parentNode.insertBefore(s, node.nextSibling); }); } // ---- persistence + live application --------------------------------- function saveChoice(explicitEM) { stored = { v: CONFIG.policyVersion, ts: new Date().toISOString(), ack: true, em: explicitEM, }; writeState(stored); em = effectiveEM(); gtagPush("consent", "update", consentModePayload()); if (em) { if (restrictiveApplied) { // Explicit OFF->ON in the same document: restore everything the // OFF state changed (not just redaction). Property-level Google // settings remain authoritative. gtagPush("set", { allow_google_signals: true, allow_ad_personalization_signals: true, restricted_data_processing: false, }); gtagPush("set", "ads_data_redaction", false); restrictiveApplied = false; } } else { pushSignalFlags(); } cleanupCookies(); activateGated(); } function acknowledgeOnly() { stored = { v: CONFIG.policyVersion, ts: new Date().toISOString(), ack: true, em: stored && typeof stored.em === "boolean" ? stored.em : null, }; writeState(stored); } // ===================================================================== // UI — injected on DOMContentLoaded. All classes prefixed mccc-. // ===================================================================== var CSS = "" + ".mccc-banner{position:fixed;left:0;right:0;bottom:0;z-index:2147483000;" + "background:#fff;color:#1b2330;border-top:4px solid #007A33;" + "box-shadow:0 -2px 12px rgba(0,0,0,.18);padding:14px 20px;" + "font:15px/1.5 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif}" + ".mccc-banner-inner{max-width:1100px;margin:0 auto;display:flex;" + "flex-wrap:wrap;align-items:center;gap:12px 24px}" + ".mccc-banner-text{flex:1 1 420px;margin:0}" + ".mccc-banner-text strong{display:block;margin-bottom:2px}" + ".mccc-banner a{color:#00662b;font-weight:600}" + ".mccc-actions{display:flex;gap:10px;flex-wrap:wrap}" + ".mccc-btn{font:inherit;font-weight:700;border-radius:999px;cursor:pointer;" + "padding:9px 22px;border:2px solid #007A33;background:#007A33;color:#fff}" + ".mccc-btn:hover{background:#00662b;border-color:#00662b}" + ".mccc-btn--ghost{background:#fff;color:#00662b}" + ".mccc-btn--ghost:hover{background:#eef5f0}" + ".mccc-btn:focus-visible,.mccc-tab:focus-visible,.mccc-close:focus-visible," + ".mccc-switch input:focus-visible+span{outline:3px solid #1b2330;outline-offset:2px}" + ".mccc-tab{position:fixed;left:12px;bottom:12px;z-index:2147482999;" + "font:13px/1 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif;" + "font-weight:700;background:#fff;color:#00662b;border:2px solid #007A33;" + "border-radius:999px;padding:8px 14px;cursor:pointer;" + "box-shadow:0 1px 6px rgba(0,0,0,.25)}" + ".mccc-tab:hover{background:#eef5f0}" + ".mccc-overlay{position:fixed;inset:0;z-index:2147483001;" + "background:rgba(27,35,48,.55);display:flex;align-items:center;" + "justify-content:center;padding:16px}" + ".mccc-modal{background:#fff;color:#1b2330;max-width:640px;width:100%;" + "max-height:85vh;overflow:auto;border-radius:10px;border-top:6px solid #007A33;" + "padding:22px 24px;font:15px/1.5 -apple-system,BlinkMacSystemFont," + "'Segoe UI',Roboto,Arial,sans-serif}" + ".mccc-modal h2{margin:0 64px 6px 0;font-size:22px}" + ".mccc-modal-intro{margin:0 64px 14px 0}" + ".mccc-close{position:absolute;background:none;border:none;font-size:26px;" + "line-height:1;cursor:pointer;color:#1b2330;padding:6px 10px}" + ".mccc-modal-wrap{position:relative}" + ".mccc-modal-wrap .mccc-close{top:10px;right:10px}" + ".mccc-cat{border:1px solid #d9dee3;border-radius:8px;padding:12px 14px;" + "margin:0 0 10px}" + ".mccc-cat-head{display:flex;align-items:center;gap:12px}" + ".mccc-cat-head h3{margin:0;font-size:16px;flex:1}" + ".mccc-always{font-size:12px;font-weight:700;text-transform:uppercase;" + "letter-spacing:.05em;color:#1f5132;background:#eef5f0;border-radius:999px;" + "padding:3px 10px;white-space:nowrap}" + ".mccc-cat-desc{margin:8px 0 4px;color:#3d4757}" + ".mccc-services{margin:6px 0 0;padding-left:18px;color:#3d4757;font-size:14px}" + ".mccc-lock-note{margin:6px 0 0;color:#1f5132;font-size:14px;font-weight:600}" + ".mccc-switch{position:relative;display:inline-flex;align-items:center;" + "gap:8px;flex-shrink:0}" + ".mccc-switch input{position:absolute;opacity:0;width:44px;height:24px;" + "margin:0;cursor:pointer}" + ".mccc-switch input:disabled{cursor:not-allowed}" + ".mccc-switch span.mccc-track{width:44px;height:24px;border-radius:999px;" + "background:#8a94a3;transition:background .15s;display:inline-block}" + ".mccc-switch span.mccc-track::after{content:'';display:block;width:18px;" + "height:18px;border-radius:50%;background:#fff;margin:3px;" + "transition:transform .15s}" + ".mccc-switch input:checked+span.mccc-track{background:#007A33}" + ".mccc-switch input:checked+span.mccc-track::after{transform:translateX(20px)}" + ".mccc-switch input:disabled+span.mccc-track{opacity:.55}" + ".mccc-state{font-size:13px;font-weight:700;color:#3d4757;min-width:2.2em}" + "@media (prefers-reduced-motion:reduce){.mccc-switch span.mccc-track," + ".mccc-switch span.mccc-track::after{transition:none}}" + ".mccc-modal-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:16px}"; var bannerEl = null, overlayEl = null, tabEl = null, lastFocus = null; function el(tag, attrs, children) { var node = doc.createElement(tag); if (attrs) Object.keys(attrs).forEach(function (k) { if (k === "text") node.textContent = attrs[k]; else if (k === "html") node.innerHTML = attrs[k]; else node.setAttribute(k, attrs[k]); }); (children || []).forEach(function (c) { node.appendChild(c); }); return node; } function removeBanner() { if (bannerEl && bannerEl.parentNode) bannerEl.parentNode.removeChild(bannerEl); bannerEl = null; showTab(); } function showTab() { if (!CONFIG.floatingTab || tabEl) return; tabEl = el("button", { "class": "mccc-tab", type: "button", text: CONFIG.text.choicesLabel, "aria-haspopup": "dialog", }); tabEl.addEventListener("click", openModal); doc.body.appendChild(tabEl); } function buildBanner() { var t = CONFIG.text; var locked = emLocked; var msg = el("p", { "class": "mccc-banner-text" }); msg.appendChild(el("strong", { text: t.bannerTitle })); msg.appendChild(doc.createTextNode( (locked ? t.gpcBannerBody : t.bannerBody) + " " + t.policyPre)); msg.appendChild(el("a", { href: CONFIG.privacyPolicyUrl, text: t.policyLinkLabel, target: "_blank", rel: "noopener", })); msg.appendChild(doc.createTextNode(t.policyPost)); var actions = el("div", { "class": "mccc-actions" }); // Accept/Continue is an ACKNOWLEDGMENT, not a recorded consent: // defaults simply apply, and GPC keeps governing for signal-senders. // The real switch lives in the panel (Review details). var main = el("button", { "class": "mccc-btn", type: "button", text: locked ? t.continueLabel : t.acceptLabel }); main.addEventListener("click", function () { acknowledgeOnly(); removeBanner(); }); actions.appendChild(main); var review = el("button", { "class": "mccc-btn mccc-btn--ghost", type: "button", text: t.reviewLabel, "aria-haspopup": "dialog", }); review.addEventListener("click", openModal); actions.appendChild(review); bannerEl = el("div", { "class": "mccc-banner", role: "region", "aria-label": t.bannerTitle, }, [el("div", { "class": "mccc-banner-inner" }, [msg, actions])]); doc.body.appendChild(bannerEl); } function closeModal() { if (overlayEl && overlayEl.parentNode) overlayEl.parentNode.removeChild(overlayEl); overlayEl = null; if (lastFocus && lastFocus.focus) lastFocus.focus(); } function openModal() { if (overlayEl) return; lastFocus = doc.activeElement; var t = CONFIG.text; var wrap = el("div", { "class": "mccc-modal-wrap" }); var dialog = el("div", { "class": "mccc-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": "mccc-title", }, [wrap]); wrap.appendChild(el("h2", { id: "mccc-title", text: t.modalTitle })); var close = el("button", { "class": "mccc-close", type: "button", "aria-label": t.closeLabel, html: "×", }); close.addEventListener("click", closeModal); wrap.appendChild(close); wrap.appendChild(el("p", { "class": "mccc-modal-intro", text: t.modalIntro })); var emInput = null; CONFIG.categories.forEach(function (c) { var head = el("div", { "class": "mccc-cat-head" }); head.appendChild(el("h3", { id: "mccc-cat-" + c.id, text: c.label })); if (c.locked) { head.appendChild(el("span", { "class": "mccc-always", text: t.alwaysOn })); } else { var stateWord = el("span", { "class": "mccc-state", "aria-hidden": "true", text: em ? t.onLabel : t.offLabel, }); emInput = el("input", { type: "checkbox", role: "switch", "aria-labelledby": "mccc-cat-" + c.id, }); emInput.checked = em; if (emLocked) emInput.disabled = true; emInput.addEventListener("change", function () { stateWord.textContent = emInput.checked ? t.onLabel : t.offLabel; }); head.appendChild(el("label", { "class": "mccc-switch" }, [emInput, el("span", { "class": "mccc-track" }), stateWord])); } var box = el("div", { "class": "mccc-cat" }, [head]); box.appendChild(el("p", { "class": "mccc-cat-desc", text: c.description })); if (!c.locked && emLocked) { box.appendChild(el("p", { "class": "mccc-lock-note", text: t.gpcLockNote, })); } var ul = el("ul", { "class": "mccc-services" }); c.services.forEach(function (s) { ul.appendChild(el("li", { text: s.name + " — " + s.purpose })); }); box.appendChild(ul); wrap.appendChild(box); }); var save = el("button", { "class": "mccc-btn", type: "button", text: t.saveLabel }); save.addEventListener("click", function () { if (emLocked) acknowledgeOnly(); else saveChoice(!!(emInput && emInput.checked)); closeModal(); removeBanner(); }); wrap.appendChild(el("div", { "class": "mccc-modal-actions" }, [save])); overlayEl = el("div", { "class": "mccc-overlay" }, [dialog]); overlayEl.addEventListener("mousedown", function (e) { if (e.target === overlayEl) closeModal(); }); dialog.addEventListener("keydown", function (e) { if (e.key === "Escape") { e.preventDefault(); closeModal(); return; } if (e.key !== "Tab") return; var focusables = dialog.querySelectorAll( "button, input:not(:disabled), a[href], [tabindex]:not([tabindex='-1'])"); if (!focusables.length) return; var first = focusables[0], last = focusables[focusables.length - 1]; if (e.shiftKey && doc.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && doc.activeElement === last) { e.preventDefault(); first.focus(); } }); doc.body.appendChild(overlayEl); close.focus(); } function initUI() { var style = doc.createElement("style"); style.textContent = CSS; doc.head.appendChild(style); activateGated(); Array.prototype.forEach.call( doc.querySelectorAll("[data-mccc-open]"), function (n) { n.addEventListener("click", function (e) { e.preventDefault(); openModal(); }); }); if (!stored || !stored.ack) buildBanner(); else showTab(); } if (doc.readyState === "loading") { doc.addEventListener("DOMContentLoaded", initUI); } else { initUI(); } // Public API (stable): window.91¸£ÀûÉçConsent win.91¸£ÀûÉçConsent = { version: CONFIG.policyVersion, open: openModal, state: function () { return { basicAnalytics: true, enhancedMeasurement: em, gpcSignal: gpcSignal, gpcHonored: gpcSignal && !em, // derived live, never stored acknowledged: !!(stored && stored.ack), }; }, }; })();

91¸£ÀûÉç

News