@pimaonline/pimaonline-themepack 2.2.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. package/LICENSE.md +30 -0
  2. package/README.md +69 -111
  3. package/dist/css/main.css +1 -1
  4. package/dist/css/themes/ait/styles.css +1 -0
  5. package/dist/css/themes/art/styles.css +1 -1
  6. package/dist/css/themes/business/styles.css +1 -1
  7. package/dist/css/themes/cards/styles.css +1 -1
  8. package/dist/css/themes/cda/styles.css +1 -1
  9. package/dist/css/themes/communication/styles.css +1 -1
  10. package/dist/css/themes/culinary/styles.css +1 -1
  11. package/dist/css/themes/ece/styles.css +1 -1
  12. package/dist/css/themes/ecn/styles.css +1 -0
  13. package/dist/css/themes/eng/styles.css +1 -0
  14. package/dist/css/themes/fsc/styles.css +1 -0
  15. package/dist/css/themes/geography/styles.css +1 -1
  16. package/dist/css/themes/geology/styles.css +1 -1
  17. package/dist/css/themes/health-it/styles.css +1 -1
  18. package/dist/css/themes/history/styles.css +1 -1
  19. package/dist/css/themes/hrm/styles.css +1 -0
  20. package/dist/css/themes/hrs/styles.css +1 -1
  21. package/dist/css/themes/journalism/styles.css +1 -1
  22. package/dist/css/themes/lang/styles.css +1 -1
  23. package/dist/css/themes/machine/styles.css +1 -1
  24. package/dist/css/themes/math/styles.css +1 -1
  25. package/dist/css/themes/minimalist/styles.css +1 -1
  26. package/dist/css/themes/music/styles.css +1 -1
  27. package/dist/css/themes/philosophy/styles.css +1 -1
  28. package/dist/css/themes/resort/styles.css +1 -1
  29. package/dist/css/themes/ss/styles.css +1 -0
  30. package/dist/css/themes/university/styles.css +1 -1
  31. package/dist/css/themes/vet/styles.css +1 -1
  32. package/dist/css/themes/welding/styles.css +1 -1
  33. package/dist/css/themes/writing/styles.css +1 -1
  34. package/dist/img/theme-images/ecn/arrow-2.svg +4 -0
  35. package/dist/img/theme-images/ecn/arrow.svg +4 -0
  36. package/dist/img/theme-images/ecn/point.svg +3 -0
  37. package/dist/img/theme-images/eng/button-bkg.svg +178 -0
  38. package/dist/img/theme-images/eng/halftone-banner.svg +1 -0
  39. package/dist/img/theme-images/eng/halftone.svg +177 -0
  40. package/dist/img/theme-images/eng/long-button-bkg.svg +353 -0
  41. package/dist/img/theme-images/fsc/bottomwave-pinkred.svg +17 -0
  42. package/dist/img/theme-images/fsc/bottomwave-redorange.svg +17 -0
  43. package/dist/img/theme-images/fsc/bottomwave-yellow.svg +17 -0
  44. package/dist/img/theme-images/fsc/bottomwave-yelloworange.svg +17 -0
  45. package/dist/img/theme-images/fsc/fire-icon.png +0 -0
  46. package/dist/img/theme-images/fsc/mainwave-pinkred.svg +17 -0
  47. package/dist/img/theme-images/fsc/mainwave-redorange.svg +17 -0
  48. package/dist/img/theme-images/fsc/mainwave-yellow.svg +17 -0
  49. package/dist/img/theme-images/fsc/mainwave-yelloworange.svg +17 -0
  50. package/dist/img/theme-images/lang/country-flags/argentina_flag.png +0 -0
  51. package/dist/img/theme-images/lang/country-flags/bolivia_flag.png +0 -0
  52. package/dist/img/theme-images/lang/country-flags/chile_flag.png +0 -0
  53. package/dist/img/theme-images/lang/country-flags/colombia_flag.png +0 -0
  54. package/dist/img/theme-images/lang/country-flags/costa-rica_flag.png +0 -0
  55. package/dist/img/theme-images/lang/country-flags/cuba_flag.png +0 -0
  56. package/dist/img/theme-images/lang/country-flags/dominican-republic_flag.png +0 -0
  57. package/dist/img/theme-images/lang/country-flags/ecuador_flag.png +0 -0
  58. package/dist/img/theme-images/lang/country-flags/el-salvador_flag.png +0 -0
  59. package/dist/img/theme-images/lang/country-flags/guatemala_flag.png +0 -0
  60. package/dist/img/theme-images/lang/country-flags/honduras_flag.png +0 -0
  61. package/dist/img/theme-images/lang/country-flags/mexico_flag.png +0 -0
  62. package/dist/img/theme-images/lang/country-flags/nicaragua_flag.png +0 -0
  63. package/dist/img/theme-images/lang/country-flags/panama_flag.png +0 -0
  64. package/dist/img/theme-images/lang/country-flags/paraguay_flag.png +0 -0
  65. package/dist/img/theme-images/lang/country-flags/peru_flag.png +0 -0
  66. package/dist/img/theme-images/lang/country-flags/puerto-rice_flag.png +0 -0
  67. package/dist/img/theme-images/lang/country-flags/spain_flag.png +0 -0
  68. package/dist/img/theme-images/lang/country-flags/uruguay_flag.png +0 -0
  69. package/dist/img/theme-images/lang/country-flags/venezuela_flag.png +0 -0
  70. package/dist/img/theme-images/music/half_note.svg +5 -5
  71. package/dist/img/theme-images/resort/flourish-left.svg +32 -32
  72. package/dist/img/theme-images/resort/flourish-main.svg +37 -37
  73. package/dist/img/theme-images/resort/flourish-right.svg +31 -31
  74. package/dist/img/theme-images/resort/separator.svg +15 -15
  75. package/dist/img/theme-images/ss/blockquote.svg +3 -0
  76. package/dist/img/theme-images/ss/list-style.svg +3 -0
  77. package/dist/img/theme-images/ss/main-large-blob.svg +3 -0
  78. package/dist/img/theme-images/ss/main-small-blob.svg +3 -0
  79. package/dist/img/theme-images/ss/small-blob.svg +3 -0
  80. package/dist/img/theme-images/ss/tall-blob.svg +3 -0
  81. package/dist/img/theme-images/widgets/separator.svg +17 -17
  82. package/dist/js/jumpTo.js +3 -3
  83. package/dist/js/scripts.js +326 -326
  84. package/dist/js/scripts2.js +541 -345
  85. package/dist/plugins/fancybox/fancybox-example.html +51 -51
  86. package/dist/plugins/fancybox/fancybox.css +72 -72
  87. package/dist/plugins/fancybox/helpers/jquery.fancybox-buttons.css +97 -97
  88. package/dist/plugins/fancybox/helpers/jquery.fancybox-buttons.js +122 -122
  89. package/dist/plugins/fancybox/helpers/jquery.fancybox-media.js +201 -201
  90. package/dist/plugins/fancybox/helpers/jquery.fancybox-thumbs.css +54 -54
  91. package/dist/plugins/fancybox/helpers/jquery.fancybox-thumbs.js +165 -165
  92. package/dist/plugins/fancybox/jquery.fancybox.css +274 -274
  93. package/dist/plugins/fancybox/jquery.fancybox.js +2018 -2018
  94. package/dist/plugins/fancybox/jquery.fancybox.pack.js +46 -46
  95. package/dist/plugins/flashcards/README.md +135 -135
  96. package/dist/plugins/flashcards/config.rb +24 -24
  97. package/dist/plugins/flashcards/css/style.css +215 -215
  98. package/dist/plugins/flashcards/flashcards-example.html +65 -65
  99. package/dist/plugins/flashcards/index.html +90 -90
  100. package/dist/plugins/flashcards/js/flash_cards.min.js +11 -11
  101. package/dist/plugins/flashcards/js/plugins/flash_cards.js +62 -62
  102. package/dist/plugins/flashcards/js/plugins/jquery.cycle.js +1147 -1147
  103. package/dist/plugins/flashcards/js/vendor/jquery-1.7.2.js +9404 -9404
  104. package/dist/plugins/flashcards/js/vendor/jquery-1.7.2.min.js +3 -3
  105. package/dist/plugins/flashcards/js/vendor/modernizr-2.5.3.min.js +3 -3
  106. package/dist/plugins/flashcards/resources/fonts/flash_cards/flash_cards.svg +20 -20
  107. package/dist/plugins/floating-particles/floating-particles.js +67 -67
  108. package/dist/plugins/font-awesome-icons/webfonts/fa-brands-400.svg +3570 -3570
  109. package/dist/plugins/font-awesome-icons/webfonts/fa-regular-400.svg +803 -803
  110. package/dist/plugins/font-awesome-icons/webfonts/fa-solid-900.svg +4700 -4700
  111. package/dist/plugins/global-homepage-overrides/global-homepage-overrides.css +539 -539
  112. package/dist/plugins/global-homepage-overrides/global-homepage-overrides.html +18 -18
  113. package/dist/plugins/global-homepage-overrides/global-homepage-overrides.js +52 -52
  114. package/dist/plugins/preview-banner/preview-banner.css +125 -125
  115. package/dist/plugins/preview-banner/preview-banner.html +17 -17
  116. package/dist/plugins/preview-banner/preview-banner.js +56 -56
  117. package/package.json +39 -40
  118. package/dist/img/theme-images/history/hisribbonv-02.png +0 -0
  119. package/dist/img/theme-images/img/bouncingball.png +0 -0
