kcc-gem-theme-original 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +142 -0
  4. data/_data/navigation.yml +154 -0
  5. data/_includes/accordion.html +36 -0
  6. data/_includes/accordion_path-sorting.html +29 -0
  7. data/_includes/body.html +5 -0
  8. data/_includes/breadcrumbs.html +44 -0
  9. data/_includes/card-section.html +26 -0
  10. data/_includes/chat-now.html +8 -0
  11. data/_includes/contacts-tan.html +94 -0
  12. data/_includes/contacts.html +96 -0
  13. data/_includes/document-head.html +25 -0
  14. data/_includes/emergency-alerts.html +29 -0
  15. data/_includes/foot.html +3 -0
  16. data/_includes/footer.html +193 -0
  17. data/_includes/hash/theme_hash.yml +1 -0
  18. data/_includes/hero-slider.html +36 -0
  19. data/_includes/nav-global.html +40 -0
  20. data/_includes/nav-local.html +34 -0
  21. data/_includes/nav-sub.html +19 -0
  22. data/_includes/preconnect.html +16 -0
  23. data/_includes/scripts/custom.html +4 -0
  24. data/_includes/scripts/google-api.html +2 -0
  25. data/_includes/scripts/google-noscript.html +9 -0
  26. data/_includes/scripts/google-tag.html +23 -0
  27. data/_includes/scripts/kcc-alerts.html +2 -0
  28. data/_includes/scripts/kcc-mega-nav.html +2 -0
  29. data/_includes/scripts/kcc-nav.html +2 -0
  30. data/_includes/scripts/kcc-theme-landing.html +2 -0
  31. data/_includes/scripts/kcc-theme.html +2 -0
  32. data/_includes/scripts/table-cdn.html +6 -0
  33. data/_includes/scripts/translate.html +11 -0
  34. data/_includes/scripts/vendor.html +8 -0
  35. data/_includes/styles/fonts.html +4 -0
  36. data/_includes/styles/main.html +6 -0
  37. data/_includes/styles/vendor.html +8 -0
  38. data/_includes/svg/check.html +11 -0
  39. data/_includes/svg/check_circle-24px.html +13 -0
  40. data/_includes/svg/email-24px.html +13 -0
  41. data/_includes/svg/goals.html +21 -0
  42. data/_includes/svg/kcc-copy.html +62 -0
  43. data/_includes/svg/kcc.html +65 -0
  44. data/_includes/svg/kcc.svg +59 -0
  45. data/_includes/svg/love.html +13 -0
  46. data/_includes/svg/place-24px.html +13 -0
  47. data/_includes/svg/save-money.html +26 -0
  48. data/_includes/tabbed-content.html +27 -0
  49. data/_includes/translate.html +3 -0
  50. data/_layouts/default.html +31 -0
  51. data/_layouts/markdown.html +13 -0
  52. data/_layouts/page.html +8 -0
  53. data/_layouts/sub-nav.html +6 -0
  54. data/assets/css/content.css +1 -0
  55. data/assets/css/main.css +1 -0
  56. data/assets/img/alert.svg +1 -0
  57. data/assets/img/arrow-right.svg +1 -0
  58. data/assets/img/arrow_right_alt.svg +1 -0
  59. data/assets/img/blank-contact.svg +1 -0
  60. data/assets/img/blue-next.svg +1 -0
  61. data/assets/img/blue-prev.svg +1 -0
  62. data/assets/img/dbl-next.svg +1 -0
  63. data/assets/img/dbl-next_mobile.svg +1 -0
  64. data/assets/img/dbl-prev.svg +1 -0
  65. data/assets/img/dbl-prev_mobile.svg +1 -0
  66. data/assets/img/facebook-f_white.svg +1 -0
  67. data/assets/img/facebook.svg +1 -0
  68. data/assets/img/heading-bg-underline-tan.png +0 -0
  69. data/assets/img/heading-bg-underline-transparent.png +0 -0
  70. data/assets/img/heading-bg-underline.png +0 -0
  71. data/assets/img/home.svg +8 -0
  72. data/assets/img/iccmc-logo_blue-transparent.png +0 -0
  73. data/assets/img/instagram-white.svg +1 -0
  74. data/assets/img/instagram.svg +1 -0
  75. data/assets/img/itransfer.png +0 -0
  76. data/assets/img/kankakee-community-college-blue.svg +1 -0
  77. data/assets/img/kankakee-community-college-word-logo.svg +1 -0
  78. data/assets/img/kcc-logo-inverse.svg +1 -0
  79. data/assets/img/kcc-logo-white.svg +1 -0
  80. data/assets/img/kcc-logo.svg +1 -0
  81. data/assets/img/kcc-placeholder-square.png +0 -0
  82. data/assets/img/kcc-placeholder.png +0 -0
  83. data/assets/img/kcc-text-logo-white.svg +1 -0
  84. data/assets/img/kcc-text-logo.svg +1 -0
  85. data/assets/img/keyboard_arrow_right.svg +1 -0
  86. data/assets/img/linkedin-white.svg +1 -0
  87. data/assets/img/linkedin.svg +1 -0
  88. data/assets/img/pause.svg +1 -0
  89. data/assets/img/pause_mobile.svg +1 -0
  90. data/assets/img/pinterest-p_white.svg +1 -0
  91. data/assets/img/pinterest.svg +1 -0
  92. data/assets/img/placeholder_16to9.jpg +0 -0
  93. data/assets/img/placeholder_4by3.jpg +0 -0
  94. data/assets/img/placeholder_square-1by1.jpg +0 -0
  95. data/assets/img/placeholder_square.jpg +0 -0
  96. data/assets/img/play.svg +1 -0
  97. data/assets/img/play_mobile.svg +1 -0
  98. data/assets/img/search.svg +1 -0
  99. data/assets/img/social-icon.svg +1 -0
  100. data/assets/img/twitter.svg +1 -0
  101. data/assets/img/video.svg +1 -0
  102. data/assets/img/volleyball.svg +1 -0
  103. data/assets/img/x-logo.svg +11 -0
  104. data/assets/img/x.svg +1 -0
  105. data/assets/img/xitter.svg +1 -0
  106. data/assets/img/youtube-white.svg +1 -0
  107. data/assets/img/youtube.svg +1 -0
  108. data/assets/img/yt-loading.png +0 -0
  109. data/assets/js/dist/102.197fcdd9ea9a90f7ba8e.bundle.js +1 -0
  110. data/assets/js/dist/13.197fcdd9ea9a90f7ba8e.bundle.js +2 -0
  111. data/assets/js/dist/13.197fcdd9ea9a90f7ba8e.bundle.js.LICENSE.txt +5 -0
  112. data/assets/js/dist/143.197fcdd9ea9a90f7ba8e.bundle.js +1 -0
  113. data/assets/js/dist/183.197fcdd9ea9a90f7ba8e.bundle.js +1 -0
  114. data/assets/js/dist/3.197fcdd9ea9a90f7ba8e.bundle.js +1 -0
  115. data/assets/js/dist/3.197fcdd9ea9a90f7ba8e.css +1 -0
  116. data/assets/js/dist/303.197fcdd9ea9a90f7ba8e.bundle.js +1 -0
  117. data/assets/js/dist/384.197fcdd9ea9a90f7ba8e.bundle.js +1 -0
  118. data/assets/js/dist/453.197fcdd9ea9a90f7ba8e.bundle.js +2 -0
  119. data/assets/js/dist/453.197fcdd9ea9a90f7ba8e.bundle.js.LICENSE.txt +5 -0
  120. data/assets/js/dist/635.197fcdd9ea9a90f7ba8e.bundle.js +2 -0
  121. data/assets/js/dist/635.197fcdd9ea9a90f7ba8e.bundle.js.LICENSE.txt +29 -0
  122. data/assets/js/dist/723.197fcdd9ea9a90f7ba8e.bundle.js +1 -0
  123. data/assets/js/dist/873.197fcdd9ea9a90f7ba8e.bundle.js +1 -0
  124. data/assets/js/dist/965.197fcdd9ea9a90f7ba8e.bundle.js +1 -0
  125. data/assets/js/dist/bootstrap.197fcdd9ea9a90f7ba8e.bundle.js +0 -0
  126. data/assets/js/dist/bootstrap.197fcdd9ea9a90f7ba8e.css +1 -0
  127. data/assets/js/dist/theme.197fcdd9ea9a90f7ba8e.bundle.js +2 -0
  128. data/assets/js/dist/theme.197fcdd9ea9a90f7ba8e.bundle.js.LICENSE.txt +47 -0
  129. data/assets/js/dist/theme.197fcdd9ea9a90f7ba8e.css +1 -0
  130. data/assets/js/nav/closeNavOnClick.js +22 -0
  131. data/assets/js/nav/highlightCurrentNav.js +44 -0
  132. data/assets/js/nav/moveSearchIcon.js +94 -0
  133. data/assets/js/nav/nav.js +13 -0
  134. data/assets/js/nav/searchToggleNav.js +46 -0
  135. data/assets/js/nav/toggleNavSearchDropdownOnWindowResize.js +52 -0
  136. data/assets/js/src/alerts.js +24 -0
  137. data/assets/js/src/all.js +83 -0
  138. data/assets/js/src/bootstrap.js +4 -0
  139. data/assets/js/src/cacheResponse.js +22 -0
  140. data/assets/js/src/campusAlertsSheetsAPI.js +53 -0
  141. data/assets/js/src/checkForPrefersReducedMotion.js +17 -0
  142. data/assets/js/src/contentHashLink.js +102 -0
  143. data/assets/js/src/createAlertsHtml.js +46 -0
  144. data/assets/js/src/footerDate.js +13 -0
  145. data/assets/js/src/getCachedResponse.js +37 -0
  146. data/assets/js/src/lazyLoad.js +13 -0
  147. data/assets/js/src/parseMarkdownToHTML.js +85 -0
  148. data/assets/js/src/sliders.js +81 -0
  149. data/assets/js/src/translate.js +62 -0
  150. data/assets/js/src/walkText.js +31 -0
  151. data/assets/js/src/wrapPowerText.js +11 -0
  152. data/assets/js/src/ytEmbed.js +30 -0
  153. data/assets/scss/0-tools/_bootstrap-overrides.scss +72 -0
  154. data/assets/scss/0-tools/_cloudcannon.scss +16 -0
  155. data/assets/scss/0-tools/_google-translate-overrides.scss +127 -0
  156. data/assets/scss/0-tools/_gsc-overrides.scss +144 -0
  157. data/assets/scss/0-tools/_vars.scss +72 -0
  158. data/assets/scss/1-base/_background.scss +5 -0
  159. data/assets/scss/1-base/_buttons.scss +121 -0
  160. data/assets/scss/1-base/_html.scss +4 -0
  161. data/assets/scss/1-base/_img.scss +12 -0
  162. data/assets/scss/1-base/_links.scss +126 -0
  163. data/assets/scss/1-base/_svg.scss +82 -0
  164. data/assets/scss/1-base/_typography.scss +640 -0
  165. data/assets/scss/2-modules/_accordion.scss +43 -0
  166. data/assets/scss/2-modules/_benefits.scss +371 -0
  167. data/assets/scss/2-modules/_breadcrumbs.scss +32 -0
  168. data/assets/scss/2-modules/_campus-alerts.scss +3 -0
  169. data/assets/scss/2-modules/_card-section.scss +9 -0
  170. data/assets/scss/2-modules/_contacts.scss +46 -0
  171. data/assets/scss/2-modules/_error.scss +10 -0
  172. data/assets/scss/2-modules/_footer.scss +122 -0
  173. data/assets/scss/2-modules/_header.scss +21 -0
  174. data/assets/scss/2-modules/_hero-slider.scss +331 -0
  175. data/assets/scss/2-modules/_img-hover.scss +70 -0
  176. data/assets/scss/2-modules/_loader.scss +9 -0
  177. data/assets/scss/2-modules/_nav-global.scss +315 -0
  178. data/assets/scss/2-modules/_nav-landing.scss +203 -0
  179. data/assets/scss/2-modules/_nav-local.scss +45 -0
  180. data/assets/scss/2-modules/_social-icons.scss +36 -0
  181. data/assets/scss/2-modules/_sub-nav.scss +32 -0
  182. data/assets/scss/2-modules/_yt-embed.scss +14 -0
  183. data/assets/scss/3-layout/_background.scss +3 -0
  184. data/assets/scss/3-layout/_content.scss +37 -0
  185. data/assets/scss/3-layout/_margins.scss +22 -0
  186. data/assets/scss/3-layout/_padding.scss +19 -0
  187. data/assets/scss/3-layout/_positioning.scss +343 -0
  188. data/assets/scss/3-layout/_section.scss +22 -0
  189. data/assets/scss/3-layout/_tables.scss +8 -0
  190. data/assets/scss/3-layout/_thank-you.scss +5 -0
  191. data/assets/scss/4-pages/index/_home.scss +15 -0
  192. data/assets/scss/bootstrap.scss +98 -0
  193. data/assets/scss/kcc-theme.scss +50 -0
  194. data/assets/scss/translate.scss +5 -0
  195. metadata +278 -0
