';
if ($query) {
echo admin_search_settings_html($query);
diff --git a/admin/tests/behat/behat_admin.php b/admin/tests/behat/behat_admin.php
index af027dec4c8..98f5d91de10 100644
--- a/admin/tests/behat/behat_admin.php
+++ b/admin/tests/behat/behat_admin.php
@@ -56,7 +56,7 @@ class behat_admin extends behat_base {
$this->execute('behat_navigation::i_select_from_flat_navigation_drawer', [get_string('administrationsite')]);
// Search by label.
- $this->execute('behat_forms::i_set_the_field_to', [get_string('query', 'admin'), $label]);
+ $this->execute('behat_forms::i_set_the_field_to', [get_string('search'), $label]);
$this->execute("behat_forms::press_button", get_string('search', 'admin'));
// Admin settings does not use the same DOM structure than other moodle forms
diff --git a/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-debug.js b/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-debug.js
index 1c0553405bc..ed3d79cd36e 100644
--- a/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-debug.js
+++ b/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-debug.js
@@ -53,6 +53,13 @@ SEARCH.prototype = {
* @protected
*/
button: null,
+ /**
+ * The cancel button for the form.
+ * @property button
+ * @type Node
+ * @protected
+ */
+ cancel: null,
/**
* The last search node if there is one.
* If there is a last search node then the last search term will be persisted between requests.
@@ -76,17 +83,28 @@ SEARCH.prototype = {
this.button = this.form.all('input[type=submit]');
this.lastsearch = this.form.one('input[name=search]');
- var div = Y.Node.create(''),
- label = Y.Node.create('');
- this.input = Y.Node.create('');
+ var div = Y.Node.create(''),
+ label = Y.Node.create('');
+ this.cancel = Y.Node.create('' +
+ '' +
+ '');
+ this.input = Y.Node.create('');
- div.append(label).append(this.input);
+ div.append(label).append(this.input).append(this.cancel);
this.select.insert(div, 'before');
this.input.on('keyup', this.typed, this);
this.select.on('change', this.validate, this);
+ this.cancel.on('click', function() {
+ this.input.set('value', '');
+ this.typed();
+ }, this);
+
if (this.lastsearch) {
this.input.set('value', this.lastsearch.get('value'));
this.typed();
@@ -133,6 +151,11 @@ SEARCH.prototype = {
last.set('selected', true);
}
}
+ if (search !== '') {
+ this.cancel.removeClass("d-none");
+ } else {
+ this.cancel.addClass("d-none");
+ }
this.validate();
}
};
diff --git a/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-min.js b/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-min.js
index 6520ca28100..30e97fccc72 100644
--- a/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-min.js
+++ b/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-min.js
@@ -1 +1 @@
-YUI.add("moodle-tool_capability-search",function(i,t){var e=function(){e.superclass.constructor.apply(this,arguments)};e.prototype={form:null,select:null,selectoptions:{},input:null,button:null,lastsearch:null,initializer:function(){this.form=i.one("#capability-overview-form"),this.select=this.form.one("select[data-search=capability]"),this.select.setStyle("minWidth",this.select.get("offsetWidth")),this.select.get("options").each(function(t){var e=t.get("value");this.selectoptions[e]=t},this),this.button=this.form.all("input[type=submit]"),this.lastsearch=this.form.one("input[name=search]");var t=i.Node.create(''),e=i.Node.create('");this.input=i.Node.create(''),t.append(e).append(this.input),this.select.insert(t,"before"),this.input.on("keyup",this.typed,this),this.select.on("change",this.validate,this),this.lastsearch&&(this.input.set("value",this.lastsearch.get("value")),this.typed(),this.select.one("option[selected]")&&this.select.set("scrollTop",this.select.one("option[selected]").get("getX"))),this.validate()},validate:function(){this.button.set("disabled",""===this.select.get("value"))},typed:function(){var t,e=this.input.get("value"),i=0,s=null;for(t in this.lastsearch&&this.lastsearch.set("value",e),this.select.all("option").remove(),this.selectoptions)0<=t.indexOf(e)&&(i++,s=this.selectoptions[t],this.select.append(this.selectoptions[t]));0===i?this.input.addClass("error"):(this.input.removeClass("error"),1===i&&s.set("selected",!0)),this.validate()}},i.extend(e,i.Base,e.prototype,{NAME:"tool_capability-search",ATTRS:{strsearch:{}}}),M.tool_capability=M.tool_capability||{},M.tool_capability.init_capability_search=function(t){new e(t)}},"@VERSION@",{requires:["base","node"]});
\ No newline at end of file
+YUI.add("moodle-tool_capability-search",function(s,t){var e=function(){e.superclass.constructor.apply(this,arguments)};e.prototype={form:null,select:null,selectoptions:{},input:null,button:null,cancel:null,lastsearch:null,initializer:function(){this.form=s.one("#capability-overview-form"),this.select=this.form.one("select[data-search=capability]"),this.select.setStyle("minWidth",this.select.get("offsetWidth")),this.select.get("options").each(function(t){var e=t.get("value");this.selectoptions[e]=t},this),this.button=this.form.all("input[type=submit]"),this.lastsearch=this.form.one("input[name=search]");var t=s.Node.create(''),e=s.Node.create('");this.cancel=s.Node.create(''),this.input=s.Node.create(''),t.append(e).append(this.input).append(this.cancel),this.select.insert(t,"before"),this.input.on("keyup",this.typed,this),this.select.on("change",this.validate,this),this.cancel.on("click",function(){this.input.set("value",""),this.typed()},this),this.lastsearch&&(this.input.set("value",this.lastsearch.get("value")),this.typed(),this.select.one("option[selected]")&&this.select.set("scrollTop",this.select.one("option[selected]").get("getX"))),this.validate()},validate:function(){this.button.set("disabled",""===this.select.get("value"))},typed:function(){var t,e=this.input.get("value"),s=0,i=null;for(t in this.lastsearch&&this.lastsearch.set("value",e),this.select.all("option").remove(),this.selectoptions)0<=t.indexOf(e)&&(s++,i=this.selectoptions[t],this.select.append(this.selectoptions[t]));0===s?this.input.addClass("error"):(this.input.removeClass("error"),1===s&&i.set("selected",!0)),""!==e?this.cancel.removeClass("d-none"):this.cancel.addClass("d-none"),this.validate()}},s.extend(e,s.Base,e.prototype,{NAME:"tool_capability-search",ATTRS:{strsearch:{}}}),M.tool_capability=M.tool_capability||{},M.tool_capability.init_capability_search=function(t){new e(t)}},"@VERSION@",{requires:["base","node"]});
\ No newline at end of file
diff --git a/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search.js b/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search.js
index 1c0553405bc..ed3d79cd36e 100644
--- a/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search.js
+++ b/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search.js
@@ -53,6 +53,13 @@ SEARCH.prototype = {
* @protected
*/
button: null,
+ /**
+ * The cancel button for the form.
+ * @property button
+ * @type Node
+ * @protected
+ */
+ cancel: null,
/**
* The last search node if there is one.
* If there is a last search node then the last search term will be persisted between requests.
@@ -76,17 +83,28 @@ SEARCH.prototype = {
this.button = this.form.all('input[type=submit]');
this.lastsearch = this.form.one('input[name=search]');
- var div = Y.Node.create(''),
- label = Y.Node.create('');
- this.input = Y.Node.create('');
+ var div = Y.Node.create(''),
+ label = Y.Node.create('');
+ this.cancel = Y.Node.create('' +
+ '' +
+ '');
+ this.input = Y.Node.create('');
- div.append(label).append(this.input);
+ div.append(label).append(this.input).append(this.cancel);
this.select.insert(div, 'before');
this.input.on('keyup', this.typed, this);
this.select.on('change', this.validate, this);
+ this.cancel.on('click', function() {
+ this.input.set('value', '');
+ this.typed();
+ }, this);
+
if (this.lastsearch) {
this.input.set('value', this.lastsearch.get('value'));
this.typed();
@@ -133,6 +151,11 @@ SEARCH.prototype = {
last.set('selected', true);
}
}
+ if (search !== '') {
+ this.cancel.removeClass("d-none");
+ } else {
+ this.cancel.addClass("d-none");
+ }
this.validate();
}
};
diff --git a/admin/tool/capability/yui/src/search/js/search.js b/admin/tool/capability/yui/src/search/js/search.js
index 1e4b96c9d91..71bd1525766 100644
--- a/admin/tool/capability/yui/src/search/js/search.js
+++ b/admin/tool/capability/yui/src/search/js/search.js
@@ -51,6 +51,13 @@ SEARCH.prototype = {
* @protected
*/
button: null,
+ /**
+ * The cancel button for the form.
+ * @property button
+ * @type Node
+ * @protected
+ */
+ cancel: null,
/**
* The last search node if there is one.
* If there is a last search node then the last search term will be persisted between requests.
@@ -74,17 +81,28 @@ SEARCH.prototype = {
this.button = this.form.all('input[type=submit]');
this.lastsearch = this.form.one('input[name=search]');
- var div = Y.Node.create(''),
- label = Y.Node.create('');
- this.input = Y.Node.create('');
+ var div = Y.Node.create(''),
+ label = Y.Node.create('');
+ this.cancel = Y.Node.create('' +
+ '' +
+ '');
+ this.input = Y.Node.create('');
- div.append(label).append(this.input);
+ div.append(label).append(this.input).append(this.cancel);
this.select.insert(div, 'before');
this.input.on('keyup', this.typed, this);
this.select.on('change', this.validate, this);
+ this.cancel.on('click', function() {
+ this.input.set('value', '');
+ this.typed();
+ }, this);
+
if (this.lastsearch) {
this.input.set('value', this.lastsearch.get('value'));
this.typed();
@@ -131,6 +149,11 @@ SEARCH.prototype = {
last.set('selected', true);
}
}
+ if (search !== '') {
+ this.cancel.removeClass("d-none");
+ } else {
+ this.cancel.addClass("d-none");
+ }
this.validate();
}
};
diff --git a/admin/tool/templatelibrary/amd/build/search.min.js b/admin/tool/templatelibrary/amd/build/search.min.js
index ea1aac52977..74f8c50e9b3 100644
--- a/admin/tool/templatelibrary/amd/build/search.min.js
+++ b/admin/tool/templatelibrary/amd/build/search.min.js
@@ -1,2 +1,2 @@
-define ("tool_templatelibrary/search",["jquery","core/ajax","core/log","core/notification","core/templates","core/config"],function(a,b,c,d,e,f){var g=function(b){e.render("tool_templatelibrary/search_results",{templates:b}).done(function(b,c){e.replaceNode(a("[data-region=\"searchresults\"]"),b,c)}).fail(d.exception)},h=function(c){var e=a("[data-field=\"component\"]").val(),f=a("[data-field=\"search\"]").val();document.location.hash=f;b.call([{methodname:"tool_templatelibrary_list_templates",args:{component:e,search:f,themename:c},done:g,fail:d.exception}],!0,!1)},i=null,j=function(a,b){if(null!==i){window.clearTimeout(i)}i=window.setTimeout(function(){a();i=null},b)},k=function(){j(h.bind(this,f.theme),400)};a("[data-region=\"list-templates\"]").on("change","[data-field=\"component\"]",k);a("[data-region=\"list-templates\"]").on("input","[data-field=\"search\"]",k);a("[data-field=\"search\"]").val(document.location.hash.replace("#",""));h(f.theme);return{}});
+define ("tool_templatelibrary/search",["jquery","core/ajax","core/log","core/notification","core/templates","core/config"],function(a,b,c,d,e,f){var g=function(b){e.render("tool_templatelibrary/search_results",{templates:b}).done(function(b,c){e.replaceNode(a("[data-region=\"searchresults\"]"),b,c)}).fail(d.exception)},h=function(c){var e=a("[data-field=\"component\"]").val(),f=a("[data-region=\"list-templates\"] [data-region=\"input\"]").val();if(""!==f){a("[data-region=\"list-templates\"] [data-action=\"clearsearch\"]").removeClass("d-none")}else{a("[data-region=\"list-templates\"] [data-action=\"clearsearch\"]").addClass("d-none")}document.location.hash=f;b.call([{methodname:"tool_templatelibrary_list_templates",args:{component:e,search:f,themename:c},done:g,fail:d.exception}],!0,!1)},i=null,j=function(a,b){if(null!==i){window.clearTimeout(i)}i=window.setTimeout(function(){a();i=null},b)},k=function(){j(h.bind(this,f.theme),400)};a("[data-region=\"list-templates\"]").on("change","[data-field=\"component\"]",k);a("[data-region=\"list-templates\"]").on("input","[data-region=\"input\"]",k);a("[data-action=\"clearsearch\"]").on("click",function(){a("[data-region=\"input\"]").val("");h(f.theme);a(this).addClass("d-none")});a("[data-region=\"input\"]").val(document.location.hash.replace("#",""));h(f.theme);return{}});
//# sourceMappingURL=search.min.js.map
diff --git a/admin/tool/templatelibrary/amd/build/search.min.js.map b/admin/tool/templatelibrary/amd/build/search.min.js.map
index 1611c7e245d..0245bb86ee5 100644
--- a/admin/tool/templatelibrary/amd/build/search.min.js.map
+++ b/admin/tool/templatelibrary/amd/build/search.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../src/search.js"],"names":["define","$","ajax","log","notification","templates","config","reloadListTemplate","templateList","render","done","result","js","replaceNode","fail","exception","refreshSearch","themename","componentStr","val","searchStr","document","location","hash","call","methodname","args","component","search","throttle","queueRefresh","callback","delay","window","clearTimeout","setTimeout","changeHandler","bind","theme","on","replace"],"mappings":"AAuBAA,OAAM,+BAAC,CAAC,QAAD,CAAW,WAAX,CAAwB,UAAxB,CAAoC,mBAApC,CAAyD,gBAAzD,CAA2E,aAA3E,CAAD,CACC,SAASC,CAAT,CAAYC,CAAZ,CAAkBC,CAAlB,CAAuBC,CAAvB,CAAqCC,CAArC,CAAgDC,CAAhD,CAAwD,IAQvDC,CAAAA,CAAkB,CAAG,SAASC,CAAT,CAAuB,CAC5CH,CAAS,CAACI,MAAV,CAAiB,qCAAjB,CAAwD,CAACJ,SAAS,CAAEG,CAAZ,CAAxD,EACKE,IADL,CACU,SAASC,CAAT,CAAiBC,CAAjB,CAAqB,CACvBP,CAAS,CAACQ,WAAV,CAAsBZ,CAAC,CAAC,iCAAD,CAAvB,CAA0DU,CAA1D,CAAkEC,CAAlE,CACH,CAHL,EAGOE,IAHP,CAGYV,CAAY,CAACW,SAHzB,CAIH,CAb0D,CAqBvDC,CAAa,CAAG,SAASC,CAAT,CAAoB,IAChCC,CAAAA,CAAY,CAAGjB,CAAC,CAAC,4BAAD,CAAD,CAA8BkB,GAA9B,EADiB,CAEhCC,CAAS,CAAGnB,CAAC,CAAC,yBAAD,CAAD,CAA2BkB,GAA3B,EAFoB,CAKpCE,QAAQ,CAACC,QAAT,CAAkBC,IAAlB,CAAyBH,CAAzB,CAEAlB,CAAI,CAACsB,IAAL,CAAU,CACN,CAACC,UAAU,CAAE,qCAAb,CACEC,IAAI,CAAE,CAACC,SAAS,CAAET,CAAZ,CAA0BU,MAAM,CAAER,CAAlC,CAA6CH,SAAS,CAAEA,CAAxD,CADR,CAEEP,IAAI,CAAEH,CAFR,CAGEO,IAAI,CAAEV,CAAY,CAACW,SAHrB,CADM,CAAV,OAMH,CAlC0D,CAoCvDc,CAAQ,CAAG,IApC4C,CA8CvDC,CAAY,CAAG,SAASC,CAAT,CAAmBC,CAAnB,CAA0B,CACzC,GAAiB,IAAb,GAAAH,CAAJ,CAAuB,CACnBI,MAAM,CAACC,YAAP,CAAoBL,CAApB,CACH,CAEDA,CAAQ,CAAGI,MAAM,CAACE,UAAP,CAAkB,UAAW,CACpCJ,CAAQ,GACRF,CAAQ,CAAG,IACd,CAHU,CAGRG,CAHQ,CAId,CAvD0D,CAyDvDI,CAAa,CAAG,UAAW,CAC3BN,CAAY,CAACd,CAAa,CAACqB,IAAd,CAAmB,IAAnB,CAAyB/B,CAAM,CAACgC,KAAhC,CAAD,CAAyC,GAAzC,CACf,CA3D0D,CA6D3DrC,CAAC,CAAC,kCAAD,CAAD,CAAoCsC,EAApC,CAAuC,QAAvC,CAAiD,4BAAjD,CAA6EH,CAA7E,EACAnC,CAAC,CAAC,kCAAD,CAAD,CAAoCsC,EAApC,CAAuC,OAAvC,CAAgD,yBAAhD,CAAyEH,CAAzE,EAEAnC,CAAC,CAAC,yBAAD,CAAD,CAA2BkB,GAA3B,CAA+BE,QAAQ,CAACC,QAAT,CAAkBC,IAAlB,CAAuBiB,OAAvB,CAA+B,GAA/B,CAAoC,EAApC,CAA/B,EACAxB,CAAa,CAACV,CAAM,CAACgC,KAAR,CAAb,CACA,MAAO,EACV,CApEK,CAAN","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 * This module adds ajax search functions to the template library page.\n *\n * @module tool_templatelibrary/search\n * @package tool_templatelibrary\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/log', 'core/notification', 'core/templates', 'core/config'],\n function($, ajax, log, notification, templates, config) {\n\n /**\n * The ajax call has returned with a new list of templates.\n *\n * @method reloadListTemplate\n * @param {String[]} templateList List of template ids.\n */\n var reloadListTemplate = function(templateList) {\n templates.render('tool_templatelibrary/search_results', {templates: templateList})\n .done(function(result, js) {\n templates.replaceNode($('[data-region=\"searchresults\"]'), result, js);\n }).fail(notification.exception);\n };\n\n /**\n * Get the current values for the form inputs and refresh the list of matching templates.\n *\n * @method refreshSearch\n * @param {String} themename The naeme of the theme.\n */\n var refreshSearch = function(themename) {\n var componentStr = $('[data-field=\"component\"]').val();\n var searchStr = $('[data-field=\"search\"]').val();\n\n // Trigger the search.\n document.location.hash = searchStr;\n\n ajax.call([\n {methodname: 'tool_templatelibrary_list_templates',\n args: {component: componentStr, search: searchStr, themename: themename},\n done: reloadListTemplate,\n fail: notification.exception}\n ], true, false);\n };\n\n var throttle = null;\n\n /**\n * Call the specified function after a delay. If this function is called again before the function is executed,\n * the function will only be executed once.\n *\n * @method queueRefresh\n * @param {function} callback\n * @param {Number} delay The time in milliseconds to delay.\n */\n var queueRefresh = function(callback, delay) {\n if (throttle !== null) {\n window.clearTimeout(throttle);\n }\n\n throttle = window.setTimeout(function() {\n callback();\n throttle = null;\n }, delay);\n };\n\n var changeHandler = function() {\n queueRefresh(refreshSearch.bind(this, config.theme), 400);\n };\n // Add change handlers to refresh the list.\n $('[data-region=\"list-templates\"]').on('change', '[data-field=\"component\"]', changeHandler);\n $('[data-region=\"list-templates\"]').on('input', '[data-field=\"search\"]', changeHandler);\n\n $('[data-field=\"search\"]').val(document.location.hash.replace('#', ''));\n refreshSearch(config.theme);\n return {};\n});\n"],"file":"search.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/search.js"],"names":["define","$","ajax","log","notification","templates","config","reloadListTemplate","templateList","render","done","result","js","replaceNode","fail","exception","refreshSearch","themename","componentStr","val","searchStr","removeClass","addClass","document","location","hash","call","methodname","args","component","search","throttle","queueRefresh","callback","delay","window","clearTimeout","setTimeout","changeHandler","bind","theme","on","replace"],"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 * This module adds ajax search functions to the template library page.\n *\n * @module tool_templatelibrary/search\n * @package tool_templatelibrary\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/log', 'core/notification', 'core/templates', 'core/config'],\n function($, ajax, log, notification, templates, config) {\n\n /**\n * The ajax call has returned with a new list of templates.\n *\n * @method reloadListTemplate\n * @param {String[]} templateList List of template ids.\n */\n var reloadListTemplate = function(templateList) {\n templates.render('tool_templatelibrary/search_results', {templates: templateList})\n .done(function(result, js) {\n templates.replaceNode($('[data-region=\"searchresults\"]'), result, js);\n }).fail(notification.exception);\n };\n\n /**\n * Get the current values for the form inputs and refresh the list of matching templates.\n *\n * @method refreshSearch\n * @param {String} themename The naeme of the theme.\n */\n var refreshSearch = function(themename) {\n var componentStr = $('[data-field=\"component\"]').val();\n var searchStr = $('[data-region=\"list-templates\"] [data-region=\"input\"]').val();\n\n if (searchStr !== '') {\n $('[data-region=\"list-templates\"] [data-action=\"clearsearch\"]').removeClass('d-none');\n } else {\n $('[data-region=\"list-templates\"] [data-action=\"clearsearch\"]').addClass('d-none');\n }\n // Trigger the search.\n document.location.hash = searchStr;\n\n ajax.call([\n {methodname: 'tool_templatelibrary_list_templates',\n args: {component: componentStr, search: searchStr, themename: themename},\n done: reloadListTemplate,\n fail: notification.exception}\n ], true, false);\n };\n\n var throttle = null;\n\n /**\n * Call the specified function after a delay. If this function is called again before the function is executed,\n * the function will only be executed once.\n *\n * @method queueRefresh\n * @param {function} callback\n * @param {Number} delay The time in milliseconds to delay.\n */\n var queueRefresh = function(callback, delay) {\n if (throttle !== null) {\n window.clearTimeout(throttle);\n }\n\n throttle = window.setTimeout(function() {\n callback();\n throttle = null;\n }, delay);\n };\n\n var changeHandler = function() {\n queueRefresh(refreshSearch.bind(this, config.theme), 400);\n };\n // Add change handlers to refresh the list.\n $('[data-region=\"list-templates\"]').on('change', '[data-field=\"component\"]', changeHandler);\n $('[data-region=\"list-templates\"]').on('input', '[data-region=\"input\"]', changeHandler);\n $('[data-action=\"clearsearch\"]').on('click', function() {\n $('[data-region=\"input\"]').val('');\n refreshSearch(config.theme);\n $(this).addClass('d-none');\n });\n\n $('[data-region=\"input\"]').val(document.location.hash.replace('#', ''));\n refreshSearch(config.theme);\n return {};\n});\n"],"file":"search.min.js"}
\ No newline at end of file
diff --git a/admin/tool/templatelibrary/amd/src/search.js b/admin/tool/templatelibrary/amd/src/search.js
index 18623dffc0c..9caf34416b2 100644
--- a/admin/tool/templatelibrary/amd/src/search.js
+++ b/admin/tool/templatelibrary/amd/src/search.js
@@ -45,8 +45,13 @@ define(['jquery', 'core/ajax', 'core/log', 'core/notification', 'core/templates'
*/
var refreshSearch = function(themename) {
var componentStr = $('[data-field="component"]').val();
- var searchStr = $('[data-field="search"]').val();
+ var searchStr = $('[data-region="list-templates"] [data-region="input"]').val();
+ if (searchStr !== '') {
+ $('[data-region="list-templates"] [data-action="clearsearch"]').removeClass('d-none');
+ } else {
+ $('[data-region="list-templates"] [data-action="clearsearch"]').addClass('d-none');
+ }
// Trigger the search.
document.location.hash = searchStr;
@@ -84,9 +89,14 @@ define(['jquery', 'core/ajax', 'core/log', 'core/notification', 'core/templates'
};
// Add change handlers to refresh the list.
$('[data-region="list-templates"]').on('change', '[data-field="component"]', changeHandler);
- $('[data-region="list-templates"]').on('input', '[data-field="search"]', changeHandler);
+ $('[data-region="list-templates"]').on('input', '[data-region="input"]', changeHandler);
+ $('[data-action="clearsearch"]').on('click', function() {
+ $('[data-region="input"]').val('');
+ refreshSearch(config.theme);
+ $(this).addClass('d-none');
+ });
- $('[data-field="search"]').val(document.location.hash.replace('#', ''));
+ $('[data-region="input"]').val(document.location.hash.replace('#', ''));
refreshSearch(config.theme);
return {};
});
diff --git a/admin/tool/templatelibrary/templates/list_templates_page.mustache b/admin/tool/templatelibrary/templates/list_templates_page.mustache
index 0526c94f1f1..c5a454e8698 100644
--- a/admin/tool/templatelibrary/templates/list_templates_page.mustache
+++ b/admin/tool/templatelibrary/templates/list_templates_page.mustache
@@ -30,26 +30,67 @@
Context variables required for this template:
* allcomponents - array of components containing templates. Each component has a name and a component attribute.
+}}
+{{!
+ 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 .
+}}
+{{!
+ @template tool_templatelibrary/list_templates_page
+
+ Moodle template to the template library
+
+ The purpose of this template is build the entire page for the template library (by including smaller templates).
+
+ Classes required for JS:
+ * none
+
+ Data attributes required for JS:
+ * data-region, data-field
+
+ Context variables required for this template:
+ * allcomponents - array of components containing templates. Each component has a name and a component attribute.
+
}}
{{> tool_templatelibrary/search_results }}
diff --git a/backup/util/ui/renderer.php b/backup/util/ui/renderer.php
index d5b44ce9189..4a1f187f921 100644
--- a/backup/util/ui/renderer.php
+++ b/backup/util/ui/renderer.php
@@ -720,7 +720,7 @@ class core_backup_renderer extends plugin_renderer_base {
public function render_restore_course_search(restore_course_search $component) {
$url = $component->get_url();
- $output = html_writer::start_tag('div', array('class' => 'restore-course-search form-inline mb-1'));
+ $output = html_writer::start_tag('div', array('class' => 'restore-course-search mb-1'));
$output .= html_writer::start_tag('div', array('class' => 'rcs-results table-sm w-75'));
$table = new html_table();
@@ -759,22 +759,14 @@ class core_backup_renderer extends plugin_renderer_base {
$output .= html_writer::table($table);
$output .= html_writer::end_tag('div');
- $output .= html_writer::start_tag('div', array('class' => 'rcs-search'));
- $attrs = array(
- 'type' => 'text',
- 'name' => restore_course_search::$VAR_SEARCH,
- 'value' => $component->get_search(),
- 'class' => 'form-control'
- );
- $output .= html_writer::empty_tag('input', $attrs);
- $attrs = array(
- 'type' => 'submit',
- 'name' => 'searchcourses',
- 'value' => get_string('search'),
- 'class' => 'btn btn-secondary'
- );
- $output .= html_writer::empty_tag('input', $attrs);
- $output .= html_writer::end_tag('div');
+ $data = [
+ 'inform' => true,
+ 'extraclasses' => 'rcs-search mb-3 w-25',
+ 'inputname' => restore_course_search::$VAR_SEARCH,
+ 'searchstring' => get_string('searchcourses'),
+ 'query' => $component->get_search(),
+ ];
+ $output .= $this->output->render_from_template('core/search_input', $data);
$output .= html_writer::end_tag('div');
return $output;
@@ -880,7 +872,7 @@ class core_backup_renderer extends plugin_renderer_base {
public function render_restore_category_search(restore_category_search $component) {
$url = $component->get_url();
- $output = html_writer::start_tag('div', array('class' => 'restore-course-search form-inline mb-1'));
+ $output = html_writer::start_tag('div', array('class' => 'restore-course-search mb-1'));
$output .= html_writer::start_tag('div', array('class' => 'rcs-results table-sm w-75'));
$table = new html_table();
@@ -922,22 +914,14 @@ class core_backup_renderer extends plugin_renderer_base {
$output .= html_writer::table($table);
$output .= html_writer::end_tag('div');
- $output .= html_writer::start_tag('div', array('class' => 'rcs-search'));
- $attrs = array(
- 'type' => 'text',
- 'name' => restore_category_search::$VAR_SEARCH,
- 'value' => $component->get_search(),
- 'class' => 'form-control'
- );
- $output .= html_writer::empty_tag('input', $attrs);
- $attrs = array(
- 'type' => 'submit',
- 'name' => 'searchcourses',
- 'value' => get_string('search'),
- 'class' => 'btn btn-secondary'
- );
- $output .= html_writer::empty_tag('input', $attrs);
- $output .= html_writer::end_tag('div');
+ $data = [
+ 'inform' => true,
+ 'extraclasses' => 'rcs-search mb-3 w-25',
+ 'inputname' => restore_category_search::$VAR_SEARCH,
+ 'searchstring' => get_string('search'),
+ 'query' => $component->get_search(),
+ ];
+ $output .= $this->output->render_from_template('core/search_input', $data);
$output .= html_writer::end_tag('div');
return $output;
diff --git a/blocks/blog_menu/block_blog_menu.php b/blocks/blog_menu/block_blog_menu.php
index 7bc53b130c9..3c4d9c9166c 100644
--- a/blocks/blog_menu/block_blog_menu.php
+++ b/blocks/blog_menu/block_blog_menu.php
@@ -50,7 +50,7 @@ class block_blog_menu extends block_base {
}
function get_content() {
- global $CFG;
+ global $CFG, $OUTPUT;
// detect if blog enabled
if ($this->content !== NULL) {
@@ -98,15 +98,14 @@ class block_blog_menu extends block_base {
// Prepare the footer for this block
if (has_capability('moodle/blog:search', context_system::instance())) {
- // Full-text search field
- $form = html_writer::tag('label', get_string('search', 'admin'), array('for' => 'blogsearchquery',
- 'class' => 'accesshide'));
- $form .= html_writer::empty_tag('input', array('id' => 'blogsearchquery', 'class' => 'form-control mr-1',
- 'type' => 'text', 'name' => 'search'));
- $form .= html_writer::empty_tag('input', array('type' => 'submit', 'class' => 'btn btn-secondary',
- 'value' => get_string('search')));
- $this->content->footer = html_writer::tag('form', html_writer::tag('div', $form), array(
- 'class' => 'blogsearchform form-inline', 'method' => 'get', 'action' => new moodle_url('/blog/index.php')));
+
+ $data = [
+ 'action' => new moodle_url('/blog/index.php'),
+ 'inputname' => 'search',
+ 'searchstring' => get_string('search', 'admin'),
+ 'extraclasses' => 'mt-3'
+ ];
+ $this->content->footer = $OUTPUT->render_from_template('core/search_input', $data);
} else {
// No footer to display
$this->content->footer = '';
diff --git a/blocks/blog_menu/tests/behat/block_blog_menu_activity.feature b/blocks/blog_menu/tests/behat/block_blog_menu_activity.feature
index 9a342d231c4..586e00c51ec 100644
--- a/blocks/blog_menu/tests/behat/block_blog_menu_activity.feature
+++ b/blocks/blog_menu/tests/behat/block_blog_menu_activity.feature
@@ -206,7 +206,7 @@ Feature: Enable Block blog menu in an activity
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Test assignment 1"
- And I set the field "blogsearchquery" to "First"
+ And I set the field "Search" to "First"
And I press "Search"
Then I should see "S1 First Blog"
And I should see "S2 First Blog"
diff --git a/blocks/blog_menu/tests/behat/block_blog_menu_course.feature b/blocks/blog_menu/tests/behat/block_blog_menu_course.feature
index 39821d29276..11d9625b02e 100644
--- a/blocks/blog_menu/tests/behat/block_blog_menu_course.feature
+++ b/blocks/blog_menu/tests/behat/block_blog_menu_course.feature
@@ -183,7 +183,7 @@ Feature: Students can use block blog menu in a course
And I log out
When I log in as "teacher1"
And I am on "Course 1" course homepage
- And I set the field "blogsearchquery" to "First"
+ And I set the field "Search" to "First"
And I press "Search"
Then I should see "S1 First Blog"
And I should see "S2 First Blog"
diff --git a/blocks/globalsearch/block_globalsearch.php b/blocks/globalsearch/block_globalsearch.php
index d3d96b20662..91892ae26eb 100644
--- a/blocks/globalsearch/block_globalsearch.php
+++ b/blocks/globalsearch/block_globalsearch.php
@@ -64,32 +64,17 @@ class block_globalsearch extends block_base {
return $this->content;
}
- $url = new moodle_url('/search/index.php');
- $this->content->footer .= html_writer::link($url, get_string('advancedsearch', 'search'));
+ $data = [
+ 'action' => new moodle_url('/search/index.php'),
+ 'inputname' => 'q',
+ 'searchstring' => get_string('search'),
+ ];
- $this->content->text = html_writer::start_tag('div', array('class' => 'searchform'));
- $this->content->text .= html_writer::start_tag('form', array('action' => $url->out()));
- $this->content->text .= html_writer::start_tag('fieldset', array('action' => 'invisiblefieldset'));
-
- // Input.
- $this->content->text .= html_writer::tag('label', get_string('search', 'search'),
- array('for' => 'searchform_search', 'class' => 'accesshide'));
- $inputoptions = array('id' => 'searchform_search', 'name' => 'q', 'class' => 'form-control',
- 'type' => 'text', 'size' => '15');
- $this->content->text .= html_writer::empty_tag('input', $inputoptions);
-
- // Context id.
if ($this->page->context && $this->page->context->contextlevel !== CONTEXT_SYSTEM) {
- $this->content->text .= html_writer::empty_tag('input', ['type' => 'hidden',
- 'name' => 'context', 'value' => $this->page->context->id]);
+ $data['hiddenfields'] = (object) ['name' => 'context', 'value' => $this->page->context->id];
}
- // Search button.
- $this->content->text .= html_writer::tag('button', get_string('search', 'search'),
- array('id' => 'searchform_button', 'type' => 'submit', 'title' => 'globalsearch', 'class' => 'btn btn-secondary'));
- $this->content->text .= html_writer::end_tag('fieldset');
- $this->content->text .= html_writer::end_tag('form');
- $this->content->text .= html_writer::end_tag('div');
+ $this->content->text = $OUTPUT->render_from_template('core/search_input', $data);
return $this->content;
}
diff --git a/blocks/search_forums/classes/output/search_form.php b/blocks/search_forums/classes/output/search_form.php
index 8213abe987f..76b41a025be 100644
--- a/blocks/search_forums/classes/output/search_form.php
+++ b/blocks/search_forums/classes/output/search_form.php
@@ -44,8 +44,6 @@ class search_form implements renderable, templatable {
protected $courseid;
/** @var moodle_url The form action URL. */
protected $actionurl;
- /** @var moodle_url The advanced search URL. */
- protected $advancedsearchurl;
/** @var help_icon The help icon. */
protected $helpicon;
@@ -56,17 +54,17 @@ class search_form implements renderable, templatable {
*/
public function __construct($courseid) {
$this->courseid = $courseid;
- $this->actionurl = new moodle_url('/mod/forum/search.php');
- $this->advancedsearchurl = new moodle_url('/mod/forum/search.php', ['id' => $this->courseid]);
+ $this->actionurl = new moodle_url('/mod/forum/search.php', ['id' => $courseid]);
$this->helpicon = new help_icon('search', 'core');
}
public function export_for_template(renderer_base $output) {
$data = [
- 'actionurl' => $this->actionurl->out(false),
- 'courseid' => $this->courseid,
- 'advancedsearchurl' => $this->advancedsearchurl->out(false),
+ 'action' => $this->actionurl,
'helpicon' => $this->helpicon->export_for_template($output),
+ 'hiddenfields' => (object) ['name' => 'id', 'value' => $this->courseid],
+ 'inputname' => 'search',
+ 'searchstring' => get_string('search')
];
return $data;
}
diff --git a/blocks/search_forums/templates/search_form.mustache b/blocks/search_forums/templates/search_form.mustache
index b5f813325b4..320a5463365 100644
--- a/blocks/search_forums/templates/search_form.mustache
+++ b/blocks/search_forums/templates/search_form.mustache
@@ -21,25 +21,23 @@
Example context (json):
{
- "actionurl": "https://domain.example/mod/forum/search.php",
- "courseid": "2",
- "advancedsearchurl": "https://domain.example/mod/forum/search.php?id=2",
+ "action": "https://moodle.local/admin/search.php",
+ "inputname": "search",
+ "searchstring": "Search settings",
+ "value": "policy",
+ "hiddenfields": [
+ {
+ "name": "course",
+ "value": "11"
+ }
+ ],
"helpicon": ""
}
}}
-
+ {{>core/search_input}}
- {{#str}}advancedsearch, block_search_forums{{/str}}
+ {{#str}}advancedsearch, block_search_forums{{/str}}
{{#helpicon}}
{{>core/help_icon}}
{{/helpicon}}
diff --git a/blocks/search_forums/tests/behat/block_search_forums_course.feature b/blocks/search_forums/tests/behat/block_search_forums_course.feature
index dfdeb6f53ed..9444846f6fa 100644
--- a/blocks/search_forums/tests/behat/block_search_forums_course.feature
+++ b/blocks/search_forums/tests/behat/block_search_forums_course.feature
@@ -29,9 +29,8 @@ Feature: The search forums block allows users to search for forum posts on cours
Scenario: Use the search forum block in a course without any forum posts
Given I log in as "student1"
And I am on "Course 1" course homepage
- When I set the following fields to these values:
- | searchform_search | Moodle |
- And I press "Go"
+ When I set the field "Search" to "Moodle"
+ And I press "Search"
Then I should see "No posts"
Scenario: Use the search forum block in a course with a hidden forum and search for posts
@@ -50,9 +49,8 @@ Feature: The search forums block allows users to search for forum posts on cours
When I log in as "student1"
And I am on "Course 1" course homepage
And "Search forums" "block" should exist
- And I set the following fields to these values:
- | searchform_search | message |
- And I press "Go"
+ When I set the field "Search" to "message"
+ And I press "Search"
Then I should see "No posts"
Scenario: Use the search forum block in a course and search for posts
@@ -65,7 +63,6 @@ Feature: The search forums block allows users to search for forum posts on cours
When I log in as "student1"
And I am on "Course 1" course homepage
And "Search forums" "block" should exist
- And I set the following fields to these values:
- | searchform_search | message |
- And I press "Go"
+ When I set the field "Search" to "message"
+ And I press "Search"
Then I should see "My subject"
diff --git a/blocks/search_forums/tests/behat/block_search_forums_frontpage.feature b/blocks/search_forums/tests/behat/block_search_forums_frontpage.feature
index 53f3ba95767..31d868376cd 100644
--- a/blocks/search_forums/tests/behat/block_search_forums_frontpage.feature
+++ b/blocks/search_forums/tests/behat/block_search_forums_frontpage.feature
@@ -17,15 +17,13 @@ Feature: The search forums block allows users to search for forum posts on front
Scenario: Use the search forum block on the frontpage and search for posts as a user
Given I log in as "student1"
And I am on site homepage
- When I set the following fields to these values:
- | searchform_search | Moodle |
- And I press "Go"
+ When I set the field "Search" to "Moodle"
+ And I press "Search"
Then I should see "No posts"
Scenario: Use the search forum block on the frontpage and search for posts as a guest
Given I log in as "guest"
And I am on site homepage
- When I set the following fields to these values:
- | searchform_search | Moodle |
- And I press "Go"
+ When I set the field "Search" to "Moodle"
+ And I press "Search"
Then I should see "No posts"
diff --git a/blocks/settings/renderer.php b/blocks/settings/renderer.php
index bceefd8a5d8..3e928483a8d 100644
--- a/blocks/settings/renderer.php
+++ b/blocks/settings/renderer.php
@@ -144,11 +144,12 @@ class block_settings_renderer extends plugin_renderer_base {
public function search_form(moodle_url $formtarget, $searchvalue) {
$data = [
- 'action' => $formtarget->out(false),
- 'label' => get_string('searchinsettings', 'admin'),
- 'searchvalue' => $searchvalue
+ 'action' => $formtarget,
+ 'inputname' => 'query',
+ 'searchstring' => get_string('searchinsettings', 'admin'),
+ 'query' => $searchvalue
];
- return $this->render_from_template('block_settings/search_form', $data);
+ return $this->render_from_template('core/search_input', $data);
}
}
diff --git a/cohort/index.php b/cohort/index.php
index 0341bf96385..02d14341f0d 100644
--- a/cohort/index.php
+++ b/cohort/index.php
@@ -103,19 +103,21 @@ if ($editcontrols = cohort_edit_controls($context, $baseurl)) {
}
// Add search form.
-$search = html_writer::start_tag('form', array('id'=>'searchcohortquery', 'method'=>'get', 'class' => 'form-inline search-cohort'));
-$search .= html_writer::start_div('mb-1');
-$search .= html_writer::label(get_string('searchcohort', 'cohort'), 'cohort_search_q', true,
- array('class' => 'mr-1')); // No : in form labels!
-$search .= html_writer::empty_tag('input', array('id' => 'cohort_search_q', 'type' => 'text', 'name' => 'search',
- 'value' => $searchquery, 'class' => 'form-control mr-1'));
-$search .= html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('search', 'cohort'),
- 'class' => 'btn btn-secondary'));
-$search .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'contextid', 'value'=>$contextid));
-$search .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'showall', 'value'=>$showall));
-$search .= html_writer::end_div();
-$search .= html_writer::end_tag('form');
-echo $search;
+$hiddenfields = [
+ (object) ['name' => 'contextid', 'value' => $contextid],
+ (object) ['name' => 'showall', 'value' => $showall]
+];
+
+$data = [
+ 'action' => new moodle_url('/cohort/index.php'),
+ 'inputname' => 'search',
+ 'searchstring' => get_string('search', 'cohort'),
+ 'query' => $searchquery,
+ 'hiddenfields' => $hiddenfields,
+ 'extraclasses' => 'mb-3'
+];
+
+echo $OUTPUT->render_from_template('core/search_input', $data);
// Output pagination bar.
echo $OUTPUT->paging_bar($cohorts['totalcohorts'], $page, 25, $baseurl);
diff --git a/contentbank/amd/build/search.min.js b/contentbank/amd/build/search.min.js
index 16c80b09dfd..8309c2460c1 100644
--- a/contentbank/amd/build/search.min.js
+++ b/contentbank/amd/build/search.min.js
@@ -1,2 +1,2 @@
-define ("core_contentbank/search",["exports","jquery","core_contentbank/selectors","core/str","core/pending","core/utils"],function(a,b,c,d,e,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=g(b);c=g(c);e=g(e);function g(a){return a&&a.__esModule?a:{default:a}}function h(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function i(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var i=a.apply(b,c);function f(a){h(i,d,e,f,g,"next",a)}function g(a){h(i,d,e,f,g,"throw",a)}f(void 0)})}}var j=function(){var a=new e.default,d=(0,b.default)(c.default.regions.contentbank);k(d);a.resolve()};a.init=j;var k=function(a){var b=a.find(c.default.elements.searchinput)[0];a.on("click",c.default.actions.search,function(c){c.preventDefault();l(a,b.value)});a.on("click",c.default.actions.clearSearch,function(c){c.preventDefault();b.value="";b.focus();l(a,b.value)});b.addEventListener("input",(0,f.debounce)(function(){l(a,b.value)},300))},l=function(){var a=i(regeneratorRuntime.mark(function a(b,e){var f,g,h,i,j;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:f=b.find(c.default.elements.clearsearch)[0];g=b.find(c.default.elements.searchicon)[0];h=b.find(c.default.elements.cbnavbarbreadcrumb)[0];i=b.find(c.default.elements.cbnavbartotalsearch)[0];j=m(b,e);if(!(0"+a.substr(d,b.length)+""+a.substr(d+b.length)}}return c}});
+define ("core_contentbank/search",["exports","jquery","core_contentbank/selectors","core/str","core/pending","core/utils"],function(a,b,c,d,e,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=g(b);c=g(c);e=g(e);function g(a){return a&&a.__esModule?a:{default:a}}function h(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function i(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var i=a.apply(b,c);function f(a){h(i,d,e,f,g,"next",a)}function g(a){h(i,d,e,f,g,"throw",a)}f(void 0)})}}var j=function(){var a=new e.default,d=(0,b.default)(c.default.regions.contentbank);k(d);a.resolve()};a.init=j;var k=function(a){var b=a.find(c.default.elements.searchinput)[0];a.on("click",c.default.actions.search,function(c){c.preventDefault();l(a,b.value)});a.on("click",c.default.actions.clearSearch,function(c){c.preventDefault();b.value="";b.focus();l(a,b.value)});b.addEventListener("input",(0,f.debounce)(function(){l(a,b.value)},300))},l=function(){var a=i(regeneratorRuntime.mark(function a(b,e){var f,g,h,i;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:f=b.find(c.default.actions.clearSearch)[0];g=b.find(c.default.elements.cbnavbarbreadcrumb)[0];h=b.find(c.default.elements.cbnavbartotalsearch)[0];i=m(b,e);if(!(0"+a.substr(d,b.length)+""+a.substr(d+b.length)}}return c}});
//# sourceMappingURL=search.min.js.map
diff --git a/contentbank/amd/build/search.min.js.map b/contentbank/amd/build/search.min.js.map
index 0e0625892fe..ae3bad772ca 100644
--- a/contentbank/amd/build/search.min.js.map
+++ b/contentbank/amd/build/search.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../src/search.js"],"names":["init","pendingPromise","Pending","root","selectors","regions","contentbank","registerListenerEvents","resolve","searchInput","find","elements","searchinput","on","actions","search","e","preventDefault","toggleSearchResultsView","value","clearSearch","focus","addEventListener","body","searchQuery","clearSearchButton","clearsearch","searchIcon","searchicon","navbarBreadcrumb","cbnavbarbreadcrumb","navbarTotal","cbnavbartotalsearch","filteredContents","filterContents","length","classList","add","remove","innerHTML","searchTerm","contents","Array","from","listitem","searchResults","forEach","content","contentName","getAttribute","toLowerCase","includes","push","contentNameElement","querySelector","cbcontentname","highlight","text","highlightText","result","pos","indexOf","substr"],"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 * Search methods for finding contents in the content bank.\n *\n * @module core_contentbank/search\n * @package core_contentbank\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_contentbank/selectors';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {debounce} from 'core/utils';\n\n/**\n * Set up the search.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.regions.contentbank);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register contentbank search related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element for the contentbank.\n */\nconst registerListenerEvents = (root) => {\n\n const searchInput = root.find(selectors.elements.searchinput)[0];\n\n root.on('click', selectors.actions.search, function(e) {\n e.preventDefault();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n root.on('click', selectors.actions.clearSearch, function(e) {\n e.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n // The search input is also triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(root, searchInput.value);\n }, 300));\n\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query.\n *\n * @method toggleSearchResultsView\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchQuery The search query.\n */\nconst toggleSearchResultsView = async(body, searchQuery) => {\n const clearSearchButton = body.find(selectors.elements.clearsearch)[0];\n const searchIcon = body.find(selectors.elements.searchicon)[0];\n\n const navbarBreadcrumb = body.find(selectors.elements.cbnavbarbreadcrumb)[0];\n const navbarTotal = body.find(selectors.elements.cbnavbartotalsearch)[0];\n // Update the results.\n const filteredContents = filterContents(body, searchQuery);\n if (searchQuery.length > 0) {\n // As the search query is present, search results should be displayed.\n\n // Display the \"clear\" search button in the activity chooser search bar.\n searchIcon.classList.add('d-none');\n clearSearchButton.classList.remove('d-none');\n\n // Change the cb-navbar to display total items found.\n navbarBreadcrumb.classList.add('d-none');\n navbarTotal.innerHTML = await getString('itemsfound', 'core_contentbank', filteredContents.length);\n navbarTotal.classList.remove('d-none');\n } else {\n // As search query is not present, the search results should be removed.\n\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n searchIcon.classList.remove('d-none');\n\n // Display again the breadcrumb in the navbar.\n navbarBreadcrumb.classList.remove('d-none');\n navbarTotal.classList.add('d-none');\n }\n};\n\n/**\n * Return the list of contents which have a name that matches the given search term.\n *\n * @method filterContents\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchTerm The search term to match.\n * @return {Array}\n */\nconst filterContents = (body, searchTerm) => {\n const contents = Array.from(body.find(selectors.elements.listitem));\n const searchResults = [];\n contents.forEach((content) => {\n const contentName = content.getAttribute('data-name');\n if (searchTerm === '' || contentName.toLowerCase().includes(searchTerm.toLowerCase())) {\n // The content matches the search criteria so it should be displayed and hightlighted.\n searchResults.push(content);\n const contentNameElement = content.querySelector(selectors.regions.cbcontentname);\n contentNameElement.innerHTML = highlight(contentName, searchTerm);\n content.classList.remove('d-none');\n } else {\n content.classList.add('d-none');\n }\n });\n\n return searchResults;\n};\n\n/**\n * Highlight a given string in a text.\n *\n * @method highlight\n * @param {String} text The whole text.\n * @param {String} highlightText The piece of text to highlight.\n * @return {String}\n */\nconst highlight = (text, highlightText) => {\n let result = text;\n if (highlightText !== '') {\n const pos = text.toLowerCase().indexOf(highlightText.toLowerCase());\n if (pos > -1) {\n result = text.substr(0, pos) + '' + text.substr(pos, highlightText.length) + '' +\n text.substr(pos + highlightText.length);\n }\n }\n\n return result;\n};\n"],"file":"search.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/search.js"],"names":["init","pendingPromise","Pending","root","selectors","regions","contentbank","registerListenerEvents","resolve","searchInput","find","elements","searchinput","on","actions","search","e","preventDefault","toggleSearchResultsView","value","clearSearch","focus","addEventListener","body","searchQuery","clearSearchButton","navbarBreadcrumb","cbnavbarbreadcrumb","navbarTotal","cbnavbartotalsearch","filteredContents","filterContents","length","classList","remove","add","innerHTML","searchTerm","contents","Array","from","listitem","searchResults","forEach","content","contentName","getAttribute","toLowerCase","includes","push","contentNameElement","querySelector","cbcontentname","highlight","text","highlightText","result","pos","indexOf","substr"],"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 * Search methods for finding contents in the content bank.\n *\n * @module core_contentbank/search\n * @package core_contentbank\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_contentbank/selectors';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {debounce} from 'core/utils';\n\n/**\n * Set up the search.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.regions.contentbank);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register contentbank search related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element for the contentbank.\n */\nconst registerListenerEvents = (root) => {\n\n const searchInput = root.find(selectors.elements.searchinput)[0];\n\n root.on('click', selectors.actions.search, function(e) {\n e.preventDefault();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n root.on('click', selectors.actions.clearSearch, function(e) {\n e.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n // The search input is also triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(root, searchInput.value);\n }, 300));\n\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query.\n *\n * @method toggleSearchResultsView\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchQuery The search query.\n */\nconst toggleSearchResultsView = async(body, searchQuery) => {\n const clearSearchButton = body.find(selectors.actions.clearSearch)[0];\n\n const navbarBreadcrumb = body.find(selectors.elements.cbnavbarbreadcrumb)[0];\n const navbarTotal = body.find(selectors.elements.cbnavbartotalsearch)[0];\n // Update the results.\n const filteredContents = filterContents(body, searchQuery);\n if (searchQuery.length > 0) {\n // As the search query is present, search results should be displayed.\n\n // Display the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.remove('d-none');\n\n // Change the cb-navbar to display total items found.\n navbarBreadcrumb.classList.add('d-none');\n navbarTotal.innerHTML = await getString('itemsfound', 'core_contentbank', filteredContents.length);\n navbarTotal.classList.remove('d-none');\n } else {\n // As search query is not present, the search results should be removed.\n\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n\n // Display again the breadcrumb in the navbar.\n navbarBreadcrumb.classList.remove('d-none');\n navbarTotal.classList.add('d-none');\n }\n};\n\n/**\n * Return the list of contents which have a name that matches the given search term.\n *\n * @method filterContents\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchTerm The search term to match.\n * @return {Array}\n */\nconst filterContents = (body, searchTerm) => {\n const contents = Array.from(body.find(selectors.elements.listitem));\n const searchResults = [];\n contents.forEach((content) => {\n const contentName = content.getAttribute('data-name');\n if (searchTerm === '' || contentName.toLowerCase().includes(searchTerm.toLowerCase())) {\n // The content matches the search criteria so it should be displayed and hightlighted.\n searchResults.push(content);\n const contentNameElement = content.querySelector(selectors.regions.cbcontentname);\n contentNameElement.innerHTML = highlight(contentName, searchTerm);\n content.classList.remove('d-none');\n } else {\n content.classList.add('d-none');\n }\n });\n\n return searchResults;\n};\n\n/**\n * Highlight a given string in a text.\n *\n * @method highlight\n * @param {String} text The whole text.\n * @param {String} highlightText The piece of text to highlight.\n * @return {String}\n */\nconst highlight = (text, highlightText) => {\n let result = text;\n if (highlightText !== '') {\n const pos = text.toLowerCase().indexOf(highlightText.toLowerCase());\n if (pos > -1) {\n result = text.substr(0, pos) + '' + text.substr(pos, highlightText.length) + '' +\n text.substr(pos + highlightText.length);\n }\n }\n\n return result;\n};\n"],"file":"search.min.js"}
\ No newline at end of file
diff --git a/contentbank/amd/build/selectors.min.js b/contentbank/amd/build/selectors.min.js
index 26786ecc15c..5aba987d6d3 100644
--- a/contentbank/amd/build/selectors.min.js
+++ b/contentbank/amd/build/selectors.min.js
@@ -1,2 +1,2 @@
-define ("core_contentbank/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{cbcontentname:b("region","cb-content-name"),contentbank:b("region","contentbank"),filearea:b("region","filearea")},actions:{search:b("action","searchcontent"),clearSearch:b("action","clearsearchcontent"),viewgrid:b("action","viewgrid"),viewlist:b("action","viewlist"),sortname:b("action","sortname"),sortdate:b("action","sortdate"),sortsize:b("action","sortsize"),sorttype:b("action","sorttype"),sortauthor:b("action","sortauthor")},elements:{listitem:".cb-listitem",cbnavbarbreadcrumb:".cb-navbar-breadbrumb",cbnavbartotalsearch:".cb-navbar-totalsearch",clearsearch:".input-group-append .clear-icon",searchicon:".input-group-append .search-icon",searchinput:"#searchinput",sortbutton:".cb-btnsort"}};a.default=c;return a.default});
+define ("core_contentbank/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{cbcontentname:b("region","cb-content-name"),contentbank:b("region","contentbank"),filearea:b("region","filearea")},actions:{search:b("action","searchcontent"),clearSearch:b("action","clearsearch"),viewgrid:b("action","viewgrid"),viewlist:b("action","viewlist"),sortname:b("action","sortname"),sortdate:b("action","sortdate"),sortsize:b("action","sortsize"),sorttype:b("action","sorttype"),sortauthor:b("action","sortauthor")},elements:{listitem:".cb-listitem",cbnavbarbreadcrumb:".cb-navbar-breadbrumb",cbnavbartotalsearch:".cb-navbar-totalsearch",searchinput:"#searchinput",sortbutton:".cb-btnsort"}};a.default=c;return a.default});
//# sourceMappingURL=selectors.min.js.map
diff --git a/contentbank/amd/build/selectors.min.js.map b/contentbank/amd/build/selectors.min.js.map
index 2efa6a5ed18..62be4440b62 100644
--- a/contentbank/amd/build/selectors.min.js.map
+++ b/contentbank/amd/build/selectors.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../src/selectors.js"],"names":["getDataSelector","name","value","regions","cbcontentname","contentbank","filearea","actions","search","clearSearch","viewgrid","viewlist","sortname","sortdate","sortsize","sorttype","sortauthor","elements","listitem","cbnavbarbreadcrumb","cbnavbartotalsearch","clearsearch","searchicon","searchinput","sortbutton"],"mappings":"+IAgCMA,CAAAA,CAAe,CAAG,SAACC,CAAD,CAAOC,CAAP,CAAiB,CACrC,sBAAgBD,CAAhB,eAAyBC,CAAzB,OACH,C,GAEc,CACXC,OAAO,CAAE,CACLC,aAAa,CAAEJ,CAAe,CAAC,QAAD,CAAW,iBAAX,CADzB,CAELK,WAAW,CAAEL,CAAe,CAAC,QAAD,CAAW,aAAX,CAFvB,CAGLM,QAAQ,CAAEN,CAAe,CAAC,QAAD,CAAW,UAAX,CAHpB,CADE,CAMXO,OAAO,CAAE,CACLC,MAAM,CAAER,CAAe,CAAC,QAAD,CAAW,eAAX,CADlB,CAELS,WAAW,CAAET,CAAe,CAAC,QAAD,CAAW,oBAAX,CAFvB,CAGLU,QAAQ,CAAEV,CAAe,CAAC,QAAD,CAAW,UAAX,CAHpB,CAILW,QAAQ,CAAEX,CAAe,CAAC,QAAD,CAAW,UAAX,CAJpB,CAKLY,QAAQ,CAAEZ,CAAe,CAAC,QAAD,CAAW,UAAX,CALpB,CAMLa,QAAQ,CAAEb,CAAe,CAAC,QAAD,CAAW,UAAX,CANpB,CAOLc,QAAQ,CAAEd,CAAe,CAAC,QAAD,CAAW,UAAX,CAPpB,CAQLe,QAAQ,CAAEf,CAAe,CAAC,QAAD,CAAW,UAAX,CARpB,CASLgB,UAAU,CAAEhB,CAAe,CAAC,QAAD,CAAW,YAAX,CATtB,CANE,CAiBXiB,QAAQ,CAAE,CACNC,QAAQ,CAAE,cADJ,CAENC,kBAAkB,CAAE,uBAFd,CAGNC,mBAAmB,CAAE,wBAHf,CAINC,WAAW,CAAE,iCAJP,CAKNC,UAAU,CAAE,kCALN,CAMNC,WAAW,CAAE,cANP,CAONC,UAAU,CAAE,aAPN,CAjBC,C","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 * Define all of the selectors we will be using on the contentbank interface.\n *\n * @module core_contentbank/selectors\n * @package core_contentbank\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n *\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n regions: {\n cbcontentname: getDataSelector('region', 'cb-content-name'),\n contentbank: getDataSelector('region', 'contentbank'),\n filearea: getDataSelector('region', 'filearea')\n },\n actions: {\n search: getDataSelector('action', 'searchcontent'),\n clearSearch: getDataSelector('action', 'clearsearchcontent'),\n viewgrid: getDataSelector('action', 'viewgrid'),\n viewlist: getDataSelector('action', 'viewlist'),\n sortname: getDataSelector('action', 'sortname'),\n sortdate: getDataSelector('action', 'sortdate'),\n sortsize: getDataSelector('action', 'sortsize'),\n sorttype: getDataSelector('action', 'sorttype'),\n sortauthor: getDataSelector('action', 'sortauthor'),\n },\n elements: {\n listitem: '.cb-listitem',\n cbnavbarbreadcrumb: '.cb-navbar-breadbrumb',\n cbnavbartotalsearch: '.cb-navbar-totalsearch',\n clearsearch: '.input-group-append .clear-icon',\n searchicon: '.input-group-append .search-icon',\n searchinput: '#searchinput',\n sortbutton: '.cb-btnsort'\n },\n};\n"],"file":"selectors.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/selectors.js"],"names":["getDataSelector","name","value","regions","cbcontentname","contentbank","filearea","actions","search","clearSearch","viewgrid","viewlist","sortname","sortdate","sortsize","sorttype","sortauthor","elements","listitem","cbnavbarbreadcrumb","cbnavbartotalsearch","searchinput","sortbutton"],"mappings":"+IAgCMA,CAAAA,CAAe,CAAG,SAACC,CAAD,CAAOC,CAAP,CAAiB,CACrC,sBAAgBD,CAAhB,eAAyBC,CAAzB,OACH,C,GAEc,CACXC,OAAO,CAAE,CACLC,aAAa,CAAEJ,CAAe,CAAC,QAAD,CAAW,iBAAX,CADzB,CAELK,WAAW,CAAEL,CAAe,CAAC,QAAD,CAAW,aAAX,CAFvB,CAGLM,QAAQ,CAAEN,CAAe,CAAC,QAAD,CAAW,UAAX,CAHpB,CADE,CAMXO,OAAO,CAAE,CACLC,MAAM,CAAER,CAAe,CAAC,QAAD,CAAW,eAAX,CADlB,CAELS,WAAW,CAAET,CAAe,CAAC,QAAD,CAAW,aAAX,CAFvB,CAGLU,QAAQ,CAAEV,CAAe,CAAC,QAAD,CAAW,UAAX,CAHpB,CAILW,QAAQ,CAAEX,CAAe,CAAC,QAAD,CAAW,UAAX,CAJpB,CAKLY,QAAQ,CAAEZ,CAAe,CAAC,QAAD,CAAW,UAAX,CALpB,CAMLa,QAAQ,CAAEb,CAAe,CAAC,QAAD,CAAW,UAAX,CANpB,CAOLc,QAAQ,CAAEd,CAAe,CAAC,QAAD,CAAW,UAAX,CAPpB,CAQLe,QAAQ,CAAEf,CAAe,CAAC,QAAD,CAAW,UAAX,CARpB,CASLgB,UAAU,CAAEhB,CAAe,CAAC,QAAD,CAAW,YAAX,CATtB,CANE,CAiBXiB,QAAQ,CAAE,CACNC,QAAQ,CAAE,cADJ,CAENC,kBAAkB,CAAE,uBAFd,CAGNC,mBAAmB,CAAE,wBAHf,CAINC,WAAW,CAAE,cAJP,CAKNC,UAAU,CAAE,aALN,CAjBC,C","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 * Define all of the selectors we will be using on the contentbank interface.\n *\n * @module core_contentbank/selectors\n * @package core_contentbank\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n *\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n regions: {\n cbcontentname: getDataSelector('region', 'cb-content-name'),\n contentbank: getDataSelector('region', 'contentbank'),\n filearea: getDataSelector('region', 'filearea')\n },\n actions: {\n search: getDataSelector('action', 'searchcontent'),\n clearSearch: getDataSelector('action', 'clearsearch'),\n viewgrid: getDataSelector('action', 'viewgrid'),\n viewlist: getDataSelector('action', 'viewlist'),\n sortname: getDataSelector('action', 'sortname'),\n sortdate: getDataSelector('action', 'sortdate'),\n sortsize: getDataSelector('action', 'sortsize'),\n sorttype: getDataSelector('action', 'sorttype'),\n sortauthor: getDataSelector('action', 'sortauthor'),\n },\n elements: {\n listitem: '.cb-listitem',\n cbnavbarbreadcrumb: '.cb-navbar-breadbrumb',\n cbnavbartotalsearch: '.cb-navbar-totalsearch',\n searchinput: '#searchinput',\n sortbutton: '.cb-btnsort'\n },\n};\n"],"file":"selectors.min.js"}
\ No newline at end of file
diff --git a/contentbank/amd/src/search.js b/contentbank/amd/src/search.js
index e604abc037b..40a99a5a0dc 100644
--- a/contentbank/amd/src/search.js
+++ b/contentbank/amd/src/search.js
@@ -80,8 +80,7 @@ const registerListenerEvents = (root) => {
* @param {String} searchQuery The search query.
*/
const toggleSearchResultsView = async(body, searchQuery) => {
- const clearSearchButton = body.find(selectors.elements.clearsearch)[0];
- const searchIcon = body.find(selectors.elements.searchicon)[0];
+ const clearSearchButton = body.find(selectors.actions.clearSearch)[0];
const navbarBreadcrumb = body.find(selectors.elements.cbnavbarbreadcrumb)[0];
const navbarTotal = body.find(selectors.elements.cbnavbartotalsearch)[0];
@@ -91,7 +90,6 @@ const toggleSearchResultsView = async(body, searchQuery) => {
// As the search query is present, search results should be displayed.
// Display the "clear" search button in the activity chooser search bar.
- searchIcon.classList.add('d-none');
clearSearchButton.classList.remove('d-none');
// Change the cb-navbar to display total items found.
@@ -103,7 +101,6 @@ const toggleSearchResultsView = async(body, searchQuery) => {
// Hide the "clear" search button in the activity chooser search bar.
clearSearchButton.classList.add('d-none');
- searchIcon.classList.remove('d-none');
// Display again the breadcrumb in the navbar.
navbarBreadcrumb.classList.remove('d-none');
diff --git a/contentbank/amd/src/selectors.js b/contentbank/amd/src/selectors.js
index 60fe955b91d..f6de1b2a0fd 100644
--- a/contentbank/amd/src/selectors.js
+++ b/contentbank/amd/src/selectors.js
@@ -42,7 +42,7 @@ export default {
},
actions: {
search: getDataSelector('action', 'searchcontent'),
- clearSearch: getDataSelector('action', 'clearsearchcontent'),
+ clearSearch: getDataSelector('action', 'clearsearch'),
viewgrid: getDataSelector('action', 'viewgrid'),
viewlist: getDataSelector('action', 'viewlist'),
sortname: getDataSelector('action', 'sortname'),
@@ -55,8 +55,6 @@ export default {
listitem: '.cb-listitem',
cbnavbarbreadcrumb: '.cb-navbar-breadbrumb',
cbnavbartotalsearch: '.cb-navbar-totalsearch',
- clearsearch: '.input-group-append .clear-icon',
- searchicon: '.input-group-append .search-icon',
searchinput: '#searchinput',
sortbutton: '.cb-btnsort'
},
diff --git a/contentbank/templates/bankcontent/search.mustache b/contentbank/templates/bankcontent/search.mustache
index 8d028633290..6fc2e582775 100644
--- a/contentbank/templates/bankcontent/search.mustache
+++ b/contentbank/templates/bankcontent/search.mustache
@@ -18,34 +18,16 @@
@template core_contentbank/bankcontent/search
Example context (json):
- {}
+ {
+ }
}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+{{< core/search_input_auto }}
+ {{$label}}{{#str}}
+ searchcontentbankbyname, contentbank
+ {{/str}}{{/label}}
+ {{$placeholder}}{{#str}}
+ search, core
+ {{/str}}{{/placeholder}}
+{{/ core/search_input_auto }}
diff --git a/course/amd/build/local/activitychooser/dialogue.min.js b/course/amd/build/local/activitychooser/dialogue.min.js
index dbb33f45fbe..d517d5b68fa 100644
--- a/course/amd/build/local/activitychooser/dialogue.min.js
+++ b/course/amd/build/local/activitychooser/dialogue.min.js
@@ -1,2 +1,2 @@
-function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_course/local/activitychooser/dialogue",["exports","jquery","core/modal_events","core_course/local/activitychooser/selectors","core/templates","core/key_codes","core/loadingicon","core_course/local/activitychooser/repository","core/notification","core/utils"],function(a,b,c,d,e,f,g,h,i,j){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.displayChooser=void 0;b=m(b);c=l(c);d=m(d);e=l(e);h=l(h);i=m(i);var v="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function k(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;k=function(){return a};return a}function l(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=k();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function m(a){return a&&a.__esModule?a:{default:a}}function n(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function o(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var h=a.apply(b,c);function f(a){n(h,d,e,f,g,"next",a)}function g(a){n(h,d,e,f,g,"throw",a)}f(void 0)})}}function p(a,b){return u(a)||t(a,b)||r(a,b)||q()}function q(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function r(a,b){if(!a)return;if("string"==typeof a)return s(a,b);var c=Object.prototype.toString.call(a).slice(8,-1);if("Object"===c&&a.constructor)c=a.constructor.name;if("Map"===c||"Set"===c)return Array.from(c);if("Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c))return s(a,b)}function s(a,b){if(null==b||b>a.length)b=a.length;for(var c=0,d=Array(b);ca.length)b=a.length;for(var c=0,d=Array(b);c.\n\n/**\n * A type of dialogue used as for choosing options.\n *\n * @module core_course/local/chooser/dialogue\n * @package core\n * @copyright 2019 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as ModalEvents from 'core/modal_events';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport * as Templates from 'core/templates';\nimport {end, arrowLeft, arrowRight, home, enter, space} from 'core/key_codes';\nimport {addIconToContainer} from 'core/loadingicon';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport Notification from 'core/notification';\nimport {debounce} from 'core/utils';\nconst getPlugin = pluginName => import(pluginName);\n\n/**\n * Given an event from the main module 'page' navigate to it's help section via a carousel.\n *\n * @method showModuleHelp\n * @param {jQuery} carousel Our initialized carousel to manipulate\n * @param {Object} moduleData Data of the module to carousel to\n * @param {jQuery} modal We need to figure out if the current modal has a footer.\n */\nconst showModuleHelp = (carousel, moduleData, modal = null) => {\n // If we have a real footer then we need to change temporarily.\n if (modal !== null && moduleData.showFooter === true) {\n modal.setFooter(Templates.render('core_course/local/activitychooser/footer_partial', moduleData));\n }\n const help = carousel.find(selectors.regions.help)[0];\n help.innerHTML = '';\n help.classList.add('m-auto');\n\n // Add a spinner.\n const spinnerPromise = addIconToContainer(help);\n\n // Used later...\n let transitionPromiseResolver = null;\n const transitionPromise = new Promise(resolve => {\n transitionPromiseResolver = resolve;\n });\n\n // Build up the html & js ready to place into the help section.\n const contentPromise = Templates.renderForPromise('core_course/local/activitychooser/help', moduleData);\n\n // Wait for the content to be ready, and for the transition to be complet.\n Promise.all([contentPromise, spinnerPromise, transitionPromise])\n .then(([{html, js}]) => Templates.replaceNodeContents(help, html, js))\n .then(() => {\n help.querySelector(selectors.regions.chooserSummary.header).focus();\n return help;\n })\n .catch(Notification.exception);\n\n // Move to the next slide, and resolve the transition promise when it's done.\n carousel.one('slid.bs.carousel', () => {\n transitionPromiseResolver();\n });\n // Trigger the transition between 'pages'.\n carousel.carousel('next');\n};\n\n/**\n * Given a user wants to change the favourite state of a module we either add or remove the status.\n * We also propergate this change across our map of modals.\n *\n * @method manageFavouriteState\n * @param {HTMLElement} modalBody The DOM node of the modal to manipulate\n * @param {HTMLElement} caller\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n */\nconst manageFavouriteState = async(modalBody, caller, partialFavourite) => {\n const isFavourite = caller.dataset.favourited;\n const id = caller.dataset.id;\n const name = caller.dataset.name;\n const internal = caller.dataset.internal;\n // Switch on fave or not.\n if (isFavourite === 'true') {\n await Repository.unfavouriteModule(name, id);\n\n partialFavourite(internal, false, modalBody);\n } else {\n await Repository.favouriteModule(name, id);\n\n partialFavourite(internal, true, modalBody);\n }\n\n};\n\n/**\n * Register chooser related event listeners.\n *\n * @method registerListenerEvents\n * @param {Promise} modal Our modal that we are working with\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nconst registerListenerEvents = (modal, mappedModules, partialFavourite, footerData) => {\n const bodyClickListener = async(e) => {\n if (e.target.closest(selectors.actions.optionActions.showSummary)) {\n const carousel = $(modal.getBody()[0].querySelector(selectors.regions.carousel));\n\n const module = e.target.closest(selectors.regions.chooserOption.container);\n const moduleName = module.dataset.modname;\n const moduleData = mappedModules.get(moduleName);\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = modal.hasFooterContent();\n showModuleHelp(carousel, moduleData, modal);\n }\n\n if (e.target.closest(selectors.actions.optionActions.manageFavourite)) {\n const caller = e.target.closest(selectors.actions.optionActions.manageFavourite);\n await manageFavouriteState(modal.getBody()[0], caller, partialFavourite);\n const activeSectionId = modal.getBody()[0].querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = modal.getBody()[0]\n .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions\n .querySelector(selectors.regions.chooserOption.container);\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(modal.getBody()[0], mappedModules, sectionChooserOptions, modal);\n }\n\n // From the help screen go back to the module overview.\n if (e.target.matches(selectors.actions.closeOption)) {\n const carousel = $(modal.getBody()[0].querySelector(selectors.regions.carousel));\n\n // Trigger the transition between 'pages'.\n carousel.carousel('prev');\n carousel.on('slid.bs.carousel', () => {\n const allModules = modal.getBody()[0].querySelector(selectors.regions.modules);\n const caller = allModules.querySelector(selectors.regions.getModuleSelector(e.target.dataset.modname));\n caller.focus();\n });\n }\n\n // The \"clear search\" button is triggered.\n if (e.target.closest(selectors.actions.clearSearch)) {\n // Clear the entered search query in the search bar and hide the search results container.\n const searchInput = modal.getBody()[0].querySelector(selectors.actions.search);\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(modal, mappedModules, searchInput.value);\n }\n };\n\n // We essentially have two types of footer.\n // A fake one that is handled within the template for chooser_help and then all of the stuff for\n // modal.footer. We need to ensure we know exactly what type of footer we are using so we know what we\n // need to manage. The below code handles a real footer going to a mnet carousel item.\n const footerClickListener = async(e) => {\n if (footerData.footer === true) {\n const footerjs = await getPlugin(footerData.customfooterjs);\n await footerjs.footerClickListener(e, footerData, modal);\n }\n };\n\n modal.getBodyPromise()\n\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(body => body[0])\n\n // Set up the carousel.\n .then(body => {\n $(body.querySelector(selectors.regions.carousel))\n .carousel({\n interval: false,\n pause: true,\n keyboard: false\n });\n\n return body;\n })\n\n // Add the listener for clicks on the body.\n .then(body => {\n body.addEventListener('click', bodyClickListener);\n return body;\n })\n\n // Add a listener for an input change in the activity chooser's search bar.\n .then(body => {\n const searchInput = body.querySelector(selectors.actions.search);\n // The search input is triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(modal, mappedModules, searchInput.value);\n }, 300));\n return body;\n })\n\n // Register event listeners related to the keyboard navigation controls.\n .then(body => {\n // Get the active chooser options section.\n const activeSectionId = body.querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = body.querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions.querySelector(selectors.regions.chooserOption.container);\n\n toggleFocusableChooserOption(firstChooserOption, true);\n initTabsKeyboardNavigation(body);\n initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions, modal);\n\n return body;\n })\n .catch();\n\n modal.getFooterPromise()\n\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(footer => footer[0])\n // Add the listener for clicks on the footer.\n .then(footer => {\n footer.addEventListener('click', footerClickListener);\n return footer;\n })\n .catch();\n};\n\n/**\n * Initialise the keyboard navigation controls for the tab list items.\n *\n * @method initTabsKeyboardNavigation\n * @param {HTMLElement} body Our modal that we are working with\n */\nconst initTabsKeyboardNavigation = (body) => {\n // Set up the tab handlers.\n const favTabNav = body.querySelector(selectors.regions.favouriteTabNav);\n const recommendedTabNav = body.querySelector(selectors.regions.recommendedTabNav);\n const defaultTabNav = body.querySelector(selectors.regions.defaultTabNav);\n const activityTabNav = body.querySelector(selectors.regions.activityTabNav);\n const resourceTabNav = body.querySelector(selectors.regions.resourceTabNav);\n const tabNavArray = [favTabNav, recommendedTabNav, defaultTabNav, activityTabNav, resourceTabNav];\n tabNavArray.forEach((element) => {\n return element.addEventListener('keydown', (e) => {\n // The first visible navigation tab link.\n const firstLink = e.target.parentElement.querySelector(selectors.elements.visibletabs);\n // The last navigation tab link. It would always be the default activities tab link.\n const lastLink = e.target.parentElement.lastElementChild;\n\n if (e.keyCode === arrowRight) {\n const nextLink = e.target.nextElementSibling;\n if (nextLink === null) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n } else if (nextLink.classList.contains('d-none')) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n } else {\n e.target.tabIndex = -1;\n nextLink.tabIndex = 0;\n nextLink.focus();\n }\n }\n if (e.keyCode === arrowLeft) {\n const previousLink = e.target.previousElementSibling;\n if (previousLink === null) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n } else if (previousLink.classList.contains('d-none')) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n } else {\n e.target.tabIndex = -1;\n previousLink.tabIndex = 0;\n previousLink.focus();\n }\n }\n if (e.keyCode === home) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n }\n if (e.keyCode === end) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n }\n if (e.keyCode === space) {\n e.preventDefault();\n e.target.click();\n }\n });\n });\n};\n\n/**\n * Initialise the keyboard navigation controls for the chooser options.\n *\n * @method initChooserOptionsKeyboardNavigation\n * @param {HTMLElement} body Our modal that we are working with\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {HTMLElement} chooserOptionsContainer The section that contains the chooser items\n * @param {Object} modal Our created modal for the section\n */\nconst initChooserOptionsKeyboardNavigation = (body, mappedModules, chooserOptionsContainer, modal = null) => {\n const chooserOptions = chooserOptionsContainer.querySelectorAll(selectors.regions.chooserOption.container);\n\n Array.from(chooserOptions).forEach((element) => {\n return element.addEventListener('keydown', (e) => {\n\n // Check for enter/ space triggers for showing the help.\n if (e.keyCode === enter || e.keyCode === space) {\n if (e.target.matches(selectors.actions.optionActions.showSummary)) {\n e.preventDefault();\n const module = e.target.closest(selectors.regions.chooserOption.container);\n const moduleName = module.dataset.modname;\n const moduleData = mappedModules.get(moduleName);\n const carousel = $(body.querySelector(selectors.regions.carousel));\n carousel.carousel({\n interval: false,\n pause: true,\n keyboard: false\n });\n\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = modal.hasFooterContent();\n showModuleHelp(carousel, moduleData, modal);\n }\n }\n\n // Next.\n if (e.keyCode === arrowRight) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const nextOption = currentOption.nextElementSibling;\n const firstOption = chooserOptionsContainer.firstElementChild;\n const toFocusOption = clickErrorHandler(nextOption, firstOption);\n focusChooserOption(toFocusOption, currentOption);\n }\n\n // Previous.\n if (e.keyCode === arrowLeft) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const previousOption = currentOption.previousElementSibling;\n const lastOption = chooserOptionsContainer.lastElementChild;\n const toFocusOption = clickErrorHandler(previousOption, lastOption);\n focusChooserOption(toFocusOption, currentOption);\n }\n\n if (e.keyCode === home) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const firstOption = chooserOptionsContainer.firstElementChild;\n focusChooserOption(firstOption, currentOption);\n }\n\n if (e.keyCode === end) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const lastOption = chooserOptionsContainer.lastElementChild;\n focusChooserOption(lastOption, currentOption);\n }\n });\n });\n};\n\n/**\n * Focus on a chooser option element and remove the previous chooser element from the focus order\n *\n * @method focusChooserOption\n * @param {HTMLElement} currentChooserOption The current chooser option element that we want to focus\n * @param {HTMLElement|null} previousChooserOption The previous focused option element\n */\nconst focusChooserOption = (currentChooserOption, previousChooserOption = null) => {\n if (previousChooserOption !== null) {\n toggleFocusableChooserOption(previousChooserOption, false);\n }\n\n toggleFocusableChooserOption(currentChooserOption, true);\n currentChooserOption.focus();\n};\n\n/**\n * Add or remove a chooser option from the focus order.\n *\n * @method toggleFocusableChooserOption\n * @param {HTMLElement} chooserOption The chooser option element which should be added or removed from the focus order\n * @param {Boolean} isFocusable Whether the chooser element is focusable or not\n */\nconst toggleFocusableChooserOption = (chooserOption, isFocusable) => {\n const chooserOptionLink = chooserOption.querySelector(selectors.actions.addChooser);\n const chooserOptionHelp = chooserOption.querySelector(selectors.actions.optionActions.showSummary);\n const chooserOptionFavourite = chooserOption.querySelector(selectors.actions.optionActions.manageFavourite);\n\n if (isFocusable) {\n // Set tabindex to 0 to add current chooser option element to the focus order.\n chooserOption.tabIndex = 0;\n chooserOptionLink.tabIndex = 0;\n chooserOptionHelp.tabIndex = 0;\n chooserOptionFavourite.tabIndex = 0;\n } else {\n // Set tabindex to -1 to remove the previous chooser option element from the focus order.\n chooserOption.tabIndex = -1;\n chooserOptionLink.tabIndex = -1;\n chooserOptionHelp.tabIndex = -1;\n chooserOptionFavourite.tabIndex = -1;\n }\n};\n\n/**\n * Small error handling function to make sure the navigated to object exists\n *\n * @method clickErrorHandler\n * @param {HTMLElement} item What we want to check exists\n * @param {HTMLElement} fallback If we dont match anything fallback the focus\n * @return {HTMLElement}\n */\nconst clickErrorHandler = (item, fallback) => {\n if (item !== null) {\n return item;\n } else {\n return fallback;\n }\n};\n\n/**\n * Render the search results in a defined container\n *\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The container where the data should be rendered\n * @param {Object} searchResultsData Data containing the module items that satisfy the search criteria\n */\nconst renderSearchResults = async(searchResultsContainer, searchResultsData) => {\n const templateData = {\n 'searchresultsnumber': searchResultsData.length,\n 'searchresults': searchResultsData\n };\n // Build up the html & js ready to place into the help section.\n const {html, js} = await Templates.renderForPromise('core_course/local/activitychooser/search_results', templateData);\n await Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query\n *\n * @method toggleSearchResultsView\n * @param {Object} modal Our created modal for the section\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {String} searchQuery The search query\n */\nconst toggleSearchResultsView = async(modal, mappedModules, searchQuery) => {\n const modalBody = modal.getBody()[0];\n const searchResultsContainer = modalBody.querySelector(selectors.regions.searchResults);\n const chooserContainer = modalBody.querySelector(selectors.regions.chooser);\n const clearSearchButton = modalBody.querySelector(selectors.elements.clearsearch);\n const searchIcon = modalBody.querySelector(selectors.elements.searchicon);\n\n if (searchQuery.length > 0) { // Search query is present.\n const searchResultsData = searchModules(mappedModules, searchQuery);\n await renderSearchResults(searchResultsContainer, searchResultsData);\n const searchResultItemsContainer = searchResultsContainer.querySelector(selectors.regions.searchResultItems);\n const firstSearchResultItem = searchResultItemsContainer.querySelector(selectors.regions.chooserOption.container);\n if (firstSearchResultItem) {\n // Set the first result item to be focusable.\n toggleFocusableChooserOption(firstSearchResultItem, true);\n // Register keyboard events on the created search result items.\n initChooserOptionsKeyboardNavigation(modalBody, mappedModules, searchResultItemsContainer, modal);\n }\n // Display the \"clear\" search button in the activity chooser search bar.\n searchIcon.classList.add('d-none');\n clearSearchButton.classList.remove('d-none');\n // Hide the default chooser options container.\n chooserContainer.setAttribute('hidden', 'hidden');\n // Display the search results container.\n searchResultsContainer.removeAttribute('hidden');\n } else { // Search query is not present.\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n searchIcon.classList.remove('d-none');\n // Hide the search results container.\n searchResultsContainer.setAttribute('hidden', 'hidden');\n // Display the default chooser options container.\n chooserContainer.removeAttribute('hidden');\n }\n};\n\n/**\n * Return the list of modules which have a name or description that matches the given search term.\n *\n * @method searchModules\n * @param {Array} modules List of available modules\n * @param {String} searchTerm The search term to match\n * @return {Array}\n */\nconst searchModules = (modules, searchTerm) => {\n if (searchTerm === '') {\n return modules;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n modules.forEach((activity) => {\n const activityName = activity.title.toLowerCase();\n const activityDesc = activity.help.toLowerCase();\n if (activityName.includes(searchTerm) || activityDesc.includes(searchTerm)) {\n searchResults.push(activity);\n }\n });\n\n return searchResults;\n};\n\n/**\n * Set up our tabindex information across the chooser.\n *\n * @method setupKeyboardAccessibility\n * @param {Promise} modal Our created modal for the section\n * @param {Map} mappedModules A map of all of the built module information\n */\nconst setupKeyboardAccessibility = (modal, mappedModules) => {\n modal.getModal()[0].tabIndex = -1;\n\n modal.getBodyPromise().then(body => {\n $(selectors.elements.tab).on('shown.bs.tab', (e) => {\n const activeSectionId = e.target.getAttribute(\"href\");\n const activeSectionChooserOptions = body[0]\n .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = activeSectionChooserOptions\n .querySelector(selectors.regions.chooserOption.container);\n const prevActiveSectionId = e.relatedTarget.getAttribute(\"href\");\n const prevActiveSectionChooserOptions = body[0]\n .querySelector(selectors.regions.getSectionChooserOptions(prevActiveSectionId));\n\n // Disable the focus of every chooser option in the previous active section.\n disableFocusAllChooserOptions(prevActiveSectionChooserOptions);\n // Enable the focus of the first chooser option in the current active section.\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(body[0], mappedModules, activeSectionChooserOptions, modal);\n });\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Disable the focus of all chooser options in a specific container (section).\n *\n * @method disableFocusAllChooserOptions\n * @param {HTMLElement} sectionChooserOptions The section that contains the chooser items\n */\nconst disableFocusAllChooserOptions = (sectionChooserOptions) => {\n const allChooserOptions = sectionChooserOptions.querySelectorAll(selectors.regions.chooserOption.container);\n allChooserOptions.forEach((chooserOption) => {\n toggleFocusableChooserOption(chooserOption, false);\n });\n};\n\n/**\n * Display the module chooser.\n *\n * @method displayChooser\n * @param {Promise} modalPromise Our created modal for the section\n * @param {Array} sectionModules An array of all of the built module information\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nexport const displayChooser = (modalPromise, sectionModules, partialFavourite, footerData) => {\n // Make a map so we can quickly fetch a specific module's object for either rendering or searching.\n const mappedModules = new Map();\n sectionModules.forEach((module) => {\n mappedModules.set(module.componentname + '_' + module.link, module);\n });\n\n // Register event listeners.\n modalPromise.then(modal => {\n registerListenerEvents(modal, mappedModules, partialFavourite, footerData);\n\n // We want to focus on the first chooser option element as soon as the modal is opened.\n setupKeyboardAccessibility(modal, mappedModules);\n\n // We want to focus on the action select when the dialog is closed.\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n\n return modal;\n }).catch();\n};\n"],"file":"dialogue.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../../../src/local/activitychooser/dialogue.js"],"names":["getPlugin","pluginName","showModuleHelp","carousel","moduleData","modal","showFooter","setFooter","Templates","render","help","find","selectors","regions","innerHTML","classList","add","spinnerPromise","transitionPromiseResolver","transitionPromise","Promise","resolve","contentPromise","renderForPromise","all","then","html","js","replaceNodeContents","querySelector","chooserSummary","header","focus","catch","Notification","exception","one","manageFavouriteState","modalBody","caller","partialFavourite","isFavourite","dataset","favourited","id","name","internal","Repository","unfavouriteModule","favouriteModule","registerListenerEvents","mappedModules","footerData","bodyClickListener","e","target","closest","actions","optionActions","showSummary","getBody","module","chooserOption","container","moduleName","modname","get","hasFooterContent","manageFavourite","activeSectionId","elements","activetab","getAttribute","sectionChooserOptions","getSectionChooserOptions","firstChooserOption","toggleFocusableChooserOption","initChooserOptionsKeyboardNavigation","matches","closeOption","on","allModules","modules","getModuleSelector","clearSearch","searchInput","search","value","toggleSearchResultsView","footerClickListener","footer","customfooterjs","footerjs","getBodyPromise","body","interval","pause","keyboard","addEventListener","initTabsKeyboardNavigation","getFooterPromise","favTabNav","favouriteTabNav","recommendedTabNav","defaultTabNav","activityTabNav","resourceTabNav","forEach","element","firstLink","parentElement","visibletabs","lastLink","lastElementChild","keyCode","arrowRight","nextLink","nextElementSibling","tabIndex","contains","arrowLeft","previousLink","previousElementSibling","home","end","space","preventDefault","click","chooserOptionsContainer","chooserOptions","querySelectorAll","Array","from","enter","currentOption","nextOption","firstOption","firstElementChild","toFocusOption","clickErrorHandler","focusChooserOption","previousOption","lastOption","currentChooserOption","previousChooserOption","isFocusable","chooserOptionLink","addChooser","chooserOptionHelp","chooserOptionFavourite","item","fallback","renderSearchResults","searchResultsContainer","searchResultsData","templateData","length","searchQuery","searchResults","chooserContainer","chooser","clearSearchButton","searchModules","searchResultItemsContainer","searchResultItems","firstSearchResultItem","remove","setAttribute","removeAttribute","searchTerm","toLowerCase","activity","activityName","title","activityDesc","includes","push","setupKeyboardAccessibility","getModal","tab","activeSectionChooserOptions","prevActiveSectionId","relatedTarget","prevActiveSectionChooserOptions","disableFocusAllChooserOptions","allChooserOptions","displayChooser","modalPromise","sectionModules","Map","set","componentname","link","getRoot","ModalEvents","hidden","destroy"],"mappings":"wqBAwBA,OACA,OACA,OACA,OAGA,OACA,O,k+DAEMA,CAAAA,CAAS,CAAG,SAAAC,CAAU,uFAAWA,CAAX,mMAAWA,CAAX,sBAAWA,CAAX,G,CAUtBC,CAAc,CAAG,SAACC,CAAD,CAAWC,CAAX,CAAwC,IAAjBC,CAAAA,CAAiB,wDAAT,IAAS,CAE3D,GAAc,IAAV,GAAAA,CAAK,EAAa,KAAAD,CAAU,CAACE,UAAjC,CAAsD,CAClDD,CAAK,CAACE,SAAN,CAAgBC,CAAS,CAACC,MAAV,CAAiB,kDAAjB,CAAqEL,CAArE,CAAhB,CACH,CACD,GAAMM,CAAAA,CAAI,CAAGP,CAAQ,CAACQ,IAAT,CAAcC,UAAUC,OAAV,CAAkBH,IAAhC,EAAsC,CAAtC,CAAb,CACAA,CAAI,CAACI,SAAL,CAAiB,EAAjB,CACAJ,CAAI,CAACK,SAAL,CAAeC,GAAf,CAAmB,QAAnB,EAP2D,GAUrDC,CAAAA,CAAc,CAAG,yBAAmBP,CAAnB,CAVoC,CAavDQ,CAAyB,CAAG,IAb2B,CAcrDC,CAAiB,CAAG,GAAIC,CAAAA,OAAJ,CAAY,SAAAC,CAAO,CAAI,CAC7CH,CAAyB,CAAGG,CAC/B,CAFyB,CAdiC,CAmBrDC,CAAc,CAAGd,CAAS,CAACe,gBAAV,CAA2B,wCAA3B,CAAqEnB,CAArE,CAnBoC,CAsB3DgB,OAAO,CAACI,GAAR,CAAY,CAACF,CAAD,CAAiBL,CAAjB,CAAiCE,CAAjC,CAAZ,EACKM,IADL,CACU,gCAAGC,CAAH,GAAGA,IAAH,CAASC,CAAT,GAASA,EAAT,OAAkBnB,CAAAA,CAAS,CAACoB,mBAAV,CAA8BlB,CAA9B,CAAoCgB,CAApC,CAA0CC,CAA1C,CAAlB,CADV,EAEKF,IAFL,CAEU,UAAM,CACRf,CAAI,CAACmB,aAAL,CAAmBjB,UAAUC,OAAV,CAAkBiB,cAAlB,CAAiCC,MAApD,EAA4DC,KAA5D,GACA,MAAOtB,CAAAA,CACV,CALL,EAMKuB,KANL,CAMWC,UAAaC,SANxB,EASAhC,CAAQ,CAACiC,GAAT,CAAa,kBAAb,CAAiC,UAAM,CACnClB,CAAyB,EAC5B,CAFD,EAIAf,CAAQ,CAACA,QAAT,CAAkB,MAAlB,CACH,C,CAWKkC,CAAoB,4CAAG,WAAMC,CAAN,CAAiBC,CAAjB,CAAyBC,CAAzB,+FACnBC,CADmB,CACLF,CAAM,CAACG,OAAP,CAAeC,UADV,CAEnBC,CAFmB,CAEdL,CAAM,CAACG,OAAP,CAAeE,EAFD,CAGnBC,CAHmB,CAGZN,CAAM,CAACG,OAAP,CAAeG,IAHH,CAInBC,CAJmB,CAIRP,CAAM,CAACG,OAAP,CAAeI,QAJP,MAML,MAAhB,GAAAL,CANqB,kCAOfM,CAAAA,CAAU,CAACC,iBAAX,CAA6BH,CAA7B,CAAmCD,CAAnC,CAPe,QASrBJ,CAAgB,CAACM,CAAD,IAAkBR,CAAlB,CAAhB,CATqB,wCAWfS,CAAAA,CAAU,CAACE,eAAX,CAA2BJ,CAA3B,CAAiCD,CAAjC,CAXe,SAarBJ,CAAgB,CAACM,CAAD,IAAiBR,CAAjB,CAAhB,CAbqB,yCAAH,uD,CA2BpBY,CAAsB,CAAG,SAAC7C,CAAD,CAAQ8C,CAAR,CAAuBX,CAAvB,CAAyCY,CAAzC,CAAwD,IAC7EC,CAAAA,CAAiB,4CAAG,WAAMC,CAAN,2GACtB,GAAIA,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB5C,UAAU6C,OAAV,CAAkBC,aAAlB,CAAgCC,WAAjD,CAAJ,CAAmE,CACzDxD,CADyD,CAC9C,cAAEE,CAAK,CAACuD,OAAN,GAAgB,CAAhB,EAAmB/B,aAAnB,CAAiCjB,UAAUC,OAAV,CAAkBV,QAAnD,CAAF,CAD8C,CAGzD0D,CAHyD,CAGhDP,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB5C,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SAAjD,CAHgD,CAIzDC,CAJyD,CAI5CH,CAAM,CAACnB,OAAP,CAAeuB,OAJ6B,CAKzD7D,CALyD,CAK5C+C,CAAa,CAACe,GAAd,CAAkBF,CAAlB,CAL4C,CAO/D5D,CAAU,CAACE,UAAX,CAAwBD,CAAK,CAAC8D,gBAAN,EAAxB,CACAjE,CAAc,CAACC,CAAD,CAAWC,CAAX,CAAuBC,CAAvB,CACjB,CAVqB,IAYlBiD,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB5C,UAAU6C,OAAV,CAAkBC,aAAlB,CAAgCU,eAAjD,CAZkB,kBAaZ7B,CAbY,CAaHe,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB5C,UAAU6C,OAAV,CAAkBC,aAAlB,CAAgCU,eAAjD,CAbG,gBAcZ/B,CAAAA,CAAoB,CAAChC,CAAK,CAACuD,OAAN,GAAgB,CAAhB,CAAD,CAAqBrB,CAArB,CAA6BC,CAA7B,CAdR,QAeZ6B,CAfY,CAeMhE,CAAK,CAACuD,OAAN,GAAgB,CAAhB,EAAmB/B,aAAnB,CAAiCjB,UAAU0D,QAAV,CAAmBC,SAApD,EAA+DC,YAA/D,CAA4E,MAA5E,CAfN,CAgBZC,CAhBY,CAgBYpE,CAAK,CAACuD,OAAN,GAAgB,CAAhB,EACzB/B,aADyB,CACXjB,UAAUC,OAAV,CAAkB6D,wBAAlB,CAA2CL,CAA3C,CADW,CAhBZ,CAkBZM,CAlBY,CAkBSF,CAAqB,CAC3C5C,aADsB,CACRjB,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SADxB,CAlBT,CAoBlBa,CAA4B,CAACD,CAAD,IAA5B,CACAE,CAAoC,CAACxE,CAAK,CAACuD,OAAN,GAAgB,CAAhB,CAAD,CAAqBT,CAArB,CAAoCsB,CAApC,CAA2DpE,CAA3D,CAApC,CArBkB,QAyBtB,GAAIiD,CAAC,CAACC,MAAF,CAASuB,OAAT,CAAiBlE,UAAU6C,OAAV,CAAkBsB,WAAnC,CAAJ,CAAqD,CAC3C5E,CAD2C,CAChC,cAAEE,CAAK,CAACuD,OAAN,GAAgB,CAAhB,EAAmB/B,aAAnB,CAAiCjB,UAAUC,OAAV,CAAkBV,QAAnD,CAAF,CADgC,CAIjDA,CAAQ,CAACA,QAAT,CAAkB,MAAlB,EACAA,CAAQ,CAAC6E,EAAT,CAAY,kBAAZ,CAAgC,UAAM,IAC5BC,CAAAA,CAAU,CAAG5E,CAAK,CAACuD,OAAN,GAAgB,CAAhB,EAAmB/B,aAAnB,CAAiCjB,UAAUC,OAAV,CAAkBqE,OAAnD,CADe,CAE5B3C,CAAM,CAAG0C,CAAU,CAACpD,aAAX,CAAyBjB,UAAUC,OAAV,CAAkBsE,iBAAlB,CAAoC7B,CAAC,CAACC,MAAF,CAASb,OAAT,CAAiBuB,OAArD,CAAzB,CAFmB,CAGlC1B,CAAM,CAACP,KAAP,EACH,CAJD,CAKH,CAGD,GAAIsB,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB5C,UAAU6C,OAAV,CAAkB2B,WAAnC,CAAJ,CAAqD,CAE3CC,CAF2C,CAE7BhF,CAAK,CAACuD,OAAN,GAAgB,CAAhB,EAAmB/B,aAAnB,CAAiCjB,UAAU6C,OAAV,CAAkB6B,MAAnD,CAF6B,CAGjDD,CAAW,CAACE,KAAZ,CAAoB,EAApB,CACAF,CAAW,CAACrD,KAAZ,GACAwD,CAAuB,CAACnF,CAAD,CAAQ8C,CAAR,CAAuBkC,CAAW,CAACE,KAAnC,CAC1B,CA5CqB,yCAAH,uDAD4D,CAoD7EE,CAAmB,4CAAG,WAAMnC,CAAN,8FACpB,KAAAF,CAAU,CAACsC,MADS,iCAEG1F,CAAAA,CAAS,CAACoD,CAAU,CAACuC,cAAZ,CAFZ,QAEdC,CAFc,uBAGdA,CAAAA,CAAQ,CAACH,mBAAT,CAA6BnC,CAA7B,CAAgCF,CAAhC,CAA4C/C,CAA5C,CAHc,yCAAH,uDApD0D,CA2DnFA,CAAK,CAACwF,cAAN,GAGCpE,IAHD,CAGM,SAAAqE,CAAI,QAAIA,CAAAA,CAAI,CAAC,CAAD,CAAR,CAHV,EAMCrE,IAND,CAMM,SAAAqE,CAAI,CAAI,CACV,cAAEA,CAAI,CAACjE,aAAL,CAAmBjB,UAAUC,OAAV,CAAkBV,QAArC,CAAF,EACKA,QADL,CACc,CACN4F,QAAQ,GADF,CAENC,KAAK,GAFC,CAGNC,QAAQ,GAHF,CADd,EAOA,MAAOH,CAAAA,CACV,CAfD,EAkBCrE,IAlBD,CAkBM,SAAAqE,CAAI,CAAI,CACVA,CAAI,CAACI,gBAAL,CAAsB,OAAtB,CAA+B7C,CAA/B,EACA,MAAOyC,CAAAA,CACV,CArBD,EAwBCrE,IAxBD,CAwBM,SAAAqE,CAAI,CAAI,CACV,GAAMT,CAAAA,CAAW,CAAGS,CAAI,CAACjE,aAAL,CAAmBjB,UAAU6C,OAAV,CAAkB6B,MAArC,CAApB,CAEAD,CAAW,CAACa,gBAAZ,CAA6B,OAA7B,CAAsC,eAAS,UAAM,CAEjDV,CAAuB,CAACnF,CAAD,CAAQ8C,CAAR,CAAuBkC,CAAW,CAACE,KAAnC,CAC1B,CAHqC,CAGnC,GAHmC,CAAtC,EAIA,MAAOO,CAAAA,CACV,CAhCD,EAmCCrE,IAnCD,CAmCM,SAAAqE,CAAI,CAAI,IAEJzB,CAAAA,CAAe,CAAGyB,CAAI,CAACjE,aAAL,CAAmBjB,UAAU0D,QAAV,CAAmBC,SAAtC,EAAiDC,YAAjD,CAA8D,MAA9D,CAFd,CAGJC,CAAqB,CAAGqB,CAAI,CAACjE,aAAL,CAAmBjB,UAAUC,OAAV,CAAkB6D,wBAAlB,CAA2CL,CAA3C,CAAnB,CAHpB,CAIJM,CAAkB,CAAGF,CAAqB,CAAC5C,aAAtB,CAAoCjB,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SAApE,CAJjB,CAMVa,CAA4B,CAACD,CAAD,IAA5B,CACAwB,CAA0B,CAACL,CAAD,CAA1B,CACAjB,CAAoC,CAACiB,CAAD,CAAO3C,CAAP,CAAsBsB,CAAtB,CAA6CpE,CAA7C,CAApC,CAEA,MAAOyF,CAAAA,CACV,CA9CD,EA+CC7D,KA/CD,GAiDA5B,CAAK,CAAC+F,gBAAN,GAGC3E,IAHD,CAGM,SAAAiE,CAAM,QAAIA,CAAAA,CAAM,CAAC,CAAD,CAAV,CAHZ,EAKCjE,IALD,CAKM,SAAAiE,CAAM,CAAI,CACZA,CAAM,CAACQ,gBAAP,CAAwB,OAAxB,CAAiCT,CAAjC,EACA,MAAOC,CAAAA,CACV,CARD,EASCzD,KATD,EAUH,C,CAQKkE,CAA0B,CAAG,SAACL,CAAD,CAAU,IAEnCO,CAAAA,CAAS,CAAGP,CAAI,CAACjE,aAAL,CAAmBjB,UAAUC,OAAV,CAAkByF,eAArC,CAFuB,CAGnCC,CAAiB,CAAGT,CAAI,CAACjE,aAAL,CAAmBjB,UAAUC,OAAV,CAAkB0F,iBAArC,CAHe,CAInCC,CAAa,CAAGV,CAAI,CAACjE,aAAL,CAAmBjB,UAAUC,OAAV,CAAkB2F,aAArC,CAJmB,CAKnCC,CAAc,CAAGX,CAAI,CAACjE,aAAL,CAAmBjB,UAAUC,OAAV,CAAkB4F,cAArC,CALkB,CAMnCC,CAAc,CAAGZ,CAAI,CAACjE,aAAL,CAAmBjB,UAAUC,OAAV,CAAkB6F,cAArC,CANkB,CAOrB,CAACL,CAAD,CAAYE,CAAZ,CAA+BC,CAA/B,CAA8CC,CAA9C,CAA8DC,CAA9D,CACpB,CAAYC,OAAZ,CAAoB,SAACC,CAAD,CAAa,CAC7B,MAAOA,CAAAA,CAAO,CAACV,gBAAR,CAAyB,SAAzB,CAAoC,SAAC5C,CAAD,CAAO,IAExCuD,CAAAA,CAAS,CAAGvD,CAAC,CAACC,MAAF,CAASuD,aAAT,CAAuBjF,aAAvB,CAAqCjB,UAAU0D,QAAV,CAAmByC,WAAxD,CAF4B,CAIxCC,CAAQ,CAAG1D,CAAC,CAACC,MAAF,CAASuD,aAAT,CAAuBG,gBAJM,CAM9C,GAAI3D,CAAC,CAAC4D,OAAF,GAAcC,YAAlB,CAA8B,CAC1B,GAAMC,CAAAA,CAAQ,CAAG9D,CAAC,CAACC,MAAF,CAAS8D,kBAA1B,CACA,GAAiB,IAAb,GAAAD,CAAJ,CAAuB,CACnB9D,CAAC,CAACC,MAAF,CAAS+D,QAAT,CAAoB,CAAC,CAArB,CACAT,CAAS,CAACS,QAAV,CAAqB,CAArB,CACAT,CAAS,CAAC7E,KAAV,EACH,CAJD,IAIO,IAAIoF,CAAQ,CAACrG,SAAT,CAAmBwG,QAAnB,CAA4B,QAA5B,CAAJ,CAA2C,CAC9CjE,CAAC,CAACC,MAAF,CAAS+D,QAAT,CAAoB,CAAC,CAArB,CACAN,CAAQ,CAACM,QAAT,CAAoB,CAApB,CACAN,CAAQ,CAAChF,KAAT,EACH,CAJM,IAIA,CACHsB,CAAC,CAACC,MAAF,CAAS+D,QAAT,CAAoB,CAAC,CAArB,CACAF,CAAQ,CAACE,QAAT,CAAoB,CAApB,CACAF,CAAQ,CAACpF,KAAT,EACH,CACJ,CACD,GAAIsB,CAAC,CAAC4D,OAAF,GAAcM,WAAlB,CAA6B,CACzB,GAAMC,CAAAA,CAAY,CAAGnE,CAAC,CAACC,MAAF,CAASmE,sBAA9B,CACA,GAAqB,IAAjB,GAAAD,CAAJ,CAA2B,CACvBnE,CAAC,CAACC,MAAF,CAAS+D,QAAT,CAAoB,CAAC,CAArB,CACAN,CAAQ,CAACM,QAAT,CAAoB,CAApB,CACAN,CAAQ,CAAChF,KAAT,EACH,CAJD,IAIO,IAAIyF,CAAY,CAAC1G,SAAb,CAAuBwG,QAAvB,CAAgC,QAAhC,CAAJ,CAA+C,CAClDjE,CAAC,CAACC,MAAF,CAAS+D,QAAT,CAAoB,CAAC,CAArB,CACAT,CAAS,CAACS,QAAV,CAAqB,CAArB,CACAT,CAAS,CAAC7E,KAAV,EACH,CAJM,IAIA,CACHsB,CAAC,CAACC,MAAF,CAAS+D,QAAT,CAAoB,CAAC,CAArB,CACAG,CAAY,CAACH,QAAb,CAAwB,CAAxB,CACAG,CAAY,CAACzF,KAAb,EACH,CACJ,CACD,GAAIsB,CAAC,CAAC4D,OAAF,GAAcS,MAAlB,CAAwB,CACpBrE,CAAC,CAACC,MAAF,CAAS+D,QAAT,CAAoB,CAAC,CAArB,CACAT,CAAS,CAACS,QAAV,CAAqB,CAArB,CACAT,CAAS,CAAC7E,KAAV,EACH,CACD,GAAIsB,CAAC,CAAC4D,OAAF,GAAcU,KAAlB,CAAuB,CACnBtE,CAAC,CAACC,MAAF,CAAS+D,QAAT,CAAoB,CAAC,CAArB,CACAN,CAAQ,CAACM,QAAT,CAAoB,CAApB,CACAN,CAAQ,CAAChF,KAAT,EACH,CACD,GAAIsB,CAAC,CAAC4D,OAAF,GAAcW,OAAlB,CAAyB,CACrBvE,CAAC,CAACwE,cAAF,GACAxE,CAAC,CAACC,MAAF,CAASwE,KAAT,EACH,CACJ,CApDM,CAqDV,CAtDD,CAuDH,C,CAWKlD,CAAoC,CAAG,SAACiB,CAAD,CAAO3C,CAAP,CAAsB6E,CAAtB,CAAgE,IAAjB3H,CAAAA,CAAiB,wDAAT,IAAS,CACnG4H,CAAc,CAAGD,CAAuB,CAACE,gBAAxB,CAAyCtH,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SAAzE,CADkF,CAGzGoE,KAAK,CAACC,IAAN,CAAWH,CAAX,EAA2BtB,OAA3B,CAAmC,SAACC,CAAD,CAAa,CAC5C,MAAOA,CAAAA,CAAO,CAACV,gBAAR,CAAyB,SAAzB,CAAoC,SAAC5C,CAAD,CAAO,CAG9C,GAAIA,CAAC,CAAC4D,OAAF,GAAcmB,OAAd,EAAuB/E,CAAC,CAAC4D,OAAF,GAAcW,OAAzC,CAAgD,CAC5C,GAAIvE,CAAC,CAACC,MAAF,CAASuB,OAAT,CAAiBlE,UAAU6C,OAAV,CAAkBC,aAAlB,CAAgCC,WAAjD,CAAJ,CAAmE,CAC/DL,CAAC,CAACwE,cAAF,GAD+D,GAEzDjE,CAAAA,CAAM,CAAGP,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB5C,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SAAjD,CAFgD,CAGzDC,CAAU,CAAGH,CAAM,CAACnB,OAAP,CAAeuB,OAH6B,CAIzD7D,CAAU,CAAG+C,CAAa,CAACe,GAAd,CAAkBF,CAAlB,CAJ4C,CAKzD7D,CAAQ,CAAG,cAAE2F,CAAI,CAACjE,aAAL,CAAmBjB,UAAUC,OAAV,CAAkBV,QAArC,CAAF,CAL8C,CAM/DA,CAAQ,CAACA,QAAT,CAAkB,CACd4F,QAAQ,GADM,CAEdC,KAAK,GAFS,CAGdC,QAAQ,GAHM,CAAlB,EAOA7F,CAAU,CAACE,UAAX,CAAwBD,CAAK,CAAC8D,gBAAN,EAAxB,CACAjE,CAAc,CAACC,CAAD,CAAWC,CAAX,CAAuBC,CAAvB,CACjB,CACJ,CAGD,GAAIiD,CAAC,CAAC4D,OAAF,GAAcC,YAAlB,CAA8B,CAC1B7D,CAAC,CAACwE,cAAF,GAD0B,GAEpBQ,CAAAA,CAAa,CAAGhF,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB5C,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SAAjD,CAFI,CAGpBwE,CAAU,CAAGD,CAAa,CAACjB,kBAHP,CAIpBmB,CAAW,CAAGR,CAAuB,CAACS,iBAJlB,CAKpBC,CAAa,CAAGC,CAAiB,CAACJ,CAAD,CAAaC,CAAb,CALb,CAM1BI,CAAkB,CAACF,CAAD,CAAgBJ,CAAhB,CACrB,CAGD,GAAIhF,CAAC,CAAC4D,OAAF,GAAcM,WAAlB,CAA6B,CACzBlE,CAAC,CAACwE,cAAF,GADyB,GAEnBQ,CAAAA,CAAa,CAAGhF,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB5C,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SAAjD,CAFG,CAGnB8E,CAAc,CAAGP,CAAa,CAACZ,sBAHZ,CAInBoB,CAAU,CAAGd,CAAuB,CAACf,gBAJlB,CAKnByB,CAAa,CAAGC,CAAiB,CAACE,CAAD,CAAiBC,CAAjB,CALd,CAMzBF,CAAkB,CAACF,CAAD,CAAgBJ,CAAhB,CACrB,CAED,GAAIhF,CAAC,CAAC4D,OAAF,GAAcS,MAAlB,CAAwB,CACpBrE,CAAC,CAACwE,cAAF,GADoB,GAEdQ,CAAAA,CAAa,CAAGhF,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB5C,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SAAjD,CAFF,CAGdyE,CAAW,CAAGR,CAAuB,CAACS,iBAHxB,CAIpBG,CAAkB,CAACJ,CAAD,CAAcF,CAAd,CACrB,CAED,GAAIhF,CAAC,CAAC4D,OAAF,GAAcU,KAAlB,CAAuB,CACnBtE,CAAC,CAACwE,cAAF,GADmB,GAEbQ,CAAAA,CAAa,CAAGhF,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB5C,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SAAjD,CAFH,CAGb+E,CAAU,CAAGd,CAAuB,CAACf,gBAHxB,CAInB2B,CAAkB,CAACE,CAAD,CAAaR,CAAb,CACrB,CACJ,CAvDM,CAwDV,CAzDD,CA0DH,C,CASKM,CAAkB,CAAG,SAACG,CAAD,CAAwD,IAAjCC,CAAAA,CAAiC,wDAAT,IAAS,CAC/E,GAA8B,IAA1B,GAAAA,CAAJ,CAAoC,CAChCpE,CAA4B,CAACoE,CAAD,IAC/B,CAEDpE,CAA4B,CAACmE,CAAD,IAA5B,CACAA,CAAoB,CAAC/G,KAArB,EACH,C,CASK4C,CAA4B,CAAG,SAACd,CAAD,CAAgBmF,CAAhB,CAAgC,IAC3DC,CAAAA,CAAiB,CAAGpF,CAAa,CAACjC,aAAd,CAA4BjB,UAAU6C,OAAV,CAAkB0F,UAA9C,CADuC,CAE3DC,CAAiB,CAAGtF,CAAa,CAACjC,aAAd,CAA4BjB,UAAU6C,OAAV,CAAkBC,aAAlB,CAAgCC,WAA5D,CAFuC,CAG3D0F,CAAsB,CAAGvF,CAAa,CAACjC,aAAd,CAA4BjB,UAAU6C,OAAV,CAAkBC,aAAlB,CAAgCU,eAA5D,CAHkC,CAKjE,GAAI6E,CAAJ,CAAiB,CAEbnF,CAAa,CAACwD,QAAd,CAAyB,CAAzB,CACA4B,CAAiB,CAAC5B,QAAlB,CAA6B,CAA7B,CACA8B,CAAiB,CAAC9B,QAAlB,CAA6B,CAA7B,CACA+B,CAAsB,CAAC/B,QAAvB,CAAkC,CACrC,CAND,IAMO,CAEHxD,CAAa,CAACwD,QAAd,CAAyB,CAAC,CAA1B,CACA4B,CAAiB,CAAC5B,QAAlB,CAA6B,CAAC,CAA9B,CACA8B,CAAiB,CAAC9B,QAAlB,CAA6B,CAAC,CAA9B,CACA+B,CAAsB,CAAC/B,QAAvB,CAAkC,CAAC,CACtC,CACJ,C,CAUKqB,CAAiB,CAAG,SAACW,CAAD,CAAOC,CAAP,CAAoB,CAC1C,GAAa,IAAT,GAAAD,CAAJ,CAAmB,CACf,MAAOA,CAAAA,CACV,CAFD,IAEO,CACH,MAAOC,CAAAA,CACV,CACJ,C,CASKC,CAAmB,4CAAG,WAAMC,CAAN,CAA8BC,CAA9B,+FAClBC,CADkB,CACH,CACjB,oBAAuBD,CAAiB,CAACE,MADxB,CAEjB,cAAiBF,CAFA,CADG,gBAMClJ,CAAAA,CAAS,CAACe,gBAAV,CAA2B,kDAA3B,CAA+EoI,CAA/E,CAND,iBAMjBjI,CANiB,GAMjBA,IANiB,CAMXC,CANW,GAMXA,EANW,gBAOlBnB,CAAAA,CAAS,CAACoB,mBAAV,CAA8B6H,CAA9B,CAAsD/H,CAAtD,CAA4DC,CAA5D,CAPkB,yCAAH,uD,CAkBnB6D,CAAuB,4CAAG,WAAMnF,CAAN,CAAa8C,CAAb,CAA4B0G,CAA5B,qGACtBvH,CADsB,CACVjC,CAAK,CAACuD,OAAN,GAAgB,CAAhB,CADU,CAEtB6F,CAFsB,CAEGnH,CAAS,CAACT,aAAV,CAAwBjB,UAAUC,OAAV,CAAkBiJ,aAA1C,CAFH,CAGtBC,CAHsB,CAGHzH,CAAS,CAACT,aAAV,CAAwBjB,UAAUC,OAAV,CAAkBmJ,OAA1C,CAHG,CAItBC,CAJsB,CAIF3H,CAAS,CAACT,aAAV,CAAwBjB,UAAU6C,OAAV,CAAkB2B,WAA1C,CAJE,MAMH,CAArB,CAAAyE,CAAW,CAACD,MANY,mBAOlBF,CAPkB,CAOEQ,CAAa,CAAC/G,CAAD,CAAgB0G,CAAhB,CAPf,gBAQlBL,CAAAA,CAAmB,CAACC,CAAD,CAAyBC,CAAzB,CARD,QASlBS,CATkB,CASWV,CAAsB,CAAC5H,aAAvB,CAAqCjB,UAAUC,OAAV,CAAkBuJ,iBAAvD,CATX,CAUlBC,CAVkB,CAUMF,CAA0B,CAACtI,aAA3B,CAAyCjB,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SAAzE,CAVN,CAWxB,GAAIsG,CAAJ,CAA2B,CAEvBzF,CAA4B,CAACyF,CAAD,IAA5B,CAEAxF,CAAoC,CAACvC,CAAD,CAAYa,CAAZ,CAA2BgH,CAA3B,CAAuD9J,CAAvD,CACvC,CAED4J,CAAiB,CAAClJ,SAAlB,CAA4BuJ,MAA5B,CAAmC,QAAnC,EAEAP,CAAgB,CAACQ,YAAjB,CAA8B,QAA9B,CAAwC,QAAxC,EAEAd,CAAsB,CAACe,eAAvB,CAAuC,QAAvC,EAtBwB,wBAyBxBP,CAAiB,CAAClJ,SAAlB,CAA4BC,GAA5B,CAAgC,QAAhC,EAEAyI,CAAsB,CAACc,YAAvB,CAAoC,QAApC,CAA8C,QAA9C,EAEAR,CAAgB,CAACS,eAAjB,CAAiC,QAAjC,EA7BwB,yCAAH,uD,CAyCvBN,CAAa,CAAG,SAAChF,CAAD,CAAUuF,CAAV,CAAyB,CAC3C,GAAmB,EAAf,GAAAA,CAAJ,CAAuB,CACnB,MAAOvF,CAAAA,CACV,CACDuF,CAAU,CAAGA,CAAU,CAACC,WAAX,EAAb,CACA,GAAMZ,CAAAA,CAAa,CAAG,EAAtB,CACA5E,CAAO,CAACyB,OAAR,CAAgB,SAACgE,CAAD,CAAc,IACpBC,CAAAA,CAAY,CAAGD,CAAQ,CAACE,KAAT,CAAeH,WAAf,EADK,CAEpBI,CAAY,CAAGH,CAAQ,CAACjK,IAAT,CAAcgK,WAAd,EAFK,CAG1B,GAAIE,CAAY,CAACG,QAAb,CAAsBN,CAAtB,GAAqCK,CAAY,CAACC,QAAb,CAAsBN,CAAtB,CAAzC,CAA4E,CACxEX,CAAa,CAACkB,IAAd,CAAmBL,CAAnB,CACH,CACJ,CAND,EAQA,MAAOb,CAAAA,CACV,C,CASKmB,CAA0B,CAAG,SAAC5K,CAAD,CAAQ8C,CAAR,CAA0B,CACzD9C,CAAK,CAAC6K,QAAN,GAAiB,CAAjB,EAAoB5D,QAApB,CAA+B,CAAC,CAAhC,CAEAjH,CAAK,CAACwF,cAAN,GAAuBpE,IAAvB,CAA4B,SAAAqE,CAAI,CAAI,CAChC,cAAElF,UAAU0D,QAAV,CAAmB6G,GAArB,EAA0BnG,EAA1B,CAA6B,cAA7B,CAA6C,SAAC1B,CAAD,CAAO,IAC1Ce,CAAAA,CAAe,CAAGf,CAAC,CAACC,MAAF,CAASiB,YAAT,CAAsB,MAAtB,CADwB,CAE1C4G,CAA2B,CAAGtF,CAAI,CAAC,CAAD,CAAJ,CAC/BjE,aAD+B,CACjBjB,UAAUC,OAAV,CAAkB6D,wBAAlB,CAA2CL,CAA3C,CADiB,CAFY,CAI1CM,CAAkB,CAAGyG,CAA2B,CACjDvJ,aADsB,CACRjB,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SADxB,CAJqB,CAM1CsH,CAAmB,CAAG/H,CAAC,CAACgI,aAAF,CAAgB9G,YAAhB,CAA6B,MAA7B,CANoB,CAO1C+G,CAA+B,CAAGzF,CAAI,CAAC,CAAD,CAAJ,CACnCjE,aADmC,CACrBjB,UAAUC,OAAV,CAAkB6D,wBAAlB,CAA2C2G,CAA3C,CADqB,CAPQ,CAWhDG,CAA6B,CAACD,CAAD,CAA7B,CAEA3G,CAA4B,CAACD,CAAD,IAA5B,CACAE,CAAoC,CAACiB,CAAI,CAAC,CAAD,CAAL,CAAU3C,CAAV,CAAyBiI,CAAzB,CAAsD/K,CAAtD,CACvC,CAfD,CAiBH,CAlBD,EAkBG4B,KAlBH,CAkBSC,UAAaC,SAlBtB,CAmBH,C,CAQKqJ,CAA6B,CAAG,SAAC/G,CAAD,CAA2B,CAC7D,GAAMgH,CAAAA,CAAiB,CAAGhH,CAAqB,CAACyD,gBAAtB,CAAuCtH,UAAUC,OAAV,CAAkBiD,aAAlB,CAAgCC,SAAvE,CAA1B,CACA0H,CAAiB,CAAC9E,OAAlB,CAA0B,SAAC7C,CAAD,CAAmB,CACzCc,CAA4B,CAACd,CAAD,IAC/B,CAFD,CAGH,C,kBAW6B,QAAjB4H,CAAAA,cAAiB,CAACC,CAAD,CAAeC,CAAf,CAA+BpJ,CAA/B,CAAiDY,CAAjD,CAAgE,CAE1F,GAAMD,CAAAA,CAAa,CAAG,GAAI0I,CAAAA,GAA1B,CACAD,CAAc,CAACjF,OAAf,CAAuB,SAAC9C,CAAD,CAAY,CAC/BV,CAAa,CAAC2I,GAAd,CAAkBjI,CAAM,CAACkI,aAAP,CAAuB,GAAvB,CAA6BlI,CAAM,CAACmI,IAAtD,CAA4DnI,CAA5D,CACH,CAFD,EAKA8H,CAAY,CAAClK,IAAb,CAAkB,SAAApB,CAAK,CAAI,CACvB6C,CAAsB,CAAC7C,CAAD,CAAQ8C,CAAR,CAAuBX,CAAvB,CAAyCY,CAAzC,CAAtB,CAGA6H,CAA0B,CAAC5K,CAAD,CAAQ8C,CAAR,CAA1B,CAGA9C,CAAK,CAAC4L,OAAN,GAAgBjH,EAAhB,CAAmBkH,CAAW,CAACC,MAA/B,CAAuC,UAAM,CACzC9L,CAAK,CAAC+L,OAAN,EACH,CAFD,EAIA,MAAO/L,CAAAA,CACV,CAZD,EAYG4B,KAZH,EAaH,C","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 * A type of dialogue used as for choosing options.\n *\n * @module core_course/local/chooser/dialogue\n * @package core\n * @copyright 2019 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as ModalEvents from 'core/modal_events';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport * as Templates from 'core/templates';\nimport {end, arrowLeft, arrowRight, home, enter, space} from 'core/key_codes';\nimport {addIconToContainer} from 'core/loadingicon';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport Notification from 'core/notification';\nimport {debounce} from 'core/utils';\nconst getPlugin = pluginName => import(pluginName);\n\n/**\n * Given an event from the main module 'page' navigate to it's help section via a carousel.\n *\n * @method showModuleHelp\n * @param {jQuery} carousel Our initialized carousel to manipulate\n * @param {Object} moduleData Data of the module to carousel to\n * @param {jQuery} modal We need to figure out if the current modal has a footer.\n */\nconst showModuleHelp = (carousel, moduleData, modal = null) => {\n // If we have a real footer then we need to change temporarily.\n if (modal !== null && moduleData.showFooter === true) {\n modal.setFooter(Templates.render('core_course/local/activitychooser/footer_partial', moduleData));\n }\n const help = carousel.find(selectors.regions.help)[0];\n help.innerHTML = '';\n help.classList.add('m-auto');\n\n // Add a spinner.\n const spinnerPromise = addIconToContainer(help);\n\n // Used later...\n let transitionPromiseResolver = null;\n const transitionPromise = new Promise(resolve => {\n transitionPromiseResolver = resolve;\n });\n\n // Build up the html & js ready to place into the help section.\n const contentPromise = Templates.renderForPromise('core_course/local/activitychooser/help', moduleData);\n\n // Wait for the content to be ready, and for the transition to be complet.\n Promise.all([contentPromise, spinnerPromise, transitionPromise])\n .then(([{html, js}]) => Templates.replaceNodeContents(help, html, js))\n .then(() => {\n help.querySelector(selectors.regions.chooserSummary.header).focus();\n return help;\n })\n .catch(Notification.exception);\n\n // Move to the next slide, and resolve the transition promise when it's done.\n carousel.one('slid.bs.carousel', () => {\n transitionPromiseResolver();\n });\n // Trigger the transition between 'pages'.\n carousel.carousel('next');\n};\n\n/**\n * Given a user wants to change the favourite state of a module we either add or remove the status.\n * We also propergate this change across our map of modals.\n *\n * @method manageFavouriteState\n * @param {HTMLElement} modalBody The DOM node of the modal to manipulate\n * @param {HTMLElement} caller\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n */\nconst manageFavouriteState = async(modalBody, caller, partialFavourite) => {\n const isFavourite = caller.dataset.favourited;\n const id = caller.dataset.id;\n const name = caller.dataset.name;\n const internal = caller.dataset.internal;\n // Switch on fave or not.\n if (isFavourite === 'true') {\n await Repository.unfavouriteModule(name, id);\n\n partialFavourite(internal, false, modalBody);\n } else {\n await Repository.favouriteModule(name, id);\n\n partialFavourite(internal, true, modalBody);\n }\n\n};\n\n/**\n * Register chooser related event listeners.\n *\n * @method registerListenerEvents\n * @param {Promise} modal Our modal that we are working with\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nconst registerListenerEvents = (modal, mappedModules, partialFavourite, footerData) => {\n const bodyClickListener = async(e) => {\n if (e.target.closest(selectors.actions.optionActions.showSummary)) {\n const carousel = $(modal.getBody()[0].querySelector(selectors.regions.carousel));\n\n const module = e.target.closest(selectors.regions.chooserOption.container);\n const moduleName = module.dataset.modname;\n const moduleData = mappedModules.get(moduleName);\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = modal.hasFooterContent();\n showModuleHelp(carousel, moduleData, modal);\n }\n\n if (e.target.closest(selectors.actions.optionActions.manageFavourite)) {\n const caller = e.target.closest(selectors.actions.optionActions.manageFavourite);\n await manageFavouriteState(modal.getBody()[0], caller, partialFavourite);\n const activeSectionId = modal.getBody()[0].querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = modal.getBody()[0]\n .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions\n .querySelector(selectors.regions.chooserOption.container);\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(modal.getBody()[0], mappedModules, sectionChooserOptions, modal);\n }\n\n // From the help screen go back to the module overview.\n if (e.target.matches(selectors.actions.closeOption)) {\n const carousel = $(modal.getBody()[0].querySelector(selectors.regions.carousel));\n\n // Trigger the transition between 'pages'.\n carousel.carousel('prev');\n carousel.on('slid.bs.carousel', () => {\n const allModules = modal.getBody()[0].querySelector(selectors.regions.modules);\n const caller = allModules.querySelector(selectors.regions.getModuleSelector(e.target.dataset.modname));\n caller.focus();\n });\n }\n\n // The \"clear search\" button is triggered.\n if (e.target.closest(selectors.actions.clearSearch)) {\n // Clear the entered search query in the search bar and hide the search results container.\n const searchInput = modal.getBody()[0].querySelector(selectors.actions.search);\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(modal, mappedModules, searchInput.value);\n }\n };\n\n // We essentially have two types of footer.\n // A fake one that is handled within the template for chooser_help and then all of the stuff for\n // modal.footer. We need to ensure we know exactly what type of footer we are using so we know what we\n // need to manage. The below code handles a real footer going to a mnet carousel item.\n const footerClickListener = async(e) => {\n if (footerData.footer === true) {\n const footerjs = await getPlugin(footerData.customfooterjs);\n await footerjs.footerClickListener(e, footerData, modal);\n }\n };\n\n modal.getBodyPromise()\n\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(body => body[0])\n\n // Set up the carousel.\n .then(body => {\n $(body.querySelector(selectors.regions.carousel))\n .carousel({\n interval: false,\n pause: true,\n keyboard: false\n });\n\n return body;\n })\n\n // Add the listener for clicks on the body.\n .then(body => {\n body.addEventListener('click', bodyClickListener);\n return body;\n })\n\n // Add a listener for an input change in the activity chooser's search bar.\n .then(body => {\n const searchInput = body.querySelector(selectors.actions.search);\n // The search input is triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(modal, mappedModules, searchInput.value);\n }, 300));\n return body;\n })\n\n // Register event listeners related to the keyboard navigation controls.\n .then(body => {\n // Get the active chooser options section.\n const activeSectionId = body.querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = body.querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions.querySelector(selectors.regions.chooserOption.container);\n\n toggleFocusableChooserOption(firstChooserOption, true);\n initTabsKeyboardNavigation(body);\n initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions, modal);\n\n return body;\n })\n .catch();\n\n modal.getFooterPromise()\n\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(footer => footer[0])\n // Add the listener for clicks on the footer.\n .then(footer => {\n footer.addEventListener('click', footerClickListener);\n return footer;\n })\n .catch();\n};\n\n/**\n * Initialise the keyboard navigation controls for the tab list items.\n *\n * @method initTabsKeyboardNavigation\n * @param {HTMLElement} body Our modal that we are working with\n */\nconst initTabsKeyboardNavigation = (body) => {\n // Set up the tab handlers.\n const favTabNav = body.querySelector(selectors.regions.favouriteTabNav);\n const recommendedTabNav = body.querySelector(selectors.regions.recommendedTabNav);\n const defaultTabNav = body.querySelector(selectors.regions.defaultTabNav);\n const activityTabNav = body.querySelector(selectors.regions.activityTabNav);\n const resourceTabNav = body.querySelector(selectors.regions.resourceTabNav);\n const tabNavArray = [favTabNav, recommendedTabNav, defaultTabNav, activityTabNav, resourceTabNav];\n tabNavArray.forEach((element) => {\n return element.addEventListener('keydown', (e) => {\n // The first visible navigation tab link.\n const firstLink = e.target.parentElement.querySelector(selectors.elements.visibletabs);\n // The last navigation tab link. It would always be the default activities tab link.\n const lastLink = e.target.parentElement.lastElementChild;\n\n if (e.keyCode === arrowRight) {\n const nextLink = e.target.nextElementSibling;\n if (nextLink === null) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n } else if (nextLink.classList.contains('d-none')) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n } else {\n e.target.tabIndex = -1;\n nextLink.tabIndex = 0;\n nextLink.focus();\n }\n }\n if (e.keyCode === arrowLeft) {\n const previousLink = e.target.previousElementSibling;\n if (previousLink === null) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n } else if (previousLink.classList.contains('d-none')) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n } else {\n e.target.tabIndex = -1;\n previousLink.tabIndex = 0;\n previousLink.focus();\n }\n }\n if (e.keyCode === home) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n }\n if (e.keyCode === end) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n }\n if (e.keyCode === space) {\n e.preventDefault();\n e.target.click();\n }\n });\n });\n};\n\n/**\n * Initialise the keyboard navigation controls for the chooser options.\n *\n * @method initChooserOptionsKeyboardNavigation\n * @param {HTMLElement} body Our modal that we are working with\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {HTMLElement} chooserOptionsContainer The section that contains the chooser items\n * @param {Object} modal Our created modal for the section\n */\nconst initChooserOptionsKeyboardNavigation = (body, mappedModules, chooserOptionsContainer, modal = null) => {\n const chooserOptions = chooserOptionsContainer.querySelectorAll(selectors.regions.chooserOption.container);\n\n Array.from(chooserOptions).forEach((element) => {\n return element.addEventListener('keydown', (e) => {\n\n // Check for enter/ space triggers for showing the help.\n if (e.keyCode === enter || e.keyCode === space) {\n if (e.target.matches(selectors.actions.optionActions.showSummary)) {\n e.preventDefault();\n const module = e.target.closest(selectors.regions.chooserOption.container);\n const moduleName = module.dataset.modname;\n const moduleData = mappedModules.get(moduleName);\n const carousel = $(body.querySelector(selectors.regions.carousel));\n carousel.carousel({\n interval: false,\n pause: true,\n keyboard: false\n });\n\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = modal.hasFooterContent();\n showModuleHelp(carousel, moduleData, modal);\n }\n }\n\n // Next.\n if (e.keyCode === arrowRight) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const nextOption = currentOption.nextElementSibling;\n const firstOption = chooserOptionsContainer.firstElementChild;\n const toFocusOption = clickErrorHandler(nextOption, firstOption);\n focusChooserOption(toFocusOption, currentOption);\n }\n\n // Previous.\n if (e.keyCode === arrowLeft) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const previousOption = currentOption.previousElementSibling;\n const lastOption = chooserOptionsContainer.lastElementChild;\n const toFocusOption = clickErrorHandler(previousOption, lastOption);\n focusChooserOption(toFocusOption, currentOption);\n }\n\n if (e.keyCode === home) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const firstOption = chooserOptionsContainer.firstElementChild;\n focusChooserOption(firstOption, currentOption);\n }\n\n if (e.keyCode === end) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const lastOption = chooserOptionsContainer.lastElementChild;\n focusChooserOption(lastOption, currentOption);\n }\n });\n });\n};\n\n/**\n * Focus on a chooser option element and remove the previous chooser element from the focus order\n *\n * @method focusChooserOption\n * @param {HTMLElement} currentChooserOption The current chooser option element that we want to focus\n * @param {HTMLElement|null} previousChooserOption The previous focused option element\n */\nconst focusChooserOption = (currentChooserOption, previousChooserOption = null) => {\n if (previousChooserOption !== null) {\n toggleFocusableChooserOption(previousChooserOption, false);\n }\n\n toggleFocusableChooserOption(currentChooserOption, true);\n currentChooserOption.focus();\n};\n\n/**\n * Add or remove a chooser option from the focus order.\n *\n * @method toggleFocusableChooserOption\n * @param {HTMLElement} chooserOption The chooser option element which should be added or removed from the focus order\n * @param {Boolean} isFocusable Whether the chooser element is focusable or not\n */\nconst toggleFocusableChooserOption = (chooserOption, isFocusable) => {\n const chooserOptionLink = chooserOption.querySelector(selectors.actions.addChooser);\n const chooserOptionHelp = chooserOption.querySelector(selectors.actions.optionActions.showSummary);\n const chooserOptionFavourite = chooserOption.querySelector(selectors.actions.optionActions.manageFavourite);\n\n if (isFocusable) {\n // Set tabindex to 0 to add current chooser option element to the focus order.\n chooserOption.tabIndex = 0;\n chooserOptionLink.tabIndex = 0;\n chooserOptionHelp.tabIndex = 0;\n chooserOptionFavourite.tabIndex = 0;\n } else {\n // Set tabindex to -1 to remove the previous chooser option element from the focus order.\n chooserOption.tabIndex = -1;\n chooserOptionLink.tabIndex = -1;\n chooserOptionHelp.tabIndex = -1;\n chooserOptionFavourite.tabIndex = -1;\n }\n};\n\n/**\n * Small error handling function to make sure the navigated to object exists\n *\n * @method clickErrorHandler\n * @param {HTMLElement} item What we want to check exists\n * @param {HTMLElement} fallback If we dont match anything fallback the focus\n * @return {HTMLElement}\n */\nconst clickErrorHandler = (item, fallback) => {\n if (item !== null) {\n return item;\n } else {\n return fallback;\n }\n};\n\n/**\n * Render the search results in a defined container\n *\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The container where the data should be rendered\n * @param {Object} searchResultsData Data containing the module items that satisfy the search criteria\n */\nconst renderSearchResults = async(searchResultsContainer, searchResultsData) => {\n const templateData = {\n 'searchresultsnumber': searchResultsData.length,\n 'searchresults': searchResultsData\n };\n // Build up the html & js ready to place into the help section.\n const {html, js} = await Templates.renderForPromise('core_course/local/activitychooser/search_results', templateData);\n await Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query\n *\n * @method toggleSearchResultsView\n * @param {Object} modal Our created modal for the section\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {String} searchQuery The search query\n */\nconst toggleSearchResultsView = async(modal, mappedModules, searchQuery) => {\n const modalBody = modal.getBody()[0];\n const searchResultsContainer = modalBody.querySelector(selectors.regions.searchResults);\n const chooserContainer = modalBody.querySelector(selectors.regions.chooser);\n const clearSearchButton = modalBody.querySelector(selectors.actions.clearSearch);\n\n if (searchQuery.length > 0) { // Search query is present.\n const searchResultsData = searchModules(mappedModules, searchQuery);\n await renderSearchResults(searchResultsContainer, searchResultsData);\n const searchResultItemsContainer = searchResultsContainer.querySelector(selectors.regions.searchResultItems);\n const firstSearchResultItem = searchResultItemsContainer.querySelector(selectors.regions.chooserOption.container);\n if (firstSearchResultItem) {\n // Set the first result item to be focusable.\n toggleFocusableChooserOption(firstSearchResultItem, true);\n // Register keyboard events on the created search result items.\n initChooserOptionsKeyboardNavigation(modalBody, mappedModules, searchResultItemsContainer, modal);\n }\n // Display the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.remove('d-none');\n // Hide the default chooser options container.\n chooserContainer.setAttribute('hidden', 'hidden');\n // Display the search results container.\n searchResultsContainer.removeAttribute('hidden');\n } else { // Search query is not present.\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n // Hide the search results container.\n searchResultsContainer.setAttribute('hidden', 'hidden');\n // Display the default chooser options container.\n chooserContainer.removeAttribute('hidden');\n }\n};\n\n/**\n * Return the list of modules which have a name or description that matches the given search term.\n *\n * @method searchModules\n * @param {Array} modules List of available modules\n * @param {String} searchTerm The search term to match\n * @return {Array}\n */\nconst searchModules = (modules, searchTerm) => {\n if (searchTerm === '') {\n return modules;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n modules.forEach((activity) => {\n const activityName = activity.title.toLowerCase();\n const activityDesc = activity.help.toLowerCase();\n if (activityName.includes(searchTerm) || activityDesc.includes(searchTerm)) {\n searchResults.push(activity);\n }\n });\n\n return searchResults;\n};\n\n/**\n * Set up our tabindex information across the chooser.\n *\n * @method setupKeyboardAccessibility\n * @param {Promise} modal Our created modal for the section\n * @param {Map} mappedModules A map of all of the built module information\n */\nconst setupKeyboardAccessibility = (modal, mappedModules) => {\n modal.getModal()[0].tabIndex = -1;\n\n modal.getBodyPromise().then(body => {\n $(selectors.elements.tab).on('shown.bs.tab', (e) => {\n const activeSectionId = e.target.getAttribute(\"href\");\n const activeSectionChooserOptions = body[0]\n .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = activeSectionChooserOptions\n .querySelector(selectors.regions.chooserOption.container);\n const prevActiveSectionId = e.relatedTarget.getAttribute(\"href\");\n const prevActiveSectionChooserOptions = body[0]\n .querySelector(selectors.regions.getSectionChooserOptions(prevActiveSectionId));\n\n // Disable the focus of every chooser option in the previous active section.\n disableFocusAllChooserOptions(prevActiveSectionChooserOptions);\n // Enable the focus of the first chooser option in the current active section.\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(body[0], mappedModules, activeSectionChooserOptions, modal);\n });\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Disable the focus of all chooser options in a specific container (section).\n *\n * @method disableFocusAllChooserOptions\n * @param {HTMLElement} sectionChooserOptions The section that contains the chooser items\n */\nconst disableFocusAllChooserOptions = (sectionChooserOptions) => {\n const allChooserOptions = sectionChooserOptions.querySelectorAll(selectors.regions.chooserOption.container);\n allChooserOptions.forEach((chooserOption) => {\n toggleFocusableChooserOption(chooserOption, false);\n });\n};\n\n/**\n * Display the module chooser.\n *\n * @method displayChooser\n * @param {Promise} modalPromise Our created modal for the section\n * @param {Array} sectionModules An array of all of the built module information\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nexport const displayChooser = (modalPromise, sectionModules, partialFavourite, footerData) => {\n // Make a map so we can quickly fetch a specific module's object for either rendering or searching.\n const mappedModules = new Map();\n sectionModules.forEach((module) => {\n mappedModules.set(module.componentname + '_' + module.link, module);\n });\n\n // Register event listeners.\n modalPromise.then(modal => {\n registerListenerEvents(modal, mappedModules, partialFavourite, footerData);\n\n // We want to focus on the first chooser option element as soon as the modal is opened.\n setupKeyboardAccessibility(modal, mappedModules);\n\n // We want to focus on the action select when the dialog is closed.\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n\n return modal;\n }).catch();\n};\n"],"file":"dialogue.min.js"}
\ No newline at end of file
diff --git a/course/amd/build/local/activitychooser/selectors.min.js b/course/amd/build/local/activitychooser/selectors.min.js
index 0b4a6edf1ea..45f8f4fd399 100644
--- a/course/amd/build/local/activitychooser/selectors.min.js
+++ b/course/amd/build/local/activitychooser/selectors.min.js
@@ -1,2 +1,2 @@
-define ("core_course/local/activitychooser/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{chooser:b("region","chooser-container"),getSectionChooserOptions:function getSectionChooserOptions(a){return"".concat(a," ").concat(b("region","chooser-options-container"))},chooserOption:{container:b("region","chooser-option-container"),actions:b("region","chooser-option-actions-container"),info:b("region","chooser-option-info-container")},chooserSummary:{container:b("region","chooser-option-summary-container"),content:b("region","chooser-option-summary-content-container"),header:b("region","summary-header"),actions:b("region","chooser-option-summary-actions-container")},carousel:b("region","carousel"),help:b("region","help"),modules:b("region","modules"),favouriteTabNav:b("region","favourite-tab-nav"),recommendedTabNav:b("region","recommended-tab-nav"),defaultTabNav:b("region","default-tab-nav"),activityTabNav:b("region","activity-tab-nav"),resourceTabNav:b("region","resources-tab-nav"),favouriteTab:b("region","favourites"),recommendedTab:b("region","recommended"),defaultTab:b("region","default"),activityTab:b("region","activity"),resourceTab:b("region","resources"),getModuleSelector:function getModuleSelector(a){return"[role=\"menuitem\"][data-modname=\"".concat(a,"\"]")},searchResults:b("region","search-results-container"),searchResultItems:b("region","search-result-items-container")},actions:{optionActions:{showSummary:b("action","show-option-summary"),manageFavourite:b("action","manage-module-favourite")},addChooser:b("action","add-chooser-option"),closeOption:b("action","close-chooser-option-summary"),hide:b("action","hide"),search:b("action","search"),clearSearch:b("action","clearsearch")},render:{favourites:b("render","favourites-area")},elements:{section:".section",sectionmodchooser:"button.section-modchooser-link",sitemenu:".block_site_main_menu",sitetopic:"div.sitetopic",tab:"a[data-toggle=\"tab\"]",activetab:"a[data-toggle=\"tab\"][aria-selected=\"true\"]",visibletabs:"a[data-toggle=\"tab\"]:not(.d-none)",searchicon:".input-group-append .search-icon",clearsearch:".input-group-append .clear"}};a.default=c;return a.default});
+define ("core_course/local/activitychooser/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{chooser:b("region","chooser-container"),getSectionChooserOptions:function getSectionChooserOptions(a){return"".concat(a," ").concat(b("region","chooser-options-container"))},chooserOption:{container:b("region","chooser-option-container"),actions:b("region","chooser-option-actions-container"),info:b("region","chooser-option-info-container")},chooserSummary:{container:b("region","chooser-option-summary-container"),content:b("region","chooser-option-summary-content-container"),header:b("region","summary-header"),actions:b("region","chooser-option-summary-actions-container")},carousel:b("region","carousel"),help:b("region","help"),modules:b("region","modules"),favouriteTabNav:b("region","favourite-tab-nav"),recommendedTabNav:b("region","recommended-tab-nav"),defaultTabNav:b("region","default-tab-nav"),activityTabNav:b("region","activity-tab-nav"),resourceTabNav:b("region","resources-tab-nav"),favouriteTab:b("region","favourites"),recommendedTab:b("region","recommended"),defaultTab:b("region","default"),activityTab:b("region","activity"),resourceTab:b("region","resources"),getModuleSelector:function getModuleSelector(a){return"[role=\"menuitem\"][data-modname=\"".concat(a,"\"]")},searchResults:b("region","search-results-container"),searchResultItems:b("region","search-result-items-container")},actions:{optionActions:{showSummary:b("action","show-option-summary"),manageFavourite:b("action","manage-module-favourite")},addChooser:b("action","add-chooser-option"),closeOption:b("action","close-chooser-option-summary"),hide:b("action","hide"),search:b("action","search"),clearSearch:b("action","clearsearch")},render:{favourites:b("render","favourites-area")},elements:{section:".section",sectionmodchooser:"button.section-modchooser-link",sitemenu:".block_site_main_menu",sitetopic:"div.sitetopic",tab:"a[data-toggle=\"tab\"]",activetab:"a[data-toggle=\"tab\"][aria-selected=\"true\"]",visibletabs:"a[data-toggle=\"tab\"]:not(.d-none)"}};a.default=c;return a.default});
//# sourceMappingURL=selectors.min.js.map
diff --git a/course/amd/build/local/activitychooser/selectors.min.js.map b/course/amd/build/local/activitychooser/selectors.min.js.map
index 177b53e8f34..13f9e9f9fcc 100644
--- a/course/amd/build/local/activitychooser/selectors.min.js.map
+++ b/course/amd/build/local/activitychooser/selectors.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../../../src/local/activitychooser/selectors.js"],"names":["getDataSelector","name","value","regions","chooser","getSectionChooserOptions","containerid","chooserOption","container","actions","info","chooserSummary","content","header","carousel","help","modules","favouriteTabNav","recommendedTabNav","defaultTabNav","activityTabNav","resourceTabNav","favouriteTab","recommendedTab","defaultTab","activityTab","resourceTab","getModuleSelector","modname","searchResults","searchResultItems","optionActions","showSummary","manageFavourite","addChooser","closeOption","hide","search","clearSearch","render","favourites","elements","section","sectionmodchooser","sitemenu","sitetopic","tab","activetab","visibletabs","searchicon","clearsearch"],"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 * Define all of the selectors we will be using on the grading interface.\n *\n * @module core_course/local/chooser/selectors\n * @package core_course\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n regions: {\n chooser: getDataSelector('region', 'chooser-container'),\n getSectionChooserOptions: containerid => `${containerid} ${getDataSelector('region', 'chooser-options-container')}`,\n chooserOption: {\n container: getDataSelector('region', 'chooser-option-container'),\n actions: getDataSelector('region', 'chooser-option-actions-container'),\n info: getDataSelector('region', 'chooser-option-info-container'),\n },\n chooserSummary: {\n container: getDataSelector('region', 'chooser-option-summary-container'),\n content: getDataSelector('region', 'chooser-option-summary-content-container'),\n header: getDataSelector('region', 'summary-header'),\n actions: getDataSelector('region', 'chooser-option-summary-actions-container'),\n },\n carousel: getDataSelector('region', 'carousel'),\n help: getDataSelector('region', 'help'),\n modules: getDataSelector('region', 'modules'),\n favouriteTabNav: getDataSelector('region', 'favourite-tab-nav'),\n recommendedTabNav: getDataSelector('region', 'recommended-tab-nav'),\n defaultTabNav: getDataSelector('region', 'default-tab-nav'),\n activityTabNav: getDataSelector('region', 'activity-tab-nav'),\n resourceTabNav: getDataSelector('region', 'resources-tab-nav'),\n favouriteTab: getDataSelector('region', 'favourites'),\n recommendedTab: getDataSelector('region', 'recommended'),\n defaultTab: getDataSelector('region', 'default'),\n activityTab: getDataSelector('region', 'activity'),\n resourceTab: getDataSelector('region', 'resources'),\n getModuleSelector: modname => `[role=\"menuitem\"][data-modname=\"${modname}\"]`,\n searchResults: getDataSelector('region', 'search-results-container'),\n searchResultItems: getDataSelector('region', 'search-result-items-container'),\n },\n actions: {\n optionActions: {\n showSummary: getDataSelector('action', 'show-option-summary'),\n manageFavourite: getDataSelector('action', 'manage-module-favourite'),\n },\n addChooser: getDataSelector('action', 'add-chooser-option'),\n closeOption: getDataSelector('action', 'close-chooser-option-summary'),\n hide: getDataSelector('action', 'hide'),\n search: getDataSelector('action', 'search'),\n clearSearch: getDataSelector('action', 'clearsearch'),\n },\n render: {\n favourites: getDataSelector('render', 'favourites-area'),\n },\n elements: {\n section: '.section',\n sectionmodchooser: 'button.section-modchooser-link',\n sitemenu: '.block_site_main_menu',\n sitetopic: 'div.sitetopic',\n tab: 'a[data-toggle=\"tab\"]',\n activetab: 'a[data-toggle=\"tab\"][aria-selected=\"true\"]',\n visibletabs: 'a[data-toggle=\"tab\"]:not(.d-none)',\n searchicon: '.input-group-append .search-icon',\n clearsearch: '.input-group-append .clear'\n },\n};\n"],"file":"selectors.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../../../src/local/activitychooser/selectors.js"],"names":["getDataSelector","name","value","regions","chooser","getSectionChooserOptions","containerid","chooserOption","container","actions","info","chooserSummary","content","header","carousel","help","modules","favouriteTabNav","recommendedTabNav","defaultTabNav","activityTabNav","resourceTabNav","favouriteTab","recommendedTab","defaultTab","activityTab","resourceTab","getModuleSelector","modname","searchResults","searchResultItems","optionActions","showSummary","manageFavourite","addChooser","closeOption","hide","search","clearSearch","render","favourites","elements","section","sectionmodchooser","sitemenu","sitetopic","tab","activetab","visibletabs"],"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 * Define all of the selectors we will be using on the grading interface.\n *\n * @module core_course/local/chooser/selectors\n * @package core_course\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n regions: {\n chooser: getDataSelector('region', 'chooser-container'),\n getSectionChooserOptions: containerid => `${containerid} ${getDataSelector('region', 'chooser-options-container')}`,\n chooserOption: {\n container: getDataSelector('region', 'chooser-option-container'),\n actions: getDataSelector('region', 'chooser-option-actions-container'),\n info: getDataSelector('region', 'chooser-option-info-container'),\n },\n chooserSummary: {\n container: getDataSelector('region', 'chooser-option-summary-container'),\n content: getDataSelector('region', 'chooser-option-summary-content-container'),\n header: getDataSelector('region', 'summary-header'),\n actions: getDataSelector('region', 'chooser-option-summary-actions-container'),\n },\n carousel: getDataSelector('region', 'carousel'),\n help: getDataSelector('region', 'help'),\n modules: getDataSelector('region', 'modules'),\n favouriteTabNav: getDataSelector('region', 'favourite-tab-nav'),\n recommendedTabNav: getDataSelector('region', 'recommended-tab-nav'),\n defaultTabNav: getDataSelector('region', 'default-tab-nav'),\n activityTabNav: getDataSelector('region', 'activity-tab-nav'),\n resourceTabNav: getDataSelector('region', 'resources-tab-nav'),\n favouriteTab: getDataSelector('region', 'favourites'),\n recommendedTab: getDataSelector('region', 'recommended'),\n defaultTab: getDataSelector('region', 'default'),\n activityTab: getDataSelector('region', 'activity'),\n resourceTab: getDataSelector('region', 'resources'),\n getModuleSelector: modname => `[role=\"menuitem\"][data-modname=\"${modname}\"]`,\n searchResults: getDataSelector('region', 'search-results-container'),\n searchResultItems: getDataSelector('region', 'search-result-items-container'),\n },\n actions: {\n optionActions: {\n showSummary: getDataSelector('action', 'show-option-summary'),\n manageFavourite: getDataSelector('action', 'manage-module-favourite'),\n },\n addChooser: getDataSelector('action', 'add-chooser-option'),\n closeOption: getDataSelector('action', 'close-chooser-option-summary'),\n hide: getDataSelector('action', 'hide'),\n search: getDataSelector('action', 'search'),\n clearSearch: getDataSelector('action', 'clearsearch'),\n },\n render: {\n favourites: getDataSelector('render', 'favourites-area'),\n },\n elements: {\n section: '.section',\n sectionmodchooser: 'button.section-modchooser-link',\n sitemenu: '.block_site_main_menu',\n sitetopic: 'div.sitetopic',\n tab: 'a[data-toggle=\"tab\"]',\n activetab: 'a[data-toggle=\"tab\"][aria-selected=\"true\"]',\n visibletabs: 'a[data-toggle=\"tab\"]:not(.d-none)'\n },\n};\n"],"file":"selectors.min.js"}
\ No newline at end of file
diff --git a/course/amd/src/local/activitychooser/dialogue.js b/course/amd/src/local/activitychooser/dialogue.js
index cd045a3de0d..a3bde76d89f 100644
--- a/course/amd/src/local/activitychooser/dialogue.js
+++ b/course/amd/src/local/activitychooser/dialogue.js
@@ -466,8 +466,7 @@ const toggleSearchResultsView = async(modal, mappedModules, searchQuery) => {
const modalBody = modal.getBody()[0];
const searchResultsContainer = modalBody.querySelector(selectors.regions.searchResults);
const chooserContainer = modalBody.querySelector(selectors.regions.chooser);
- const clearSearchButton = modalBody.querySelector(selectors.elements.clearsearch);
- const searchIcon = modalBody.querySelector(selectors.elements.searchicon);
+ const clearSearchButton = modalBody.querySelector(selectors.actions.clearSearch);
if (searchQuery.length > 0) { // Search query is present.
const searchResultsData = searchModules(mappedModules, searchQuery);
@@ -481,7 +480,6 @@ const toggleSearchResultsView = async(modal, mappedModules, searchQuery) => {
initChooserOptionsKeyboardNavigation(modalBody, mappedModules, searchResultItemsContainer, modal);
}
// Display the "clear" search button in the activity chooser search bar.
- searchIcon.classList.add('d-none');
clearSearchButton.classList.remove('d-none');
// Hide the default chooser options container.
chooserContainer.setAttribute('hidden', 'hidden');
@@ -490,7 +488,6 @@ const toggleSearchResultsView = async(modal, mappedModules, searchQuery) => {
} else { // Search query is not present.
// Hide the "clear" search button in the activity chooser search bar.
clearSearchButton.classList.add('d-none');
- searchIcon.classList.remove('d-none');
// Hide the search results container.
searchResultsContainer.setAttribute('hidden', 'hidden');
// Display the default chooser options container.
diff --git a/course/amd/src/local/activitychooser/selectors.js b/course/amd/src/local/activitychooser/selectors.js
index 6b54bbcde26..dd20e756c9f 100644
--- a/course/amd/src/local/activitychooser/selectors.js
+++ b/course/amd/src/local/activitychooser/selectors.js
@@ -86,8 +86,6 @@ export default {
sitetopic: 'div.sitetopic',
tab: 'a[data-toggle="tab"]',
activetab: 'a[data-toggle="tab"][aria-selected="true"]',
- visibletabs: 'a[data-toggle="tab"]:not(.d-none)',
- searchicon: '.input-group-append .search-icon',
- clearsearch: '.input-group-append .clear'
+ visibletabs: 'a[data-toggle="tab"]:not(.d-none)'
},
};
diff --git a/course/classes/management_renderer.php b/course/classes/management_renderer.php
index 25340f849f6..3de3c7a5bbf 100644
--- a/course/classes/management_renderer.php
+++ b/course/classes/management_renderer.php
@@ -1291,56 +1291,19 @@ class core_course_management_renderer extends plugin_renderer_base {
* Renders html to display a course search form
*
* @param string $value default value to populate the search field
- * @param string $format display format - 'plain' (default), 'short' or 'navbar'
* @return string
*/
- public function course_search_form($value = '', $format = 'plain') {
- static $count = 0;
- $formid = 'coursesearch';
- if ((++$count) > 1) {
- $formid .= $count;
- }
+ public function course_search_form($value = '') {
- switch ($format) {
- case 'navbar' :
- $formid = 'coursesearchnavbar';
- $inputid = 'navsearchbox';
- $inputsize = 20;
- break;
- case 'short' :
- $inputid = 'shortsearchbox';
- $inputsize = 12;
- break;
- default :
- $inputid = 'coursesearchbox';
- $inputsize = 30;
- }
-
- $strsearchcourses = get_string("searchcourses");
- $searchurl = new moodle_url('/course/management.php');
-
- $output = html_writer::start_div('row');
- $output .= html_writer::start_div('col-md-12');
- $output .= html_writer::start_tag('form', array('class' => 'card', 'id' => $formid,
- 'action' => $searchurl, 'method' => 'get'));
- $output .= html_writer::start_tag('fieldset', array('class' => 'coursesearchbox invisiblefieldset'));
- $output .= html_writer::tag('legend', $this->output->heading($strsearchcourses.': ', 2, 'm-0'),
- array('class' => 'card-header'));
- $output .= html_writer::start_div('card-body');
- $output .= html_writer::start_div('input-group col-sm-6 col-lg-4 m-auto');
- $output .= html_writer::empty_tag('input', array('class' => 'form-control', 'type' => 'text', 'id' => $inputid,
- 'size' => $inputsize, 'name' => 'search', 'value' => s($value), 'aria-label' => get_string('searchcourses')));
- $output .= html_writer::start_tag('span', array('class' => 'input-group-btn'));
- $output .= html_writer::tag('button', get_string('go'), array('class' => 'btn btn-primary', 'type' => 'submit'));
- $output .= html_writer::end_tag('span');
- $output .= html_writer::end_div();
- $output .= html_writer::end_div();
- $output .= html_writer::end_tag('fieldset');
- $output .= html_writer::end_tag('form');
- $output .= html_writer::end_div();
- $output .= html_writer::end_div();
-
- return $output;
+ $data = [
+ 'action' => new moodle_url('/course/management.php'),
+ 'btnclass' => 'btn-primary',
+ 'extraclasses' => 'my-3 d-flex justify-content-center',
+ 'inputname' => 'search',
+ 'searchstring' => get_string('searchcourses'),
+ 'value' => $value
+ ];
+ return $this->render_from_template('core/search_input', $data);
}
/**
diff --git a/course/management.php b/course/management.php
index 39ad5a77b7b..5afc685bb5b 100644
--- a/course/management.php
+++ b/course/management.php
@@ -487,6 +487,9 @@ if (count($notificationsfail) > 0) {
}
// Start the management form.
+
+echo $renderer->course_search_form($search);
+
echo $renderer->management_form_start();
echo $renderer->accessible_skipto_links($displaycategorylisting, $displaycourselisting, $displaycoursedetail);
@@ -518,6 +521,5 @@ echo $renderer->grid_end();
// End of the management form.
echo $renderer->management_form_end();
-echo $renderer->course_search_form($search);
echo $renderer->footer();
diff --git a/course/renderer.php b/course/renderer.php
index da82415caf2..89b7e0c7566 100644
--- a/course/renderer.php
+++ b/course/renderer.php
@@ -399,48 +399,22 @@ class core_course_renderer extends plugin_renderer_base {
}
/**
- * Renders html to display a course search form.
+ * Renders html to display a course search form
*
* @param string $value default value to populate the search field
- * @param string $format display format - 'plain' (default), 'short' or 'navbar'
* @return string
*/
- public function course_search_form($value = '', $format = 'plain') {
- static $count = 0;
- $formid = 'coursesearch';
- if ((++$count) > 1) {
- $formid .= $count;
- }
+ public function course_search_form($value = '') {
- switch ($format) {
- case 'navbar' :
- $formid = 'coursesearchnavbar';
- $inputid = 'navsearchbox';
- $inputsize = 20;
- break;
- case 'short' :
- $inputid = 'shortsearchbox';
- $inputsize = 12;
- break;
- default :
- $inputid = 'coursesearchbox';
- $inputsize = 30;
- }
-
- $data = new stdClass();
- $data->searchurl = \core_search\manager::get_course_search_url()->out(false);
- $data->id = $formid;
- $data->inputid = $inputid;
- $data->inputsize = $inputsize;
- $data->value = $value;
- $data->areaids = 'core_course-course';
-
- if ($format != 'navbar') {
- $helpicon = new \help_icon('coursesearch', 'core');
- $data->helpicon = $helpicon->export_for_template($this);
- }
-
- return $this->render_from_template('core_course/course_search_form', $data);
+ $data = [
+ 'action' => \core_search\manager::get_course_search_url(),
+ 'btnclass' => 'btn-primary',
+ 'inputname' => 'q',
+ 'searchstring' => get_string('searchcourses'),
+ 'hiddenfields' => (object) ['name' => 'areaids', 'value' => 'core_course-course'],
+ 'query' => $value
+ ];
+ return $this->render_from_template('core/search_input', $data);
}
/**
@@ -1890,6 +1864,13 @@ class core_course_renderer extends plugin_renderer_base {
public function search_courses($searchcriteria) {
global $CFG;
$content = '';
+
+ $search = '';
+ if (!empty($searchcriteria['search'])) {
+ $search = $searchcriteria['search'];
+ }
+ $content .= $this->course_search_form($search);
+
if (!empty($searchcriteria)) {
// print search results
@@ -1931,18 +1912,6 @@ class core_course_renderer extends plugin_renderer_base {
$content .= $this->heading(get_string('searchresults'). ": $totalcount");
$content .= $courseslist;
}
-
- if (!empty($searchcriteria['search'])) {
- // print search form only if there was a search by search string, otherwise it is confusing
- $content .= $this->box_start('generalbox mdl-align');
- $content .= $this->course_search_form($searchcriteria['search']);
- $content .= $this->box_end();
- }
- } else {
- // just print search form
- $content .= $this->box_start('generalbox mdl-align');
- $content .= $this->course_search_form();
- $content .= $this->box_end();
}
return $content;
}
@@ -2464,7 +2433,7 @@ class core_course_renderer extends plugin_renderer_base {
break;
case FRONTPAGECOURSESEARCH:
- $output .= $this->box($this->course_search_form('', 'short'), 'mdl-align');
+ $output .= $this->box($this->course_search_form(''), 'd-flex justify-content-center');
break;
}
diff --git a/course/search.php b/course/search.php
index 144767d452f..ccaf4b302a9 100644
--- a/course/search.php
+++ b/course/search.php
@@ -95,7 +95,7 @@ if (empty($searchcriteria)) {
$aurl = new moodle_url('/course/management.php', $searchcriteria);
$searchform = $OUTPUT->single_button($aurl, get_string('managecourses'), 'get');
} else {
- $searchform = $courserenderer->course_search_form($search, 'navbar');
+ $searchform = $courserenderer->course_search_form($search);
}
$PAGE->set_button($searchform);
diff --git a/course/templates/local/activitychooser/search.mustache b/course/templates/local/activitychooser/search.mustache
index 92839805e96..70dfac6b560 100644
--- a/course/templates/local/activitychooser/search.mustache
+++ b/course/templates/local/activitychooser/search.mustache
@@ -22,31 +22,14 @@
Example context (json):
{}
}}
-
diff --git a/course/tests/behat/course_search.feature b/course/tests/behat/course_search.feature
index 635bc2d1d91..bd5c99c5f67 100644
--- a/course/tests/behat/course_search.feature
+++ b/course/tests/behat/course_search.feature
@@ -20,8 +20,8 @@ Feature: Courses can be searched for and moved in bulk.
Scenario: Search courses finds correct results
Given I log in as "admin"
And I go to the courses management page
- When I set the field "coursesearchbox" to "Biology"
- And I press "Go"
+ When I set the field "Search" to "Biology"
+ And I press "Search"
Then I should see "Biology Y1"
And I should see "Biology Y2"
And I should not see "English Y1"
@@ -31,8 +31,8 @@ Feature: Courses can be searched for and moved in bulk.
Scenario: Search courses and move results in bulk
Given I log in as "admin"
And I go to the courses management page
- And I set the field "coursesearchbox" to "Biology"
- And I press "Go"
+ And I set the field "Search" to "Biology"
+ And I press "Search"
When I select course "Biology Y1" in the management interface
And I select course "Biology Y2" in the management interface
And I set the field "menumovecoursesto" to "Science"
diff --git a/lang/en/moodle.php b/lang/en/moodle.php
index 2d8bd7267fe..602f1baeb2b 100644
--- a/lang/en/moodle.php
+++ b/lang/en/moodle.php
@@ -2096,6 +2096,7 @@ $string['today'] = 'Today';
$string['todaylogs'] = 'Today\'s logs';
$string['toeveryone'] = 'to everyone';
$string['toggleemojipicker'] = 'Toggle emoji picker';
+$string['togglesearch'] = 'Toggle search input';
$string['toomanybounces'] = 'That email address has had too many bounces. You must change it to continue.';
$string['toomanytags'] = 'This search included too many tags; some will have been ignored.';
$string['toomanytoshow'] = 'There are too many users to show.';
diff --git a/lib/amd/build/search-input.min.js b/lib/amd/build/search-input.min.js
deleted file mode 100644
index 5e2f8db12b9..00000000000
--- a/lib/amd/build/search-input.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-define ("core/search-input",["jquery"],function(a){var b=null,c=function(a){if(b.hasClass("expanded")){e()}else{d(a)}},d=function(c){var d=a(document).width();if("keydown"===c.type&&13!==c.keyCode&&32!==c.keyCode){return}if(767>=d&&("click"===c.type||"keydown"===c.type)){f();return}else if(767>=d){return}if("keydown"===c.type){c.preventDefault()}b.addClass("expanded");b.find("form").addClass("expanded");b.find("input").focus()},e=function(){b.removeClass("expanded");b.find("form").removeClass("expanded")},f=function(){b.find("form").submit()};return{init:function init(d){b=a("#"+d);b.on("click mouseover keydown","div",c)}}});
-//# sourceMappingURL=search-input.min.js.map
diff --git a/lib/amd/build/search-input.min.js.map b/lib/amd/build/search-input.min.js.map
deleted file mode 100644
index f6a55ed1d8e..00000000000
--- a/lib/amd/build/search-input.min.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["../src/search-input.js"],"names":["define","$","wrapper","toggleForm","ev","hasClass","hideForm","showForm","windowWidth","document","width","type","keyCode","submitForm","preventDefault","addClass","find","focus","removeClass","submit","init","id","on"],"mappings":"AAyBAA,OAAM,qBAAC,CAAC,QAAD,CAAD,CAAa,SAASC,CAAT,CAAY,IAOvBC,CAAAA,CAAO,CAAG,IAPa,CAgBvBC,CAAU,CAAG,SAASC,CAAT,CAAa,CAE1B,GAAIF,CAAO,CAACG,QAAR,CAAiB,UAAjB,CAAJ,CAAkC,CAC9BC,CAAQ,EACX,CAFD,IAEO,CACHC,CAAQ,CAACH,CAAD,CACX,CACJ,CAvB0B,CAgCvBG,CAAQ,CAAG,SAASH,CAAT,CAAa,CAExB,GAAII,CAAAA,CAAW,CAAGP,CAAC,CAACQ,QAAD,CAAD,CAAYC,KAAZ,EAAlB,CAGA,GAAgB,SAAZ,GAAAN,CAAE,CAACO,IAAH,EAAwC,EAAf,GAAAP,CAAE,CAACQ,OAA5B,EAA6D,EAAf,GAAAR,CAAE,CAACQ,OAArD,CAAqE,CACjE,MACH,CAED,GAAmB,GAAf,EAAAJ,CAAW,GAAwB,OAAZ,GAAAJ,CAAE,CAACO,IAAH,EAAmC,SAAZ,GAAAP,CAAE,CAACO,IAAtC,CAAf,CAA0E,CAEtEE,CAAU,GACV,MACH,CAJD,IAIO,IAAmB,GAAf,EAAAL,CAAJ,CAAwB,CAE3B,MACH,CAED,GAAgB,SAAZ,GAAAJ,CAAE,CAACO,IAAP,CAA2B,CAEvBP,CAAE,CAACU,cAAH,EACH,CAEDZ,CAAO,CAACa,QAAR,CAAiB,UAAjB,EACAb,CAAO,CAACc,IAAR,CAAa,MAAb,EAAqBD,QAArB,CAA8B,UAA9B,EACAb,CAAO,CAACc,IAAR,CAAa,OAAb,EAAsBC,KAAtB,EACH,CA1D0B,CAkEvBX,CAAQ,CAAG,UAAW,CACtBJ,CAAO,CAACgB,WAAR,CAAoB,UAApB,EACAhB,CAAO,CAACc,IAAR,CAAa,MAAb,EAAqBE,WAArB,CAAiC,UAAjC,CACH,CArE0B,CA8EvBL,CAAU,CAAG,UAAW,CACxBX,CAAO,CAACc,IAAR,CAAa,MAAb,EAAqBG,MAArB,EACH,CAhF0B,CAkF3B,MAA8C,CAS1CC,IAAI,CAAE,cAASC,CAAT,CAAa,CACfnB,CAAO,CAAGD,CAAC,CAAC,IAAMoB,CAAP,CAAX,CACAnB,CAAO,CAACoB,EAAR,CAAW,yBAAX,CAAsC,KAAtC,CAA6CnB,CAA7C,CACH,CAZyC,CAcjD,CAhGK,CAAN","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 * Search box.\n *\n * @module core/search-input\n * @class search-input\n * @package core\n * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since Moodle 3.1\n */\ndefine(['jquery'], function($) {\n\n /**\n * This search box div node.\n *\n * @private\n */\n var wrapper = null;\n\n /**\n * Toggles the form visibility.\n *\n * @param {Event} ev\n * @method toggleForm\n * @private\n */\n var toggleForm = function(ev) {\n\n if (wrapper.hasClass('expanded')) {\n hideForm();\n } else {\n showForm(ev);\n }\n };\n\n /**\n * Shows the form or submits it depending on the window size.\n *\n * @param {Event} ev\n * @method showForm\n * @private\n */\n var showForm = function(ev) {\n\n var windowWidth = $(document).width();\n\n // We are only interested in enter and space keys (accessibility).\n if (ev.type === 'keydown' && ev.keyCode !== 13 && ev.keyCode !== 32) {\n return;\n }\n\n if (windowWidth <= 767 && (ev.type === 'click' || ev.type === 'keydown')) {\n // Move to the search page when using small window sizes as the input requires too much space.\n submitForm();\n return;\n } else if (windowWidth <= 767) {\n // Ignore mousedown events in while using small window sizes.\n return;\n }\n\n if (ev.type === 'keydown') {\n // We don't want to submit the form unless the user hits enter.\n ev.preventDefault();\n }\n\n wrapper.addClass('expanded');\n wrapper.find('form').addClass('expanded');\n wrapper.find('input').focus();\n };\n\n /**\n * Hides the form.\n *\n * @method hideForm\n * @private\n */\n var hideForm = function() {\n wrapper.removeClass('expanded');\n wrapper.find('form').removeClass('expanded');\n };\n\n /**\n * Submits the form.\n *\n * @param {Event} ev\n * @method submitForm\n * @private\n */\n var submitForm = function() {\n wrapper.find('form').submit();\n };\n\n return /** @alias module:core/search-input */ {\n // Public variables and functions.\n\n /**\n * Assigns listeners to the requested select box.\n *\n * @method init\n * @param {Number} id The search wrapper div id\n */\n init: function(id) {\n wrapper = $('#' + id);\n wrapper.on('click mouseover keydown', 'div', toggleForm);\n }\n };\n});\n"],"file":"search-input.min.js"}
\ No newline at end of file
diff --git a/lib/amd/src/search-input.js b/lib/amd/src/search-input.js
deleted file mode 100644
index 85f9bb14946..00000000000
--- a/lib/amd/src/search-input.js
+++ /dev/null
@@ -1,122 +0,0 @@
-// 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 .
-
-/**
- * Search box.
- *
- * @module core/search-input
- * @class search-input
- * @package core
- * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since Moodle 3.1
- */
-define(['jquery'], function($) {
-
- /**
- * This search box div node.
- *
- * @private
- */
- var wrapper = null;
-
- /**
- * Toggles the form visibility.
- *
- * @param {Event} ev
- * @method toggleForm
- * @private
- */
- var toggleForm = function(ev) {
-
- if (wrapper.hasClass('expanded')) {
- hideForm();
- } else {
- showForm(ev);
- }
- };
-
- /**
- * Shows the form or submits it depending on the window size.
- *
- * @param {Event} ev
- * @method showForm
- * @private
- */
- var showForm = function(ev) {
-
- var windowWidth = $(document).width();
-
- // We are only interested in enter and space keys (accessibility).
- if (ev.type === 'keydown' && ev.keyCode !== 13 && ev.keyCode !== 32) {
- return;
- }
-
- if (windowWidth <= 767 && (ev.type === 'click' || ev.type === 'keydown')) {
- // Move to the search page when using small window sizes as the input requires too much space.
- submitForm();
- return;
- } else if (windowWidth <= 767) {
- // Ignore mousedown events in while using small window sizes.
- return;
- }
-
- if (ev.type === 'keydown') {
- // We don't want to submit the form unless the user hits enter.
- ev.preventDefault();
- }
-
- wrapper.addClass('expanded');
- wrapper.find('form').addClass('expanded');
- wrapper.find('input').focus();
- };
-
- /**
- * Hides the form.
- *
- * @method hideForm
- * @private
- */
- var hideForm = function() {
- wrapper.removeClass('expanded');
- wrapper.find('form').removeClass('expanded');
- };
-
- /**
- * Submits the form.
- *
- * @param {Event} ev
- * @method submitForm
- * @private
- */
- var submitForm = function() {
- wrapper.find('form').submit();
- };
-
- return /** @alias module:core/search-input */ {
- // Public variables and functions.
-
- /**
- * Assigns listeners to the requested select box.
- *
- * @method init
- * @param {Number} id The search wrapper div id
- */
- init: function(id) {
- wrapper = $('#' + id);
- wrapper.on('click mouseover keydown', 'div', toggleForm);
- }
- };
-});
diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php
index ba5366fc048..dd9bd247977 100644
--- a/lib/outputrenderers.php
+++ b/lib/outputrenderers.php
@@ -3222,31 +3222,13 @@ EOD;
return '';
}
- if ($id == false) {
- $id = uniqid();
- } else {
- // Needs to be cleaned, we use it for the input id.
- $id = clean_param($id, PARAM_ALPHANUMEXT);
- }
-
- // JS to animate the form.
- $this->page->requires->js_call_amd('core/search-input', 'init', array($id));
-
- $searchicon = html_writer::tag('div', $this->pix_icon('a/search', get_string('search', 'search'), 'moodle'),
- array('role' => 'button', 'tabindex' => 0));
- $formattrs = array('class' => 'search-input-form', 'action' => $CFG->wwwroot . '/search/index.php');
- $inputattrs = array('type' => 'text', 'name' => 'q', 'placeholder' => get_string('search', 'search'),
- 'size' => 13, 'tabindex' => -1, 'id' => 'id_q_' . $id, 'class' => 'form-control');
-
- $contents = html_writer::tag('label', get_string('enteryoursearchquery', 'search'),
- array('for' => 'id_q_' . $id, 'class' => 'accesshide')) . html_writer::empty_tag('input', $inputattrs);
- if ($this->page->context && $this->page->context->contextlevel !== CONTEXT_SYSTEM) {
- $contents .= html_writer::empty_tag('input', ['type' => 'hidden',
- 'name' => 'context', 'value' => $this->page->context->id]);
- }
- $searchinput = html_writer::tag('form', $contents, $formattrs);
-
- return html_writer::tag('div', $searchicon . $searchinput, array('class' => 'search-input-wrapper nav-link', 'id' => $id));
+ $data = [
+ 'action' => new moodle_url('/search/index.php'),
+ 'hiddenfields' => (object) ['name' => 'context', 'value' => $this->page->context->id],
+ 'inputname' => 'q',
+ 'searchstring' => get_string('search'),
+ ];
+ return $this->render_from_template('core/search_input_navbar', $data);
}
/**
diff --git a/blocks/settings/templates/search_form.mustache b/lib/templates/checkbox.mustache
similarity index 52%
rename from blocks/settings/templates/search_form.mustache
rename to lib/templates/checkbox.mustache
index 2ae6011112e..518e662b2cf 100644
--- a/blocks/settings/templates/search_form.mustache
+++ b/lib/templates/checkbox.mustache
@@ -15,22 +15,20 @@
along with Moodle. If not, see .
}}
{{!
- @template block_settings/search_form
+ @template core/checkbox
- This template renders the search form.
+ Chooser search template.
Example context (json):
{
- "action": "https://domain.example/admin/search.php",
- "label": "Search in settings",
- "searchvalue": "Find this setting",
- "quote": "Search"
+ "name": "fullsearch",
+ "id": "fullsearch",
+ "checked": true,
+ "value": "1",
+ "label": "Reset options"
}
}}
-
+
+
+
+
\ No newline at end of file
diff --git a/lib/templates/popover_region.mustache b/lib/templates/popover_region.mustache
index 2047541cfab..610681e5324 100644
--- a/lib/templates/popover_region.mustache
+++ b/lib/templates/popover_region.mustache
@@ -36,7 +36,7 @@
\ No newline at end of file
diff --git a/lib/templates/search_input_auto.mustache b/lib/templates/search_input_auto.mustache
new file mode 100644
index 00000000000..7e89b09b105
--- /dev/null
+++ b/lib/templates/search_input_auto.mustache
@@ -0,0 +1,63 @@
+{{!
+ 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 .
+}}
+{{!
+ @template core/search_input_auto
+
+ Search input that auto searches.
+
+ Example context (json):
+ {
+ "action": "https://moodle.local/admin/search.php",
+ "extraclasses": "my-2",
+ "inputname": "search",
+ "searchstring": "Search settings",
+ "sesskey": "sesskey",
+ "value": "policy",
+ "btnclass": "primary",
+ "hiddenfields": [
+ {
+ "name": "course",
+ "value": "11"
+ }
+ ]
+ }
+}}
+
+
+
+
+
+
+
diff --git a/lib/templates/search_input_navbar.mustache b/lib/templates/search_input_navbar.mustache
new file mode 100644
index 00000000000..4f07bc4a8de
--- /dev/null
+++ b/lib/templates/search_input_navbar.mustache
@@ -0,0 +1,116 @@
+{{!
+ 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 .
+}}
+{{!
+ @template core/search_input_navbar
+
+ Navbar search input template.
+
+ Example context (json):
+ {
+ "action": "https://moodle.local/admin/search.php",
+ "inputname": "search",
+ "searchstring": "Search",
+ "hiddenfields": [
+ {
+ "name": "cmid",
+ "value": "11"
+ }
+ ]
+ }
+}}
+