@pimaonline/pimaonline-themepack 3.10.12 → 3.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. package/dist/css/main.css +1 -1
  2. package/dist/css/themes/ait/styles.css +1 -1
  3. package/dist/css/themes/ajs/styles.css +1 -1
  4. package/dist/css/themes/aviation/styles.css +1 -1
  5. package/dist/css/themes/bct/styles.css +1 -1
  6. package/dist/css/themes/bio/styles.css +1 -1
  7. package/dist/css/themes/business/styles.css +1 -1
  8. package/dist/css/themes/cad/styles.css +1 -1
  9. package/dist/css/themes/cards/styles.css +1 -1
  10. package/dist/css/themes/cda/styles.css +1 -1
  11. package/dist/css/themes/cis/styles.css +1 -1
  12. package/dist/css/themes/computer-information-systems/styles.css +1 -1
  13. package/dist/css/themes/culinary/styles.css +1 -1
  14. package/dist/css/themes/culinary/versions/black-marble.css +1 -1
  15. package/dist/css/themes/culinary/versions/stainless.css +1 -1
  16. package/dist/css/themes/culinary/versions/wood.css +1 -1
  17. package/dist/css/themes/dental/styles.css +1 -1
  18. package/dist/css/themes/ece/styles.css +1 -1
  19. package/dist/css/themes/ecn/styles.css +1 -1
  20. package/dist/css/themes/eng/styles.css +1 -1
  21. package/dist/css/themes/fashion/styles.css +1 -1
  22. package/dist/css/themes/fitness/styles.css +1 -1
  23. package/dist/css/themes/fsc/styles.css +1 -1
  24. package/dist/css/themes/geography/styles.css +1 -1
  25. package/dist/css/themes/geology/styles.css +1 -1
  26. package/dist/css/themes/health-it/styles.css +1 -1
  27. package/dist/css/themes/history/styles.css +1 -1
  28. package/dist/css/themes/hrm/styles.css +1 -1
  29. package/dist/css/themes/hrs/styles.css +1 -1
  30. package/dist/css/themes/journalism/styles.css +1 -1
  31. package/dist/css/themes/lang/styles.css +1 -1
  32. package/dist/css/themes/lgm/styles.css +1 -1
  33. package/dist/css/themes/machine/styles.css +1 -1
  34. package/dist/css/themes/math/styles.css +1 -1
  35. package/dist/css/themes/mgt/styles.css +1 -1
  36. package/dist/css/themes/music/styles.css +1 -1
  37. package/dist/css/themes/philosophy/styles.css +1 -1
  38. package/dist/css/themes/pht/styles.css +1 -1
  39. package/dist/css/themes/psy/styles.css +1 -1
  40. package/dist/css/themes/soc/styles.css +1 -1
  41. package/dist/css/themes/ss/styles.css +1 -1
  42. package/dist/css/themes/university/styles.css +1 -1
  43. package/dist/css/themes/vet/styles.css +1 -1
  44. package/dist/css/themes/welding/styles.css +1 -1
  45. package/dist/js/scripts-ts.js +1 -0
  46. package/dist/js/scripts2.js +674 -128
  47. package/dist/plugins/fancybox/helpers/jquery.fancybox-buttons.js.LICENSE.txt +15 -0
  48. package/dist/plugins/fancybox/helpers/jquery.fancybox-media.js.LICENSE.txt +64 -0
  49. package/dist/plugins/fancybox/helpers/jquery.fancybox-thumbs.js.LICENSE.txt +16 -0
  50. package/dist/plugins/fancybox/jquery.fancybox.js.LICENSE.txt +11 -0
  51. package/dist/plugins/fancybox/jquery.fancybox.pack.js.LICENSE.txt +1 -0
  52. package/dist/plugins/flashcards/js/plugins/jquery.cycle.js.LICENSE.txt +20 -0
  53. package/dist/plugins/flashcards/js/vendor/jquery-1.7.2.js.LICENSE.txt +22 -0
  54. package/dist/plugins/flashcards/js/vendor/jquery-1.7.2.min.js.LICENSE.txt +1 -0
  55. package/package.json +18 -3
@@ -1,6 +1,7 @@
1
1
  const columnWidget = document.querySelector("#column-widget");
