-
Notifications
You must be signed in to change notification settings - Fork 60
OC-1441 Detach step elements from DOM or stop playback of closed steps #112
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
Closed
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,30 +7,120 @@ function MentoringWithStepsBlock(runtime, element) { | |
| } | ||
|
|
||
| var children = runtime.children(element); | ||
|
|
||
| var steps = []; | ||
|
|
||
| for (var i = 0; i < children.length; i++) { | ||
| var child = children[i]; | ||
| var blockType = $(child.element).data('block-type'); | ||
| if (blockType === 'sb-step') { | ||
| steps.push(child); | ||
| registerStep(child); | ||
| } | ||
| } | ||
|
|
||
| var activeStep = $('.mentoring', element).data('active-step'); | ||
| var activeStepIndex = $('.mentoring', element).data('active-step'); | ||
| var attemptsTemplate = _.template($('#xblock-attempts-template').html()); | ||
| var message = $('.sb-step-message', element); | ||
| var checkmark, submitDOM, nextDOM, reviewButtonDOM, tryAgainDOM, | ||
| gradeDOM, attemptsDOM, reviewLinkDOM, submitXHR; | ||
| var reviewStepDOM = $("div.xblock[data-block-type=sb-review-step], div.xblock-v1[data-block-type=sb-review-step]", element); | ||
| var hasAReviewStep = reviewStepDOM.length == 1; | ||
|
|
||
| /** | ||
| * Registers step with this StepBuilder instance: Creates and stores a wrapper that contains the XBlock instance | ||
| * and associated DOM element, as well as some additional metadata used to safely | ||
| * remove and re-attach xblock to the DOM. See showStep and hideStep to see why we need to do it. | ||
| * @param step xblock instance to register | ||
| */ | ||
| function registerStep(step) { | ||
| var $element = $(step.element); | ||
| var $anchor = $('<span class="xblock-sb-anchor"/>'); | ||
| $anchor.insertBefore($element); | ||
| var step_wrapper = { | ||
| $element: $element, | ||
| xblock: step, | ||
| $anchor: $anchor | ||
| }; | ||
| steps.push(step_wrapper); | ||
| } | ||
|
|
||
| /** | ||
| * Returns wrapper for the active step | ||
| * @returns {*}, an object containing the XBlock instance and associated DOM element for the active step, as well | ||
| * as additional metadata (see registerStep where this object is created for properties) | ||
| */ | ||
| function getWrapperForActiveStep() { | ||
| return steps[activeStepIndex]; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the active step | ||
| * @returns {*} | ||
| */ | ||
| function getActiveStep() { | ||
| return getWrapperForActiveStep().xblock; | ||
| } | ||
|
|
||
| /** | ||
| * Calls a function for each registered step. The object passed to this function is a step wrapper object | ||
| * (see registerStep where this object is created for a list of properties) | ||
| * | ||
| * @param func single arg function. | ||
| */ | ||
| function forEachStep(func){ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jbzdak I like this function, it enables some nice refactorings :) |
||
| for (var idx=0; idx < steps.length; idx++) { | ||
| func(steps[idx]); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Displays a step | ||
| * @param step_wrapper | ||
| */ | ||
| function showStep(step_wrapper) { | ||
| step_wrapper.$element.show(); | ||
| step_wrapper.xblock.updateChildren(); | ||
| } | ||
|
|
||
| /** | ||
| * Hides a step | ||
| * @param step_wrapper | ||
| */ | ||
| function hideStep(step_wrapper) { | ||
| // This is a hacky workaround, but it works. It detaches this element from DOM, and re-attaches it immediately, | ||
| // which has the side effect of stopping any video elements (tested with Ooyala and Youtube). | ||
| // This solution was chosen as permanently detaching xblocks from DOM is not anticipated by: | ||
| // * Selenium tests | ||
| // * Some javascript code here | ||
| // * Anything else that inspects DOM elements and makes choices | ||
| step_wrapper.$element.detach(); | ||
| step_wrapper.$element.insertAfter(step_wrapper.$anchor); | ||
| step_wrapper.$element.hide(); | ||
| } | ||
|
|
||
| /** | ||
| * Displays the active step | ||
| */ | ||
| function showActiveStep() { | ||
| var step = getWrapperForActiveStep(); | ||
| showStep(step); | ||
| } | ||
|
|
||
| /** | ||
| * Hides all steps | ||
| */ | ||
| function hideAllSteps() { | ||
| forEachStep(function(step){ | ||
| hideStep(step); | ||
| }); | ||
| } | ||
|
|
||
| function isLastStep() { | ||
| return (activeStep === steps.length-1); | ||
| return (activeStepIndex === steps.length-1); | ||
| } | ||
|
|
||
| function atReviewStep() { | ||
| return (activeStep === -1); | ||
| return (activeStepIndex === -1); | ||
| } | ||
|
|
||
| function someAttemptsLeft() { | ||
|
|
@@ -49,7 +139,7 @@ function MentoringWithStepsBlock(runtime, element) { | |
| } else { | ||
| checkmark.addClass('checkmark-incorrect icon-exclamation fa-exclamation'); | ||
| } | ||
| var step = steps[activeStep]; | ||
| var step = getActiveStep(); | ||
| if (typeof step.showFeedback == 'function') { | ||
| step.showFeedback(response); | ||
| } | ||
|
|
@@ -78,14 +168,14 @@ function MentoringWithStepsBlock(runtime, element) { | |
| function submit() { | ||
| submitDOM.attr('disabled', 'disabled'); // Disable the button until the results load. | ||
| var submitUrl = runtime.handlerUrl(element, 'submit'); | ||
|
|
||
| var hasQuestion = steps[activeStep].hasQuestion(); | ||
| var data = steps[activeStep].getSubmitData(); | ||
| data["active_step"] = activeStep; | ||
| var activeStep = getActiveStep(); | ||
| var hasQuestion = activeStep.hasQuestion(); | ||
| var data = activeStep.getSubmitData(); | ||
| data["active_step"] = activeStepIndex; | ||
| $.post(submitUrl, JSON.stringify(data)).success(function(response) { | ||
| showFeedback(response); | ||
| activeStep = response.active_step; | ||
| if (activeStep === -1) { | ||
| activeStepIndex = response.active_step; | ||
| if (activeStepIndex === -1) { | ||
| // We are now showing the review step / end | ||
| // Update the number of attempts. | ||
| attemptsDOM.data('num_attempts', response.num_attempts); | ||
|
|
@@ -102,27 +192,21 @@ function MentoringWithStepsBlock(runtime, element) { | |
| } | ||
|
|
||
| function getResults() { | ||
| var step = steps[activeStep]; | ||
| step.getResults(handleReviewResults); | ||
| getActiveStep().getResults(handleReviewResults); | ||
| } | ||
|
|
||
| function handleReviewResults(response) { | ||
| // Show step-level feedback | ||
| showFeedback(response); | ||
| // Forward to active step to show answer level feedback | ||
| var step = steps[activeStep]; | ||
| var step = getActiveStep(); | ||
| var results = response.results; | ||
| var options = { | ||
| checkmark: checkmark | ||
| }; | ||
| step.handleReview(results, options); | ||
| } | ||
|
|
||
| function hideAllSteps() { | ||
| for (var i=0; i < steps.length; i++) { | ||
| $(steps[i].element).hide(); | ||
| } | ||
| } | ||
|
|
||
| function clearSelections() { | ||
| $('input[type=radio], input[type=checkbox]', element).prop('checked', false); | ||
|
|
@@ -139,7 +223,7 @@ function MentoringWithStepsBlock(runtime, element) { | |
| } | ||
|
|
||
| function updateNextLabel() { | ||
| var step = steps[activeStep]; | ||
| var step = getActiveStep(); | ||
| nextDOM.attr('value', step.getStepLabel()); | ||
| } | ||
|
|
||
|
|
@@ -164,7 +248,7 @@ function MentoringWithStepsBlock(runtime, element) { | |
| nextDOM.on('click', updateDisplay); | ||
| reviewButtonDOM.on('click', showGrade); | ||
|
|
||
| var step = steps[activeStep]; | ||
| var step = getActiveStep(); | ||
| if (step.hasQuestion()) { // Step includes one or more questions | ||
| nextDOM.attr('disabled', 'disabled'); | ||
| submitDOM.show(); | ||
|
|
@@ -231,7 +315,7 @@ function MentoringWithStepsBlock(runtime, element) { | |
| } | ||
|
|
||
| function jumpToReview(stepIndex) { | ||
| activeStep = stepIndex; | ||
| activeStepIndex = stepIndex; | ||
| cleanAll(); | ||
| showActiveStep(); | ||
| updateNextLabel(); | ||
|
|
@@ -245,7 +329,7 @@ function MentoringWithStepsBlock(runtime, element) { | |
| nextDOM.show(); | ||
| nextDOM.removeAttr('disabled'); | ||
| } | ||
| var step = steps[activeStep]; | ||
| var step = getActiveStep(); | ||
|
|
||
| tryAgainDOM.hide(); | ||
| if (step.hasQuestion()) { | ||
|
|
@@ -269,11 +353,6 @@ function MentoringWithStepsBlock(runtime, element) { | |
| } // Don't show attempts if unlimited attempts available (max_attempts === 0) | ||
| } | ||
|
|
||
| function showActiveStep() { | ||
| var step = steps[activeStep]; | ||
| $(step.element).show(); | ||
| step.updateChildren(); | ||
| } | ||
|
|
||
| function onChange() { | ||
| // We do not allow users to modify answers belonging to a step after submitting them: | ||
|
|
@@ -286,7 +365,7 @@ function MentoringWithStepsBlock(runtime, element) { | |
|
|
||
| function validateXBlock() { | ||
| var isValid = true; | ||
| var step = steps[activeStep]; | ||
| var step = getActiveStep(); | ||
| if (step) { | ||
| isValid = step.validate(); | ||
| } | ||
|
|
@@ -298,15 +377,13 @@ function MentoringWithStepsBlock(runtime, element) { | |
| } | ||
|
|
||
| function initSteps(options) { | ||
| for (var i=0; i < steps.length; i++) { | ||
| var step = steps[i]; | ||
| var mentoring = { | ||
| forEachStep(function (step) { | ||
| options.mentoring = { | ||
| setContent: setContent, | ||
| publish_event: publishEvent | ||
| }; | ||
| options.mentoring = mentoring; | ||
| step.initChildren(options); | ||
| } | ||
| step.xblock.initChildren(options); | ||
| }); | ||
| } | ||
|
|
||
| function setContent(dom, content) { | ||
|
|
@@ -346,7 +423,7 @@ function MentoringWithStepsBlock(runtime, element) { | |
| } | ||
|
|
||
| function reviewNextStep() { | ||
| jumpToReview(activeStep+1); | ||
| jumpToReview(activeStepIndex+1); | ||
| } | ||
|
|
||
| function handleTryAgain(result) { | ||
|
|
@@ -355,7 +432,7 @@ function MentoringWithStepsBlock(runtime, element) { | |
| // and interrupting their experience with the current unit | ||
| notify('navigation', {state: 'lock'}); | ||
|
|
||
| activeStep = result.active_step; | ||
| activeStepIndex = result.active_step; | ||
| clearSelections(); | ||
| updateDisplay(); | ||
| tryAgainDOM.hide(); | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jbzdak "Registers step with this StepBuilder instance: Creates and stores a wrapper that contains the XBlock instance and associated DOM element, as well as some additional metadata ..."
Very happy to see documentation for all of the new functions! :)