Merge branch 'MDL-69164-master' of https://github.com/JBThong/moodle
This commit is contained in:
Vendored
+23
@@ -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
@@ -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);
|
||||
});
|
||||
};
|
||||
@@ -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());
|
||||
|
||||
@@ -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
@@ -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
@@ -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');
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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"}
|
||||
@@ -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';
|
||||
};
|
||||
@@ -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
@@ -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');
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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().
|
||||
|
||||
Reference in New Issue
Block a user