2
+ const contentLockWidgets = document.querySelectorAll(".content-lock-widget");
2
3
  const contentLockInstructions = document.querySelectorAll(".instructions");
3
- const contentUnlockBtns = document.querySelectorAll(".unlock-btn");
4
+ const contentLockQuizzes = document.querySelectorAll(".quiz");
4
5
  const contentWrapper = document.querySelector("#content-wrapper");
5
6
  const courseBody = document.querySelector("body");
6
7
  const docHead = document.querySelector("head");
@@ -36,8 +37,7 @@ const addAria = () => {
36
37
  if (contentWrapper) {
37
38
  contentWrapper.setAttribute("role", "main");
38
39
  } else if (!contentWrapper) {
39
- console.error("Document error: does not contain #content-wrapper.");
40
- return;
40
+ console.log("Document error: does not contain #content-wrapper.");
41
41
  }
42
42
  if (secondColumn) {
43
43
  secondColumn.setAttribute("role", "region");
@@ -61,11 +61,9 @@ const addGrid = () => {
61
61
  } else if (contentWrapper && !secondColumn && !thirdColumn && !columnWidget && !videoWrapper) {
62
62
  courseBody.id = "one-column";
63
63
  } else if (contentWrapper && !secondColumn && (thirdColumn || columnWidget)) {
64
- console.error("Document error: <body> is missing id because #second-column doesn't exist.");
65
- return;
64
+ console.log("Document error: <body> is missing id because #second-column doesn't exist.");
66
65
  } else {
67
- console.error("Document error: unable to determine the page layout for setting <body> id.");
68
- return;
66
+ console.log("Document error: unable to determine the page layout for setting <body> id.");
69
67
  }
70
68
 
71
69
  const topLevelElements = document.body.children;
@@ -92,8 +90,7 @@ const addGrid = () => {
92
90
  }
93
91
 
94
92
  if (foundNestedElement) {
95
- console.error("Document error: Additional content outside #content-wrapper, #second-column, #third-column, or footer.");
96
- return;
93
+ console.log("Document error: Additional content outside #content-wrapper, #second-column, #third-column, or footer.");
97
94
  }
98
95
  };
99
96
  addGrid();
@@ -108,11 +105,10 @@ const addMediaContainersAria = () => {
108
105
 
109
106
  // Check if media container items are present
110
107
  if (!iframe) {
111
- console.error("Document error: no iframe found for media container");
112
- return;
108
+ console.log("Document error: no iframe found for media container");
113
109
  }
114
110
  if (!mediaObject) {
115
- console.error("Document error: no media object found for media container");
111
+ console.log("Document error: no media object found for media container");
116
112
  }
117
113
 
118
114
  // If element DOES NOT have "aria-describedby" && it DOES have a sibling element.
@@ -133,8 +129,7 @@ iconClasses.forEach(icon => {
133
129
  const metaTagRef = docHead.querySelector("meta[name='viewport']");
134
130
  //Check if viewport meta tag exists
135
131
  if (!metaTagRef) {
136
- console.error("Document error: could not find viewport meta tag");
137
- return;
132
+ console.log("Document error: could not find viewport meta tag");
138
133
  }
139
134
 
140
135
  const iconCDN = document.createElement("link");
@@ -149,8 +144,7 @@ iconClasses.forEach(icon => {
149
144
  const checkGalleryWrapperParent = () => {
150
145
  galleryWrappers.forEach((galleryWrapper) => {
151
146
  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
+ console.log(`Document error: parent of .gallery-wrapper does not have the .image-gallery class.`);
154
148
  }
155
149
  });
156
150
  };
@@ -160,8 +154,7 @@ checkGalleryWrapperParent();
160
154
  const checkImageBoxParent = () => {
161
155
  imgBoxes.forEach((imgBox) => {
162
156
  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;
157
+ console.log(`Document error: parent of .image-box does not have the .gallery-wrapper class.`);
165
158
  }
166
159
  });
167
160
  };
@@ -173,8 +166,7 @@ const checkGalleryWrapperChildren = () => {
173
166
  let directChildren = Array.from(galleryWrapper.children).every(child => child.classList.contains("image-box"));
174
167
 
175
168
  if (!directChildren) {
176
- console.error(`Document error: not all direct children of .gallery-wrapper have the .image-box class.`);
177
- return;
169
+ console.log(`Document error: not all direct children of .gallery-wrapper have the .image-box class.`);
178
170
  }
179
171
  });
180
172
  };
@@ -276,19 +268,16 @@ const callTabsWidget = () => {
276
268
 
277
269
  //Check that there are more than just one tab
278
270
  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;
271
+ console.log("Document error: please add more than just one tab for tabs widget");
281
272
  }
282
273
 
283
274
  // Check amount of tab elements present
284
275
  if (tabInputs.length < tabLabels.length || tabInputs.length < tabDivs.length) {
285
- console.error("Document error: missing tab input(s) in tab widget");
286
- return;
276
+ console.log("Document error: missing tab input(s) in tab widget");
287
277
  }
288
278
 
289
279
  if (tabLabels.length < tabInputs.length || tabLabels.length < tabDivs.length) {
290
- console.error("Document error: missing tab label(s) in tab widget");
291
- return;
280
+ console.log("Document error: missing tab label(s) in tab widget");
292
281
  }
293
282
 
294
283
  let groupNum = index + 1;
@@ -297,24 +286,24 @@ const callTabsWidget = () => {
297
286
  tab.setAttribute("role", "region");
298
287
  tab.setAttribute("aria-label", `tab group ${groupNum}`)
299
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.
300
290
  for (tabIndex = 0; tabIndex < tabInputs.length; tabIndex++) {
301
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
+
302
297
  let tabNum = tabsWidgetsNum + 1;
303
298
 
304
299
  // 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;
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");
313
303
  }
314
304
 
315
- if (tabInputs == null) {
316
- console.error("Document error: no divs (tab panels) found for tabs widget");
317
- return;
305
+ if (tabLabels === null) {
306
+ console.log("Document error: no labels found for tabs widget");
318
307
  }
319
308
 
320
309
  //Add class, id, name, and aria-described by for inputs
@@ -336,13 +325,39 @@ const callTabsWidget = () => {
336
325
  }
337
326
 
338
327
  //Add attributes for hide tab
339
- if (tabIndex + 1 == tabInputs.length) {
340
- tabLabels[tabIndex].classList.add("hide-tab");
341
- tabInputs[tabIndex].checked = true;
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;
342
336
  if (tabDivs[tabIndex]) {
343
337
  tabDivs[tabIndex].classList.add("hide-panel");
344
338
  }
345
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
+
346
361
  tabsWidgetsNum++;
347
362
  }
348
363
  })
@@ -396,17 +411,15 @@ const callVocabList = () => {
396
411
 
397
412
  // Check for terms and definitions in the vocab list
398
413
  if (terms < 1) {
399
- console.error("Document error: no terms found in vocab list");
400
- return;
414
+ console.log("Document error: no terms found in vocab list");
401
415
  }
402
416
 
403
417
  if (definitions < 1) {
404
- console.error("Document error: no definitions found in vocab list");
405
- return;
418
+ console.log("Document error: no definitions found in vocab list");
406
419
  }
407
420
 
408
421
  if (terms > definitions) {
409
- console.error("Document error: more terms than definitions in vocab list")
422
+ console.log("Document error: more terms than definitions in vocab list")
410
423
  }
411
424
 
412
425
  // If there are more than 2 terms and 2 definitions, then check for a button
@@ -558,91 +571,361 @@ const cleanMarkup = () => {
558
571
  });
559
572
  };
560
573
  cleanMarkup();
561
- // Content Lock Widget
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) => {
562
594
 
563
- // Save text content of unlock button
564
- const unlockContent = [];
595
+ // Toggle the key status to true to unlock the content
596
+ contentLockData[courseNumber].keys[contentLockKeyNum] = true;
565
597
 
566
- contentUnlockBtns.forEach((button) => {
567
- unlockContent.push(button.textContent)
568
- })
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 = "";
569
623
 
570
- // Create an object that keeps track of keys and their statuses
571
- let contentLockData = JSON.parse(localStorage.getItem("contentLockData")) || {};
624
+ // Clear error message
625
+ errorMessage.textContent = "";
572
626
 
573
- if (lockedContent) {
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 ––––––––––––––––
574
745
 
575
746
  // Checks the URL for the course number
576
747
  const currentURL = window.parent.location.href;
577
748
  const match = currentURL.match(/\/content\/(\d+)/);
578
749
  const courseNumber = match ? match[1] : null;
579
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
+
580
762
  // Add event listener for storage changes
581
763
  window.addEventListener("storage", (event) => {
582
764
  if (courseNumber) {
583
- handleLocalStorageUpdate(event, courseNumber);
765
+ handleContentLockLocalStorageUpdate(event, courseNumber);
584
766
  }
585
767
  });
586
768
 
587
- if (courseNumber) {
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");
588
870
 
589
- // Check if the course data object has the course number
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
+ */
590
874
  if (!contentLockData.hasOwnProperty(courseNumber)) {
591
875
  contentLockData[courseNumber] = {
592
876
  keys: {},
593
- id: 0,
594
877
  };
595
- } else {
596
- // Reset the id count to 0 when the page is loaded
597
- contentLockData[courseNumber].id = 0;
598
878
  }
599
879
 
600
880
  // Get key number for each content area
601
- lockedContent.forEach((contentArea, index) => {
602
- let keyNum = contentArea.getAttribute("data-key");
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
+ }
603
894
 
604
- // Check if key already exists within the course, if it doesn't, add the key
605
- if (!contentLockData[courseNumber].keys[keyNum]) {
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
+ }
606
905
 
607
- // Add the key to the course keys
608
- contentLockData[courseNumber].keys[keyNum] = false;
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;
609
910
  }
610
911
  });
611
912
 
612
913
  // 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");
914
+ contentUnlockBtns.forEach((unlockButton, buttonIndex) => {
617
915
 
618
- // Add alert to ensure the user confirms the action to unlock the content
619
- const confirmed = window.confirm(`Please confirm: ${unlockContent[index]}`)
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);
620
918
 
621
- if (confirmed) {
919
+ // Add tab index to each unlock button
920
+ unlockButton.setAttribute("tabIndex", "0");
622
921
 
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
- }
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)
642
925
  });
