ÿØÿà JFIF    ÿÛ „  ( %!1"%)+...383-7(-.+  -%%--------+------------/----------------------------ÿÀ  ¶" ÿÄ     ÿÄ @    !1AQaq‘¡"±ÁÑð2’BRráS‚ñ3b¢CT²Ò #cÿÄ    ÿÄ '   1!AQ2a"‘±#ÿÚ   ? ò(/p‘—–¢·,M|¯G»R£üešT. ôŸEoCFñ‡ 4ÔkåóxR|µÁ¨ÓDà!Ç´µ˜‡p ÍQ‹§!þž^õWsŒy  åÁ§$á«%ºq˜™JyyË0y«^Ϗ -3ÃÏËÈ£Æ\P’ä„s&•‹Üs üô'àWt¹pñCp$bÚF`·7.^)¾!)ÒÙ˜–ÅÏš^h¾ø³·ìe±ŒaeÖ—Í,e=B}Jéߦ$$½—šÙÜCC¯¶è8β–Åkèwx LÉngT±æ¹£Ócœ¿Í/©Éÿ ‰ÚCÒ­mq;Tt¶–}0"zÖPcd1š­¤tˆ„ÐÓâqú[™öUCŠ2޿د­É(¸ßúçšmW,Ò ºf‹Ån™ÄLêÁ8üÁkÁ€L¥ÍuJ1œôrÂSŒÓöt!7EUíïb)AËi¬â-ÙHk8òÁiXYwÅ)O^"ÇØriÛô{»wRUKÙÏÚlÓ¬€‚’שT ] ªb—ÊîKC^×M“xùäñz¬zJ× {4[¹M'IÆeH1LCWÕ]œûº WDæà°OÅ‚¹c²ÞuA–rL`HšpCez‡V’— Ž‚Ë„–ñ­'µR0ÕÖ=À:±G»C\nÆÉ5*¢ †3šhQ4ýÒi$Õ1"Ü]¢p-q3zcQ$ªö¨¥æf›‘»´ÝÚHá„e²JÇ–iÊ:·à¬Øc5Åk»Laª*wi [0Ówk ¦a¦0Õ²ÅÅ‚T¸’³q:Æ<¹ú Ò˜Æ}2VE•Ѐp¡Ô3Úº …h³^LJºçì¥Ánó|˜ÖˆÎˆ+« ¦dø‘Mº–d\ªDðÛ„Ì×LÝ——Ð9Ç|•FèçVm™¨€™3õS–9Äå¢<̉Ìm¢±`·º˜&Z²ž Ë®D«¶»6™‰©­HÈ Àá«h­i³04æ’hxœi3ï¨(ÓL·†‹¶ËpŒ ƒÜÒ? :õdsÀ –3æÓ·Üjâ¬9—]8n¼&]I F£¼…ì–—Œ0v°LǐÅfÛ2TÖ’aµ˜N@ÖBSÏ^à´4-±°b×nÈÈœÆD=KdŒø…ÄÈK,›ÖR§fjü$^ÔÎ!„6^ s:Üun\ôkCÞò÷<—Lúl Í›G¹ÀPá¼Nc¡šÒ…ÙWͳœ±vÊáЄ×9‹P‰cEØ‘™›µŸP<×g1 º©Ø¬wiÁ^l5× I/';Ë+´Z‡ÓÄFÕp[Èð’ HÄ ó 9¬Dl4“é±Ï”V=^Hð;žLädÉa‚°Ë,#/Ž~²BÔÛ QãñQtIeórVB,ÏÂf‹4#Μc‹É3ZIÖ Œ¢¥·üxa‡Ãçè„jŠ[5 Ä!U?,3žÎ×…ðdÓ÷háŠA‰ìÄ4÷ÃÜZÃE{‰®+W\ZÍE[‰»µk»MÝ­`¢©†˜ÃV‹-f¢¡†¢a«eŠ%«Y¨©q%jâd,Ôq`) ¤¦…šˆ ¦E>0Ùb²t–Ÿ  ÷jsςҒKÈcø(é›;k(’#ôŒQ ™C> õ³M9óºÆµ¤çâ;uÀ)5>RÔ¸fÓ~Ȧ—’-$fe«"FâUˆÊwµPÏu w+"åÉó¨6s™Û9(À¨ù©hE°½†n`m~“@©›p!Vkhµ Á1šbEqqÊ@ºèl›2 –Yã%‘‚hÛ{a¶‡ÅÞM²¬çL34®Çl]®‹ŽØŒm'RÜe35æöˆ­Á­ cP9šeÁköo´L³Ì=p8]”‡B¶,šº|ɏdzXˆØk*ÇÚ{#ñ‰pã(€¶‡9ý'šÜ³½¯kƒ†Â’ëSOƒ•Å®H6+a¢µˆ‹Y¨b˜b+XˆÖ!°u!¢"µˆˆn2ˆ Å0Ä`Å Ô®c(ÓÜGº•Ä»Œ âWî%q.áÐÄ÷î%umÍ£+ÜQ,Vn¦-GphU¸¢X­&,Gt ·K¢ÅÅ·¥RÄÊÉbKn N­R2g$VµÕó Qr#–ÓZUÏð0ÎtÙÀœz ëöo“Îk¯n„T°"SÞ+³/…€½"ižãO%Í%&Δâ‘ËØ´IˆÓ°Ë‰ÞM3.™yçÁzit›`4C`k¢¼á!álÄï sÂ[×!¤l¾æo"ƒIÔ'@ªu)3›ô®“@Z à¾] üœ'‡4¸PÞkÁ-qÄH‰ÓÍBd´ÿ —¹°ÄA&ÊUC„öz‚‚RæŠtf¬>˜ÑidšEçÑL÷áý¡V±h§­c¿¥¨ƒ@g¼…Ýö^ÞËc.ž„æ“I[ ª+·zÐÒÚ,½Ð»¶€C›7jk$๖€Ž¾ÅÙpp¶ÍþéΤ ½ØÎŒq ñpæžÅ¥#Øâ49ÃÂK^MYV“,¶Éz_òæ:…;Õ«¦I3âJ£¤û5$7Èxün\â]Êe¯(Ÿ&Ž‹¶2<6Ćf× æ¢ƱqWı[(ÙÂŽç‚T7¨@ÈÉÍß‚ïÙ"&3TYlGށ†)†#5ŠA©^T2ÆÁ†)©†©†¤yGXÁ†©"†)©¼ÅV WS†#©]R–r‹q5ÕbâWþBh¯u+ªÅÄ×ü„nÑ^êkªÁj‰jeœWˆÕÕ`µDµ2Ì+Ä µDµµ5Ô{¤Þ0RF’H÷AÛ<ÊÁ§ ƈæ0Ñ£ê4 öÚµ[¬/1²€â<-¬šö²r×0o×w ´xÐÒË„ g01¬„åEM蛇Ã\6µE›‘fÙO k÷CºnÓ1`iîÒ² áÂ!ÑLÄÿ ³¬\õ*:M=Ç»c‹2[‰ØsÕLò\ŒK L^™üÄN\¶#» Ç\……ßïIœÉ$¸ÎñÍ®ÔHÇ Ûí/´>Li”„†g^$ 7NÍÑÑ2M2ªNg=k½Ð}Ÿl6‡ —JWˆ”ÞéT·+µ¦üÂFè¢Vs6ýÆ4»2+2IÝ9ô9®ÇGÙÇ1+Ä8;*Õ§ž½D*îÑWÚçá3Ma¤·ˆ¥t}š²Ü…Q+Æüµ8HíS”üxGéÁˆ¦Ël„éÈ 5 µÄƒ6äÜ(kIš¯S†fÖ&¸®×v|Äyˆ*禌Éù@7-®Ä[]ÎÖ¿ê`º…=:…žJVnݳvðÖKE¼‚ »mà ÷f®:Ë ™–4rð¡4` ܹçž~‹C=œ…¾Ïj|VDtK.ƒ"9 Ï ­–éXá³6WÒïIL-Ї Ç edit/tree/move_options_tree.js000064400000007643152266501050012542 0ustar00// 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 . /** * Keyboard navigation and aria-tree compatibility for the grade move options. * * @module core_grades/bulkactions/edit/tree/move_options_tree * @copyright 2023 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import Tree from 'core/tree'; import {getList} from 'core/normalise'; /** @constant {Object} The object containing the relevant selectors. */ const Selectors = { moveOptionsTree: '#destination-selector [role="tree"]', moveOption: '#destination-selector [role="treeitem"]', toggleGroupLink: '#destination-selector .collapse-list-link', }; export default class MoveOptionsTree extends Tree { /** @property {function|null} afterSelectMoveOptionCallback Callback function to run after selecting a move option. */ afterSelectMoveOptionCallback = null; /** @property {HTMLElement|null} selectedMoveOption The selected move option. */ selectedMoveOption = null; /** * The class constructor. * * @param {function|null} afterSelectMoveOptionCallback Callback function used to define actions that should be run * after selecting a move option. * @returns {void} */ constructor(afterSelectMoveOptionCallback) { super(Selectors.moveOptionsTree); this.afterSelectMoveOptionCallback = afterSelectMoveOptionCallback; } /** * Handle a key down event. * * @method handleKeyDown * @param {Event} e The event. */ handleKeyDown(e) { // If the user presses enter or space, select the item. if (e.keyCode === this.keys.enter || e.keyCode === this.keys.space) { this.selectMoveOption(e.target); } else { // Otherwise, let the default behaviour happen. super.handleKeyDown(e); } } /** * Handle an item click. * * @param {Event} event The click event. * @param {jQuery} item The item clicked. * @returns {void} */ handleItemClick(event, item) { const isToggleGroupLink = event.target.closest(Selectors.toggleGroupLink); // If the click is on the toggle group (chevron) link, let the default behaviour happen. if (isToggleGroupLink) { super.handleItemClick(event, item); return; } // If the click is on the item itself, select it. this.selectMoveOption(getList(item)[0]); } /** * Select a move option. * * @method selectMoveOption * @param {HTMLElement} moveOption The move option to select. */ selectMoveOption(moveOption) { // Create the cache of the visible items. this.refreshVisibleItemsCache(); // Deselect all the move options. document.querySelectorAll(Selectors.moveOption).forEach(item => { item.dataset.selected = "false"; }); // Select and set the focus on the specified move option. moveOption.dataset.selected = "true"; this.selectedMoveOption = moveOption; moveOption.focus(); // Call the callback function if it is defined. if (typeof this.afterSelectMoveOptionCallback === 'function') { this.afterSelectMoveOptionCallback(); } } } edit/tree/bulk_actions.js000064400000005771152266501050011457 0ustar00// 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 . import BulkActions from "core/bulkactions/bulk_actions"; import GradebookEditTreeBulkMove from "core_grades/bulkactions/edit/tree/move"; /** * Class for defining the bulk actions area in the gradebook setup page. * * @module core_grades/bulkactions/edit/tree/bulk_actions * @copyright 2023 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ const Selectors = { selectBulkItemCheckbox: 'input[type="checkbox"].itemselect' }; export default class GradebookEditTreeBulkActions extends BulkActions { /** @property {int|null} courseID The course ID. */ courseID = null; /** * Returns the instance of the class. * * @param {int} courseID * @returns {GradebookEditTreeBulkActions} */ static init(courseID) { return new this(courseID); } /** * The class constructor. * * @param {int} courseID The course ID. * @returns {void} */ constructor(courseID) { super(); this.courseID = courseID; } /** * Returns the array of the relevant bulk action objects for the gradebook setup page. * * @method getBulkActions * @returns {Array} */ getBulkActions() { return [ new GradebookEditTreeBulkMove(this.courseID) ]; } /** * Returns the array of selected items. * * @method getSelectedItems * @returns {Array} */ getSelectedItems() { return document.querySelectorAll(`${Selectors.selectBulkItemCheckbox}:checked`); } /** * Adds the listener for the item select change event. * * @method registerItemSelectChangeEvent * @param {function} eventHandler The event handler function. * @returns {void} */ registerItemSelectChangeEvent(eventHandler) { const itemSelectCheckboxes = document.querySelectorAll(Selectors.selectBulkItemCheckbox); itemSelectCheckboxes.forEach((checkbox) => { checkbox.addEventListener('change', eventHandler.bind(this)); }); } /** * Defines the action for deselecting a selected item. * * @method deselectItem * @param {HTMLElement} selectedItem The selected element. * @returns {void} */ deselectItem(selectedItem) { selectedItem.checked = false; } } edit/tree/move.js000064400000013760152266501050007745 0ustar00// 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 . /** * Class that defines the bulk move action in the gradebook setup page. * * @module core_grades/bulkactions/edit/tree/move * @copyright 2023 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import BulkAction from 'core/bulkactions/bulk_action'; import {get_string as getString} from 'core/str'; import ModalSaveCancel from 'core/modal_save_cancel'; import Templates from 'core/templates'; import Ajax from 'core/ajax'; import ModalEvents from 'core/modal_events'; import MoveOptionsTree from 'core_grades/bulkactions/edit/tree/move_options_tree'; /** @constant {Object} The object containing the relevant selectors. */ const Selectors = { editTreeForm: '#gradetreeform', bulkMoveInput: 'input[name="bulkmove"]', bulkMoveAfterInput: 'input[name="moveafter"]' }; export default class GradebookEditTreeBulkMove extends BulkAction { /** @property {int|null} courseId The course ID. */ courseId = null; /** @property {MoveOptionsTree|null} moveOptionsTree The move options tree object. */ moveOptionsTree = null; /** @property {string|null} gradeTree The grade tree structure. */ gradeTree = null; /** * The class constructor. * * @param {int} courseId The course ID. * @returns {void} */ constructor(courseId) { super(); this.courseId = courseId; } /** * Defines the selector of the element that triggers the bulk move action. * * @returns {string} The bulk move action trigger selector. */ getBulkActionTriggerSelector() { return '[data-type="bulkactions"] [data-action="move"]'; } /** * Defines the behavior once the bulk move action is triggered. * * @method executeBulkAction * @returns {void} */ async triggerBulkAction() { const modal = await this.showModal(); this.registerCustomListenerEvents(modal); } /** * Renders the bulk move action trigger element. * * @method renderBulkActionTrigger * @param {boolean} showInDropdown Whether the action is displayed under a 'More' dropdown or as a separate button. * @param {number} index The index of the action. * @returns {Promise} The bulk move action trigger promise */ async renderBulkActionTrigger(showInDropdown, index) { return Templates.render('core_grades/bulkactions/edit/tree/bulk_move_trigger', { showindropdown: showInDropdown, isfirst: index === 0, }); } /** * Register custom event listeners. * * @method registerCustomClickListenerEvents * @param {Object} modal The modal object. * @returns {void} */ async registerCustomListenerEvents(modal) { await modal.getBody(); // Initialize the move options tree once the modal is shown. modal.getRoot().on(ModalEvents.shown, () => { this.moveOptionsTree = new MoveOptionsTree(() => { // Enable the 'Move' action button once something is selected. modal.setButtonDisabled('save', false); }); }); // Destroy the modal once it is hidden. modal.getRoot().on(ModalEvents.hidden, () => { modal.destroy(); }); // Define the move action event. modal.getRoot().on(ModalEvents.save, () => { // Make sure that a move option is selected. if (this.moveOptionsTree && this.moveOptionsTree.selectedMoveOption) { // Set the relevant form values. document.querySelector(Selectors.bulkMoveInput).value = 1; document.querySelector(Selectors.bulkMoveAfterInput).value = this.moveOptionsTree.selectedMoveOption.dataset.id; // Submit the form. document.querySelector(Selectors.editTreeForm).submit(); } }); } /** * Fetch the grade tree structure for the current course. * * @method fetchGradeTree * @returns {Promise} The grade tree promise */ fetchGradeTree() { const request = { methodname: 'core_grades_get_grade_tree', args: { courseid: this.courseId, }, }; return Ajax.call([request])[0]; } /** * Renders the bulk move modal body. * * @method renderModalBody * @returns {Promise} The modal body promise */ async renderModalBody() { // We need to fetch the grade tree structure only once. if (this.gradeTree === null) { this.gradeTree = await this.fetchGradeTree(); } return Templates.render('core_grades/bulkactions/edit/tree/bulk_move_grade_tree', JSON.parse(this.gradeTree)); } /** * Show the bulk move modal. * * @method showModal * @returns {Promise} The modal promise */ async showModal() { const modal = await ModalSaveCancel.create({ title: await getString('movesitems', 'grades'), body: await this.renderModalBody(), buttons: { save: await getString('move') }, large: true, }); // Disable the 'Move' action button until something is selected. modal.setButtonDisabled('save', true); modal.show(); return modal; } } edit/tree/bulk_actions.min.js.map000064400000007526152266731110013017 0ustar00{"version":3,"file":"bulk_actions.min.js","sources":["../../../../src/bulkactions/edit/tree/bulk_actions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport BulkActions from \"core/bulkactions/bulk_actions\";\nimport GradebookEditTreeBulkMove from \"core_grades/bulkactions/edit/tree/move\";\n\n/**\n * Class for defining the bulk actions area in the gradebook setup page.\n *\n * @module core_grades/bulkactions/edit/tree/bulk_actions\n * @copyright 2023 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"].itemselect'\n};\n\nexport default class GradebookEditTreeBulkActions extends BulkActions {\n\n /** @property {int|null} courseID The course ID. */\n courseID = null;\n\n /**\n * Returns the instance of the class.\n *\n * @param {int} courseID\n * @returns {GradebookEditTreeBulkActions}\n */\n static init(courseID) {\n return new this(courseID);\n }\n\n /**\n * The class constructor.\n *\n * @param {int} courseID The course ID.\n * @returns {void}\n */\n constructor(courseID) {\n super();\n this.courseID = courseID;\n }\n\n /**\n * Returns the array of the relevant bulk action objects for the gradebook setup page.\n *\n * @method getBulkActions\n * @returns {Array}\n */\n getBulkActions() {\n return [\n new GradebookEditTreeBulkMove(this.courseID)\n ];\n }\n\n /**\n * Returns the array of selected items.\n *\n * @method getSelectedItems\n * @returns {Array}\n */\n getSelectedItems() {\n return document.querySelectorAll(`${Selectors.selectBulkItemCheckbox}:checked`);\n }\n\n /**\n * Adds the listener for the item select change event.\n *\n * @method registerItemSelectChangeEvent\n * @param {function} eventHandler The event handler function.\n * @returns {void}\n */\n registerItemSelectChangeEvent(eventHandler) {\n const itemSelectCheckboxes = document.querySelectorAll(Selectors.selectBulkItemCheckbox);\n itemSelectCheckboxes.forEach((checkbox) => {\n checkbox.addEventListener('change', eventHandler.bind(this));\n });\n }\n\n /**\n * Defines the action for deselecting a selected item.\n *\n * @method deselectItem\n * @param {HTMLElement} selectedItem The selected element.\n * @returns {void}\n */\n deselectItem(selectedItem) {\n selectedItem.checked = false;\n }\n}\n"],"names":["Selectors","GradebookEditTreeBulkActions","BulkActions","courseID","this","constructor","getBulkActions","GradebookEditTreeBulkMove","getSelectedItems","document","querySelectorAll","registerItemSelectChangeEvent","eventHandler","forEach","checkbox","addEventListener","bind","deselectItem","selectedItem","checked"],"mappings":";;;;;;;;MA0BMA,iCACsB,0CAGPC,qCAAqCC,kCAW1CC,iBACD,IAAIC,KAAKD,UASpBE,YAAYF,0CAlBD,6IAoBFA,SAAWA,SASpBG,uBACW,CACH,IAAIC,cAA0BH,KAAKD,WAU3CK,0BACWC,SAASC,2BAAoBV,8CAUxCW,8BAA8BC,cACGH,SAASC,iBAAiBV,kCAClCa,SAASC,WAC1BA,SAASC,iBAAiB,SAAUH,aAAaI,KAAKZ,UAW9Da,aAAaC,cACTA,aAAaC,SAAU"}edit/tree/move_options_tree.min.js.map000064400000011750152266731110014074 0ustar00{"version":3,"file":"move_options_tree.min.js","sources":["../../../../src/bulkactions/edit/tree/move_options_tree.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Keyboard navigation and aria-tree compatibility for the grade move options.\n *\n * @module core_grades/bulkactions/edit/tree/move_options_tree\n * @copyright 2023 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Tree from 'core/tree';\nimport {getList} from 'core/normalise';\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n moveOptionsTree: '#destination-selector [role=\"tree\"]',\n moveOption: '#destination-selector [role=\"treeitem\"]',\n toggleGroupLink: '#destination-selector .collapse-list-link',\n};\n\nexport default class MoveOptionsTree extends Tree {\n\n /** @property {function|null} afterSelectMoveOptionCallback Callback function to run after selecting a move option. */\n afterSelectMoveOptionCallback = null;\n\n /** @property {HTMLElement|null} selectedMoveOption The selected move option. */\n selectedMoveOption = null;\n\n /**\n * The class constructor.\n *\n * @param {function|null} afterSelectMoveOptionCallback Callback function used to define actions that should be run\n * after selecting a move option.\n * @returns {void}\n */\n constructor(afterSelectMoveOptionCallback) {\n super(Selectors.moveOptionsTree);\n this.afterSelectMoveOptionCallback = afterSelectMoveOptionCallback;\n }\n\n /**\n * Handle a key down event.\n *\n * @method handleKeyDown\n * @param {Event} e The event.\n */\n handleKeyDown(e) {\n // If the user presses enter or space, select the item.\n if (e.keyCode === this.keys.enter || e.keyCode === this.keys.space) {\n this.selectMoveOption(e.target);\n } else { // Otherwise, let the default behaviour happen.\n super.handleKeyDown(e);\n }\n }\n\n /**\n * Handle an item click.\n *\n * @param {Event} event The click event.\n * @param {jQuery} item The item clicked.\n * @returns {void}\n */\n handleItemClick(event, item) {\n const isToggleGroupLink = event.target.closest(Selectors.toggleGroupLink);\n // If the click is on the toggle group (chevron) link, let the default behaviour happen.\n if (isToggleGroupLink) {\n super.handleItemClick(event, item);\n return;\n }\n // If the click is on the item itself, select it.\n this.selectMoveOption(getList(item)[0]);\n }\n\n /**\n * Select a move option.\n *\n * @method selectMoveOption\n * @param {HTMLElement} moveOption The move option to select.\n */\n selectMoveOption(moveOption) {\n // Create the cache of the visible items.\n this.refreshVisibleItemsCache();\n // Deselect all the move options.\n document.querySelectorAll(Selectors.moveOption).forEach(item => {\n item.dataset.selected = \"false\";\n });\n // Select and set the focus on the specified move option.\n moveOption.dataset.selected = \"true\";\n this.selectedMoveOption = moveOption;\n moveOption.focus();\n // Call the callback function if it is defined.\n if (typeof this.afterSelectMoveOptionCallback === 'function') {\n this.afterSelectMoveOptionCallback();\n }\n }\n}\n"],"names":["Selectors","MoveOptionsTree","Tree","constructor","afterSelectMoveOptionCallback","handleKeyDown","e","keyCode","this","keys","enter","space","selectMoveOption","target","handleItemClick","event","item","closest","moveOption","refreshVisibleItemsCache","document","querySelectorAll","forEach","dataset","selected","selectedMoveOption","focus"],"mappings":"icA2BMA,0BACe,sCADfA,qBAEU,0CAFVA,0BAGe,kDAGAC,wBAAwBC,cAezCC,YAAYC,qCACFJ,gFAbsB,gDAGX,WAWZI,8BAAgCA,8BASzCC,cAAcC,GAENA,EAAEC,UAAYC,KAAKC,KAAKC,OAASJ,EAAEC,UAAYC,KAAKC,KAAKE,WACpDC,iBAAiBN,EAAEO,cAElBR,cAAcC,GAW5BQ,gBAAgBC,MAAOC,MACOD,MAAMF,OAAOI,QAAQjB,iCAGrCc,gBAAgBC,MAAOC,WAI5BJ,kBAAiB,sBAAQI,MAAM,IASxCJ,iBAAiBM,iBAERC,2BAELC,SAASC,iBAAiBrB,sBAAsBsB,SAAQN,OACpDA,KAAKO,QAAQC,SAAW,WAG5BN,WAAWK,QAAQC,SAAW,YACzBC,mBAAqBP,WAC1BA,WAAWQ,QAEuC,mBAAvClB,KAAKJ,oCACPA"}edit/tree/move.min.js.map000064400000017534152266731110011310 0ustar00{"version":3,"file":"move.min.js","sources":["../../../../src/bulkactions/edit/tree/move.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Class that defines the bulk move action in the gradebook setup page.\n *\n * @module core_grades/bulkactions/edit/tree/move\n * @copyright 2023 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport {get_string as getString} from 'core/str';\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport Templates from 'core/templates';\nimport Ajax from 'core/ajax';\nimport ModalEvents from 'core/modal_events';\nimport MoveOptionsTree from 'core_grades/bulkactions/edit/tree/move_options_tree';\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n editTreeForm: '#gradetreeform',\n bulkMoveInput: 'input[name=\"bulkmove\"]',\n bulkMoveAfterInput: 'input[name=\"moveafter\"]'\n};\n\nexport default class GradebookEditTreeBulkMove extends BulkAction {\n\n /** @property {int|null} courseId The course ID. */\n courseId = null;\n\n /** @property {MoveOptionsTree|null} moveOptionsTree The move options tree object. */\n moveOptionsTree = null;\n\n /** @property {string|null} gradeTree The grade tree structure. */\n gradeTree = null;\n\n /**\n * The class constructor.\n *\n * @param {int} courseId The course ID.\n * @returns {void}\n */\n constructor(courseId) {\n super();\n this.courseId = courseId;\n }\n\n /**\n * Defines the selector of the element that triggers the bulk move action.\n *\n * @returns {string} The bulk move action trigger selector.\n */\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"move\"]';\n }\n\n /**\n * Defines the behavior once the bulk move action is triggered.\n *\n * @method executeBulkAction\n * @returns {void}\n */\n async triggerBulkAction() {\n const modal = await this.showModal();\n this.registerCustomListenerEvents(modal);\n }\n\n /**\n * Renders the bulk move action trigger element.\n *\n * @method renderBulkActionTrigger\n * @param {boolean} showInDropdown Whether the action is displayed under a 'More' dropdown or as a separate button.\n * @param {number} index The index of the action.\n * @returns {Promise} The bulk move action trigger promise\n */\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('core_grades/bulkactions/edit/tree/bulk_move_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n\n /**\n * Register custom event listeners.\n *\n * @method registerCustomClickListenerEvents\n * @param {Object} modal The modal object.\n * @returns {void}\n */\n async registerCustomListenerEvents(modal) {\n await modal.getBody();\n // Initialize the move options tree once the modal is shown.\n modal.getRoot().on(ModalEvents.shown, () => {\n this.moveOptionsTree = new MoveOptionsTree(() => {\n // Enable the 'Move' action button once something is selected.\n modal.setButtonDisabled('save', false);\n });\n });\n // Destroy the modal once it is hidden.\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n // Define the move action event.\n modal.getRoot().on(ModalEvents.save, () => {\n // Make sure that a move option is selected.\n if (this.moveOptionsTree && this.moveOptionsTree.selectedMoveOption) {\n // Set the relevant form values.\n document.querySelector(Selectors.bulkMoveInput).value = 1;\n document.querySelector(Selectors.bulkMoveAfterInput).value = this.moveOptionsTree.selectedMoveOption.dataset.id;\n // Submit the form.\n document.querySelector(Selectors.editTreeForm).submit();\n }\n });\n }\n\n /**\n * Fetch the grade tree structure for the current course.\n *\n * @method fetchGradeTree\n * @returns {Promise} The grade tree promise\n */\n fetchGradeTree() {\n const request = {\n methodname: 'core_grades_get_grade_tree',\n args: {\n courseid: this.courseId,\n },\n };\n return Ajax.call([request])[0];\n }\n\n /**\n * Renders the bulk move modal body.\n *\n * @method renderModalBody\n * @returns {Promise} The modal body promise\n */\n async renderModalBody() {\n // We need to fetch the grade tree structure only once.\n if (this.gradeTree === null) {\n this.gradeTree = await this.fetchGradeTree();\n }\n\n return Templates.render('core_grades/bulkactions/edit/tree/bulk_move_grade_tree',\n JSON.parse(this.gradeTree));\n }\n\n /**\n * Show the bulk move modal.\n *\n * @method showModal\n * @returns {Promise} The modal promise\n */\n async showModal() {\n const modal = await ModalSaveCancel.create({\n title: await getString('movesitems', 'grades'),\n body: await this.renderModalBody(),\n buttons: {\n save: await getString('move')\n },\n large: true,\n });\n // Disable the 'Move' action button until something is selected.\n modal.setButtonDisabled('save', true);\n modal.show();\n\n return modal;\n }\n}\n"],"names":["Selectors","GradebookEditTreeBulkMove","BulkAction","constructor","courseId","getBulkActionTriggerSelector","modal","this","showModal","registerCustomListenerEvents","showInDropdown","index","Templates","render","showindropdown","isfirst","getBody","getRoot","on","ModalEvents","shown","moveOptionsTree","MoveOptionsTree","setButtonDisabled","hidden","destroy","save","selectedMoveOption","document","querySelector","value","dataset","id","submit","fetchGradeTree","request","methodname","args","courseid","Ajax","call","gradeTree","JSON","parse","ModalSaveCancel","create","title","body","renderModalBody","buttons","large","show"],"mappings":"i9BAgCMA,uBACY,iBADZA,wBAEa,yBAFbA,6BAGkB,gCAGHC,kCAAkCC,qBAiBnDC,YAAYC,kDAdD,6CAGO,uCAGN,WAUHA,SAAWA,SAQpBC,qCACW,iFAUDC,YAAcC,KAAKC,iBACpBC,6BAA6BH,qCAWRI,eAAgBC,cACnCC,mBAAUC,OAAO,sDAAuD,CAC3EC,eAAgBJ,eAChBK,QAAmB,IAAVJ,2CAWkBL,aACzBA,MAAMU,UAEZV,MAAMW,UAAUC,GAAGC,sBAAYC,OAAO,UAC7BC,gBAAkB,IAAIC,4BAAgB,KAEvChB,MAAMiB,kBAAkB,QAAQ,SAIxCjB,MAAMW,UAAUC,GAAGC,sBAAYK,QAAQ,KACnClB,MAAMmB,aAGVnB,MAAMW,UAAUC,GAAGC,sBAAYO,MAAM,KAE7BnB,KAAKc,iBAAmBd,KAAKc,gBAAgBM,qBAE7CC,SAASC,cAAc7B,yBAAyB8B,MAAQ,EACxDF,SAASC,cAAc7B,8BAA8B8B,MAAQvB,KAAKc,gBAAgBM,mBAAmBI,QAAQC,GAE7GJ,SAASC,cAAc7B,wBAAwBiC,aAW3DC,uBACUC,QAAU,CACZC,WAAY,6BACZC,KAAM,CACFC,SAAU/B,KAAKH,kBAGhBmC,cAAKC,KAAK,CAACL,UAAU,kCAWL,OAAnB5B,KAAKkC,iBACAA,gBAAkBlC,KAAK2B,kBAGzBtB,mBAAUC,OAAO,yDACpB6B,KAAKC,MAAMpC,KAAKkC,oCAUdnC,YAAcsC,2BAAgBC,OAAO,CACvCC,YAAa,mBAAU,aAAc,UACrCC,WAAYxC,KAAKyC,kBACjBC,QAAS,CACLvB,WAAY,mBAAU,SAE1BwB,OAAO,WAGX5C,MAAMiB,kBAAkB,QAAQ,GAChCjB,MAAM6C,OAEC7C"}edit/tree/bulk_actions.min.js000064400000003153152266731110012233 0ustar00define("core_grades/bulkactions/edit/tree/bulk_actions",["exports","core/bulkactions/bulk_actions","core_grades/bulkactions/edit/tree/move"],(function(_exports,_bulk_actions,_move){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_actions=_interopRequireDefault(_bulk_actions),_move=_interopRequireDefault(_move); /** * Class for defining the bulk actions area in the gradebook setup page. * * @module core_grades/bulkactions/edit/tree/bulk_actions * @copyright 2023 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ const Selectors_selectBulkItemCheckbox='input[type="checkbox"].itemselect';class GradebookEditTreeBulkActions extends _bulk_actions.default{static init(courseID){return new this(courseID)}constructor(courseID){var obj,key,value;super(),value=null,(key="courseID")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.courseID=courseID}getBulkActions(){return[new _move.default(this.courseID)]}getSelectedItems(){return document.querySelectorAll("".concat(Selectors_selectBulkItemCheckbox,":checked"))}registerItemSelectChangeEvent(eventHandler){document.querySelectorAll(Selectors_selectBulkItemCheckbox).forEach((checkbox=>{checkbox.addEventListener("change",eventHandler.bind(this))}))}deselectItem(selectedItem){selectedItem.checked=!1}}return _exports.default=GradebookEditTreeBulkActions,_exports.default})); //# sourceMappingURL=bulk_actions.min.js.mapedit/tree/move.min.js000064400000006043152266731110010525 0ustar00define("core_grades/bulkactions/edit/tree/move",["exports","core/bulkactions/bulk_action","core/str","core/modal_save_cancel","core/templates","core/ajax","core/modal_events","core_grades/bulkactions/edit/tree/move_options_tree"],(function(_exports,_bulk_action,_str,_modal_save_cancel,_templates,_ajax,_modal_events,_move_options_tree){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_templates=_interopRequireDefault(_templates),_ajax=_interopRequireDefault(_ajax),_modal_events=_interopRequireDefault(_modal_events),_move_options_tree=_interopRequireDefault(_move_options_tree);const Selectors_editTreeForm="#gradetreeform",Selectors_bulkMoveInput='input[name="bulkmove"]',Selectors_bulkMoveAfterInput='input[name="moveafter"]';class GradebookEditTreeBulkMove extends _bulk_action.default{constructor(courseId){super(),_defineProperty(this,"courseId",null),_defineProperty(this,"moveOptionsTree",null),_defineProperty(this,"gradeTree",null),this.courseId=courseId}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="move"]'}async triggerBulkAction(){const modal=await this.showModal();this.registerCustomListenerEvents(modal)}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("core_grades/bulkactions/edit/tree/bulk_move_trigger",{showindropdown:showInDropdown,isfirst:0===index})}async registerCustomListenerEvents(modal){await modal.getBody(),modal.getRoot().on(_modal_events.default.shown,(()=>{this.moveOptionsTree=new _move_options_tree.default((()=>{modal.setButtonDisabled("save",!1)}))})),modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy()})),modal.getRoot().on(_modal_events.default.save,(()=>{this.moveOptionsTree&&this.moveOptionsTree.selectedMoveOption&&(document.querySelector(Selectors_bulkMoveInput).value=1,document.querySelector(Selectors_bulkMoveAfterInput).value=this.moveOptionsTree.selectedMoveOption.dataset.id,document.querySelector(Selectors_editTreeForm).submit())}))}fetchGradeTree(){const request={methodname:"core_grades_get_grade_tree",args:{courseid:this.courseId}};return _ajax.default.call([request])[0]}async renderModalBody(){return null===this.gradeTree&&(this.gradeTree=await this.fetchGradeTree()),_templates.default.render("core_grades/bulkactions/edit/tree/bulk_move_grade_tree",JSON.parse(this.gradeTree))}async showModal(){const modal=await _modal_save_cancel.default.create({title:await(0,_str.get_string)("movesitems","grades"),body:await this.renderModalBody(),buttons:{save:await(0,_str.get_string)("move")},large:!0});return modal.setButtonDisabled("save",!0),modal.show(),modal}}return _exports.default=GradebookEditTreeBulkMove,_exports.default})); //# sourceMappingURL=move.min.js.mapedit/tree/move_options_tree.min.js000064400000003223152266731110013314 0ustar00define("core_grades/bulkactions/edit/tree/move_options_tree",["exports","core/tree","core/normalise"],(function(_exports,_tree,_normalise){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_tree=(obj=_tree)&&obj.__esModule?obj:{default:obj};const Selectors_moveOptionsTree='#destination-selector [role="tree"]',Selectors_moveOption='#destination-selector [role="treeitem"]',Selectors_toggleGroupLink="#destination-selector .collapse-list-link";class MoveOptionsTree extends _tree.default{constructor(afterSelectMoveOptionCallback){super(Selectors_moveOptionsTree),_defineProperty(this,"afterSelectMoveOptionCallback",null),_defineProperty(this,"selectedMoveOption",null),this.afterSelectMoveOptionCallback=afterSelectMoveOptionCallback}handleKeyDown(e){e.keyCode===this.keys.enter||e.keyCode===this.keys.space?this.selectMoveOption(e.target):super.handleKeyDown(e)}handleItemClick(event,item){event.target.closest(Selectors_toggleGroupLink)?super.handleItemClick(event,item):this.selectMoveOption((0,_normalise.getList)(item)[0])}selectMoveOption(moveOption){this.refreshVisibleItemsCache(),document.querySelectorAll(Selectors_moveOption).forEach((item=>{item.dataset.selected="false"})),moveOption.dataset.selected="true",this.selectedMoveOption=moveOption,moveOption.focus(),"function"==typeof this.afterSelectMoveOptionCallback&&this.afterSelectMoveOptionCallback()}}return _exports.default=MoveOptionsTree,_exports.default})); //# sourceMappingURL=move_options_tree.min.js.mapbulk_actions.js000064400000023501152267224730007572 0ustar00// 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 . import Templates from 'core/templates'; import {get_string as getString} from 'core/str'; import {disableStickyFooter, enableStickyFooter} from 'core/sticky-footer'; /** * Base class for defining a bulk actions area within a page. * * @module core/bulkactions/bulk_actions * @copyright 2023 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** @constant {Object} The object containing the relevant selectors. */ const Selectors = { stickyFooterContainer: '#sticky-footer', selectedItemsCountContainer: '[data-type="bulkactions"] [data-for="bulkcount"]', cancelBulkActionModeElement: '[data-type="bulkactions"] [data-action="bulkcancel"]', bulkModeContainer: '[data-type="bulkactions"]', bulkActionsContainer: '[data-type="bulkactions"] [data-for="bulktools"]' }; export default class BulkActions { /** @property {string|null} initialStickyFooterContent The initial content of the sticky footer. */ initialStickyFooterContent = null; /** @property {Array} selectedItems The array of selected item elements. */ selectedItems = []; /** @property {boolean} isBulkActionsModeEnabled Whether the bulk actions mode is enabled. */ isBulkActionsModeEnabled = false; /** * @property {int} maxButtons Sets the maximum number of action buttons to display. If exceeded, additional actions * are shown in a dropdown menu. */ maxButtons = 5; /** * The class constructor. * * @param {int|null} maxButtons Sets the maximum number of action buttons to display. If exceeded, additional actions * are shown in a dropdown menu. * @returns {void} */ constructor(maxButtons = null) { if (!this.getStickyFooterContainer()) { throw new Error('Sticky footer not found.'); } // Store any pre-existing content in the sticky footer. When bulk actions mode is enabled, this content will be // replaced with the bulk actions content and restored when bulk actions mode is disabled. this.initialStickyFooterContent = this.getStickyFooterContainer().innerHTML; if (maxButtons) { this.maxButtons = maxButtons; } // Register and handle the item select change event. this.registerItemSelectChangeEvent(async() => { this.selectedItems = this.getSelectedItems(); if (this.selectedItems.length > 0) { // At least one item is selected. // If the bulk actions mode is already enabled only update the selected items count. if (this.isBulkActionsModeEnabled) { await this.updateBulkItemSelection(); } else { // Otherwise, enable the bulk action mode. await this.enableBulkActionsMode(); } } else { // No items are selected, disable the bulk action mode. this.disableBulkActionsMode(); } }); } /** * Returns the array of the relevant bulk action objects. * * @method getBulkActions * @returns {Array} */ getBulkActions() { throw new Error(`getBulkActions() must be implemented in ${this.constructor.name}`); } /** * Returns the array of selected items. * * @method getSelectedItems * @returns {Array} */ getSelectedItems() { throw new Error(`getSelectedItems() must be implemented in ${this.constructor.name}`); } /** * Adds the listener for the item select change event. * The event handler function that is passed as a parameter should be called right after the event is triggered. * * @method registerItemSelectChangeEvent * @param {function} eventHandler The event handler function. * @returns {void} */ registerItemSelectChangeEvent(eventHandler) { throw new Error(`registerItemSelectChangeEvent(${eventHandler}) must be implemented in ${this.constructor.name}`); } /** * Defines the action for deselecting a selected item. * * The base bulk actions class supports deselecting all selected items but does not have knowledge of the type of the * selected element. Therefore, each subclass must explicitly define the action of resetting the attributes that * indicate a selected state. * * @method deselectItem * @param {HTMLElement} selectedItem The selected element. * @returns {void} */ deselectItem(selectedItem) { throw new Error(`deselectItem(${selectedItem}) must be implemented in ${this.constructor.name}`); } /** * Returns the sticky footer container. * * @method getStickyFooterContainer * @returns {HTMLElement} */ getStickyFooterContainer() { return document.querySelector(Selectors.stickyFooterContainer); } /** * Enables the bulk action mode. * * @method enableBulkActionsMode * @returns {Promise} */ async enableBulkActionsMode() { // Make sure that the sticky footer is enabled. enableStickyFooter(); // Render the bulk actions content in the sticky footer container. this.getStickyFooterContainer().innerHTML = await this.renderBulkActions(); const bulkModeContainer = this.getStickyFooterContainer().querySelector(Selectors.bulkModeContainer); const bulkActionsContainer = bulkModeContainer.querySelector(Selectors.bulkActionsContainer); this.getBulkActions().forEach((bulkAction) => { // Register the listener events for each available bulk action. bulkAction.registerListenerEvents(bulkActionsContainer); // Set the selected items for each available bulk action. bulkAction.setSelectedItems(this.selectedItems); }); // Register the click listener event for the cancel bulk mode button. bulkModeContainer.addEventListener('click', (e) => { if (e.target.closest(Selectors.cancelBulkActionModeElement)) { // Deselect all selected items. this.selectedItems.forEach((item) => { this.deselectItem(item); }); // Disable the bulk action mode. this.disableBulkActionsMode(); } }); this.isBulkActionsModeEnabled = true; // Calling `renderBulkActions()` already renders the item selection count. // Because of this, screen readers will not announce the item selection count on first load given that aria-live regions // must be present in the DOM and have their contents changed before screen readers can announce their content. // So, we call `updateBulkItemSelection()` after a short delay to ensure that screen readers announce the item count. setTimeout(async() => { await this.updateBulkItemSelection(); }, 300); } /** * Disables the bulk action mode. * * @method disableBulkActionsMode * @returns {void} */ disableBulkActionsMode() { // If there was any previous (initial) content in the sticky footer, restore it. if (this.initialStickyFooterContent.length > 0) { this.getStickyFooterContainer().innerHTML = this.initialStickyFooterContent; } else { // No previous content to restore, disable the sticky footer. disableStickyFooter(); } this.isBulkActionsModeEnabled = false; } /** * Renders the bulk actions content. * * @method renderBulkActions * @returns {Promise} */ async renderBulkActions() { const data = { bulkselectioncount: this.selectedItems.length, actions: [], moreactions: [], hasmoreactions: false, }; const bulkActions = this.getBulkActions(); const showMoreButton = bulkActions.length > this.maxButtons; // Get all bulk actions and render them in order. const actions = await Promise.all( bulkActions.map((bulkAction, index) => bulkAction.renderBulkActionTrigger( showMoreButton && (index >= this.maxButtons - 1), index ) ) ); // Separate rendered actions into data.actions and data.moreactions in the correct order. actions.forEach((actionTrigger, index) => { if (showMoreButton && (index >= this.maxButtons - 1)) { data.moreactions.push({'actiontrigger': actionTrigger}); } else { data.actions.push({'actiontrigger': actionTrigger}); } }); data.hasmoreactions = data.moreactions.length > 0; return Templates.render('core/bulkactions/bulk_actions', data); } /** * Updates the selected items count in the bulk actions content. * * @method updateBulkItemSelection * @returns {void} */ async updateBulkItemSelection() { const bulkSelection = await getString('bulkselection', 'core', this.selectedItems.length); document.querySelector(Selectors.selectedItemsCountContainer).innerHTML = bulkSelection; } } bulk_action.js000064400000005706152267224730007416 0ustar00// 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 . /** * Base class for defining a bulk action. * * @module core/bulkactions/bulk_action * @copyright 2023 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ export default class BulkAction { /** @property {array} selectedItems The array of selected item elements. */ selectedItems = []; /** * Registers the listener events for the bulk actions. * * @method registerListenerEvents * @param {HTMLElement} containerElement The container element for the bulk actions. * @returns {void} */ registerListenerEvents(containerElement) { // Listen for the click event on the bulk action trigger element. containerElement.addEventListener('click', (e) => { if (e.target.closest(this.getBulkActionTriggerSelector())) { e.preventDefault(); this.triggerBulkAction(); } }); } /** * Setter method for the selectedItems property. * * @method setSelectedItems * @param {Array} selectedItems The array of selected item elements.. * @returns {void} */ setSelectedItems(selectedItems) { this.selectedItems = selectedItems; } /** * Defines the selector of the element that triggers the bulk action. * * @method getBulkActionTriggerSelector * @returns {string} */ getBulkActionTriggerSelector() { throw new Error(`getBulkActionTriggerSelector() must be implemented in ${this.constructor.name}`); } /** * Defines the behavior once the bulk action is triggered. * * @method triggerBulkAction */ triggerBulkAction() { throw new Error(`triggerBulkAction() must be implemented in ${this.constructor.name}`); } /** * Renders the bulk action trigger element. * * @method renderBulkActionTrigger * @param {boolean} showInDropdown Whether the action is displayed under a 'More' dropdown or as a separate button. * @param {number} index The index of the action. * @returns {Promise} */ renderBulkActionTrigger(showInDropdown = false, index) { throw new Error(`renderBulkActionTrigger(${showInDropdown}, ${index}) must be implemented in ${this.constructor.name}`); } } bulk_action_trigger.mustache000064400000003162152267235550012332 0ustar00{{! 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/bulkactions/bulk_action_trigger Renders a trigger element for a given bulk action. Context variables required for this template: * showindropdown - Whether the action is displayed under a 'More' dropdown or as a separate button. * isfirst - Whether the action is the first one in the list. Example context (json): { "showindropdown": false, "isfirst": true } }} {{#showindropdown}} {{$title}}{{/title}} {{/showindropdown}} {{^showindropdown}} {{/showindropdown}} bulk_actions.mustache000064400000006557152267235550011005 0ustar00{{! 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/bulkactions/bulk_actions Renders a section with all available bulk actions. Context variables required for this template: * actions - Array of objects with the following properties: * actiontrigger - The HTML for the action trigger. * hasmoreactions - Whether there are more actions than the ones displayed. * moreactions - Array of objects with the following properties: * actiontrigger - The HTML for the action trigger. Example context (json): { "actions": [ { "actiontrigger": "" } ], "hasmoreactions": true, "moreactions": [ { "actiontrigger": "" } ] } }}
 {{#str}} bulkselection, core, {{bulkselectioncount}} {{/str}}