1// Microsoft Login (Preview) 2(function() { 3 // Released 10/24/2023 4 return; 5 6 $('body').append('<div class="modal fade" id="modalMsLoginNotice" tabindex="-1" role="dialog" aria-labelledby="modalMsLoginNoticeTitle" aria-hidden="true"><div class="modal-dialog modal-dialog-centered" role="document"><div class="modal-content"><div class="modal-header"><h5 class="modal-title" id="modalMsLoginNoticeTitle">New Login Experience — Coming Soon!</h5><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button></div><div class="modal-body"><p>Beginning <strong>October 24, 2023</strong>, Yavapai College will be transitioning to a new login experience with <strong>Microsoft</strong>.</p><div id="modalMsLoginPreview" style="width: 100%; height: 120px; background-image: url(https://www.yc.edu/v6/information-technology-services/img/sso/microsoftlogin.min.jpg); background-size: cover; margin: 25px 0;"></div><p><strong><a class="text-success" href="https://www.yc.edu/microsoftlogin" target="_blank">Click here</a></strong> to learn more about this important change.</p></div><div class="modal-footer"><a class="btn btn-primary" href="https://www.yc.edu/login">Continue to <strong>myYC</strong> →</a></div></div></div></div>'); 7 8 $('nav a.nav-myyc[href="https://www.yc.edu/login"], .library-header a.header__btn[href="https://www.yc.edu/login"]').unbind('click').click(function(e) { 9 e.preventDefault(); 10 11 var $modal = $('#modalMsLoginNotice'); 12 if (!$modal.length) 13 { 14 window.location.href = 'https://www.yc.edu/login'; 15 return; 16 } 17 18 $modal.modal('show'); 19 }); 20})(); 21 22/* 23 * YC Important Alerts 24 * 25 * Features: 26 * - Remote fetch and automatic refresh of alerts 27 * - Alert ribbon management 28 * - Alert modal management 29 * - Alert expiry 30 * 31 * Test vectors: 32 * - Static announcement (active; non-expiring; non-critical) 33 * - Temporary announcement (active; expiring; non-critical) 34 * - Emergency alert (active; expiring; critical) 35 * - Automatic refresh from source updates on interval 36 * - Automatic removal of expired alerts 37 * - Automatic render of emergency alert modal 38 * - Automatic dismissal of expired modal 39 * 40 * Notes: 41 * - Expiring alerts must be published with start date if enforced server-side (pre-publish) 42 */ 43// Eager load important alerts from authoritative URL in the event of an emergency 44(function() { 45 const ALERT_SRC_REFRESH = 120000; 46 const ALERT_SRC_URL = '/v6/index.html'; 47 const ALERT_SRC_CTA_REWRITE_BASEURL = '/v6/'; 48 const ALERT_ITEM = '.important-alerts-v62-alert-item'; 49 const ALERT_SRC_CTA_BUTTON = '.important-alerts-v62-alert-item a.btn'; 50 const ALERT_SRC_ELEMENT = '.important-alerts-v62'; 51 const ALERT_SRC_MODAL_ELEMENT = '#important-alerts-v62-alert-item-critical.modal'; 52 const ALERT_MODAL_PARENT = '.header-v62'; 53 54 const ALERT_MODAL_EXEMPT_PATHS = [ 55 /^\/v6\/library\//, 56 /^\/v6\/alert.*\.html/ 57 ]; 58 59 const ALERT_RIBBON_CREATE_IF_NOT_EXISTS = true; 60 61 var $body = $('body'); 62 63 function _modalExempt() 64 { 65 for (var i = 0; i < ALERT_MODAL_EXEMPT_PATHS.length; i++) 66 { 67 if (window.location.pathname.match(ALERT_MODAL_EXEMPT_PATHS[i])) 68 return true; 69 } 70 71 return false; 72 } 73 74 // Portable CSS for replicating to sub-sites without further modification of sub-site assets (i.e. /v6/library) 75 (function() { 76 var $styles = $([ 77 '<style>', 78 79 // Important Alerts v6.2 80 '@import url("https://fonts.googleapis.com/css2?family=Barlow+Semi+Condensed:ital,wght@0,400;1,400;1,700&display=swap");', 81 82 // Sized for 4 lines of text with no action, or 2 lines of text with action 83 '.important-alerts-v62 .carousel { height: unset; background-color: #fff; min-height: 140px; }', 84 '.important-alerts-v62 .carousel .carousel-control-next-icon,.important-alerts-v62 .carousel .carousel-control-prev-icon { transform: rotate(-90deg); }', 85 '.important-alerts-v62 .carousel .carousel-control-prev-icon { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' fill=\'%230005\' viewBox=\'0 0 8 8\'%3E%3Cpath d=\'M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z\'/%3E%3C/svg%3E"); }', 86 '.important-alerts-v62 .carousel .carousel-control-next-icon { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' fill=\'%230005\' viewBox=\'0 0 8 8\'%3E%3Cpath d=\'M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z\'/%3
86E%3C/svg%3E"); }', 87 '@media (min-width: 992px) { .important-alerts-v62 .carousel { min-height: unset; } }', 88 '.important-alerts-v62-alert-heading { color: unset; font-family: "Barlow Semi Condensed", sans-serif !important; font-weight: bold; font-size: 18px; text-decoration: none; min-height: 40px; }', 89 '.important-alerts-v62-alert-item { border-color: transparent; }', 90 '.important-alerts-v62-alert-item .container { max-width: 1275px; padding: 0; }', 91 '.important-alerts-v62-alert-item.alert { border-radius: 0; }', 92 '.important-alerts-v62-alert-item.alert-info { background-color: #fff; color: #165137; }', 93 '.important-alerts-v62-alert-item.alert-danger { background-color: #f60000; color: #fff; text-transform: uppercase; }', 94 '.important-alerts-v62-alert-item.alert-danger .btn.btn-myyc { background-color: #fff; color: #f60000; font-weight: bold; }', 95 96 // Overrides for legacy important alerts 97 '#important-alerts .alert { border-radius: 0; background-color: #fff; color: #000; padding: 1rem 2rem .15rem 2rem !important; border: 0; }', 98 '#important-alerts .alert * { text-align: left !important; line-height: 1.25; }', 99 '#important-alerts .alert .important-alerts-v62-alert-heading { line-height: 1; margin-bottom: 1rem; }', 100 '#important-alerts .alert .important-alerts-v62-alert-heading a.alert-link { text-decoration: none; color: #165137; font-family: "Barlow Semi Condensed", sans-serif !important; font-size: 1.25rem !important; }', 101 '#important-alerts .alert .important-alerts-v62-alert-heading a:after { display: inline-block; width: 0px; height: 0px; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-width: 0.3em 0.3em 0px; border-top-style: solid; border-top-color: initial; border-right-style: solid; border-right-color: transparent; border-bottom-style: initial; border-bottom-color: initial; border-left-style: solid; border-left-color: transparent; }', 102 '#important-alerts .alert .important-alerts-v62-alert-heading+div div { margin-bottom: 1rem; }', 103 104 // Remove extraneous right padding when modal is opened (https://stackoverflow.com/a/38909520) 105 '.modal, body.modal-open { padding-right: 0 !important; }', 106 107 // TODO: Apply .important-alerts-v62-* scope to portable CSS overrides 108 '.modal-title { position: static; }', 109 '.btn-myyc { background-color: #ffc107; color: #000; border-radius: 0; font-weight: bold; }', 110 '.btn-myyc:hover { color: #000c; }', 111 112 // Carousel vertical scroll (https://qawithexperts.com/article/bootstrap/vertical-carousel-bootstrap-4-and-bootstrap-3-with-jquery/327) 113 '.vert .carousel-item-next.carousel-item-left, .vert .carousel-item-prev.carousel-item-right { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); }', 114 '.vert .carousel-item-next, .vert .active.carousel-item-right { -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100% 0); }', 115 '.vert .carousel-item-prev, .vert .active.carousel-item-left { -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); }', 116 '</style>' 117 ].join('\n')) 118 .appendTo($body); 119 })(); 120 121 // Note: Activation of in-page critical alert modal is deferred until refresh() is called to mitigate early render of in-page critical alert when cleared at ALERT_SRC_URL 122 var $ribbon = $(ALERT_SRC_ELEMENT); 123 var $modal = $(ALERT_SRC_MODAL_ELEMENT); 124 125 if (!$ribbon.length) 126 { 127 if (!ALERT_RIBBON_CREATE_IF_NOT_EXISTS) 128 { 129 window.console && console.warn('No ALERT_SRC_ELEMENT found in current page'); 130 return; 131 } 132 133 // Find next best target to attach $ribbon to on-the-fly 134 $ribbon = $('<div />').addClass(ALERT_SRC_ELEMENT.replace(/^\./, '')); 135 136 if ($('header').length != 0) 137 { 138 $ribbon.insertAfter($('header').first()) 139 } 140 else 141 { 142 $ribbon.prependTo($body); 143 } 144 } 145 146 function _rewriteCTABaseURL() { 147 var $self = $(this); 148 var href = $self.attr('href'); 149 if (href.match(/^\/|https?:\/\//)) 150 return; 151 152 $self.attr('href', ALERT_SRC_CTA_REWRITE_BASEURL + href); 153 } 154 155 function _modalShown($m) 156 { 157 // Implemented for Bootstrap 4.1.3 (tested with jQuery 3.3.1) 158 return !!((($m.data()||{})['bs.modal']||{})._isShown); 159 } 160 161 function _getServerUTCOffsetMinutes() 162 { 163 // TODO: Refactor 164 return 420; 165 } 166 167 function _toLocalTime(dt, remoteOffsetMinutes) 168 { 169 var local = new Date(); 170 var offset = (local.getTimezoneOffset() - remoteOffsetMinutes) * 60000; 171 172 return new Date(dt.getTime() + offset); 173 } 174 175 function _itemExpired(item) 176 {
177 var validFromServerMS = parseInt(item.data('valid-from')); 178 var validUntilServerMS = parseInt(item.data('valid-until')); 179 180 var dt = new Date(); 181 182 if (!isNaN(validFromServerMS)) 183 { 184 var validFromLocalTime = _toLocalTime(new Date(validFromServerMS), _getServerUTCOffsetMinutes()); 185 if (dt < validFromLocalTime) 186 return true; 187 } 188 189 if (!isNaN(validUntilServerMS)) 190 { 191 var validUntilLocalTime = _toLocalTime(new Date(validUntilServerMS), _getServerUTCOffsetMinutes()); 192 if (dt > validUntilLocalTime) 193 return true; 194 } 195 196 return false; 197 } 198 199 function _getJChecksum($e) { 200 return ($e.html()||'').replace(/\s+/g, ' ').trim(); 201 } 202 203 // Content update checksums must be maintained and evaluated independent of DOM manipulation over extraneous plugin activity (i.e. font awesome SVG rendering of .modal .fa-xmark, etc.) 204 var _checksums = { 205 ribbon: _getJChecksum($ribbon), 206 modal: _getJChecksum($modal) 207 }; 208 209 // PATCH: 2022-12-12 0832 Ensure modal is still rendered when current page reflects published emergency alert 210 var fallbackModalProcessed = false; 211 var refresh = function() { 212 $.get(ALERT_SRC_URL + '?ia_refresh=' + ((new Date()).getTime()), function(res) { 213 var $res = $(res); 214 215 // $ribbon 216 var $src = $res.find(ALERT_SRC_ELEMENT); 217 if ($src.length) 218 { 219 var ribbonChecksum = _getJChecksum($src); 220 if (ribbonChecksum != _checksums['ribbon']) 221 { 222 _checksums['ribbon'] = ribbonChecksum; 223 224 // Update with relative CTA links in banner rewritten as root-relative (ALERT_SRC_CTA_REWRITE_BASEURL) 225 $src.find(ALERT_SRC_CTA_BUTTON).each(_rewriteCTABaseURL); 226 227 // Prevent premature rendering of expired alerts 228 $src.find(ALERT_ITEM).each(function() { 229 var $item = $(this); 230 231 if (_itemExpired($item)) 232 $item.closest('.carousel-item').remove(); 233 }); 234 235 $ribbon.html($src.html()); 236 237 // Show $ribbon if previously hidden 238 if (!$ribbon.is(':visible')) 239 $ribbon.show(); 240 } 241 } 242 else 243 { 244 // Hide any currently shown $ribbon if subsequently removed in $src 245 if ($ribbon.is(':visible')) 246 $src.hide(); 247 } 248 249 // $modal 250 var $modalSrc = $res.find(ALERT_SRC_MODAL_ELEMENT); 251 if ($modalSrc.length) 252 { 253 var modalChecksum = _getJChecksum($modalSrc); 254 if (modalChecksum != _checksums['modal']) 255 { 256 _checksums['modal'] = modalChecksum; 257 258 // Note: $modal creation is deferred to here due to prerequisite _checksumMatch() 259 if (!$modal.length) 260 $modal = $modalSrc.appendTo($body); 261 262 // Update modal with relative CTA links in modal rewritten as root-relative (ALERT_SRC_CTA_REWRITE_BASEURL) 263 $modalSrc.find('a.btn').each(_rewriteCTABaseURL); 264 265 // Must replaceWith to maintain data attributes (i.e. valid-until) 266 $modal.html($modalSrc.html()); 267 268 // Show $modal if previously hidden 269 if (!_itemExpired($modalSrc) && !_modalShown($modal)) 270 { 271 if (!_modalExempt()) 272 $modal.modal('show'); 273 274 // Mark initial load (with latest sourced from refresh) 275 fallbackModalProcessed = true; 276 } 277 } 278 else 279 { 280 // Initial load (with latest sourced from in-page) of default in-page $modal has been deferred until this point to mitigate unnecessary rendering of expired modal 281 if ($modal.length) 282 {
283 if (!_itemExpired($modal)) 284 { 285 if (!fallbackModalProcessed && !_modalShown($modal)) 286 { 287 if (!_modalExempt()) 288 $modal.modal('show'); 289 290 fallbackModalProcessed = true; 291 } 292 } 293 else 294 { 295 if (_modalShown($modal)) 296 $modal.modal('hide'); 297 } 298 } 299 } 300 } 301 else 302 { 303 // Hide any currently shown $modal if subsequently removed in $modalSrc 304 if (_modalShown($modal)) 305 $modal.modal('hide'); 306 } 307 }) 308 309 // Fallback if HTTP source fails (reverted to available in-page modal) 310 .fail(function() { 311 // Do not resurrect $modal on subsequent failures (ALERT_SRC_REFRESH) once processed (once per page load) 312 if (!fallbackModalProcessed && $modal.length && !_itemExpired($modal) && !_modalShown($modal)) 313 { 314 if (!_modalExempt()) 315 $modal.modal('show'); 316 317 fallbackModalProcessed = true; 318 } 319 }) 320 321 // Cleanup alert expired items between source updates 322 .always(function() { 323 $ribbon.find(ALERT_ITEM).each(function() { 324 var $item = $(this); 325 326 if (_itemExpired($item)) 327 $item.closest('.carousel-item').remove(); 328 }); 329 330 // Hide any currently shown $modal if expired 331 if (_itemExpired($modal) && _modalShown($modal)) 332 $modal.modal('hide'); 333 334 // Re-apply .active class on resulting .carousel-item set 335 var $criticalCarouselItem = $ribbon.find('.carousel-item .alert-danger').first().closest('.carousel-item'); 336 337 var $first = ($criticalCarouselItem.length != 0) 338 ? $criticalCarouselItem 339 : $ribbon.find('.carousel-item').first(); 340 341 $ribbon.find('.carousel-item').removeClass('active'); 342 $first.addClass('active'); 343 344 // Restart carousel 345 // Note: data-interval must be reset once emergency alert has been cleared 346 $carousel = $ribbon.find('.carousel'); 347 $carousel.carousel('dispose') 348 .carousel({ 349 interval: ($criticalCarouselItem.length != 0) 350 ? 0 351 : parseInt($carousel.data('interval')) 352 || parseInt($carousel.data('interval-default')) 353 || 4000 354 }); 355 }); 356 }; 357 358 // Auto-refresh 359 setInterval(refresh, ALERT_SRC_REFRESH); 360 refresh(); 361})(); 362 363// Toggle parent .carousel active cycle based on .modal state 364(function() { 365 $('.modal').on('shown.bs.modal', function (e) { 366 var $self = $(this); 367 368 var $carousel = $self.closest('.carousel'); 369 if (!$carousel.length) return; 370 371 $carousel.carousel('pause'); 372 }); 373 374 $('.modal').on('hidden.bs.modal', function (e) { 375 var $self = $(this); 376 377 var $carousel = $self.closest('.carousel'); 378 if (!$carousel.length) return; 379 380 $carousel.carousel('cycle'); 381 }) 382})(); 383 384// Reset src of YouTube embeds when parent .modal is actively being closed (hide.bs.modal) 385(function() { 386 $('iframe[src^="https://www\.youtube\.com/embed/"]').each(function() { 387 var $self = $(this); 388 var src = $self.attr('src'); 389 390 var $modal = $self.closest('.modal'); 391 if (!$modal.length) return; 392 393 $modal 394 .on('hide.bs.modal', function() { 395 $self.attr('src', src); 396 }); 397 });
398})(); 399 400 401// Adds an alt="" and a aria-hidden=true for the tracking pixel which was kicking up a 508 compliance 402// Uses jQuery's $(document).ready() to ensure inner function is called 403 404(function (){ 405 $(document).ready(function() { 406 // jQuery attribute selection [width=1[height=1] with regular expressions [src^="..."] 407 $('img[src^="https://pubads.g.doubleclick.net/"][width=1][height=1]') 408 .attr('alt', 'googoolpix') 409 .attr('aria-hidden', 'true') 410 }); 411})(); 412 413 414 415// Created from cgpt with Jerry 416 417 document.addEventListener('DOMContentLoaded', function() { 418 // Find any 1x1 pixel images from Google's domain 419 var pixelImgs = document.querySelectorAll('img[src*="pubads.g.doubleclick.net"]'); 420 pixelImgs.forEach(function(img) { 421 img.setAttribute('alt', ''); 422 img.setAttribute('aria-hidden', 'true'); 423 }); 424 }); 425 426// jerryz 427 428// Add alt="tracking pixel" to all 1x1 tracking pixel images 429window.addEventListener('load', function() { 430 const imgs = document.querySelectorAll('img'); 431 imgs.forEach(img => { 432 // Check for 1x1 pixel images 433 if (img.width === 1 && img.height === 1 && !img.hasAttribute('alt')) { 434 img.alt = 'tracking pixel'; 435 } 436 }); 437}); 438 439 440 441 442 443 444 445 446 447 448 449// fix positive tabindex that are causing issues with 508 compliance, specifically the ivy bot chat Features 450 451(function () { 452 document.addEventListener("DOMContentLoaded", function () { 453 const elements = document.querySelectorAll('[tabindex]'); 454 455 elements.forEach(function (el) { 456 const tabindex = parseInt(el.getAttribute("tabindex"), 10); 457 if (tabindex > 0) { 458 el.setAttribute("tabindex", "0"); 459 console.log(`Corrected tabindex from ${tabindex} to 0 on:`, el); 460 } 461 }); 462 }); 463})();
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.