643
926
 
644
927
  // Apply initial classes based on key status
645
- const keyNum = lockedContent[index].getAttribute("data-key");
928
+ let contentLockKeyNum = lockedContent[buttonIndex].getAttribute("data-key");
646
929
 
647
930
  // Make sure contentLockData[courseNumber] is initialized
648
931
  if (!contentLockData[courseNumber]) {
@@ -652,9 +935,9 @@ if (lockedContent) {
652
935
  };
653
936
  }
654
937
 
655
- if (contentLockData[courseNumber].keys[keyNum]) {
656
- lockedContent[index].classList.add("open");
657
- contentLockInstructions[index].classList.add("complete");
938
+ if (contentLockData[courseNumber].keys[contentLockKeyNum]) {
939
+ lockedContent[buttonIndex].classList.add("open");
940
+ contentLockInstructions[buttonIndex].classList.add("complete");
658
941
  }
659
942
  });
660
943
 
@@ -663,36 +946,17 @@ if (lockedContent) {
663
946
 
664
947
  // Save the updated contentLockData object to local storage
665
948
  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
949
 
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
- }
950
+ }).catch(error => {
951
+ // Handle the error if needed
952
+ console.log("Error occurred during applyAttributesToQuiz:", error);
694
953
  });
695
954
  }
955
+
956
+ document.addEventListener('DOMContentLoaded', () => {
957
+ // Your script code here
958
+ if (contentLockWidgets) { handleContentLockWidget(contentLockWidgets) }
959
+ });
696
960
  // Flip Card Widget
697
961
  function callFlipCardWidget() {
698
962
  // Loop through each card
@@ -704,13 +968,11 @@ function callFlipCardWidget() {
704
968
 
705
969
  // Check to ensure each card has the .flip-card class
706
970
  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
971
+ console.log("Document error: missing .flip-card class for flip card widget");
709
972
  }
710
973
  // Check to ensure each card has the .inner-card class
711
974
  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
975
+ console.log("Document error: missing .inner-card class for flip card widget");
714
976
  }
715
977
 
716
978
  flipCard.forEach((card) => {
@@ -950,6 +1212,290 @@ callJquery();
950
1212
  const jumpTo = (anchor) => {
951
1213
  document.getElementById(anchor).scrollIntoView();
952
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();
953
1499
  // Array for all themes that require theme specific js
954
1500
  const customJsThemes = ["ecn", "hrs", "ss"];
955
1501