PageSourceSearch

https://www.cuw.edu/_files/js/global.js?v=1

js cuw.edu collected 2026-09-24 09:21:49 UTC 10,873 bytes, 297 lines download raw bytes

1const reducedMotion = window.matchMedia(`(prefers-reduced-motion: reduce)`) === true || window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true;
2let motion = 1;
3
4//if reduced motion set, replace videos with img poster 
5if( reducedMotion ) {
6    motion = 0;
7    $('.bg-video').each(function() {
8        var poster = $(this).attr('poster');
9        var title = $(this).attr('title');
10        var classes = $(this).attr('class');
11        var image = $('<img>').attr({'src': poster, 'alt': title, 'class': classes});
12        $(this).replaceWith(image);
13    });
14}
15
16//focus on search bar on offcanvas search open
17$('#nav-search').on('shown.bs.offcanvas', function(){
18    $('#search-bar').focus();
19});
20
21//load delayed blog images
22$('#nav-search').on('show.bs.offcanvas', function(){
23    $( $('img[data-src]', this) ).each(function() {
24        $(this).attr('src', $(this).attr('data-src')).removeAttr('data-src').on('load', function() {
25            $(this).removeClass('placeholder');
26        });
27    });
28});
29
30//toggle menus open/close aria + hide other on open
31$(document).on('hide.bs.offcanvas', function(e) {
32    $("[data-bs-target='#" + e.target.id + "']").attr('aria-pressed', false);
33    $('body').attr('offcanvas','');
34}).on('show.bs.offcanvas', function(e) {
35    $('body').attr('offcanvas',e.target.id);
36    $('.offcanvas-top.show, .offcanvas-end.show, .offcanvas-bottom.show, .offcanvas-start.show').offcanvas('hide');
37    $(e.relatedTarget).attr('aria-pressed', true);
38    var footerPosition = $('footer').offset().top;
39    var viewportTop = $(window).scrollTop();
40    var viewportHeight = $(window).height();
41    var viewportBottom = viewportTop + viewportHeight;
42    //if primary nav, scroll to top
43    if( e.target.id == 'nav-primary' ) {
44        setTimeout(function() {
45            $(window).scrollTop(0);
46            $('#nav-primary').css('top', ($('#alert').height() ?? 0) + ($('#header').height() ?? 0));
47        }, 0);
48    }
49    //if section nav past footer, scroll to top of footer
50    else if( e.target.id == 'section-menu' && footerPosition < viewportBottom ) {
51        setTimeout(function() {
52            $(window).scrollTop(footerPosition - viewportHeight);
53        }, 0);
54    }
55});
56
57//initialize carousels
58let initializeCarousels = [...$('.carousel')].map(el => new bootstrap.Carousel(el));
59
60/* =====================================================
61SCROLLING
62===================================================== */
63
64//delay scrolling to prevent triggering too many events
65var scrollTimer = 0;
66$(document).scroll(function() {
67    var minScrollTime = 250;
68    if(!scrollTimer) {
69        scrollTimer = setTimeout(function() {
70            var scrollPosition = $(document).scrollTop();
71            sectionMenuUpdate(scrollPosition);
72            mobileMenuUpdate(scrollPosition);
73            scrollTimer = null;
74        }, minScrollTime);
75    }
76});
77
78//update mobile menu position when it hits footer (fix for sticky issues)
79var mobileNav = $('#nav-mobile');
80function mobileMenuUpdate(position) {
81    if( mobileNav.is(":visible") ) {
82        var footerTop = $('footer').offset().top;
83        var viewportHeight = $(window).height();
84        var viewportBottom = position + viewportHeight;
85        var mobileNavHeight = mobileNav.height();
86        $('#nav-mobile-spacer').height(mobileNavHeight)
87        if( footerTop < viewportBottom ) {
88            if( !mobileNav.hasClass('position-relative') ) {
89                mobileNav.addClass('position-relative');
90                $('#nav-mobile-spacer').hide();
91            }
92        } else {
93            if( mobileNav.hasClass('position-relative') ) {
94                mobileNav.removeClass('position-relative');
95                $('#nav-mobile-spacer').show();
96            }
97        }
98    }
99}
100
101//scroll spy that actually works (thanks Bootstrap)
102var sectionMenuIds = $('#section-menu-nav li a');
103function sectionMenuUpdate(position) {
104    sectionMenuIds.each(function() {
105        var container = $(this).attr('href');
106        var containerTop = $(container).offset().top;
107        var containerHeight = $(container).outerHeight();
108        var containerBottom = containerTop + containerHeight;
109        if( position < (containerBottom - 200) && position >= (containerTop - 200) ) {
110            $(this).addClass('active');
111        } else {
112            $(this).removeClass('active');
113        }
114    });
115}
116
117//hide offcanvas section menu on anchor click
118$('#section-menu a').on('click', function() {
119    $('#section-menu').offcanvas('hide');
120});
121
122/* =====================================================
123UNWRAPPER
124===================================================== */
125
126function numberConverter(txt) {
127    if ($.isNumeric(txt)) {
128        return Number(txt);
129    } else {
130        return txt;
131    }
132}
133
134function unwrapper(selector) {
135    var unwrapped = new Object();
136    var wrapped = selector.split(';');
137    $(wrapped).each(function(x, y) {
138        var package = y.split(':');
139        if(package[1].indexOf(",") !== -1) {
140            var parcelGroup = [];
141            parcel = package[1].split(',');
142            $(parcel).each(function(x, z){
143                parcelGroup.push( numberConverter(z) );
144            });
145            unwrapped[package[0]] = parcelGroup;
146        } else {
147            unwrapped[package[0]] = numberConverter(package[1]);
148        }
149    });
150    return unwrapped;
151}
152
153/* =====================================================
154ANNOTATION ANIMATION TRIGGER
155===================================================== */
156
157var annotationObserver = new IntersectionObserver(annotationIntersection, {
158    threshold: 0.5
159});
160function annotationIntersection(entries, opts) {
161    entries.forEach(entry => {
162        if(entry.isIntersecting && !$(entry.target).hasClass('annotated')) {
163            var itemGroup = [];
164            if( $(entry.target).attr('data-annotate') == 'group' ) {
165                var selector = {
166                    list: $(entry.target).find('[data-annotate-item]'),
167                    attr: 'data-annotate-item'
168                };
169            } else {
170                var selector = {
171                    list: $(entry.target),
172                    attr: 'data-annotate'
173                };
174            }
175            $(selector.list).each(function() {
176                var options = unwrapper( $(this).attr(selector.attr) );
177                options['animate'] = motion;
178                var item = RoughNotation.annotate(this, options);
179                itemGroup.push(item);
180            });
181            RoughNotation.annotationGroup(itemGroup).show();
182            $(entry.target).addClass('annotated');
183        } else if(motion) {
184            setTimeout(function() {
185                $(entry.target).removeClass('annotated').parent().find('.rough-annotation').remove();
186            }, 300);
187        }
188    });
189}
190$('[data-annotate]').each(function() {
191    annotationObserver.observe(this);
192});
193
194/* =====================================================
195LAZY VIDEO TRIGGER
196===================================================== */
197
198var videoObserver = new IntersectionObserver(videoIntersection, {
199    threshold: 0,
200    rootMargin: '100%'
201});
202
203function videoIntersection(entries, opts) {
204    entries.forEach(entry => {
205        var videoSources = $('source', entry.target);
206        if( entry.isIntersecting && !videoSources.first().attr('src') ) {
207            $(videoSources).each(function() {
208                $(this).attr('src', $(this).attr('data-src')).removeAttr('data-src');
209            });
210            $(entry.target).trigger('load');
211            videoObserver.unobserve(entry.target);
212        }
213    });
214}
215
216$('video[loading=lazy]').each(function() {
217    videoObserver.observe(this);
218});
219
220/* =====================================================
221LAZY SCRIPT TRIGGER
222===================================================== */
223
224var scriptObserver = new IntersectionObserver(lazyIntersection, {
225	threshold: 0,
226	rootMargin: '100%'
227});
228
229function lazyIntersection(entries, opts) {
230	entries.forEach(entry => {
231		var scriptSource = $(entry.target).attr('data-lazy-script');
232		if( entry.isIntersecting && scriptSource ) {
233			$(entry.target).removeAttr('data-lazy-script');
234			$.getScript(scriptSource);
235			scriptObserver.unobserve(entry.target);
236		}
237	});
238}
239
240$('[data-lazy-script]').each(function() {
241	scriptObserver.observe(this);
242});
243  
244/* =====================================================
245PREEMPTIVE MODAL
246===================================================== */
247const url = window.location.search;
248const params = new URLSearchParams(url);
249
250if( params.has('modal') ) {
251    var modal = params.get('modal');
252    if( $('#' + modal).length ) {
253        new bootstrap.Modal($('#' + modal)).show();
254    }
255}
256
257/* =====================================================
258VIDEO MODAL
259===================================================== */
260$('.modal-video').on('show.bs.modal', function(e){
261    var btn = $(e.relatedTarget);
262    var videoID = btn.data('video-id');
263    var videoType = btn.data('video-type');
264    var videoTitle = btn.data('video-title');
265
266    switch (videoType) {
267        case 'wistia':
268            var videoSrc = "https://fast.wistia.net/embed/iframe/"+videoID+"?videoFoam=false&autoPlay=true&playerColor=005596";
269            break;
270        case 'youtube':
271            var videoSrc = "https://www.youtube.com/embed/"+videoID+"?rel=0&controls=1&showinfo=0&autoplay=1&color=white";
272            break;
273        case 'panopto':
274            var videoSrc = "https://cuwaa.hosted.panopto.com/Panopto/Pages/Embed.aspx?id="+videoID+"&autoplay=true&offerviewer=false&showtitle=false&showbrand=false&captions=false&interactivity=all&hideoverlay=false";
275            break;
276        default:
277            var videoSrc = "";
278    }
279    //if none of these data attributes are blank
280    if(videoTitle && videoID && videoSrc) {
281        $('.modal-title', this).text(videoTitle);
282        $('.modal-player', this).attr({'src': videoSrc, 'title': videoTitle}).addClass('loading');
283        $('.modal-player.loading').one('load', function(){				
284            $('.modal-player').removeClass('placeholder loading');
285        });
286    } else {
287        $('.modal-player', this).removeClass('placeholder loading').attr({'src': '', 'title': 'Error'});
288        $('.alert', this).show();
289        console.log("ERROR: required video data attribute missing.");
290    }
291});
292//reset modal on close
293$('.modal-video').on('hidden.bs.modal', function() {
294    $(this).find('.modal-player').addClass('placeholder').attr({'src': '', 'title': 'Loa
294ding...'});
295    $(this).find('.modal-title').text('Video');
296    $(this).find('.alert').hide();
297});

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.