@pimaonline/pimaonline-themepack 2.1.6 → 2.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,61 +1,83 @@
1
1
  /// @description Main JS file for PimaOnline Themepack
2
2
  /// @dependencies jQuery 3.3.1 or later
3
3
 
4
- // Grid
5
- if (document.getElementById("second-column") && document.getElementById("third-column")) {
6
- document.getElementsByTagName("body")[0].id = "three-column";
7
-
8
- } else if (document.getElementById("second-column") && !document.getElementById("column-widget")) {
9
- document.getElementsByTagName("body")[0].id = "two-column";
10
-
11
- } else if (document.getElementById("column-widget")) {
12
- document.getElementsByTagName("body")[0].id = "two-col-widget";
13
-
14
- } else if (document.getElementById("video-wrapper")) {
15
- document.getElementsByTagName("body")[0].id = "video-grid";
4
+ const courseBody = document.querySelector("body");
5
+ const contentWrapper = document.querySelector("#content-wrapper");
6
+ const secondColumn = document.querySelector("#second-column");
7
+ const thirdColumn = document.querySelector("#third-column");
8
+ const columnWidget = document.querySelector("#column-widget");
9
+ const videoWrapper = document.querySelector("#video-wrapper");
10
+ const rolePres = document.querySelectorAll('[role="presentation"]');
11
+ const imageGallery = document.querySelector(".image-gallery");
12
+ const vocabListWidget = document.querySelector("dl.vocab-list");
13
+ const vocabTerms = document.querySelectorAll("dl.vocab-list dt");
14
+ const vocabDefs = document.querySelectorAll("dl.vocab-list dd");
15
+ const vocabCloseBtns = document.querySelectorAll("dl.vocab-list button");
16
+ const vocabLists = document.querySelectorAll("dl[class^='vocab-list']");
17
+ const mediaContainers = document.querySelectorAll(".media-container");
18
+ const tabsWidgets = document.querySelectorAll(".tabs");
16
19
 
17
- } else {
18
- document.getElementsByTagName("body")[0].id = "one-column";
19
- }
20
+ // Grid
21
+ const addGrid = () => {
22
+ if (secondColumn && thirdColumn) {
23
+ courseBody.id = "three-column";
24
+ } else if (secondColumn && !columnWidget) {
25
+ courseBody.id = "two-column";
26
+ } else if (columnWidget) {
27
+ courseBody.id = "two-col-widget";
28
+ } else if (videoWrapper) {
29
+ courseBody.id = "video-grid";
30
+ } else {
31
+ courseBody.id = "one-column";
32
+ }
33
+ };
34
+ addGrid();
20
35
 
21
36
  // JS to add role and aria-label to content-wrapper, second-column, and third-column
22
- let cwAttrib = document.querySelector("#content-wrapper");
23
- cwAttrib.setAttribute("role", "main");
24
-
25
- if (document.querySelector("#second-column")) {
26
- let scAttrib = document.querySelector("#second-column");
27
- scAttrib.setAttribute("role", "region");
28
- scAttrib.setAttribute("aria-label", "second column");
29
- }
30
-
31
- if (document.querySelector("#third-column")) {
32
- let tcAttrib = document.querySelector("#third-column");
33
- tcAttrib.setAttribute("role", "region");
34
- tcAttrib.setAttribute("aria-label", "third column");
35
- }
36
-
37
- // Remove role="presentation" attr
38
- if (document.querySelectorAll('[role="presentation"]')) {
39
- let rolePres = document.querySelectorAll('[role="presentation"]');
40
- rolePres.forEach((roleElem) => roleElem.removeAttribute("role"));
41
- }
37
+ const addAria = () => {
38
+ contentWrapper.setAttribute("role", "main");
39
+ if (secondColumn) {
40
+ secondColumn.setAttribute("role", "region");
41
+ secondColumn.setAttribute("aria-label", "Second column");
42
+ }
43
+ if (thirdColumn) {
44
+ thirdColumn.setAttribute("role", "region");
45
+ thirdColumn.setAttribute("aria-label", "Third column");
46
+ }
47
+ };
48
+ addAria();
49
+
50
+ // Clean up HTML
51
+ const cleanMarkup = () => {
52
+ // Remove role="presentation" attr from any element that has it
53
+ if (rolePres) {
54
+ rolePres.forEach((roleElem) => roleElem.removeAttribute("role"));
55
+ }
56
+ // Set functino to remove atrributes from elements
57
+ const discardAttributes = (element, ...attributes) => {
58
+ attributes.forEach((attribute) => element.removeAttribute(attribute));
59
+ }
60
+ // Remove attributes from tables
61
+ const tableElems = document.querySelectorAll("table, thead, tbody, tr, th, td");
62
+ tableElems.forEach((elem) => {
63
+ discardAttributes(elem, "cellspacing", "cellpadding", "width", "style");
64
+ });
65
+ };
66
+ cleanMarkup();
42
67
 
