fix: guard TOC navigation against re-entry (#63)#69
Conversation
When the user clicks a TOC entry, `exescorm_activate_item()` calls
`exescorm_tree_node.closeAll()` and later `openAll()` to collapse and
re-expand the entire YUI TreeView. Those operations mutate node
selection state and YUI fires additional `select` events while the
tree settles, which the `tree.after('select', ...)` handler then
interprets as a fresh navigation request and calls
`exescorm_activate_item()` again with a *different* node (typically a
sibling). The visible symptoms reported in #63 — the iframe loading
twice and "click X, see Y" — line up with that pattern.
Add an `exescorm_navigating` flag set right before the first state
change and cleared after `openAll()`. The select handler returns early
when the flag is set, so only the original user click drives the
navigation.
The fix is defensive: I have not been able to reproduce locally on
Moodle 5.0, but every signal in the report (intermittent, multiple
iframe loads, sibling shown instead of clicked item) points at
re-entry through this code path. If the symptom persists after
upgrading we'll need to capture a console trace inside the failing
session.
ignaciogros
left a comment
There was a problem hiding this comment.
The navigation menu works correctly, including keyboard support. Thank you!
But there are still some issues with the pagination buttons:
“Previous”, “Next”, and “Level up” work as expected, but “Previous within this level” and “Next within this level” do not.
Example:
Publish the sample SCORM and go to section “b”. The expected behavior when clicking “Next within this level” would be to navigate to section “c”, but this does not happen (in my tests, nothing happens). Additionally, when being on “b”, “Previous within this level” is disabled. This may be because index.html is not at the same level. In other cases, those buttons behave like “Previous” and “Next”, without respecting the hierarchical level.
If this becomes too problematic, I suggest temporarily hiding these buttons and releasing the next version with only “Previous” and “Next”.
There is also a JavaScript error when loading the page, but it does not appear to affect functionality:
[moodle-exe-bridge] Missing __MOODLE_EXE_CONFIG__
ignaciogros
left a comment
There was a problem hiding this comment.
Hi @erseco,
Could you please take a look at it? Navigation may be problematic. I think that if it is not possible to resolve this soon, it might be a good idea to hide the #nav_skipprev and #nav_skipnext buttons.
What do you think?
Thank you.
exescorm_update_siblings grouped entries by treating each scoesnav key as a candidate parentscoid. Top-level sections have no parentscoid at all (they are the roots of the displayed organization; the organization node has no launchable URL and is absent from scoesnav), so they never matched and never received prevsibling/nextsibling. As a result exescorm_skipprev/exescorm_skipnext returned null for them and the "Previous within this level" / "Next within this level" buttons did nothing at the top level (they still worked at deeper levels, where parentscoid is set). Group every entry by its parentscoid value instead, bucketing the parentless top-level roots together under a sentinel key, then link each group as siblings. Deeper levels are unaffected (same groups as before) and exescorm_get_siblings still only fills in undefined prevsibling/nextsibling, preserving server-computed links. Verified on Moodle 4.5 with the example SCORM from #63: on section "b", "Next within this level" now navigates to "c" and "Previous within this level" to "a"; deeper-level navigation and the TOC re-entry guard are unchanged.
|
@ignaciogros thanks for the thorough testing — you were right, and I've now fixed the "within this level" buttons ( Root causeThis was a pre-existing bug, separate from the re-entry guard. The catch: top-level sections (
Deeper levels (e.g. Fix
Verified on Moodle 4.5 (Docker, your sample SCORM)
The Could you re-test when you have a moment? Re-requesting your review. Thanks! |
ignaciogros
left a comment
There was a problem hiding this comment.
Thanks, @erseco.
It works much better now, but I think there are still some issues.
Please download the example SCORM ZIP and go to page "b1" (or another page that is the first one of its level). You'll see that the "Previous within this level" button is enabled, when there's no previous page in that level.
It happens the same with some of the last pages of a level: go to "a 2 2", and you'll see that the "Next within this level" button es enabled, but if you go to "b 2", it's not.
The "Previous/Next within this level" buttons (#nav_skipprev / #nav_skipnext) stayed enabled at the first/last item of a level, and inconsistently so (e.g. "a 2 2" enabled but "b 2" disabled). Their disabled state was derived from exescorm_skipprev()/exescorm_skipnext(), which climb to the parent when the current item has no same-level sibling (plain Previous/Next reuse that fallback) — so at a level boundary the button resolved to a parent-level node and looked navigable. Gate the disabled state of both buttons on the authoritative prevsibling / nextsibling metadata in scoes_nav, so they are disabled exactly when there is no previous/next sibling at the current level. When a sibling exists the behaviour is unchanged; Previous/Next/Up and the #63 re-entry guard are untouched. Verified on Moodle 5.0.5 with the sample SCORM: all 12 TOC nodes now show the correct within-level button states, the buttons navigate to the right sibling, and each TOC click still loads its page exactly once (no #63 regression).
|
@ignaciogros thanks again — you nailed it. Fixed in 05d077e and re-tested on Moodle 5.0.5 (Docker) with your sample SCORM. Root causeThe disabled state of the "within this level" buttons ( FixThe disabled state of each "within this level" button is now gated on the authoritative Verified (your sample SCORM, all 12 TOC nodes)
Before / after, on
Re-requesting your review. Thanks! |
Summary
Speculative fix for #63. Tagging this draft because I have not been able to reproduce the issue locally on Moodle 5.0, but the code path I'm patching is the only thing in
module.jsthat matches every detail of the bug report.Root-cause hypothesis
`exescorm_activate_item()` (the function that loads the SCO matching the clicked TOC entry) calls `exescorm_tree_node.closeAll()` and later `exescorm_tree_node.openAll()` to collapse and re-expand the whole YUI TreeView. Those calls mutate node selection state and YUI fires additional `select` events while the tree settles, which the `tree.after('select', ...)` handler then interprets as a fresh navigation request and calls `exescorm_activate_item()` again with a different node (typically a sibling).
That maps cleanly onto every detail Ignacio described:
It also explains why I can't reproduce on Moodle 5.0: YUI TreeView's exact event ordering changed between versions, and the spurious `select` may simply not fire on the build I tested.
Fix
Single-flag re-entry guard:
17 added lines, no behavioural change for the non-buggy path.
Test plan
Sibling repo
`mod_exeweb` doesn't share this code path (its viewer renders the package as a single iframe with no TOC), so no parallel PR is needed there. Per `AGENTS.md`'s twin-plugin checklist, audited `mod_exeweb/amd/src/editor_modal.js` and confirmed.
Moodle Playground Preview
The changes in this pull request can be previewed and tested using a Moodle Playground instance.