From 3c19cbbb18801c800b5d3ee48dd3a489a5275599 Mon Sep 17 00:00:00 2001 From: Thong Bui Date: Fri, 24 Sep 2021 09:10:58 +0700 Subject: [PATCH] MDL-72507 Quiz: Quiz auto-save does not detect uploaded files --- lib/form/amd/build/events.min.js | 2 +- lib/form/amd/build/events.min.js.map | 2 +- lib/form/amd/src/events.js | 44 +++++++++++++++---- lib/form/filemanager.js | 12 +++++ .../moodle-mod_quiz-autosave-debug.js | 4 ++ .../moodle-mod_quiz-autosave-min.js | 2 +- .../moodle-mod_quiz-autosave.js | 4 ++ mod/quiz/yui/src/autosave/js/autosave.js | 4 ++ 8 files changed, 63 insertions(+), 11 deletions(-) diff --git a/lib/form/amd/build/events.min.js b/lib/form/amd/build/events.min.js index cd603c6172a..0da5bc9f50e 100644 --- a/lib/form/amd/build/events.min.js +++ b/lib/form/amd/build/events.min.js @@ -1,2 +1,2 @@ -define ("core_form/events",["exports","core/str","core/event_dispatcher","jquery","core/yui"],function(a,b,c,d,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.types=a.triggerUploadCompleted=a.triggerUploadStarted=a.notifyUploadCompleted=a.notifyUploadStarted=a.notifyFieldValidationFailure=a.notifyFormSubmittedByJavascript=a.notifyFormError=a.eventTypes=void 0;d=g(d);f=g(f);function g(a){return a&&a.__esModule?a:{default:a}}var h;(0,b.get_string)("changesmadereallygoaway","moodle").then(function(a){h=a;return a}).catch();var i=function(a){if(a){a.returnValue=h}},j={formError:"core_form/error",formSubmittedByJavascript:"core_form/submittedByJavascript",formFieldValidationFailed:"core_form/fieldValidationFailed",uploadStarted:"core_form/uploadStarted",uploadCompleted:"core_form/uploadCompleted"};a.eventTypes=j;a.notifyFormError=function notifyFormError(a){return(0,c.dispatchEvent)(j.formError,{},a)};a.notifyFormSubmittedByJavascript=function notifyFormSubmittedByJavascript(a){var b=1.\n\n/**\n * Javascript events for the `core_form` subsystem.\n *\n * @module core_form/events\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.10\n *\n * @example Example of listening to a form event.\n * import {eventTypes as formEventTypes} from 'core_form/events';\n *\n * document.addEventListener(formEventTypes.formSubmittedByJavascript, e => {\n * window.console.log(e.target); // The form that was submitted.\n * window.console.log(e.detail.skipValidation); // Whether form validation was skipped.\n * });\n */\n\nimport {get_string as getString} from 'core/str';\nimport {dispatchEvent} from 'core/event_dispatcher';\n\nlet changesMadeString;\ngetString('changesmadereallygoaway', 'moodle').then(string => {\n changesMadeString = string;\n return string;\n}).catch();\n\n/**\n * Prevent user navigate away when upload progress still running.\n * @param {Event} e The event\n */\nconst changesMadeCheck = e => {\n if (e) {\n e.returnValue = changesMadeString;\n }\n};\n\n/**\n * Events for `core_form`.\n *\n * @constant\n * @property {String} formError See {@link event:formError}\n * @property {String} formFieldValidationFailed See {@link event:formFieldValidationFailed}\n * @property {String} formSubmittedByJavascript See {@link event:formSubmittedByJavascript}\n */\nexport const eventTypes = {\n /**\n * An event triggered when a form contains an error\n *\n * @event formError\n * @type {CustomEvent}\n * @property {HTMLElement} target The form field which errored\n */\n formError: 'core_form/error',\n\n /**\n * An event triggered when an mform is about to be submitted via javascript.\n *\n * @event formSubmittedByJavascript\n * @type {CustomEvent}\n * @property {HTMLElement} target The form that was submitted\n * @property {object} detail\n * @property {boolean} detail.skipValidation Whether the form was submitted without validation (i.e. via a Cancel button)\n * @property {boolean} detail.fallbackHandled Whether the legacy YUI event has been handled\n */\n formSubmittedByJavascript: 'core_form/submittedByJavascript',\n\n /**\n * An event triggered upon form field validation failure.\n *\n * @event formFieldValidationFailed\n * @type {CustomEvent}\n * @property {HTMLElement} target The field that failed validation\n * @property {object} detail\n * @property {String} detail.message The message displayed upon failure\n */\n formFieldValidationFailed: 'core_form/fieldValidationFailed',\n\n /**\n * An event triggered when an upload is started\n *\n * @event uploadStarted\n * @type {CustomEvent}\n * @property {HTMLElement} target The location where the upload began\n */\n uploadStarted: 'core_form/uploadStarted',\n\n /**\n * An event triggered when an upload completes\n *\n * @event uploadCompleted\n * @type {CustomEvent}\n * @property {HTMLElement} target The location where the upload completd\n */\n uploadCompleted: 'core_form/uploadCompleted',\n};\n\n// These are only imported for legacy.\nimport jQuery from 'jquery';\nimport Y from 'core/yui';\n\n/**\n * Trigger an event to indicate that a form field contained an error.\n *\n * @method notifyFormError\n * @param {HTMLElement} field The form field causing the error\n * @returns {CustomEvent}\n * @fires formError\n */\nexport const notifyFormError = field => dispatchEvent(eventTypes.formError, {}, field);\n\n/**\n * Trigger an event to indiciate that a form was submitted by Javascript.\n *\n * @method\n * @param {HTMLElement} form The form that was submitted\n * @param {Boolean} skipValidation Submit the form without validation. E.g. \"Cancel\".\n * @param {Boolean} fallbackHandled The legacy YUI event has been handled\n * @returns {CustomEvent}\n * @fires formSubmittedByJavascript\n */\nexport const notifyFormSubmittedByJavascript = (form, skipValidation = false, fallbackHandled = false) => {\n if (skipValidation) {\n window.skipClientValidation = true;\n }\n\n const customEvent = dispatchEvent(\n eventTypes.formSubmittedByJavascript,\n {\n skipValidation,\n fallbackHandled,\n },\n form\n );\n\n if (skipValidation) {\n window.skipClientValidation = false;\n }\n\n return customEvent;\n};\n\n/**\n * Trigger an event to indicate that a form field contained an error.\n *\n * @method notifyFieldValidationFailure\n * @param {HTMLElement} field The field which failed validation\n * @param {String} message The message displayed\n * @returns {CustomEvent}\n * @fires formFieldValidationFailed\n */\nexport const notifyFieldValidationFailure = (field, message) => dispatchEvent(\n eventTypes.formFieldValidationFailed,\n {\n message,\n },\n field,\n {\n cancelable: true\n }\n);\n\n/**\n * Trigger an event to indicate that an upload was started.\n *\n * @method\n * @param {String} elementId The element which was uploaded to\n * @returns {CustomEvent}\n * @fires uploadStarted\n */\nexport const notifyUploadStarted = elementId => {\n // Add an additional check for changes made.\n window.addEventListener('beforeunload', changesMadeCheck);\n\n return dispatchEvent(\n eventTypes.uploadStarted,\n {},\n document.getElementById(elementId),\n {\n bubbles: true,\n cancellable: false,\n }\n );\n};\n\n/**\n * Trigger an event to indicate that an upload was completed.\n *\n * @method\n * @param {String} elementId The element which was uploaded to\n * @returns {CustomEvent}\n * @fires uploadCompleted\n */\nexport const notifyUploadCompleted = elementId => {\n // Remove the additional check for changes made.\n window.removeEventListener('beforeunload', changesMadeCheck);\n\n return dispatchEvent(\n eventTypes.uploadCompleted,\n {},\n document.getElementById(elementId),\n {\n bubbles: true,\n cancellable: false,\n }\n );\n};\n\n/**\n * Trigger upload start event.\n *\n * @method\n * @param {String} elementId\n * @returns {CustomEvent}\n * @fires uploadStarted\n * @deprecated Since Moodle 4.0 See {@link module:core_form/events.notifyUploadStarted notifyUploadStarted}\n */\nexport const triggerUploadStarted = notifyUploadStarted;\n\n/**\n * Trigger upload complete event.\n *\n * @method\n * @param {String} elementId\n * @returns {CustomEvent}\n * @fires uploadCompleted\n * @deprecated Since Moodle 4.0 See {@link module:core_form/events.notifyUploadCompleted notifyUploadCompleted}\n */\nexport const triggerUploadCompleted = notifyUploadCompleted;\n\n/**\n * List of the events.\n *\n * @deprecated since Moodle 4.0. See {@link module:core_form/events.eventTypes eventTypes} instead.\n **/\nexport const types = {\n uploadStarted: 'core_form/uploadStarted',\n uploadCompleted: 'core_form/uploadCompleted',\n};\n\nlet legacyEventsRegistered = false;\nif (!legacyEventsRegistered) {\n // The following event triggers are legacy and will be removed in the future.\n // The following approach provides a backwards-compatability layer for the new events.\n // Code should be updated to make use of native events.\n Y.use('event', 'moodle-core-event', () => {\n\n // Watch for the new native formError event, and trigger the legacy YUI event.\n document.addEventListener(eventTypes.formError, e => {\n const element = Y.one(e.target);\n const formElement = Y.one(e.target.closest('form'));\n\n Y.Global.fire(\n M.core.globalEvents.FORM_ERROR,\n {\n formid: formElement.generateID(),\n elementid: element.generateID(),\n }\n );\n });\n\n // Watch for the new native formSubmittedByJavascript event, and trigger the legacy YUI event.\n document.addEventListener(eventTypes.formSubmittedByJavascript, e => {\n if (e.detail.fallbackHandled) {\n // This event was originally generated by a YUI event.\n // Do not generate another as this will recurse.\n return;\n }\n\n if (e.skipValidation) {\n window.skipClientValidation = true;\n }\n\n // Trigger the legacy YUI event.\n const form = Y.one(e.target);\n form.fire(\n M.core.event.FORM_SUBMIT_AJAX,\n {\n currentTarget: form,\n fallbackHandled: true,\n }\n );\n\n if (e.skipValidation) {\n window.skipClientValidation = false;\n }\n });\n });\n\n // Watch for the new native formFieldValidationFailed event, and trigger the legacy jQuery event.\n document.addEventListener(eventTypes.formFieldValidationFailed, e => {\n // Note: The \"core_form-field-validation\" event is hard-coded in core/event.\n // This is not included to prevent cyclic module dependencies.\n const legacyEvent = jQuery.Event(\"core_form-field-validation\");\n\n jQuery(e.target).trigger(legacyEvent, e.detail.message);\n });\n\n legacyEventsRegistered = true;\n}\n"],"file":"events.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/events.js"],"names":["changesMadeString","then","string","catch","changesMadeCheck","e","returnValue","eventTypes","formError","formSubmittedByJavascript","formFieldValidationFailed","uploadStarted","uploadCompleted","uploadChanged","notifyFormError","field","notifyFormSubmittedByJavascript","form","skipValidation","fallbackHandled","window","skipClientValidation","customEvent","notifyFieldValidationFailure","message","cancelable","notifyUploadStarted","elementId","addEventListener","document","getElementById","bubbles","cancellable","notifyUploadCompleted","removeEventListener","legacyEventsRegistered","Y","use","element","one","target","formElement","closest","Global","fire","M","core","globalEvents","FORM_ERROR","formid","generateID","elementid","detail","event","FORM_SUBMIT_AJAX","currentTarget","legacyEvent","jQuery","Event","trigger","notifyUploadChanged"],"mappings":"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","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 .\n\n/**\n * Javascript events for the `core_form` subsystem.\n *\n * @module core_form/events\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.10\n *\n * @example Example of listening to a form event.\n * import {eventTypes as formEventTypes} from 'core_form/events';\n *\n * document.addEventListener(formEventTypes.formSubmittedByJavascript, e => {\n * window.console.log(e.target); // The form that was submitted.\n * window.console.log(e.detail.skipValidation); // Whether form validation was skipped.\n * });\n */\n\nimport {get_string as getString} from 'core/str';\nimport {dispatchEvent} from 'core/event_dispatcher';\n\nlet changesMadeString;\ngetString('changesmadereallygoaway', 'moodle').then(string => {\n changesMadeString = string;\n return string;\n}).catch();\n\n/**\n * Prevent user navigate away when upload progress still running.\n * @param {Event} e The event\n */\nconst changesMadeCheck = e => {\n if (e) {\n e.returnValue = changesMadeString;\n }\n};\n\n/**\n * Events for `core_form`.\n *\n * @constant\n * @property {String} formError See {@link event:core_form/error}\n * @property {String} formFieldValidationFailed See {@link event:core_form/fieldValidationFailed}\n * @property {String} formSubmittedByJavascript See {@link event:core_form/submittedByJavascript}\n * @property {String} uploadChanged See {@link event:core_form/uploadChanged}\n */\nexport const eventTypes = {\n /**\n * An event triggered when a form contains an error\n *\n * @event formError\n * @type {CustomEvent}\n * @property {HTMLElement} target The form field which errored\n */\n formError: 'core_form/error',\n\n /**\n * An event triggered when an mform is about to be submitted via javascript.\n *\n * @event core_form/submittedByJavascript\n * @type {CustomEvent}\n * @property {HTMLElement} target The form that was submitted\n * @property {object} detail\n * @property {boolean} detail.skipValidation Whether the form was submitted without validation (i.e. via a Cancel button)\n * @property {boolean} detail.fallbackHandled Whether the legacy YUI event has been handled\n */\n formSubmittedByJavascript: 'core_form/submittedByJavascript',\n\n /**\n * An event triggered upon form field validation failure.\n *\n * @event core_form/fieldValidationFailed\n * @type {CustomEvent}\n * @property {HTMLElement} target The field that failed validation\n * @property {object} detail\n * @property {String} detail.message The message displayed upon failure\n */\n formFieldValidationFailed: 'core_form/fieldValidationFailed',\n\n /**\n * An event triggered when an upload is started\n *\n * @event core_form/uploadStarted\n * @type {CustomEvent}\n * @property {HTMLElement} target The location where the upload began\n */\n uploadStarted: 'core_form/uploadStarted',\n\n /**\n * An event triggered when an upload completes\n *\n * @event core_form/uploadCompleted\n * @type {CustomEvent}\n * @property {HTMLElement} target The location where the upload completed\n */\n uploadCompleted: 'core_form/uploadCompleted',\n\n /**\n * An event triggered when a file upload field has been changed.\n *\n * @event core_form/uploadChanged\n * @type {CustomEvent}\n * @property {HTMLElement} target The form field which was changed\n */\n uploadChanged: 'core_form/uploadChanged',\n};\n\n// These are only imported for legacy.\nimport jQuery from 'jquery';\nimport Y from 'core/yui';\n\n/**\n * Trigger an event to indicate that a form field contained an error.\n *\n * @method notifyFormError\n * @param {HTMLElement} field The form field causing the error\n * @returns {CustomEvent}\n * @fires formError\n */\nexport const notifyFormError = field => dispatchEvent(eventTypes.formError, {}, field);\n\n/**\n * Trigger an event to indiciate that a form was submitted by Javascript.\n *\n * @method\n * @param {HTMLElement} form The form that was submitted\n * @param {Boolean} skipValidation Submit the form without validation. E.g. \"Cancel\".\n * @param {Boolean} fallbackHandled The legacy YUI event has been handled\n * @returns {CustomEvent}\n * @fires formSubmittedByJavascript\n */\nexport const notifyFormSubmittedByJavascript = (form, skipValidation = false, fallbackHandled = false) => {\n if (skipValidation) {\n window.skipClientValidation = true;\n }\n\n const customEvent = dispatchEvent(\n eventTypes.formSubmittedByJavascript,\n {\n skipValidation,\n fallbackHandled,\n },\n form\n );\n\n if (skipValidation) {\n window.skipClientValidation = false;\n }\n\n return customEvent;\n};\n\n/**\n * Trigger an event to indicate that a form field contained an error.\n *\n * @method notifyFieldValidationFailure\n * @param {HTMLElement} field The field which failed validation\n * @param {String} message The message displayed\n * @returns {CustomEvent}\n * @fires formFieldValidationFailed\n */\nexport const notifyFieldValidationFailure = (field, message) => dispatchEvent(\n eventTypes.formFieldValidationFailed,\n {\n message,\n },\n field,\n {\n cancelable: true\n }\n);\n\n/**\n * Trigger an event to indicate that an upload was started.\n *\n * @method\n * @param {String} elementId The element which was uploaded to\n * @returns {CustomEvent}\n * @fires uploadStarted\n */\nexport const notifyUploadStarted = elementId => {\n // Add an additional check for changes made.\n window.addEventListener('beforeunload', changesMadeCheck);\n\n return dispatchEvent(\n eventTypes.uploadStarted,\n {},\n document.getElementById(elementId),\n {\n bubbles: true,\n cancellable: false,\n }\n );\n};\n\n/**\n * Trigger an event to indicate that an upload was completed.\n *\n * @method\n * @param {String} elementId The element which was uploaded to\n * @returns {CustomEvent}\n * @fires uploadCompleted\n */\nexport const notifyUploadCompleted = elementId => {\n // Remove the additional check for changes made.\n window.removeEventListener('beforeunload', changesMadeCheck);\n\n return dispatchEvent(\n eventTypes.uploadCompleted,\n {},\n document.getElementById(elementId),\n {\n bubbles: true,\n cancellable: false,\n }\n );\n};\n\n/**\n * Trigger upload start event.\n *\n * @method\n * @param {String} elementId\n * @returns {CustomEvent}\n * @fires uploadStarted\n * @deprecated Since Moodle 4.0 See {@link module:core_form/events.notifyUploadStarted notifyUploadStarted}\n */\nexport const triggerUploadStarted = notifyUploadStarted;\n\n/**\n * Trigger upload complete event.\n *\n * @method\n * @param {String} elementId\n * @returns {CustomEvent}\n * @fires uploadCompleted\n * @deprecated Since Moodle 4.0 See {@link module:core_form/events.notifyUploadCompleted notifyUploadCompleted}\n */\nexport const triggerUploadCompleted = notifyUploadCompleted;\n\n/**\n * List of the events.\n *\n * @deprecated since Moodle 4.0. See {@link module:core_form/events.eventTypes eventTypes} instead.\n **/\nexport const types = {\n uploadStarted: 'core_form/uploadStarted',\n uploadCompleted: 'core_form/uploadCompleted',\n};\n\nlet legacyEventsRegistered = false;\nif (!legacyEventsRegistered) {\n // The following event triggers are legacy and will be removed in the future.\n // The following approach provides a backwards-compatability layer for the new events.\n // Code should be updated to make use of native events.\n Y.use('event', 'moodle-core-event', () => {\n\n // Watch for the new native formError event, and trigger the legacy YUI event.\n document.addEventListener(eventTypes.formError, e => {\n const element = Y.one(e.target);\n const formElement = Y.one(e.target.closest('form'));\n\n Y.Global.fire(\n M.core.globalEvents.FORM_ERROR,\n {\n formid: formElement.generateID(),\n elementid: element.generateID(),\n }\n );\n });\n\n // Watch for the new native formSubmittedByJavascript event, and trigger the legacy YUI event.\n document.addEventListener(eventTypes.formSubmittedByJavascript, e => {\n if (e.detail.fallbackHandled) {\n // This event was originally generated by a YUI event.\n // Do not generate another as this will recurse.\n return;\n }\n\n if (e.skipValidation) {\n window.skipClientValidation = true;\n }\n\n // Trigger the legacy YUI event.\n const form = Y.one(e.target);\n form.fire(\n M.core.event.FORM_SUBMIT_AJAX,\n {\n currentTarget: form,\n fallbackHandled: true,\n }\n );\n\n if (e.skipValidation) {\n window.skipClientValidation = false;\n }\n });\n });\n\n // Watch for the new native formFieldValidationFailed event, and trigger the legacy jQuery event.\n document.addEventListener(eventTypes.formFieldValidationFailed, e => {\n // Note: The \"core_form-field-validation\" event is hard-coded in core/event.\n // This is not included to prevent cyclic module dependencies.\n const legacyEvent = jQuery.Event(\"core_form-field-validation\");\n\n jQuery(e.target).trigger(legacyEvent, e.detail.message);\n });\n\n legacyEventsRegistered = true;\n}\n\n/**\n * Trigger an event to notify the file upload field has been changed.\n *\n * @method\n * @param {string} elementId The element which was changed\n * @returns {CustomEvent}\n * @fires uploadChanged\n */\nexport const notifyUploadChanged = elementId => dispatchEvent(\n eventTypes.uploadChanged,\n {},\n document.getElementById(elementId),\n {\n bubbles: true,\n cancellable: false,\n }\n);\n"],"file":"events.min.js"} \ No newline at end of file diff --git a/lib/form/amd/src/events.js b/lib/form/amd/src/events.js index fc2658ea104..cbe92b8d2d4 100644 --- a/lib/form/amd/src/events.js +++ b/lib/form/amd/src/events.js @@ -53,9 +53,10 @@ const changesMadeCheck = e => { * Events for `core_form`. * * @constant - * @property {String} formError See {@link event:formError} - * @property {String} formFieldValidationFailed See {@link event:formFieldValidationFailed} - * @property {String} formSubmittedByJavascript See {@link event:formSubmittedByJavascript} + * @property {String} formError See {@link event:core_form/error} + * @property {String} formFieldValidationFailed See {@link event:core_form/fieldValidationFailed} + * @property {String} formSubmittedByJavascript See {@link event:core_form/submittedByJavascript} + * @property {String} uploadChanged See {@link event:core_form/uploadChanged} */ export const eventTypes = { /** @@ -70,7 +71,7 @@ export const eventTypes = { /** * An event triggered when an mform is about to be submitted via javascript. * - * @event formSubmittedByJavascript + * @event core_form/submittedByJavascript * @type {CustomEvent} * @property {HTMLElement} target The form that was submitted * @property {object} detail @@ -82,7 +83,7 @@ export const eventTypes = { /** * An event triggered upon form field validation failure. * - * @event formFieldValidationFailed + * @event core_form/fieldValidationFailed * @type {CustomEvent} * @property {HTMLElement} target The field that failed validation * @property {object} detail @@ -93,7 +94,7 @@ export const eventTypes = { /** * An event triggered when an upload is started * - * @event uploadStarted + * @event core_form/uploadStarted * @type {CustomEvent} * @property {HTMLElement} target The location where the upload began */ @@ -102,11 +103,20 @@ export const eventTypes = { /** * An event triggered when an upload completes * - * @event uploadCompleted + * @event core_form/uploadCompleted * @type {CustomEvent} - * @property {HTMLElement} target The location where the upload completd + * @property {HTMLElement} target The location where the upload completed */ uploadCompleted: 'core_form/uploadCompleted', + + /** + * An event triggered when a file upload field has been changed. + * + * @event core_form/uploadChanged + * @type {CustomEvent} + * @property {HTMLElement} target The form field which was changed + */ + uploadChanged: 'core_form/uploadChanged', }; // These are only imported for legacy. @@ -312,3 +322,21 @@ if (!legacyEventsRegistered) { legacyEventsRegistered = true; } + +/** + * Trigger an event to notify the file upload field has been changed. + * + * @method + * @param {string} elementId The element which was changed + * @returns {CustomEvent} + * @fires uploadChanged + */ +export const notifyUploadChanged = elementId => dispatchEvent( + eventTypes.uploadChanged, + {}, + document.getElementById(elementId), + { + bubbles: true, + cancellable: false, + } +); diff --git a/lib/form/filemanager.js b/lib/form/filemanager.js index 19199bf8bc0..ee94df516c6 100644 --- a/lib/form/filemanager.js +++ b/lib/form/filemanager.js @@ -209,6 +209,10 @@ M.form_filemanager.init = function(Y, options) { this.check_buttons(); this.refresh(this.currentpath); M.form_filemanager.formChangeChecker.markFormChangedFromNode(this.filemanager.getDOMNode()); + + require(['core_form/events'], function(FormEvent) { + FormEvent.notifyUploadChanged(this.filemanager.get('id')); + }.bind(this)); }, check_buttons: function() { if (this.filecount>0) { @@ -466,6 +470,10 @@ M.form_filemanager.init = function(Y, options) { args.scope.refresh(obj[0], {action: 'delete'}); } M.form_filemanager.formChangeChecker.markFormChangedFromNode(this.scope.filemanager.getDOMNode()); + + require(['core_form/events'], function(FormEvent) { + FormEvent.notifyUploadChanged(this.scope.filemanager.get('id')); + }.bind(this)); } }); }; @@ -1001,6 +1009,10 @@ M.form_filemanager.init = function(Y, options) { args.scope.filecount--; args.scope.refresh(obj.filepath, {action: 'delete'}); M.form_filemanager.formChangeChecker.markFormChangedFromNode(this.scope.filemanager.getDOMNode()); + + require(['core_form/events'], function(FormEvent) { + FormEvent.notifyUploadChanged(this.scope.filemanager.get('id')); + }.bind(this)); } }); }; diff --git a/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-debug.js b/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-debug.js index 403fb96fd5d..cfc5a49aa03 100644 --- a/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-debug.js +++ b/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-debug.js @@ -204,6 +204,10 @@ M.mod_quiz.autosave = { this.form.delegate('change', this.value_changed, this.SELECTORS.CHANGE_ELEMENTS, this); this.form.on('submit', this.stop_autosaving, this); + require(['core_form/events'], function(FormEvent) { + window.addEventListener(FormEvent.eventTypes.uploadChanged, this.value_changed.bind(this)); + }.bind(this)); + this.init_tinymce(this.TINYMCE_DETECTION_REPEATS); this.save_hidden_field_values(); diff --git a/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-min.js b/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-min.js index 393c2d90ea0..4a29c25dbdb 100644 --- a/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-min.js +++ b/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-min.js @@ -1 +1 @@ -YUI.add("moodle-mod_quiz-autosave",function(s,e){M.mod_quiz=M.mod_quiz||{},M.mod_quiz.autosave={TINYMCE_DETECTION_DELAY:500,TINYMCE_DETECTION_REPEATS:20,WATCH_HIDDEN_DELAY:1e3,FAILURES_BEFORE_NOTIFY:1,FIRST_SUCCESSFUL_SAVE:-1,SELECTORS:{QUIZ_FORM:"#responseform",VALUE_CHANGE_ELEMENTS:'input, textarea, [contenteditable="true"]',CHANGE_ELEMENTS:"input, select",HIDDEN_INPUTS:"input[type=hidden]",CONNECTION_ERROR:"#connection-error",CONNECTION_OK:"#connection-ok"},AUTOSAVE_HANDLER:M.cfg.wwwroot+"/mod/quiz/autosave.ajax.php",delay:12e4,form:null,dirty:!1,delay_timer:null,save_transaction:null,savefailures:0,editor_change_handler:null,hidden_field_values:{},init:function(e){this.form=s.one(this.SELECTORS.QUIZ_FORM),this.form&&(this.delay=1e3*e,this.form.delegate("valuechange",this.value_changed,this.SELECTORS.VALUE_CHANGE_ELEMENTS,this),this.form.delegate("change",this.value_changed,this.SELECTORS.CHANGE_ELEMENTS,this),this.form.on("submit",this.stop_autosaving,this),this.init_tinymce(this.TINYMCE_DETECTION_REPEATS),this.save_hidden_field_values(),this.watch_hidden_fields())},save_hidden_field_values:function(){this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(e){var t=e.get("name");t&&(this.hidden_field_values[t]=e.get("value"))},this)},watch_hidden_fields:function(){this.detect_hidden_field_changes(),s.later(this.WATCH_HIDDEN_DELAY,this,this.watch_hidden_fields)},detect_hidden_field_changes:function(){this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(e){var t=e.get("name"),i=e.get("value");t&&(t in this.hidden_field_values&&i===this.hidden_field_values[t]||(this.hidden_field_values[t]=i,this.value_changed({target:e})))},this)},init_tinymce:function(e){"undefined"!=typeof window.tinyMCE?(this.editor_change_handler=s.bind(this.editor_changed,this),window.tinyMCE.onAddEditor.add(s.bind(this.init_tinymce_editor,this))):0M.mod_quiz.timer.endtime},stop_autosaving:function(){this.cancel_delay(),this.delay_timer=!0,this.save_transaction&&this.save_transaction.abort()}}},"@VERSION@",{requires:["base","node","event","event-valuechange","node-event-delegate","io-form"]}); \ No newline at end of file +YUI.add("moodle-mod_quiz-autosave",function(s,e){M.mod_quiz=M.mod_quiz||{},M.mod_quiz.autosave={TINYMCE_DETECTION_DELAY:500,TINYMCE_DETECTION_REPEATS:20,WATCH_HIDDEN_DELAY:1e3,FAILURES_BEFORE_NOTIFY:1,FIRST_SUCCESSFUL_SAVE:-1,SELECTORS:{QUIZ_FORM:"#responseform",VALUE_CHANGE_ELEMENTS:'input, textarea, [contenteditable="true"]',CHANGE_ELEMENTS:"input, select",HIDDEN_INPUTS:"input[type=hidden]",CONNECTION_ERROR:"#connection-error",CONNECTION_OK:"#connection-ok"},AUTOSAVE_HANDLER:M.cfg.wwwroot+"/mod/quiz/autosave.ajax.php",delay:12e4,form:null,dirty:!1,delay_timer:null,save_transaction:null,savefailures:0,editor_change_handler:null,hidden_field_values:{},init:function(e){this.form=s.one(this.SELECTORS.QUIZ_FORM),this.form&&(this.delay=1e3*e,this.form.delegate("valuechange",this.value_changed,this.SELECTORS.VALUE_CHANGE_ELEMENTS,this),this.form.delegate("change",this.value_changed,this.SELECTORS.CHANGE_ELEMENTS,this),this.form.on("submit",this.stop_autosaving,this),require(["core_form/events"],function(e){window.addEventListener(e.eventTypes.uploadChanged,this.value_changed.bind(this))}.bind(this)),this.init_tinymce(this.TINYMCE_DETECTION_REPEATS),this.save_hidden_field_values(),this.watch_hidden_fields())},save_hidden_field_values:function(){this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(e){var t=e.get("name");t&&(this.hidden_field_values[t]=e.get("value"))},this)},watch_hidden_fields:function(){this.detect_hidden_field_changes(),s.later(this.WATCH_HIDDEN_DELAY,this,this.watch_hidden_fields)},detect_hidden_field_changes:function(){this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(e){var t=e.get("name"),i=e.get("value");t&&(t in this.hidden_field_values&&i===this.hidden_field_values[t]||(this.hidden_field_values[t]=i,this.value_changed({target:e})))},this)},init_tinymce:function(e){"undefined"!=typeof window.tinyMCE?(this.editor_change_handler=s.bind(this.editor_changed,this),window.tinyMCE.onAddEditor.add(s.bind(this.init_tinymce_editor,this))):0M.mod_quiz.timer.endtime},stop_autosaving:function(){this.cancel_delay(),this.delay_timer=!0,this.save_transaction&&this.save_transaction.abort()}}},"@VERSION@",{requires:["base","node","event","event-valuechange","node-event-delegate","io-form"]}); \ No newline at end of file diff --git a/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave.js b/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave.js index 674d946a173..88ed5e72511 100644 --- a/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave.js +++ b/mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave.js @@ -203,6 +203,10 @@ M.mod_quiz.autosave = { this.form.delegate('change', this.value_changed, this.SELECTORS.CHANGE_ELEMENTS, this); this.form.on('submit', this.stop_autosaving, this); + require(['core_form/events'], function(FormEvent) { + window.addEventListener(FormEvent.eventTypes.uploadChanged, this.value_changed.bind(this)); + }.bind(this)); + this.init_tinymce(this.TINYMCE_DETECTION_REPEATS); this.save_hidden_field_values(); diff --git a/mod/quiz/yui/src/autosave/js/autosave.js b/mod/quiz/yui/src/autosave/js/autosave.js index 9633f7310e5..f64bbd40524 100644 --- a/mod/quiz/yui/src/autosave/js/autosave.js +++ b/mod/quiz/yui/src/autosave/js/autosave.js @@ -202,6 +202,10 @@ M.mod_quiz.autosave = { this.form.delegate('change', this.value_changed, this.SELECTORS.CHANGE_ELEMENTS, this); this.form.on('submit', this.stop_autosaving, this); + require(['core_form/events'], function(FormEvent) { + window.addEventListener(FormEvent.eventTypes.uploadChanged, this.value_changed.bind(this)); + }.bind(this)); + this.init_tinymce(this.TINYMCE_DETECTION_REPEATS); this.save_hidden_field_values();