@pimaonline/pimaonline-themepack 2.4.1 → 3.10.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +30 -0
- package/README.md +50 -61
- package/dist/css/main.css +1 -1
- package/dist/css/plugins/alt-icons.css +1 -0
- package/dist/css/plugins/font-awesome.css +1 -1
- package/dist/css/routes.css +1 -1
- package/dist/css/themes/aviation/styles.css +1 -1
- package/dist/css/themes/bct/styles.css +1 -0
- package/dist/css/themes/bio/styles.css +1 -0
- package/dist/css/themes/business/styles.css +1 -1
- package/dist/css/themes/ecn/styles.css +1 -0
- package/dist/css/themes/eng/styles.css +1 -0
- package/dist/css/themes/fsc/styles.css +1 -0
- package/dist/css/themes/hrs/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/psy/styles.css +1 -0
- package/dist/css/themes/ss/styles.css +1 -0
- package/dist/img/theme-images/bct/Blueprint-background.png +0 -0
- package/dist/img/theme-images/bio/original/dna.svg +1 -0
- package/dist/img/theme-images/bio/original/humans.svg +1 -0
- package/dist/img/theme-images/bio/original/leaves.svg +33 -0
- package/dist/img/theme-images/bio/original/marine-bottom.svg +1 -0
- package/dist/img/theme-images/bio/original/marine-top.svg +1 -0
- package/dist/img/theme-images/bio/original/microbes.svg +1 -0
- package/dist/img/theme-images/ecn/arrow-2.svg +4 -0
- package/dist/img/theme-images/ecn/arrow.svg +4 -0
- package/dist/img/theme-images/ecn/point.svg +3 -0
- package/dist/img/theme-images/eng/button-bkg.svg +178 -0
- package/dist/img/theme-images/eng/halftone-banner.svg +1 -0
- package/dist/img/theme-images/eng/halftone.svg +177 -0
- package/dist/img/theme-images/eng/long-button-bkg.svg +353 -0
- package/dist/img/theme-images/fsc/bottomwave-pinkred.svg +17 -0
- package/dist/img/theme-images/fsc/bottomwave-redorange.svg +17 -0
- package/dist/img/theme-images/fsc/bottomwave-yellow.svg +17 -0
- package/dist/img/theme-images/fsc/bottomwave-yelloworange.svg +17 -0
- package/dist/img/theme-images/fsc/fire-icon.png +0 -0
- package/dist/img/theme-images/fsc/mainwave-pinkred.svg +17 -0
- package/dist/img/theme-images/fsc/mainwave-redorange.svg +17 -0
- package/dist/img/theme-images/fsc/mainwave-yellow.svg +17 -0
- package/dist/img/theme-images/fsc/mainwave-yelloworange.svg +17 -0
- package/dist/img/theme-images/lang/country-flags/argentina_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/bolivia_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/chile_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/colombia_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/costa-rica_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/cuba_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/dominican-republic_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/ecuador_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/el-salvador_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/guatemala_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/honduras_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/mexico_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/nicaragua_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/panama_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/paraguay_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/peru_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/puerto-rice_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/spain_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/uruguay_flag.png +0 -0
- package/dist/img/theme-images/lang/country-flags/venezuela_flag.png +0 -0
- package/dist/img/theme-images/psy/counseling-icon.svg +1 -0
- package/dist/img/theme-images/psy/psych-icon.svg +17 -0
- package/dist/img/theme-images/ss/blockquote.svg +3 -0
- package/dist/img/theme-images/ss/list-style.svg +3 -0
- package/dist/img/theme-images/ss/main-large-blob.svg +3 -0
- package/dist/img/theme-images/ss/main-small-blob.svg +3 -0
- package/dist/img/theme-images/ss/small-blob.svg +3 -0
- package/dist/img/theme-images/ss/tall-blob.svg +3 -0
- package/dist/js/scripts2.js +890 -245
- package/dist/js/themes/ecn.js +13 -0
- package/dist/js/themes/hrs.js +19 -0
- package/dist/js/themes/ss.js +197 -0
- package/dist/plugins/font-awesome-icons/webfonts/brands.svg +1460 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-brands-400.ttf +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-brands-400.woff2 +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-regular-400.ttf +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-regular-400.woff2 +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-solid-900.ttf +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-solid-900.woff2 +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-v4compatibility.ttf +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-v4compatibility.woff2 +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/regular.svg +497 -0
- package/dist/plugins/font-awesome-icons/webfonts/solid.svg +4178 -0
- package/package.json +4 -2
- package/dist/plugins/font-awesome-icons/webfonts/fa-brands-400.eot +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-brands-400.svg +0 -3570
- package/dist/plugins/font-awesome-icons/webfonts/fa-brands-400.woff +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-regular-400.eot +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-regular-400.svg +0 -803
- package/dist/plugins/font-awesome-icons/webfonts/fa-regular-400.woff +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-solid-900.eot +0 -0
- package/dist/plugins/font-awesome-icons/webfonts/fa-solid-900.svg +0 -4700
- package/dist/plugins/font-awesome-icons/webfonts/fa-solid-900.woff +0 -0
package/dist/js/scripts2.js
CHANGED
|
@@ -1,45 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const courseBody = document.querySelector("body");
|
|
1
|
+
const columnWidget = document.querySelector("#column-widget");
|
|
2
|
+
const contentLockInstructions = document.querySelectorAll(".content-lock-instructions");
|
|
3
|
+
const contentUnlockBtns = document.querySelectorAll(".content-unlock-btn");
|
|
5
4
|
const contentWrapper = document.querySelector("#content-wrapper");
|
|
5
|
+
const courseBody = document.querySelector("body");
|
|
6
|
+
const docHead = document.querySelector("head");
|
|
7
|
+
const flipCards = document.querySelectorAll(".flip-card-group");
|
|
8
|
+
const focusReaderTooltipText = "Highlight text as you scroll";
|
|
9
|
+
const galleryWrappers = document.querySelectorAll(".gallery-wrapper");
|
|
10
|
+
const h5pIframes = document.querySelectorAll("iframe");
|
|
11
|
+
const h5pResizer = document.createElement("script");
|
|
12
|
+
const h5pResizerExists = docHead.querySelector("script[src='https://pima.h5p.com/js/h5p-resizer.js']");
|
|
13
|
+
// This array contains CDNs for Bootstrap and Remix icon libraries stored as objects
|
|
14
|
+
const iconClasses = [
|
|
15
|
+
{ class: "bi-", cdn: "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" },
|
|
16
|
+
{ class: "ri-", cdn: "https://cdn.jsdelivr.net/npm/remixicon@4.0.1/fonts/remixicon.css" }
|
|
17
|
+
];
|
|
18
|
+
const imageGallery = document.querySelector(".image-gallery");
|
|
19
|
+
const imgBoxes = document.querySelectorAll(".image-box");
|
|
20
|
+
const imgGalleries = document.querySelectorAll(".image-gallery");
|
|
21
|
+
const lockedContent = document.querySelectorAll(".locked-content");
|
|
22
|
+
const mediaContainers = document.querySelectorAll(".media-container");
|
|
23
|
+
const rolePres = document.querySelectorAll('[role="presentation"]');
|
|
6
24
|
const secondColumn = document.querySelector("#second-column");
|
|
25
|
+
const tables = document.querySelectorAll(".display, .display-lg")
|
|
26
|
+
const tabsWidgets = document.querySelectorAll(".tabs");
|
|
7
27
|
const thirdColumn = document.querySelector("#third-column");
|
|
8
|
-
const columnWidget = document.querySelector("#column-widget");
|
|
9
|
-
const videoWrapper = document.querySelector("#video-wrapper");
|
|
10
|
-
const rolePres = document.querySelectorAll('[role="presentation"]');
|
|
11
|
-
const imageGallery = document.querySelector(".image-gallery");
|
|
12
|
-
const vocabListWidget = document.querySelector("dl.vocab-list");
|
|
13
|
-
const vocabTerms = document.querySelectorAll("dl.vocab-list dt");
|
|
14
|
-
const vocabDefs = document.querySelectorAll("dl.vocab-list dd");
|
|
15
28
|
const vocabCloseBtns = document.querySelectorAll("dl.vocab-list button");
|
|
29
|
+
const vocabDefs = document.querySelectorAll("dl.vocab-list dd");
|
|
30
|
+
const vocabListWidget = document.querySelector("dl.vocab-list");
|
|
16
31
|
const vocabLists = document.querySelectorAll("dl[class^='vocab-list']");
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const h5pIframes = document.querySelectorAll("iframe");
|
|
20
|
-
const docHead = document.querySelector("head");
|
|
21
|
-
const h5pResizerExists = docHead.querySelector("script[src='https://pima.h5p.com/js/h5p-resizer.js']");
|
|
22
|
-
const h5pResizer = document.createElement("script");
|
|
23
|
-
|
|
24
|
-
// Grid
|
|
25
|
-
const addGrid = () => {
|
|
26
|
-
if (secondColumn && thirdColumn) {
|
|
27
|
-
courseBody.id = "three-column";
|
|
28
|
-
} else if (secondColumn && !columnWidget) {
|
|
29
|
-
courseBody.id = "two-column";
|
|
30
|
-
} else if (columnWidget) {
|
|
31
|
-
courseBody.id = "two-col-widget";
|
|
32
|
-
} else if (videoWrapper) {
|
|
33
|
-
courseBody.id = "video-grid";
|
|
34
|
-
} else {
|
|
35
|
-
courseBody.id = "one-column";
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
addGrid();
|
|
39
|
-
|
|
32
|
+
const vocabTerms = document.querySelectorAll("dl.vocab-list dt");
|
|
33
|
+
const videoWrapper = document.querySelector("#video-wrapper");
|
|
40
34
|
// JS to add role and aria-label to content-wrapper, second-column, and third-column
|
|
41
35
|
const addAria = () => {
|
|
42
|
-
contentWrapper
|
|
36
|
+
if (contentWrapper) {
|
|
37
|
+
contentWrapper.setAttribute("role", "main");
|
|
38
|
+
} else if (!contentWrapper) {
|
|
39
|
+
console.error("Document error: does not contain #content-wrapper.");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
43
42
|
if (secondColumn) {
|
|
44
43
|
secondColumn.setAttribute("role", "region");
|
|
45
44
|
secondColumn.setAttribute("aria-label", "Second column");
|
|
@@ -50,192 +49,247 @@ const addAria = () => {
|
|
|
50
49
|
}
|
|
51
50
|
};
|
|
52
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.error("Document error: <body> is missing id because #second-column doesn't exist.");
|
|
65
|
+
return;
|
|
66
|
+
} else {
|
|
67
|
+
console.error("Document error: unable to determine the page layout for setting <body> id.");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
53
70
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
const topLevelElements = document.body.children;
|
|
72
|
+
let foundNestedElement = false;
|
|
73
|
+
|
|
74
|
+
// Check for additional content outside #content-wrapper, #second-column, #third-column, or footer
|
|
75
|
+
for (let i = 0; i < topLevelElements.length; i++) {
|
|
76
|
+
const element = topLevelElements[i];
|
|
77
|
+
|
|
78
|
+
if (
|
|
79
|
+
element.id !== "content-wrapper" &&
|
|
80
|
+
element.id !== "second-column" &&
|
|
81
|
+
element.id !== "third-column" &&
|
|
82
|
+
element.id !== "column-widget" &&
|
|
83
|
+
element.tagName !== "HEADER" &&
|
|
84
|
+
element.tagName !== "FOOTER" &&
|
|
85
|
+
element.tagName !== "SCRIPT" &&
|
|
86
|
+
element.id !== "loom-companion-mv3" &&
|
|
87
|
+
element.className !== "focus-reader-switches"
|
|
88
|
+
) {
|
|
89
|
+
foundNestedElement = true;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (foundNestedElement) {
|
|
95
|
+
console.error("Document error: Additional content outside #content-wrapper, #second-column, #third-column, or footer.");
|
|
96
|
+
return;
|
|
63
97
|
}
|
|
64
|
-
// Remove attributes from tables
|
|
65
|
-
const tableElems = document.querySelectorAll("table, thead, tbody, tr, th, td");
|
|
66
|
-
tableElems.forEach((elem) => {
|
|
67
|
-
discardAttributes(elem, "cellspacing", "cellpadding", "width", "style");
|
|
68
|
-
});
|
|
69
98
|
};
|
|
70
|
-
|
|
99
|
+
addGrid();
|
|
100
|
+
// Media Container
|
|
101
|
+
const addMediaContainersAria = () => {
|
|
102
|
+
mediaContainers.forEach((eachContainer, index) => {
|
|
103
|
+
// loopID: find the current index value, convert it to its letter equivalent, then convert to lowercase
|
|
104
|
+
let loopId = String.fromCharCode(index + 65).toLowerCase();
|
|
105
|
+
let mediaObject = eachContainer.querySelector(".media-object");
|
|
106
|
+
let iframe = eachContainer.querySelector("iframe");
|
|
107
|
+
let mediaInfo = eachContainer.querySelector(".media-info");
|
|
71
108
|
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let headertext = [],
|
|
77
|
-
headers = tables[table].querySelectorAll(".display table th, table.display th, .display-lg table th, table.display-lg th"),
|
|
78
|
-
tablebody = tables[table].querySelector(".display table tbody, table.display tbody, .display-lg table tbody, table.display-lg tbody");
|
|
79
|
-
for (let header = 0; header < headers.length; header++) {
|
|
80
|
-
let current = headers[header];
|
|
81
|
-
headertext.push(current.textContent.replace(/\r?\n|\r/, ""));
|
|
109
|
+
// Check if media container items are present
|
|
110
|
+
if (!iframe) {
|
|
111
|
+
console.error("Document error: no iframe found for media container");
|
|
112
|
+
return;
|
|
82
113
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
col.setAttribute("data-th", headertext[j]);
|
|
86
|
-
}
|
|
114
|
+
if (!mediaObject) {
|
|
115
|
+
console.error("Document error: no media object found for media container");
|
|
87
116
|
}
|
|
88
|
-
|
|
117
|
+
|
|
118
|
+
// If element DOES NOT have "aria-describedby" && it DOES have a sibling element.
|
|
119
|
+
if (!iframe.hasAttribute("aria-describedby") && mediaInfo != null) {
|
|
120
|
+
iframe.setAttribute("aria-describedby", `${loopId}`);
|
|
121
|
+
mediaInfo.id = `${[loopId]}`;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
89
124
|
}
|
|
90
|
-
|
|
125
|
+
if (mediaContainers) { addMediaContainersAria(); }
|
|
126
|
+
// -------- Add CDNs for Bootstrap and Remix icon libraries ---------
|
|
127
|
+
|
|
128
|
+
// 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.
|
|
129
|
+
|
|
130
|
+
iconClasses.forEach(icon => {
|
|
131
|
+
const iconElement = document.querySelector(`[class*='${icon.class}']`);
|
|
132
|
+
if (iconElement) {
|
|
133
|
+
const metaTagRef = docHead.querySelector("meta[name='viewport']");
|
|
134
|
+
//Check if viewport meta tag exists
|
|
135
|
+
if (!metaTagRef) {
|
|
136
|
+
console.error("Document error: could not find viewport meta tag");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
91
139
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
document.getElementById(anchor).scrollIntoView();
|
|
96
|
-
}
|
|
140
|
+
const iconCDN = document.createElement("link");
|
|
141
|
+
iconCDN.setAttribute("href", icon.cdn);
|
|
142
|
+
iconCDN.setAttribute("rel", "stylesheet");
|
|
97
143
|
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
// Create link element with font-awesome cdn and append it to the <head>
|
|
101
|
-
const docHead = document.querySelector("head");
|
|
102
|
-
const fontAwesomeCdn = document.createElement("link");
|
|
103
|
-
fontAwesomeCdn.setAttribute("rel", "stylesheet");
|
|
104
|
-
fontAwesomeCdn.setAttribute("href", "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css");
|
|
105
|
-
docHead.appendChild(fontAwesomeCdn);
|
|
106
|
-
// Begin image gallery
|
|
107
|
-
const imgGalleries = document.querySelectorAll(".image-gallery"),
|
|
108
|
-
imgBoxes = document.querySelectorAll(".image-box"),
|
|
109
|
-
modalBoxContent = `<div class="modal-box invisible">
|
|
110
|
-
<div class="gallery-overlay"></div>
|
|
111
|
-
<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>
|
|
112
|
-
</div>
|
|
113
|
-
<button class="hide-gallery">Hide</button>`;
|
|
114
|
-
|
|
115
|
-
// createModalBox.innerHTML = modalBoxContent;
|
|
116
|
-
for (let imgGallery = 0; imgGallery < imgGalleries.length; imgGallery++) {
|
|
117
|
-
imgGalleries[imgGallery].insertAdjacentHTML("afterbegin", modalBoxContent);
|
|
144
|
+
// Stylesheets are added after meta tag and before themepack stylesheets and scripts to ensure proper styling override
|
|
145
|
+
docHead.insertBefore(iconCDN, metaTagRef.nextSibling);
|
|
118
146
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
for (let imgBox = 0; imgBox < imgBoxes.length; imgBox++) {
|
|
128
|
-
imgBoxes[imgBox].onclick = function () {
|
|
129
|
-
modalBox.classList.remove("invisible");
|
|
130
|
-
let imgSrc = this.querySelector("img").src;
|
|
131
|
-
modalImg.src = imgSrc;
|
|
132
|
-
let imgCaption = this.querySelector("img").alt;
|
|
133
|
-
modalCaption.innerHTML = imgCaption;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Make images tab-able
|
|
137
|
-
imgBoxes[imgBox].setAttribute("tabindex", "0");
|
|
138
|
-
imgBoxes[imgBox].addEventListener("keydown", function (enter) {
|
|
139
|
-
if (enter.key === "Enter") {
|
|
140
|
-
modalBox.classList.remove("invisible");
|
|
141
|
-
let imgSrc = this.querySelector("img").src;
|
|
142
|
-
modalImg.src = imgSrc;
|
|
143
|
-
let imgCaption = this.querySelector("img").alt;
|
|
144
|
-
modalCaption.innerHTML = imgCaption;
|
|
145
|
-
}
|
|
146
|
-
})
|
|
147
|
+
});
|
|
148
|
+
// Check if parent of .gallery-wrapper has .image-gallery class
|
|
149
|
+
const checkGalleryWrapperParent = () => {
|
|
150
|
+
galleryWrappers.forEach((galleryWrapper) => {
|
|
151
|
+
if (!galleryWrapper.parentNode.classList.contains("image-gallery")) {
|
|
152
|
+
console.error(`Document error: parent of .gallery-wrapper does not have the .image-gallery class.`);
|
|
153
|
+
return;
|
|
147
154
|
}
|
|
148
|
-
|
|
149
|
-
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
checkGalleryWrapperParent();
|
|
158
|
+
|
|
159
|
+
// Check if parent of .image-box has .gallery-wrapper class
|
|
160
|
+
const checkImageBoxParent = () => {
|
|
161
|
+
imgBoxes.forEach((imgBox) => {
|
|
162
|
+
if (!imgBox.parentNode.classList.contains("gallery-wrapper")) {
|
|
163
|
+
console.error(`Document error: parent of .image-box does not have the .gallery-wrapper class.`);
|
|
164
|
+
return;
|
|
150
165
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
checkImageBoxParent();
|
|
169
|
+
|
|
170
|
+
// Check if direct children of .gallery-wrapper have .image-box class
|
|
171
|
+
const checkGalleryWrapperChildren = () => {
|
|
172
|
+
galleryWrappers.forEach((galleryWrapper) => {
|
|
173
|
+
let directChildren = Array.from(galleryWrapper.children).every(child => child.classList.contains("image-box"));
|
|
174
|
+
|
|
175
|
+
if (!directChildren) {
|
|
176
|
+
console.error(`Document error: not all direct children of .gallery-wrapper have the .image-box class.`);
|
|
177
|
+
return;
|
|
155
178
|
}
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
checkGalleryWrapperChildren();
|
|
156
182
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
183
|
+
// Function to add Font Awesome CDN to the head
|
|
184
|
+
const addFontAwesomeCdn = () => {
|
|
185
|
+
const fontAwesomeCdn = document.createElement("link");
|
|
186
|
+
fontAwesomeCdn.rel = "stylesheet";
|
|
187
|
+
fontAwesomeCdn.href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css";
|
|
188
|
+
docHead.appendChild(fontAwesomeCdn);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Function to create modal box HTML string
|
|
192
|
+
const createModalBox = () => {
|
|
193
|
+
return `<div class="modal-box invisible">
|
|
194
|
+
<div class="gallery-overlay"></div>
|
|
195
|
+
<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>
|
|
196
|
+
</div>
|
|
197
|
+
<button class="hide-gallery">Hide</button>`;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Function to initialize the image gallery
|
|
201
|
+
const callImageGallery = () => {
|
|
202
|
+
// Insert modal box HTML at the beginning of each image gallery
|
|
203
|
+
imgGalleries.forEach((gallery) => {
|
|
204
|
+
gallery.insertAdjacentHTML("afterbegin", createModalBox());
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Select necessary elements for later use
|
|
208
|
+
const overlay = document.querySelector(".gallery-overlay"),
|
|
209
|
+
modalBox = document.querySelector(".modal-box"),
|
|
210
|
+
modalImg = document.querySelector(".modal-box--image img"),
|
|
211
|
+
modalCaption = document.querySelector(".img-caption"),
|
|
212
|
+
closeImg = document.querySelector(".close-img"),
|
|
213
|
+
hideGalleries = document.querySelectorAll(".hide-gallery");
|
|
214
|
+
|
|
215
|
+
// Function to show modal with specified image source and caption
|
|
216
|
+
const showModal = (imgSrc, imgCaption) => {
|
|
217
|
+
modalBox.classList.remove("invisible");
|
|
218
|
+
modalImg.src = imgSrc;
|
|
219
|
+
modalCaption.innerHTML = imgCaption;
|
|
171
220
|
};
|
|
172
|
-
}
|
|
173
|
-
if (imageGallery) {
|
|
174
|
-
callImageGallery();
|
|
175
|
-
}
|
|
176
221
|
|
|
177
|
-
//
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
vocabLists[btn].children[node].removeAttribute("class");
|
|
188
|
-
}
|
|
189
|
-
}});
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
for (let activeTerm = 0; activeTerm < vocabTerms.length; activeTerm++) {
|
|
193
|
-
vocabTerms[activeTerm].addEventListener("click", function() {
|
|
194
|
-
this.classList.toggle("active");
|
|
195
|
-
let termPanel = this.nextElementSibling;
|
|
196
|
-
if (termPanel.style.display === "block") { termPanel.removeAttribute("style"); }
|
|
197
|
-
else { termPanel.style.display = "block"; }
|
|
222
|
+
// Function to hide the modal
|
|
223
|
+
const hideModal = () => {
|
|
224
|
+
modalBox.classList.add("invisible");
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Attach event listeners to each image box
|
|
228
|
+
imgBoxes.forEach((imgBox) => {
|
|
229
|
+
// Show modal on click
|
|
230
|
+
imgBox.addEventListener("click", function () {
|
|
231
|
+
showModal(this.querySelector("img").src, this.querySelector("img").alt);
|
|
198
232
|
});
|
|
199
233
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
else { termPanel.style.display = "block"; }
|
|
234
|
+
// Make images tab-able and show modal on Enter key press
|
|
235
|
+
imgBox.setAttribute("tabindex", "0");
|
|
236
|
+
imgBox.addEventListener("keydown", function (event) {
|
|
237
|
+
if (event.key === "Enter") {
|
|
238
|
+
showModal(this.querySelector("img").src, this.querySelector("img").alt);
|
|
206
239
|
}
|
|
207
240
|
});
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
if (vocabListWidget) {callVocabList();}
|
|
211
|
-
|
|
212
|
-
// Media Container
|
|
213
|
-
const addMediaContainersAria = () => {
|
|
214
|
-
mediaContainers.forEach((eachContainer, index) => {
|
|
215
|
-
// loopID: find the current index value, convert it to its letter equivalent, then convert to lowercase
|
|
216
|
-
let loopId = String.fromCharCode(index + 65).toLowerCase();
|
|
217
|
-
let mediaObject = eachContainer.querySelector(".media-object");
|
|
218
|
-
let iframe = mediaObject.firstElementChild;
|
|
219
|
-
let mediaInfo = mediaObject.nextElementSibling;
|
|
241
|
+
});
|
|
220
242
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
243
|
+
// Attach event listeners for overlay, Escape key, and close button to hide the modal
|
|
244
|
+
overlay.onclick = hideModal;
|
|
245
|
+
window.onkeydown = (event) => {
|
|
246
|
+
if (event.keyCode === 27) {
|
|
247
|
+
hideModal();
|
|
225
248
|
}
|
|
249
|
+
};
|
|
250
|
+
closeImg.onclick = hideModal;
|
|
251
|
+
|
|
252
|
+
// Attach event listeners for "Hide/Show" button to toggle gallery visibility
|
|
253
|
+
hideGalleries.forEach((hideGallery) => {
|
|
254
|
+
hideGallery.addEventListener("click", () => {
|
|
255
|
+
hideGallery.nextElementSibling.classList.toggle("invisible");
|
|
256
|
+
hideGallery.innerHTML = hideGallery.innerHTML === "Hide" ? "Show" : "Hide";
|
|
257
|
+
});
|
|
226
258
|
});
|
|
227
|
-
}
|
|
228
|
-
if (mediaContainers) {addMediaContainersAria();}
|
|
259
|
+
};
|
|
229
260
|
|
|
261
|
+
// Check if imageGallery exists before initializing the image gallery
|
|
262
|
+
if (imageGallery) {
|
|
263
|
+
// Add Font Awesome CDN and initialize the image gallery
|
|
264
|
+
addFontAwesomeCdn();
|
|
265
|
+
callImageGallery();
|
|
266
|
+
}
|
|
230
267
|
//Tabs Widget
|
|
231
268
|
const callTabsWidget = () => {
|
|
232
269
|
|
|
233
270
|
let tabsWidgetsNum = 0;
|
|
234
271
|
|
|
235
|
-
tabsWidgets.forEach((tab,index) => {
|
|
236
|
-
let tabInputs = tab.querySelectorAll("input")
|
|
237
|
-
let tabLabels = tab.querySelectorAll("label")
|
|
238
|
-
let tabDivs = tab.querySelectorAll("div")
|
|
272
|
+
tabsWidgets.forEach((tab, index) => {
|
|
273
|
+
let tabInputs = tab.querySelectorAll("input");
|
|
274
|
+
let tabLabels = tab.querySelectorAll("label");
|
|
275
|
+
let tabDivs = tab.querySelectorAll("div");
|
|
276
|
+
|
|
277
|
+
//Check that there are more than just one tab
|
|
278
|
+
if (tabInputs.length < 2 || tabLabels.length < 2 || tabDivs.length < 2) {
|
|
279
|
+
console.error("Document error: please add more than just one tab for tabs widget");
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Check amount of tab elements present
|
|
284
|
+
if (tabInputs.length < tabLabels.length || tabInputs.length < tabDivs.length) {
|
|
285
|
+
console.error("Document error: missing tab input(s) in tab widget");
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (tabLabels.length < tabInputs.length || tabLabels.length < tabDivs.length) {
|
|
290
|
+
console.error("Document error: missing tab label(s) in tab widget");
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
239
293
|
|
|
240
294
|
let groupNum = index + 1;
|
|
241
295
|
|
|
@@ -243,97 +297,629 @@ const callTabsWidget = () => {
|
|
|
243
297
|
tab.setAttribute("role", "region");
|
|
244
298
|
tab.setAttribute("aria-label", `tab group ${groupNum}`)
|
|
245
299
|
|
|
246
|
-
for(tabIndex = 0; tabIndex < tabInputs.length; tabIndex++) {
|
|
300
|
+
for (tabIndex = 0; tabIndex < tabInputs.length; tabIndex++) {
|
|
247
301
|
|
|
248
302
|
let tabNum = tabsWidgetsNum + 1;
|
|
249
303
|
|
|
304
|
+
// Check on present variables
|
|
305
|
+
if (tabInputs == null) {
|
|
306
|
+
console.error("Document error: no inputs found for tabs widget");
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (tabLabels == null) {
|
|
311
|
+
console.error("Document error: no labels found for tabs widget");
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (tabInputs == null) {
|
|
316
|
+
console.error("Document error: no divs (tab panels) found for tabs widget");
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
250
320
|
//Add class, id, name, and aria-described by for inputs
|
|
251
321
|
tabInputs[tabIndex].classList.add("tab-input");
|
|
252
322
|
tabInputs[tabIndex].setAttribute("type", "radio")
|
|
253
323
|
tabInputs[tabIndex].setAttribute("id", `tab${tabNum}`);
|
|
254
|
-
tabInputs[tabIndex].setAttribute("name", `hint-group-${groupNum}`
|
|
255
|
-
tabInputs[tabIndex].setAttribute("aria-describedby", `tabHeading${tabNum}`)
|
|
324
|
+
tabInputs[tabIndex].setAttribute("name", `hint-group-${groupNum}`);
|
|
325
|
+
tabInputs[tabIndex].setAttribute("aria-describedby", `tabHeading${tabNum}`);
|
|
326
|
+
|
|
256
327
|
//Add class and for for labels
|
|
257
328
|
tabLabels[tabIndex].classList.add("tab-header");
|
|
258
|
-
tabLabels[tabIndex].setAttribute("for", `tab${tabNum}`)
|
|
329
|
+
tabLabels[tabIndex].setAttribute("for", `tab${tabNum}`);
|
|
330
|
+
|
|
259
331
|
//Add class, tabindex, and id for divs
|
|
260
|
-
tabDivs[tabIndex]
|
|
261
|
-
|
|
262
|
-
|
|
332
|
+
if (tabDivs[tabIndex]) {
|
|
333
|
+
tabDivs[tabIndex].classList.add("tab-panel");
|
|
334
|
+
tabDivs[tabIndex].setAttribute("tabindex", 0);
|
|
335
|
+
tabDivs[tabIndex].setAttribute("id", `tabHeading${tabNum}`);
|
|
336
|
+
}
|
|
337
|
+
|
|
263
338
|
//Add attributes for hide tab
|
|
264
|
-
if(tabIndex + 1 == tabInputs.length) {
|
|
265
|
-
tabLabels[tabIndex].classList.add("hide-tab")
|
|
339
|
+
if (tabIndex + 1 == tabInputs.length) {
|
|
340
|
+
tabLabels[tabIndex].classList.add("hide-tab");
|
|
266
341
|
tabInputs[tabIndex].checked = true;
|
|
267
|
-
tabDivs[tabIndex]
|
|
342
|
+
if (tabDivs[tabIndex]) {
|
|
343
|
+
tabDivs[tabIndex].classList.add("hide-panel");
|
|
344
|
+
}
|
|
268
345
|
}
|
|
269
346
|
tabsWidgetsNum++;
|
|
270
347
|
}
|
|
271
348
|
})
|
|
272
349
|
}
|
|
273
|
-
if (tabsWidgets) {callTabsWidget();}
|
|
350
|
+
if (tabsWidgets) { callTabsWidget(); }
|
|
351
|
+
// Vocab list widget
|
|
352
|
+
const callVocabList = () => {
|
|
274
353
|
|
|
275
|
-
|
|
276
|
-
|
|
354
|
+
const handleVocabClose = (vocabItem) => {
|
|
355
|
+
if (vocabItem) {
|
|
356
|
+
let listDefinitions = vocabItem.querySelectorAll("dd");
|
|
357
|
+
let listTerms = vocabItem.querySelectorAll("dt");
|
|
277
358
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
359
|
+
// If the button is clicked and it is the DD - then hide it
|
|
360
|
+
listDefinitions.forEach((definition) => {
|
|
361
|
+
definition.style.display = "none";
|
|
362
|
+
})
|
|
282
363
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
364
|
+
// If the button is clicked and it is the DT - then remove active class
|
|
365
|
+
listTerms.forEach((term) => {
|
|
366
|
+
term.classList.remove("active");
|
|
367
|
+
})
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Check if the vocab list has one or multiple items within
|
|
372
|
+
vocabLists.forEach((list) => {
|
|
287
373
|
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
374
|
+
//Count and ensure it has more than 1 term and definition
|
|
375
|
+
let terms = 0;
|
|
376
|
+
let definitions = 0;
|
|
377
|
+
let closeBtnPresent = false;
|
|
378
|
+
|
|
379
|
+
// If the list contains more than one set of <dt> and <dd> tags then add a close button
|
|
380
|
+
for (let listIndex = 0; listIndex < list.children.length; listIndex++) {
|
|
381
|
+
// Count terms
|
|
382
|
+
if (list.children[listIndex].tagName == "DT") {
|
|
383
|
+
list.children[listIndex].setAttribute("tabindex", "0");
|
|
384
|
+
terms++;
|
|
292
385
|
}
|
|
293
|
-
|
|
386
|
+
// Count definitions
|
|
387
|
+
if (list.children[listIndex].tagName == "DD") {
|
|
388
|
+
definitions++;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
//Check for close all button
|
|
392
|
+
if (list.children[listIndex].tagName == "BUTTON") {
|
|
393
|
+
closeBtnPresent = true;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Check for terms and definitions in the vocab list
|
|
398
|
+
if (terms < 1) {
|
|
399
|
+
console.error("Document error: no terms found in vocab list");
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (definitions < 1) {
|
|
404
|
+
console.error("Document error: no definitions found in vocab list");
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (terms > definitions) {
|
|
409
|
+
console.error("Document error: more terms than definitions in vocab list")
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// If there are more than 2 terms and 2 definitions, then check for a button
|
|
413
|
+
if (terms >= 2 && definitions >= 2) {
|
|
414
|
+
|
|
415
|
+
// If there isn't a close button then add one
|
|
416
|
+
if (!closeBtnPresent) {
|
|
417
|
+
// Add a close button
|
|
418
|
+
const closeButton = document.createElement("button");
|
|
419
|
+
closeButton.textContent = "Close All"; // Set the button text as needed
|
|
420
|
+
// Add click event listener for button
|
|
421
|
+
closeButton.addEventListener("click", () => handleVocabClose(list))
|
|
422
|
+
|
|
423
|
+
// Add keydown event listener for button
|
|
424
|
+
closeButton.addEventListener("keydown", (event) => {
|
|
425
|
+
if (event.key == "Enter") {
|
|
426
|
+
handleVocabClose(list);
|
|
427
|
+
}
|
|
428
|
+
})
|
|
429
|
+
// Append the button to the end of the list
|
|
430
|
+
list.appendChild(closeButton);
|
|
431
|
+
}
|
|
432
|
+
// If button is present, remove it
|
|
433
|
+
else {
|
|
434
|
+
let closeBtn = list.querySelector("button");
|
|
435
|
+
|
|
436
|
+
// Add the same event listeners as if you were to add a new button
|
|
437
|
+
closeBtn.addEventListener("click", () => handleVocabClose(list))
|
|
438
|
+
|
|
439
|
+
closeBtn.addEventListener("keydown", (event) => {
|
|
440
|
+
if (event.key == "Enter") {
|
|
441
|
+
handleVocabClose(list);
|
|
442
|
+
}
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
}
|
|
446
|
+
} else {
|
|
447
|
+
|
|
448
|
+
// List does not have more than 2 pairs of terms and definitions
|
|
449
|
+
|
|
450
|
+
// Don't add a close button since there is only one term, but remove the button if it is present
|
|
451
|
+
for (let listIndex = 0; listIndex < list.children.length; listIndex++) {
|
|
452
|
+
|
|
453
|
+
//Check for close all button
|
|
454
|
+
if (list.children[listIndex].tagName == "BUTTON") {
|
|
455
|
+
closeBtnPresent = true;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (closeBtnPresent) {
|
|
460
|
+
let closeBtn = list.querySelector("button");
|
|
461
|
+
closeBtn.style.display = "none";
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
// Loop through all the terms and apply click and keydown event
|
|
467
|
+
for (let activeTerm = 0; activeTerm < vocabTerms.length; activeTerm++) {
|
|
468
|
+
// Add click event for toggling vocab terms
|
|
469
|
+
vocabTerms[activeTerm].addEventListener("click", function () {
|
|
470
|
+
// When clicked, toggle the active class
|
|
471
|
+
this.classList.toggle("active");
|
|
472
|
+
|
|
473
|
+
// Target the definition <dd> element
|
|
474
|
+
let termPanel = this.nextElementSibling;
|
|
475
|
+
|
|
476
|
+
// Toggle the display from none to block
|
|
477
|
+
if (termPanel.style.display === "block") {
|
|
478
|
+
termPanel.style.display = "none";
|
|
479
|
+
} else {
|
|
480
|
+
termPanel.style.display = "block";
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Start a while loop to continue through the DOM
|
|
484
|
+
while (termPanel.nextElementSibling) {
|
|
485
|
+
// Move to the next sibling element
|
|
486
|
+
termPanel = termPanel.nextElementSibling;
|
|
487
|
+
|
|
488
|
+
// Check if the current element is a <dd>
|
|
489
|
+
if (termPanel.tagName === "DD") {
|
|
490
|
+
// Toggle the display from none to block
|
|
491
|
+
if (termPanel.style.display === "block") {
|
|
492
|
+
termPanel.style.display = "none";
|
|
493
|
+
} else {
|
|
494
|
+
termPanel.style.display = "block";
|
|
495
|
+
}
|
|
496
|
+
} else {
|
|
497
|
+
// Stop the loop if the current element is not a <dd>
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
// Add keydown event for toggling vocab terms
|
|
505
|
+
vocabTerms[activeTerm].addEventListener("keydown", function (e) {
|
|
506
|
+
|
|
507
|
+
// When user hits enter, toggle the active class
|
|
508
|
+
if (e.key === "Enter") {
|
|
509
|
+
// When clicked, toggle the active class
|
|
510
|
+
this.classList.toggle("active");
|
|
511
|
+
|
|
512
|
+
// Target the definition <dd> element
|
|
513
|
+
let termPanel = this.nextElementSibling;
|
|
514
|
+
|
|
515
|
+
// Toggle the display from none to block
|
|
516
|
+
if (termPanel.style.display === "block") {
|
|
517
|
+
termPanel.style.display = "none";
|
|
518
|
+
} else {
|
|
519
|
+
termPanel.style.display = "block";
|
|
520
|
+
}
|
|
521
|
+
// Start a while loop to continue through the DOM
|
|
522
|
+
while (termPanel.nextElementSibling) {
|
|
523
|
+
// Move to the next sibling element
|
|
524
|
+
termPanel = termPanel.nextElementSibling;
|
|
525
|
+
|
|
526
|
+
// Check if the current element is a <dd>
|
|
527
|
+
if (termPanel.tagName === "DD") {
|
|
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
|
+
} else {
|
|
535
|
+
// Stop the loop if the current element is not a <dd>
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
});
|
|
294
541
|
}
|
|
295
542
|
}
|
|
543
|
+
if (vocabListWidget) { callVocabList(); }
|
|
544
|
+
// Clean up HTML
|
|
545
|
+
const cleanMarkup = () => {
|
|
546
|
+
// Remove role="presentation" attr from any element that has it
|
|
547
|
+
if (rolePres) {
|
|
548
|
+
rolePres.forEach((roleElem) => roleElem.removeAttribute("role"));
|
|
549
|
+
}
|
|
550
|
+
// Set functino to remove atrributes from elements
|
|
551
|
+
const discardAttributes = (element, ...attributes) => {
|
|
552
|
+
attributes.forEach((attribute) => element.removeAttribute(attribute));
|
|
553
|
+
}
|
|
554
|
+
// Remove attributes from tables
|
|
555
|
+
const tableElems = document.querySelectorAll("table, thead, tbody, tr, th, td");
|
|
556
|
+
tableElems.forEach((elem) => {
|
|
557
|
+
discardAttributes(elem, "cellspacing", "cellpadding", "width", "style");
|
|
558
|
+
});
|
|
559
|
+
};
|
|
560
|
+
cleanMarkup();
|
|
561
|
+
// Content Lock Widget
|
|
296
562
|
|
|
297
|
-
//
|
|
298
|
-
const
|
|
563
|
+
// Save text content of unlock button
|
|
564
|
+
const unlockContent = [];
|
|
299
565
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
566
|
+
contentUnlockBtns.forEach((button) => {
|
|
567
|
+
unlockContent.push(button.textContent)
|
|
568
|
+
})
|
|
569
|
+
|
|
570
|
+
// Create an object that keeps track of keys and their statuses
|
|
571
|
+
let contentLockData = JSON.parse(localStorage.getItem("contentLockData")) || {};
|
|
572
|
+
|
|
573
|
+
if (lockedContent) {
|
|
574
|
+
|
|
575
|
+
// Checks the URL for the course number
|
|
576
|
+
const currentURL = window.parent.location.href;
|
|
577
|
+
const match = currentURL.match(/\/content\/(\d+)/);
|
|
578
|
+
const courseNumber = match ? match[1] : null;
|
|
579
|
+
|
|
580
|
+
// Add event listener for storage changes
|
|
581
|
+
window.addEventListener("storage", (event) => {
|
|
582
|
+
if (courseNumber) {
|
|
583
|
+
handleLocalStorageUpdate(event, courseNumber);
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
if (courseNumber) {
|
|
588
|
+
|
|
589
|
+
// Check if the course data object has the course number
|
|
590
|
+
if (!contentLockData.hasOwnProperty(courseNumber)) {
|
|
591
|
+
contentLockData[courseNumber] = {
|
|
592
|
+
keys: {},
|
|
593
|
+
id: 0,
|
|
594
|
+
};
|
|
595
|
+
} else {
|
|
596
|
+
// Reset the id count to 0 when the page is loaded
|
|
597
|
+
contentLockData[courseNumber].id = 0;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Get key number for each content area
|
|
601
|
+
lockedContent.forEach((contentArea, index) => {
|
|
602
|
+
let keyNum = contentArea.getAttribute("data-key");
|
|
603
|
+
|
|
604
|
+
// Check if key already exists within the course, if it doesn't, add the key
|
|
605
|
+
if (!contentLockData[courseNumber].keys[keyNum]) {
|
|
606
|
+
|
|
607
|
+
// Add the key to the course keys
|
|
608
|
+
contentLockData[courseNumber].keys[keyNum] = false;
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
// Go through each show/hide button and add click listener
|
|
613
|
+
contentUnlockBtns.forEach((button, index) => {
|
|
614
|
+
|
|
615
|
+
button.addEventListener("click", function () {
|
|
616
|
+
const keyNum = lockedContent[index].getAttribute("data-key");
|
|
617
|
+
|
|
618
|
+
// Add alert to ensure the user confirms the action to unlock the content
|
|
619
|
+
const confirmed = window.confirm(`Please confirm: ${unlockContent[index]}`)
|
|
620
|
+
|
|
621
|
+
if (confirmed) {
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
// Toggle the key status
|
|
625
|
+
contentLockData[courseNumber].keys[keyNum] = !contentLockData[courseNumber].keys[keyNum];
|
|
626
|
+
|
|
627
|
+
// Toggle classes based on key status
|
|
628
|
+
if (contentLockData[courseNumber].keys[keyNum]) {
|
|
629
|
+
lockedContent[index].classList.add("open");
|
|
630
|
+
contentLockInstructions[index].classList.add("complete");
|
|
631
|
+
} else {
|
|
632
|
+
lockedContent[index].classList.remove("open");
|
|
633
|
+
contentLockInstructions[index].classList.remove("complete");
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Save the updated contentLockData object to local storage
|
|
637
|
+
localStorage.setItem("contentLockData", JSON.stringify(contentLockData));
|
|
638
|
+
|
|
639
|
+
// Update the hidden content based on the key status
|
|
640
|
+
checkHiddenContent(courseNumber);
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
// Apply initial classes based on key status
|
|
645
|
+
const keyNum = lockedContent[index].getAttribute("data-key");
|
|
646
|
+
|
|
647
|
+
// Make sure contentLockData[courseNumber] is initialized
|
|
648
|
+
if (!contentLockData[courseNumber]) {
|
|
649
|
+
contentLockData[courseNumber] = {
|
|
650
|
+
keys: {},
|
|
651
|
+
id: 0
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (contentLockData[courseNumber].keys[keyNum]) {
|
|
656
|
+
lockedContent[index].classList.add("open");
|
|
657
|
+
contentLockInstructions[index].classList.add("complete");
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
// Call the checkHiddenContent function initially
|
|
662
|
+
checkHiddenContent(courseNumber);
|
|
663
|
+
|
|
664
|
+
// Save the updated contentLockData object to local storage
|
|
665
|
+
localStorage.setItem("contentLockData", JSON.stringify(contentLockData));
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Function to run when local storage is updated
|
|
670
|
+
function handleLocalStorageUpdate(event, courseNumber) {
|
|
671
|
+
if (event && event.key === "contentLockData" && event.newValue) {
|
|
672
|
+
// Update contentLockData variable
|
|
673
|
+
contentLockData = JSON.parse(event.newValue);
|
|
674
|
+
// Run your code here
|
|
675
|
+
checkHiddenContent(courseNumber);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Check content areas function
|
|
680
|
+
|
|
681
|
+
function checkHiddenContent(courseNumber) {
|
|
682
|
+
|
|
683
|
+
// Get key number for each content area
|
|
684
|
+
lockedContent.forEach((contentArea, index) => {
|
|
685
|
+
let keyNum = contentArea.getAttribute("data-key");
|
|
686
|
+
// Toggle classes based on key status
|
|
687
|
+
if (contentLockData[courseNumber].keys[keyNum]) {
|
|
688
|
+
lockedContent[index].classList.add("open");
|
|
689
|
+
contentLockInstructions[index].classList.add("complete");
|
|
690
|
+
} else {
|
|
691
|
+
lockedContent[index].classList.remove("open");
|
|
692
|
+
contentLockInstructions[index].classList.remove("complete");
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
// Flip Card Widget
|
|
697
|
+
function callFlipCardWidget() {
|
|
698
|
+
// Loop through each card
|
|
699
|
+
flipCards.forEach((flipCardGroup) => {
|
|
700
|
+
|
|
701
|
+
let flipCard = flipCardGroup.querySelectorAll(".flip-card");
|
|
702
|
+
let innerFlipCard = flipCardGroup.querySelectorAll(".inner-card");
|
|
703
|
+
let numOfCardsInGroup = flipCardGroup.children.length;
|
|
704
|
+
|
|
705
|
+
// Check to ensure each card has the .flip-card class
|
|
706
|
+
if (numOfCardsInGroup !== flipCard.length) {
|
|
707
|
+
console.error("Document error: missing .flip-card class for flip card widget");
|
|
708
|
+
return; // Stop execution if there's an error
|
|
709
|
+
}
|
|
710
|
+
// Check to ensure each card has the .inner-card class
|
|
711
|
+
if (numOfCardsInGroup !== innerFlipCard.length) {
|
|
712
|
+
console.error("Document error: missing .inner-card class for flip card widget");
|
|
713
|
+
return; // Stop execution if there's an error
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
flipCard.forEach((card) => {
|
|
717
|
+
let innerFlipCard = card.querySelectorAll(".inner-card");
|
|
718
|
+
|
|
719
|
+
innerFlipCard.forEach((innerCard) => {
|
|
720
|
+
innerCard.setAttribute("tabindex", 0); // Add tab index to allow flip cards to be tabbable
|
|
721
|
+
innerCard.addEventListener("click", () => {
|
|
722
|
+
innerCard.offsetHeight; // Force reflow by accessing the offsetHeight property
|
|
723
|
+
innerCard.classList.toggle("flip");
|
|
724
|
+
})
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
// Add a keydownevent event to each card
|
|
728
|
+
innerCard.addEventListener("keydown", (event) => {
|
|
729
|
+
if (event.key === "Enter") {
|
|
730
|
+
// Force reflow by accessing the offsetHeight property
|
|
731
|
+
innerCard.offsetHeight;
|
|
732
|
+
innerCard.classList.toggle("flip");
|
|
733
|
+
}
|
|
734
|
+
})
|
|
735
|
+
})
|
|
736
|
+
})
|
|
303
737
|
})
|
|
304
738
|
}
|
|
305
739
|
|
|
306
|
-
//
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
740
|
+
// If flip cards are present in the file, run this code
|
|
741
|
+
if (flipCards) { callFlipCardWidget() }
|
|
742
|
+
if (document.querySelector("body[focus-reader]")) {
|
|
743
|
+
|
|
744
|
+
// Initializations
|
|
745
|
+
let focusOn = false;
|
|
746
|
+
|
|
747
|
+
const fr_contentWrapper = document.querySelector("#content-wrapper");
|
|
748
|
+
const fr_thisBody = document.querySelector("body[focus-reader]");
|
|
749
|
+
|
|
750
|
+
// Add fontAwesome to header
|
|
751
|
+
const fr_pageHead = document.querySelector("head");
|
|
752
|
+
const fontAweLink = document.createElement("link");
|
|
753
|
+
fontAweLink.rel = "stylesheet";
|
|
754
|
+
fontAweLink.href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css";
|
|
755
|
+
fr_pageHead.append(fontAweLink);
|
|
756
|
+
|
|
757
|
+
// add intersection observer to head tag
|
|
758
|
+
const observerScript = document.createElement("script");
|
|
759
|
+
observerScript.setAttribute("src", "https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver");
|
|
760
|
+
fr_pageHead.append(observerScript);
|
|
761
|
+
|
|
762
|
+
// Create Switches container
|
|
763
|
+
const fr_switchesContainer = document.createElement("div");
|
|
764
|
+
fr_switchesContainer.className = "focus-reader-switches";
|
|
765
|
+
fr_thisBody.append(fr_switchesContainer);
|
|
766
|
+
|
|
767
|
+
// Focus-text container
|
|
768
|
+
const fr_focusTextContainer = document.createElement("div");
|
|
769
|
+
fr_switchesContainer.append(fr_focusTextContainer);
|
|
770
|
+
|
|
771
|
+
// Focus-text Icon
|
|
772
|
+
const fr_focusTextIcon = document.createElement("p");
|
|
773
|
+
fr_focusTextIcon.innerHTML = "Focus Text"
|
|
774
|
+
fr_focusTextContainer.append(fr_focusTextIcon);
|
|
775
|
+
|
|
776
|
+
// info icon
|
|
777
|
+
const fr_infoIcon = document.createElement("i");
|
|
778
|
+
fr_infoIcon.classList.add("fa-solid");
|
|
779
|
+
fr_infoIcon.classList.add("fa-circle-info");
|
|
780
|
+
fr_focusTextContainer.append(fr_infoIcon);
|
|
781
|
+
|
|
782
|
+
// info tooltip
|
|
783
|
+
const fr_infoTooltip = document.createElement("span");
|
|
784
|
+
const focusReaderTooltipText = "Highlight text as you scroll";
|
|
785
|
+
fr_infoTooltip.classList.add("info-tooltip");
|
|
786
|
+
fr_infoTooltip.innerHTML = focusReaderTooltipText;
|
|
787
|
+
fr_infoIcon.append(fr_infoTooltip);
|
|
788
|
+
|
|
789
|
+
// Focus-text switch button
|
|
790
|
+
const fr_focusTextSwitch = document.createElement("button");
|
|
791
|
+
fr_focusTextSwitch.id = "focus-text";
|
|
792
|
+
fr_focusTextSwitch.innerHTML = (focusOn) ? `<i class="fas fa-toggle-on"></i>` : `<i class="fas fa-toggle-off"></i>`;
|
|
793
|
+
fr_focusTextContainer.append(fr_focusTextSwitch);
|
|
794
|
+
|
|
795
|
+
// Focus-text logic
|
|
796
|
+
fr_focusTextSwitch.addEventListener("click", () => {
|
|
797
|
+
if (focusOn) {
|
|
798
|
+
focusOn = !focusOn;
|
|
799
|
+
fr_contentWrapper.removeAttribute("on");
|
|
800
|
+
fr_focusTextSwitch.innerHTML = (focusOn) ? `<i class="fas fa-toggle-on"></i>` : `<i class="fas fa-toggle-off"></i>`;
|
|
801
|
+
removeSpans(fr_contentWrapper);
|
|
802
|
+
} else {
|
|
803
|
+
focusOn = !focusOn;
|
|
804
|
+
fr_contentWrapper.setAttribute("on", "");
|
|
805
|
+
fr_focusTextSwitch.innerHTML = (focusOn) ? `<i class="fas fa-toggle-on"></i>` : `<i class="fas fa-toggle-off"></i>`;
|
|
806
|
+
const elements = document.querySelectorAll("#content-wrapper .content-body :is(p, li, dd, dt, blockquote)");
|
|
807
|
+
elements.forEach(element => {
|
|
808
|
+
traverseAndWrapTextInSpans(element, focusText);
|
|
319
809
|
});
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
const options = {
|
|
814
|
+
root: null,
|
|
815
|
+
rootMargin: '-48.9% 0px',
|
|
816
|
+
threshold: 0.00
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
const focusText = new IntersectionObserver(entries => {
|
|
820
|
+
entries.forEach(entry => {
|
|
821
|
+
if (entry.isIntersecting) {
|
|
822
|
+
entry.target.closest("span").classList.add("focus-text");
|
|
823
|
+
} else {
|
|
824
|
+
entry.target.closest("span").classList.remove("focus-text");
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
}, options);
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
function traverseAndWrapTextInSpans(node, focusText) {
|
|
833
|
+
if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== '') {
|
|
834
|
+
// Split the text into words
|
|
835
|
+
const words = node.textContent.split(' ');
|
|
836
|
+
|
|
837
|
+
// Wrap each word in a span
|
|
838
|
+
words.forEach(word => {
|
|
839
|
+
if (word !== '') {
|
|
840
|
+
const span = document.createElement("span");
|
|
841
|
+
span.textContent = word + ' '; // Add a space after the word to preserve spacing
|
|
842
|
+
|
|
843
|
+
// Observe the span
|
|
844
|
+
focusText.observe(span);
|
|
845
|
+
|
|
846
|
+
// Insert the span before the text node
|
|
847
|
+
node.parentNode.insertBefore(span, node);
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
// Remove the original text node
|
|
852
|
+
node.parentNode.removeChild(node);
|
|
853
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
854
|
+
// Recursively process child nodes
|
|
855
|
+
Array.from(node.childNodes).forEach(childNode => {
|
|
856
|
+
traverseAndWrapTextInSpans(childNode, focusText);
|
|
320
857
|
});
|
|
321
|
-
callAnimateBorder.observe(hrsBorders[hrsBorder]);
|
|
322
858
|
}
|
|
323
|
-
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
function removeSpans(node) {
|
|
862
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
863
|
+
// 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
|
|
864
|
+
if (node.tagName === 'SPAN' && (node.className === 'focus-text' || node.className === '' || node.textContent.trim() === '' || node.attributes.length === 0)) {
|
|
865
|
+
const textNode = document.createTextNode(node.textContent);
|
|
866
|
+
node.parentNode.insertBefore(textNode, node);
|
|
867
|
+
node.parentNode.removeChild(node);
|
|
868
|
+
} else {
|
|
869
|
+
// Recursively process child nodes
|
|
870
|
+
Array.from(node.childNodes).forEach(childNode => {
|
|
871
|
+
removeSpans(childNode);
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
// Change footnotes from 'show' to 'hide'
|
|
877
|
+
const footnotes = document.querySelector(".toggle-footnotes");
|
|
324
878
|
|
|
879
|
+
if (footnotes) {
|
|
880
|
+
footnotes.addEventListener("click", () => {
|
|
881
|
+
footnotes.innerHTML = (footnotes.innerHTML === "[Show Footnotes]") ? "[Hide Footnotes]" : "[Show Footnotes]";
|
|
882
|
+
})
|
|
883
|
+
}
|
|
325
884
|
// Add h5p resizer dynamically
|
|
326
885
|
h5pResizer.setAttribute("src", "https://pima.h5p.com/js/h5p-resizer.js");
|
|
327
886
|
h5pResizer.setAttribute("charset", "UTF-8");
|
|
328
887
|
h5pResizer.setAttribute("defer", "");
|
|
329
888
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
889
|
+
// If any iframes are detected run this function
|
|
890
|
+
function checkIframes() {
|
|
891
|
+
h5pIframes.forEach(function (h5pIframe) {
|
|
892
|
+
const src = h5pIframe.getAttribute("src");
|
|
893
|
+
if (src.includes("/d2l/common/dialogs/quickLink") || src.includes("https://pima.h5p.com/content") || src.includes("h5p") && !h5pResizerExists) {
|
|
894
|
+
docHead.appendChild(h5pResizer);
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// Call function if iframes exist
|
|
900
|
+
if (h5pIframes) { checkIframes() }
|
|
336
901
|
|
|
902
|
+
|
|
903
|
+
// Helper JS for Responsive Tables
|
|
904
|
+
const initResponsiveTables = () => {
|
|
905
|
+
for (let table = 0; table < tables.length; table++) {
|
|
906
|
+
let headertext = [],
|
|
907
|
+
headers = tables[table].querySelectorAll(".display table th, table.display th, .display-lg table th, table.display-lg th"),
|
|
908
|
+
tablebody = tables[table].querySelector(".display table tbody, table.display tbody, .display-lg table tbody, table.display-lg tbody");
|
|
909
|
+
for (let header = 0; header < headers.length; header++) {
|
|
910
|
+
let current = headers[header];
|
|
911
|
+
headertext.push(current.textContent.replace(/\r?\n|\r/, ""));
|
|
912
|
+
}
|
|
913
|
+
for (let y = 0, row; row = tablebody.rows[y]; y++) {
|
|
914
|
+
for (let j = 0, col; col = row.cells[j]; j++) {
|
|
915
|
+
col.setAttribute("data-th", headertext[j]);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (tables) {
|
|
921
|
+
initResponsiveTables();
|
|
922
|
+
}
|
|
337
923
|
// Call function with jQuery scripts
|
|
338
924
|
const callJquery = () => {
|
|
339
925
|
// Toggle Button's Arrow Right Points Down on Click
|
|
@@ -358,4 +944,63 @@ const callJquery = () => {
|
|
|
358
944
|
});
|
|
359
945
|
$(".tooltip .video-container").parent().css("width", "450px");
|
|
360
946
|
}
|
|
361
|
-
callJquery();
|
|
947
|
+
callJquery();
|
|
948
|
+
// This is called by anchor links via onlick="" in the HTML
|
|
949
|
+
// Added because default anchor links don't work on all browsers using D2L
|
|
950
|
+
const jumpTo = (anchor) => {
|
|
951
|
+
document.getElementById(anchor).scrollIntoView();
|
|
952
|
+
}
|
|
953
|
+
// Array for all themes that require theme specific js
|
|
954
|
+
const customJsThemes = ["ecn", "hrs", "ss"];
|
|
955
|
+
|
|
956
|
+
//Search links for theme styles
|
|
957
|
+
const themeJsCheck = () => {
|
|
958
|
+
|
|
959
|
+
const links = document.querySelectorAll("link");
|
|
960
|
+
|
|
961
|
+
links.forEach((link) => {
|
|
962
|
+
const href = link.getAttribute("href");
|
|
963
|
+
customJsThemes.forEach((theme) => {
|
|
964
|
+
// If theme requires custom js, run addThemeScript()
|
|
965
|
+
if (href && href.includes(`/${theme}/styles.css`)) {
|
|
966
|
+
addThemeScript(theme);
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
// Adds a script to the head for that particular theme
|
|
972
|
+
function addThemeScript(theme) {
|
|
973
|
+
let themeScript = document.createElement("script");
|
|
974
|
+
// URL references theme-specific js module from CDN
|
|
975
|
+
themeScript.src = `https://cdn.jsdelivr.net/npm/@pimaonline/pimaonline-themepack/dist/js/themes/${theme}.js`;
|
|
976
|
+
document.head.appendChild(themeScript);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
themeJsCheck()
|
|
981
|
+
// Toggle footnotes
|
|
982
|
+
const toggleBtns = document.querySelectorAll(".toggle-btn, .toggle-footnotes");
|
|
983
|
+
|
|
984
|
+
if (toggleBtns) {
|
|
985
|
+
if (document.querySelector(".toggle-btn") || document.querySelector(".toggle-footnotes")) {
|
|
986
|
+
for (let toggleBtn = 0; toggleBtn < toggleBtns.length; toggleBtn++) {
|
|
987
|
+
// Add tabindex
|
|
988
|
+
toggleBtns[toggleBtn].setAttribute("tabindex", "0");
|
|
989
|
+
// Show/hide on click
|
|
990
|
+
toggleBtns[toggleBtn].addEventListener("click", () => {
|
|
991
|
+
if (toggleBtns[toggleBtn].nextElementSibling) {
|
|
992
|
+
toggleBtns[toggleBtn].nextElementSibling.classList.toggle("show");
|
|
993
|
+
}
|
|
994
|
+
})
|
|
995
|
+
|
|
996
|
+
// Show/hide on enter for users who use tab
|
|
997
|
+
toggleBtns[toggleBtn].addEventListener("keydown", (enter) => {
|
|
998
|
+
if (enter.keyCode === 13) {
|
|
999
|
+
if (toggleBtns[toggleBtn].nextElementSibling) {
|
|
1000
|
+
toggleBtns[toggleBtn].nextElementSibling.classList.toggle("show");
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
})
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|