43
68
  // Helper JS for Responsive Tables
44
- function initResponsiveTables() {
45
- var tables = document.querySelectorAll(".display, .display-lg")
46
-
47
- for (var t = 0; t < tables.length; t++) {
48
-
49
- var headertext = [],
50
- headers = tables[t].querySelectorAll(".display table th, table.display th, .display-lg table th, table.display-lg th"),
51
- tablebody = tables[t].querySelector(".display table tbody, table.display tbody, .display-lg table tbody, table.display-lg tbody");
52
-
53
- for (var i = 0; i < headers.length; i++) {
54
- var current = headers[i];
69
+ const initResponsiveTables = () => {
70
+ const tables = document.querySelectorAll(".display, .display-lg")
71
+ for (let table = 0; table < tables.length; table++) {
72
+ let headertext = [],
73
+ headers = tables[table].querySelectorAll(".display table th, table.display th, .display-lg table th, table.display-lg th"),
74
+ tablebody = tables[table].querySelector(".display table tbody, table.display tbody, .display-lg table tbody, table.display-lg tbody");
75
+ for (let header = 0; header < headers.length; header++) {
76
+ let current = headers[header];
55
77
  headertext.push(current.textContent.replace(/\r?\n|\r/, ""));
56
78
  }
57
- for (var y = 0, row; row = tablebody.rows[y]; y++) {
58
- for (var j = 0, col; col = row.cells[j]; j++) {
79
+ for (let y = 0, row; row = tablebody.rows[y]; y++) {
80
+ for (let j = 0, col; col = row.cells[j]; j++) {
59
81
  col.setAttribute("data-th", headertext[j]);
60
82
  }
61
83
  }
@@ -63,143 +85,224 @@ function initResponsiveTables() {
63
85
  }
64
86
  initResponsiveTables();
65
87
 
66
- // Remove inline CSS from tables
67
- const discardAttributes = (element, ...attributes) =>
68
- attributes.forEach((attribute) => element.removeAttribute(attribute));
69
-
70
- document.querySelectorAll("table, thead, tbody, tr, th, td").forEach((elem) => {
71
- // Function parameters: discardAttributes(element, attribute1, attribute2, attribute3, ...)
72
- discardAttributes(elem, "cellspacing", "cellpadding", "width", "style");
73
- });
74
-
75
- // Append scripts-async.js to DOM
76
- const addScript = (script) => {
77
- let newScript = document.createElement("script");
78
- newScript.setAttribute("src", script);
79
-
80
- document.body.append(newScript);
81
- }
82
-
83
- if(document.querySelector("[onclick]")) {
84
- addScript("https://d2l.pima.edu/shared/webdev/course-files/js/jumpTo.js");
85
- }
88
+ // This is called by anchor links via onlick="" in the HTML
89
+ // Added because default anchor links don't work on all browsers using D2L
90
+ const jumpTo = (anchor) => {
91
+ document.getElementById(anchor).scrollIntoView();
92
+ }
86
93
 
87
94
  // Image gallery
88
- if(document.querySelector(".image-gallery")) {
89
- const modalBox = document.querySelector('.modal-box'),
90
- overlay = document.querySelector('.gallery-overlay'),
91
- imgBoxes = document.querySelectorAll('.image-box'),
92
- modalImg = document.querySelector('.modal-box--image img'),
93
- closeImg = document.querySelector('.close-img');
94
-
95
-
96
- for (let imgBox = 0; imgBox < imgBoxes.length; imgBox++) {
97
- imgBoxes[imgBox].onclick = function () {
98
- modalBox.classList.remove('invisible');
99
-
100
- let imgSrc = this.querySelector('img').src;
101
-
102
- modalImg.src = imgSrc;
103
- }
104
- }
105
-
106
- overlay.onclick = function () {
107
- modalBox.classList.add('invisible');
95
+ const callImageGallery = () => {
96
+ // Create link element with font-awesome cdn and append it to the <head>
97
+ const docHead = document.querySelector("head");
98
+ const fontAwesomeCdn = document.createElement("link");
99
+ fontAwesomeCdn.setAttribute("rel", "stylesheet");
100
+ fontAwesomeCdn.setAttribute("href", "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css");
101
+ docHead.appendChild(fontAwesomeCdn);
102
+ // Begin image gallery
103
+ const imgGalleries = document.querySelectorAll(".image-gallery"),
104
+ imgBoxes = document.querySelectorAll(".image-box"),
105
+ modalBoxContent = `<div class="modal-box invisible">
106
+ <div class="gallery-overlay"></div>
107
+ <figure class="modal-box--image"><i class="fa-solid fa-x close-img"></i> <img src="#" alt="image here" /><figcaption class="img-caption"></figcaption></figure>
108
+ </div>
109
+ <button class="hide-gallery">Hide</button>`;
110
+
111
+ // createModalBox.innerHTML = modalBoxContent;
112
+ for (let imgGallery = 0; imgGallery < imgGalleries.length; imgGallery++) {
113
+ imgGalleries[imgGallery].insertAdjacentHTML("afterbegin", modalBoxContent);
108
114
  }
109
115
 
110
- window.onkeydown = function (esc) {
111
- if (esc.keyCode === 27) {
112
- modalBox.classList.add('invisible');
116
+ if (document.querySelector(".modal-box")) {
117
+ const overlay = document.querySelector(".gallery-overlay"),
118
+ modalBox = document.querySelector(".modal-box"),
119
+ modalImg = document.querySelector(".modal-box--image img"),
120
+ modalCaption = document.querySelector(".img-caption"),
121
+ closeImg = document.querySelector(".close-img");
122
+
123
+ for (let imgBox = 0; imgBox < imgBoxes.length; imgBox++) {
124
+ imgBoxes[imgBox].onclick = function () {
125
+ modalBox.classList.remove("invisible");
126
+ let imgSrc = this.querySelector("img").src;
127
+ modalImg.src = imgSrc;
128
+ let imgCaption = this.querySelector("img").alt;
129
+ modalCaption.innerHTML = imgCaption;
130
+ }
113
131
  }
114
- }
115
-
116
- closeImg.onclick = function () {
117
- modalBox.classList.add("invisible");
118
- }
119
-
120
- const hideGallery = document.querySelector(".hide-gallery"),
121
- galleryWrapper = document.querySelector(".gallery-wrapper");
122
-
123
- hideGallery.addEventListener("click", () => {
124
- galleryWrapper.classList.toggle("invisible");
125
- });
132
+ overlay.onclick = () => {
133
+ modalBox.classList.add("invisible");
134
+ }
135
+ window.onkeydown = (esc) => {
136
+ if (esc.keyCode === 27) {
137
+ modalBox.classList.add("invisible");
138
+ }
139
+ }
140
+ closeImg.onclick = () => {
141
+ modalBox.classList.add("invisible");
142
+ }
143
+ const hideGalleries = document.querySelectorAll(".hide-gallery");
144
+ for (let hideGallery = 0; hideGallery < hideGalleries.length; hideGallery++) {
145
+ hideGalleries[hideGallery].addEventListener("click", () => {
146
+ hideGalleries[hideGallery].nextElementSibling.classList.toggle("invisible");
147
+ if (hideGalleries[hideGallery].innerHTML === "Hide") {
148
+ hideGalleries[hideGallery].innerHTML = "Show";
149
+ } else {
150
+ hideGalleries[hideGallery].innerHTML = "Hide";
151
+ }
152
+ });
153
+ }
154
+ };
155
+ }
156
+ if (imageGallery) {
157
+ callImageGallery();
126
158
  }
127
159
 
128
160
  // Vocab list widget
129
- const vocabListWidget = document.querySelector("dl.vocab-list");
130
- const vocabTerms = document.querySelectorAll("dl.vocab-list dt");
131
- const vocabDefs = document.querySelectorAll("dl.vocab-list dd");
132
-
133
- if (vocabListWidget) {
134
- const vocabCloseBtn = document.querySelector("dl.vocab-list button");
135
-
136
- vocabCloseBtn.addEventListener("click", function() {
137
- for (let def = 0; def < vocabDefs.length; def++) {
138
- vocabDefs[def].removeAttribute("style");
139
- }
140
-
141
- for (let term = 0; term < vocabTerms.length; term++) {
142
- vocabTerms[term].removeAttribute("class");
161
+ const callVocabList = () => {
162
+ if (vocabCloseBtns) {
163
+ for(let btn = 0; btn < vocabCloseBtns.length; btn++) {
164
+ vocabCloseBtns[btn].addEventListener("click", () => {
165
+ for(let node = 0; node < vocabLists[btn].children.length;node++) {
166
+ if(vocabLists[btn].children[node].tagName == "DD") {
167
+ vocabLists[btn].children[node].removeAttribute("style");
168
+ }
169
+ if(vocabLists[btn].children[node].tagName == "DT") {
170
+ vocabLists[btn].children[node].removeAttribute("class");
171
+ }
172
+ }});
143
173
  }
144
- });
145
-
174
+ }
146
175
  for (let activeTerm = 0; activeTerm < vocabTerms.length; activeTerm++) {
147
176
  vocabTerms[activeTerm].addEventListener("click", function() {
148
177
  this.classList.toggle("active");
178
+ let termPanel = this.nextElementSibling;
179
+ if (termPanel.style.display === "block") { termPanel.removeAttribute("style"); }
180
+ else { termPanel.style.display = "block"; }
181
+ });
149
182
 
183
+ vocabTerms[activeTerm].addEventListener("keydown", function(e) {
184
+ if(e.key === "Enter") {
185
+ this.classList.toggle("active");
150
186
  let termPanel = this.nextElementSibling;
151
187
  if (termPanel.style.display === "block") { termPanel.removeAttribute("style"); }
152
188
  else { termPanel.style.display = "block"; }
189
+ }
153
190
  });
154
191
  }
155
192
  }
193
+ if (vocabListWidget) {callVocabList();}
194
+
195
+ //
196
+ const addMediaContainersAria = () => {
197
+ mediaContainers.forEach((eachContainer, index) => {
198
+ // loopID: find the current index value, convert it to its letter equivalent, then convert to lowercase
199
+ let loopId = String.fromCharCode(index + 65).toLowerCase();
200
+ let mediaObject = eachContainer.querySelector(".media-object");
201
+ let iframe = mediaObject.firstElementChild;
202
+ let mediaInfo = mediaObject.nextElementSibling;
203
+
204
+ // If element DOES NOT have "aria-describedby" && it DOES have a sibling element.
205
+ if (!iframe.hasAttribute("aria-describedby") && mediaInfo != null) {
206
+ iframe.setAttribute("aria-describedby", `${loopId}`);
207
+ mediaInfo.id = `${[loopId]}`;
208
+ }
209
+ });
210
+ }
211
+ if (mediaContainers) {addMediaContainersAria();}
212
+
213
+ //Tabs Widget
214
+ const callTabsWidget = () => {
215
+
216
+ let tabsWidgetsNum = 0;
217
+
218
+ tabsWidgets.forEach((tab,index) => {
219
+ let tabInputs = tab.querySelectorAll("input")
220
+ let tabLabels = tab.querySelectorAll("label")
221
+ let tabDivs = tab.querySelectorAll("div")
222
+
223
+ let groupNum = index + 1;
224
+
225
+ //Add region and aria-label to parent div
226
+ tab.setAttribute("role", "region");
227
+ tab.setAttribute("aria-label", `tab group ${groupNum}`)
228
+
229
+ for(tabIndex = 0; tabIndex < tabInputs.length; tabIndex++) {
230
+
231
+ let tabNum = tabsWidgetsNum + 1;
232
+
233
+ //Add class, id, name, and aria-described by for inputs
234
+ tabInputs[tabIndex].classList.add("tab-input");
235
+ tabInputs[tabIndex].setAttribute("type", "radio")
236
+ tabInputs[tabIndex].setAttribute("id", `tab${tabNum}`);
237
+ tabInputs[tabIndex].setAttribute("name", `hint-group-${groupNum}` )
238
+ tabInputs[tabIndex].setAttribute("aria-describedby", `tabHeading${tabNum}`)
239
+ //Add class and for for labels
240
+ tabLabels[tabIndex].classList.add("tab-header");
241
+ tabLabels[tabIndex].setAttribute("for", `tab${tabNum}`)
242
+ //Add class, tabindex, and id for divs
243
+ tabDivs[tabIndex].classList.add("tab-panel")
244
+ tabDivs[tabIndex].setAttribute("tabindex", 0)
245
+ tabDivs[tabIndex].setAttribute("id", `tabHeading${tabNum}`)
246
+ //Add attributes for hide tab
247
+ if(tabIndex + 1 == tabInputs.length) {
248
+ tabLabels[tabIndex].classList.add("hide-tab")
249
+ tabInputs[tabIndex].checked = true;
250
+ tabDivs[tabIndex].classList.add("hide-panel")
251
+ }
252
+ tabsWidgetsNum++;
253
+ }
254
+ })
255
+ }
256
+ if (tabsWidgets) {callTabsWidget();}
156
257
 
157
- // Show/Hide Toggle Button for Accordion
158
- $('.toggle-btn').on('click', function () {
159
- $(this).next().slideToggle(200);
160
- });
161
-
162
- // Show/Hide for Footnotes and Footer
163
- $('.toggle-footnotes').on('click', function () {
164
- $(this).next().slideToggle(200);
165
- $(this).text($(this).text() == '[Hide Footnotes]' ? '[Show Footnotes]' : '[Hide Footnotes]');
166
- });
167
-
168
- // Toggle Button's Arrow Right Points Down on Click
169
- $('.arrow-right').on('click', function () {
170
- $(this).toggleClass('arrow-down');
171
- });
172
-
173
- Fancybox
174
- $("[data-fancybox]").fancybox({
175
- idleTime: false,
176
- topRatio: 0.1,
177
- helpers: {
178
- title: {
179
- type: 'inside'
180
- },
181
- }
182
- });
183
-
184
- /*------WIDGET SCRIPTS------ */
185
- /* TOOLTIP ------ */
186
- // Allows Screen readers to toggle a tooltip on click and to say if the tooltip is collapsed or expanded.
258
+ // Toggle footnotes
259
+ const toggleBtns = document.querySelectorAll(".toggle-btn, .toggle-footnotes");
187
260
 
188
- $(".tooltip").click(function () {
189
- $(this).children(".tip-hover").toggle();
190
- if ($(this).children(".tip-hover").is(':visible')) {
191
- $(this).attr('aria-expanded', 'true');
192
- $(this).removeClass('hidden');
193
- } else {
194
- $(this).attr('aria-expanded', 'false');
195
- $(this).addClass('hidden');
196
- }
197
- });
261
+ if (document.querySelector(".toggle-btn") || document.querySelector(".toggle-footnotes")) {
262
+ for (let toggleBtn = 0; toggleBtn < toggleBtns.length; toggleBtn++) {
263
+ // Add tabindex
264
+ toggleBtns[toggleBtn].setAttribute("tabindex", "0");
198
265
 
199
- var start = 999;
266
+ // Show/hide on click
267
+ toggleBtns[toggleBtn].addEventListener("click", () => {
268
+ toggleBtns[toggleBtn].nextElementSibling.classList.toggle("show");
269
+ })
200
270
 
201
- $('.tooltip').each(function (i) {
202
- $(this).css('z-index', start--);
203
- });
271
+ // Show/hide on enter for users who use tab
272
+ toggleBtns[toggleBtn].addEventListener("keydown", (enter) => {
273
+ if (enter.keyCode === 13) {
274
+ toggleBtns[toggleBtn].nextElementSibling.classList.toggle("show");
275
+ }
276
+ })
277
+ }
278
+ }
204
279
 
205
- $(".tooltip .video-container").parent().css("width", "450px");
280
+ // Call function with jQuery scripts
281
+ const callJquery = () => {
282
+ // Show/Hide Toggle Button for Accordion widget
283
+ $('.toggle-btn').on('click', function () {
284
+ $(this).next().slideToggle(200);
285
+ });
286
+ // Toggle Button's Arrow Right Points Down on Click
287
+ $('.arrow-right').on('click', function () {
288
+ $(this).toggleClass('arrow-down');
289
+ });
290
+ // TOOLTIP
291
+ // Allows Screen readers to toggle a tooltip on click and to say if the tooltip is collapsed or expanded.
292
+ $(".tooltip").click(function () {
293
+ $(this).children(".tip-hover").toggle();
294
+ if ($(this).children(".tip-hover").is(':visible')) {
295
+ $(this).attr('aria-expanded', 'true');
296
+ $(this).removeClass('hidden');
297
+ } else {
298
+ $(this).attr('aria-expanded', 'false');
299
+ $(this).addClass('hidden');
300
+ }
301
+ });
302
+ let start = 999;
303
+ $('.tooltip').each(function (i) {
304
+ $(this).css('z-index', start--);
305
+ });
306
+ $(".tooltip .video-container").parent().css("width", "450px");
307
+ }
308
+ callJquery();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pimaonline/pimaonline-themepack",
3
- "version": "2.1.6",
3
+ "version": "2.1.8",
4
4
  "description": "A cloud based, course theme-pack for building courses efficiently within the D2L learning environment.",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -16,6 +16,7 @@
16
16
  ],
17
17
  "scripts": {
18
18
  "gulp": "gulp",
19
+ "webdocs": "gulp pug-docs && gulp pug-index",
19
20
  "copy-assets": "gulp copy-js && gulp copy-imgs && gulp copy-plugins",
20
21
  "sass-main": "gulp sass-main",
21
22
  "sass-main-themes": "gulp sass-main-themes",