diff --git a/amd/build/ui_ace.min.js b/amd/build/ui_ace.min.js
index 74e1b69a..d6426c67 100644
--- a/amd/build/ui_ace.min.js
+++ b/amd/build/ui_ace.min.js
@@ -14,6 +14,6 @@
* @copyright Richard Lobb, 2015, 2017, The University of Canterbury
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-define("qtype_coderunner/ui_ace",["jquery"],(function($){function AceWrapper(textareaId,w,h,params){var session,code,textarea=$(document.getElementById(textareaId)),wrapper=$(document.getElementById(textareaId+"_wrapper")),focused=textarea[0]===document.activeElement,lang=params.lang,t=this;try{window.ace.require("ace/ext/language_tools"),this.modelist=window.ace.require("ace/ext/modelist"),this.textareaId=textareaId,this.textarea=textarea,this.enabled=!1,this.contents_changed=!1,this.capturingTab=!1,this.clickInProgress=!1,this.editNode=$("
"),this.editNode.css({resize:"none",height:h,width:"100%"}),this.editor=window.ace.edit(this.editNode.get(0)),textarea.prop("readonly")&&this.editor.setReadOnly(!0),this.editor.setOptions({enableBasicAutocompletion:!0,enableLiveAutocompletion:params.live_autocompletion,fontSize:params.font_size?params.font_size:"14px",newLineMode:"unix"}),this.editor.$blockScrolling=1/0,session=this.editor.getSession(),code=this.textarea.val(),(void 0===params.import_from_scratchpad||params.import_from_scratchpad)&&(code=this.extract_from_json_maybe(code)),session.setValue(code);const userTheme=window.localStorage.getItem("qtype_coderunner.ace.theme"),consider_prefers=params.auto_switch_light_dark&&window.matchMedia;null!==userTheme?this.editor.setTheme(userTheme):consider_prefers&&window.matchMedia("(prefers-color-scheme: dark)").matches?this.editor.setTheme("ace/theme/tomorrow_night"):consider_prefers&&window.matchMedia("(prefers-color-scheme: light)").matches?this.editor.setTheme("ace/theme/textmate"):params.theme?this.editor.setTheme("ace/theme/"+params.theme):this.editor.setTheme("ace/theme/textmate"),this.currentTheme=this.editor.getTheme(),this.fixSlowLoad(),this.setLanguage(lang),this.setEventHandlers(textarea),this.captureTab(),this.editor.renderer.on("afterRender",(function(){var gutter=wrapper.find(".ace_gutter");gutter.hasClass("moodle-has-zindex")||(gutter.addClass("moodle-has-zindex"),focused&&(t.editor.focus(),t.editor.navigateFileEnd()),t.aceLabel=wrapper.find(".answerprompt"),t.aceLabel.attr("for","ace_"+textareaId),t.aceTextarea=wrapper.find(".ace_text-input"),t.aceTextarea.attr("id","ace_"+textareaId))})),this.fail=!1}catch(err){this.fail=!0}}return AceWrapper.prototype.extract_from_json_maybe=function(code){try{code=JSON.parse(code).answer_code[0]}catch(err){}return code},AceWrapper.prototype.failed=function(){return this.fail},AceWrapper.prototype.failMessage=function(){return"ace_ui_notready"},AceWrapper.prototype.sync=function(){const thisThemeNow=this.editor.getTheme(),globalTheme=window.localStorage.getItem("qtype_coderunner.ace.theme");thisThemeNow!==this.currentTheme?(this.currentTheme=thisThemeNow,window.localStorage.setItem("qtype_coderunner.ace.theme",thisThemeNow)):globalTheme&&thisThemeNow!=globalTheme&&(this.editor.setTheme(globalTheme),this.currentTheme=globalTheme)},AceWrapper.prototype.syncIntervalSecs=function(){return 2},AceWrapper.prototype.setLanguage=function(language){var session=this.editor.getSession(),mode=this.findMode(language);mode&&session.setMode(mode.mode)},AceWrapper.prototype.getElement=function(){return this.editNode},AceWrapper.prototype.captureTab=function(){this.capturingTab=!0,this.editor.commands.bindKeys({Tab:"indent","Shift-Tab":"outdent"})},AceWrapper.prototype.releaseTab=function(){this.capturingTab=!1,this.editor.commands.bindKeys({Tab:null,"Shift-Tab":null})},AceWrapper.prototype.fixSlowLoad=function(){const observer=new IntersectionObserver((()=>{$(document).trigger("mousemove")})),editNode=this.editNode.get(0);observer.observe(editNode)},AceWrapper.prototype.setEventHandlers=function(){var t=this;this.editor.getSession().on("change",(function(){t.textarea.val(t.editor.getSession().getValue()),t.contents_changed=!0})),this.editor.on("blur",(function(){t.contents_changed&&t.textarea.trigger("change")})),this.editor.on("mousedown",(function(){t.clickInProgress=!0})),this.editor.on("focus",(function(){t.clickInProgress?t.captureTab():t.releaseTab()})),this.editor.on("click",(function(){t.clickInProgress=!1})),this.editor.container.addEventListener("keydown",(function(e){void 0!==e.which&&0===e.which||(77===e.keyCode&&e.ctrlKey&&!e.altKey?(t.capturingTab?t.releaseTab():t.captureTab(),e.preventDefault()):27===e.keyCode?t.releaseTab():e.shiftKey||e.ctrlKey||e.altKey||9==e.keyCode||t.captureTab())}),!0)},AceWrapper.prototype.destroy=function(){var focused;this.fail||(focused=this.editor.isFocused(),this.textarea.val(this.editor.getSession().getValue()),this.editor.destroy(),$(this.editNode).remove(),focused&&(this.textarea.focus(),this.textarea[0].selectionStart=this.textarea[0].value.length))},AceWrapper.prototype.hasFocus=function(){return this.editor.isFocused()},AceWrapper.prototype.findMode=function(language){var candidate,filename,result,candidates,nameMap={octave:"matlab",nodejs:"javascript","c#":"cs"};if("string"==typeof language){language.toLowerCase()in nameMap&&(language=nameMap[language.toLowerCase()]),candidates=[language,language.replace(/\d+$/,"")];for(var i=0;i"),this.editNode.css({resize:"none",height:h,width:"100%"}),this.editor=window.ace.edit(this.editNode.get(0)),textarea.prop("readonly")&&this.editor.setReadOnly(!0),this.editor.setOptions({enableBasicAutocompletion:!0,enableLiveAutocompletion:params.live_autocompletion,fontSize:params.font_size?params.font_size:"14px",newLineMode:"unix"}),this.editor.$blockScrolling=1/0,session=this.editor.getSession(),code=this.textarea.val(),(void 0===params.import_from_scratchpad||params.import_from_scratchpad)&&(code=this.extract_from_json_maybe(code)),session.setValue(code);const userTheme=window.localStorage.getItem("qtype_coderunner.ace.theme"),consider_prefers=params.auto_switch_light_dark&&window.matchMedia;null!==userTheme?this.editor.setTheme(userTheme):consider_prefers&&window.matchMedia("(prefers-color-scheme: dark)").matches?this.editor.setTheme("ace/theme/tomorrow_night"):consider_prefers&&window.matchMedia("(prefers-color-scheme: light)").matches?this.editor.setTheme("ace/theme/textmate"):params.theme?this.editor.setTheme("ace/theme/"+params.theme):this.editor.setTheme("ace/theme/textmate"),this.currentTheme=this.editor.getTheme(),this.fixSlowLoad(),this.setLanguage(lang),this.setEventHandlers(textarea),this.captureTab(),this.editor.renderer.on("afterRender",(function(){var gutter=wrapper.find(".ace_gutter");gutter.hasClass("moodle-has-zindex")||(gutter.addClass("moodle-has-zindex"),focused&&(t.editor.focus(),t.editor.navigateFileEnd()),t.aceLabel=wrapper.find(".answerprompt"),t.aceLabel.attr("for","ace_"+textareaId),t.aceTextarea=wrapper.find(".ace_text-input"),t.aceTextarea.attr("id","ace_"+textareaId))})),this.fail=!1}catch(err){this.fail=!0}}return AceWrapper.prototype.extract_from_json_maybe=function(code){try{code=JSON.parse(code).answer_code[0]}catch(err){}return code},AceWrapper.prototype.failed=function(){return this.fail},AceWrapper.prototype.failMessage=function(){return"ace_ui_notready"},AceWrapper.prototype.sync=function(){const thisThemeNow=this.editor.getTheme(),globalTheme=window.localStorage.getItem("qtype_coderunner.ace.theme");thisThemeNow!==this.currentTheme?(this.currentTheme=thisThemeNow,window.localStorage.setItem("qtype_coderunner.ace.theme",thisThemeNow)):globalTheme&&thisThemeNow!=globalTheme&&(this.editor.setTheme(globalTheme),this.currentTheme=globalTheme)},AceWrapper.prototype.syncIntervalSecs=function(){return 2},AceWrapper.prototype.setLanguage=function(language){var session=this.editor.getSession(),mode=this.findMode(language);mode&&session.setMode(mode.mode)},AceWrapper.prototype.getElement=function(){return this.editNode},AceWrapper.prototype.captureTab=function(){this.capturingTab=!0,this.editor.commands.bindKeys({Tab:"indent","Shift-Tab":"outdent"})},AceWrapper.prototype.releaseTab=function(){this.capturingTab=!1,this.editor.commands.bindKeys({Tab:null,"Shift-Tab":null})},AceWrapper.prototype.fixSlowLoad=function(){const observer=new IntersectionObserver((()=>{$(document).trigger("mousemove")})),editNode=this.editNode.get(0);observer.observe(editNode)},AceWrapper.prototype.setEventHandlers=function(){var t=this;this.editor.getSession().on("change",(function(){t.textarea.val(t.editor.getSession().getValue()),t.contents_changed=!0})),this.editor.on("blur",(function(){t.contents_changed&&t.textarea.trigger("change")})),this.editor.on("mousedown",(function(){t.clickInProgress=!0})),this.editor.on("focus",(function(){t.clickInProgress?t.captureTab():t.releaseTab()})),this.editor.on("click",(function(){t.clickInProgress=!1})),this.editor.container.addEventListener("keydown",(function(e){void 0!==e.which&&0===e.which||(77===e.keyCode&&e.ctrlKey&&!e.altKey?(t.capturingTab?t.releaseTab():t.captureTab(),e.preventDefault()):27===e.keyCode?t.releaseTab():e.shiftKey||e.ctrlKey||e.altKey||9==e.keyCode||t.captureTab())}),!0)},AceWrapper.prototype.destroy=function(){var focused;this.fail||(focused=this.editor.isFocused(),this.textarea.val(this.editor.getSession().getValue()),this.editor.destroy(),$(this.editNode).remove(),focused&&(this.textarea.focus(),this.textarea[0].selectionStart=this.textarea[0].value.length))},AceWrapper.prototype.hasFocus=function(){return this.editor.isFocused()},AceWrapper.prototype.findMode=function(language){var candidate,filename,result,candidates,nameMap={octave:"matlab",nodejs:"javascript","c#":"cs",pypy3:"python"};if("string"==typeof language){language.toLowerCase()in nameMap&&(language=nameMap[language.toLowerCase()]),candidates=[language,language.replace(/\d+$/,"")];for(var i=0;i.\n\n/**\n * JavaScript to interface to the Ace editor, which is used both in\n * the author editing page and by the student question submission page.\n * The class defined in this module is a plugin for the InterfaceWrapper class\n * declared in userinterfacewrapper.js. See that file for an explanation of\n * the interface to this module.\n *\n * A special case behaviour of the AceWrapper is that it needs to know\n * the Programming language that is being edited. This MUST be provided in\n * the constructor params parameter (an associative array) as a string\n * with key 'lang'.\n *\n * @module qtype_coderunner/ui_ace\n * @copyright Richard Lobb, 2015, 2017, The University of Canterbury\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n// Thanks to Ulrich Dangel for the initial implementation of Ace within\n// CodeRunner.\n\n// WARNING: The ace editor must have already been loaded before this\n// module is used, as it assumes window.ace exists.\n\ndefine(['jquery'], function($) {\n const GLOBAL_THEME_KEY = 'qtype_coderunner.ace.theme';\n const ACE_DARK_THEME = 'ace/theme/tomorrow_night';\n const ACE_LIGHT_THEME = 'ace/theme/textmate';\n /**\n * Constructor for the Ace interface object.\n * @param {string} textareaId The ID of the HTML textarea element to be wrapped.\n * @param {int} w The width in pixels of the textarea.\n * @param {int} h The height in pixels of the textarea.\n * @param {object} params The UI parameter object.\n */\n function AceWrapper(textareaId, w, h, params) {\n var textarea = $(document.getElementById(textareaId)),\n wrapper = $(document.getElementById(textareaId + '_wrapper')),\n focused = textarea[0] === document.activeElement,\n lang = params.lang,\n session,\n code,\n t = this; // For embedded callbacks.\n\n try {\n window.ace.require(\"ace/ext/language_tools\");\n this.modelist = window.ace.require('ace/ext/modelist');\n this.textareaId = textareaId;\n this.textarea = textarea;\n this.enabled = false;\n this.contents_changed = false;\n this.capturingTab = false;\n this.clickInProgress = false;\n\n this.editNode = $(\"\"); // Ace editor manages this\n this.editNode.css({\n resize: 'none',\n height: h,\n width: \"100%\"\n });\n\n this.editor = window.ace.edit(this.editNode.get(0));\n if (textarea.prop('readonly')) {\n this.editor.setReadOnly(true);\n }\n\n this.editor.setOptions({\n enableBasicAutocompletion: true,\n enableLiveAutocompletion: params.live_autocompletion,\n fontSize: params.font_size ? params.font_size : \"14px\",\n newLineMode: \"unix\",\n });\n\n this.editor.$blockScrolling = Infinity;\n\n session = this.editor.getSession();\n code = this.textarea.val();\n if (params.import_from_scratchpad === undefined || params.import_from_scratchpad) {\n code = this.extract_from_json_maybe(code);\n }\n session.setValue(code);\n\n // If there's a user-defined theme in local storage, use that.\n // Otherwise use the 'prefers-color-scheme' option if given or\n // the question/system defaults if not.\n const userTheme = window.localStorage.getItem(GLOBAL_THEME_KEY);\n const consider_prefers = params.auto_switch_light_dark && window.matchMedia;\n if (userTheme !== null) {\n this.editor.setTheme(userTheme);\n } else if (consider_prefers && window.matchMedia('(prefers-color-scheme: dark)').matches) {\n this.editor.setTheme(ACE_DARK_THEME);\n } else if (consider_prefers && window.matchMedia('(prefers-color-scheme: light)').matches) {\n this.editor.setTheme(ACE_LIGHT_THEME);\n } else if (params.theme) {\n this.editor.setTheme(\"ace/theme/\" + params.theme);\n } else {\n this.editor.setTheme(ACE_LIGHT_THEME);\n }\n this.currentTheme = this.editor.getTheme();\n\n this.fixSlowLoad();\n\n this.setLanguage(lang);\n\n this.setEventHandlers(textarea);\n this.captureTab();\n\n // Try to tell Moodle about parts of the editor with z-index.\n // It is hard to be sure if this is complete. ACE adds all its CSS using JavaScript.\n // Here, we just deal with things that are known to cause a problem.\n // Can't do these operations until editor has rendered. So ...\n this.editor.renderer.on('afterRender', function() {\n var gutter = wrapper.find('.ace_gutter');\n if (gutter.hasClass('moodle-has-zindex')) {\n return; // So we only do what follows once.\n }\n gutter.addClass('moodle-has-zindex');\n\n if (focused) {\n t.editor.focus();\n t.editor.navigateFileEnd();\n }\n t.aceLabel = wrapper.find('.answerprompt');\n t.aceLabel.attr('for', 'ace_' + textareaId);\n\n t.aceTextarea = wrapper.find('.ace_text-input');\n t.aceTextarea.attr('id', 'ace_' + textareaId);\n });\n\n this.fail = false;\n }\n catch(err) {\n // Something ugly happened. Probably ace editor hasn't been loaded\n this.fail = true;\n }\n }\n\n AceWrapper.prototype.extract_from_json_maybe = function(code) {\n // If the given code looks like JSON from the Scratchpad UI,\n // extract and return the answer_code attribute.\n try {\n const jsonObj = JSON.parse(code);\n code = jsonObj.answer_code[0];\n } catch(err) {}\n\n return code;\n };\n\n AceWrapper.prototype.failed = function() {\n return this.fail;\n };\n\n AceWrapper.prototype.failMessage = function() {\n return 'ace_ui_notready';\n };\n\n // Sync to TextArea\n AceWrapper.prototype.sync = function() {\n // The data is always sync'd to the text area. But here we use sync to\n // poll the value of the current theme and record in browser local\n // storage if the value for this particular Ace instance has changed\n // from the current working theme (set by code),\n // implying a user menu action. If that happens the global user theme\n // is set and is subsequently used by all Ace windows.\n const thisThemeNow = this.editor.getTheme();\n const globalTheme = window.localStorage.getItem(GLOBAL_THEME_KEY);\n if (thisThemeNow !== this.currentTheme) {\n // User has changed the theme via menu. Record in global storage so\n // other editor instances can switch to it.\n this.currentTheme = thisThemeNow;\n window.localStorage.setItem(GLOBAL_THEME_KEY, thisThemeNow);\n // console.log(`Menu theme change. Global theme now ${thisThemeNow}`);\n } else if (globalTheme && thisThemeNow != globalTheme) {\n // Another window has set the theme (since if there had been a\n // global theme when we started, we'd have used it.\n this.editor.setTheme(globalTheme);\n this.currentTheme = globalTheme;\n // console.log(`Global theme change found: ${globalTheme}`);\n }\n };\n\n AceWrapper.prototype.syncIntervalSecs = function() {\n return 2;\n };\n\n AceWrapper.prototype.setLanguage = function(language) {\n var session = this.editor.getSession(),\n mode = this.findMode(language);\n if (mode) {\n session.setMode(mode.mode);\n }\n };\n\n AceWrapper.prototype.getElement = function() {\n return this.editNode;\n };\n\n AceWrapper.prototype.captureTab = function () {\n this.capturingTab = true;\n this.editor.commands.bindKeys({'Tab': 'indent', 'Shift-Tab': 'outdent'});\n };\n\n AceWrapper.prototype.releaseTab = function () {\n this.capturingTab = false;\n this.editor.commands.bindKeys({'Tab': null, 'Shift-Tab': null});\n };\n\n // Sometimes Ace editors do not load until the mouse is moved. To fix this,\n // 'move' the mouse using JQuery when the editor div enters the viewport.\n AceWrapper.prototype.fixSlowLoad = function () {\n const observer = new IntersectionObserver( () => {\n $(document).trigger('mousemove');\n });\n const editNode = this.editNode.get(0); // Non-JQuerry node.\n observer.observe(editNode);\n };\n\n AceWrapper.prototype.setEventHandlers = function () {\n var TAB = 9,\n ESC = 27,\n KEY_M = 77,\n t = this;\n\n this.editor.getSession().on('change', function() {\n t.textarea.val(t.editor.getSession().getValue());\n t.contents_changed = true;\n });\n\n this.editor.on('blur', function() {\n if (t.contents_changed) {\n t.textarea.trigger('change');\n }\n });\n\n this.editor.on('mousedown', function() {\n // Event order seems to be (\\ is where the mouse button is pressed, / released):\n // Chrome: \\ mousedown, mouseup, focusin / click.\n // Firefox/IE: \\ mousedown, focusin / mouseup, click.\n t.clickInProgress = true;\n });\n\n this.editor.on('focus', function() {\n if (t.clickInProgress) {\n t.captureTab();\n } else {\n t.releaseTab();\n }\n });\n\n this.editor.on('click', function() {\n t.clickInProgress = false;\n });\n\n this.editor.container.addEventListener('keydown', function(e) {\n if (e.which === undefined || e.which !== 0) { // Normal keypress?\n if (e.keyCode === KEY_M && e.ctrlKey && !e.altKey) {\n if (t.capturingTab) {\n t.releaseTab();\n } else {\n t.captureTab();\n }\n e.preventDefault(); // Firefox uses this for mute audio in current browser tab.\n }\n else if (e.keyCode === ESC) {\n t.releaseTab();\n }\n else if (!(e.shiftKey || e.ctrlKey || e.altKey || e.keyCode == TAB)) {\n t.captureTab();\n }\n }\n }, true);\n };\n\n AceWrapper.prototype.destroy = function () {\n var focused;\n if (!this.fail) {\n // Proceed only if this wrapper was correctly constructed\n focused = this.editor.isFocused();\n this.textarea.val(this.editor.getSession().getValue()); // Copy data back\n this.editor.destroy();\n $(this.editNode).remove();\n if (focused) {\n this.textarea.focus();\n this.textarea[0].selectionStart = this.textarea[0].value.length;\n }\n }\n };\n\n AceWrapper.prototype.hasFocus = function() {\n return this.editor.isFocused();\n };\n\n AceWrapper.prototype.findMode = function (language) {\n var candidate,\n filename,\n result,\n candidates = [], // List of candidate modes.\n nameMap = {\n 'octave': 'matlab',\n 'nodejs': 'javascript',\n 'c#': 'cs'\n };\n\n if (typeof language !== 'string') {\n return undefined;\n }\n if (language.toLowerCase() in nameMap) {\n language = nameMap[language.toLowerCase()];\n }\n\n candidates = [language, language.replace(/\\d+$/, \"\")];\n for (var i = 0; i < candidates.length; i++) {\n candidate = candidates[i];\n filename = \"input.\" + candidate;\n result = this.modelist.modesByName[candidate] ||\n this.modelist.modesByName[candidate.toLowerCase()] ||\n this.modelist.getModeForPath(filename) ||\n this.modelist.getModeForPath(filename.toLowerCase());\n\n if (result && result.name !== 'text') {\n return result;\n }\n }\n return undefined;\n };\n\n AceWrapper.prototype.resize = function(w, h) {\n this.editNode.outerHeight(h);\n this.editNode.outerWidth(w);\n this.editor.resize();\n };\n\n /**\n * Allow fullscreen mode for the Ace editor.\n *\n * @return {Boolean} True if fullscreen mode is allowed, false otherwise.\n */\n AceWrapper.prototype.allowFullScreen = function() {\n return true;\n };\n\n return {\n Constructor: AceWrapper\n };\n});\n"],"names":["define","$","AceWrapper","textareaId","w","h","params","session","code","textarea","document","getElementById","wrapper","focused","activeElement","lang","t","this","window","ace","require","modelist","enabled","contents_changed","capturingTab","clickInProgress","editNode","css","resize","height","width","editor","edit","get","prop","setReadOnly","setOptions","enableBasicAutocompletion","enableLiveAutocompletion","live_autocompletion","fontSize","font_size","newLineMode","$blockScrolling","Infinity","getSession","val","undefined","import_from_scratchpad","extract_from_json_maybe","setValue","userTheme","localStorage","getItem","consider_prefers","auto_switch_light_dark","matchMedia","setTheme","matches","theme","currentTheme","getTheme","fixSlowLoad","setLanguage","setEventHandlers","captureTab","renderer","on","gutter","find","hasClass","addClass","focus","navigateFileEnd","aceLabel","attr","aceTextarea","fail","err","prototype","JSON","parse","answer_code","failed","failMessage","sync","thisThemeNow","globalTheme","setItem","syncIntervalSecs","language","mode","findMode","setMode","getElement","commands","bindKeys","releaseTab","observer","IntersectionObserver","trigger","observe","getValue","container","addEventListener","e","which","keyCode","ctrlKey","altKey","preventDefault","shiftKey","destroy","isFocused","remove","selectionStart","value","length","hasFocus","candidate","filename","result","candidates","nameMap","toLowerCase","replace","i","modesByName","getModeForPath","name","outerHeight","outerWidth","allowFullScreen","Constructor"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"ui_ace.min.js","sources":["../src/ui_ace.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 .\n\n/**\n * JavaScript to interface to the Ace editor, which is used both in\n * the author editing page and by the student question submission page.\n * The class defined in this module is a plugin for the InterfaceWrapper class\n * declared in userinterfacewrapper.js. See that file for an explanation of\n * the interface to this module.\n *\n * A special case behaviour of the AceWrapper is that it needs to know\n * the Programming language that is being edited. This MUST be provided in\n * the constructor params parameter (an associative array) as a string\n * with key 'lang'.\n *\n * @module qtype_coderunner/ui_ace\n * @copyright Richard Lobb, 2015, 2017, The University of Canterbury\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n// Thanks to Ulrich Dangel for the initial implementation of Ace within\n// CodeRunner.\n\n// WARNING: The ace editor must have already been loaded before this\n// module is used, as it assumes window.ace exists.\n\ndefine(['jquery'], function($) {\n const GLOBAL_THEME_KEY = 'qtype_coderunner.ace.theme';\n const ACE_DARK_THEME = 'ace/theme/tomorrow_night';\n const ACE_LIGHT_THEME = 'ace/theme/textmate';\n /**\n * Constructor for the Ace interface object.\n * @param {string} textareaId The ID of the HTML textarea element to be wrapped.\n * @param {int} w The width in pixels of the textarea.\n * @param {int} h The height in pixels of the textarea.\n * @param {object} params The UI parameter object.\n */\n function AceWrapper(textareaId, w, h, params) {\n var textarea = $(document.getElementById(textareaId)),\n wrapper = $(document.getElementById(textareaId + '_wrapper')),\n focused = textarea[0] === document.activeElement,\n lang = params.lang,\n session,\n code,\n t = this; // For embedded callbacks.\n\n try {\n window.ace.require(\"ace/ext/language_tools\");\n this.modelist = window.ace.require('ace/ext/modelist');\n this.textareaId = textareaId;\n this.textarea = textarea;\n this.enabled = false;\n this.contents_changed = false;\n this.capturingTab = false;\n this.clickInProgress = false;\n\n this.editNode = $(\"\"); // Ace editor manages this\n this.editNode.css({\n resize: 'none',\n height: h,\n width: \"100%\"\n });\n\n this.editor = window.ace.edit(this.editNode.get(0));\n if (textarea.prop('readonly')) {\n this.editor.setReadOnly(true);\n }\n\n this.editor.setOptions({\n enableBasicAutocompletion: true,\n enableLiveAutocompletion: params.live_autocompletion,\n fontSize: params.font_size ? params.font_size : \"14px\",\n newLineMode: \"unix\",\n });\n\n this.editor.$blockScrolling = Infinity;\n\n session = this.editor.getSession();\n code = this.textarea.val();\n if (params.import_from_scratchpad === undefined || params.import_from_scratchpad) {\n code = this.extract_from_json_maybe(code);\n }\n session.setValue(code);\n\n // If there's a user-defined theme in local storage, use that.\n // Otherwise use the 'prefers-color-scheme' option if given or\n // the question/system defaults if not.\n const userTheme = window.localStorage.getItem(GLOBAL_THEME_KEY);\n const consider_prefers = params.auto_switch_light_dark && window.matchMedia;\n if (userTheme !== null) {\n this.editor.setTheme(userTheme);\n } else if (consider_prefers && window.matchMedia('(prefers-color-scheme: dark)').matches) {\n this.editor.setTheme(ACE_DARK_THEME);\n } else if (consider_prefers && window.matchMedia('(prefers-color-scheme: light)').matches) {\n this.editor.setTheme(ACE_LIGHT_THEME);\n } else if (params.theme) {\n this.editor.setTheme(\"ace/theme/\" + params.theme);\n } else {\n this.editor.setTheme(ACE_LIGHT_THEME);\n }\n this.currentTheme = this.editor.getTheme();\n\n this.fixSlowLoad();\n\n this.setLanguage(lang);\n\n this.setEventHandlers(textarea);\n this.captureTab();\n\n // Try to tell Moodle about parts of the editor with z-index.\n // It is hard to be sure if this is complete. ACE adds all its CSS using JavaScript.\n // Here, we just deal with things that are known to cause a problem.\n // Can't do these operations until editor has rendered. So ...\n this.editor.renderer.on('afterRender', function() {\n var gutter = wrapper.find('.ace_gutter');\n if (gutter.hasClass('moodle-has-zindex')) {\n return; // So we only do what follows once.\n }\n gutter.addClass('moodle-has-zindex');\n\n if (focused) {\n t.editor.focus();\n t.editor.navigateFileEnd();\n }\n t.aceLabel = wrapper.find('.answerprompt');\n t.aceLabel.attr('for', 'ace_' + textareaId);\n\n t.aceTextarea = wrapper.find('.ace_text-input');\n t.aceTextarea.attr('id', 'ace_' + textareaId);\n });\n\n this.fail = false;\n }\n catch(err) {\n // Something ugly happened. Probably ace editor hasn't been loaded\n this.fail = true;\n }\n }\n\n AceWrapper.prototype.extract_from_json_maybe = function(code) {\n // If the given code looks like JSON from the Scratchpad UI,\n // extract and return the answer_code attribute.\n try {\n const jsonObj = JSON.parse(code);\n code = jsonObj.answer_code[0];\n } catch(err) {}\n\n return code;\n };\n\n AceWrapper.prototype.failed = function() {\n return this.fail;\n };\n\n AceWrapper.prototype.failMessage = function() {\n return 'ace_ui_notready';\n };\n\n // Sync to TextArea\n AceWrapper.prototype.sync = function() {\n // The data is always sync'd to the text area. But here we use sync to\n // poll the value of the current theme and record in browser local\n // storage if the value for this particular Ace instance has changed\n // from the current working theme (set by code),\n // implying a user menu action. If that happens the global user theme\n // is set and is subsequently used by all Ace windows.\n const thisThemeNow = this.editor.getTheme();\n const globalTheme = window.localStorage.getItem(GLOBAL_THEME_KEY);\n if (thisThemeNow !== this.currentTheme) {\n // User has changed the theme via menu. Record in global storage so\n // other editor instances can switch to it.\n this.currentTheme = thisThemeNow;\n window.localStorage.setItem(GLOBAL_THEME_KEY, thisThemeNow);\n // console.log(`Menu theme change. Global theme now ${thisThemeNow}`);\n } else if (globalTheme && thisThemeNow != globalTheme) {\n // Another window has set the theme (since if there had been a\n // global theme when we started, we'd have used it.\n this.editor.setTheme(globalTheme);\n this.currentTheme = globalTheme;\n // console.log(`Global theme change found: ${globalTheme}`);\n }\n };\n\n AceWrapper.prototype.syncIntervalSecs = function() {\n return 2;\n };\n\n AceWrapper.prototype.setLanguage = function(language) {\n var session = this.editor.getSession(),\n mode = this.findMode(language);\n if (mode) {\n session.setMode(mode.mode);\n }\n };\n\n AceWrapper.prototype.getElement = function() {\n return this.editNode;\n };\n\n AceWrapper.prototype.captureTab = function () {\n this.capturingTab = true;\n this.editor.commands.bindKeys({'Tab': 'indent', 'Shift-Tab': 'outdent'});\n };\n\n AceWrapper.prototype.releaseTab = function () {\n this.capturingTab = false;\n this.editor.commands.bindKeys({'Tab': null, 'Shift-Tab': null});\n };\n\n // Sometimes Ace editors do not load until the mouse is moved. To fix this,\n // 'move' the mouse using JQuery when the editor div enters the viewport.\n AceWrapper.prototype.fixSlowLoad = function () {\n const observer = new IntersectionObserver( () => {\n $(document).trigger('mousemove');\n });\n const editNode = this.editNode.get(0); // Non-JQuerry node.\n observer.observe(editNode);\n };\n\n AceWrapper.prototype.setEventHandlers = function () {\n var TAB = 9,\n ESC = 27,\n KEY_M = 77,\n t = this;\n\n this.editor.getSession().on('change', function() {\n t.textarea.val(t.editor.getSession().getValue());\n t.contents_changed = true;\n });\n\n this.editor.on('blur', function() {\n if (t.contents_changed) {\n t.textarea.trigger('change');\n }\n });\n\n this.editor.on('mousedown', function() {\n // Event order seems to be (\\ is where the mouse button is pressed, / released):\n // Chrome: \\ mousedown, mouseup, focusin / click.\n // Firefox/IE: \\ mousedown, focusin / mouseup, click.\n t.clickInProgress = true;\n });\n\n this.editor.on('focus', function() {\n if (t.clickInProgress) {\n t.captureTab();\n } else {\n t.releaseTab();\n }\n });\n\n this.editor.on('click', function() {\n t.clickInProgress = false;\n });\n\n this.editor.container.addEventListener('keydown', function(e) {\n if (e.which === undefined || e.which !== 0) { // Normal keypress?\n if (e.keyCode === KEY_M && e.ctrlKey && !e.altKey) {\n if (t.capturingTab) {\n t.releaseTab();\n } else {\n t.captureTab();\n }\n e.preventDefault(); // Firefox uses this for mute audio in current browser tab.\n }\n else if (e.keyCode === ESC) {\n t.releaseTab();\n }\n else if (!(e.shiftKey || e.ctrlKey || e.altKey || e.keyCode == TAB)) {\n t.captureTab();\n }\n }\n }, true);\n };\n\n AceWrapper.prototype.destroy = function () {\n var focused;\n if (!this.fail) {\n // Proceed only if this wrapper was correctly constructed\n focused = this.editor.isFocused();\n this.textarea.val(this.editor.getSession().getValue()); // Copy data back\n this.editor.destroy();\n $(this.editNode).remove();\n if (focused) {\n this.textarea.focus();\n this.textarea[0].selectionStart = this.textarea[0].value.length;\n }\n }\n };\n\n AceWrapper.prototype.hasFocus = function() {\n return this.editor.isFocused();\n };\n\n AceWrapper.prototype.findMode = function (language) {\n var candidate,\n filename,\n result,\n candidates = [], // List of candidate modes.\n nameMap = {\n 'octave': 'matlab',\n 'nodejs': 'javascript',\n 'c#': 'cs',\n 'pypy3': 'python'\n };\n\n if (typeof language !== 'string') {\n return undefined;\n }\n if (language.toLowerCase() in nameMap) {\n language = nameMap[language.toLowerCase()];\n }\n\n candidates = [language, language.replace(/\\d+$/, \"\")];\n for (var i = 0; i < candidates.length; i++) {\n candidate = candidates[i];\n filename = \"input.\" + candidate;\n result = this.modelist.modesByName[candidate] ||\n this.modelist.modesByName[candidate.toLowerCase()] ||\n this.modelist.getModeForPath(filename) ||\n this.modelist.getModeForPath(filename.toLowerCase());\n\n if (result && result.name !== 'text') {\n return result;\n }\n }\n return undefined;\n };\n\n AceWrapper.prototype.resize = function(w, h) {\n this.editNode.outerHeight(h);\n this.editNode.outerWidth(w);\n this.editor.resize();\n };\n\n /**\n * Allow fullscreen mode for the Ace editor.\n *\n * @return {Boolean} True if fullscreen mode is allowed, false otherwise.\n */\n AceWrapper.prototype.allowFullScreen = function() {\n return true;\n };\n\n return {\n Constructor: AceWrapper\n };\n});\n"],"names":["define","$","AceWrapper","textareaId","w","h","params","session","code","textarea","document","getElementById","wrapper","focused","activeElement","lang","t","this","window","ace","require","modelist","enabled","contents_changed","capturingTab","clickInProgress","editNode","css","resize","height","width","editor","edit","get","prop","setReadOnly","setOptions","enableBasicAutocompletion","enableLiveAutocompletion","live_autocompletion","fontSize","font_size","newLineMode","$blockScrolling","Infinity","getSession","val","undefined","import_from_scratchpad","extract_from_json_maybe","setValue","userTheme","localStorage","getItem","consider_prefers","auto_switch_light_dark","matchMedia","setTheme","matches","theme","currentTheme","getTheme","fixSlowLoad","setLanguage","setEventHandlers","captureTab","renderer","on","gutter","find","hasClass","addClass","focus","navigateFileEnd","aceLabel","attr","aceTextarea","fail","err","prototype","JSON","parse","answer_code","failed","failMessage","sync","thisThemeNow","globalTheme","setItem","syncIntervalSecs","language","mode","findMode","setMode","getElement","commands","bindKeys","releaseTab","observer","IntersectionObserver","trigger","observe","getValue","container","addEventListener","e","which","keyCode","ctrlKey","altKey","preventDefault","shiftKey","destroy","isFocused","remove","selectionStart","value","length","hasFocus","candidate","filename","result","candidates","nameMap","toLowerCase","replace","i","modesByName","getModeForPath","name","outerHeight","outerWidth","allowFullScreen","Constructor"],"mappings":"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"}
\ No newline at end of file
diff --git a/amd/src/ui_ace.js b/amd/src/ui_ace.js
index cc1d31d8..bdbb323d 100644
--- a/amd/src/ui_ace.js
+++ b/amd/src/ui_ace.js
@@ -312,7 +312,8 @@ define(['jquery'], function($) {
nameMap = {
'octave': 'matlab',
'nodejs': 'javascript',
- 'c#': 'cs'
+ 'c#': 'cs',
+ 'pypy3': 'python'
};
if (typeof language !== 'string') {