@@ -0,0 +1,22 @@
1
+ const navLinks = '.nav-link:not(.dropdown-toggle)'; // Bootstrap 4 class
2
+
3
+ function hideBootstrapMenu(menu, Collapse) {
4
+ const bsCollapse = new Collapse(menu, { toggle: false });
5
+
6
+ bsCollapse.hide();
7
+ }
8
+
9
+ function closeMenuOnClick(Collapse) {
10
+ document.addEventListener('click', e => {
11
+ if ( !e.target.matches(navLinks) || e.target.classList.contains('dropdown-toggle') ) // Bail out of the rest of the code if the click event's target is not what we want!
12
+ return;
13
+
14
+ const menu = document.getElementById('mainNavContent');
15
+
16
+ if (menu.classList.contains('show')) {
17
+ hideBootstrapMenu(menu, Collapse);
18
+ }
19
+ });
20
+ }
21
+
22
+ export default closeMenuOnClick;
@@ -0,0 +1,44 @@
1
+ // Custom Vanilla JS to highlight the user's current location in the navigation bar and the sub-nav navigation bar
2
+ function setActive(link) {
3
+ const li = link.parentNode;
4
+
5
+ li.classList.add('active');
6
+ link.insertAdjacentHTML('beforeend', ' <span class="visually-hidden">(current)</span>');
7
+ }
8
+
9
+ function checkNavLinks(navList) {
10
+ const pathname = window.location.pathname;
11
+ const locationIsContactHash = window.location.hash === '#contact';
12
+ const locationIsHome = window.location.pathname === '/';
13
+
14
+ [...navList].forEach(item => {
15
+ const link = item.querySelector('a');
16
+ const href = link.getAttribute('href').replace(/^\/?\.\.\/(\.\.\/)?(\.\.\/)?/g, '/');
17
+ const linkIsHome = link.textContent.toLowerCase() === 'home';
18
+ const linkIsMatch = (pathname.indexOf(href) !== -1);
19
+
20
+ if (locationIsHome || locationIsContactHash) {
21
+ if (linkIsHome) {
22
+ setActive(link);
23
+ }
24
+ } else {
25
+ if (linkIsMatch && !linkIsHome) {
26
+ setActive(link);
27
+ }
28
+ }
29
+ });
30
+ }
31
+
32
+ function highlightNav() {
33
+ const navList = document.querySelectorAll('.js-nav-item');
34
+
35
+ if (document.getElementById('subNavNav')) {
36
+ const subNavList = document.querySelectorAll('.js-sub-nav-item');
37
+
38
+ checkNavLinks(subNavList);
39
+ }
40
+
41
+ checkNavLinks(navList);
42
+ }
43
+
44
+ export default highlightNav;
@@ -0,0 +1,94 @@
1
+ function checkXIcon() {
2
+ const checkXIconOnLoad = (function() {
3
+ let executed = false;
4
+ return function() {
5
+ if (!executed) {
6
+ executed = true;
7
+ // do checkXIconOnLoad
8
+ const icon = document.getElementById('xIcon');
9
+ const xIsHidden = icon.getAttribute('style') === 'display: none;';
10
+ xIsHidden ? removeClear() : clearXIcon();
11
+ }
12
+ };
13
+ })();
14
+ checkXIconOnLoad(); // "do checkXIconOnLoad" happens
15
+ }
16
+
17
+ function clearXIcon() {
18
+ const targetEl = document.querySelector('button.gsc-search-button-v2');
19
+ targetEl.classList.add('gsc-overrides__clear-x');
20
+ }
21
+
22
+ function removeClear() {
23
+ const targetEl = document.querySelector('button.gsc-search-button-v2');
24
+ targetEl.classList.remove('gsc-overrides__clear-x');
25
+ }
26
+
27
+ function addId() {
28
+ const xIcon = document.querySelector('.gsst_a');
29
+ xIcon.setAttribute('id', 'xIcon');
30
+ }
31
+
32
+ function gscInit() {
33
+ var cx = '006320264078644364913:sy48bet-lr8';
34
+ var gcse = document.createElement('script');
35
+ gcse.type = 'text/javascript';
36
+ gcse.async = true;
37
+ gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
38
+ var s = document.getElementsByTagName('script')[0];
39
+ s.parentNode.insertBefore(gcse, s);
40
+ }
41
+
42
+ function moveSearchIcon() {
43
+ const pageHasGSearch = document.getElementById('searchCollapse');
44
+
45
+ if ( pageHasGSearch ) {
46
+ let initSearchPromise = new Promise((resolve, reject) => {
47
+ gscInit();
48
+ resolve();
49
+ });
50
+ initSearchPromise.then(() => {
51
+
52
+ let addIdPromise = new Promise((resolve, reject) => {
53
+
54
+ const targetNode = document.getElementById('searchCollapse');
55
+ const config = { attributes: true, childList: true, subtree: true };
56
+ const callback = function(mutationsList, observer) {
57
+ for(const mutation of mutationsList) {
58
+ if (mutation.type == 'childList') {
59
+ addId();
60
+ resolve();
61
+ }
62
+ }
63
+ };
64
+ const observer = new MutationObserver(callback);
65
+ observer.observe(targetNode, config);
66
+ // Later, you can stop observing
67
+ //observer.disconnect();
68
+ });
69
+ addIdPromise.then(() => {
70
+ checkXIcon();
71
+ const targetNode = document.getElementById('xIcon');
72
+ const config = { attributes: true, childList: true, subtree: true };
73
+ const callback = function(mutationsList, observer) {
74
+ for(const mutation of mutationsList) {
75
+ if (mutation.type == 'attributes') {
76
+ const xIsHidden = targetNode.getAttribute('style') === 'display: none;';
77
+ if (xIsHidden) {
78
+ removeClear();
79
+ } else {
80
+ clearXIcon();
81
+ }
82
+ }
83
+ }
84
+ };
85
+ const observer = new MutationObserver(callback);
86
+ observer.observe(targetNode, config);
87
+ // Later, you can stop observing
88
+ //observer.disconnect();
89
+ });
90
+ });
91
+ }
92
+ }
93
+
94
+ export default moveSearchIcon;
@@ -0,0 +1,13 @@
1
+ import highlightNav from './highlightCurrentNav.js';
2
+ import searchToggle from './searchToggleNav.js';
3
+ import moveSearchIcon from './moveSearchIcon.js';
4
+ // import closeMenuOnClick from './closeNavOnClick.js';
5
+ import toggleSearchDropdownOnWindowResize from './toggleNavSearchDropdownOnWindowResize.js';
6
+
7
+
8
+ export default function nav() {
9
+ highlightNav();
10
+ searchToggle();
11
+ moveSearchIcon();
12
+ toggleSearchDropdownOnWindowResize();
13
+ }
@@ -0,0 +1,46 @@
1
+ // Custom JS to toggle the search form on mobile devices
2
+ const mainNav = document.getElementById('mainNav');
3
+ const globalNav = document.getElementById('globalNav');
4
+
5
+ function switchToX(icon, button) {
6
+ icon.style.backgroundImage = 'url("/assets/img/x.svg")';
7
+ icon.setAttribute('alt', 'Close icon');
8
+ button.setAttribute('aria-label', 'Close search');
9
+ }
10
+
11
+ function switchToSearch(icon, button) {
12
+ icon.style.backgroundImage = 'url("/assets/img/search.svg")';
13
+ icon.setAttribute('alt', 'Search icon');
14
+ button.setAttribute('aria-label', 'Open search');
15
+ }
16
+
17
+ function searchToggle() {
18
+ if (!document.getElementById('searchIcon'))
19
+ return;
20
+
21
+ const searchButton = document.getElementById('searchIcon');
22
+
23
+ document.addEventListener('click', function (e) {
24
+ // If the clicked element doesn't have the right selector, bail
25
+ if (!e.target.closest('#searchIcon')) return;
26
+ // Don't follow the link
27
+ e.preventDefault();
28
+
29
+ const searchIconElement = document.getElementById('searchImg');
30
+ const searchCollapse = document.getElementById('searchCollapse');
31
+ const searchIconBackgroundImage = searchIconElement.style.backgroundImage;
32
+ const iconIsSearch = (searchIconBackgroundImage.indexOf('assets/img/search.svg') != -1);
33
+
34
+ if (iconIsSearch) {
35
+ switchToX(searchIconElement, searchButton);
36
+ searchCollapse.setAttribute('aria-hidden', 'false');
37
+ } else {
38
+ switchToSearch(searchIconElement, searchButton);
39
+ searchCollapse.setAttribute('aria-hidden', 'true');
40
+ }
41
+ searchCollapse.classList.toggle('nav-global__search-collapse--visible');
42
+ mainNav.classList.toggle('nav-local__search-toggle');
43
+ globalNav.classList.toggle('nav-global__search-toggle');
44
+ }, false);
45
+ }
46
+ export default searchToggle;
@@ -0,0 +1,52 @@
1
+ // Custom JS to Close the Navigation menu, if its open, & if the screen goes above 992px wide (Bootstrap 4 'lg' devices)
2
+ const searchCollapseVisibleClass = 'nav-global__search-collapse--visible'; // Class in the HTML when the search collapse is open/visible
3
+ const globalNavSearchVisibleClass = 'nav-global__search-toggle';
4
+ const localNavSearchVisibleClass = 'nav-local__search-toggle';
5
+
6
+ function removeClassFromElement(el, classToRemove) {
7
+ el.classList.remove(classToRemove);
8
+ }
9
+
10
+ function checkElementCollapseState(el, classToCheckFor) {
11
+ if ( ! el.classList.contains(classToCheckFor) )
12
+ return;
13
+
14
+ removeClassFromElement(el, classToCheckFor);
15
+ }
16
+
17
+ function toggleSearchIconToX(searchIcon) {
18
+ const searchSpan = searchIcon.querySelector('#searchImg');
19
+
20
+ searchIcon.setAttribute('aria-label', 'Toggle Search');
21
+ searchSpan.setAttribute('alt', 'Open icon');
22
+ searchSpan.setAttribute('style', 'background-image: url("/assets/img/search.svg")');
23
+ }
24
+
25
+ function checkSearchToggleIcon(searchIcon) {
26
+ let ariaLabel = searchIcon.getAttribute('aria-label');
27
+
28
+ if ( ! ariaLabel === 'Toggle Close' )
29
+ return;
30
+
31
+ toggleSearchIconToX(searchIcon);
32
+ }
33
+
34
+ function windowResizeHandler() {
35
+ if ( window.innerWidth >= 992 ) {
36
+ const searchCollapseElement = document.getElementById('searchCollapse');
37
+ const globalNav = document.getElementById('globalNav');
38
+ const localNav = document.getElementById('mainNav');
39
+ const searchIcon = document.getElementById('searchIcon');
40
+
41
+ checkElementCollapseState(searchCollapseElement, searchCollapseVisibleClass);
42
+ checkElementCollapseState(globalNav, globalNavSearchVisibleClass);
43
+ checkElementCollapseState(localNav, localNavSearchVisibleClass);
44
+ checkSearchToggleIcon(searchIcon);
45
+ }
46
+ }
47
+
48
+ function toggleSearchDropdownOnWindowResize() {
49
+ window.addEventListener('resize', windowResizeHandler);
50
+ }
51
+
52
+ export default toggleSearchDropdownOnWindowResize;
@@ -0,0 +1,24 @@
1
+ /*
2
+ // Custom JS | written by https://github.com/wdzajicek
3
+ // © 2020 Kankakee Community College
4
+ // =================================================== */
5
+ import start from './campusAlertsSheetsAPI.js';
6
+ import getCachedResponse from './getCachedResponse.js';
7
+ import checkForPrefersReducedMotion from './checkForPrefersReducedMotion.js';
8
+
9
+ function alerts(Collapse) {
10
+ checkForPrefersReducedMotion();
11
+
12
+ // Checks if our cached alert is already in sessionStorage
13
+ if (window.sessionStorage.getItem('Alert-Content')) {
14
+ // If not, build the alert from a new Google API response
15
+ gapi.load('client', () => {
16
+ start(Collapse);
17
+ });
18
+ } else {
19
+ // Otherwise, build the alert from our cached response
20
+ getCachedResponse(Collapse);
21
+ }
22
+ }
23
+
24
+ export default alerts;
@@ -0,0 +1,83 @@
1
+ import '../../scss/kcc-theme.scss'; // Import scss file into webpack main entry-point for webpack compiled css
2
+
3
+ import Collapse from 'bootstrap/js/dist/collapse';
4
+
5
+ // function loadModule(...theArgs) {
6
+ // const len = theArgs.length;
7
+ // let module, defaultFn, path;
8
+
9
+ // module = theArgs[0];
10
+ // len == 2 ? path = theArgs[1] : len == 3 ? path = theArgs[2] : null;
11
+ // len == 2 ? defaultFn = module : defaultFn = theArgs[1];
12
+ // return import(`${path}${module}.js`).then(({ default: defaultFn }) => defaultFn() );
13
+ // }
14
+
15
+ window.addEventListener('load', async () => {
16
+
17
+ if (document.querySelector('[data-bs-toggle="dropdown"]')) {
18
+ // Dropdown does not need to be called to existing dropdown HTML markup work
19
+ const { default: Dropdown } = await import('bootstrap/js/dist/dropdown');
20
+ }
21
+
22
+ import('./alerts').then(({ default: alerts }) => alerts(Collapse));
23
+
24
+ import('../nav/nav').then(({ default: nav }) => nav());
25
+
26
+ if (document.getElementById('google_translate_element')) {
27
+ import('../../scss/translate.scss').then(() => {
28
+ import('./translate').then(({ default: translate }) => translate());
29
+ })
30
+ }
31
+
32
+ if (document.querySelector('.hero-slider__slider')) {
33
+ import('./wrapPowerText')
34
+ .then(({ default: wrapPowerText }) => wrapPowerText())
35
+ .then(() => {
36
+ import('./sliders').then(({ default: initSliders }) => initSliders())
37
+ })
38
+ }
39
+
40
+ if (document.querySelector('img[data-src]')) {
41
+ import('./lazyLoad').then(({ default: lazyLoad }) => lazyLoad());
42
+ }
43
+
44
+ if (document.getElementById('currentYear')) {
45
+ import('./footerDate')
46
+ .then(({ default: footerDate }) => footerDate());
47
+ }
48
+
49
+ import('./walkText').then(({ default: walkText }) => walkText(document.body));
50
+
51
+ if (document.querySelector('[data-bs-toggle="modal"]')) {
52
+ // This import enables modals in pages with modal HTML markup
53
+ const { default: Modal } = await import('bootstrap/js/dist/modal');
54
+ }
55
+
56
+ // loadModule('alerts','./').then(() => { // Get the campus alerts message & build it out
57
+ // loadModule('addClassToOpenNavbar', './');
58
+ // return;
59
+ // }).then(() => {
60
+ // if (document.getElementById('google_translate_element')) { // Check for elements used with Google Translate
61
+ // import('../../scss/translate.scss').then(() => { // Load custom styling for Google Translate conditionally
62
+ // loadModule('translate', 'watchForMenuClicks', './'); // Then load the custom translate JS for functionality
63
+ // });
64
+ // }
65
+ // }).then(() => {
66
+ // import('../nav/nav/nav').then(({ default: nav }) => nav()); // JS for custom navbar functionality
67
+ // });
68
+
69
+ // loadModule('wrapPowerText', './').then(() => {
70
+ // return loadModule('sliders', 'initSliders', './');
71
+ // }).then(() => {
72
+ // if (document.querySelector('img[data-src]')) {
73
+ // return loadModule('lazyLoad', './');
74
+ // }
75
+ // }).then(() => {
76
+ // return loadModule('footerDate', './');
77
+ // }).then(() => {
78
+ // import('./walkText').then(({default: walkText}) => {
79
+ // walkText(document.body); // walkText() needs an argument
80
+ // });
81
+ // });
82
+
83
+ });
@@ -0,0 +1,4 @@
1
+ import '../../scss/bootstrap.scss';
2
+
3
+ // This JS file only serves as an entry point for compiling a
4
+ // stylesheet for Bootstrap 5 (only the components/utilities we use).
@@ -0,0 +1,22 @@
1
+ /*
2
+ // Custom JS | written by https://github.com/wdzajicek
3
+ // © 2020 Kankakee Community College
4
+ // =================================================== */
5
+ // Modules' default function stores our Google Sheet response in sessionStorage to be retrieved later
6
+ // Each key gets set to a column in our data
7
+ // Each key's value gets set to the corresponding cell in the row below
8
+ // ====================================================================
9
+ function cacheResponse(response) { // response from Google API's spreadsheet.values.get() method
10
+ const values = response.result.values; // This is where the table's data is in a Sheets response in Sheets API V4
11
+ const [, headerRow, bodyRow] = values; // 1st row = instructions, 2nd row = header row, 3rd row = body row
12
+
13
+ for (let i = 0, len = bodyRow.length; i < len; i++ ) {
14
+ const cell = bodyRow[i];
15
+ const column = headerRow[i];
16
+
17
+ window.sessionStorage.setItem(column.replace(' ', '-'), cell);
18
+ }
19
+ //window.sessionStorage.clear();
20
+ }
21
+
22
+ export default cacheResponse;
@@ -0,0 +1,53 @@
1
+ /*
2
+ // Custom JS | written by https://github.com/wdzajicek
3
+ // © 2020 Kankakee Community College
4
+ // =================================================== */
5
+ // 1. Execute Google API call to grab Google Sheet data from:
6
+ // https://docs.google.com/spreadsheets/d/1plXBiZY5pVbhNT-mszxEuqCl4zy8wMnz9gXXbbT_yLs/edit#gid=0
7
+ // 2. Build & inject the alert message into the page
8
+ // 3. Run the `contentHashLink` module after alert has painted into DOM (and altered documents hight)
9
+ // 4. Cache the API response in sessionStorage
10
+ // =================================================== //
11
+ import createAlertsHtml from './createAlertsHtml.js';
12
+ import contentHashLink from './contentHashLink.js';
13
+ import cacheResponse from './cacheResponse.js';
14
+
15
+ const SHEET_KEY = '1plXBiZY5pVbhNT-mszxEuqCl4zy8wMnz9gXXbbT_yLs'; // Corresponds to the ID of the Google Sheet
16
+ const SHEET_TAB = 'Alerts'; // Corresponds to the tab of workbook: either 'Alerts' or 'Alerts Testing' unless you make a new one.
17
+ // const devSheetTab = 'ALERTS_TESTING';
18
+ const sheetParams = {
19
+ spreadsheetId: SHEET_KEY,
20
+ range: SHEET_TAB
21
+ // range: devSheetTab
22
+ }
23
+ const apiParams = { // This is configuration for API call with spreadsheets that are setup as readonly
24
+ 'apiKey': 'AIzaSyCEBsbXfFcdbkASlg-PodD1rT_Fe3Nw62A',
25
+ 'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/sheets/v4/rest']
26
+ };
27
+
28
+ function init(Collapse) {
29
+ gapi.client.init(apiParams).then(() => { // Executes an API request, and returns a Promise.
30
+ return gapi.client.sheets.spreadsheets.values.get(sheetParams)
31
+ }).then((response) => {
32
+ createAlertsHtml(response, Collapse); // Build the html & inject it into the DOM
33
+ return response;
34
+ }).then((response) => {
35
+ cacheResponse(response); // Cache the Google API response for subsequent page loads in the site
36
+ }, (err)=> {
37
+ console.error("Execute error", err);
38
+ contentHashLink(Collapse);
39
+ });
40
+ }
41
+
42
+ function start(Collapse) {
43
+ if ( ! document.getElementById('emergencyAlerts') )
44
+ return contentHashLink(Collapse);
45
+
46
+ init(Collapse);
47
+ //var t1 = performance.now();
48
+ //console.info("Call to 'init' took " + (t1 - t0) + " milliseconds.");
49
+ }
50
+ // Loads the JavaScript client library and invokes `start` afterwards.
51
+ // Usage:
52
+ // gapi.load('client', start);
53
+ export default start;
@@ -0,0 +1,17 @@
1
+ function checkForPrefersReduceMotion() {
2
+ const reducedMotionMediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
3
+ let reducedMotion = reducedMotionMediaQuery.matches ? true : false;
4
+
5
+ localStorage.setItem('userPrefersReducedMotion', reducedMotion);
6
+
7
+ reducedMotionMediaQuery.addEventListener('change', _e => {
8
+ if (reducedMotionMediaQuery.matches) {
9
+ reducedMotion = true;
10
+ } else {
11
+ reducedMotion = false;
12
+ }
13
+ localStorage.setItem('userPrefersReducedMotion', reducedMotion);
14
+ });
15
+ }
16
+
17
+ export default checkForPrefersReduceMotion;
@@ -0,0 +1,102 @@
1
+ // Custom JS to do cool stuff with BS accordions and tabs by manipulating URL hashes and query's
2
+ // EXAMPLE:
3
+ // https://<ORIGIN>/?id=course-withdrawals#tuition-payment-and-deadlines
4
+ // The above URL will:
5
+ // 1. Open the #tuition-payment-and-deadlines accordion if it exists
6
+ // 2. The ?id=course-withdrawals query will:
7
+ // - look inside the opened accordion for an element with the id 'course-withdrawals', and
8
+ // - scroll that matching element into the user's viewport (in this case it's a heading within that accordion card)
9
+ //
10
+ // This JS will allow us to link to a specific area of content in a page where a traditional hash link wouldn't work
11
+ // In this case hash links won't work because the element with he matching ID is "stuck" in a closed accordion or tab.
12
+ //
13
+ const idRegex = /^id=/g; // Lets just cache these reused regex's here
14
+ const queryStartRegex = /^\?/g;
15
+ const endingSlashRegex = /\/$/g;
16
+ const REDUCED_MOTION_STORAGE_KEY = 'userPrefersReducedMotion'; // This localStorage key is set by module: './checkForPrefersReducedMotion.js'
17
+ const scrollIntoViewOptionsObject = {
18
+ behavior: 'smooth',
19
+ block: 'center'
20
+ }
21
+ const reducedMotionscrollIntoViewOptionsObject = {
22
+ block: 'center'
23
+ }
24
+
25
+ function focusElement(el) {
26
+ const prefersReducedMotion = window.localStorage.getItem(REDUCED_MOTION_STORAGE_KEY);
27
+
28
+ prefersReducedMotion == 'true' ? el.scrollIntoView(reducedMotionscrollIntoViewOptionsObject) : el.scrollIntoView(scrollIntoViewOptionsObject);
29
+ return el.focus();
30
+ }
31
+
32
+ function processIdQuery(query, hash) {
33
+ let id = query.replace(idRegex, '');
34
+ const parentEl = document.querySelector(hash);
35
+ const heading = parentEl.querySelector(`#${id}`);
36
+
37
+ focusElement(heading);
38
+ }
39
+
40
+ function checkForQuery(query, hash) {
41
+ query.search(idRegex) !== -1 ?
42
+ processIdQuery(query, hash)
43
+ : null;
44
+ }
45
+
46
+ function findContentTarget(hash) {
47
+ const target = document.querySelector(hash);
48
+
49
+ focusElement(target);
50
+ }
51
+
52
+ async function checkForMatchingTabOrAccordion(hash, Collapse) {
53
+ if (document.querySelector(`.nav-tabs a[href="${hash}"]`)) { // Looks for a matching BS4 tab element
54
+ const { default: Tab } = await import('bootstrap/js/dist/tab');
55
+ let tab = document.querySelector(`.nav-tabs a[href="${hash}"]`);
56
+ const bsTab = new Tab(tab, { toggle: false });
57
+
58
+ tab.addEventListener('shown.bs.tab', () => {
59
+ if (window.location.search) {
60
+ checkForQuery(window.location.search.replace(queryStartRegex, ''), hash);
61
+ }
62
+ });
63
+ bsTab.show();
64
+ findContentTarget(`${hash}-label`); // You need to .scrollIntoView() & .focus() on the tab-label which is an <a href="...">. It won't work to do .scrollIntoView() and .focus() on the div
65
+ } else if (document.querySelector(`.accordion ${hash}.collapse`)) { // Looks for a matching BS4 collapse element
66
+ let card = document.querySelector(hash);
67
+ const bsCollapse = new Collapse(card, { toggle: false });
68
+
69
+ card.addEventListener('shown.bs.collapse', _e => {
70
+ if (window.location.search) {
71
+ checkForQuery(window.location.search.replace(queryStartRegex, ''), hash);
72
+ }
73
+ });
74
+ bsCollapse.show();
75
+ findContentTarget(hash);
76
+ }
77
+ }
78
+
79
+ function checkForHash(Collapse) {
80
+ if (window.location.hash) {
81
+ let hash = window.location.hash.replace(endingSlashRegex, '');
82
+
83
+ checkForMatchingTabOrAccordion(hash, Collapse);
84
+ }
85
+ return;
86
+ }
87
+
88
+ function initContentHashLink(Collapse) {
89
+ checkForHash(Collapse);
90
+ window.addEventListener('hashchange', _e => {
91
+ checkForHash(Collapse);
92
+ }, false);
93
+ }
94
+
95
+ function contentHashLink(Collapse) {
96
+ if (!document.querySelector('#accordion') && !document.querySelector('.nav.nav-tabs'))
97
+ return;
98
+
99
+ initContentHashLink(Collapse);
100
+ }
101
+
102
+ export default contentHashLink;
@@ -0,0 +1,46 @@
1
+ /*
2
+ // Custom JS | written by https://github.com/wdzajicek
3
+ // © 2020 Kankakee Community College
4
+ // =================================================== */
5
+ // JS module to build alert message using data from Google Sheets API v4
6
+ //
7
+ // This exported module requires you pass it's default-function the `response` object from the API call, as the only argument
8
+ import contentHashLink from './contentHashLink.js';
9
+ import parseMarkdownToHTML from './parseMarkdownToHTML.js'; // Parses a simplified markdown into html & creates the paragraph el's with appropriate class
10
+
11
+ function injectAlert(target, alert) {
12
+ target.innerHTML = alert;
13
+ return target.classList.add('position__offset-alert--visible');
14
+ }
15
+
16
+ function createAlertsHtml(response, Collapse) { // Incoming response from our Google Sheet via the Sheets API
17
+ // This is where the cell values hide in the response object from the Google API.
18
+
19
+ let [visibility, allPages, content, expire, start, end] = response.result.values[2]; // The 3rd row has our table's data
20
+ if (visibility === 'FALSE') return contentHashLink(Collapse); // Predefined dropdown options in the Sheet are `'TRUE'` & `'FALSE'`
21
+
22
+ const alertDiv = document.getElementById('emergencyAlerts'); // This targets an element built into the DOM that we inject everything into.
23
+ let d = new Date;
24
+ let s = new Date(start);
25
+ let e = new Date(end);
26
+ const alertIsActive = expire === 'FALSE' || expire === 'TRUE' && s.getTime() <= d.getTime() && e.getTime() > d.getTime();
27
+ const indexPageOnly = allPages === 'TRUE' || allPages === 'FALSE' && window.location.pathname == '/';
28
+ let alert = `
29
+ <div class="container">
30
+ <div class="row">
31
+ <div class="col">
32
+ <div class="alert alert-warning">
33
+ ${parseMarkdownToHTML(content)}
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </div>`;
38
+
39
+ [d,s,e].map(d => d.setHours(0, 0, 0, 0));
40
+ if (alertIsActive && indexPageOnly) {
41
+ injectAlert(alertDiv, alert);
42
+ }
43
+ return contentHashLink(Collapse);
44
+ }
45
+
46
+ export default createAlertsHtml;
@@ -0,0 +1,13 @@
1
+ // Update copyright year if its not current
2
+ const yearSpan = document.getElementById('currentYear');
3
+
4
+ function footerDate() {
5
+ const d = new Date();
6
+ const fullYear = d.getFullYear();
7
+
8
+ if (yearSpan.innerHTML === `${fullYear}`)
9
+ return;
10
+
11
+ yearSpan.innerHTML = fullYear;
12
+ }
13
+ export default footerDate;