@pimaonline/pimaonline-themepack 3.12.1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +12 -23
- package/dist/css/main.css +1 -1
- package/dist/css/plugins/alt-icons.css +1 -1
- package/dist/css/plugins/font-awesome.css +1 -1
- package/dist/css/routes.css +1 -1
- package/dist/css/themes/ait/styles.css +1 -1
- package/dist/css/themes/ajs/styles.css +1 -1
- package/dist/css/themes/ant/styles.css +1 -0
- package/dist/css/themes/art/styles.css +1 -1
- package/dist/css/themes/aviation/styles.css +1 -1
- package/dist/css/themes/bct/styles.css +1 -1
- package/dist/css/themes/bio/styles.css +1 -1
- package/dist/css/themes/business/styles.css +1 -1
- package/dist/css/themes/cad/styles.css +1 -1
- package/dist/css/themes/cards/styles.css +1 -1
- package/dist/css/themes/cas/styles.css +1 -0
- package/dist/css/themes/cda/styles.css +1 -1
- package/dist/css/themes/chm/styles.css +1 -0
- package/dist/css/themes/cis/styles.css +1 -1
- package/dist/css/themes/communication/styles.css +1 -1
- package/dist/css/themes/computer-information-systems/styles.css +1 -1
- package/dist/css/themes/culinary/styles.css +1 -1
- package/dist/css/themes/culinary/versions/black-marble.css +1 -1
- package/dist/css/themes/culinary/versions/stainless.css +1 -1
- package/dist/css/themes/culinary/versions/wood.css +1 -1
- package/dist/css/themes/dental/styles.css +1 -1
- package/dist/css/themes/ece/styles.css +1 -1
- package/dist/css/themes/ecn/styles.css +1 -1
- package/dist/css/themes/eng/styles.css +1 -1
- package/dist/css/themes/fashion/styles.css +1 -1
- package/dist/css/themes/fitness/styles.css +1 -1
- package/dist/css/themes/fsc/styles.css +1 -1
- package/dist/css/themes/geography/styles.css +1 -1
- package/dist/css/themes/geology/styles.css +1 -1
- package/dist/css/themes/health-it/styles.css +1 -1
- package/dist/css/themes/history/styles.css +1 -1
- package/dist/css/themes/hrm/styles.css +1 -1
- package/dist/css/themes/hrs/styles.css +1 -1
- package/dist/css/themes/journalism/styles.css +1 -1
- package/dist/css/themes/lang/styles.css +1 -1
- package/dist/css/themes/lgm/styles.css +1 -1
- package/dist/css/themes/machine/styles.css +1 -1
- package/dist/css/themes/math/styles.css +1 -1
- package/dist/css/themes/mgt/styles.css +1 -1
- package/dist/css/themes/minimalist/styles.css +1 -1
- package/dist/css/themes/mkt/styles.css +1 -0
- package/dist/css/themes/music/styles.css +1 -1
- package/dist/css/themes/nursing/styles.css +1 -0
- package/dist/css/themes/philosophy/styles.css +1 -1
- package/dist/css/themes/pht/styles.css +1 -1
- package/dist/css/themes/phy/styles.css +1 -0
- package/dist/css/themes/pos/styles.css +1 -0
- package/dist/css/themes/psy/styles.css +1 -1
- package/dist/css/themes/reading/styles.css +1 -0
- package/dist/css/themes/resort/styles.css +1 -1
- package/dist/css/themes/soc/styles.css +1 -1
- package/dist/css/themes/ss/styles.css +1 -1
- package/dist/css/themes/tps/styles.css +1 -0
- package/dist/css/themes/university/styles.css +1 -1
- package/dist/css/themes/vet/styles.css +1 -1
- package/dist/css/themes/welding/styles.css +1 -1
- package/dist/css/themes/writing/styles.css +1 -1
- package/dist/img/general/arrow-right-black.svg +1 -0
- package/dist/img/general/arrow-right-primary-blue.svg +1 -0
- package/dist/img/general/arrow-right-white.svg +1 -0
- package/dist/img/theme-images/ant/texture.png +0 -0
- package/dist/img/theme-images/cas/city-vector.svg +92 -0
- package/dist/img/theme-images/cas/farm-vector.svg +1 -0
- package/dist/img/theme-images/cas/ocean-vector.svg +1 -0
- package/dist/img/theme-images/cas/recycle-symbol.svg +1 -0
- package/dist/img/theme-images/chm/chem-letters/letter-a.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-b.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-c.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-d.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-e.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-f.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-g.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-h.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-i.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-j.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-k.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-l.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-m.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-n.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-o.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-p.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-q.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-r.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-s.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-t.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-u.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-v.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-w.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-x.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-y.png +0 -0
- package/dist/img/theme-images/chm/chem-letters/letter-z.png +0 -0
- package/dist/img/theme-images/mkt/blue/bluebars.svg +36 -0
- package/dist/img/theme-images/mkt/blue/blueheader.jpg +0 -0
- package/dist/img/theme-images/mkt/blue/bluepie.svg +42 -0
- package/dist/img/theme-images/mkt/green/greenbars.svg +36 -0
- package/dist/img/theme-images/mkt/green/greenheader.jpg +0 -0
- package/dist/img/theme-images/mkt/green/greenpie.svg +42 -0
- package/dist/img/theme-images/mkt/yellow/yellowbars.svg +36 -0
- package/dist/img/theme-images/mkt/yellow/yellowheader.jpg +0 -0
- package/dist/img/theme-images/mkt/yellow/yellowpie.svg +42 -0
- package/dist/img/theme-images/nursing/blue/bottomright-blue.svg +95 -0
- package/dist/img/theme-images/nursing/blue/topleft-blue.svg +111 -0
- package/dist/img/theme-images/nursing/green/bottomright-green.svg +95 -0
- package/dist/img/theme-images/nursing/green/topleft-green.svg +111 -0
- package/dist/img/theme-images/nursing/purple/bottomright-purple.svg +95 -0
- package/dist/img/theme-images/nursing/purple/topleft-purple.svg +111 -0
- package/dist/img/theme-images/nursing/teal/bottomright-teal.svg +95 -0
- package/dist/img/theme-images/nursing/teal/topleft-teal.svg +111 -0
- package/dist/img/theme-images/phy/background.svg +150 -0
- package/dist/img/theme-images/phy/tape-style1.svg +8 -0
- package/dist/img/theme-images/reading/bg10.jpg +0 -0
- package/dist/js/scripts2.js +1 -1552
- package/dist/js/themes/cas.js +1 -0
- package/dist/js/themes/chm.js +1 -0
- package/dist/js/themes/ecn.js +1 -13
- package/dist/js/themes/hrs.js +1 -19
- package/dist/js/themes/ss.js +1 -197
- package/dist/plugins/fancybox/fancybox-example.html +1 -1
- package/dist/plugins/fancybox/helpers/jquery.fancybox-buttons.js +2 -122
- package/dist/plugins/fancybox/helpers/jquery.fancybox-media.js +2 -201
- package/dist/plugins/fancybox/helpers/jquery.fancybox-thumbs.js +2 -165
- package/dist/plugins/fancybox/jquery.fancybox.js +2 -2018
- package/dist/plugins/fancybox/jquery.fancybox.pack.js +2 -46
- package/dist/plugins/flashcards/flashcards-example.html +1 -1
- package/dist/plugins/flashcards/js/flash_cards.min.js +1 -12
- package/dist/plugins/flashcards/js/plugins/flash_cards.js +1 -62
- package/dist/plugins/flashcards/js/plugins/jquery.cycle.js +2 -1148
- package/dist/plugins/flashcards/js/vendor/jquery-1.7.2.js +2 -9404
- package/dist/plugins/flashcards/js/vendor/jquery-1.7.2.min.js +2 -4
- package/dist/plugins/flashcards/js/vendor/modernizr-2.5.3.min.js +1 -4
- package/dist/plugins/floating-particles/floating-particles.js +1 -68
- package/dist/plugins/global-homepage-overrides/global-homepage-overrides.js +1 -53
- package/dist/plugins/preview-banner/preview-banner.js +1 -57
- package/package.json +12 -16
- package/dist/js/jumpTo.js +0 -4
- package/dist/js/scripts-ts.js +0 -1
- package/dist/js/scripts.js +0 -327
package/dist/js/scripts2.js
CHANGED
@@ -1,1552 +1 @@
|
|
1
|
-
const columnWidget = document.querySelector("#column-widget");
|
2
|
-
const contentLockWidgets = document.querySelectorAll(".content-lock-widget");
|
3
|
-
const contentLockInstructions = document.querySelectorAll(".instructions");
|
4
|
-
const contentLockQuizzes = document.querySelectorAll(".quiz");
|
5
|
-
const contentWrapper = document.querySelector("#content-wrapper");
|
6
|
-
const courseBody = document.querySelector("body");
|
7
|
-
const docHead = document.querySelector("head");
|
8
|
-
const flipCards = document.querySelectorAll(".flip-card-group");
|
9
|
-
const focusReaderTooltipText = "Highlight text as you scroll";
|
10
|
-
const galleryWrappers = document.querySelectorAll(".gallery-wrapper");
|
11
|
-
const h5pIframes = document.querySelectorAll("iframe");
|
12
|
-
const h5pResizer = document.createElement("script");
|
13
|
-
const h5pResizerExists = docHead.querySelector("script[src='https://pima.h5p.com/js/h5p-resizer.js']");
|
14
|
-
// This array contains CDNs for Bootstrap and Remix icon libraries stored as objects
|
15
|
-
const iconClasses = [
|
16
|
-
{ class: "bi-", cdn: "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" },
|
17
|
-
{ class: "ri-", cdn: "https://cdn.jsdelivr.net/npm/remixicon@4.0.1/fonts/remixicon.css" }
|
18
|
-
];
|
19
|
-
const imageGallery = document.querySelector(".image-gallery");
|
20
|
-
const imgBoxes = document.querySelectorAll(".image-box");
|
21
|
-
const imgGalleries = document.querySelectorAll(".image-gallery");
|
22
|
-
const lockedContent = document.querySelectorAll(".locked-content");
|
23
|
-
const mediaContainers = document.querySelectorAll(".media-container");
|
24
|
-
const rolePres = document.querySelectorAll('[role="presentation"]');
|
25
|
-
const secondColumn = document.querySelector("#second-column");
|
26
|
-
const tables = document.querySelectorAll(".display, .display-lg")
|
27
|
-
const tabsWidgets = document.querySelectorAll(".tabs");
|
28
|
-
const thirdColumn = document.querySelector("#third-column");
|
29
|
-
const vocabCloseBtns = document.querySelectorAll("dl.vocab-list button");
|
30
|
-
const vocabDefs = document.querySelectorAll("dl.vocab-list dd");
|
31
|
-
const vocabListWidget = document.querySelector("dl.vocab-list");
|
32
|
-
const vocabLists = document.querySelectorAll("dl[class^='vocab-list']");
|
33
|
-
const vocabTerms = document.querySelectorAll("dl.vocab-list dt");
|
34
|
-
const videoWrapper = document.querySelector("#video-wrapper");
|
35
|
-
// JS to add role and aria-label to content-wrapper, second-column, and third-column
|
36
|
-
const addAria = () => {
|
37
|
-
if (contentWrapper) {
|
38
|
-
contentWrapper.setAttribute("role", "main");
|
39
|
-
} else if (!contentWrapper) {
|
40
|
-
console.log("Document error: does not contain #content-wrapper.");
|
41
|
-
}
|
42
|
-
if (secondColumn) {
|
43
|
-
secondColumn.setAttribute("role", "region");
|
44
|
-
secondColumn.setAttribute("aria-label", "Second column");
|
45
|
-
}
|
46
|
-
if (thirdColumn) {
|
47
|
-
thirdColumn.setAttribute("role", "region");
|
48
|
-
thirdColumn.setAttribute("aria-label", "Third column");
|
49
|
-
}
|
50
|
-
};
|
51
|
-
addAria();
|
52
|
-
const addGrid = () => {
|
53
|
-
if (contentWrapper && secondColumn && thirdColumn) {
|
54
|
-
courseBody.id = "three-column";
|
55
|
-
} else if (contentWrapper && secondColumn && !columnWidget) {
|
56
|
-
courseBody.id = "two-column";
|
57
|
-
} else if (contentWrapper && secondColumn && columnWidget) {
|
58
|
-
courseBody.id = "two-col-widget";
|
59
|
-
} else if ((contentWrapper && videoWrapper)) {
|
60
|
-
courseBody.id = "video-grid";
|
61
|
-
} else if (contentWrapper && !secondColumn && !thirdColumn && !columnWidget && !videoWrapper) {
|
62
|
-
courseBody.id = "one-column";
|
63
|
-
} else if (contentWrapper && !secondColumn && (thirdColumn || columnWidget)) {
|
64
|
-
console.log("Document error: <body> is missing id because #second-column doesn't exist.");
|
65
|
-
} else {
|
66
|
-
console.log("Document error: unable to determine the page layout for setting <body> id.");
|
67
|
-
}
|
68
|
-
|
69
|
-
const topLevelElements = document.body.children;
|
70
|
-
let foundNestedElement = false;
|
71
|
-
|
72
|
-
// Check for additional content outside #content-wrapper, #second-column, #third-column, or footer
|
73
|
-
for (let i = 0; i < topLevelElements.length; i++) {
|
74
|
-
const element = topLevelElements[i];
|
75
|
-
|
76
|
-
if (
|
77
|
-
element.id !== "content-wrapper" &&
|
78
|
-
element.id !== "second-column" &&
|
79
|
-
element.id !== "third-column" &&
|
80
|
-
element.id !== "column-widget" &&
|
81
|
-
element.tagName !== "HEADER" &&
|
82
|
-
element.tagName !== "FOOTER" &&
|
83
|
-
element.tagName !== "SCRIPT" &&
|
84
|
-
element.id !== "loom-companion-mv3" &&
|
85
|
-
element.className !== "focus-reader-switches"
|
86
|
-
) {
|
87
|
-
foundNestedElement = true;
|
88
|
-
break;
|
89
|
-
}
|
90
|
-
}
|
91
|
-
|
92
|
-
if (foundNestedElement) {
|
93
|
-
console.log("Document error: Additional content outside #content-wrapper, #second-column, #third-column, or footer.");
|
94
|
-
}
|
95
|
-
};
|
96
|
-
addGrid();
|
97
|
-
// Media Container
|
98
|
-
const addMediaContainersAria = () => {
|
99
|
-
mediaContainers.forEach((eachContainer, index) => {
|
100
|
-
// loopID: find the current index value, convert it to its letter equivalent, then convert to lowercase
|
101
|
-
let loopId = String.fromCharCode(index + 65).toLowerCase();
|
102
|
-
let mediaObject = eachContainer.querySelector(".media-object");
|
103
|
-
let iframe = eachContainer.querySelector("iframe");
|
104
|
-
let mediaInfo = eachContainer.querySelector(".media-info");
|
105
|
-
|
106
|
-
// Check if media container items are present
|
107
|
-
if (!iframe) {
|
108
|
-
console.log("Document error: no iframe found for media container");
|
109
|
-
}
|
110
|
-
if (!mediaObject) {
|
111
|
-
console.log("Document error: no media object found for media container");
|
112
|
-
}
|
113
|
-
|
114
|
-
// If element DOES NOT have "aria-describedby" && it DOES have a sibling element.
|
115
|
-
if (!iframe.hasAttribute("aria-describedby") && mediaInfo != null) {
|
116
|
-
iframe.setAttribute("aria-describedby", `${loopId}`);
|
117
|
-
mediaInfo.id = `${[loopId]}`;
|
118
|
-
}
|
119
|
-
});
|
120
|
-
}
|
121
|
-
if (mediaContainers) { addMediaContainersAria(); }
|
122
|
-
// -------- Add CDNs for Bootstrap and Remix icon libraries ---------
|
123
|
-
|
124
|
-
// The respective CDN will be added to <head> only if a page contains an icon with a prefix specific to that library. We use forEach to loop through iconClasses because that's more efficient than using multiple if statements to make sure only the necessary CDNs are added.
|
125
|
-
|
126
|
-
iconClasses.forEach(icon => {
|
127
|
-
const iconElement = document.querySelector(`[class*='${icon.class}']`);
|
128
|
-
if (iconElement) {
|
129
|
-
const metaTagRef = docHead.querySelector("meta[name='viewport']");
|
130
|
-
//Check if viewport meta tag exists
|
131
|
-
if (!metaTagRef) {
|
132
|
-
console.log("Document error: could not find viewport meta tag");
|
133
|
-
}
|
134
|
-
|
135
|
-
const iconCDN = document.createElement("link");
|
136
|
-
iconCDN.setAttribute("href", icon.cdn);
|
137
|
-
iconCDN.setAttribute("rel", "stylesheet");
|
138
|
-
|
139
|
-
// Stylesheets are added after meta tag and before themepack stylesheets and scripts to ensure proper styling override
|
140
|
-
docHead.insertBefore(iconCDN, metaTagRef.nextSibling);
|
141
|
-
}
|
142
|
-
});
|
143
|
-
// Check if parent of .gallery-wrapper has .image-gallery class
|
144
|
-
const checkGalleryWrapperParent = () => {
|
145
|
-
galleryWrappers.forEach((galleryWrapper) => {
|
146
|
-
if (!galleryWrapper.parentNode.classList.contains("image-gallery")) {
|
147
|
-
console.log(`Document error: parent of .gallery-wrapper does not have the .image-gallery class.`);
|
148
|
-
}
|
149
|
-
});
|
150
|
-
};
|
151
|
-
checkGalleryWrapperParent();
|
152
|
-
|
153
|
-
// Check if parent of .image-box has .gallery-wrapper class
|
154
|
-
const checkImageBoxParent = () => {
|
155
|
-
imgBoxes.forEach((imgBox) => {
|
156
|
-
if (!imgBox.parentNode.classList.contains("gallery-wrapper")) {
|
157
|
-
console.log(`Document error: parent of .image-box does not have the .gallery-wrapper class.`);
|
158
|
-
}
|
159
|
-
});
|
160
|
-
};
|
161
|
-
checkImageBoxParent();
|
162
|
-
|
163
|
-
// Check if direct children of .gallery-wrapper have .image-box class
|
164
|
-
const checkGalleryWrapperChildren = () => {
|
165
|
-
galleryWrappers.forEach((galleryWrapper) => {
|
166
|
-
let directChildren = Array.from(galleryWrapper.children).every(child => child.classList.contains("image-box"));
|
167
|
-
|
168
|
-
if (!directChildren) {
|
169
|
-
console.log(`Document error: not all direct children of .gallery-wrapper have the .image-box class.`);
|
170
|
-
}
|
171
|
-
});
|
172
|
-
};
|
173
|
-
checkGalleryWrapperChildren();
|
174
|
-
|
175
|
-
// Function to add Font Awesome CDN to the head
|
176
|
-
const addFontAwesomeCdn = () => {
|
177
|
-
const fontAwesomeCdn = document.createElement("link");
|
178
|
-
fontAwesomeCdn.rel = "stylesheet";
|
179
|
-
fontAwesomeCdn.href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css";
|
180
|
-
docHead.appendChild(fontAwesomeCdn);
|
181
|
-
};
|
182
|
-
|
183
|
-
// Function to create modal box HTML string
|
184
|
-
const createModalBox = () => {
|
185
|
-
return `<div class="modal-box invisible">
|
186
|
-
<div class="gallery-overlay"></div>
|
187
|
-
<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>
|
188
|
-
</div>
|
189
|
-
<button class="hide-gallery">Hide</button>`;
|
190
|
-
};
|
191
|
-
|
192
|
-
// Function to initialize the image gallery
|
193
|
-
const callImageGallery = () => {
|
194
|
-
// Insert modal box HTML at the beginning of each image gallery
|
195
|
-
imgGalleries.forEach((gallery) => {
|
196
|
-
gallery.insertAdjacentHTML("afterbegin", createModalBox());
|
197
|
-
});
|
198
|
-
|
199
|
-
// Select necessary elements for later use
|
200
|
-
const overlay = document.querySelector(".gallery-overlay"),
|
201
|
-
modalBox = document.querySelector(".modal-box"),
|
202
|
-
modalImg = document.querySelector(".modal-box--image img"),
|
203
|
-
modalCaption = document.querySelector(".img-caption"),
|
204
|
-
closeImg = document.querySelector(".close-img"),
|
205
|
-
hideGalleries = document.querySelectorAll(".hide-gallery");
|
206
|
-
|
207
|
-
// Function to show modal with specified image source and caption
|
208
|
-
const showModal = (imgSrc, imgCaption) => {
|
209
|
-
modalBox.classList.remove("invisible");
|
210
|
-
modalImg.src = imgSrc;
|
211
|
-
modalCaption.innerHTML = imgCaption;
|
212
|
-
};
|
213
|
-
|
214
|
-
// Function to hide the modal
|
215
|
-
const hideModal = () => {
|
216
|
-
modalBox.classList.add("invisible");
|
217
|
-
};
|
218
|
-
|
219
|
-
// Attach event listeners to each image box
|
220
|
-
imgBoxes.forEach((imgBox) => {
|
221
|
-
// Show modal on click
|
222
|
-
imgBox.addEventListener("click", function () {
|
223
|
-
showModal(this.querySelector("img").src, this.querySelector("img").alt);
|
224
|
-
});
|
225
|
-
|
226
|
-
// Make images tab-able and show modal on Enter key press
|
227
|
-
imgBox.setAttribute("tabindex", "0");
|
228
|
-
imgBox.addEventListener("keydown", function (event) {
|
229
|
-
if (event.key === "Enter") {
|
230
|
-
showModal(this.querySelector("img").src, this.querySelector("img").alt);
|
231
|
-
}
|
232
|
-
});
|
233
|
-
});
|
234
|
-
|
235
|
-
// Attach event listeners for overlay, Escape key, and close button to hide the modal
|
236
|
-
overlay.onclick = hideModal;
|
237
|
-
window.onkeydown = (event) => {
|
238
|
-
if (event.keyCode === 27) {
|
239
|
-
hideModal();
|
240
|
-
}
|
241
|
-
};
|
242
|
-
closeImg.onclick = hideModal;
|
243
|
-
|
244
|
-
// Attach event listeners for "Hide/Show" button to toggle gallery visibility
|
245
|
-
hideGalleries.forEach((hideGallery) => {
|
246
|
-
hideGallery.addEventListener("click", () => {
|
247
|
-
hideGallery.nextElementSibling.classList.toggle("invisible");
|
248
|
-
hideGallery.innerHTML = hideGallery.innerHTML === "Hide" ? "Show" : "Hide";
|
249
|
-
});
|
250
|
-
});
|
251
|
-
};
|
252
|
-
|
253
|
-
// Check if imageGallery exists before initializing the image gallery
|
254
|
-
if (imageGallery) {
|
255
|
-
// Add Font Awesome CDN and initialize the image gallery
|
256
|
-
addFontAwesomeCdn();
|
257
|
-
callImageGallery();
|
258
|
-
}
|
259
|
-
//Tabs Widget
|
260
|
-
const callTabsWidget = () => {
|
261
|
-
|
262
|
-
let tabsWidgetsNum = 0;
|
263
|
-
|
264
|
-
tabsWidgets.forEach((tab, index) => {
|
265
|
-
let tabInputs = tab.querySelectorAll("input");
|
266
|
-
let tabLabels = tab.querySelectorAll("label");
|
267
|
-
let tabDivs = tab.querySelectorAll("div");
|
268
|
-
|
269
|
-
//Check that there are more than just one tab
|
270
|
-
if (tabInputs.length < 2 || tabLabels.length < 2 || tabDivs.length < 2) {
|
271
|
-
console.log("Document error: please add more than just one tab for tabs widget");
|
272
|
-
}
|
273
|
-
|
274
|
-
// Check amount of tab elements present
|
275
|
-
if (tabInputs.length < tabLabels.length || tabInputs.length < tabDivs.length) {
|
276
|
-
console.log("Document error: missing tab input(s) in tab widget");
|
277
|
-
}
|
278
|
-
|
279
|
-
if (tabLabels.length < tabInputs.length || tabLabels.length < tabDivs.length) {
|
280
|
-
console.log("Document error: missing tab label(s) in tab widget");
|
281
|
-
}
|
282
|
-
|
283
|
-
let groupNum = index + 1;
|
284
|
-
|
285
|
-
//Add region and aria-label to parent div
|
286
|
-
tab.setAttribute("role", "region");
|
287
|
-
tab.setAttribute("aria-label", `tab group ${groupNum}`)
|
288
|
-
|
289
|
-
// Iterate over the entries of the tabInputs NodeList using a for loop. Inside the loop, tabIndex is the index of the current tab input element. tabInput is the actual input element itself.
|
290
|
-
for (tabIndex = 0; tabIndex < tabInputs.length; tabIndex++) {
|
291
|
-
|
292
|
-
// Add the hide-tab class to the "Hide" label
|
293
|
-
if (tabLabels[tabIndex].textContent.trim() === "Hide") {
|
294
|
-
tabLabels[tabIndex].classList.add("hide-tab");
|
295
|
-
}
|
296
|
-
|
297
|
-
let tabNum = tabsWidgetsNum + 1;
|
298
|
-
|
299
|
-
// Check on present variables
|
300
|
-
if (tabInputs === null) {
|
301
|
-
console.log("Document error: no inputs found for tabs widget");
|
302
|
-
console.log("Document error: no divs (tab panels) found for tabs widget");
|
303
|
-
}
|
304
|
-
|
305
|
-
if (tabLabels === null) {
|
306
|
-
console.log("Document error: no labels found for tabs widget");
|
307
|
-
}
|
308
|
-
|
309
|
-
//Add class, id, name, and aria-described by for inputs
|
310
|
-
tabInputs[tabIndex].classList.add("tab-input");
|
311
|
-
tabInputs[tabIndex].setAttribute("type", "radio")
|
312
|
-
tabInputs[tabIndex].setAttribute("id", `tab${tabNum}`);
|
313
|
-
tabInputs[tabIndex].setAttribute("name", `hint-group-${groupNum}`);
|
314
|
-
tabInputs[tabIndex].setAttribute("aria-describedby", `tabHeading${tabNum}`);
|
315
|
-
|
316
|
-
//Add class and for for labels
|
317
|
-
tabLabels[tabIndex].classList.add("tab-header");
|
318
|
-
tabLabels[tabIndex].setAttribute("for", `tab${tabNum}`);
|
319
|
-
|
320
|
-
//Add class, tabindex, and id for divs
|
321
|
-
if (tabDivs[tabIndex]) {
|
322
|
-
tabDivs[tabIndex].classList.add("tab-panel");
|
323
|
-
tabDivs[tabIndex].setAttribute("tabindex", 0);
|
324
|
-
tabDivs[tabIndex].setAttribute("id", `tabHeading${tabNum}`);
|
325
|
-
}
|
326
|
-
|
327
|
-
//Add attributes for hide tab
|
328
|
-
if (tabIndex === 0) { // Change this condition to check the first tab
|
329
|
-
tabLabels[tabIndex].classList.remove("hide-tab"); // Remove the hide-tab class
|
330
|
-
tabInputs[tabIndex].checked = true; // Check the first tab input
|
331
|
-
if (tabDivs[tabIndex]) {
|
332
|
-
tabDivs[tabIndex].classList.remove("hide-panel"); // Remove the hide-panel class
|
333
|
-
}
|
334
|
-
} else {
|
335
|
-
tabInputs[tabIndex].checked = false;
|
336
|
-
if (tabDivs[tabIndex]) {
|
337
|
-
tabDivs[tabIndex].classList.add("hide-panel");
|
338
|
-
}
|
339
|
-
}
|
340
|
-
|
341
|
-
// Add click event listener to each tab label
|
342
|
-
tabLabels[tabIndex].addEventListener("click", ((index) => {
|
343
|
-
return () => {
|
344
|
-
// Check if the clicked label is the "Hide" label
|
345
|
-
if (tabLabels[index].textContent.trim() === "Hide") {
|
346
|
-
// Hide all tab panels
|
347
|
-
tabDivs.forEach(div => div.classList.add("hide-panel"));
|
348
|
-
} else {
|
349
|
-
// Uncheck all tab inputs
|
350
|
-
tabInputs.forEach(input => input.checked = false);
|
351
|
-
// Check the clicked tab input
|
352
|
-
tabInputs[index].checked = true;
|
353
|
-
// Show the clicked tab panel
|
354
|
-
if (tabDivs[index]) {
|
355
|
-
tabDivs[index].classList.remove("hide-panel");
|
356
|
-
}
|
357
|
-
}
|
358
|
-
};
|
359
|
-
})(tabIndex)); // Immediately invoke the function with the current tabIndex
|
360
|
-
|
361
|
-
tabsWidgetsNum++;
|
362
|
-
}
|
363
|
-
})
|
364
|
-
}
|
365
|
-
if (tabsWidgets) { callTabsWidget(); }
|
366
|
-
// Vocab list widget
|
367
|
-
const callVocabList = () => {
|
368
|
-
|
369
|
-
const handleVocabClose = (vocabItem) => {
|
370
|
-
if (vocabItem) {
|
371
|
-
let listDefinitions = vocabItem.querySelectorAll("dd");
|
372
|
-
let listTerms = vocabItem.querySelectorAll("dt");
|
373
|
-
|
374
|
-
// If the button is clicked and it is the DD - then hide it
|
375
|
-
listDefinitions.forEach((definition) => {
|
376
|
-
definition.style.display = "none";
|
377
|
-
})
|
378
|
-
|
379
|
-
// If the button is clicked and it is the DT - then remove active class
|
380
|
-
listTerms.forEach((term) => {
|
381
|
-
term.classList.remove("active");
|
382
|
-
})
|
383
|
-
}
|
384
|
-
}
|
385
|
-
|
386
|
-
// Check if the vocab list has one or multiple items within
|
387
|
-
vocabLists.forEach((list) => {
|
388
|
-
|
389
|
-
//Count and ensure it has more than 1 term and definition
|
390
|
-
let terms = 0;
|
391
|
-
let definitions = 0;
|
392
|
-
let closeBtnPresent = false;
|
393
|
-
|
394
|
-
// If the list contains more than one set of <dt> and <dd> tags then add a close button
|
395
|
-
for (let listIndex = 0; listIndex < list.children.length; listIndex++) {
|
396
|
-
// Count terms
|
397
|
-
if (list.children[listIndex].tagName == "DT") {
|
398
|
-
list.children[listIndex].setAttribute("tabindex", "0");
|
399
|
-
terms++;
|
400
|
-
}
|
401
|
-
// Count definitions
|
402
|
-
if (list.children[listIndex].tagName == "DD") {
|
403
|
-
definitions++;
|
404
|
-
}
|
405
|
-
|
406
|
-
//Check for close all button
|
407
|
-
if (list.children[listIndex].tagName == "BUTTON") {
|
408
|
-
closeBtnPresent = true;
|
409
|
-
}
|
410
|
-
}
|
411
|
-
|
412
|
-
// Check for terms and definitions in the vocab list
|
413
|
-
if (terms < 1) {
|
414
|
-
console.log("Document error: no terms found in vocab list");
|
415
|
-
}
|
416
|
-
|
417
|
-
if (definitions < 1) {
|
418
|
-
console.log("Document error: no definitions found in vocab list");
|
419
|
-
}
|
420
|
-
|
421
|
-
if (terms > definitions) {
|
422
|
-
console.log("Document error: more terms than definitions in vocab list")
|
423
|
-
}
|
424
|
-
|
425
|
-
// If there are more than 2 terms and 2 definitions, then check for a button
|
426
|
-
if (terms >= 2 && definitions >= 2) {
|
427
|
-
|
428
|
-
// If there isn't a close button then add one
|
429
|
-
if (!closeBtnPresent) {
|
430
|
-
// Add a close button
|
431
|
-
const closeButton = document.createElement("button");
|
432
|
-
closeButton.textContent = "Close All"; // Set the button text as needed
|
433
|
-
// Add click event listener for button
|
434
|
-
closeButton.addEventListener("click", () => handleVocabClose(list))
|
435
|
-
|
436
|
-
// Add keydown event listener for button
|
437
|
-
closeButton.addEventListener("keydown", (event) => {
|
438
|
-
if (event.key == "Enter") {
|
439
|
-
handleVocabClose(list);
|
440
|
-
}
|
441
|
-
})
|
442
|
-
// Append the button to the end of the list
|
443
|
-
list.appendChild(closeButton);
|
444
|
-
}
|
445
|
-
// If button is present, remove it
|
446
|
-
else {
|
447
|
-
let closeBtn = list.querySelector("button");
|
448
|
-
|
449
|
-
// Add the same event listeners as if you were to add a new button
|
450
|
-
closeBtn.addEventListener("click", () => handleVocabClose(list))
|
451
|
-
|
452
|
-
closeBtn.addEventListener("keydown", (event) => {
|
453
|
-
if (event.key == "Enter") {
|
454
|
-
handleVocabClose(list);
|
455
|
-
}
|
456
|
-
})
|
457
|
-
|
458
|
-
}
|
459
|
-
} else {
|
460
|
-
|
461
|
-
// List does not have more than 2 pairs of terms and definitions
|
462
|
-
|
463
|
-
// Don't add a close button since there is only one term, but remove the button if it is present
|
464
|
-
for (let listIndex = 0; listIndex < list.children.length; listIndex++) {
|
465
|
-
|
466
|
-
//Check for close all button
|
467
|
-
if (list.children[listIndex].tagName == "BUTTON") {
|
468
|
-
closeBtnPresent = true;
|
469
|
-
}
|
470
|
-
}
|
471
|
-
|
472
|
-
if (closeBtnPresent) {
|
473
|
-
let closeBtn = list.querySelector("button");
|
474
|
-
closeBtn.style.display = "none";
|
475
|
-
}
|
476
|
-
}
|
477
|
-
})
|
478
|
-
|
479
|
-
// Loop through all the terms and apply click and keydown event
|
480
|
-
for (let activeTerm = 0; activeTerm < vocabTerms.length; activeTerm++) {
|
481
|
-
// Add click event for toggling vocab terms
|
482
|
-
vocabTerms[activeTerm].addEventListener("click", function () {
|
483
|
-
// When clicked, toggle the active class
|
484
|
-
this.classList.toggle("active");
|
485
|
-
|
486
|
-
// Target the definition <dd> element
|
487
|
-
let termPanel = this.nextElementSibling;
|
488
|
-
|
489
|
-
// Toggle the display from none to block
|
490
|
-
if (termPanel.style.display === "block") {
|
491
|
-
termPanel.style.display = "none";
|
492
|
-
} else {
|
493
|
-
termPanel.style.display = "block";
|
494
|
-
}
|
495
|
-
|
496
|
-
// Start a while loop to continue through the DOM
|
497
|
-
while (termPanel.nextElementSibling) {
|
498
|
-
// Move to the next sibling element
|
499
|
-
termPanel = termPanel.nextElementSibling;
|
500
|
-
|
501
|
-
// Check if the current element is a <dd>
|
502
|
-
if (termPanel.tagName === "DD") {
|
503
|
-
// Toggle the display from none to block
|
504
|
-
if (termPanel.style.display === "block") {
|
505
|
-
termPanel.style.display = "none";
|
506
|
-
} else {
|
507
|
-
termPanel.style.display = "block";
|
508
|
-
}
|
509
|
-
} else {
|
510
|
-
// Stop the loop if the current element is not a <dd>
|
511
|
-
break;
|
512
|
-
}
|
513
|
-
}
|
514
|
-
});
|
515
|
-
|
516
|
-
|
517
|
-
// Add keydown event for toggling vocab terms
|
518
|
-
vocabTerms[activeTerm].addEventListener("keydown", function (e) {
|
519
|
-
|
520
|
-
// When user hits enter, toggle the active class
|
521
|
-
if (e.key === "Enter") {
|
522
|
-
// When clicked, toggle the active class
|
523
|
-
this.classList.toggle("active");
|
524
|
-
|
525
|
-
// Target the definition <dd> element
|
526
|
-
let termPanel = this.nextElementSibling;
|
527
|
-
|
528
|
-
// Toggle the display from none to block
|
529
|
-
if (termPanel.style.display === "block") {
|
530
|
-
termPanel.style.display = "none";
|
531
|
-
} else {
|
532
|
-
termPanel.style.display = "block";
|
533
|
-
}
|
534
|
-
// Start a while loop to continue through the DOM
|
535
|
-
while (termPanel.nextElementSibling) {
|
536
|
-
// Move to the next sibling element
|
537
|
-
termPanel = termPanel.nextElementSibling;
|
538
|
-
|
539
|
-
// Check if the current element is a <dd>
|
540
|
-
if (termPanel.tagName === "DD") {
|
541
|
-
// Toggle the display from none to block
|
542
|
-
if (termPanel.style.display === "block") {
|
543
|
-
termPanel.style.display = "none";
|
544
|
-
} else {
|
545
|
-
termPanel.style.display = "block";
|
546
|
-
}
|
547
|
-
} else {
|
548
|
-
// Stop the loop if the current element is not a <dd>
|
549
|
-
break;
|
550
|
-
}
|
551
|
-
}
|
552
|
-
}
|
553
|
-
});
|
554
|
-
}
|
555
|
-
}
|
556
|
-
if (vocabListWidget) { callVocabList(); }
|
557
|
-
// Clean up HTML
|
558
|
-
const cleanMarkup = () => {
|
559
|
-
// Remove role="presentation" attr from any element that has it
|
560
|
-
if (rolePres) {
|
561
|
-
rolePres.forEach((roleElem) => roleElem.removeAttribute("role"));
|
562
|
-
}
|
563
|
-
// Set functino to remove atrributes from elements
|
564
|
-
const discardAttributes = (element, ...attributes) => {
|
565
|
-
attributes.forEach((attribute) => element.removeAttribute(attribute));
|
566
|
-
}
|
567
|
-
// Remove attributes from tables
|
568
|
-
const tableElems = document.querySelectorAll("table, thead, tbody, tr, th, td");
|
569
|
-
tableElems.forEach((elem) => {
|
570
|
-
discardAttributes(elem, "cellspacing", "cellpadding", "width", "style");
|
571
|
-
});
|
572
|
-
};
|
573
|
-
cleanMarkup();
|
574
|
-
const handleContentLockWidget = (contentLockWidgets) => {
|
575
|
-
|
576
|
-
const lockedContentQuizIncorrectAnswer = "Incorrect response. Please try again."; // Error response for quizzes
|
577
|
-
const lockedContentQuizEmptyResponse = "Please select an option before submitting."; // Error response for quizzes
|
578
|
-
const unlockBtnContent = []; // Save text content of unlock button
|
579
|
-
|
580
|
-
// ––––––––– Functions –––––––––
|
581
|
-
|
582
|
-
// Function to run when local storage is updated
|
583
|
-
const handleContentLockLocalStorageUpdate = (event, courseNumber) => {
|
584
|
-
if (event && event.key === "contentLockData" && event.newValue) {
|
585
|
-
// Update contentLockData variable
|
586
|
-
contentLockData = JSON.parse(event.newValue);
|
587
|
-
// Run your code here
|
588
|
-
checkHiddenContent(courseNumber);
|
589
|
-
}
|
590
|
-
}
|
591
|
-
|
592
|
-
// Function that checks key status, toggles classes, and updates local storage, and checks for other content areas
|
593
|
-
const unlockCheck = (contentLockKeyNum, courseNumber, buttonIndex) => {
|
594
|
-
|
595
|
-
// Toggle the key status to true to unlock the content
|
596
|
-
contentLockData[courseNumber].keys[contentLockKeyNum] = true;
|
597
|
-
|
598
|
-
// Toggle classes based on key status
|
599
|
-
if (contentLockData[courseNumber].keys[contentLockKeyNum]) {
|
600
|
-
lockedContent[buttonIndex].classList.add("open");
|
601
|
-
contentLockInstructions[buttonIndex].classList.add("complete");
|
602
|
-
} else {
|
603
|
-
lockedContent[buttonIndex].classList.remove("open");
|
604
|
-
contentLockInstructions[buttonIndex].classList.remove("complete");
|
605
|
-
}
|
606
|
-
|
607
|
-
// Save the updated contentLockData object to local storage
|
608
|
-
localStorage.setItem("contentLockData", JSON.stringify(contentLockData));
|
609
|
-
|
610
|
-
// Update the hidden content based on the key status
|
611
|
-
checkHiddenContent(courseNumber);
|
612
|
-
}
|
613
|
-
|
614
|
-
// Function to display the error message from the form
|
615
|
-
const displayError = (quizForm, contentLockErrorMessage) => {
|
616
|
-
|
617
|
-
// Create a new paragraph element for the error message
|
618
|
-
let contentLockerrorContainer = quizForm.querySelector(".error-container");
|
619
|
-
let errorMessage = document.createElement("span");
|
620
|
-
|
621
|
-
// Clear any previous error messages
|
622
|
-
contentLockerrorContainer.innerHTML = "";
|
623
|
-
|
624
|
-
// Clear error message
|
625
|
-
errorMessage.textContent = "";
|
626
|
-
|
627
|
-
// Append the blank text message to the container
|
628
|
-
contentLockerrorContainer.appendChild(errorMessage);
|
629
|
-
|
630
|
-
// Add a timer to apply the new error message to ensure the user understands it is a new error message
|
631
|
-
setTimeout(function () {
|
632
|
-
|
633
|
-
// Clear error message
|
634
|
-
errorMessage.textContent = contentLockErrorMessage;
|
635
|
-
|
636
|
-
// Append the new error message to the container
|
637
|
-
contentLockerrorContainer.appendChild(errorMessage);
|
638
|
-
|
639
|
-
}, 200); // 200 milliseconds = .2 seconds
|
640
|
-
}
|
641
|
-
|
642
|
-
// Quiz checking function
|
643
|
-
const submitContentLockQuiz = (contentLockKeyNum) => {
|
644
|
-
let contentLockQuizForm = document.getElementById(`quiz${contentLockKeyNum}`);
|
645
|
-
|
646
|
-
// This selects the option that the user selected
|
647
|
-
let selectedOption = contentLockQuizForm.querySelector("input[name='options']:checked");
|
648
|
-
|
649
|
-
// This finds the input option that had the correct answer
|
650
|
-
let contentLockQuizcorrectAnswer = contentLockQuizForm.querySelector("input.correct-answer");
|
651
|
-
|
652
|
-
// Initialize a variable for error message, that can changed based on which error
|
653
|
-
let contentLockErrorMessage = "";
|
654
|
-
|
655
|
-
// If there is a selected option, run the logic to see if it matches
|
656
|
-
if (selectedOption) {
|
657
|
-
// If selected option matches correct answer
|
658
|
-
if (selectedOption.value === contentLockQuizcorrectAnswer.value) {
|
659
|
-
|
660
|
-
// Tells the user they selected the correct response
|
661
|
-
alert("Correct response");
|
662
|
-
return true;
|
663
|
-
} else {
|
664
|
-
|
665
|
-
// Sets the error message for the user
|
666
|
-
contentLockErrorMessage = lockedContentQuizIncorrectAnswer;
|
667
|
-
displayError(contentLockQuizForm, contentLockErrorMessage)
|
668
|
-
return false;
|
669
|
-
}
|
670
|
-
} else {
|
671
|
-
contentLockErrorMessage = lockedContentQuizEmptyResponse;
|
672
|
-
displayError(contentLockQuizForm, contentLockErrorMessage)
|
673
|
-
return false;
|
674
|
-
}
|
675
|
-
}
|
676
|
-
|
677
|
-
/* This function loops through each content area on the page
|
678
|
-
* and checks the status of the data-key to see if it should
|
679
|
-
* be unlocked or not
|
680
|
-
*/
|
681
|
-
const checkHiddenContent = (courseNumber) => {
|
682
|
-
|
683
|
-
// Get data-key number for each content area
|
684
|
-
lockedContent.forEach((contentArea, contentAreaIndex) => {
|
685
|
-
let contentLockKeyNum = contentArea.getAttribute("data-key");
|
686
|
-
|
687
|
-
// Toggle classes based on key status
|
688
|
-
if (contentLockData[courseNumber].keys[contentLockKeyNum]) {
|
689
|
-
lockedContent[contentAreaIndex].classList.add("open");
|
690
|
-
contentLockInstructions[contentAreaIndex].classList.add("complete");
|
691
|
-
} else {
|
692
|
-
lockedContent[contentAreaIndex].classList.remove("open");
|
693
|
-
contentLockInstructions[contentAreaIndex].classList.remove("complete");
|
694
|
-
}
|
695
|
-
});
|
696
|
-
}
|
697
|
-
|
698
|
-
/* This function is when a user clicks or keysdown to unlock
|
699
|
-
* a content area. This function is the same wether clicked on
|
700
|
-
* or if it is through a keyboard
|
701
|
-
*/
|
702
|
-
const handleContentUnlock = (unlockButton, buttonIndex, unlockBtnContent, courseNumber) => {
|
703
|
-
|
704
|
-
/* This variable acts as a flag, for determining if a content unlock button is related to a quiz
|
705
|
-
* or if the content related to this button is supposed to use a confirmation window.
|
706
|
-
*/
|
707
|
-
let quizElement = false;
|
708
|
-
|
709
|
-
// Loop through all the contentUnlockBtns and add an event listener
|
710
|
-
|
711
|
-
let contentLockKeyNum = lockedContent[buttonIndex].getAttribute("data-key");
|
712
|
-
|
713
|
-
// This is used as a flag. Needs to be set to true in order to unlock the content
|
714
|
-
let contentLockAnswerConfirmed = false;
|
715
|
-
|
716
|
-
//Query Selector to see if there is a quiz option or not
|
717
|
-
if (unlockButton.tagName === "INPUT") {
|
718
|
-
quizElement = true;
|
719
|
-
}
|
720
|
-
|
721
|
-
// If quiz exists for this data key number, otherwise, do the confirmation window
|
722
|
-
if (quizElement) {
|
723
|
-
|
724
|
-
// Show the html for the quiz. The html will handle the quiz functionality. Returns true or false.
|
725
|
-
contentLockAnswerConfirmed = submitContentLockQuiz(contentLockKeyNum)
|
726
|
-
if (contentLockAnswerConfirmed) {
|
727
|
-
|
728
|
-
// Unlock the content and perform other related actions (see function for more)
|
729
|
-
unlockCheck(contentLockKeyNum, courseNumber, buttonIndex)
|
730
|
-
}
|
731
|
-
} else {
|
732
|
-
|
733
|
-
/* Add a confirmation window that pops up and waits for the user to confirm
|
734
|
-
* the instructions were followed to unlock the content(if no quiz is available)
|
735
|
-
*/
|
736
|
-
contentLockAnswerConfirmed = window.confirm(`Please confirm: ${unlockBtnContent[buttonIndex]}`)
|
737
|
-
|
738
|
-
if (contentLockAnswerConfirmed) {
|
739
|
-
unlockCheck(contentLockKeyNum, courseNumber, buttonIndex)
|
740
|
-
}
|
741
|
-
}
|
742
|
-
}
|
743
|
-
|
744
|
-
// –––––––––––––––– End of Functions ––––––––––––––––
|
745
|
-
|
746
|
-
// Checks the URL for the course number
|
747
|
-
const currentURL = window.parent.location.href;
|
748
|
-
const match = currentURL.match(/\/content\/(\d+)/);
|
749
|
-
const courseNumber = match ? match[1] : null;
|
750
|
-
|
751
|
-
// Error Checking - course number exists
|
752
|
-
if (!courseNumber) {
|
753
|
-
console.log("Document Error: course number does not exist");
|
754
|
-
}
|
755
|
-
|
756
|
-
// Letters are used in inputs as values. Each letter represents an input option.
|
757
|
-
const optionLetters = ["a", "b", "c", "d", "e", "f", "g", "h", "i"];
|
758
|
-
|
759
|
-
// Create an object that keeps track of keys and their statuses
|
760
|
-
const contentLockData = JSON.parse(localStorage.getItem("contentLockData")) || {};
|
761
|
-
|
762
|
-
// Add event listener for storage changes
|
763
|
-
window.addEventListener("storage", (event) => {
|
764
|
-
if (courseNumber) {
|
765
|
-
handleContentLockLocalStorageUpdate(event, courseNumber);
|
766
|
-
}
|
767
|
-
});
|
768
|
-
|
769
|
-
// Apply the quiz IDs and search if quizzes are being used
|
770
|
-
contentLockWidgets.forEach((contentLockWidget, contentLockWidgetIndex) => {
|
771
|
-
|
772
|
-
let contentLockForm = contentLockWidget.querySelector(".quiz form");
|
773
|
-
|
774
|
-
/* If a quiz is present in content lock widget, then go through the page
|
775
|
-
* and assign the quiz ID based on the data-key for the corresponding
|
776
|
-
* locked-content.
|
777
|
-
*/
|
778
|
-
if (contentLockForm) {
|
779
|
-
let lockedContent = contentLockWidget.querySelector(".locked-content");
|
780
|
-
let contentLockKeyNum = lockedContent.getAttribute("data-key");
|
781
|
-
let contentLockQuizcorrectAnswer = contentLockWidget.querySelector(".quiz .correct-answer");
|
782
|
-
|
783
|
-
// Error checking - locked-content class
|
784
|
-
if (!lockedContent) {
|
785
|
-
console.log(`Document Error: missing the locked-content class for widget number ${contentLockWidgetIndex + 1} on this page`)
|
786
|
-
}
|
787
|
-
|
788
|
-
// Error checking - data key
|
789
|
-
if (!contentLockKeyNum) {
|
790
|
-
console.log(`Document Error: missing data-key for locked content for widget number ${contentLockWidgetIndex + 1} on this page`)
|
791
|
-
}
|
792
|
-
|
793
|
-
// Error Check - correct-answer class for the quiz
|
794
|
-
if (!contentLockQuizcorrectAnswer) {
|
795
|
-
console.log(`Document Error: missing correct-answer for content lock quiz for widget number ${contentLockWidgetIndex + 1} on this page`)
|
796
|
-
}
|
797
|
-
|
798
|
-
// Assign each form an ID
|
799
|
-
contentLockForm.id = `quiz${contentLockKeyNum}`;
|
800
|
-
|
801
|
-
// Search for error container in the widget
|
802
|
-
let errorContainerExists = contentLockWidget.querySelector(".error-container");
|
803
|
-
|
804
|
-
// If the error container doesn't exist, add it via HTML
|
805
|
-
if (!errorContainerExists) {
|
806
|
-
let errorContainer = document.createElement("div");
|
807
|
-
errorContainer.classList.add("error-container");
|
808
|
-
let contentLockSubmitBtn = contentLockWidget.querySelector("input[value='Submit']");
|
809
|
-
if (contentLockSubmitBtn) {
|
810
|
-
// Add the error container above the submit button in the quiz
|
811
|
-
contentLockSubmitBtn.insertAdjacentHTML("beforebegin", errorContainer.outerHTML)
|
812
|
-
} else {
|
813
|
-
console.log(`Document Error: quiz is missing an input with value 'Submit' for widget number ${contentLockWidgetIndex + 1} on this page.`)
|
814
|
-
}
|
815
|
-
}
|
816
|
-
}
|
817
|
-
// If the content lock widget does not use a form and uses a confirmation window instead
|
818
|
-
else {
|
819
|
-
// Error checking - ensure it has an unlock button in the HTML
|
820
|
-
let unlockBtn = contentLockWidget.querySelector(".unlock-btn");
|
821
|
-
if (!unlockBtn) {
|
822
|
-
console.log(`Document Error: locked content area (that is not using a quiz) is missing <a> with .unlock-btn for widget number ${contentLockWidgetIndex + 1} on this page.`)
|
823
|
-
}
|
824
|
-
}
|
825
|
-
})
|
826
|
-
|
827
|
-
/* Function to apply attributes to quiz elements. This function uses
|
828
|
-
* an asynchronous function to ensure that the JS doesn't run until
|
829
|
-
* after this function is complete and the promise has been returned.
|
830
|
-
* This helps ensure that no other querySelectors are broken.
|
831
|
-
*/
|
832
|
-
const applyAttributesToQuiz = () => {
|
833
|
-
|
834
|
-
// Returns the resolve value or reject value depending on if the promise is successful or fails
|
835
|
-
return new Promise((resolve, reject) => {
|
836
|
-
let contentLockQuizForms = document.querySelectorAll(".quiz form");
|
837
|
-
|
838
|
-
// If there are forms, then apply the specific attributes to the inputs
|
839
|
-
if (contentLockQuizForms) {
|
840
|
-
contentLockQuizForms.forEach((quizForm) => {
|
841
|
-
let quizInputs = quizForm.querySelectorAll("input");
|
842
|
-
|
843
|
-
// Loop through all the inputs and apply the correct attributes
|
844
|
-
quizInputs.forEach((quizInput, InputIndex) => {
|
845
|
-
|
846
|
-
// If the values are null, then populate it with the right attributes
|
847
|
-
if (!quizInput.getAttribute("value")) {
|
848
|
-
quizInput.setAttribute("value", optionLetters[InputIndex]);
|
849
|
-
quizInput.setAttribute("name", "options");
|
850
|
-
quizInput.setAttribute("type", "radio");
|
851
|
-
}
|
852
|
-
|
853
|
-
// If the input has the value "submit" then apply different attributes for submit button
|
854
|
-
else if (quizInput.getAttribute("value") == "Submit") {
|
855
|
-
quizInput.setAttribute("type", "button");
|
856
|
-
quizInput.classList.add("unlock-btn");
|
857
|
-
}
|
858
|
-
})
|
859
|
-
})
|
860
|
-
}
|
861
|
-
resolve(); // Resolve the Promise once attributes are applied
|
862
|
-
});
|
863
|
-
}
|
864
|
-
applyAttributesToQuiz().then(() => {
|
865
|
-
|
866
|
-
/* This query selector relies on the dynamically added attributes above, so do not put this
|
867
|
-
* variable with the other ones at the top of the file
|
868
|
-
*/
|
869
|
-
const contentUnlockBtns = document.querySelectorAll(".unlock-btn");
|
870
|
-
|
871
|
-
/* Check if the course data object has the course number. If not, then add it.
|
872
|
-
* The contentLockData object that lives in local storage stores the course number and keys.
|
873
|
-
*/
|
874
|
-
if (!contentLockData.hasOwnProperty(courseNumber)) {
|
875
|
-
contentLockData[courseNumber] = {
|
876
|
-
keys: {},
|
877
|
-
};
|
878
|
-
}
|
879
|
-
|
880
|
-
// Get key number for each content area
|
881
|
-
lockedContent.forEach((contentArea, contentAreaIndex) => {
|
882
|
-
let contentLockKeyNum = contentArea.getAttribute("data-key");
|
883
|
-
|
884
|
-
// Error check - ensure it is wrapped in parent container
|
885
|
-
let contentLockWrapper = contentArea.parentElement;
|
886
|
-
if (contentLockWrapper) {
|
887
|
-
let hasContentLockWrapper = contentLockWrapper.classList.contains("content-lock-widget");
|
888
|
-
if (!hasContentLockWrapper) {
|
889
|
-
console.log(`Document Error: content lock widget wrapper is missing .content-lock-widget class for widget number ${contentAreaIndex + 1} on this page`);
|
890
|
-
}
|
891
|
-
} else {
|
892
|
-
console.log(`Document Error: content lock widget is missing parent container for widget number ${contentAreaIndex + 1} on this page`);
|
893
|
-
}
|
894
|
-
|
895
|
-
// Error check - ensure it has an instructions <div> as sibling element
|
896
|
-
let lockedContentSibling = contentArea.nextElementSibling;
|
897
|
-
if (lockedContentSibling) {
|
898
|
-
let hasInstructions = lockedContentSibling.classList.contains("instructions");
|
899
|
-
if (!hasInstructions) {
|
900
|
-
console.log(`Document Error: instructions <div> is missing .instructions class for widget number ${contentAreaIndex + 1} on this page`);
|
901
|
-
}
|
902
|
-
} else {
|
903
|
-
console.log(`Document Error: locked content area is missing <div> with instructions for widget number ${contentAreaIndex + 1} on this page`);
|
904
|
-
}
|
905
|
-
|
906
|
-
// Check if key already exists within the course, if it doesn't, add the key
|
907
|
-
if (!contentLockData[courseNumber].keys[contentLockKeyNum]) {
|
908
|
-
// Add the key to the course keys and set it equal to false by default
|
909
|
-
contentLockData[courseNumber].keys[contentLockKeyNum] = false;
|
910
|
-
}
|
911
|
-
});
|
912
|
-
|
913
|
-
// Go through each show/hide button and add click listener
|
914
|
-
contentUnlockBtns.forEach((unlockButton, buttonIndex) => {
|
915
|
-
|
916
|
-
// For each unlock-btn take the text and put the text content into an array for the confirmation window to use
|
917
|
-
unlockBtnContent.push(unlockButton.textContent);
|
918
|
-
|
919
|
-
// Add tab index to each unlock button
|
920
|
-
unlockButton.setAttribute("tabIndex", "0");
|
921
|
-
|
922
|
-
// Applies click event listener (and by default in HTML the keydown event will fire this function) to unlock the content
|
923
|
-
unlockButton.addEventListener("click", () => {
|
924
|
-
handleContentUnlock(unlockButton, buttonIndex, unlockBtnContent, courseNumber)
|
925
|
-
});
|
926
|
-
|
927
|
-
// Apply initial classes based on key status
|
928
|
-
let contentLockKeyNum = lockedContent[buttonIndex].getAttribute("data-key");
|
929
|
-
|
930
|
-
// Make sure contentLockData[courseNumber] is initialized
|
931
|
-
if (!contentLockData[courseNumber]) {
|
932
|
-
contentLockData[courseNumber] = {
|
933
|
-
keys: {},
|
934
|
-
id: 0
|
935
|
-
};
|
936
|
-
}
|
937
|
-
|
938
|
-
if (contentLockData[courseNumber].keys[contentLockKeyNum]) {
|
939
|
-
lockedContent[buttonIndex].classList.add("open");
|
940
|
-
contentLockInstructions[buttonIndex].classList.add("complete");
|
941
|
-
}
|
942
|
-
});
|
943
|
-
|
944
|
-
// Call the checkHiddenContent function initially
|
945
|
-
checkHiddenContent(courseNumber);
|
946
|
-
|
947
|
-
// Save the updated contentLockData object to local storage
|
948
|
-
localStorage.setItem("contentLockData", JSON.stringify(contentLockData));
|
949
|
-
|
950
|
-
}).catch(error => {
|
951
|
-
// Handle the error if needed
|
952
|
-
console.log("Error occurred during applyAttributesToQuiz:", error);
|
953
|
-
});
|
954
|
-
}
|
955
|
-
|
956
|
-
document.addEventListener('DOMContentLoaded', () => {
|
957
|
-
// Your script code here
|
958
|
-
if (contentLockWidgets) { handleContentLockWidget(contentLockWidgets) }
|
959
|
-
});
|
960
|
-
// Flip Card Widget
|
961
|
-
function callFlipCardWidget() {
|
962
|
-
// Loop through each card
|
963
|
-
flipCards.forEach((flipCardGroup) => {
|
964
|
-
|
965
|
-
let flipCard = flipCardGroup.querySelectorAll(".flip-card");
|
966
|
-
let innerFlipCard = flipCardGroup.querySelectorAll(".inner-card");
|
967
|
-
let numOfCardsInGroup = flipCardGroup.children.length;
|
968
|
-
|
969
|
-
// Check to ensure each card has the .flip-card class
|
970
|
-
if (numOfCardsInGroup !== flipCard.length) {
|
971
|
-
console.log("Document error: missing .flip-card class for flip card widget");
|
972
|
-
}
|
973
|
-
// Check to ensure each card has the .inner-card class
|
974
|
-
if (numOfCardsInGroup !== innerFlipCard.length) {
|
975
|
-
console.log("Document error: missing .inner-card class for flip card widget");
|
976
|
-
}
|
977
|
-
|
978
|
-
flipCard.forEach((card) => {
|
979
|
-
let innerFlipCard = card.querySelectorAll(".inner-card");
|
980
|
-
|
981
|
-
innerFlipCard.forEach((innerCard) => {
|
982
|
-
innerCard.setAttribute("tabindex", 0); // Add tab index to allow flip cards to be tabbable
|
983
|
-
innerCard.addEventListener("click", () => {
|
984
|
-
innerCard.offsetHeight; // Force reflow by accessing the offsetHeight property
|
985
|
-
innerCard.classList.toggle("flip");
|
986
|
-
})
|
987
|
-
|
988
|
-
|
989
|
-
// Add a keydownevent event to each card
|
990
|
-
innerCard.addEventListener("keydown", (event) => {
|
991
|
-
if (event.key === "Enter") {
|
992
|
-
// Force reflow by accessing the offsetHeight property
|
993
|
-
innerCard.offsetHeight;
|
994
|
-
innerCard.classList.toggle("flip");
|
995
|
-
}
|
996
|
-
})
|
997
|
-
})
|
998
|
-
})
|
999
|
-
})
|
1000
|
-
}
|
1001
|
-
|
1002
|
-
// If flip cards are present in the file, run this code
|
1003
|
-
if (flipCards) { callFlipCardWidget() }
|
1004
|
-
if (document.querySelector("body[focus-reader]")) {
|
1005
|
-
|
1006
|
-
// Initializations
|
1007
|
-
let focusOn = false;
|
1008
|
-
|
1009
|
-
const fr_contentWrapper = document.querySelector("#content-wrapper");
|
1010
|
-
const fr_thisBody = document.querySelector("body[focus-reader]");
|
1011
|
-
|
1012
|
-
// Add fontAwesome to header
|
1013
|
-
const fr_pageHead = document.querySelector("head");
|
1014
|
-
const fontAweLink = document.createElement("link");
|
1015
|
-
fontAweLink.rel = "stylesheet";
|
1016
|
-
fontAweLink.href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css";
|
1017
|
-
fr_pageHead.append(fontAweLink);
|
1018
|
-
|
1019
|
-
// add intersection observer to head tag
|
1020
|
-
const observerScript = document.createElement("script");
|
1021
|
-
observerScript.setAttribute("src", "https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver");
|
1022
|
-
fr_pageHead.append(observerScript);
|
1023
|
-
|
1024
|
-
// Create Switches container
|
1025
|
-
const fr_switchesContainer = document.createElement("div");
|
1026
|
-
fr_switchesContainer.className = "focus-reader-switches";
|
1027
|
-
fr_thisBody.append(fr_switchesContainer);
|
1028
|
-
|
1029
|
-
// Focus-text container
|
1030
|
-
const fr_focusTextContainer = document.createElement("div");
|
1031
|
-
fr_switchesContainer.append(fr_focusTextContainer);
|
1032
|
-
|
1033
|
-
// Focus-text Icon
|
1034
|
-
const fr_focusTextIcon = document.createElement("p");
|
1035
|
-
fr_focusTextIcon.innerHTML = "Focus Text"
|
1036
|
-
fr_focusTextContainer.append(fr_focusTextIcon);
|
1037
|
-
|
1038
|
-
// info icon
|
1039
|
-
const fr_infoIcon = document.createElement("i");
|
1040
|
-
fr_infoIcon.classList.add("fa-solid");
|
1041
|
-
fr_infoIcon.classList.add("fa-circle-info");
|
1042
|
-
fr_focusTextContainer.append(fr_infoIcon);
|
1043
|
-
|
1044
|
-
// info tooltip
|
1045
|
-
const fr_infoTooltip = document.createElement("span");
|
1046
|
-
const focusReaderTooltipText = "Highlight text as you scroll";
|
1047
|
-
fr_infoTooltip.classList.add("info-tooltip");
|
1048
|
-
fr_infoTooltip.innerHTML = focusReaderTooltipText;
|
1049
|
-
fr_infoIcon.append(fr_infoTooltip);
|
1050
|
-
|
1051
|
-
// Focus-text switch button
|
1052
|
-
const fr_focusTextSwitch = document.createElement("button");
|
1053
|
-
fr_focusTextSwitch.id = "focus-text";
|
1054
|
-
fr_focusTextSwitch.innerHTML = (focusOn) ? `<i class="fas fa-toggle-on"></i>` : `<i class="fas fa-toggle-off"></i>`;
|
1055
|
-
fr_focusTextContainer.append(fr_focusTextSwitch);
|
1056
|
-
|
1057
|
-
// Focus-text logic
|
1058
|
-
fr_focusTextSwitch.addEventListener("click", () => {
|
1059
|
-
if (focusOn) {
|
1060
|
-
focusOn = !focusOn;
|
1061
|
-
fr_contentWrapper.removeAttribute("on");
|
1062
|
-
fr_focusTextSwitch.innerHTML = (focusOn) ? `<i class="fas fa-toggle-on"></i>` : `<i class="fas fa-toggle-off"></i>`;
|
1063
|
-
removeSpans(fr_contentWrapper);
|
1064
|
-
} else {
|
1065
|
-
focusOn = !focusOn;
|
1066
|
-
fr_contentWrapper.setAttribute("on", "");
|
1067
|
-
fr_focusTextSwitch.innerHTML = (focusOn) ? `<i class="fas fa-toggle-on"></i>` : `<i class="fas fa-toggle-off"></i>`;
|
1068
|
-
const elements = document.querySelectorAll("#content-wrapper .content-body :is(p, li, dd, dt, blockquote)");
|
1069
|
-
elements.forEach(element => {
|
1070
|
-
traverseAndWrapTextInSpans(element, focusText);
|
1071
|
-
});
|
1072
|
-
}
|
1073
|
-
});
|
1074
|
-
|
1075
|
-
const options = {
|
1076
|
-
root: null,
|
1077
|
-
rootMargin: '-48.9% 0px',
|
1078
|
-
threshold: 0.00
|
1079
|
-
};
|
1080
|
-
|
1081
|
-
const focusText = new IntersectionObserver(entries => {
|
1082
|
-
entries.forEach(entry => {
|
1083
|
-
if (entry.isIntersecting) {
|
1084
|
-
entry.target.closest("span").classList.add("focus-text");
|
1085
|
-
} else {
|
1086
|
-
entry.target.closest("span").classList.remove("focus-text");
|
1087
|
-
}
|
1088
|
-
});
|
1089
|
-
}, options);
|
1090
|
-
|
1091
|
-
|
1092
|
-
}
|
1093
|
-
|
1094
|
-
function traverseAndWrapTextInSpans(node, focusText) {
|
1095
|
-
if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== '') {
|
1096
|
-
// Split the text into words
|
1097
|
-
const words = node.textContent.split(' ');
|
1098
|
-
|
1099
|
-
// Wrap each word in a span
|
1100
|
-
words.forEach(word => {
|
1101
|
-
if (word !== '') {
|
1102
|
-
const span = document.createElement("span");
|
1103
|
-
span.textContent = word + ' '; // Add a space after the word to preserve spacing
|
1104
|
-
|
1105
|
-
// Observe the span
|
1106
|
-
focusText.observe(span);
|
1107
|
-
|
1108
|
-
// Insert the span before the text node
|
1109
|
-
node.parentNode.insertBefore(span, node);
|
1110
|
-
}
|
1111
|
-
});
|
1112
|
-
|
1113
|
-
// Remove the original text node
|
1114
|
-
node.parentNode.removeChild(node);
|
1115
|
-
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
1116
|
-
// Recursively process child nodes
|
1117
|
-
Array.from(node.childNodes).forEach(childNode => {
|
1118
|
-
traverseAndWrapTextInSpans(childNode, focusText);
|
1119
|
-
});
|
1120
|
-
}
|
1121
|
-
}
|
1122
|
-
|
1123
|
-
function removeSpans(node) {
|
1124
|
-
if (node.nodeType === Node.ELEMENT_NODE) {
|
1125
|
-
// 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
|
1126
|
-
if (node.tagName === 'SPAN' && (node.className === 'focus-text' || node.className === '' || node.textContent.trim() === '' || node.attributes.length === 0)) {
|
1127
|
-
const textNode = document.createTextNode(node.textContent);
|
1128
|
-
node.parentNode.insertBefore(textNode, node);
|
1129
|
-
node.parentNode.removeChild(node);
|
1130
|
-
} else {
|
1131
|
-
// Recursively process child nodes
|
1132
|
-
Array.from(node.childNodes).forEach(childNode => {
|
1133
|
-
removeSpans(childNode);
|
1134
|
-
});
|
1135
|
-
}
|
1136
|
-
}
|
1137
|
-
}
|
1138
|
-
// Change footnotes from 'show' to 'hide'
|
1139
|
-
const footnotes = document.querySelector(".toggle-footnotes");
|
1140
|
-
|
1141
|
-
if (footnotes) {
|
1142
|
-
footnotes.addEventListener("click", () => {
|
1143
|
-
footnotes.innerHTML = (footnotes.innerHTML === "[Show Footnotes]") ? "[Hide Footnotes]" : "[Show Footnotes]";
|
1144
|
-
})
|
1145
|
-
}
|
1146
|
-
// Add h5p resizer dynamically
|
1147
|
-
h5pResizer.setAttribute("src", "https://pima.h5p.com/js/h5p-resizer.js");
|
1148
|
-
h5pResizer.setAttribute("charset", "UTF-8");
|
1149
|
-
h5pResizer.setAttribute("defer", "");
|
1150
|
-
|
1151
|
-
// If any iframes are detected run this function
|
1152
|
-
function checkIframes() {
|
1153
|
-
h5pIframes.forEach(function (h5pIframe) {
|
1154
|
-
const src = h5pIframe.getAttribute("src");
|
1155
|
-
if (src.includes("/d2l/common/dialogs/quickLink") || src.includes("https://pima.h5p.com/content") || src.includes("h5p") && !h5pResizerExists) {
|
1156
|
-
docHead.appendChild(h5pResizer);
|
1157
|
-
}
|
1158
|
-
});
|
1159
|
-
}
|
1160
|
-
|
1161
|
-
// Call function if iframes exist
|
1162
|
-
if (h5pIframes) { checkIframes() }
|
1163
|
-
|
1164
|
-
|
1165
|
-
// Helper JS for Responsive Tables
|
1166
|
-
const initResponsiveTables = () => {
|
1167
|
-
for (let table = 0; table < tables.length; table++) {
|
1168
|
-
let headertext = [],
|
1169
|
-
headers = tables[table].querySelectorAll(".display table th, table.display th, .display-lg table th, table.display-lg th"),
|
1170
|
-
tablebody = tables[table].querySelector(".display table tbody, table.display tbody, .display-lg table tbody, table.display-lg tbody");
|
1171
|
-
for (let header = 0; header < headers.length; header++) {
|
1172
|
-
let current = headers[header];
|
1173
|
-
headertext.push(current.textContent.replace(/\r?\n|\r/, ""));
|
1174
|
-
}
|
1175
|
-
for (let y = 0, row; row = tablebody.rows[y]; y++) {
|
1176
|
-
for (let j = 0, col; col = row.cells[j]; j++) {
|
1177
|
-
col.setAttribute("data-th", headertext[j]);
|
1178
|
-
}
|
1179
|
-
}
|
1180
|
-
}
|
1181
|
-
}
|
1182
|
-
if (tables) {
|
1183
|
-
initResponsiveTables();
|
1184
|
-
}
|
1185
|
-
// Call function with jQuery scripts
|
1186
|
-
const callJquery = () => {
|
1187
|
-
// Toggle Button's Arrow Right Points Down on Click
|
1188
|
-
$('.arrow-right').on('click', function () {
|
1189
|
-
$(this).toggleClass('arrow-down');
|
1190
|
-
});
|
1191
|
-
// TOOLTIP
|
1192
|
-
// Allows Screen readers to toggle a tooltip on click and to say if the tooltip is collapsed or expanded.
|
1193
|
-
$(".tooltip").click(function () {
|
1194
|
-
$(this).children(".tip-hover").toggle();
|
1195
|
-
if ($(this).children(".tip-hover").is(':visible')) {
|
1196
|
-
$(this).attr('aria-expanded', 'true');
|
1197
|
-
$(this).removeClass('hidden');
|
1198
|
-
} else {
|
1199
|
-
$(this).attr('aria-expanded', 'false');
|
1200
|
-
$(this).addClass('hidden');
|
1201
|
-
}
|
1202
|
-
});
|
1203
|
-
let start = 999;
|
1204
|
-
$('.tooltip').each(function (i) {
|
1205
|
-
$(this).css('z-index', start--);
|
1206
|
-
});
|
1207
|
-
$(".tooltip .video-container").parent().css("width", "450px");
|
1208
|
-
}
|
1209
|
-
callJquery();
|
1210
|
-
// This is called by anchor links via onlick="" in the HTML
|
1211
|
-
// Added because default anchor links don't work on all browsers using D2L
|
1212
|
-
const jumpTo = (anchor) => {
|
1213
|
-
document.getElementById(anchor).scrollIntoView();
|
1214
|
-
}
|
1215
|
-
const sliderWidgets = document.querySelectorAll(".slider-widget");
|
1216
|
-
const slideHeight = 300;
|
1217
|
-
|
1218
|
-
// Function to check if all direct children of each slider have the 'slider-item' class
|
1219
|
-
const checkSliderChildren = () => {
|
1220
|
-
[...sliderWidgets].forEach((slider) => {
|
1221
|
-
// Check if every direct child of the slider has the 'slider-item' class
|
1222
|
-
let sliderChildren = [...slider.children].every(child => child.classList.contains("slider-item"));
|
1223
|
-
|
1224
|
-
// Log an error message if any direct child does not have the 'slider-item' class
|
1225
|
-
if (!sliderChildren) {
|
1226
|
-
console.log(`Document error: not all direct children of .slider have the .slider-item class.`);
|
1227
|
-
}
|
1228
|
-
});
|
1229
|
-
};
|
1230
|
-
checkSliderChildren();
|
1231
|
-
|
1232
|
-
// Function to hide all slides except the active one and deactivate all dots except the active one
|
1233
|
-
const hideSlidesAndDots = (sliderItems, sliderDots, newIndex) => {
|
1234
|
-
// Iterate over each slider item
|
1235
|
-
sliderItems.forEach((item, index) => {
|
1236
|
-
// Set the display style based on whether the item is the active slide
|
1237
|
-
item.style.display = index === newIndex ? "block" : "none";
|
1238
|
-
});
|
1239
|
-
// Iterate over each dot
|
1240
|
-
sliderDots.forEach((sliderDot, index) => {
|
1241
|
-
// Toggle the 'active' class based on whether the dot corresponds to the active slide
|
1242
|
-
sliderDot.classList.toggle("active", index === newIndex);
|
1243
|
-
});
|
1244
|
-
};
|
1245
|
-
|
1246
|
-
// Function to update the display of slides and dots when a new slide is selected
|
1247
|
-
const updateSlide = (sliderItems, sliderDots, newIndex) => {
|
1248
|
-
// Call the function to hide all slides and deactivate all dots except for the active ones
|
1249
|
-
hideSlidesAndDots(sliderItems, sliderDots, newIndex);
|
1250
|
-
};
|
1251
|
-
|
1252
|
-
// Function to create a button element with the specified class name
|
1253
|
-
const createSliderButton = (className) => {
|
1254
|
-
// Create a new button element
|
1255
|
-
const sliderButton = document.createElement("button");
|
1256
|
-
// Set the class name of the button
|
1257
|
-
sliderButton.className = className;
|
1258
|
-
// Set tabindex to -1 to skip when tabbing
|
1259
|
-
sliderButton.setAttribute("tabindex", "-1");
|
1260
|
-
// Return the created button
|
1261
|
-
return sliderButton;
|
1262
|
-
};
|
1263
|
-
|
1264
|
-
// Function to vertically center the content of a slide if it is short and does not contain videos
|
1265
|
-
const totalSlideContentHeight = (sliderItem) => {
|
1266
|
-
// Initialize a variable to keep track of the total height of the content
|
1267
|
-
let totalContentHeight = 0;
|
1268
|
-
// Iterate over each child element of the slider item
|
1269
|
-
for (let child of sliderItem.children) {
|
1270
|
-
// Add the height of the child to the total content height
|
1271
|
-
totalContentHeight += child.offsetHeight;
|
1272
|
-
}
|
1273
|
-
// Check if the total content height is less than 300px and the item does not contain a video
|
1274
|
-
if (totalContentHeight < slideHeight && !sliderItem.querySelector(".media-container")) {
|
1275
|
-
// Create a wrapper div to contain the content
|
1276
|
-
const sliderWrapper = document.createElement("div");
|
1277
|
-
// Move each child of the slider item into the wrapper
|
1278
|
-
while (sliderItem.firstChild) {
|
1279
|
-
sliderWrapper.appendChild(sliderItem.firstChild);
|
1280
|
-
}
|
1281
|
-
// Append the wrapper to the slider item
|
1282
|
-
sliderItem.appendChild(sliderWrapper);
|
1283
|
-
// Add the 'short-content' class to the slider item to apply vertical centering
|
1284
|
-
sliderItem.classList.add("short-content");
|
1285
|
-
}
|
1286
|
-
};
|
1287
|
-
|
1288
|
-
// Initialize each slider with navigation buttons, dots, and keyboard navigation
|
1289
|
-
[...sliderWidgets].forEach((slider, index) => {
|
1290
|
-
// Set accessibility attributes for the slider
|
1291
|
-
slider.setAttribute("tabindex", "0");
|
1292
|
-
slider.setAttribute("role", "group");
|
1293
|
-
|
1294
|
-
// Create a hidden label for the slider for screen readers
|
1295
|
-
const sliderLabel = document.createElement("span");
|
1296
|
-
sliderLabel.id = `slider-label-${index + 1}`;
|
1297
|
-
sliderLabel.textContent = "Interactive Slider";
|
1298
|
-
sliderLabel.hidden = true;
|
1299
|
-
// Insert the label at the beginning of the slider
|
1300
|
-
slider.prepend(sliderLabel);
|
1301
|
-
|
1302
|
-
// Associate the label with the slider for screen readers
|
1303
|
-
slider.setAttribute("aria-labelledby", `slider-label-${index + 1}`);
|
1304
|
-
|
1305
|
-
// Initialize the current index for the slider
|
1306
|
-
let currentSlide = 0;
|
1307
|
-
// Select all slider items within the current slider
|
1308
|
-
const sliderItems = [...slider.querySelectorAll(".slider-item")];
|
1309
|
-
// Create a div to contain the dots for navigation
|
1310
|
-
const sliderDotsBar = document.createElement("div");
|
1311
|
-
sliderDotsBar.className = "slider-dots-bar";
|
1312
|
-
// Append the dots bar to the slider
|
1313
|
-
slider.appendChild(sliderDotsBar);
|
1314
|
-
// Initialize an array to keep track of the dots
|
1315
|
-
let sliderDots = [];
|
1316
|
-
|
1317
|
-
// Create dots for each slide and set up click events to navigate to the slide
|
1318
|
-
sliderItems.forEach((sliderItem, i) => {
|
1319
|
-
// Create a span element to represent a dot
|
1320
|
-
const sliderDot = document.createElement("span");
|
1321
|
-
sliderDot.className = "slider-dot";
|
1322
|
-
// Add a click event listener to the dot
|
1323
|
-
sliderDot.addEventListener("click", () => {
|
1324
|
-
// Update the current index to the index of the clicked dot
|
1325
|
-
currentSlide = i;
|
1326
|
-
// Call the function to update the slide display
|
1327
|
-
hideSlidesAndDots(sliderItems, sliderDots, currentSlide);
|
1328
|
-
// Ensure the current slide is displayed
|
1329
|
-
sliderItems[currentSlide].style.display = "block";
|
1330
|
-
// Add the 'active' class to the current dot
|
1331
|
-
sliderDot.classList.add("active");
|
1332
|
-
// Call the function to align the slide content if necessary
|
1333
|
-
totalSlideContentHeight(sliderItems[currentSlide]);
|
1334
|
-
});
|
1335
|
-
// Append the dot to the dots bar
|
1336
|
-
sliderDotsBar.appendChild(sliderDot);
|
1337
|
-
// Add the dot to the array of dots
|
1338
|
-
sliderDots.push(sliderDot);
|
1339
|
-
|
1340
|
-
// Hide the slide if it's not the current one
|
1341
|
-
sliderItem.style.display = i !== currentSlide ? "none" : "block";
|
1342
|
-
// If it's the current slide, add the 'active' class to the corresponding dot
|
1343
|
-
if (i === currentSlide) {
|
1344
|
-
sliderDot.classList.add("active");
|
1345
|
-
}
|
1346
|
-
});
|
1347
|
-
|
1348
|
-
// Create navigation buttons and set up click events to navigate between slides
|
1349
|
-
const sliderLeftArrow = createSliderButton("slider-arrow icon-chevron-left");
|
1350
|
-
const sliderRightArrow = createSliderButton("slider-arrow icon-chevron-right");
|
1351
|
-
// Append the navigation buttons to the slider
|
1352
|
-
slider.appendChild(sliderLeftArrow);
|
1353
|
-
slider.appendChild(sliderRightArrow);
|
1354
|
-
|
1355
|
-
// Add a click event listener to the left arrow button
|
1356
|
-
sliderLeftArrow.addEventListener("click", () => {
|
1357
|
-
// Update the current index to the previous slide, wrapping around if necessary
|
1358
|
-
currentSlide = currentSlide > 0 ? currentSlide - 1 : sliderItems.length - 1;
|
1359
|
-
// Call the function to update the slide display
|
1360
|
-
updateSlide(sliderItems, sliderDots, currentSlide);
|
1361
|
-
// Call the function to align the slide content if necessary
|
1362
|
-
totalSlideContentHeight(sliderItems[currentSlide]);
|
1363
|
-
});
|
1364
|
-
|
1365
|
-
// Add a click event listener to the right arrow button
|
1366
|
-
sliderRightArrow.addEventListener("click", () => {
|
1367
|
-
// Update the current index to the next slide, wrapping around if necessary
|
1368
|
-
currentSlide = currentSlide < sliderItems.length - 1 ? currentSlide + 1 : 0;
|
1369
|
-
// Call the function to update the slide display
|
1370
|
-
updateSlide(sliderItems, sliderDots, currentSlide);
|
1371
|
-
// Call the function to align the slide content if necessary
|
1372
|
-
totalSlideContentHeight(sliderItems[currentSlide]);
|
1373
|
-
});
|
1374
|
-
|
1375
|
-
// Function to update the label text when the slide changes
|
1376
|
-
function updateSliderLabel() {
|
1377
|
-
// Update the text content of the label to reflect the current slide number
|
1378
|
-
sliderLabel.textContent = `Interactive Slide: slide ${currentSlide + 1} of ${sliderItems.length}`;
|
1379
|
-
}
|
1380
|
-
|
1381
|
-
// Set up keyboard navigation for the slider
|
1382
|
-
slider.addEventListener("keydown", (event) => {
|
1383
|
-
// Handle the left and right arrow keys
|
1384
|
-
switch (event.key) {
|
1385
|
-
case "ArrowLeft":
|
1386
|
-
// Update the current index to the previous slide, wrapping around if necessary
|
1387
|
-
currentSlide = currentSlide > 0 ? currentSlide - 1 : sliderItems.length - 1;
|
1388
|
-
break;
|
1389
|
-
case "ArrowRight":
|
1390
|
-
// Update the current index to the next slide, wrapping around if necessary
|
1391
|
-
currentSlide = currentSlide < sliderItems.length - 1 ? currentSlide + 1 : 0;
|
1392
|
-
break;
|
1393
|
-
default:
|
1394
|
-
// If any other key is pressed, do nothing and return
|
1395
|
-
return;
|
1396
|
-
}
|
1397
|
-
// Call the function to update the slide display
|
1398
|
-
updateSlide(sliderItems, sliderDots, currentSlide);
|
1399
|
-
// Call the function to align the slide content if necessary
|
1400
|
-
totalSlideContentHeight(sliderItems[currentSlide]);
|
1401
|
-
// Prevent the default action for the keydown event
|
1402
|
-
event.preventDefault();
|
1403
|
-
// Call the function to update the label text
|
1404
|
-
updateSliderLabel();
|
1405
|
-
// Focus the slider widget
|
1406
|
-
slider.focus();
|
1407
|
-
});
|
1408
|
-
|
1409
|
-
// Variables to store the starting and ending X coordinates of a touch event
|
1410
|
-
let touchStartX = 0;
|
1411
|
-
let touchEndX = 0;
|
1412
|
-
|
1413
|
-
// Event listener for the 'touchstart' event, which fires when a touch point is placed on the touch surface
|
1414
|
-
slider.addEventListener("touchstart", (e) => {
|
1415
|
-
// Store the X coordinate of the touch point when the touch starts
|
1416
|
-
touchStartX = e.changedTouches[0].screenX;
|
1417
|
-
}, false);
|
1418
|
-
|
1419
|
-
// Event listener for the 'touchend' event, which fires when a touch point is removed from the touch surface
|
1420
|
-
slider.addEventListener("touchend", (e) => {
|
1421
|
-
// Store the X coordinate of the touch point when the touch ends
|
1422
|
-
touchEndX = e.changedTouches[0].screenX;
|
1423
|
-
// Call the function to handle the swipe gesture
|
1424
|
-
sliderSwipe();
|
1425
|
-
}, false);
|
1426
|
-
|
1427
|
-
// Function to handle the swipe gesture
|
1428
|
-
const sliderSwipe = () => {
|
1429
|
-
// Calculate the difference between the starting and ending X coordinates
|
1430
|
-
let diffX = touchEndX - touchStartX;
|
1431
|
-
|
1432
|
-
// Check if the absolute value of the difference is greater than a minimum threshold (50 pixels)
|
1433
|
-
if (Math.abs(diffX) > 50) { // Minimum distance for a swipe
|
1434
|
-
// Determine the direction of the swipe based on the sign of the difference
|
1435
|
-
if (diffX > 0) {
|
1436
|
-
// If the difference is positive, the user swiped to the left
|
1437
|
-
// Update the current slide index to the previous slide, wrapping around if necessary
|
1438
|
-
currentSlide = currentSlide > 0 ? currentSlide - 1 : sliderItems.length - 1;
|
1439
|
-
} else {
|
1440
|
-
// If the difference is negative, the user swiped to the right
|
1441
|
-
// Update the current slide index to the next slide, wrapping around if necessary
|
1442
|
-
currentSlide = currentSlide < sliderItems.length - 1 ? currentSlide + 1 : 0;
|
1443
|
-
}
|
1444
|
-
// Call the function to update the slide display
|
1445
|
-
updateSlide(sliderItems, sliderDots, currentSlide);
|
1446
|
-
// Call the function to align the slide content if necessary
|
1447
|
-
totalSlideContentHeight(sliderItems[currentSlide]);
|
1448
|
-
// Refocus the slider widget to maintain accessibility
|
1449
|
-
slider.focus();
|
1450
|
-
}
|
1451
|
-
}
|
1452
|
-
});
|
1453
|
-
|
1454
|
-
// Function to check if the slider contains all required elements for navigation
|
1455
|
-
const checkSliderNavs = (slider, index) => {
|
1456
|
-
// Select the navigation controls within the context of the current slider
|
1457
|
-
const sliderNextButton = slider.querySelector(".icon-chevron-right");
|
1458
|
-
const sliderPrevButton = slider.querySelector(".icon-chevron-left");
|
1459
|
-
const sliderDotsBar = slider.querySelector(".slider-dots-bar");
|
1460
|
-
|
1461
|
-
// Check if the navigation controls are present
|
1462
|
-
if (!sliderNextButton) {
|
1463
|
-
console.log(`Document error: next button is missing for slider ${index + 1}.`);
|
1464
|
-
}
|
1465
|
-
if (!sliderPrevButton) {
|
1466
|
-
console.log(`Document error: previous button is missing for slider ${index + 1}.`);
|
1467
|
-
}
|
1468
|
-
if (!sliderDotsBar) {
|
1469
|
-
console.log(`Document error: dots bar is missing for slider ${index + 1}.`);
|
1470
|
-
}
|
1471
|
-
};
|
1472
|
-
|
1473
|
-
// Call the function for each slider to perform the check
|
1474
|
-
[...sliderWidgets].forEach((slider, index) => {
|
1475
|
-
checkSliderNavs(slider, index);
|
1476
|
-
});
|
1477
|
-
|
1478
|
-
// Check if slider items are empty
|
1479
|
-
const checkEmptySliderItems = () => {
|
1480
|
-
[...sliderWidgets].forEach((slider, sliderIndex) => {
|
1481
|
-
// Get all .slider-item children of the current slider
|
1482
|
-
let sliderItems = [...slider.querySelectorAll(".slider-item")];
|
1483
|
-
|
1484
|
-
// Check each .slider-item to see if it is empty
|
1485
|
-
sliderItems.forEach((item, itemIndex) => {
|
1486
|
-
// An element is considered empty if it has no children, or all its children are either comment nodes or text nodes with only whitespace
|
1487
|
-
const isEmpty = [...item.childNodes].every(node => {
|
1488
|
-
return node.nodeType === Node.COMMENT_NODE || (node.nodeType === Node.TEXT_NODE && !node.textContent.trim());
|
1489
|
-
});
|
1490
|
-
|
1491
|
-
// Log an error message if the .slider-item is empty
|
1492
|
-
if (isEmpty) {
|
1493
|
-
console.log(`Document error: .slider-item at index ${itemIndex} in slider ${sliderIndex + 1} is empty.`);
|
1494
|
-
}
|
1495
|
-
});
|
1496
|
-
});
|
1497
|
-
};
|
1498
|
-
checkEmptySliderItems();
|
1499
|
-
// Array for all themes that require theme specific js
|
1500
|
-
const customJsThemes = ["ecn", "hrs", "ss"];
|
1501
|
-
|
1502
|
-
//Search links for theme styles
|
1503
|
-
const themeJsCheck = () => {
|
1504
|
-
|
1505
|
-
const links = document.querySelectorAll("link");
|
1506
|
-
|
1507
|
-
links.forEach((link) => {
|
1508
|
-
const href = link.getAttribute("href");
|
1509
|
-
customJsThemes.forEach((theme) => {
|
1510
|
-
// If theme requires custom js, run addThemeScript()
|
1511
|
-
if (href && href.includes(`/${theme}/styles.css`)) {
|
1512
|
-
addThemeScript(theme);
|
1513
|
-
}
|
1514
|
-
});
|
1515
|
-
});
|
1516
|
-
|
1517
|
-
// Adds a script to the head for that particular theme
|
1518
|
-
function addThemeScript(theme) {
|
1519
|
-
let themeScript = document.createElement("script");
|
1520
|
-
// URL references theme-specific js module from CDN
|
1521
|
-
themeScript.src = `https://cdn.jsdelivr.net/npm/@pimaonline/pimaonline-themepack/dist/js/themes/${theme}.js`;
|
1522
|
-
document.head.appendChild(themeScript);
|
1523
|
-
}
|
1524
|
-
}
|
1525
|
-
|
1526
|
-
themeJsCheck()
|
1527
|
-
// Toggle footnotes
|
1528
|
-
const toggleBtns = document.querySelectorAll(".toggle-btn, .toggle-footnotes");
|
1529
|
-
|
1530
|
-
if (toggleBtns) {
|
1531
|
-
if (document.querySelector(".toggle-btn") || document.querySelector(".toggle-footnotes")) {
|
1532
|
-
for (let toggleBtn = 0; toggleBtn < toggleBtns.length; toggleBtn++) {
|
1533
|
-
// Add tabindex
|
1534
|
-
toggleBtns[toggleBtn].setAttribute("tabindex", "0");
|
1535
|
-
// Show/hide on click
|
1536
|
-
toggleBtns[toggleBtn].addEventListener("click", () => {
|
1537
|
-
if (toggleBtns[toggleBtn].nextElementSibling) {
|
1538
|
-
toggleBtns[toggleBtn].nextElementSibling.classList.toggle("show");
|
1539
|
-
}
|
1540
|
-
})
|
1541
|
-
|
1542
|
-
// Show/hide on enter for users who use tab
|
1543
|
-
toggleBtns[toggleBtn].addEventListener("keydown", (enter) => {
|
1544
|
-
if (enter.keyCode === 13) {
|
1545
|
-
if (toggleBtns[toggleBtn].nextElementSibling) {
|
1546
|
-
toggleBtns[toggleBtn].nextElementSibling.classList.toggle("show");
|
1547
|
-
}
|
1548
|
-
}
|
1549
|
-
})
|
1550
|
-
}
|
1551
|
-
}
|
1552
|
-
}
|
1
|
+
(()=>{"use strict";var e={152:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.discardAttributes=void 0,t.discardAttributes=(e,...t)=>{t.forEach((t=>e.removeAttribute(t)))}},80:(e,t,o)=>{o(271),o(381),o(34),o(645),o(71),o(0),o(161),o(609),o(795),o(308),o(721),o(121),o(81),o(786),o(407),o(952),o(174),o(433),o(709),o(393)},271:(e,t,o)=>{Object.defineProperty(t,"__esModule",{value:!0});const n=o(79),r=[{class:"bi-",cdn:new URL("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css")},{class:"ri-",cdn:new URL("https://cdn.jsdelivr.net/npm/remixicon@4.0.1/fonts/remixicon.css")}];document.querySelectorAll(r.map((e=>`[class*='${e.class}']`)).join(", ")).length>0&&r.forEach((e=>{var t;if(document.querySelector(`[class*='${e.class}']`)){const o=null!==(t=null===n.docHead||void 0===n.docHead?void 0:n.docHead.querySelector("meta[name='viewport']"))&&void 0!==t?t:null;o||console.warn("Document error: could not find viewport meta tag");const r=document.createElement("link");r.setAttribute("href",e.cdn.toString()),r.setAttribute("rel","stylesheet"),o&&(null===n.docHead||void 0===n.docHead||n.docHead.insertBefore(r,o.nextSibling))}}))},381:(e,t,o)=>{Object.defineProperty(t,"__esModule",{value:!0});const n=o(79);n.contentWrapper?n.contentWrapper.setAttribute("role","main"):n.contentWrapper||console.log("Document error: does not contain #content-wrapper."),n.secondColumn&&(n.secondColumn.setAttribute("role","region"),n.secondColumn.setAttribute("aria-label","Second column")),n.thirdColumn&&(n.thirdColumn.setAttribute("role","region"),n.thirdColumn.setAttribute("aria-label","Third column"))},34:()=>{const e=document.querySelectorAll(".arrow-right");e.length>0&&e.forEach((e=>{(e=>{e.addEventListener("click",(t=>{e.classList.toggle("arrow-down")})),e.addEventListener("keydown",(t=>{"Enter"!==t.key&&" "!==t.key||e.classList.toggle("arrow-down")}))})(e)}))},645:()=>{const e=document.querySelectorAll("dl.vocab-list dt"),t=(document.querySelectorAll("dl.vocab-list dd"),document.querySelectorAll("dl.vocab-list")),o=(document.querySelectorAll("dl.vocab-list button"),e=>{e.classList.toggle("active");let t=e.nextElementSibling instanceof HTMLElement?e.nextElementSibling:null;for(t&&"block"===t.style.display?t.style.display="none":t&&(t.style.display="block");t&&t.nextElementSibling;){const e=t.nextElementSibling;if(e instanceof HTMLElement){if(t=e,!t||"DD"!==t.tagName)break;"block"===t.style.display?t.style.display="none":t.style.display="block"}}}),n=e=>{if(e){let t=e.querySelectorAll("dd"),o=e.querySelectorAll("dt");t.forEach((e=>{e.style.display="none"})),o.forEach((e=>{e.classList.remove("active")}))}};t.length>0&&(()=>{t.forEach((e=>{let t=0,o=0,r=!1;for(let n=0;n<e.children.length;n++)"DT"==e.children[n].tagName&&(e.children[n].setAttribute("tabindex","0"),t++),"DD"==e.children[n].tagName&&o++,"BUTTON"==e.children[n].tagName&&(r=!0);if(t<1&&console.warn("Document error: no terms found in vocab list"),o<1&&console.warn("Document error: no definitions found in vocab list"),t>o&&console.warn("Document error: more terms than definitions in vocab list"),t>=2&&o>=2)if(r){let t=e.querySelector("button");null==t||t.addEventListener("click",(()=>n(e)))}else{const t=document.createElement("button");t.textContent="Close All",t.addEventListener("click",(()=>n(e))),e.appendChild(t)}else{for(let t=0;t<e.children.length;t++)"BUTTON"==e.children[t].tagName&&(r=!0);if(r){let t=e.querySelector("button");t&&(t.style.display="none")}}}));for(let t=0;t<e.length;t++)e[t].addEventListener("click",(n=>{o(e[t])})),e[t].addEventListener("keydown",(n=>{"Enter"===n.key&&o(e[t])}))})()},71:(e,t,o)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.cleanMarkup=void 0;const n=o(79),r=o(152);t.cleanMarkup=()=>{n.rolePres&&n.rolePres.forEach((e=>e.removeAttribute("role"))),document.querySelectorAll(l.join(",")).forEach((e=>{(0,r.discardAttributes)(e,...c)})),n.jqueryScript&&n.jqueryScript.remove()};const l=["table","thead","tbody","tr","th","td"],c=["cellspacing","cellpadding","width","style"];var i;(n.rolePres&&n.rolePres.length>0||(i=c,l.some((e=>i.some((t=>null!==document.querySelector(`${e}[${t}]`))))))||n.jqueryScript)&&(0,t.cleanMarkup)()},0:()=>{const e=document.querySelectorAll(".content-lock-widget"),t=document.querySelectorAll(".locked-content"),o=document.querySelectorAll(".instructions"),n=[],r=e=>{const r=(e,n,r)=>{u[n].keys[e]=!0,u[n].keys[e]?(t[r].classList.add("open"),o[r].classList.add("complete")):(t[r].classList.remove("open"),o[r].classList.remove("complete")),localStorage.setItem("contentLockData",JSON.stringify(u)),c(n)},l=(e,t)=>{let o=e.querySelector(".error-container"),n=document.createElement("span");o&&(o.innerHTML="",o.appendChild(n)),n.textContent="",setTimeout((function(){n.textContent=t,o&&o.appendChild(n)}),200)},c=e=>{t.forEach(((n,r)=>{let l=n.getAttribute("data-key");if(l){let n=parseInt(l,10);u[e].keys[n]?(t[r].classList.add("open"),o[r].classList.add("complete")):(t[r].classList.remove("open"),o[r].classList.remove("complete"))}}))},i=window.parent.location.href.match(/\/content\/(\d+)/),s=i?i[1]:null,a=["a","b","c","d","e","f","g","h","i"],d=localStorage.getItem("contentLockData");let u=d?JSON.parse(d):{};if(s){const i=parseInt(s,10);window.addEventListener("storage",(e=>{s&&((e,t)=>{e&&"contentLockData"===e.key&&e.newValue&&(u=JSON.parse(e.newValue),c(t))})(e,i)}));let d=1;e.forEach(((e,o)=>{let n=e.querySelector(".quiz form");if(t){if(n){let t=e.querySelector(".locked-content"),r=e.querySelector(".quiz .correct-answer");if(r&&(t||console.warn(`Document error: missing the locked-content class for widget number ${o+1} on this page`),r||console.warn(`Document error: missing correct-answer for content lock quiz for widget number ${o+1} on this page`),n.id=`quiz${d}`,!e.querySelector(".error-container"))){let t=document.createElement("div");t.classList.add("error-container");let n=e.querySelector("input[value='Submit']");n?n.insertAdjacentHTML("beforebegin",t.outerHTML):console.warn(`Document error: quiz is missing an input with value 'Submit' for widget number ${o+1} on this page.`)}}d++}else e.querySelector(".unlock-btn")||console.warn(`Document error: locked content area (that is not using a quiz) is missing <a> with .unlock-btn for widget number ${o+1} on this page.`)})),new Promise(((e,t)=>{let o=document.querySelectorAll(".quiz form");o&&o.forEach((e=>{e.querySelectorAll("input").forEach(((e,t)=>{e.getAttribute("value")?"Submit"==e.getAttribute("value")&&(e.setAttribute("type","button"),e.classList.add("unlock-btn")):(e.setAttribute("value",a[t]),e.setAttribute("name","options"),e.setAttribute("type","radio"))}))})),e()})).then((()=>{(()=>{const e=new Map;t.forEach((t=>{const o=t.getAttribute("data-key");if(o)if(e.has(o)){const t=e.get(o)||0;e.set(o,t+1)}else e.set(o,1)}));let o=!1;e.forEach(((e,t)=>{e>1&&(console.warn(`Document error: multiple quizzes found with the same data key (${t})`),o=!0)}))})();const e=document.querySelectorAll(".unlock-btn");u.hasOwnProperty(s)||(u[i]={keys:{}}),t.forEach(((e,t)=>{let o=e.getAttribute("data-key");if(o){let n=parseInt(o,10),r=e.parentElement;r?r.classList.contains("content-lock-widget")||console.warn(`Document error: content lock widget wrapper is missing .content-lock-widget class for widget number ${t+1} on this page`):console.warn(`Document error: content lock widget is missing parent container for widget number ${t+1} on this page`);let l=e.nextElementSibling;if(l)if(l.classList.contains("instructions")&&"DIV"===l.tagName){if(!l.querySelector(".unlock-btn")){let e=l.nextElementSibling;e?e.classList.contains("quiz")||console.warn(`Document error: missing .quiz class after locked-content ${t+1} on this page`):console.warn(`Document error: missing .unlock-btn class inside instructions ${t+1} on this page`)}}else console.warn(`Document error: instructions <div> is missing .instructions class for widget number ${t+1} on this page`);else console.warn(`Document error: locked content area is missing <div> with instructions for widget number ${t+1} on this page`);u[i].keys[n]||(u[i].keys[n]=!1)}else o||console.warn(`Document error: missing data-key for locked content for widget number ${t+1} on this page`)})),e.forEach(((e,c)=>{let s=t[c].getAttribute("data-key");if(s){let a=parseInt(s,10);null!==e.textContent&&n.push(e.textContent),e.setAttribute("tabIndex","0"),e.addEventListener("click",(()=>((e,t,o,n,c)=>{let i=!1,s=!1;"INPUT"===t.tagName&&(i=!0),i?(s=(e=>{let t=document.getElementById(`quiz${e}`);if(t){let e=t.querySelector("input[name='options']:checked"),o=t.querySelector("input.correct-answer"),n="";return e?o?e.value===o.value?(alert("Correct response"),!0):(n="Incorrect response. Please try again.",l(t,n),!1):(console.warn("Document error: no .correct-answer class applied"),!1):(n="Please select an option before submitting.",l(t,n),!1)}return console.warn("Document error: quiz id for content lock quiz, not being applied"),!1})(e),s&&r(e,c,o)):(s=window.confirm(`Please confirm: ${n[o]}`),s&&r(e,c,o))})(a,e,c,n,i))),u[i]||(u[i]={keys:{},id:0}),u[i].keys[a]&&(t[c].classList.add("open"),o[c].classList.add("complete"))}})),c(i),localStorage.setItem("contentLockData",JSON.stringify(u))})).catch((e=>{console.warn("Document error: error occurred during applyAttributesToQuiz:",e)}))}else console.warn("Document error: No course number found")};document.addEventListener("DOMContentLoaded",(()=>{e.length>0&&r(e)}))},161:()=>{const e=document.querySelectorAll(".flip-card-widget, .flip-card-group");e.length>0&&e.forEach((e=>{let t=e.querySelectorAll(".flip-card"),o=e.querySelectorAll(".inner-card"),n=e.children.length;n!==t.length&&console.warn("Document error: missing .flip-card class for flip card widget"),n!==o.length&&console.warn("Document error: missing .inner-card class for flip card widget"),t.forEach((e=>{e.querySelectorAll(".inner-card").forEach((e=>{e.setAttribute("tabindex","0"),e.addEventListener("click",(()=>{e.offsetHeight,e.classList.toggle("flip")})),e.addEventListener("keydown",(t=>{"Enter"===t.key&&(e.offsetHeight,e.classList.toggle("flip"))}))}))}))}))},609:()=>{if(document.querySelector("body[focus-reader]")){let o=!1;const n=document.querySelector("#content-wrapper"),r=document.querySelector("body[focus-reader]"),l=document.querySelector("head"),c=document.createElement("link");c.rel="stylesheet",c.href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css",l&&l.append(c);const i=document.createElement("script");i.src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver",l&&l.append(i);const s=document.createElement("div");s.className="focus-reader-switches",r&&r.append(s);const a=document.createElement("div");s.append(a);const d=document.createElement("p");d.innerHTML="Focus Text",a.append(d);const u=document.createElement("i");u.classList.add("fa-solid"),u.classList.add("fa-circle-info"),a.append(u);const m=document.createElement("span");m.classList.add("info-tooltip"),m.innerHTML="Highlight text as you scroll",u.append(m);const p=document.createElement("button");p.id="focus-text",p.innerHTML=o?'<i class="fas fa-toggle-on"></i>':'<i class="fas fa-toggle-off"></i>',a.append(p),p.addEventListener("click",(()=>{o?(o=!o,n&&(n.removeAttribute("on"),p.innerHTML=o?'<i class="fas fa-toggle-on"></i>':'<i class="fas fa-toggle-off"></i>',t(n))):(o=!o,n&&(n.setAttribute("on",""),p.innerHTML=o?'<i class="fas fa-toggle-on"></i>':'<i class="fas fa-toggle-off"></i>',document.querySelectorAll("#content-wrapper .content-body :is(p, li, dd, dt, blockquote)").forEach((t=>{e({node:t,focusText:g})}))))}));const g=new IntersectionObserver((e=>{e.forEach((e=>{var t,o;e.isIntersecting?null===(t=e.target.closest("span"))||void 0===t||t.classList.add("focus-text"):null===(o=e.target.closest("span"))||void 0===o||o.classList.remove("focus-text")}))}),{root:null,rootMargin:"-48.9% 0px",threshold:0})}function e({node:t,focusText:o}){var n,r;t.nodeType===Node.TEXT_NODE&&""!==(null===(n=t.textContent)||void 0===n?void 0:n.trim())?(t.textContent.split(" ").forEach((e=>{var n;if(""!==e){const r=document.createElement("span");r.textContent=e+" ",o.observe(r),null===(n=t.parentNode)||void 0===n||n.insertBefore(r,t)}})),null===(r=t.parentNode)||void 0===r||r.removeChild(t)):t.nodeType===Node.ELEMENT_NODE&&Array.from(t.childNodes).forEach((t=>{e({node:t,focusText:o})}))}function t(e){var o,n,r;if(e.nodeType===Node.ELEMENT_NODE){const l=e;if(l instanceof HTMLElement&&"SPAN"===l.tagName&&("focus-text"===l.className||""===l.className||!(null===(o=l.textContent)||void 0===o?void 0:o.trim())||0===l.attributes.length)){const e=document.createTextNode(l.textContent||"");null===(n=l.parentNode)||void 0===n||n.insertBefore(e,l),null===(r=l.parentNode)||void 0===r||r.removeChild(l)}else Array.from(l.childNodes).forEach((e=>{t(e)}))}}},795:()=>{const e=document.querySelector(".toggle-footnotes");e&&(e=>{var t;const o=document.createElement("button");o.classList.add("toggle-footnotes"),o.textContent="[Show Footnotes]",null===(t=e.parentNode)||void 0===t||t.replaceChild(o,e),o.addEventListener("click",(()=>{o.textContent="[Show Footnotes]"===o.textContent?"[Hide Footnotes]":"[Show Footnotes]";const e=o.nextElementSibling;e&&e.classList.toggle("show")}))})(e)},308:(e,t,o)=>{Object.defineProperty(t,"__esModule",{value:!0});const n=o(79);(()=>{if(null!==n.courseBody){n.contentWrapper&&n.secondColumn&&n.thirdColumn?n.courseBody.id="three-column":n.contentWrapper&&n.secondColumn&&!n.columnWidget?n.courseBody.id="two-column":n.contentWrapper&&n.secondColumn&&n.columnWidget?n.courseBody.id="two-col-widget":n.contentWrapper&&n.videoWrapper?n.courseBody.id="video-grid":!n.contentWrapper||n.secondColumn||n.thirdColumn||n.columnWidget||n.videoWrapper?n.contentWrapper&&!n.secondColumn&&(n.thirdColumn||n.columnWidget)?console.log("Document error: <body> is missing id because #second-column doesn't exist."):console.log("Document error: unable to determine the page layout for setting <body> id."):n.courseBody.id="one-column";const e=document.body.children;let t=!1;for(let o=0;o<e.length;o++){const n=e[o];if("content-wrapper"!==n.id&&"second-column"!==n.id&&"third-column"!==n.id&&"column-widget"!==n.id&&"HEADER"!==n.tagName&&"FOOTER"!==n.tagName&&"SCRIPT"!==n.tagName&&"loom-companion-mv3"!==n.id&&"focus-reader-switches"!==n.className){t=!0;break}}t&&console.log("Document error: Additional content outside #content-wrapper, #second-column, #third-column, or footer.")}else console.log("Document error: no <body> element found.")})()},721:(e,t,o)=>{Object.defineProperty(t,"__esModule",{value:!0});const n=o(79),r=document.createElement("script");r.setAttribute("src","https://pima.h5p.com/js/h5p-resizer.js"),r.setAttribute("charset","UTF-8"),r.setAttribute("defer","");const l=document.querySelectorAll("iframe"),c=null===n.docHead||void 0===n.docHead?void 0:n.docHead.querySelector("script[src='https://pima.h5p.com/js/h5p-resizer.js']");l.length>0&&l.forEach((e=>{const t=e.getAttribute("src");t&&(t.includes("/d2l/common/dialogs/quickLink")||t.includes("https://pima.h5p.com/content")||t.includes("h5p"))&&!c&&n.docHead&&n.docHead.appendChild(r)}))},121:(e,t,o)=>{Object.defineProperty(t,"__esModule",{value:!0});const n=o(79),r=document.querySelectorAll(".gallery-wrapper"),l=document.querySelectorAll(".image-box"),c=document.querySelectorAll(".image-gallery");r.length>0&&r.forEach((e=>{const t=e.parentNode;t instanceof Element&&t.classList.contains("image-gallery")||console.warn("Document error: parent of .gallery-wrapper does not have the .image-gallery class.")})),l.length>0&&l.forEach((e=>{const t=e.parentNode;t instanceof Element&&t.classList.contains("gallery-wrapper")||console.log("Document error: parent of .image-box does not have the .gallery-wrapper class.")})),r.length>0&&r.forEach((e=>{Array.from(e.children).every((e=>e instanceof HTMLElement&&e.classList.contains("image-box")))||console.warn("Document error: not all direct children of .gallery-wrapper have the .image-box class.")}));c.length>0&&((()=>{const e=document.createElement("link");e.rel="stylesheet",e.href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css",null===n.docHead||void 0===n.docHead||n.docHead.appendChild(e)})(),(()=>{c.forEach((e=>{e.insertAdjacentHTML("afterbegin",'<div class="modal-box invisible">\n <div class="gallery-overlay"></div>\n <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>\n </div>\n <button class="hide-gallery">Hide</button>')}));const e=document.querySelector(".gallery-overlay"),t=document.querySelector(".modal-box"),o=document.querySelector(".modal-box--image img"),n=document.querySelector(".img-caption"),r=document.querySelector(".close-img"),i=document.querySelectorAll(".hide-gallery"),s=e=>{null==t||t.classList.remove("invisible"),o&&(o.src=e.imgSrc),n&&(n.innerHTML=e.imgCaption)},a=()=>{null==t||t.classList.add("invisible")};l.forEach((e=>{e.addEventListener("click",(function(){var e,t;s({imgSrc:(null===(e=this.querySelector("img"))||void 0===e?void 0:e.src)||"",imgCaption:(null===(t=this.querySelector("img"))||void 0===t?void 0:t.alt)||""})})),e.setAttribute("tabindex","0"),e.addEventListener("keydown",(function(e){var t,o;"Enter"===e.key&&s({imgSrc:(null===(t=this.querySelector("img"))||void 0===t?void 0:t.src)||"",imgCaption:(null===(o=this.querySelector("img"))||void 0===o?void 0:o.alt)||""})}))})),e&&(e.onclick=a),window.onkeydown=e=>{"Escape"===e.key&&a()},r&&(r.onclick=a),i.forEach((e=>{e.addEventListener("click",(()=>{var t;null===(t=e.nextElementSibling)||void 0===t||t.classList.toggle("invisible"),e.innerHTML="Hide"===e.innerHTML?"Show":"Hide"}))}))})())},81:()=>{document.addEventListener("DOMContentLoaded",(()=>{document.querySelectorAll("a[href^='#']").forEach((e=>{var t;const o=null===(t=e.getAttribute("href"))||void 0===t?void 0:t.substring(1);o&&e.setAttribute("onclick",`jumpTo("${o}")`)}))}))},786:()=>{const e=document.querySelectorAll(".media-container");e.length>0&&e.forEach(((e,t)=>{let o=String.fromCharCode(t+65).toLowerCase(),n=e.querySelector(".media-object"),r=e.querySelector("iframe"),l=e.querySelector(".media-info");r||console.warn("Document error: no iframe found for media container"),n||console.warn("Document error: no media object found for media container"),r&&!r.hasAttribute("aria-describedby")&&l&&(r.setAttribute("aria-describedby",`${o}`),l.id=`${[o]}`)}))},407:()=>{const e=document.querySelectorAll(".display, .display-lg");e.length>0&&[...e].forEach((e=>{const t=e.querySelectorAll(".display table th, table.display th, .display-lg table th, table.display-lg th"),o=e.querySelector(".display table tbody, table.display tbody, .display-lg table tbody, table.display-lg tbody");if(t.length>0){const e=[...t].map((e=>{var t;return(null===(t=e.textContent)||void 0===t?void 0:t.replace(/\r?\n|\r/,""))||""}));o?[...o.rows].forEach((t=>{[...t.cells].forEach(((t,o)=>{t.setAttribute("data-th",e[o])}))})):console.warn("Document error: no table <tbody> found")}else console.warn("Document error: no table headers <th> found")}))},393:()=>{const e=document.querySelectorAll(".slider-widget");e.length>0&&(()=>{[...e].forEach((e=>{[...e.children].every((e=>e.classList.contains("slider-item")))||console.warn("Document error: not all direct children of .slider have the .slider-item class.")}));const t=(e,t,o)=>{e.forEach(((e,t)=>{e.style.display=t===o?"block":"none"})),t.forEach(((e,t)=>{e.classList.toggle("active",t===o)}))},o=(e,o,n)=>{t(e,o,n)},n=e=>{const t=document.createElement("button");return t.className=e,t.setAttribute("tabindex","-1"),t},r=e=>{let t=0;for(let o=0;o<e.children.length;o++){const n=e.children[o];n instanceof HTMLElement&&(t+=n.offsetHeight)}if(t<300&&!e.querySelector(".media-container")){const t=document.createElement("div");for(;e.firstChild;)t.appendChild(e.firstChild);e.appendChild(t),e.classList.add("short-content")}};document.querySelectorAll(".slider-widget").forEach(((e,l)=>{e.setAttribute("tabindex","0"),e.setAttribute("role","group");const c=document.createElement("span");c.id=`slider-label-${l+1}`,c.textContent="Interactive Slider",c.hidden=!0,e.prepend(c),e.setAttribute("aria-labelledby",`slider-label-${l+1}`);let i=0;const s=Array.from(e.querySelectorAll(".slider-item")),a=document.createElement("div");a.className="slider-dots-bar",e.appendChild(a);let d=[];s.forEach(((e,o)=>{const n=document.createElement("span");n.className="slider-dot",n.addEventListener("click",(()=>{i=o,t(s,d,i),s[i].style.display="block",n.classList.add("active"),r(s[i])})),a.appendChild(n),d.push(n),e.style.display=o!==i?"none":"block",o===i&&n.classList.add("active")}));const u=n("slider-arrow icon-chevron-left"),m=n("slider-arrow icon-chevron-right");e.appendChild(u),e.appendChild(m),u.addEventListener("click",(()=>{i=i>0?i-1:s.length-1,o(s,d,i),r(s[i])})),m.addEventListener("click",(()=>{i=i<s.length-1?i+1:0,o(s,d,i),r(s[i])})),e.addEventListener("keydown",(t=>{switch(t.key){case"ArrowLeft":i=i>0?i-1:s.length-1;break;case"ArrowRight":i=i<s.length-1?i+1:0;break;default:return}o(s,d,i),r(s[i]),t.preventDefault(),c.textContent=`Interactive Slide: slide ${i+1} of ${s.length}`,e.focus()}));let p=0,g=0;e.addEventListener("touchstart",(e=>{p=e.changedTouches[0].screenX}),!1),e.addEventListener("touchend",(e=>{g=e.changedTouches[0].screenX,h()}),!1);const h=()=>{let t=g-p;Math.abs(t)>50&&(i=t>0?i>0?i-1:s.length-1:i<s.length-1?i+1:0,o(s,d,i),r(s[i]),e.focus())}})),e.forEach(((e,t)=>{((e,t)=>{const o=e.querySelector(".icon-chevron-right"),n=e.querySelector(".icon-chevron-left"),r=e.querySelector(".slider-dots-bar");o||console.warn(`Document error: next button is missing for slider ${t+1}.`),n||console.warn(`Document error: previous button is missing for slider ${t+1}.`),r||console.warn(`Document error: dots bar is missing for slider ${t+1}.`)})(e,t)})),e.forEach(((e,t)=>{Array.from(e.querySelectorAll(".slider-item")).forEach(((e,o)=>{Array.from(e.childNodes).every((e=>e.nodeType===Node.COMMENT_NODE||e.nodeType===Node.TEXT_NODE&&!e.textContent.trim()))&&console.warn(`Document error: .slider-item at index ${o} in slider ${t+1} is empty.`)}))}))})()},952:()=>{const e=document.querySelectorAll(".tabs, .tabs-widget");e.length>0&&(()=>{let t=0;e.forEach(((e,o)=>{var n,r,l,c,i,s,a,d,u,m,p,g;let h=e.querySelectorAll("input"),f=e.querySelectorAll("label"),y=e.querySelectorAll("div");if((h.length<2||f.length<2||y.length<2)&&console.warn("Document error: add more than just one tab for tabs widget"),h.length<3||f.length<3||y.length<3){let e=f[1].querySelector("span");e&&("Hide"!==e.textContent&&""!==e.textContent||console.warn("Document error: add more tabs, than just 1 tab and the hide tab"))}let b=f[f.length-1].querySelector("span");b&&"Hide"!==b.textContent&&console.warn("Document error: ensure last tab is a hide tab and label text is 'Hide'"),(h.length<f.length||h.length<y.length)&&console.warn("Document error: missing tab input(s) in tab widget"),(f.length<h.length||f.length<y.length)&&console.warn("Document error: missing tab label(s) in tab widget");let v=o+1;e.setAttribute("role","region"),e.setAttribute("aria-label",`tab group ${v}`);for(let e=0;e<h.length;e++){"Hide"===(null===(r=null===(n=f[e])||void 0===n?void 0:n.textContent)||void 0===r?void 0:r.trim())&&f[e].classList.add("hide-tab");let o=t+1;h&&y||(console.warn("Document error: no inputs found for tabs widget"),console.warn("Document error: no divs (tab panels) found for tabs widget")),f||console.warn("Document error: no labels found for tabs widget"),null===(l=h[e])||void 0===l||l.classList.add("tab-input"),null===(c=h[e])||void 0===c||c.setAttribute("type","radio"),null===(i=h[e])||void 0===i||i.setAttribute("id",`tab${o}`),null===(s=h[e])||void 0===s||s.setAttribute("name",`hint-group-${v}`),null===(a=h[e])||void 0===a||a.setAttribute("aria-describedby",`tabHeading${o}`),null===(d=f[e])||void 0===d||d.classList.add("tab-header"),null===(u=f[e])||void 0===u||u.setAttribute("for",`tab${o}`),y[e]&&(null===(m=y[e])||void 0===m||m.classList.add("tab-panel"),null===(p=y[e])||void 0===p||p.setAttribute("tabindex","0"),null===(g=y[e])||void 0===g||g.setAttribute("id",`tabHeading${o}`)),h[e].checked=0===e,e+1==h.length&&(f[e].classList.add("hide-tab"),y[e]&&y[e].classList.add("hide-panel")),t++}}))})()},174:()=>{const e=["cas","ecn","hrs","ss","chm"];document.querySelectorAll("link").forEach((t=>{const o=t.getAttribute("href");e.forEach((e=>{o&&o.includes(`/${e}/styles.css`)&&function(e){let t=document.createElement("script");t.src=`https://cdn.jsdelivr.net/npm/@pimaonline/pimaonline-themepack/dist/js/themes/${e}.js`,document.head.appendChild(t)}(e)}))}))},433:()=>{const e=document.querySelectorAll(".toggle-btn");e.length>0&&(()=>{if(document.querySelector(".toggle-btn"))for(let t=0;t<e.length;t++)e[t].setAttribute("tabindex","0"),e[t].addEventListener("click",(()=>{const o=e[t].nextElementSibling;o&&o.classList.toggle("show")})),e[t].addEventListener("keydown",(o=>{if("Enter"===o.key){const o=e[t].nextElementSibling;o&&o.classList.toggle("show")}}))})()},709:()=>{const e="toolTip",t="toolTipHover",o=document.querySelectorAll(".tooltip");o.length>0&&(()=>{const n=(e,t)=>{t.style.display="block",e.classList.toggle("hidden",!1)},r=(e,t)=>{t.style.display="none",e.setAttribute("aria-expanded","false"),e.classList.toggle("hidden",!0)};o.forEach(((o,l)=>{let c=l+1;o.id=`${e}${c}`,o.role="tooltip",o.tabIndex=0,o.setAttribute("aria-expanded","false"),o.setAttribute("aria-controls",`${t}${c.toString()}`);const i=o.querySelector(".tip-hover");i?(i.id=`${t}${c}`,i.setAttribute("aria-describedby",`${e}${c.toString()}`),o.addEventListener("mouseover",(()=>n(o,i))),o.addEventListener("mouseout",(()=>r(o,i))),o.addEventListener("focus",(()=>n(o,i))),o.addEventListener("blur",(()=>r(o,i))),o.addEventListener("touchstart",(()=>n(o,i))),o.addEventListener("touchend",(()=>r(o,i)))):console.warn("Document error: tooltip is missing .tip-hover class");let s=30-c;o.style.zIndex=s.toString()})),document.querySelectorAll(".tooltip .video-container, .tooltip .video-container").forEach((e=>{const t=e.parentElement;t&&(t.style.width="450px")}))})()},79:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.jqueryScript=t.videoWrapper=t.rolePres=t.thirdColumn=t.secondColumn=t.docHead=t.courseBody=t.contentWrapper=t.columnWidget=void 0,t.columnWidget=document.querySelector("#column-widget"),t.contentWrapper=document.querySelector("#content-wrapper"),t.courseBody=document.querySelector("body"),t.docHead=document.querySelector("head"),t.secondColumn=document.querySelector("#second-column"),t.thirdColumn=document.querySelector("#third-column"),t.rolePres=document.querySelectorAll('[role="presentation"]'),t.videoWrapper=document.querySelector("#video-wrapper"),t.jqueryScript=document.querySelector('script[src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"]')}},t={};!function o(n){var r=t[n];if(void 0!==r)return r.exports;var l=t[n]={exports:{}};return e[n](l,l.exports,o),l.exports}(80)})();
|