diff --git a/amd/build/editor_modal.min.js b/amd/build/editor_modal.min.js index 80635bb..17f9848 100644 --- a/amd/build/editor_modal.min.js +++ b/amd/build/editor_modal.min.js @@ -19,6 +19,20 @@ define(["exports", "core/str", "core/log"], function(exports, str, log) { var requestCounter = 0; var openAttemptCount = 0; var openResponseTimer = null; + var hostWindow = null; + var hostDocument = null; + + function getHostWindow() { + var candidate = window; + try { + while (candidate.parent && candidate.parent !== candidate && candidate.parent.document) { + candidate = candidate.parent; + } + } catch (e) { + // Cross-origin parent — stay where we are. + } + return candidate; + } var MAX_OPEN_ATTEMPTS = 3; var OPEN_RESPONSE_TIMEOUT_MS = 3000; @@ -106,7 +120,8 @@ define(["exports", "core/str", "core/log"], function(exports, str, log) { } function createLoadingModal() { - var modal = document.createElement("div"); + var targetDocument = hostDocument || document; + var modal = targetDocument.createElement("div"); modal.className = "exeweb-loading-modal"; modal.id = "exeweb-loading-modal"; @@ -123,7 +138,7 @@ define(["exports", "core/str", "core/log"], function(exports, str, log) { '

' + texts.saving + '

' + '

' + texts.wait + '

' + ''; - document.body.appendChild(modal); + targetDocument.body.appendChild(modal); return modal; }); } @@ -435,6 +450,8 @@ define(["exports", "core/str", "core/log"], function(exports, str, log) { var doClose = function() { var wasShowingLoader = isSaving || (skipConfirm === true); + var activeWindow = hostWindow || window; + var activeDocument = hostDocument || document; overlay.remove(); overlay = null; @@ -449,9 +466,11 @@ define(["exports", "core/str", "core/log"], function(exports, str, log) { openAttemptCount = 0; clearOpenResponseTimer(); - document.body.style.overflow = ""; - window.removeEventListener("message", handleMessage); - document.removeEventListener("keydown", handleKeydown); + activeDocument.body.style.overflow = ""; + activeWindow.removeEventListener("message", handleMessage); + activeDocument.removeEventListener("keydown", handleKeydown); + hostWindow = null; + hostDocument = null; if (wasShowingLoader) { setTimeout(function() { @@ -545,11 +564,16 @@ define(["exports", "core/str", "core/log"], function(exports, str, log) { }); overlay.appendChild(iframe); - document.body.appendChild(overlay); - document.body.style.overflow = "hidden"; + // Attach the overlay to the topmost same-origin window so the modal covers + // the full viewport even when triggered from inside a small frame (e.g. the + // FRAME display mode top frame, or the embedded iframe). + hostWindow = getHostWindow(); + hostDocument = hostWindow.document; + hostDocument.body.appendChild(overlay); + hostDocument.body.style.overflow = "hidden"; - window.addEventListener("message", handleMessage); - document.addEventListener("keydown", handleKeydown); + hostWindow.addEventListener("message", handleMessage); + hostDocument.addEventListener("keydown", handleKeydown); } function init() { diff --git a/amd/src/editor_modal.js b/amd/src/editor_modal.js index 5d9fdc9..e33d3fc 100644 --- a/amd/src/editor_modal.js +++ b/amd/src/editor_modal.js @@ -19,6 +19,20 @@ let session = null; let requestCounter = 0; let openAttemptCount = 0; let openResponseTimer = null; +let hostWindow = null; +let hostDocument = null; + +const getHostWindow = () => { + let candidate = window; + try { + while (candidate.parent && candidate.parent !== candidate && candidate.parent.document) { + candidate = candidate.parent; + } + } catch { + // Cross-origin parent — stay where we are. + } + return candidate; +}; const MAX_OPEN_ATTEMPTS = 3; const OPEN_RESPONSE_TIMEOUT_MS = 3000; @@ -105,7 +119,8 @@ const setSaveLabel = async(key, fallback) => { }; const createLoadingModal = async() => { - const modal = document.createElement('div'); + const targetDocument = hostDocument || document; + const modal = targetDocument.createElement('div'); modal.className = 'exeweb-loading-modal'; modal.id = 'exeweb-loading-modal'; @@ -126,7 +141,7 @@ const createLoadingModal = async() => { `; - document.body.appendChild(modal); + targetDocument.body.appendChild(modal); return modal; }; @@ -431,6 +446,8 @@ export const close = async(skipConfirm) => { } const wasShowingLoader = isSaving || (skipConfirm === true); + const activeWindow = hostWindow || window; + const activeDocument = hostDocument || document; overlay.remove(); overlay = null; @@ -445,9 +462,11 @@ export const close = async(skipConfirm) => { openAttemptCount = 0; clearOpenResponseTimer(); - document.body.style.overflow = ''; - window.removeEventListener('message', handleMessage); - document.removeEventListener('keydown', handleKeydown); + activeDocument.body.style.overflow = ''; + activeWindow.removeEventListener('message', handleMessage); + activeDocument.removeEventListener('keydown', handleKeydown); + hostWindow = null; + hostDocument = null; if (wasShowingLoader) { setTimeout(() => { @@ -527,11 +546,16 @@ export const open = async(cmid, editorUrl, activityName, packageUrl, saveUrl, se }); overlay.appendChild(iframe); - document.body.appendChild(overlay); - document.body.style.overflow = 'hidden'; + // Attach the overlay to the topmost same-origin window so the modal covers + // the full viewport even when triggered from inside a small frame (e.g. the + // FRAME display mode top frame, or the embedded iframe). + hostWindow = getHostWindow(); + hostDocument = hostWindow.document; + hostDocument.body.appendChild(overlay); + hostDocument.body.style.overflow = 'hidden'; - window.addEventListener('message', handleMessage); - document.addEventListener('keydown', handleKeydown); + hostWindow.addEventListener('message', handleMessage); + hostDocument.addEventListener('keydown', handleKeydown); }; export const init = () => { diff --git a/locallib.php b/locallib.php index c0d44c6..633e803 100644 --- a/locallib.php +++ b/locallib.php @@ -87,6 +87,8 @@ function exeweb_display_frame($exeweb, $cm, $course, $file) { $PAGE->set_pagelayout('frametop'); $PAGE->activityheader->set_description(exeweb_get_intro($exeweb, $cm, true)); exeweb_print_header($exeweb, $cm, $course); + // Show action bar with Edit button in the top frame. + echo $PAGE->get_renderer('mod_exeweb')->generate_action_bar($cm); if (!exeweb_is_teacher_mode_visible($exeweb)) { exeweb_require_teacher_mode_hider_for_content_frame(); } @@ -218,6 +220,9 @@ function exeweb_print_workaround($exeweb, $cm, $course, $file) { exeweb_print_header($exeweb, $cm, $course); + // Show action bar with Edit button when user has edit capability. + echo $PAGE->get_renderer('mod_exeweb')->generate_action_bar($cm); + echo '
'; switch ($exeweb->display) { case RESOURCELIB_DISPLAY_POPUP: