-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The Zooming Image Tool is broken #31436
Comments
Hi @arbrandes. Have you had the chance to keep working on this issue? how could we help to unblock it? |
I have not, unfortunately. I'll see about taking this to the DEPR working group. |
This is marked for Palm.1, released a couple of months ago. Is it realistic to change it to the Palm.3 milestone or Quince? |
@arbrandes Could you announce this deprecation so that we can move it to 'Communicated'? |
The Zooming Image Tool template is broken because it uses a deprecated feature of HTML called "HTML blocks". What are HTML blocks, why were they deprecated, and how can we fix the template to avoid using them? HTML blocks were introduced in HTML5 as a way to allow authors to create self-contained blocks of content that could be easily styled and laid out on a web page. However, they were never widely adopted and have since been deprecated in favor of other layout methods such as CSS Flexbox and Grid. One reason why HTML blocks were deprecated is that they can cause compatibility issues with older browsers and assistive technologies. For example, some screen readers may not be able to properly read the contents of an HTML block, making it difficult or impossible for users with disabilities to access the content. To fix the Zooming Image Tool template and avoid using HTML blocks, we can replace them with CSS Flexbox or Grid. For example, we can wrap the image and its associated text in a container element and use CSS Flexbox to align the elements horizontally and vertically. Here's an example of how the updated code might look: |
@cheskiduty, if you're up to creating a PR to demonstrate the solution (say, in the next couple of weeks), that would be great! Otherwise, I'm afraid we'll go ahead with the deprecation. @dianakhuang, @mariajgrimaldi, I think we should target Quince, at this point. |
Hi @arbrandes I have been following up on this issue and am in charge of it from Edunext. The first thing I have done is test the proposed workaround. const zoomImgLoupe = ({
mainClassName = 'zoom_image',
imageZoomFrameClassName = 'image_zoom_frame',
loupeWidth = 200,
loupeHeight = 200,
} = {}) => {
const updateFuncs = [];
let mouseE;
function addUpdateFunc(func) {
updateFuncs.push(func);
}
function removeUpdateFunc(func) {
updateFuncs.splice(updateFuncs.indexOf(func), 1);
}
function callUpdateFuncs() {
for (const func of updateFuncs) {
func();
}
}
function updateMouseE(e) {
mouseE = e;
}
function update() {
callUpdateFuncs();
reqUpdate();
}
function reqUpdate() {
window.requestAnimationFrame(update);
}
function getTotalPos(oEl) {
const oRect = document.body.getBoundingClientRect();
const tRect = oEl.getBoundingClientRect();
return [tRect.left - oRect.left, tRect.top - oRect.top];
}
function findClass(ele, targetClass) {
if (typeof ele.className === 'string') {
return ele.className.includes(targetClass);
}
}
const zoomImgs = [];
function getZoomData(img) {
let res;
for (let i = 0; i < zoomImgs.length; i++) {
const data = zoomImgs[i];
if (data.img === img) {
res = data;
break;
}
}
return res;
}
function addZoomImg(img) {
if (getZoomData(img) !== undefined) {
console.error('!!! TRIED TO ADD ALREADY EXISTING IMG !!!');
return;
}
const data = {
img: img,
}
function upd() {
const zoomScale = Number(data.img.dataset.zoom_scale);
const pointerOnWindowX = mouseE.clientX + window.scrollX;
const pointerOnWindowY = mouseE.clientY + window.scrollY;
const [imageOnWindowX, imageOnWindowY] = getTotalPos(data.img);
const loupeOnPointerY = window.innerHeight - mouseE.clientY < loupeHeight ? -loupeHeight-10 : 0;
data.zoomImg.style.width = (data.img.offsetWidth * zoomScale).toString() + 'px';
data.zoomImg.style.height = (data.img.offsetHeight * zoomScale).toString() + 'px';
data.hoverDiv.style.transform = `translate(-50%, ${loupeOnPointerY}px) translate(${pointerOnWindowX}px, ${pointerOnWindowY}px)`;
data.zoomImg.style.transform = `translate(${(data.hoverDiv.offsetWidth * 0.5).toString()}px, ${(data.hoverDiv.offsetHeight * 0.5).toString()}px) translate(${(imageOnWindowX - pointerOnWindowX) * zoomScale}px, ${(imageOnWindowY - pointerOnWindowY) * zoomScale}px)`;
}
img.style.cursor = 'crosshair';
img.onmouseenter = () => {
const hoverDiv = document.createElement('div');
hoverDiv.className = imageZoomFrameClassName;
hoverDiv.style.cssText = `all: initial; width: ${loupeWidth}px; height: ${loupeHeight}px; position: absolute; border-radius: 50%; box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.5); pointer-events: none; overflow: hidden; z-index: 999; background-color: white;`;
document.body.insertBefore(hoverDiv, document.body.firstChild);
data.hoverDiv = hoverDiv;
const zoomImg = document.createElement('img');
zoomImg.className = 'mag_image';
zoomImg.src = img.dataset.zoom_image || img.src;
zoomImg.style.cssText = 'all: initial; pointer-events: none;';
zoomImg.style.pointerEvents = 'none';
hoverDiv.appendChild(zoomImg);
data.zoomImg = zoomImg;
addUpdateFunc(upd);
}
img.onmouseleave = () => {
removeUpdateFunc(upd);
data.hoverDiv.remove();
}
}
function childAdded(child) {
if (findClass(child, mainClassName)) {
addZoomImg(child)
}
}
const observer = new MutationObserver((mutationList) => {
for (const mutation of mutationList) {
if (mutation.type === 'childList') {
for (const node of mutation.addedNodes) {
if (node.nodeType === 1) {
childAdded(node);
}
}
}
}
});
function newChild(cChild) {
childAdded(cChild);
searchChildren(cChild);
}
function searchChildren(oChild) {
const children = oChild.children;
for (let i = 0; i < children.length; i++) {
const cChild = children[i];
newChild(cChild);
}
}
const observerEle = document.body;
newChild(observerEle);
observer.observe(observerEle, {childList: true, subtree: true});
document.onmousemove = updateMouseE;
reqUpdate();
} and use the following HTML <img
class="zoom_image"
src="https://studio.edx.org/c4x/edX/DemoX/asset/pathways_detail_01.png"
data-zoom_scale="2.5"
/> The result of implementing this in the LMS was the following: 2024-10-15.22-48-55.mp4Note However, when trying to implement this, I ran into the same problem Uncaught ReferenceError: JavascriptLoader is not defined Therefore, I do not think this workaround is a suitable solution. In the process I was working on understanding the root problem, and it really is something simple, we are trying to call a function which has not yet been loaded in the DOM, therefore, I had to find a way to replace the form in which the <div class="script_placeholder" data-src="https://studio.edx.org/c4x/edX/DemoX/asset/jquery.loupeAndLightbox.js"></div> Coming to the following alternative: <script type="text/javascript">// <![CDATA[
function loadScript(url, callback) {
let script = document.createElement("script");
script.type = "text/javascript";
if (script.readyState) { // IE
script.onreadystatechange = function() {
if (script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
callback();
}
};
} else { // Other browsers
script.onload = function() {
callback();
};
}
script.src = url;
document.head.appendChild(script);
}
function callback() {
window.onload = zoomImgLoupe()
}
loadScript("/assets/zoomImgLoupe.js",callback);
// ]]></script> This code first load the script through the URL sent in With this alternative, I have made the original utility work in LMS 2024-10-15.23-05-57.mp4I think this would be the correct workaround, because we don't have a real problem in the |
@jignaciopm, thanks a lot for the investigation and solution! It certainly looks good to me! Would you be able to submit a PR to fix the issue? |
Yes @arbrandes of course. It is here. I await your review. |
As noted in the PR, while it fixes the loading of the script, the script itself - which is hosted by a course in edx.org - doesn't work correctly. Unless we want to include that script in the HTML block itself, I still think we should deprecate this template. |
FYI, there's a discussion post with a workaround to the javascript failures: https://discuss.openedx.org/t/image-zoom-tool-workaround/9917 That said, I agree this template should be deprecated, unless and until it's moved to an xBlock with appropriate maintenance and support. |
Alright, here's something that seems to work! |
Aaand the fixes have been merged, both to master and Sumac. |
Description
During release testing for Olive, it was discovered by the Build Test Release group that the Zooming Image Tool was broken.
Since then, attempts have been made to work around the problem. In the latest case, a PR fixes the loading of the tool, but it turns out that the script - which is hosted in a course at edx.org - is itself broken, as one can see in this comment.
It was originally proposed that this functionality be removed, to be later reimplemented as an XBlock. Doing so would make it possible to maintain and use the code properly, instead of relying on a remote third-party.
However, a whole XBlock just for a few lines of Javascript and a couple of images is likely too much, so an alternate plan was devised where all of the relevant pieces are inlined in the template itself. See this PR for the implementation.
PRs
The text was updated successfully, but these errors were encountered: