diff --git a/lib/amd/build/utils.min.js b/lib/amd/build/utils.min.js
new file mode 100644
index 00000000000..ba55c9c9e28
--- /dev/null
+++ b/lib/amd/build/utils.min.js
@@ -0,0 +1,2 @@
+define ("core/utils",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.debounce=a.throttle=void 0;a.throttle=function throttle(a,b){var c=!1,d=null,e=function(){for(var f=arguments.length,g=Array(f),h=0;h.\n\n/**\n * Utility functions.\n *\n * @copyright 2019 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n /**\n * Create a wrapper function to throttle the execution of the given\n * function to at most once every specified period.\n *\n * If the function is attempted to be executed while it's in cooldown\n * (during the wait period) then it'll immediately execute again as\n * soon as the cooldown is over.\n *\n * @param {Function} func The function to throttle\n * @param {Number} wait The number of milliseconds to wait between executions\n * @return {Function}\n */\nexport const throttle = (func, wait) => {\n let onCooldown = false;\n let runAgain = null;\n const run = function(...args) {\n if (runAgain === null) {\n // This is the first time the function has been called.\n runAgain = false;\n } else {\n // This function has been called a second time during the wait period\n // so re-run it once the wait period is over.\n runAgain = true;\n }\n\n if (onCooldown) {\n // Function has already run for this wait period.\n return;\n }\n\n func.apply(this, args);\n onCooldown = true;\n\n setTimeout(() => {\n const recurse = runAgain;\n onCooldown = false;\n runAgain = null;\n\n if (recurse) {\n run(args);\n }\n }, wait);\n };\n\n return run;\n};\n\n/**\n * Create a wrapper function to debounce the execution of the given\n * function. Each attempt to execute the function will reset the cooldown\n * period.\n *\n * @param {Function} func The function to debounce\n * @param {Number} wait The number of milliseconds to wait after the final attempt to execute\n * @return {Function}\n */\nexport const debounce = (func, wait) => {\n let timeout = null;\n return function(...args) {\n clearTimeout(timeout);\n timeout = setTimeout(() => {\n func.apply(this, args);\n }, wait);\n };\n};\n"],"file":"utils.min.js"}
\ No newline at end of file
diff --git a/lib/amd/src/utils.js b/lib/amd/src/utils.js
new file mode 100644
index 00000000000..933e99fb413
--- /dev/null
+++ b/lib/amd/src/utils.js
@@ -0,0 +1,87 @@
+// 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 .
+
+/**
+ * Utility functions.
+ *
+ * @copyright 2019 Ryan Wyllie
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+ /**
+ * Create a wrapper function to throttle the execution of the given
+ * function to at most once every specified period.
+ *
+ * If the function is attempted to be executed while it's in cooldown
+ * (during the wait period) then it'll immediately execute again as
+ * soon as the cooldown is over.
+ *
+ * @param {Function} func The function to throttle
+ * @param {Number} wait The number of milliseconds to wait between executions
+ * @return {Function}
+ */
+export const throttle = (func, wait) => {
+ let onCooldown = false;
+ let runAgain = null;
+ const run = function(...args) {
+ if (runAgain === null) {
+ // This is the first time the function has been called.
+ runAgain = false;
+ } else {
+ // This function has been called a second time during the wait period
+ // so re-run it once the wait period is over.
+ runAgain = true;
+ }
+
+ if (onCooldown) {
+ // Function has already run for this wait period.
+ return;
+ }
+
+ func.apply(this, args);
+ onCooldown = true;
+
+ setTimeout(() => {
+ const recurse = runAgain;
+ onCooldown = false;
+ runAgain = null;
+
+ if (recurse) {
+ run(args);
+ }
+ }, wait);
+ };
+
+ return run;
+};
+
+/**
+ * Create a wrapper function to debounce the execution of the given
+ * function. Each attempt to execute the function will reset the cooldown
+ * period.
+ *
+ * @param {Function} func The function to debounce
+ * @param {Number} wait The number of milliseconds to wait after the final attempt to execute
+ * @return {Function}
+ */
+export const debounce = (func, wait) => {
+ let timeout = null;
+ return function(...args) {
+ clearTimeout(timeout);
+ timeout = setTimeout(() => {
+ func.apply(this, args);
+ }, wait);
+ };
+};