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: