feat: add scripts for dynamic content loading and embedding
Introduce global.js to streamline script loading and URL parameter parsing, enabling conditional embedding of Moodle elements. When the `embed` parameter is set to `1`, specific interface elements are hidden, and necessary scripts (jQuery and Bootstrap) are loaded to enhance form interactions and AJAX handling. Add script.js to handle dynamic content presentation within a modal. This includes script loading, modal management, and transition between content items, ensuring seamless navigation and adherence to required fields. Fosters a clean, embedded viewing experience and improves content presentation and interaction.
This commit is contained in:
commit
a7368163bf
2 changed files with 285 additions and 0 deletions
123
global.js
Normal file
123
global.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Function to load a script into the DOM
|
||||
function loadScript(url, integrity, crossorigin) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement("script");
|
||||
script.src = url;
|
||||
script.integrity = integrity;
|
||||
script.crossOrigin = crossorigin;
|
||||
script.onload = () => resolve(script);
|
||||
script.onerror = () => reject(new Error(`Failed to load script: ${url}`));
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to get URL parameters
|
||||
function getUrlParameter(name) {
|
||||
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
|
||||
var results = regex.exec(location.search);
|
||||
return results === null
|
||||
? ""
|
||||
: decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
}
|
||||
|
||||
// Check if the 'embed' parameter is set to '1'
|
||||
if (getUrlParameter("embed") === "1") {
|
||||
// Apply custom CSS to hide Moodle interface elements
|
||||
var style = document.createElement("style");
|
||||
style.innerHTML = `
|
||||
body.path-mod {
|
||||
#header, #footer, .breadcrumb, .navbar, .side-pre, .side-post, .drawer, .drawer-toggler, .footer-popover, .navigation {
|
||||
display: none !important;
|
||||
}
|
||||
#region-main {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
loadScript(
|
||||
"https://code.jquery.com/jquery-3.7.1.min.js",
|
||||
"sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=",
|
||||
"anonymous"
|
||||
)
|
||||
.then(() => {
|
||||
console.log("jQuery loaded successfully");
|
||||
// Load Bootstrap after jQuery is loaded
|
||||
return loadScript(
|
||||
"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js",
|
||||
"sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz",
|
||||
"anonymous"
|
||||
);
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Bootstrap loaded successfully");
|
||||
$(document).ready(function () {
|
||||
// Set required attribute on inputs associated with labels containing <i title="Required field">
|
||||
$('label:has(i[title="Required field"])').each(function () {
|
||||
var inputId = $(this).attr("for");
|
||||
$("#" + inputId).attr("required", "required");
|
||||
});
|
||||
|
||||
// Hide the modal if the 'cancel' button is clicked
|
||||
$("[name=cancel]").click(function () {
|
||||
hideContent();
|
||||
});
|
||||
|
||||
$("form").each(function () {
|
||||
var form = $(this);
|
||||
|
||||
form.submit(function (event) {
|
||||
event.preventDefault(); // Prevent the default form submission
|
||||
|
||||
// Check if the form is valid
|
||||
if (!form[0].checkValidity()) {
|
||||
form[0].reportValidity();
|
||||
return;
|
||||
}
|
||||
|
||||
// Serialize the form data
|
||||
var formData = form.serialize();
|
||||
|
||||
// Add the submit button's data to formData
|
||||
var submitButton = form.find(
|
||||
'input[type="submit"][name="savevalues"]'
|
||||
);
|
||||
if (submitButton.length) {
|
||||
formData +=
|
||||
"&" +
|
||||
encodeURIComponent(submitButton.attr("name")) +
|
||||
"=" +
|
||||
encodeURIComponent(submitButton.val());
|
||||
}
|
||||
|
||||
console.log(formData);
|
||||
|
||||
// Perform the AJAX request
|
||||
$.ajax({
|
||||
url: form.attr("action"), // Use the form's action attribute
|
||||
type: form.attr("method"), // Use the form's method attribute
|
||||
data: formData,
|
||||
success: function (response) {
|
||||
// Call the nextContent function upon successful submission
|
||||
window.top.nextContent();
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
// Handle any errors that occur during the AJAX request
|
||||
console.error(
|
||||
"Form submission failed: " + status + " - " + error
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
});
|
162
script.js
Normal file
162
script.js
Normal file
|
@ -0,0 +1,162 @@
|
|||
function loadScript(url, integrity, crossorigin) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement("script");
|
||||
script.src = url;
|
||||
script.integrity = integrity;
|
||||
script.crossOrigin = crossorigin;
|
||||
script.onload = () => resolve(script);
|
||||
script.onerror = () => reject(new Error(`Failed to load script: ${url}`));
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
function getFeedbackLinks() {
|
||||
// Select all <a> elements with the class "stretched-link"
|
||||
const links = document.querySelectorAll("a.stretched-link");
|
||||
|
||||
// Initialize an array to store the href attributes
|
||||
const feedbackLinks = [];
|
||||
|
||||
// Iterate through the selected links
|
||||
links.forEach((link) => {
|
||||
const href = link.getAttribute("href");
|
||||
// Check if the href includes "/feedback/"
|
||||
if (href && href.includes("/feedback/")) {
|
||||
feedbackLinks.push(href);
|
||||
}
|
||||
});
|
||||
|
||||
// Return the 1-indexed list of href attributes
|
||||
return feedbackLinks.map((href, index) => ({ index: index + 1, href }));
|
||||
}
|
||||
|
||||
var currentScript = document.currentScript;
|
||||
|
||||
var myModal = document.getElementById("fullScreenModal");
|
||||
|
||||
myModal.addEventListener("shown.bs.modal", function () {
|
||||
var modalContent = myModal.querySelector(".modal-content");
|
||||
var modalWidth = modalContent.offsetWidth;
|
||||
var modalHeight = modalContent.offsetHeight;
|
||||
|
||||
const modalIframe = window.$("#modal-iframe");
|
||||
|
||||
modalIframe.css("width", "100%");
|
||||
modalIframe.css("height", "100%");
|
||||
modalIframe.css("border", "none");
|
||||
});
|
||||
|
||||
loadScript(
|
||||
"https://code.jquery.com/jquery-3.7.1.min.js",
|
||||
"sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=",
|
||||
"anonymous"
|
||||
)
|
||||
.then(() => {
|
||||
console.log("jQuery loaded successfully");
|
||||
window.$ = jQuery;
|
||||
return loadScript(
|
||||
"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js",
|
||||
"sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz",
|
||||
"anonymous"
|
||||
);
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Bootstrap loaded successfully");
|
||||
var scriptId = "script-" + Math.random().toString(36).substring(2, 9);
|
||||
currentScript.id = scriptId;
|
||||
|
||||
contentUrl = $(currentScript).attr("data-content-url");
|
||||
|
||||
function openInModal(url, block) {
|
||||
$("#modal-iframe").attr("src", url);
|
||||
if (block) {
|
||||
$("#fullScreenModal").attr("data-block", block);
|
||||
} else {
|
||||
$("#fullScreenModal").removeAttr("data-block");
|
||||
}
|
||||
}
|
||||
|
||||
function showContent(url, block) {
|
||||
bootstrap.Modal.getOrCreateInstance(
|
||||
document.getElementById("fullScreenModal")
|
||||
).show();
|
||||
if (url) {
|
||||
openInModal(url, block);
|
||||
}
|
||||
}
|
||||
|
||||
function hideContent() {
|
||||
bootstrap.Modal.getOrCreateInstance(
|
||||
document.getElementById("fullScreenModal")
|
||||
).hide();
|
||||
openInModal("about:blank");
|
||||
}
|
||||
|
||||
function nextContent() {
|
||||
// Get the currently executing <script> element
|
||||
var modal = $("#fullScreenModal");
|
||||
var currentLi = $("#" + modal.attr("data-block"));
|
||||
|
||||
// Find the next <li> sibling with the "section" class
|
||||
var nextLi = currentLi.next("li.section");
|
||||
|
||||
// Find the <a> element with the "stretched-link" class within the next <li> element
|
||||
var aLink = nextLi.find("a.stretched-link");
|
||||
|
||||
if (aLink.length > 0) {
|
||||
// Get the "href" attribute of the <a> element
|
||||
var hrefValue = aLink.attr("href");
|
||||
|
||||
// Check if "feedback" is in the "href" attribute
|
||||
if (hrefValue.includes("feedback")) {
|
||||
// Get the id URL parameter for debugging purposes
|
||||
var url = new URL(hrefValue);
|
||||
var id = url.searchParams.get("id");
|
||||
|
||||
// Replace "view" with "complete" in the "href" attribute
|
||||
hrefValue = hrefValue.replace("view", "complete");
|
||||
|
||||
// Go to embed mode
|
||||
hrefValue = hrefValue + "&embed=1";
|
||||
}
|
||||
|
||||
return openInModal(hrefValue, nextLi.attr("id"));
|
||||
} else {
|
||||
// Check if there is a button with the "btn-primary" class
|
||||
var button = nextLi.find("button.btn-primary");
|
||||
|
||||
if (button.length > 0) {
|
||||
return button.click();
|
||||
}
|
||||
}
|
||||
|
||||
hideContent();
|
||||
}
|
||||
|
||||
nextActivity = nextContent;
|
||||
window.top.nextContent = nextContent;
|
||||
|
||||
$(currentScript)
|
||||
.parent()
|
||||
.append(
|
||||
`<button id="contentButton-` +
|
||||
scriptId +
|
||||
`"class="btn btn-primary" type="button"
|
||||
> 360° Content </button>`
|
||||
);
|
||||
|
||||
var button = $("#contentButton-" + scriptId);
|
||||
button.click(function () {
|
||||
console.log("Button clicked!");
|
||||
showContent(
|
||||
contentUrl,
|
||||
$(currentScript).closest("li.section").attr("id")
|
||||
);
|
||||
});
|
||||
$("head").append(
|
||||
' <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">'
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
Loading…
Reference in a new issue