PageSourceSearch

https://ung.edu//_uploads/js/encode/cookie-notice.js

js ung.edu collected 2026-10-01 08:36:37 UTC 8,755 bytes, 111 lines download raw bytes

1// These scripts won't load until AFTER the user has consented to cookies.
2const delayedScriptURLs = [
3  'https://px.premion.com/pixel.js?pxid=22e6947f-bfb8-4161-806d-94ff743a592f',
4  'https://ung.shell.451.io/',
5];// Recommended: Put the optional trailing comma on the last URL in the above array anyway to avoid future issues.
6// Valid URL Formats
7//   A. For non-"UNG.edu" [and UNG.edu] scripts
8//       Absolute URL: `https://example.com/folder/file.js?p=v&id=123`
9//   B. For "UNG.edu" scripts only:
10//      Root-Relative: `/folder/file.js` (which will translate to: `https://ung.edu/folder/file.js`)
11
12// If `localStorage` does NOT have record of a previous acknowledgment from the user for *this* ("cookieA13t") notification :
13if (!localStorage.getItem("cookieA13t")) {
14
15  // TODO: Reroute from this static testing value(s) to dynamic imported value from Cascade Block
16  const notices = {
17    /* STARTS: EXAMPLE
18    "noticeName":{                 // Unique Identifier for this message within the `notices` library Object here. Used as the first param in the `notify()` function
19        "msg":"The message text.", // Required
20        "btn":"The button text",   // Optional, if present, a button will be appended to the code. (TODO: Style appropriately)
21        "ico":"msgType",           // [A yet-to-be] Predefined set of icons that accompany and categorize the message type. ("info","notice","alert","warning", etc.)
22        "bgc":"color",             // Optional, CSS-valid color (or predefined varName) to add to the bg of the message.
23        "loc":"fallbackID",        // Optional, if the second param in the `notify()` is absent/invalid, this will be the fallback location for the message. (If *neither* provided, the message will default as a modal popup in `notify()`.)
24    },
25    ** END OF: EXAMPLE */
26    "cookieA13t": {
27      "msg": `<p>This website uses cookies.<br> Learn more about how we treat <a href="https://ung.edu/legal/privacy.php" style="white-space:nowrap">privacy information</a>.<br> For questions or requests, complete a <a href="https://forms.cloud.microsoft/r/8mL7rt921i" style="white-space:nowrap">data subject request form</a>.</p>`,
28      "btn": "OK",
29      "ico": "info",
30      "bgc": "blue",
31      "loc": "aboveFooter",
32    },
33    // "snowClosure":{
34    //     "msg":"All campuses closed due to snow",
35    //     "btn":"Dismiss",
36    //     "ico":"snow",
37    //     "bgc":"yellow",
38    //     "loc":"belowNavbar",
39    // },
40  };// ENDOF: Hard-coded test values.
41
42  // If possible, utilize the new (c.2026) `Trusted Types API` to create a robust and centralized `Content Security Policy` to lock down "risky sinks".
43  // If possible, create a function (here named: "unscript()" ) using the Policy to lock down "risky sinks", otherwise [for outdated browsers] use a simple function without additional built-in security constraints.
44  // Remember, function EXPRESSIONS can be upgraded to a const, however at the disadvantage of not being hoisted as they are in function DECLARATIONS; therefore "const functions" must be positioned *before* they are invoked.
45  const unscriptPattern = function unscriptPattern(strPar) {
46    return strPar.replaceAll(/(?:(?:&lt;|<)span(?:(?! style\s*?=\s*?).)*?(?:>|&gt;)(.*?)(?:&lt;|<)\/span(?:>|&gt;))/gi, "$1")// Remove any SPANs without [style] attributes
47      .replaceAll(/ data-[^ =]+ *= *(?:"[^"]*"|''[^'']*'')/gi, '')//        Remove any "data-*"" attributes anywhere.
48      .replaceAll(/ on\w+\s*=\s*(?:"[^"]*"|''[^'']*'')/gi, '')//            Remove any "on*"" listener attributes anywhere. (onClick, oninput, etc.)
49      .replaceAll(/ (?:src|href)\s*=\s*(['"`]).*?javascript:.*?\1/gi, '')// Remove "javascript:" value protocols for either `src` or `href` attributes (straight up bad)
50      .replaceAll(/javascript:/gi, 'JavaScript -')//                      Replace any remaining suspicious "javascript:" to innocuous "JavaScript -", just in case.
51      .replaceAll(/(?:<|&lt;)(script|style|link|form|iframe).*?(?:\1(?:>|&gt;))/gi, '')// Remove any paired <script../script>||<link../link>||etc.
52      .replaceAll(/(?:<|&lt;)(script|style|link|form|iframe)[^>]*?(?:>|&gt;)/gi, '')//    Remove any self-closing <script../>||<link../>||etc.
53  }
54
55  if (window.trustedTypes && trustedTypes.createPolicy) {
56    /* `trustedTypes` are supported, so: */
57    const unscriptPolicy = trustedTypes.createPolicy("unscriptPolicy", {
58      createHTML: (string) => unscriptPattern(string)
59    });// Wrap the function in the policy
60    function unscript(strPar) { return unscriptPolicy.createHTML(strPar) }// And use it via the policy
61  } else {
62    function unscript(strPar) { return unscriptPattern(strPar) }// otherwise, (for outdated browsers) just use the simple pattern without additional security constraints
63  }
64
65  function notify(cookieId) {
66    const btnTxt = notices[cookieId].btn;
67    const cookieMsg = unscript(notices[cookieId].msg);
68
69    // Note: The SCRIPT and the CSS are expecting these elements to be organized in the following structure. If any changes occur, remember to refactor the code to compensate for such changes.
70    const cookieHTML = `<aside aria-label="Cookie Notice" id="${cookieId}" role="dialog"><div tabindex="0">${cookieMsg}<button type="button" id="${cookieId}Btn">${btnTxt}</button></div></aside>`;
71
72    document.body.insertAdjacentHTML('beforeend', cookieHTML);// Inject code at the bottom of the page (so as not to be mistakenly deemed as "important" and thus used in auto-generated URL summary/snippets for every page), but:
73
74    const cookieBox = document.getElementById(`${cookieId}`).firstElementChild;
75    cookieBox.focus();//                 a11y: Give it immediate focus so that screen-reader/keyboard users can't miss it.
76
77    const cookieBoxComputedStyle = window.getComputedStyle(cookieBox);// Remember: Placement/Timing matters here: if the CSS/STYLEs come AFTER this SCRIPT, then the element's measurements are taken immediately/BEFORE the elements are restyled!
78//  const cookieBoxMarginOffsetY = parseFloat(cookieBoxComputedStyle.marginTop) + parseFloat(cookieBoxComputedStyle.marginBottom); // computedStyles typically Strings ending with 'px' as units. parseFloat removes the units and converts the String to a, well: FLoat.
79    document.body.style.paddingBottom = (cookieBox.offsetHeight /* + cookieBoxMarginOffsetY */)  + "px";// A11y: Whenever cookie notification box is visible, add a blank region to the bottom of equal height where it can be scrolled off as to not unintentionally obscure the footer content.
80    //TODO: Implement ResizeObserver.observe(cookieBox) to change the body.paddingBottom whenever the cookieBox changes it's height (from the viewport changing its width and causing the contents to wrap/re-flow.)
81
82    /** /// Accommodate anchor scrolling with the height of the header-navbar fixed to the top.
83        let fixedTopHeight = document.querySelector('BODY > HEADER').offsetHeight + document.querySelector('BODY > NAV').offsetHeight
84        document.body.style.scrollPaddingTop = ( fixedTopHeight )+"px"; // ~Don't forget your units. Wait: this process should have been done already in another function much earlier. Commenting this out.
85    /**/
86
87    document.getElementById(`${cookieId}Btn`).addEventListener('click', (evt) => { // Add a click-event listener to the button to:
88      localStorage.setItem(cookieId, true);//                   Save the user acknowledgment to hide this message until next browser cache clearing.
89      document.body.style.removeProperty("padding-bottom");//   Remove the extra space that was created as a spillover.
90      document.querySelector(`.skip-link,[href="#main-content"],[tabindex],A:first-of-type`).focus();// Return the "focus-pointer" back to the top of the page for keyboard-navigators.
91      evt.currentTarget.parentElement.parentElement.remove();// And also remove the message from the current screen, obvi.
92
93      // Once they acknowledge: Import delayed Javascripts.
94      loadConsentedScripts();
95    });// END: btn.addEventListener()
96
97  }// END: notify() function declaration
98  notify("cookieA13t", "msgBoard"); // Notify them of this notification.
99}else{// Otherwise, they previously consented, so:
100    loadConsentedScripts();// Import delayed Javascripts
101}
102
103function loadConsentedScripts(){
104  for(scriptURL of delayedScriptURLs){
105    const delayedScript = document.createElement('script');
106    delayedScript.src = scriptURL;
107    delayedScript.async = true;
108    if(scriptURL==='https://ung.shell.451.io/')delayedScript.dataset.shell451='';// Because they wanted to be "different", AKA: "difficult".
109    document.body.appendChild(delayedScript);
110  }
111}

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.