@@ -1,345 +1,541 @@
1
- /// @description Main JS file for PimaOnline Themepack
2
- /// @dependencies jQuery 3.3.1 or later
3
-
4
- const courseBody = document.querySelector("body");
5
- const contentWrapper = document.querySelector("#content-wrapper");
6
- const secondColumn = document.querySelector("#second-column");
7
- const thirdColumn = document.querySelector("#third-column");
8
- const columnWidget = document.querySelector("#column-widget");
9
- const videoWrapper = document.querySelector("#video-wrapper");
10
- const rolePres = document.querySelectorAll('[role="presentation"]');
11
- const imageGallery = document.querySelector(".image-gallery");
12
- const vocabListWidget = document.querySelector("dl.vocab-list");
13
- const vocabTerms = document.querySelectorAll("dl.vocab-list dt");
14
- const vocabDefs = document.querySelectorAll("dl.vocab-list dd");
15
- const vocabCloseBtns = document.querySelectorAll("dl.vocab-list button");
16
- const vocabLists = document.querySelectorAll("dl[class^='vocab-list']");
17
- const mediaContainers = document.querySelectorAll(".media-container");
18
- const tabsWidgets = document.querySelectorAll(".tabs");
19
-
20
- // Grid
21
- const addGrid = () => {
22
- if (secondColumn && thirdColumn) {
23
- courseBody.id = "three-column";
24
- } else if (secondColumn && !columnWidget) {
25
- courseBody.id = "two-column";
26
- } else if (columnWidget) {
27
- courseBody.id = "two-col-widget";
28
- } else if (videoWrapper) {
29
- courseBody.id = "video-grid";
30
- } else {
31
- courseBody.id = "one-column";
32
- }
33
- };
34
- addGrid();
35
-
36
- // JS to add role and aria-label to content-wrapper, second-column, and third-column
37
- const addAria = () => {
38
- contentWrapper.setAttribute("role", "main");
39
- if (secondColumn) {
40
- secondColumn.setAttribute("role", "region");
41
- secondColumn.setAttribute("aria-label", "Second column");
42
- }
43
- if (thirdColumn) {
44
- thirdColumn.setAttribute("role", "region");
45
- thirdColumn.setAttribute("aria-label", "Third column");
46
- }
47
- };
48
- addAria();
49
-
50
- // Clean up HTML
51
- const cleanMarkup = () => {
52
- // Remove role="presentation" attr from any element that has it
53
- if (rolePres) {
54
- rolePres.forEach((roleElem) => roleElem.removeAttribute("role"));
55
- }
56
- // Set functino to remove atrributes from elements
57
- const discardAttributes = (element, ...attributes) => {
58
- attributes.forEach((attribute) => element.removeAttribute(attribute));
59
- }
60
- // Remove attributes from tables
61
- const tableElems = document.querySelectorAll("table, thead, tbody, tr, th, td");
62
- tableElems.forEach((elem) => {
63
- discardAttributes(elem, "cellspacing", "cellpadding", "width", "style");
64
- });
65
- };
66
- cleanMarkup();
67
-
68
- // Helper JS for Responsive Tables
69
- const initResponsiveTables = () => {
70
- const tables = document.querySelectorAll(".display, .display-lg")
71
- for (let table = 0; table < tables.length; table++) {
72
- let headertext = [],
73
- headers = tables[table].querySelectorAll(".display table th, table.display th, .display-lg table th, table.display-lg th"),
74
- tablebody = tables[table].querySelector(".display table tbody, table.display tbody, .display-lg table tbody, table.display-lg tbody");
75
- for (let header = 0; header < headers.length; header++) {
76
- let current = headers[header];
77
- headertext.push(current.textContent.replace(/\r?\n|\r/, ""));
78
- }
79
- for (let y = 0, row; row = tablebody.rows[y]; y++) {
80
- for (let j = 0, col; col = row.cells[j]; j++) {
81
- col.setAttribute("data-th", headertext[j]);
82
- }
83
- }
84
- }
85
- }
86
- initResponsiveTables();
87
-
88
- // This is called by anchor links via onlick="" in the HTML
89
- // Added because default anchor links don't work on all browsers using D2L
90
- const jumpTo = (anchor) => {
91
- document.getElementById(anchor).scrollIntoView();
92
- }
93
-
94
- // Image gallery
95
- const callImageGallery = () => {
96
- // Create link element with font-awesome cdn and append it to the <head>
97
- const docHead = document.querySelector("head");
98
- const fontAwesomeCdn = document.createElement("link");
99
- fontAwesomeCdn.setAttribute("rel", "stylesheet");
100
- fontAwesomeCdn.setAttribute("href", "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css");
101
- docHead.appendChild(fontAwesomeCdn);
102
- // Begin image gallery
103
- const imgGalleries = document.querySelectorAll(".image-gallery"),
104
- imgBoxes = document.querySelectorAll(".image-box"),
105
- modalBoxContent = `<div class="modal-box invisible">
106
- <div class="gallery-overlay"></div>
107
- <figure class="modal-box--image"><i class="fa-solid fa-x close-img"></i> <img src="#" alt="image here" /><figcaption class="img-caption"></figcaption></figure>
108
- </div>
109
- <button class="hide-gallery">Hide</button>`;
110
-
111
- // createModalBox.innerHTML = modalBoxContent;
112
- for (let imgGallery = 0; imgGallery < imgGalleries.length; imgGallery++) {
113
- imgGalleries[imgGallery].insertAdjacentHTML("afterbegin", modalBoxContent);
114
- }
115
-
116
- if (document.querySelector(".modal-box")) {
117
- const overlay = document.querySelector(".gallery-overlay"),
118
- modalBox = document.querySelector(".modal-box"),
119
- modalImg = document.querySelector(".modal-box--image img"),
120
- modalCaption = document.querySelector(".img-caption"),
121
- closeImg = document.querySelector(".close-img");
122
-
123
- for (let imgBox = 0; imgBox < imgBoxes.length; imgBox++) {
124
- imgBoxes[imgBox].onclick = function () {
125
- modalBox.classList.remove("invisible");
126
- let imgSrc = this.querySelector("img").src;
127
- modalImg.src = imgSrc;
128
- let imgCaption = this.querySelector("img").alt;
129
- modalCaption.innerHTML = imgCaption;
130
- }
131
-
132
- // Make images tab-able
133
- imgBoxes[imgBox].setAttribute("tabindex", "0");
134
- imgBoxes[imgBox].addEventListener("keydown", function (enter) {
135
- if (enter.key === "Enter") {
136
- modalBox.classList.remove("invisible");
137
- let imgSrc = this.querySelector("img").src;
138
- modalImg.src = imgSrc;
139
- let imgCaption = this.querySelector("img").alt;
140
- modalCaption.innerHTML = imgCaption;
141
- }
142
- })
143
- }
144
- overlay.onclick = () => {
145
- modalBox.classList.add("invisible");
146
- }
147
- window.onkeydown = (esc) => {
148
- if (esc.keyCode === 27) {
149
- modalBox.classList.add("invisible");
150
- }
151
- }
152
-
153
- closeImg.onclick = () => {
154
- modalBox.classList.add("invisible");
155
- }
156
- const hideGalleries = document.querySelectorAll(".hide-gallery");
157
- for (let hideGallery = 0; hideGallery < hideGalleries.length; hideGallery++) {
158
- hideGalleries[hideGallery].addEventListener("click", () => {
159
- hideGalleries[hideGallery].nextElementSibling.classList.toggle("invisible");
160
- if (hideGalleries[hideGallery].innerHTML === "Hide") {
161
- hideGalleries[hideGallery].innerHTML = "Show";
162
- } else {
163
- hideGalleries[hideGallery].innerHTML = "Hide";
164
- }
165
- });
166
- }
167
- };
168
- }
169
- if (imageGallery) {
170
- callImageGallery();
171
- }
172
-
173
- // Vocab list widget
174
- const callVocabList = () => {
175
- if (vocabCloseBtns) {
176
- for(let btn = 0; btn < vocabCloseBtns.length; btn++) {
177
- vocabCloseBtns[btn].addEventListener("click", () => {
178
- for(let node = 0; node < vocabLists[btn].children.length;node++) {
179
- if(vocabLists[btn].children[node].tagName == "DD") {
180
- vocabLists[btn].children[node].removeAttribute("style");
181
- }
182
- if(vocabLists[btn].children[node].tagName == "DT") {
183
- vocabLists[btn].children[node].removeAttribute("class");
184
- }
185
- }});
186
- }
187
- }
188
- for (let activeTerm = 0; activeTerm < vocabTerms.length; activeTerm++) {
189
- vocabTerms[activeTerm].addEventListener("click", function() {
190
- this.classList.toggle("active");
191
- let termPanel = this.nextElementSibling;
192
- if (termPanel.style.display === "block") { termPanel.removeAttribute("style"); }
193
- else { termPanel.style.display = "block"; }
194
- });
195
-
196
- vocabTerms[activeTerm].addEventListener("keydown", function(e) {
197
- if(e.key === "Enter") {
198
- this.classList.toggle("active");
199
- let termPanel = this.nextElementSibling;
200
- if (termPanel.style.display === "block") { termPanel.removeAttribute("style"); }
201
- else { termPanel.style.display = "block"; }
202
- }
203
- });
204
- }
205
- }
206
- if (vocabListWidget) {callVocabList();}
207
-
208
- // Media Container
209
- const addMediaContainersAria = () => {
210
- mediaContainers.forEach((eachContainer, index) => {
211
- // loopID: find the current index value, convert it to its letter equivalent, then convert to lowercase
212
- let loopId = String.fromCharCode(index + 65).toLowerCase();
213
- let mediaObject = eachContainer.querySelector(".media-object");
214
- let iframe = mediaObject.firstElementChild;
215
- let mediaInfo = mediaObject.nextElementSibling;
216
-
217
- // If element DOES NOT have "aria-describedby" && it DOES have a sibling element.
218
- if (!iframe.hasAttribute("aria-describedby") && mediaInfo != null) {
219
- iframe.setAttribute("aria-describedby", `${loopId}`);
220
- mediaInfo.id = `${[loopId]}`;
221
- }
222
- });
223
- }
224
- if (mediaContainers) {addMediaContainersAria();}
225
-
226
- //Tabs Widget
227
- const callTabsWidget = () => {
228
-
229
- let tabsWidgetsNum = 0;
230
-
231
- tabsWidgets.forEach((tab,index) => {
232
- let tabInputs = tab.querySelectorAll("input")
233
- let tabLabels = tab.querySelectorAll("label")
234
- let tabDivs = tab.querySelectorAll("div")
235
-
236
- let groupNum = index + 1;
237
-
238
- //Add region and aria-label to parent div
239
- tab.setAttribute("role", "region");
240
- tab.setAttribute("aria-label", `tab group ${groupNum}`)
241
-
242
- for(tabIndex = 0; tabIndex < tabInputs.length; tabIndex++) {
243
-
244
- let tabNum = tabsWidgetsNum + 1;
245
-
246
- //Add class, id, name, and aria-described by for inputs
247
- tabInputs[tabIndex].classList.add("tab-input");
248
- tabInputs[tabIndex].setAttribute("type", "radio")
249
- tabInputs[tabIndex].setAttribute("id", `tab${tabNum}`);
250
- tabInputs[tabIndex].setAttribute("name", `hint-group-${groupNum}` )
251
- tabInputs[tabIndex].setAttribute("aria-describedby", `tabHeading${tabNum}`)
252
- //Add class and for for labels
253
- tabLabels[tabIndex].classList.add("tab-header");
254
- tabLabels[tabIndex].setAttribute("for", `tab${tabNum}`)
255
- //Add class, tabindex, and id for divs
256
- tabDivs[tabIndex].classList.add("tab-panel")
257
- tabDivs[tabIndex].setAttribute("tabindex", 0)
258
- tabDivs[tabIndex].setAttribute("id", `tabHeading${tabNum}`)
259
- //Add attributes for hide tab
260
- if(tabIndex + 1 == tabInputs.length) {
261
- tabLabels[tabIndex].classList.add("hide-tab")
262
- tabInputs[tabIndex].checked = true;
263
- tabDivs[tabIndex].classList.add("hide-panel")
264
- }
265
- tabsWidgetsNum++;
266
- }
267
- })
268
- }
269
- if (tabsWidgets) {callTabsWidget();}
270
-
271
- // Toggle footnotes
272
- const toggleBtns = document.querySelectorAll(".toggle-btn, .toggle-footnotes");
273
-
274
- if (document.querySelector(".toggle-btn") || document.querySelector(".toggle-footnotes")) {
275
- for (let toggleBtn = 0; toggleBtn < toggleBtns.length; toggleBtn++) {
276
- // Add tabindex
277
- toggleBtns[toggleBtn].setAttribute("tabindex", "0");
278
-
279
- // Show/hide on click
280
- toggleBtns[toggleBtn].addEventListener("click", () => {
281
- toggleBtns[toggleBtn].nextElementSibling.classList.toggle("show");
282
- })
283
-
284
- // Show/hide on enter for users who use tab
285
- toggleBtns[toggleBtn].addEventListener("keydown", (enter) => {
286
- if (enter.keyCode === 13) {
287
- toggleBtns[toggleBtn].nextElementSibling.classList.toggle("show");
288
- }
289
- })
290
- }
291
- }
292
-
293
- // Change footnotes from 'show' to 'hide'
294
- const footnotes = document.querySelector(".toggle-footnotes");
295
-
296
- if(footnotes) {
297
- footnotes.addEventListener("click", () => {
298
- footnotes.innerHTML = (footnotes.innerHTML === "[Show Footnotes]") ? "[Hide Footnotes]" : "[Show Footnotes]";
299
- })
300
- }
301
-
302
- // Animated border for HRS theme
303
- const hrsBorders = document.querySelectorAll(".hrs-border");
304
-
305
- if(hrsBorders) {
306
- for (let hrsBorder = 0; hrsBorder < hrsBorders.length; hrsBorder++) {
307
- const callAnimateBorder = new IntersectionObserver(entries => {
308
- // Loop over the entries
309
- entries.forEach(entry => {
310
- // If the element is visible
311
- if (entry.isIntersecting) {
312
- // Add the animation class
313
- entry.target.classList.add('animate-border');
314
- }
315
- });
316
- });
317
- callAnimateBorder.observe(hrsBorders[hrsBorder]);
318
- }
319
- };
320
-
321
- // Call function with jQuery scripts
322
- const callJquery = () => {
323
- // Toggle Button's Arrow Right Points Down on Click
324
- $('.arrow-right').on('click', function () {
325
- $(this).toggleClass('arrow-down');
326
- });
327
- // TOOLTIP
328
- // Allows Screen readers to toggle a tooltip on click and to say if the tooltip is collapsed or expanded.
329
- $(".tooltip").click(function () {
330
- $(this).children(".tip-hover").toggle();
331
- if ($(this).children(".tip-hover").is(':visible')) {
332
- $(this).attr('aria-expanded', 'true');
333
- $(this).removeClass('hidden');
334
- } else {
335
- $(this).attr('aria-expanded', 'false');
336
- $(this).addClass('hidden');
337
- }
338
- });
339
- let start = 999;
340
- $('.tooltip').each(function (i) {
341
- $(this).css('z-index', start--);
342
- });
343
- $(".tooltip .video-container").parent().css("width", "450px");
344
- }
345
- callJquery();
1
+ /// @description Main JS file for PimaOnline Themepack
2
+ /// @dependencies jQuery 3.3.1 or later
3
+
4
+ const courseBody = document.querySelector("body");
5
+ const contentWrapper = document.querySelector("#content-wrapper");
6
+ const secondColumn = document.querySelector("#second-column");
7
+ const thirdColumn = document.querySelector("#third-column");
8
+ const columnWidget = document.querySelector("#column-widget");
9
+ const videoWrapper = document.querySelector("#video-wrapper");
10
+ const rolePres = document.querySelectorAll('[role="presentation"]');
11
+ const imageGallery = document.querySelector(".image-gallery");
12
+ const vocabListWidget = document.querySelector("dl.vocab-list");
13
+ const vocabTerms = document.querySelectorAll("dl.vocab-list dt");
14
+ const vocabDefs = document.querySelectorAll("dl.vocab-list dd");
15
+ const vocabCloseBtns = document.querySelectorAll("dl.vocab-list button");
16
+ const vocabLists = document.querySelectorAll("dl[class^='vocab-list']");
17
+ const mediaContainers = document.querySelectorAll(".media-container");
18
+ const tabsWidgets = document.querySelectorAll(".tabs");
19
+ const h5pIframes = document.querySelectorAll("iframe");
20
+ const docHead = document.querySelector("head");
21
+ const h5pResizerExists = docHead.querySelector("script[src='https://pima.h5p.com/js/h5p-resizer.js']");
22
+ const h5pResizer = document.createElement("script");
23
+ const focusReaderTooltipText = "Highlight text as you scroll";
24
+
25
+ // Grid
26
+ const addGrid = () => {
27
+ if (secondColumn && thirdColumn) {
28
+ courseBody.id = "three-column";
29
+ } else if (secondColumn && !columnWidget) {
30
+ courseBody.id = "two-column";
31
+ } else if (columnWidget) {
32
+ courseBody.id = "two-col-widget";
33
+ } else if (videoWrapper) {
34
+ courseBody.id = "video-grid";
35
+ } else {
36
+ courseBody.id = "one-column";
37
+ }
38
+ };
39
+ addGrid();
40
+
41
+ // JS to add role and aria-label to content-wrapper, second-column, and third-column
42
+ const addAria = () => {
43
+ contentWrapper.setAttribute("role", "main");
44
+ if (secondColumn) {
45
+ secondColumn.setAttribute("role", "region");
46
+ secondColumn.setAttribute("aria-label", "Second column");
47
+ }
48
+ if (thirdColumn) {
49
+ thirdColumn.setAttribute("role", "region");
50
+ thirdColumn.setAttribute("aria-label", "Third column");
51
+ }
52
+ };
53
+ addAria();
54
+
55
+ // Clean up HTML
56
+ const cleanMarkup = () => {
57
+ // Remove role="presentation" attr from any element that has it
58
+ if (rolePres) {
59
+ rolePres.forEach((roleElem) => roleElem.removeAttribute("role"));
60
+ }
61
+ // Set functino to remove atrributes from elements
62
+ const discardAttributes = (element, ...attributes) => {
63
+ attributes.forEach((attribute) => element.removeAttribute(attribute));
64
+ }
65
+ // Remove attributes from tables
66
+ const tableElems = document.querySelectorAll("table, thead, tbody, tr, th, td");
67
+ tableElems.forEach((elem) => {
68
+ discardAttributes(elem, "cellspacing", "cellpadding", "width", "style");
69
+ });
70
+ };
71
+ cleanMarkup();
72
+
73
+ // Helper JS for Responsive Tables
74
+ const initResponsiveTables = () => {
75
+ const tables = document.querySelectorAll(".display, .display-lg")
76
+ for (let table = 0; table < tables.length; table++) {
77
+ let headertext = [],
78
+ headers = tables[table].querySelectorAll(".display table th, table.display th, .display-lg table th, table.display-lg th"),
79
+ tablebody = tables[table].querySelector(".display table tbody, table.display tbody, .display-lg table tbody, table.display-lg tbody");
80
+ for (let header = 0; header < headers.length; header++) {
81
+ let current = headers[header];
82
+ headertext.push(current.textContent.replace(/\r?\n|\r/, ""));
83
+ }
84
+ for (let y = 0, row; row = tablebody.rows[y]; y++) {
85
+ for (let j = 0, col; col = row.cells[j]; j++) {
86
+ col.setAttribute("data-th", headertext[j]);
87
+ }
88
+ }
89
+ }
90
+ }
91
+ initResponsiveTables();
92
+
93
+ // This is called by anchor links via onlick="" in the HTML
94
+ // Added because default anchor links don't work on all browsers using D2L
95
+ const jumpTo = (anchor) => {
96
+ document.getElementById(anchor).scrollIntoView();
97
+ }
98
+
99
+ // Image gallery
100
+ const callImageGallery = () => {
101
+ // Create link element with font-awesome cdn and append it to the <head>
102
+ const docHead = document.querySelector("head");
103
+ const fontAwesomeCdn = document.createElement("link");
104
+ fontAwesomeCdn.setAttribute("rel", "stylesheet");
105
+ fontAwesomeCdn.setAttribute("href", "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css");
106
+ docHead.appendChild(fontAwesomeCdn);
107
+ // Begin image gallery
108
+ const imgGalleries = document.querySelectorAll(".image-gallery"),
109
+ imgBoxes = document.querySelectorAll(".image-box"),
110
+ modalBoxContent = `<div class="modal-box invisible">
111
+ <div class="gallery-overlay"></div>
112
+ <figure class="modal-box--image"><i class="fa-solid fa-x close-img"></i> <img src="#" alt="image here" /><figcaption class="img-caption"></figcaption></figure>
113
+ </div>
114
+ <button class="hide-gallery">Hide</button>`;
115
+
116
+ // createModalBox.innerHTML = modalBoxContent;
117
+ for (let imgGallery = 0; imgGallery < imgGalleries.length; imgGallery++) {
118
+ imgGalleries[imgGallery].insertAdjacentHTML("afterbegin", modalBoxContent);
119
+ }
120
+
121
+ if (document.querySelector(".modal-box")) {
122
+ const overlay = document.querySelector(".gallery-overlay"),
123
+ modalBox = document.querySelector(".modal-box"),
124
+ modalImg = document.querySelector(".modal-box--image img"),
125
+ modalCaption = document.querySelector(".img-caption"),
126
+ closeImg = document.querySelector(".close-img");
127
+
128
+ for (let imgBox = 0; imgBox < imgBoxes.length; imgBox++) {
129
+ imgBoxes[imgBox].onclick = function () {
130
+ modalBox.classList.remove("invisible");
131
+ let imgSrc = this.querySelector("img").src;
132
+ modalImg.src = imgSrc;
133
+ let imgCaption = this.querySelector("img").alt;
134
+ modalCaption.innerHTML = imgCaption;
135
+ }
136
+
137
+ // Make images tab-able
138
+ imgBoxes[imgBox].setAttribute("tabindex", "0");
139
+ imgBoxes[imgBox].addEventListener("keydown", function (enter) {
140
+ if (enter.key === "Enter") {
141
+ modalBox.classList.remove("invisible");
142
+ let imgSrc = this.querySelector("img").src;
143
+ modalImg.src = imgSrc;
144
+ let imgCaption = this.querySelector("img").alt;
145
+ modalCaption.innerHTML = imgCaption;
146
+ }
147
+ })
148
+ }
149
+ overlay.onclick = () => {
150
+ modalBox.classList.add("invisible");
151
+ }
152
+ window.onkeydown = (esc) => {
153
+ if (esc.keyCode === 27) {
154
+ modalBox.classList.add("invisible");
155
+ }
156
+ }
157
+
158
+ closeImg.onclick = () => {
159
+ modalBox.classList.add("invisible");
160
+ }
161
+ const hideGalleries = document.querySelectorAll(".hide-gallery");
162
+ for (let hideGallery = 0; hideGallery < hideGalleries.length; hideGallery++) {
163
+ hideGalleries[hideGallery].addEventListener("click", () => {
164
+ hideGalleries[hideGallery].nextElementSibling.classList.toggle("invisible");
165
+ if (hideGalleries[hideGallery].innerHTML === "Hide") {
166
+ hideGalleries[hideGallery].innerHTML = "Show";
167
+ } else {
168
+ hideGalleries[hideGallery].innerHTML = "Hide";
169
+ }
170
+ });
171
+ }
172
+ };
173
+ }
174
+ if (imageGallery) {
175
+ callImageGallery();
176
+ }
177
+
178
+ // Vocab list widget
179
+ const callVocabList = () => {
180
+ if (vocabCloseBtns) {
181
+ for (let btn = 0; btn < vocabCloseBtns.length; btn++) {
182
+ vocabCloseBtns[btn].addEventListener("click", () => {
183
+ for (let node = 0; node < vocabLists[btn].children.length; node++) {
184
+ if (vocabLists[btn].children[node].tagName == "DD") {
185
+ vocabLists[btn].children[node].removeAttribute("style");
186
+ }
187
+ if (vocabLists[btn].children[node].tagName == "DT") {
188
+ vocabLists[btn].children[node].removeAttribute("class");
189
+ }
190
+ }
191
+ });
192
+ }
193
+ }
194
+ for (let activeTerm = 0; activeTerm < vocabTerms.length; activeTerm++) {
195
+ vocabTerms[activeTerm].addEventListener("click", function () {
196
+ this.classList.toggle("active");
197
+ let termPanel = this.nextElementSibling;
198
+ if (termPanel.style.display === "block") { termPanel.removeAttribute("style"); }
199
+ else { termPanel.style.display = "block"; }
200
+ });
201
+
202
+ vocabTerms[activeTerm].addEventListener("keydown", function (e) {
203
+ if (e.key === "Enter") {
204
+ this.classList.toggle("active");
205
+ let termPanel = this.nextElementSibling;
206
+ if (termPanel.style.display === "block") { termPanel.removeAttribute("style"); }
207
+ else { termPanel.style.display = "block"; }
208
+ }
209
+ });
210
+ }
211
+ }
212
+ if (vocabListWidget) { callVocabList(); }
213
+
214
+ // Media Container
215
+ const addMediaContainersAria = () => {
216
+ mediaContainers.forEach((eachContainer, index) => {
217
+ // loopID: find the current index value, convert it to its letter equivalent, then convert to lowercase
218
+ let loopId = String.fromCharCode(index + 65).toLowerCase();
219
+ let mediaObject = eachContainer.querySelector(".media-object");
220
+ let iframe = mediaObject.firstElementChild;
221
+ let mediaInfo = mediaObject.nextElementSibling;
222
+
223
+ // If element DOES NOT have "aria-describedby" && it DOES have a sibling element.
224
+ if (!iframe.hasAttribute("aria-describedby") && mediaInfo != null) {
225
+ iframe.setAttribute("aria-describedby", `${loopId}`);
226
+ mediaInfo.id = `${[loopId]}`;
227
+ }
228
+ });
229
+ }
230
+ if (mediaContainers) { addMediaContainersAria(); }
231
+
232
+ //Tabs Widget
233
+ const callTabsWidget = () => {
234
+
235
+ let tabsWidgetsNum = 0;
236
+
237
+ tabsWidgets.forEach((tab, index) => {
238
+ let tabInputs = tab.querySelectorAll("input")
239
+ let tabLabels = tab.querySelectorAll("label")
240
+ let tabDivs = tab.querySelectorAll("div")
241
+
242
+ let groupNum = index + 1;
243
+
244
+ //Add region and aria-label to parent div
245
+ tab.setAttribute("role", "region");
246
+ tab.setAttribute("aria-label", `tab group ${groupNum}`)
247
+
248
+ for (tabIndex = 0; tabIndex < tabInputs.length; tabIndex++) {
249
+
250
+ let tabNum = tabsWidgetsNum + 1;
251
+
252
+ //Add class, id, name, and aria-described by for inputs
253
+ tabInputs[tabIndex].classList.add("tab-input");
254
+ tabInputs[tabIndex].setAttribute("type", "radio")
255
+ tabInputs[tabIndex].setAttribute("id", `tab${tabNum}`);
256
+ tabInputs[tabIndex].setAttribute("name", `hint-group-${groupNum}`)
257
+ tabInputs[tabIndex].setAttribute("aria-describedby", `tabHeading${tabNum}`)
258
+ //Add class and for for labels
259
+ tabLabels[tabIndex].classList.add("tab-header");
260
+ tabLabels[tabIndex].setAttribute("for", `tab${tabNum}`)
261
+ //Add class, tabindex, and id for divs
262
+ tabDivs[tabIndex].classList.add("tab-panel")
263
+ tabDivs[tabIndex].setAttribute("tabindex", 0)
264
+ tabDivs[tabIndex].setAttribute("id", `tabHeading${tabNum}`)
265
+ //Add attributes for hide tab
266
+ if (tabIndex + 1 == tabInputs.length) {
267
+ tabLabels[tabIndex].classList.add("hide-tab")
268
+ tabInputs[tabIndex].checked = true;
269
+ tabDivs[tabIndex].classList.add("hide-panel")
270
+ }
271
+ tabsWidgetsNum++;
272
+ }
273
+ })
274
+ }
275
+ if (tabsWidgets) { callTabsWidget(); }
276
+
277
+ // Toggle footnotes
278
+ const toggleBtns = document.querySelectorAll(".toggle-btn, .toggle-footnotes");
279
+
280
+ if (document.querySelector(".toggle-btn") || document.querySelector(".toggle-footnotes")) {
281
+ for (let toggleBtn = 0; toggleBtn < toggleBtns.length; toggleBtn++) {
282
+ // Add tabindex
283
+ toggleBtns[toggleBtn].setAttribute("tabindex", "0");
284
+
285
+ // Show/hide on click
286
+ toggleBtns[toggleBtn].addEventListener("click", () => {
287
+ toggleBtns[toggleBtn].nextElementSibling.classList.toggle("show");
288
+ })
289
+
290
+ // Show/hide on enter for users who use tab
291
+ toggleBtns[toggleBtn].addEventListener("keydown", (enter) => {
292
+ if (enter.keyCode === 13) {
293
+ toggleBtns[toggleBtn].nextElementSibling.classList.toggle("show");
294
+ }
295
+ })
296
+ }
297
+ }
298
+
299
+ // Change footnotes from 'show' to 'hide'
300
+ const footnotes = document.querySelector(".toggle-footnotes");
301
+
302
+ if (footnotes) {
303
+ footnotes.addEventListener("click", () => {
304
+ footnotes.innerHTML = (footnotes.innerHTML === "[Show Footnotes]") ? "[Hide Footnotes]" : "[Show Footnotes]";
305
+ })
306
+ }
307
+
308
+ // Animated border for HRS theme
309
+ const hrsBorders = document.querySelectorAll(".hrs-border");
310
+
311
+ if (hrsBorders) {
312
+ for (let hrsBorder = 0; hrsBorder < hrsBorders.length; hrsBorder++) {
313
+ const callAnimateBorder = new IntersectionObserver(entries => {
314
+ // Loop over the entries
315
+ entries.forEach(entry => {
316
+ // If the element is visible
317
+ if (entry.isIntersecting) {
318
+ // Add the animation class
319
+ entry.target.classList.add('animate-border');
320
+ }
321
+ });
322
+ });
323
+ callAnimateBorder.observe(hrsBorders[hrsBorder]);
324
+ }
325
+ };
326
+
327
+ // Add h5p resizer dynamically
328
+ h5pResizer.setAttribute("src", "https://pima.h5p.com/js/h5p-resizer.js");
329
+ h5pResizer.setAttribute("charset", "UTF-8");
330
+ h5pResizer.setAttribute("defer", "");
331
+
332
+ h5pIframes.forEach(function (h5pIframe) {
333
+ const src = h5pIframe.getAttribute("src");
334
+ if (src.includes("/d2l/common/dialogs/quickLink") || src.includes("https://pima.h5p.com/content") && !h5pResizerExists) {
335
+ docHead.appendChild(h5pResizer);
336
+ }
337
+ });
338
+
339
+ if (document.querySelector("body[focus-reader]")) {
340
+
341
+ // Initializations
342
+ let focusOn = false;
343
+
344
+ const fr_contentWrapper = document.querySelector("#content-wrapper");
345
+ const fr_thisBody = document.querySelector("body[focus-reader]");
346
+
347
+ // Add fontAwesome to header
348
+ const fr_pageHead = document.querySelector("head");
349
+ const fontAweLink = document.createElement("link");
350
+ fontAweLink.rel = "stylesheet";
351
+ fontAweLink.href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css";
352
+ fr_pageHead.append(fontAweLink);
353
+
354
+ // add intersection observer to head tag
355
+ const observerScript = document.createElement("script");
356
+ observerScript.setAttribute("src", "https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver");
357
+ fr_pageHead.append(observerScript);
358
+
359
+ // Create Switches container
360
+ const fr_switchesContainer = document.createElement("div");
361
+ fr_switchesContainer.className = "focus-reader-switches";
362
+ fr_thisBody.append(fr_switchesContainer);
363
+
364
+ // Focus-text container
365
+ const fr_focusTextContainer = document.createElement("div");
366
+ fr_switchesContainer.append(fr_focusTextContainer);
367
+
368
+ // Focus-text Icon
369
+ const fr_focusTextIcon = document.createElement("p");
370
+ fr_focusTextIcon.innerHTML = "Focus Text"
371
+ fr_focusTextContainer.append(fr_focusTextIcon);
372
+
373
+ // info icon
374
+ const fr_infoIcon = document.createElement("i");
375
+ fr_infoIcon.classList.add("fa-solid");
376
+ fr_infoIcon.classList.add("fa-circle-info");
377
+ fr_focusTextContainer.append(fr_infoIcon);
378
+
379
+ // info tooltip
380
+ const fr_infoTooltip = document.createElement("span");
381
+ const focusReaderTooltipText = "Highlight text as you scroll";
382
+ fr_infoTooltip.classList.add("info-tooltip");
383
+ fr_infoTooltip.innerHTML = focusReaderTooltipText;
384
+ fr_infoIcon.append(fr_infoTooltip);
385
+
386
+ // Focus-text switch button
387
+ const fr_focusTextSwitch = document.createElement("button");
388
+ fr_focusTextSwitch.id = "focus-text";
389
+ fr_focusTextSwitch.innerHTML = (focusOn) ? `<i class="fas fa-toggle-on"></i>` : `<i class="fas fa-toggle-off"></i>`;
390
+ fr_focusTextContainer.append(fr_focusTextSwitch);
391
+
392
+ // Focus-text logic
393
+ fr_focusTextSwitch.addEventListener("click", () => {
394
+ if (focusOn) {
395
+ focusOn = !focusOn;
396
+ fr_contentWrapper.removeAttribute("on");
397
+ fr_focusTextSwitch.innerHTML = (focusOn) ? `<i class="fas fa-toggle-on"></i>` : `<i class="fas fa-toggle-off"></i>`;
398
+ removeSpans(fr_contentWrapper);
399
+ } else {
400
+ focusOn = !focusOn;
401
+ fr_contentWrapper.setAttribute("on", "");
402
+ fr_focusTextSwitch.innerHTML = (focusOn) ? `<i class="fas fa-toggle-on"></i>` : `<i class="fas fa-toggle-off"></i>`;
403
+ const elements = document.querySelectorAll("#content-wrapper .content-body :is(p, li, dd, dt, blockquote)");
404
+ elements.forEach(element => {
405
+ traverseAndWrapTextInSpans(element, focusText);
406
+ });
407
+ }
408
+ });
409
+
410
+ const options = {
411
+ root: null,
412
+ rootMargin: '-48.9% 0px',
413
+ threshold: 0.00
414
+ };
415
+
416
+ const focusText = new IntersectionObserver(entries => {
417
+ entries.forEach(entry => {
418
+ if (entry.isIntersecting) {
419
+ entry.target.closest("span").classList.add("focus-text");
420
+ } else {
421
+ entry.target.closest("span").classList.remove("focus-text");
422
+ }
423
+ });
424
+ }, options);
425
+
426
+
427
+ }
428
+
429
+ function traverseAndWrapTextInSpans(node, focusText) {
430
+ if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== '') {
431
+ // Split the text into words
432
+ const words = node.textContent.split(' ');
433
+
434
+ // Wrap each word in a span
435
+ words.forEach(word => {
436
+ if (word !== '') {
437
+ const span = document.createElement("span");
438
+ span.textContent = word + ' '; // Add a space after the word to preserve spacing
439
+
440
+ // Observe the span
441
+ focusText.observe(span);
442
+
443
+ // Insert the span before the text node
444
+ node.parentNode.insertBefore(span, node);
445
+ }
446
+ });
447
+
448
+ // Remove the original text node
449
+ node.parentNode.removeChild(node);
450
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
451
+ // Recursively process child nodes
452
+ Array.from(node.childNodes).forEach(childNode => {
453
+ traverseAndWrapTextInSpans(childNode, focusText);
454
+ });
455
+ }
456
+ }
457
+
458
+ function removeSpans(node) {
459
+ if (node.nodeType === Node.ELEMENT_NODE) {
460
+ // If the node is a span with the class 'focus-text', an empty class attribute, is empty, or has no attributes, replace it with a text node
461
+ if (node.tagName === 'SPAN' && (node.className === 'focus-text' || node.className === '' || node.textContent.trim() === '' || node.attributes.length === 0)) {
462
+ const textNode = document.createTextNode(node.textContent);
463
+ node.parentNode.insertBefore(textNode, node);
464
+ node.parentNode.removeChild(node);
465
+ } else {
466
+ // Recursively process child nodes
467
+ Array.from(node.childNodes).forEach(childNode => {
468
+ removeSpans(childNode);
469
+ });
470
+ }
471
+ }
472
+ }
473
+
474
+ // Reset margin-top and padding-top for content bodies that don't have headings as the first child element
475
+ const ecnStylesheet = document.querySelector("[href*='themes/ecn/styles.css']");
476
+ const ecnContentBodies = document.querySelectorAll(".content-body");
477
+
478
+ if (ecnStylesheet) {
479
+ ecnContentBodies.forEach((contentBody) => {
480
+ const firstChildHeading = contentBody.querySelector(":first-child");
481
+ if (firstChildHeading && firstChildHeading.tagName !== "H2" && firstChildHeading.tagName !== "H3") {
482
+ contentBody.style.marginTop = "unset";
483
+ contentBody.style.paddingTop = "15px";
484
+ }
485
+ })
486
+ }
487
+
488
+
489
+ // Call function with jQuery scripts
490
+ const callJquery = () => {
491
+ // Toggle Button's Arrow Right Points Down on Click
492
+ $('.arrow-right').on('click', function () {
493
+ $(this).toggleClass('arrow-down');
494
+ });
495
+ // TOOLTIP
496
+ // Allows Screen readers to toggle a tooltip on click and to say if the tooltip is collapsed or expanded.
497
+ $(".tooltip").click(function () {
498
+ $(this).children(".tip-hover").toggle();
499
+ if ($(this).children(".tip-hover").is(':visible')) {
500
+ $(this).attr('aria-expanded', 'true');
501
+ $(this).removeClass('hidden');
502
+ } else {
503
+ $(this).attr('aria-expanded', 'false');
504
+ $(this).addClass('hidden');
505
+ }
506
+ });
507
+ let start = 999;
508
+ $('.tooltip').each(function (i) {
509
+ $(this).css('z-index', start--);
510
+ });
511
+ $(".tooltip .video-container").parent().css("width", "450px");
512
+ }
513
+ callJquery();
514
+
515
+ // Array for all themes that require theme specific js
516
+ const customJsThemes = ["ss"];
517
+
518
+ //Search links for theme styles
519
+ const themeJsCheck = () => {
520
+
521
+ const links = document.querySelectorAll("link");
522
+
523
+ links.forEach((link) => {
524
+ const href = link.getAttribute("href");
525
+ customJsThemes.forEach((theme) => {
526
+ // If theme requires custom js, run addThemeScript()
527
+ if (href && href.includes(`/${theme}/styles.css`)) {
528
+ addThemeScript(theme);
529
+ }
530
+ });
531
+ });
532
+
533
+ // Adds a script to the head for that particular theme
534
+ function addThemeScript(theme) {
535
+ let themeScript = document.createElement("script");
536
+ themeScript.src = `../js/themes/${theme}.js`;
537
+ document.head.appendChild(themeScript);
538
+ }
539
+ }
540
+
541
+ themeJsCheck()