Merge branch 'MDL-69164-master' of https://github.com/JBThong/moodle

This commit is contained in:
Andrew Nicols
2023-01-24 11:45:27 +08:00
21 changed files with 456 additions and 217 deletions
+23
View File
@@ -0,0 +1,23 @@
define("core/scroll_manager",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.watchScrollButtonSaves=_exports.scrollToSavedPosition=_exports.saveScrollPositionToForm=_exports.saveScrollPos=_exports.initLinksScrollPos=void 0;
/**
* Scroll manager is a class that help with saving the scroll positing when you
* click on an action icon, and then when the page is reloaded after processing
* the action, it scrolls you to exactly where you were. This is much nicer for
* the user.
*
* To use this in your code, you need to ensure that:
* 1. The button that triggers the action has to have a click event handler that
* calls saveScrollPos()
* 2. After doing the processing, the redirect() function will add 'mdlscrollto'
* parameter into the redirect url automatically.
* 3. Finally, on the page that is reloaded (which should be the same as the one
* the user started on) you need to call scrollToSavedPosition()
* on page load.
*
* @module core/scroll_manager
* @copyright 2021 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
let scrollingElement=null;const getScrollingElement=()=>{if(null===scrollingElement){const page=document.getElementById("page");scrollingElement=(element=>{const hasScrollableContent=element.scrollHeight>element.clientHeight,isOverflowHidden=-1!==window.getComputedStyle(element).overflowY.indexOf("hidden");return hasScrollableContent&&!isOverflowHidden})(page)?page:document.scrollingElement}return scrollingElement},getScrollPos=()=>getScrollingElement().scrollTop;_exports.saveScrollPos=elementId=>{const form=document.getElementById(elementId).closest("form");form&&saveScrollPositionToForm(form)};_exports.watchScrollButtonSaves=()=>{document.addEventListener("click",(e=>{const button=e.target.closest('[data-savescrollposition="true"]');button&&saveScrollPositionToForm(button.form)}))};const saveScrollPositionToForm=form=>{(form=>{const element=form.querySelector("input[name=mdlscrollto]");if(element)return element;const scrollPos=document.createElement("input");return scrollPos.type="hidden",scrollPos.name="mdlscrollto",form.appendChild(scrollPos),scrollPos})(form).value=getScrollPos()};_exports.saveScrollPositionToForm=saveScrollPositionToForm;_exports.initLinksScrollPos=()=>{document.addEventListener("click",(e=>{if(!e.target.closest("a[data-save-scroll=true]"))return;e.preventDefault();const url=new URL(e.target.href);url.searchParams.set("mdlscrollto",getScrollPos()),window.location=url}))};_exports.scrollToSavedPosition=()=>{const url=new URL(window.location.href);if(!url.searchParams.has("mdlscrollto"))return;const scrollPosition=url.searchParams.get("mdlscrollto"),scrollingElement=getScrollingElement();scrollingElement.scrollTo(0,scrollPosition),document.addEventListener("DOMContentLoaded",(()=>{scrollingElement.scrollTo(0,scrollPosition)}))}}));
//# sourceMappingURL=scroll_manager.min.js.map
File diff suppressed because one or more lines are too long
+181
View File
@@ -0,0 +1,181 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Scroll manager is a class that help with saving the scroll positing when you
* click on an action icon, and then when the page is reloaded after processing
* the action, it scrolls you to exactly where you were. This is much nicer for
* the user.
*
* To use this in your code, you need to ensure that:
* 1. The button that triggers the action has to have a click event handler that
* calls saveScrollPos()
* 2. After doing the processing, the redirect() function will add 'mdlscrollto'
* parameter into the redirect url automatically.
* 3. Finally, on the page that is reloaded (which should be the same as the one
* the user started on) you need to call scrollToSavedPosition()
* on page load.
*
* @module core/scroll_manager
* @copyright 2021 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/** @property {HTMLElement} scrollingElement the current scrolling element. */
let scrollingElement = null;
/**
* Is the element scrollable?
*
* @param {HTMLElement} element Element.
* @returns {boolean}
*/
const isScrollable = (element) => {
// Check if the element has scrollable content.
const hasScrollableContent = element.scrollHeight > element.clientHeight;
// If 'overflow-y' is set to hidden, the scroll bar is't show.
const elementOverflow = window.getComputedStyle(element).overflowY;
const isOverflowHidden = elementOverflow.indexOf('hidden') !== -1;
return hasScrollableContent && !isOverflowHidden;
};
/**
* Get the scrolling element.
*
* @returns {HTMLElement}
*/
const getScrollingElement = () => {
if (scrollingElement === null) {
const page = document.getElementById('page');
if (isScrollable(page)) {
scrollingElement = page;
} else {
scrollingElement = document.scrollingElement;
}
}
return scrollingElement;
};
/**
* Get current scroll position.
*
* @returns {Number} Scroll position.
*/
const getScrollPos = () => {
const scrollingElement = getScrollingElement();
return scrollingElement.scrollTop;
};
/**
* Get the scroll position for this form.
*
* @param {HTMLFormElement} form
* @returns {HTMLInputElement}
*/
const getScrollPositionElement = (form) => {
const element = form.querySelector('input[name=mdlscrollto]');
if (element) {
return element;
}
const scrollPos = document.createElement('input');
scrollPos.type = 'hidden';
scrollPos.name = 'mdlscrollto';
form.appendChild(scrollPos);
return scrollPos;
};
/**
* In the form that contains the element, set the value of the form field with
* name mdlscrollto to the current scroll position. If there is no element with
* that name, it creates a hidden form field with that name within the form.
*
* @param {string} elementId The element in the form.
*/
export const saveScrollPos = (elementId) => {
const element = document.getElementById(elementId);
const form = element.closest('form');
if (!form) {
return;
}
saveScrollPositionToForm(form);
};
/**
* Init event handlers for all links with data-savescrollposition=true.
* Set the value to the closest form.
*/
export const watchScrollButtonSaves = () => {
document.addEventListener('click', (e) => {
const button = e.target.closest('[data-savescrollposition="true"]');
if (button) {
saveScrollPositionToForm(button.form);
}
});
};
/**
* Save the position to form.
*
* @param {Object} form The form is saved scroll position.
*/
export const saveScrollPositionToForm = (form) => {
getScrollPositionElement(form).value = getScrollPos();
};
/**
* Init event handlers for all links with data-save-scroll=true.
* Handle to add mdlscrollto parameter to link using js when we click on the link.
*
*/
export const initLinksScrollPos = () => {
document.addEventListener('click', (e) => {
const link = e.target.closest('a[data-save-scroll=true]');
if (!link) {
return;
}
e.preventDefault();
const url = new URL(e.target.href);
url.searchParams.set('mdlscrollto', getScrollPos());
window.location = url;
});
};
/**
* If there is a parameter like mdlscrollto=123 in the URL, scroll to that saved position.
*/
export const scrollToSavedPosition = () => {
const url = new URL(window.location.href);
if (!url.searchParams.has('mdlscrollto')) {
return;
}
const scrollPosition = url.searchParams.get('mdlscrollto');
// Event onDOMReady is the effective one here. I am leaving the immediate call to
// window.scrollTo in case it reduces flicker.
const scrollingElement = getScrollingElement();
scrollingElement.scrollTo(0, scrollPosition);
document.addEventListener('DOMContentLoaded', () => {
scrollingElement.scrollTo(0, scrollPosition);
});
};
+3 -3
View File
@@ -39,7 +39,7 @@ list($thispageurl, $contexts, $cmid, $cm, $quiz, $pagevars) =
$returnurl = optional_param('returnurl', '', PARAM_LOCALURL);
$addonpage = optional_param('addonpage', 0, PARAM_INT);
$category = optional_param('category', 0, PARAM_INT);
$scrollpos = optional_param('scrollpos', 0, PARAM_INT);
$mdlscrollto = optional_param('mdlscrollto', 0, PARAM_INT);
// Get the course object and related bits.
if (!$course = $DB->get_record('course', array('id' => $quiz->course))) {
@@ -59,8 +59,8 @@ if ($returnurl) {
} else {
$returnurl = new moodle_url('/mod/quiz/edit.php', array('cmid' => $cmid));
}
if ($scrollpos) {
$returnurl->param('scrollpos', $scrollpos);
if ($mdlscrollto) {
$returnurl->param('mdlscrollto', $mdlscrollto);
}
$defaultcategoryobj = question_make_default_categories($contexts->all());
+8 -3
View File
@@ -563,8 +563,8 @@ class renderer extends plugin_renderer_base {
'value' => '0', 'id' => 'timeup']);
$output .= html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'sesskey',
'value' => sesskey()]);
$output .= html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'scrollpos',
'value' => '', 'id' => 'scrollpos']);
$output .= html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'mdlscrollto',
'value' => '', 'id' => 'mdlscrollto']);
// Add a hidden field with questionids. Do this at the end of the form, so
// if you navigate before the form has finished loading, it does not wipe all
@@ -622,9 +622,14 @@ class renderer extends plugin_renderer_base {
public function redo_question_button($slot, $disabled) {
$attributes = ['type' => 'submit', 'name' => 'redoslot' . $slot,
'value' => get_string('redoquestion', 'quiz'),
'class' => 'mod_quiz-redo_question_button btn btn-secondary'];
'class' => 'mod_quiz-redo_question_button btn btn-secondary',
'id' => 'redoslot' . $slot . '-submit',
'data-savescrollposition' => 'true',
];
if ($disabled) {
$attributes['disabled'] = 'disabled';
} else {
$this->page->requires->js_call_amd('core_question/question_engine', 'initSubmitButton', [$attributes['id']]);
}
return html_writer::div(html_writer::empty_tag('input', $attributes));
}
+5 -6
View File
@@ -46,9 +46,7 @@ require_once(__DIR__ . '/../../config.php');
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
require_once($CFG->dirroot . '/question/editlib.php');
// These params are only passed from page request to request while we stay on
// this page otherwise they would go in question_edit_setup.
$scrollpos = optional_param('scrollpos', '', PARAM_INT);
$mdlscrollto = optional_param('mdlscrollto', '', PARAM_INT);
list($thispageurl, $contexts, $cmid, $cm, $quiz, $pagevars) =
question_edit_setup('editq', '/mod/quiz/edit.php', true);
@@ -81,9 +79,6 @@ foreach ($params as $key => $value) {
}
$afteractionurl = new moodle_url($thispageurl);
if ($scrollpos) {
$afteractionurl->param('scrollpos', $scrollpos);
}
if (optional_param('repaginate', false, PARAM_BOOL) && confirm_sesskey()) {
// Re-paginate the quiz.
@@ -94,6 +89,10 @@ if (optional_param('repaginate', false, PARAM_BOOL) && confirm_sesskey()) {
redirect($afteractionurl);
}
if ($mdlscrollto) {
$afteractionurl->param('mdlscrollto', $mdlscrollto);
}
if (($addquestion = optional_param('addquestion', 0, PARAM_INT)) && confirm_sesskey()) {
// Add a single question to the current quiz.
$structure->check_can_be_edited();
+6 -2
View File
@@ -26,7 +26,9 @@
M.mod_quiz = M.mod_quiz || {};
M.mod_quiz.init_attempt_form = function(Y) {
M.core_question_engine.init_form(Y, '#responseform');
require(['core_question/question_engine'], function(qEngine) {
qEngine.initForm('#responseform');
});
Y.on('submit', M.mod_quiz.timer.stop, '#responseform');
require(['core_form/changechecker'], function(FormChangeChecker) {
FormChangeChecker.watchFormById('responseform');
@@ -34,7 +36,9 @@ M.mod_quiz.init_attempt_form = function(Y) {
};
M.mod_quiz.init_review_form = function(Y) {
M.core_question_engine.init_form(Y, '.questionflagsaveform');
require(['core_question/question_engine'], function(qEngine) {
qEngine.initForm('.questionflagsaveform');
});
Y.on('submit', function(e) { e.halt(); }, '.questionflagsaveform');
};
+3 -3
View File
@@ -45,7 +45,7 @@ $previous = optional_param('previous', false, PARAM_BOOL);
$next = optional_param('next', false, PARAM_BOOL);
$finishattempt = optional_param('finishattempt', false, PARAM_BOOL);
$timeup = optional_param('timeup', 0, PARAM_BOOL); // True if form was submitted by timer.
$scrollpos = optional_param('scrollpos', '', PARAM_RAW);
$mdlscrollto = optional_param('mdlscrollto', '', PARAM_RAW);
$cmid = optional_param('cmid', null, PARAM_INT);
$attemptobj = quiz_create_attempt_handling_errors($attemptid, $cmid);
@@ -62,8 +62,8 @@ if ($page == -1) {
$nexturl = $attemptobj->summary_url();
} else {
$nexturl = $attemptobj->attempt_url(null, $page);
if ($scrollpos !== '') {
$nexturl->param('scrollpos', $scrollpos);
if ($mdlscrollto !== '') {
$nexturl->param('mdlscrollto', $mdlscrollto);
}
}
+10
View File
@@ -0,0 +1,10 @@
define("core_question/question_engine",["exports","core/scroll_manager","core_form/submit"],(function(_exports,scrollManager,formSubmit){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
/**
* JavaScript required by the question engine.
*
* @module core_question/question_engine
* @copyright 2021 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.preventRepeatSubmission=_exports.initSubmitButton=_exports.initForm=void 0,scrollManager=_interopRequireWildcard(scrollManager),formSubmit=_interopRequireWildcard(formSubmit);_exports.initSubmitButton=button=>{formSubmit.init(button),scrollManager.watchScrollButtonSaves()};_exports.initForm=formSelector=>{const form=document.querySelector(formSelector);form.setAttribute("autocomplete","off"),form.addEventListener("submit",preventRepeatSubmission),form.addEventListener("key",(event=>{13===event.keyCode&&(event.target.matches("a")||event.target.matches('input[type="submit"]')||event.target.matches("input[type=img]")||event.target.matches("textarea")||event.target.matches("[contenteditable=true]")||event.preventDefault())}));[...form.querySelectorAll(".questionflagsavebutton")].forEach((node=>node.remove())),scrollManager.scrollToSavedPosition()};const preventRepeatSubmission=event=>{const form=event.target.closest("form");"1"!==form.dataset.formSubmitted?(setTimeout((()=>{[...form.querySelectorAll("input[type=submit]")].forEach((input=>input.setAttribute("disabled",!0)))})),form.dataset.formSubmitted="1"):event.preventDefault()};_exports.preventRepeatSubmission=preventRepeatSubmission}));
//# sourceMappingURL=question_engine.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"question_engine.min.js","sources":["../src/question_engine.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * JavaScript required by the question engine.\n *\n * @module core_question/question_engine\n * @copyright 2021 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as scrollManager from 'core/scroll_manager';\nimport * as formSubmit from 'core_form/submit';\n\n/**\n * Initialise a question submit button. This saves the scroll position and\n * sets the fragment on the form submit URL so the page reloads in the right place.\n *\n * @param {string} button the id of the button in the HTML.\n */\nexport const initSubmitButton = button => {\n formSubmit.init(button);\n scrollManager.watchScrollButtonSaves();\n};\n\n/**\n * Initialise a form that contains questions printed using print_question.\n * This has the effect of:\n * 1. Turning off browser autocomlete.\n * 2. Stopping enter from submitting the form (or toggling the next flag) unless\n * keyboard focus is on the submit button or the flag.\n * 3. Removes any '.questionflagsavebutton's, since we have JavaScript to toggle\n * the flags using ajax.\n * 4. Scroll to the position indicated by scrollpos= in the URL, if it is there.\n * 5. Prevent the user from repeatedly submitting the form.\n *\n * @param {string} formSelector Selector to identify the form.\n */\nexport const initForm = (formSelector) => {\n const form = document.querySelector(formSelector);\n form.setAttribute('autocomplete', 'off');\n\n form.addEventListener('submit', preventRepeatSubmission);\n\n form.addEventListener('key', (event) => {\n if (event.keyCode !== 13) {\n return;\n }\n\n if (event.target.matches('a')) {\n return;\n }\n\n if (event.target.matches('input[type=\"submit\"]')) {\n return;\n }\n\n if (event.target.matches('input[type=img]')) {\n return;\n }\n\n if (event.target.matches('textarea') || event.target.matches('[contenteditable=true]')) {\n return;\n }\n\n event.preventDefault();\n });\n\n const questionFlagSaveButtons = form.querySelectorAll('.questionflagsavebutton');\n [...questionFlagSaveButtons].forEach((node) => node.remove());\n\n // Note: The scrollToSavedPosition function tries to wait until the content has loaded before firing.\n scrollManager.scrollToSavedPosition();\n};\n\n/**\n * Event handler to stop a question form being submitted more than once.\n *\n * @param {object} event the form submit event.\n */\nexport const preventRepeatSubmission = (event) => {\n const form = event.target.closest('form');\n if (form.dataset.formSubmitted === '1') {\n event.preventDefault();\n return;\n }\n\n setTimeout(() => {\n [...form.querySelectorAll('input[type=submit]')].forEach((input) => input.setAttribute('disabled', true));\n });\n form.dataset.formSubmitted = '1';\n};\n"],"names":["button","formSubmit","init","scrollManager","watchScrollButtonSaves","formSelector","form","document","querySelector","setAttribute","addEventListener","preventRepeatSubmission","event","keyCode","target","matches","preventDefault","querySelectorAll","forEach","node","remove","scrollToSavedPosition","closest","dataset","formSubmitted","setTimeout","input"],"mappings":";;;;;;;+QAgCgCA,SAC5BC,WAAWC,KAAKF,QAChBG,cAAcC,4CAgBOC,qBACfC,KAAOC,SAASC,cAAcH,cACpCC,KAAKG,aAAa,eAAgB,OAElCH,KAAKI,iBAAiB,SAAUC,yBAEhCL,KAAKI,iBAAiB,OAAQE,QACJ,KAAlBA,MAAMC,UAIND,MAAME,OAAOC,QAAQ,MAIrBH,MAAME,OAAOC,QAAQ,yBAIrBH,MAAME,OAAOC,QAAQ,oBAIrBH,MAAME,OAAOC,QAAQ,aAAeH,MAAME,OAAOC,QAAQ,2BAI7DH,MAAMI,yBAGsBV,KAAKW,iBAAiB,4BACzBC,SAASC,MAASA,KAAKC,WAGpDjB,cAAckB,+BAQLV,wBAA2BC,cAC9BN,KAAOM,MAAME,OAAOQ,QAAQ,QACC,MAA/BhB,KAAKiB,QAAQC,eAKjBC,YAAW,SACHnB,KAAKW,iBAAiB,uBAAuBC,SAASQ,OAAUA,MAAMjB,aAAa,YAAY,QAEvGH,KAAKiB,QAAQC,cAAgB,KAPzBZ,MAAMI"}
+104
View File
@@ -0,0 +1,104 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* JavaScript required by the question engine.
*
* @module core_question/question_engine
* @copyright 2021 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import * as scrollManager from 'core/scroll_manager';
import * as formSubmit from 'core_form/submit';
/**
* Initialise a question submit button. This saves the scroll position and
* sets the fragment on the form submit URL so the page reloads in the right place.
*
* @param {string} button the id of the button in the HTML.
*/
export const initSubmitButton = button => {
formSubmit.init(button);
scrollManager.watchScrollButtonSaves();
};
/**
* Initialise a form that contains questions printed using print_question.
* This has the effect of:
* 1. Turning off browser autocomlete.
* 2. Stopping enter from submitting the form (or toggling the next flag) unless
* keyboard focus is on the submit button or the flag.
* 3. Removes any '.questionflagsavebutton's, since we have JavaScript to toggle
* the flags using ajax.
* 4. Scroll to the position indicated by scrollpos= in the URL, if it is there.
* 5. Prevent the user from repeatedly submitting the form.
*
* @param {string} formSelector Selector to identify the form.
*/
export const initForm = (formSelector) => {
const form = document.querySelector(formSelector);
form.setAttribute('autocomplete', 'off');
form.addEventListener('submit', preventRepeatSubmission);
form.addEventListener('key', (event) => {
if (event.keyCode !== 13) {
return;
}
if (event.target.matches('a')) {
return;
}
if (event.target.matches('input[type="submit"]')) {
return;
}
if (event.target.matches('input[type=img]')) {
return;
}
if (event.target.matches('textarea') || event.target.matches('[contenteditable=true]')) {
return;
}
event.preventDefault();
});
const questionFlagSaveButtons = form.querySelectorAll('.questionflagsavebutton');
[...questionFlagSaveButtons].forEach((node) => node.remove());
// Note: The scrollToSavedPosition function tries to wait until the content has loaded before firing.
scrollManager.scrollToSavedPosition();
};
/**
* Event handler to stop a question form being submitted more than once.
*
* @param {object} event the form submit event.
*/
export const preventRepeatSubmission = (event) => {
const form = event.target.closest('form');
if (form.dataset.formSubmitted === '1') {
event.preventDefault();
return;
}
setTimeout(() => {
[...form.querySelectorAll('input[type=submit]')].forEach((input) => input.setAttribute('disabled', true));
});
form.dataset.formSubmitted = '1';
};
+9 -8
View File
@@ -37,7 +37,7 @@ $wizardnow = optional_param('wizardnow', '', PARAM_ALPHA);
$originalreturnurl = optional_param('returnurl', 0, PARAM_LOCALURL);
$appendqnumstring = optional_param('appendqnumstring', '', PARAM_ALPHA);
$inpopup = optional_param('inpopup', 0, PARAM_BOOL);
$scrollpos = optional_param('scrollpos', 0, PARAM_INT);
$mdlscrollto = optional_param('mdlscrollto', 0, PARAM_INT);
\core_question\local\bank\helper::require_plugin_enabled('qbank_editquestion');
@@ -72,8 +72,8 @@ if ($appendqnumstring !== '') {
if ($inpopup !== 0) {
$url->param('inpopup', $inpopup);
}
if ($scrollpos) {
$url->param('scrollpos', $scrollpos);
if ($mdlscrollto) {
$url->param('mdlscrollto', $mdlscrollto);
}
$PAGE->set_url($url);
@@ -92,8 +92,8 @@ if ($originalreturnurl) {
} else {
$returnurl = $questionbankurl;
}
if ($scrollpos) {
$returnurl->param('scrollpos', $scrollpos);
if ($mdlscrollto) {
$returnurl->param('mdlscrollto', $mdlscrollto);
}
if ($cmid) {
@@ -204,7 +204,7 @@ if ($wizardnow !== '') {
}
$toform = fullclone($question); // Send the question object and a few more parameters to the form.
$toform->category = "{$category->id},{$category->contextid}";
$toform->scrollpos = $scrollpos;
$toform->mdlscrollto = $mdlscrollto;
if ($formeditable && $id) {
$toform->categorymoveto = $toform->category;
}
@@ -332,10 +332,11 @@ if ($mform->is_cancelled()) {
}
} else {
$nexturlparams = array(
$nexturlparams = [
'returnurl' => $originalreturnurl,
'appendqnumstring' => $appendqnumstring,
'scrollpos' => $scrollpos);
'mdlscrollto' => $mdlscrollto,
];
if (isset($fromform->nextpageparam) && is_array($fromform->nextpageparam)) {
// Useful for passing data to the next page which is not saved in the database.
$nexturlparams += $fromform->nextpageparam;
+1 -9
View File
@@ -1,11 +1,3 @@
define("qbank_previewquestion/preview",["exports","jquery"],(function(_exports,_jquery){var obj;
/**
* Javascript for preview.
*
* @module qbank_preview/preview
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};_exports.init=(redirect,url)=>{if(!redirect){document.getElementById("close-previewquestion-page").onclick=()=>{null===window.opener?location.href=url:window.close()}}setupQuestionForm("responseform")};const setupQuestionForm=formElement=>{let form=document.getElementById(formElement);form&&(autocompleteOff(form),preventRepeatSubmission(form),removeClass(".questionflagsavebutton",form),scrollToSavedPos(form))},autocompleteOff=form=>{form.setAttribute("autocomplete","off")},preventRepeatSubmission=form=>{form.addEventListener("submit",(function(){return(0,_jquery.default)(this).submit((function(){return!1})),!0}))},removeClass=(classname,form)=>{form.querySelectorAll(classname).forEach((e=>e.remove()))},scrollToSavedPos=form=>{let matches=window.location.href.match(/^.*[?&]scrollpos=(\d*)(?:&|$|#).*$/,"$1");matches&&(window.scrollTo(0,matches[1]),form.addEventListener("DOMContentLoaded",(()=>{window.scrollTo(0,matches[1])})))}}));
define("qbank_previewquestion/preview",["exports","core_question/question_engine"],(function(_exports,_question_engine){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0;_exports.init=(redirect,url)=>{if(!redirect){document.getElementById("close-previewquestion-page").addEventListener("click",(e=>{e.preventDefault(),null===window.opener?location.href=url:window.close()}))}(0,_question_engine.initForm)("#responseform")}}));
//# sourceMappingURL=preview.min.js.map
@@ -1 +1 @@
{"version":3,"file":"preview.min.js","sources":["../src/preview.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript for preview.\n *\n * @module qbank_preview/preview\n * @copyright 2021 Catalyst IT Australia Pty Ltd\n * @author Safat Shahin <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\n\n/**\n * Set up the actions.\n *\n * @method init\n * @param {bool} redirect Redirect.\n * @param {string} url url to redirect.\n */\nexport const init = (redirect, url) => {\n if (!redirect) {\n let closeButton = document.getElementById('close-previewquestion-page');\n closeButton.onclick = () => {\n if (window.opener === null) {\n location.href = url;\n } else {\n window.close();\n }\n };\n }\n // Set up the form to be displayed.\n setupQuestionForm('responseform');\n};\n\n/**\n * Set up the form element to be displayed.\n *\n * @method setupQuestionForm\n * @param {string} formElement The form element.\n */\nconst setupQuestionForm = (formElement) => {\n let form = document.getElementById(formElement);\n if (form) {\n // Turning off browser autocomplete.\n autocompleteOff(form);\n // Stop a question form being submitted more than once.\n preventRepeatSubmission(form);\n // Removes any '.questionflagsavebutton's, since we have JavaScript to toggle.\n removeClass('.questionflagsavebutton', form);\n // Scroll to the position indicated by scrollpos= in the URL, if it is there.\n scrollToSavedPos(form);\n }\n};\n\n/**\n * Set the autocomplete off.\n *\n * @method autocompleteOff\n * @param {object} form The form element.\n */\nconst autocompleteOff = (form) => {\n form.setAttribute(\"autocomplete\", \"off\");\n};\n\n/**\n * Event handler to stop a question form being submitted more than once.\n *\n * @method preventRepeatSubmission\n * @param {object} form The form element.\n */\nconst preventRepeatSubmission = (form) => {\n form.addEventListener(\"submit\", function() {\n $(this).submit(function() {\n return false;\n });\n return true;\n });\n};\n\n/**\n * Removes a class inside an element.\n *\n * @method removeClass\n * @param {string} classname Class name.\n * @param {object} form The form element.\n */\nconst removeClass = (classname, form) => {\n form.querySelectorAll(classname).forEach(e => e.remove());\n};\n\n/**\n * If there is a parameter like scrollpos=123 in the URL, scroll to that saved position.\n * (Note: Moodle 4.0 and above do NOT support Internet Explorer 11 and below.)\n *\n * @method scrollToSavedPos\n * @param {object} form The form element.\n */\nconst scrollToSavedPos = (form) => {\n let matches = window.location.href.match(/^.*[?&]scrollpos=(\\d*)(?:&|$|#).*$/, '$1');\n if (matches) {\n // DOMContentLoaded is the effective one here. I am leaving the immediate call to\n // window.scrollTo in case it reduces flicker.\n window.scrollTo(0, matches[1]);\n form.addEventListener(\"DOMContentLoaded\", () => {\n window.scrollTo(0, matches[1]);\n });\n }\n};\n"],"names":["redirect","url","document","getElementById","onclick","window","opener","location","href","close","setupQuestionForm","formElement","form","autocompleteOff","preventRepeatSubmission","removeClass","scrollToSavedPos","setAttribute","addEventListener","this","submit","classname","querySelectorAll","forEach","e","remove","matches","match","scrollTo"],"mappings":";;;;;;;;wJAiCoB,CAACA,SAAUC,WACtBD,SAAU,CACOE,SAASC,eAAe,8BAC9BC,QAAU,KACI,OAAlBC,OAAOC,OACPC,SAASC,KAAOP,IAEhBI,OAAOI,SAKnBC,kBAAkB,uBAShBA,kBAAqBC,kBACnBC,KAAOV,SAASC,eAAeQ,aAC/BC,OAEAC,gBAAgBD,MAEhBE,wBAAwBF,MAExBG,YAAY,0BAA2BH,MAEvCI,iBAAiBJ,QAUnBC,gBAAmBD,OACrBA,KAAKK,aAAa,eAAgB,QAShCH,wBAA2BF,OAC7BA,KAAKM,iBAAiB,UAAU,qCAC1BC,MAAMC,QAAO,kBACJ,MAEJ,MAWTL,YAAc,CAACM,UAAWT,QAC5BA,KAAKU,iBAAiBD,WAAWE,SAAQC,GAAKA,EAAEC,YAU9CT,iBAAoBJ,WAClBc,QAAUrB,OAAOE,SAASC,KAAKmB,MAAM,qCAAsC,MAC3ED,UAGArB,OAAOuB,SAAS,EAAGF,QAAQ,IAC3Bd,KAAKM,iBAAiB,oBAAoB,KACtCb,OAAOuB,SAAS,EAAGF,QAAQ"}
{"version":3,"file":"preview.min.js","sources":["../src/preview.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript for preview.\n *\n * @module qbank_preview/preview\n * @copyright 2021 Catalyst IT Australia Pty Ltd\n * @author Safat Shahin <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {initForm as initQuestionEngineForm} from 'core_question/question_engine';\n\n/**\n * Set up the actions.\n *\n * @method init\n * @param {bool} redirect Redirect.\n * @param {string} url url to redirect.\n */\nexport const init = (redirect, url) => {\n if (!redirect) {\n const closeButton = document.getElementById('close-previewquestion-page');\n closeButton.addEventListener('click', (e) => {\n e.preventDefault();\n if (window.opener === null) {\n location.href = url;\n } else {\n window.close();\n }\n });\n }\n // Set up the form to be displayed.\n initQuestionEngineForm('#responseform');\n};\n"],"names":["redirect","url","document","getElementById","addEventListener","e","preventDefault","window","opener","location","href","close"],"mappings":"mNAiCoB,CAACA,SAAUC,WACtBD,SAAU,CACSE,SAASC,eAAe,8BAChCC,iBAAiB,SAAUC,IACnCA,EAAEC,iBACoB,OAAlBC,OAAOC,OACPC,SAASC,KAAOT,IAEhBM,OAAOI,yCAKI"}
@@ -22,7 +22,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import $ from 'jquery';
import {initForm as initQuestionEngineForm} from 'core_question/question_engine';
/**
* Set up the actions.
@@ -33,90 +33,16 @@ import $ from 'jquery';
*/
export const init = (redirect, url) => {
if (!redirect) {
let closeButton = document.getElementById('close-previewquestion-page');
closeButton.onclick = () => {
const closeButton = document.getElementById('close-previewquestion-page');
closeButton.addEventListener('click', (e) => {
e.preventDefault();
if (window.opener === null) {
location.href = url;
} else {
window.close();
}
};
});
}
// Set up the form to be displayed.
setupQuestionForm('responseform');
};
/**
* Set up the form element to be displayed.
*
* @method setupQuestionForm
* @param {string} formElement The form element.
*/
const setupQuestionForm = (formElement) => {
let form = document.getElementById(formElement);
if (form) {
// Turning off browser autocomplete.
autocompleteOff(form);
// Stop a question form being submitted more than once.
preventRepeatSubmission(form);
// Removes any '.questionflagsavebutton's, since we have JavaScript to toggle.
removeClass('.questionflagsavebutton', form);
// Scroll to the position indicated by scrollpos= in the URL, if it is there.
scrollToSavedPos(form);
}
};
/**
* Set the autocomplete off.
*
* @method autocompleteOff
* @param {object} form The form element.
*/
const autocompleteOff = (form) => {
form.setAttribute("autocomplete", "off");
};
/**
* Event handler to stop a question form being submitted more than once.
*
* @method preventRepeatSubmission
* @param {object} form The form element.
*/
const preventRepeatSubmission = (form) => {
form.addEventListener("submit", function() {
$(this).submit(function() {
return false;
});
return true;
});
};
/**
* Removes a class inside an element.
*
* @method removeClass
* @param {string} classname Class name.
* @param {object} form The form element.
*/
const removeClass = (classname, form) => {
form.querySelectorAll(classname).forEach(e => e.remove());
};
/**
* If there is a parameter like scrollpos=123 in the URL, scroll to that saved position.
* (Note: Moodle 4.0 and above do NOT support Internet Explorer 11 and below.)
*
* @method scrollToSavedPos
* @param {object} form The form element.
*/
const scrollToSavedPos = (form) => {
let matches = window.location.href.match(/^.*[?&]scrollpos=(\d*)(?:&|$|#).*$/, '$1');
if (matches) {
// DOMContentLoaded is the effective one here. I am leaving the immediate call to
// window.scrollTo in case it reduces flicker.
window.scrollTo(0, matches[1]);
form.addEventListener("DOMContentLoaded", () => {
window.scrollTo(0, matches[1]);
});
}
initQuestionEngineForm('#responseform');
};
+3 -3
View File
@@ -190,9 +190,9 @@ if (data_submitted() && confirm_sesskey()) {
question_engine::save_questions_usage_by_activity($quba);
$transaction->allow_commit();
$scrollpos = optional_param('scrollpos', '', PARAM_RAW);
if ($scrollpos !== '') {
$actionurl->param('scrollpos', (int) $scrollpos);
$mdlscrollto = optional_param('mdlscrollto', '', PARAM_RAW);
if ($mdlscrollto !== '') {
$actionurl->param('mdlscrollto', (int) $mdlscrollto);
}
redirect($actionurl);
}
+4 -4
View File
@@ -52,21 +52,21 @@ class qbehaviour_interactive_renderer extends qbehaviour_renderer {
return '';
}
$attributes = array(
$attributes = [
'type' => 'submit',
'id' => $qa->get_behaviour_field_name('tryagain'),
'name' => $qa->get_behaviour_field_name('tryagain'),
'value' => get_string('tryagain', 'qbehaviour_interactive'),
'class' => 'submit btn btn-secondary',
);
'data-savescrollposition' => 'true',
];
if ($options->readonly === qbehaviour_interactive::TRY_AGAIN_VISIBLE_READONLY) {
// This means the question really was rendered with read-only option.
$attributes['disabled'] = 'disabled';
}
$output = html_writer::empty_tag('input', $attributes);
if (empty($attributes['disabled'])) {
$this->page->requires->js_init_call('M.core_question_engine.init_submit_button',
array($attributes['id']));
$this->page->requires->js_call_amd('core_question/question_engine', 'initSubmitButton', [$attributes['id']]);
}
return $output;
}
+2 -2
View File
@@ -236,14 +236,14 @@ abstract class qbehaviour_renderer extends plugin_renderer_base {
'name' => $qa->get_behaviour_field_name('submit'),
'value' => get_string('check', 'question'),
'class' => 'submit btn btn-secondary',
'data-savescrollposition' => 'true',
);
if ($options->readonly) {
$attributes['disabled'] = 'disabled';
}
$output = html_writer::empty_tag('input', $attributes);
if (!$options->readonly) {
$this->page->requires->js_init_call('M.core_question_engine.init_submit_button',
array($attributes['id']));
$this->page->requires->js_call_amd('core_question/question_engine', 'initSubmitButton', [$attributes['id']]);
}
return $output;
}
+78 -91
View File
@@ -19,105 +19,87 @@
* @package moodlecore
* @subpackage questionengine
* @copyright 2008 The Open University
* @deprecated since Moodle 4.0
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Scroll manager is a class that help with saving the scroll positing when you
* click on an action icon, and then when the page is reloaded after processing
* the action, it scrolls you to exactly where you were. This is much nicer for
* the user.
*
* To use this in your code, you need to ensure that:
* 1. The button that triggers the action has to have a click event handler that
* calls M.core_scroll_manager.save_scroll_pos
* 2. The script that process the action has to grab the scrollpos parameter
* using $scrollpos = optional_param('scrollpos', 0, PARAM_INT);
* 3. After doing the processing, it must add ->param('scrollpos', $scrollpos)
* to the URL that it redirects to.
* 4. Finally, on the page that is reloaded (which should be the same as the one
* the user started on) you need to call M.core_scroll_manager.scroll_to_saved_pos
* on page load.
*/
M.core_scroll_manager = M.core_scroll_manager || {};
/**
* In the form that contains the element, set the value of the form field with
* name scrollpos to the current scroll position. If there is no element with
* that name, it creates a hidden form field wiht that name within the form.
* @param element the element in the form. Should be something that can be
* passed to Y.one.
*/
M.core_scroll_manager.save_scroll_pos = function(Y, element) {
if (typeof(element) == 'string') {
// Have to use getElementById here because element id can contain :.
element = Y.one(document.getElementById(element));
}
var form = element.ancestor('form');
if (!form) {
return;
}
var scrollpos = form.one('input[name=scrollpos]');
if (!scrollpos) {
scrollpos = form.appendChild(form.create('<input type="hidden" name="scrollpos" />'));
}
scrollpos.set('value', form.get('docScrollY'));
}
// TODO Remove the scroll manager and deprecation layer in 4.6 MDL-76685.
/* eslint-disable */
var loadedPromise = new Promise(function(resolve) {
require(['core/scroll_manager'], function(ScrollManager) {
var transitionLayer = {};
/**
* Event handler that can be used on a link. Assumes that the link already
* contains at least one URL parameter.
*/
M.core_scroll_manager.save_scroll_action = function(e) {
var link = e.target.ancestor('a[href]');
if (!link) {
M.core_scroll_manager.save_scroll_pos({}, e.target);
return;
}
link.set('href', link.get('href') + '&scrollpos=' + link.get('docScrollY'));
}
var deprecatedNotice = function(functionName, newFunctionName) {
window.console.error(
"The " + functionName + " function has been deprecated. " +
"Please use core/scroll_manager::" + newFunctionName + "() instead"
);
};
/**
* If there is a parameter like scrollpos=123 in the URL, scroll to that saved position.
* @deprecated since Moodle 4.0
* @see question\bank\qbank_previewquestion\amd\src
* @todo Final deprecation on Moodle 4.4 MDL-72438
*/
M.core_scroll_manager.scroll_to_saved_pos = function(Y) {
Y.log("The scroll_to_saved_pos function has been deprecated. " +
"Please use scrollToSavedPos() in qbank_preview/preview.js instead.", 'moodle-core-notification', 'warn');
transitionLayer.save_scroll_pos = function(Y, element) {
deprecatedNotice('save_scroll_pos', 'saveScrollPos');
ScrollManager.saveScrollPos(element);
};
var matches = window.location.href.match(/^.*[?&]scrollpos=(\d*)(?:&|$|#).*$/, '$1');
if (matches) {
// onDOMReady is the effective one here. I am leaving the immediate call to
// window.scrollTo in case it reduces flicker.
window.scrollTo(0, matches[1]);
Y.on('domready', function() { window.scrollTo(0, matches[1]); });
transitionLayer.scroll_to_saved_pos = function() {
deprecatedNotice('scroll_to_saved_pos', 'scrollToSavedPosition');
ScrollManager.scrollToSavedPosition();
};
// And the following horror is necessary to make it work in IE 8.
// Note that the class ie8 on body is only there in Moodle 2.0 and OU Moodle.
if (Y.one('body').hasClass('ie')) {
M.core_scroll_manager.force_ie_to_scroll(Y, matches[1])
}
}
}
M.core_scroll_manager = transitionLayer;
/**
* Beat IE into submission.
* @param targetpos the target scroll position.
*/
M.core_scroll_manager.force_ie_to_scroll = function(Y, targetpos) {
var hackcount = 25;
function do_scroll() {
window.scrollTo(0, targetpos);
hackcount -= 1;
if (hackcount > 0) {
setTimeout(do_scroll, 10);
}
}
Y.on('load', do_scroll, window);
resolve(transitionLayer);
});
});
var callPromisedFunction = function(functionName, args) {
loadedPromise.then(function(transitionLayer) {
transitionLayer[functionName].apply(null, args);
});
};
if (!M.core_scroll_manager.save_scroll_pos) {
// Note: This object is short lived.
// It only lives until the new scroll manager is loaded, at which point it is replaced.
/**
* In the form that contains the element, set the value of the form field with
* name scrollpos to the current scroll position. If there is no element with
* that name, it creates a hidden form field with that name within the form.
* @deprecated since Moodle 4.0
* @see core/scroll_manager
* @param element the element in the form. Should be something that can be
* passed to Y.one.
*/
M.core_scroll_manager.save_scroll_pos = function(Y, element) {
callPromisedFunction(M.core_scroll_manager.save_scroll_pos, [Y, element]);
};
/**
* Event handler that can be used on a link. Assumes that the link already
* contains at least one URL parameter.
* @deprecated since Moodle 4.0
* @see core/scroll_manager
*/
M.core_scroll_manager.save_scroll_action = function() {
Y.log("The scroll_to_saved_pos function has been deprecated. " +
"Please use initLinksScrollPos in core/scroll_manager instead.", 'moodle-core-notification', 'warn');
};
/**
* If there is a parameter like scrollpos=123 in the URL, scroll to that saved position.
* @deprecated since Moodle 4.0
* @see core/scroll_manager
* @todo Final deprecation on Moodle 4.4 MDL-72438
*/
M.core_scroll_manager.scroll_to_saved_pos = function(Y) {
callPromisedFunction(M.core_scroll_manager.scroll_to_saved_pos, Y);
};
}
/* eslint-enable */
M.core_question_engine = M.core_question_engine || {};
@@ -129,9 +111,14 @@ M.core_question_engine.questionformalreadysubmitted = false;
/**
* Initialise a question submit button. This saves the scroll position and
* sets the fragment on the form submit URL so the page reloads in the right place.
* @deprecated since Moodle 4.0
* @see core_question/question_engine
* @param button the id of the button in the HTML.
*/
M.core_question_engine.init_submit_button = function(Y, button) {
Y.log("The core_question_engine.init_submit_button function has been deprecated. " +
"Please use initSubmitButton in core_question/question_engine instead.", 'moodle-core-notification', 'warn');
require(['core_form/submit'], function(submit) {
submit.init(button);
});
@@ -160,12 +147,12 @@ M.core_question_engine.init_submit_button = function(Y, button) {
* @param Y the Yahoo object. Needs to have the DOM and Event modules loaded.
* @param form something that can be passed to Y.one, to find the form element.
* @deprecated since Moodle 4.0
* @see question\bank\qbank_previewquestion\amd\src
* @see core_question/question_engine
* @todo Final deprecation on Moodle 4.4 MDL-72438
*/
M.core_question_engine.init_form = function(Y, form) {
Y.log("The core_question_engine.init_form function has been deprecated. " +
"Please use setupQuestionForm() in qbank_preview/preview.js instead.", 'moodle-core-notification', 'warn');
"Please use init_form in core_question/question_engine instead.", 'moodle-core-notification', 'warn');
Y.one(form).setAttribute('autocomplete', 'off');
@@ -188,12 +175,12 @@ M.core_question_engine.init_form = function(Y, form) {
* @param e the form submit event.
* @param form the form element.
* @deprecated since Moodle 4.0
* @see question\bank\qbank_previewquestion\amd\src
* @see core_question/question_engine
* @todo Final deprecation on Moodle 4.4 MDL-72438
*/
M.core_question_engine.prevent_repeat_submission = function(e, Y) {
Y.log("The prevent_repeat_submission function has been deprecated. " +
"Please use preventRepeatSubmission in qbank_preview/preview.js instead.", 'moodle-core-notification', 'warn');
"Please use preventRepeatSubmission in core_question/question_engine instead.", 'moodle-core-notification', 'warn');
if (M.core_question_engine.questionformalreadysubmitted) {
e.halt();
+2 -2
View File
@@ -52,8 +52,8 @@ abstract class question_wizard_form extends moodleform {
$mform->addElement('hidden', 'returnurl');
$mform->setType('returnurl', PARAM_LOCALURL);
$mform->addElement('hidden', 'scrollpos');
$mform->setType('scrollpos', PARAM_INT);
$mform->addElement('hidden', 'mdlscrollto');
$mform->setType('mdlscrollto', PARAM_INT);
$mform->addElement('hidden', 'appendqnumstring');
$mform->setType('appendqnumstring', PARAM_ALPHA);
+5
View File
@@ -1,5 +1,10 @@
This files describes API changes for code that uses the question API.
=== 4.2 ===
1) The question/qengine.js has been deprecated. We create core_question/question_engine
and core/scroll_manager to replace it.
=== 4.1 ===
1) get_bulk_action_key() in core_question\local\bank\bulk_action_base class is deprecated and renamed to get_key().