ÿØÿà 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-Ї Ç actions.min.js000064400000012250152266416600007334 0ustar00define("core_badges/actions",["exports","core_badges/selectors","core/notification","core/prefetch","core/str","core/ajax","core/pending","core/event_dispatcher","core/toast","core_reportbuilder/local/events","core_reportbuilder/local/selectors"],(function(_exports,_selectors,_notification,_prefetch,_str,_ajax,_pending,_event_dispatcher,_toast,reportEvents,reportSelectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Various actions on badges - enabling, disabling, etc. * * @module core_badges/actions * @copyright 2024 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_selectors=_interopRequireDefault(_selectors),_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_pending=_interopRequireDefault(_pending),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);_exports.init=()=>{(0,_prefetch.prefetchStrings)("core_badges",["reviewconfirm","activatesuccess","deactivatesuccess","awardoncron","numawardstat"]),(0,_prefetch.prefetchStrings)("core",["confirm","enable"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const enableOption=event.target.closest(_selectors.default.actions.enablebadge);if(enableOption){event.preventDefault();const reportElement=event.target.closest(reportSelectors.regions.report),triggerElement=reportElement?enableOption.closest(".dropdown").querySelector(".dropdown-toggle"):null,badgeId=enableOption.dataset.badgeid,badgeName=enableOption.dataset.badgename;_notification.default.saveCancelPromise((0,_str.getString)("confirm","core"),(0,_str.getString)("reviewconfirm","core_badges",badgeName),(0,_str.getString)("enable","core"),{triggerElement:triggerElement}).then((()=>async function(badgeId,badgeName,reportElement){var request={methodname:"core_badges_enable_badges",args:{badgeids:[badgeId]}};const pendingPromise=new _pending.default("core_badges/enable");try{const result=await _ajax.default.call([request])[0];if(reportElement)!function(badgeName,result){var _result$result2;if((null===(_result$result2=result.result)||void 0===_result$result2?void 0:_result$result2.length)>0){var _result$result3;(0,_toast.add)((0,_str.getString)("activatesuccess","core_badges",badgeName),{type:"success"});const awards=null===(_result$result3=result.result)||void 0===_result$result3?void 0:_result$result3.pop().awards;"cron"==awards?(0,_toast.add)((0,_str.getString)("awardoncron","core_badges",{badgename:badgeName})):awards>0&&(0,_toast.add)((0,_str.getString)("numawardstat","core_badges",{badgename:badgeName,awards:awards}))}else result.warnings.length>0&&(0,_toast.add)(result.warnings[0].message,{type:"danger"})}(badgeName,result),(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement);else{var _result$result;const awards=null===(_result$result=result.result)||void 0===_result$result?void 0:_result$result.pop().awards;document.location=document.location.pathname+"?id=".concat(badgeId,"&awards=").concat(awards)}}catch(error){_notification.default.exception(error)}pendingPromise.resolve()}(badgeId,badgeName,reportElement))).catch((()=>{}))}const disableOption=event.target.closest(_selectors.default.actions.disablebadge);if(disableOption){event.preventDefault();!async function(badgeId,badgeName,reportElement){var request={methodname:"core_badges_disable_badges",args:{badgeids:[badgeId]}};try{const result=await _ajax.default.call([request])[0];reportElement?(!function(badgeName,result){result.result?(0,_toast.add)((0,_str.getString)("deactivatesuccess","core_badges",badgeName),{type:"success"}):result.warnings.length>0&&(0,_toast.add)(result.warnings[0].message,{type:"danger"})}(badgeName,result),(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement)):document.location=document.location.pathname+"?id=".concat(badgeId)}catch(error){_notification.default.exception(error)}}(disableOption.dataset.badgeid,disableOption.dataset.badgename,event.target.closest(reportSelectors.regions.report))}}))}})); //# sourceMappingURL=actions.min.js.mapactionbar/initials.min.js.map000064400000025225152266416600012234 0ustar00{"version":3,"file":"initials.min.js","sources":["../../src/actionbar/initials.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 * A small dropdown to filter users.\n *\n * @module core_course/actionbar/initials\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport * as Url from 'core/url';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport Dropdown from 'theme_boost/bootstrap/dropdown';\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n// Contain our selectors within this file until they could be of use elsewhere.\nconst selectors = {\n pageListItem: 'page-item',\n pageClickableItem: '.page-link',\n activeItem: 'active',\n formDropdown: '.initialsdropdownform',\n parentDomNode: '.initials-selector',\n firstInitial: 'firstinitial',\n lastInitial: 'lastinitial',\n initialBars: '.initialbar', // Both first and last name use this class.\n targetButton: 'initialswidget',\n formItems: {\n type: 'submit',\n save: 'save',\n cancel: 'cancel'\n }\n};\n\n/**\n * Our initial hook into the module which will eventually allow us to handle the dropdown initials bar form.\n *\n * @param {String} callingLink The link to redirect upon form submission.\n * @param {String} firstInitialParam The URL parameter to set for the first name initial.\n * @param {String} lastInitialParam The URL parameter to set for the last name initial.\n * @param {Array} additionalParams Any additional parameters to set for the URL.\n */\nexport const init = (callingLink, firstInitialParam = 'sifirst',\n lastInitialParam = 'silast', additionalParams = []) => {\n if (registered) {\n return;\n }\n const pendingPromise = new Pending();\n registerListenerEvents(callingLink, firstInitialParam, lastInitialParam, additionalParams);\n // BS events always bubble so, we need to listen for the event higher up the chain.\n document.querySelector(selectors.parentDomNode).addEventListener('shown.bs.dropdown', () => {\n document.querySelector(selectors.pageClickableItem).focus({preventScroll: true});\n });\n pendingPromise.resolve();\n registered = true;\n};\n\n/**\n * Register event listeners.\n *\n * @param {String} callingLink The link to redirect upon form submission.\n * @param {String} firstInitialParam The URL parameter to set for the first name initial.\n * @param {String} lastInitialParam The URL parameter to set for the last name initial.\n * @param {Array} additionalParams Any additional parameters to set for the URL.\n */\nconst registerListenerEvents = (callingLink, firstInitialParam = 'sifirst',\n lastInitialParam = 'silast', additionalParams = []) => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n // Register events.\n events.forEach((event) => {\n document.addEventListener(event, (e) => {\n // Always fetch the latest information when we click as state is a fickle thing.\n let {firstActive, lastActive, sifirst, silast} = onClickVariables();\n let itemToReset = '';\n\n // Prevent the usual form behaviour.\n if (e.target.closest(selectors.formDropdown)) {\n e.preventDefault();\n }\n\n // Handle the state of active initials before form submission.\n if (e.target.closest(`${selectors.formDropdown} .${selectors.pageListItem}`)) {\n // Ensure the li items don't cause weird clicking emptying out the form.\n if (e.target.classList.contains(selectors.pageListItem)) {\n return;\n }\n\n const initialsBar = e.target.closest(selectors.initialBars); // Find out which initial bar we are in.\n\n // We want to find the current active item in the menu area the user selected.\n // We also want to fetch the raw item out of the array for instant manipulation.\n if (initialsBar.classList.contains(selectors.firstInitial)) {\n sifirst = e.target;\n itemToReset = firstActive;\n } else {\n silast = e.target;\n itemToReset = lastActive;\n }\n swapActiveItems(itemToReset, e);\n }\n\n // Handle form submissions.\n if (e.target.closest(`${selectors.formDropdown}`) && e.target.type === selectors.formItems.type) {\n if (e.target.dataset.action === selectors.formItems.save) {\n // Ensure we strip out the value (All) as it messes with the PHP side of the initials bar.\n // Then we will redirect the user back onto the page with new filters applied.\n const params = {\n 'id': e.target.closest(selectors.formDropdown).dataset.courseid,\n [firstInitialParam]: sifirst.parentElement.classList.contains('initialbarall') ? '' : sifirst.value,\n [lastInitialParam]: silast.parentElement.classList.contains('initialbarall') ? '' : silast.value,\n };\n\n // If additional parameters are passed, add them here (overriding any already set above).\n for (const [key, value] of Object.entries(additionalParams)) {\n params[key] = value;\n }\n window.location = Url.relativeUrl(callingLink, params);\n }\n if (e.target.dataset.action === selectors.formItems.cancel) {\n Dropdown.getOrCreateInstance(document.querySelector(`.${selectors.targetButton}`)).toggle();\n }\n }\n });\n });\n};\n\n/**\n * A small abstracted helper function which allows us to ensure we have up-to-date lists of nodes.\n *\n * @returns {{firstActive: HTMLElement, lastActive: HTMLElement, sifirst: ?String, silast: ?String}}\n */\nconst onClickVariables = () => {\n // Ensure we have an up-to-date initials bar.\n const firstItems = [...document.querySelectorAll(`.${selectors.firstInitial} li`)];\n const lastItems = [...document.querySelectorAll(`.${selectors.lastInitial} li`)];\n const firstActive = firstItems.filter((item) => item.classList.contains(selectors.activeItem))[0];\n const lastActive = lastItems.filter((item) => item.classList.contains(selectors.activeItem))[0];\n // Ensure we retain both of the selections from a previous instance.\n let sifirst = firstActive.querySelector(selectors.pageClickableItem);\n let silast = lastActive.querySelector(selectors.pageClickableItem);\n return {firstActive, lastActive, sifirst, silast};\n};\n\n/**\n * Given we are provided the old li and current click event, swap around the active properties.\n *\n * @param {HTMLElement} itemToReset\n * @param {Event} e\n */\nconst swapActiveItems = (itemToReset, e) => {\n itemToReset.classList.remove(selectors.activeItem);\n itemToReset.querySelector(selectors.pageClickableItem).ariaCurrent = false;\n\n // Set the select item as the current item.\n const itemToSetActive = e.target.parentElement;\n itemToSetActive.classList.add(selectors.activeItem);\n e.target.ariaCurrent = true;\n};\n"],"names":["registered","selectors","type","save","cancel","callingLink","firstInitialParam","lastInitialParam","additionalParams","pendingPromise","Pending","registerListenerEvents","document","querySelector","addEventListener","focus","preventScroll","resolve","events","CustomEvents","activate","keyboardActivate","define","forEach","event","e","firstActive","lastActive","sifirst","silast","onClickVariables","itemToReset","target","closest","preventDefault","classList","contains","swapActiveItems","dataset","action","params","courseid","parentElement","value","key","Object","entries","window","location","Url","relativeUrl","getOrCreateInstance","toggle","firstItems","querySelectorAll","lastItems","filter","item","remove","ariaCurrent","add"],"mappings":";;;;;;;g5BAiCIA,YAAa,QAGXC,uBACY,YADZA,4BAEiB,aAFjBA,qBAGU,SAHVA,uBAIY,wBAJZA,wBAKa,qBALbA,uBAMY,eANZA,sBAOW,cAPXA,sBAQW,cARXA,uBASY,iBATZA,oBAUS,CACPC,KAAM,SACNC,KAAM,OACNC,OAAQ,wBAYI,SAACC,iBAAaC,yEAAoB,UAC9CC,wEAAmB,SAAUC,wEAAmB,MAChDR,wBAGES,eAAiB,IAAIC,iBAC3BC,uBAAuBN,YAAaC,kBAAmBC,iBAAkBC,kBAEzEI,SAASC,cAAcZ,yBAAyBa,iBAAiB,qBAAqB,KAClFF,SAASC,cAAcZ,6BAA6Bc,MAAM,CAACC,eAAe,OAE9EP,eAAeQ,UACfjB,YAAa,SAWXW,uBAAyB,SAACN,iBAAaC,yEAAoB,UACzDC,wEAAmB,SAAUC,wEAAmB,SAC9CU,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,qDAEXC,OAAOV,SAAUM,QAG9BA,OAAOK,SAASC,QACZZ,SAASE,iBAAiBU,OAAQC,QAE1BC,YAACA,YAADC,WAAcA,WAAdC,QAA0BA,QAA1BC,OAAmCA,QAAUC,mBAC7CC,YAAc,MAGdN,EAAEO,OAAOC,QAAQhC,yBACjBwB,EAAES,iBAIFT,EAAEO,OAAOC,kBAAWhC,oCAA2BA,yBAA2B,IAEtEwB,EAAEO,OAAOG,UAAUC,SAASnC,+BAIZwB,EAAEO,OAAOC,QAAQhC,uBAIrBkC,UAAUC,SAASnC,yBAC/B2B,QAAUH,EAAEO,OACZD,YAAcL,cAEdG,OAASJ,EAAEO,OACXD,YAAcJ,YAElBU,gBAAgBN,YAAaN,MAI7BA,EAAEO,OAAOC,kBAAWhC,0BAA6BwB,EAAEO,OAAO9B,OAASD,oBAAoBC,KAAM,IACzFuB,EAAEO,OAAOM,QAAQC,SAAWtC,oBAAoBE,KAAM,OAGhDqC,OAAS,IACLf,EAAEO,OAAOC,QAAQhC,wBAAwBqC,QAAQG,UACtDnC,mBAAoBsB,QAAQc,cAAcP,UAAUC,SAAS,iBAAmB,GAAKR,QAAQe,OAC7FpC,kBAAmBsB,OAAOa,cAAcP,UAAUC,SAAS,iBAAmB,GAAKP,OAAOc,WAI1F,MAAOC,IAAKD,SAAUE,OAAOC,QAAQtC,kBACtCgC,OAAOI,KAAOD,MAElBI,OAAOC,SAAWC,IAAIC,YAAY7C,YAAamC,QAE/Cf,EAAEO,OAAOM,QAAQC,SAAWtC,oBAAoBG,0BACvC+C,oBAAoBvC,SAASC,yBAAkBZ,0BAA2BmD,iBAYjGtB,iBAAmB,WAEfuB,WAAa,IAAIzC,SAAS0C,4BAAqBrD,gCAC/CsD,UAAY,IAAI3C,SAAS0C,4BAAqBrD,+BAC9CyB,YAAc2B,WAAWG,QAAQC,MAASA,KAAKtB,UAAUC,SAASnC,wBAAuB,GACzF0B,WAAa4B,UAAUC,QAAQC,MAASA,KAAKtB,UAAUC,SAASnC,wBAAuB,OAEzF2B,QAAUF,YAAYb,cAAcZ,6BACpC4B,OAASF,WAAWd,cAAcZ,mCAC/B,CAACyB,YAAAA,YAAaC,WAAAA,WAAYC,QAAAA,QAASC,OAAAA,SASxCQ,gBAAkB,CAACN,YAAaN,KAClCM,YAAYI,UAAUuB,OAAOzD,sBAC7B8B,YAAYlB,cAAcZ,6BAA6B0D,aAAc,EAG7ClC,EAAEO,OAAOU,cACjBP,UAAUyB,IAAI3D,sBAC9BwB,EAAEO,OAAO2B,aAAc"}actionbar/group.min.js.map000064400000005101152266416600011543 0ustar00{"version":3,"file":"group.min.js","sources":["../../src/actionbar/group.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 GroupSearch from 'core_group/comboboxsearch/group';\n\n/**\n * Allow the user to search for groups in the action bar.\n *\n * @module core_course/actionbar/group\n * @copyright 2024 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class Group extends GroupSearch {\n\n baseUrl;\n\n /**\n * Construct the class.\n *\n * @param {string} baseUrl The base URL for the page.\n * @param {int|null} cmid ID of the course module initiating the group search (optional).\n */\n constructor(baseUrl, cmid = null) {\n super(cmid);\n this.baseUrl = baseUrl;\n }\n\n /**\n * Allow the class to be invoked via PHP.\n *\n * @param {string} baseUrl The base URL for the page.\n * @param {int|null} cmid ID of the course module initiating the group search (optional).\n * @returns {Group}\n */\n static init(baseUrl, cmid = null) {\n return new Group(baseUrl, cmid);\n }\n\n /**\n * Build up the link that is dedicated to a particular result.\n *\n * @param {Number} groupID The ID of the group selected.\n * @returns {string}\n */\n selectOneLink(groupID) {\n const url = new URL(this.baseUrl);\n url.searchParams.set('groupsearchvalue', this.getSearchTerm());\n url.searchParams.set('group', groupID);\n\n return url.toString();\n }\n}\n"],"names":["Group","GroupSearch","constructor","baseUrl","selectOneLink","groupID","url","URL","this","searchParams","set","getSearchTerm","toString"],"mappings":";;;;;;;;MAwBqBA,cAAcC,eAU/BC,YAAYC,sEAAgB,4KAEnBA,QAAUA,oBAUPA,gBACD,IAAIH,MAAMG,+DADO,MAU5BC,cAAcC,eACJC,IAAM,IAAIC,IAAIC,KAAKL,gBACzBG,IAAIG,aAAaC,IAAI,mBAAoBF,KAAKG,iBAC9CL,IAAIG,aAAaC,IAAI,QAASL,SAEvBC,IAAIM"}actionbar/initials.min.js000064400000012464152266416600011461 0ustar00define("core_course/actionbar/initials",["exports","core/pending","core/url","core/custom_interaction_events","theme_boost/bootstrap/dropdown"],(function(_exports,_pending,Url,_custom_interaction_events,_dropdown){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * A small dropdown to filter users. * * @module core_course/actionbar/initials * @copyright 2022 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Url=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Url),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_dropdown=_interopRequireDefault(_dropdown);let registered=!1;const selectors_pageListItem="page-item",selectors_pageClickableItem=".page-link",selectors_activeItem="active",selectors_formDropdown=".initialsdropdownform",selectors_parentDomNode=".initials-selector",selectors_firstInitial="firstinitial",selectors_lastInitial="lastinitial",selectors_initialBars=".initialbar",selectors_targetButton="initialswidget",selectors_formItems={type:"submit",save:"save",cancel:"cancel"};_exports.init=function(callingLink){let firstInitialParam=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"sifirst",lastInitialParam=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"silast",additionalParams=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(callingLink,firstInitialParam,lastInitialParam,additionalParams),document.querySelector(selectors_parentDomNode).addEventListener("shown.bs.dropdown",(()=>{document.querySelector(selectors_pageClickableItem).focus({preventScroll:!0})})),pendingPromise.resolve(),registered=!0};const registerListenerEvents=function(callingLink){let firstInitialParam=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"sifirst",lastInitialParam=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"silast",additionalParams=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(e=>{let{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}=onClickVariables(),itemToReset="";if(e.target.closest(selectors_formDropdown)&&e.preventDefault(),e.target.closest("".concat(selectors_formDropdown," .").concat(selectors_pageListItem))){if(e.target.classList.contains(selectors_pageListItem))return;e.target.closest(selectors_initialBars).classList.contains(selectors_firstInitial)?(sifirst=e.target,itemToReset=firstActive):(silast=e.target,itemToReset=lastActive),swapActiveItems(itemToReset,e)}if(e.target.closest("".concat(selectors_formDropdown))&&e.target.type===selectors_formItems.type){if(e.target.dataset.action===selectors_formItems.save){const params={id:e.target.closest(selectors_formDropdown).dataset.courseid,[firstInitialParam]:sifirst.parentElement.classList.contains("initialbarall")?"":sifirst.value,[lastInitialParam]:silast.parentElement.classList.contains("initialbarall")?"":silast.value};for(const[key,value]of Object.entries(additionalParams))params[key]=value;window.location=Url.relativeUrl(callingLink,params)}e.target.dataset.action===selectors_formItems.cancel&&_dropdown.default.getOrCreateInstance(document.querySelector(".".concat(selectors_targetButton))).toggle()}}))}))},onClickVariables=()=>{const firstItems=[...document.querySelectorAll(".".concat(selectors_firstInitial," li"))],lastItems=[...document.querySelectorAll(".".concat(selectors_lastInitial," li"))],firstActive=firstItems.filter((item=>item.classList.contains(selectors_activeItem)))[0],lastActive=lastItems.filter((item=>item.classList.contains(selectors_activeItem)))[0];let sifirst=firstActive.querySelector(selectors_pageClickableItem),silast=lastActive.querySelector(selectors_pageClickableItem);return{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}},swapActiveItems=(itemToReset,e)=>{itemToReset.classList.remove(selectors_activeItem),itemToReset.querySelector(selectors_pageClickableItem).ariaCurrent=!1;e.target.parentElement.classList.add(selectors_activeItem),e.target.ariaCurrent=!0}})); //# sourceMappingURL=initials.min.js.mapactionbar/group.min.js000064400000002233152266416600010772 0ustar00define("core_course/actionbar/group",["exports","core_group/comboboxsearch/group"],(function(_exports,_group){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_group=(obj=_group)&&obj.__esModule?obj:{default:obj}; /** * Allow the user to search for groups in the action bar. * * @module core_course/actionbar/group * @copyright 2024 Shamim Rezaie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class Group extends _group.default{constructor(baseUrl){super(arguments.length>1&&void 0!==arguments[1]?arguments[1]:null),function(obj,key,value){key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}(this,"baseUrl",void 0),this.baseUrl=baseUrl}static init(baseUrl){return new Group(baseUrl,arguments.length>1&&void 0!==arguments[1]?arguments[1]:null)}selectOneLink(groupID){const url=new URL(this.baseUrl);return url.searchParams.set("groupsearchvalue",this.getSearchTerm()),url.searchParams.set("group",groupID),url.toString()}}return _exports.default=Group,_exports.default})); //# sourceMappingURL=group.min.js.maprepository.min.js000064400000004271152266416600010117 0ustar00define("core_h5p/repository",["exports","core/ajax","core/config"],(function(_exports,_ajax,config){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.postStatement=_exports.postState=_exports.deleteState=void 0,config=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj} /** * Module to handle AJAX interactions. * * @module core_h5p/repository * @copyright 2023 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */(config);_exports.postStatement=(component,statements)=>(0,_ajax.call)([{methodname:"core_xapi_statement_post",args:{component:component,requestjson:JSON.stringify(statements)}}])[0];_exports.postState=(component,activityId,agent,stateId,stateData)=>{const requestUrl=new URL("".concat(config.wwwroot,"/lib/ajax/service.php"));requestUrl.searchParams.set("sesskey",config.sesskey),navigator.sendBeacon(requestUrl,JSON.stringify([{index:0,methodname:"core_xapi_post_state",args:{component:component,activityId:activityId,agent:JSON.stringify(agent),stateId:stateId,stateData:stateData}}]))};_exports.deleteState=(component,activityId,agent,stateId)=>(0,_ajax.call)([{methodname:"core_xapi_delete_state",args:{component:component,activityId:activityId,agent:JSON.stringify(agent),stateId:stateId}}])[0]})); //# sourceMappingURL=repository.min.js.maprecommendations.min.js000064400000002053152266416600011063 0ustar00define("core_course/recommendations",["exports","core/ajax","core/notification"],(function(_exports,_ajax,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * A javascript module to handle toggling activity chooser recommendations. * * @module core_course/recommendations * @copyright 2020 Adrian Greeve * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_notification=_interopRequireDefault(_notification);const toggleRecommendation=e=>{let data={methodname:"core_course_toggle_activity_recommendation",args:{area:e.currentTarget.dataset.area,id:e.currentTarget.dataset.id}};_ajax.default.call([data])[0].fail(_notification.default.exception)};_exports.init=()=>{document.querySelectorAll("[data-area]").forEach((checkbox=>{checkbox.addEventListener("change",toggleRecommendation)}))}})); //# sourceMappingURL=recommendations.min.js.mapcopy_modal.min.js.map000064400000015622152266416600010604 0ustar00{"version":3,"file":"copy_modal.min.js","sources":["../src/copy_modal.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 * This module provides the course copy modal from the course and\n * category management screen.\n *\n * @module core_course/copy_modal\n * @copyright 2020 onward The Moodle Users Association \n * @author Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.9\n */\n\nimport {get_string as getString} from 'core/str';\nimport Modal from 'core/modal';\nimport * as ajax from 'core/ajax';\nimport * as Fragment from 'core/fragment';\nimport Notification from 'core/notification';\nimport * as Config from 'core/config';\n\nexport default class CopyModal {\n static init(context) {\n return new CopyModal(context);\n }\n\n constructor(context) {\n this.contextid = context;\n\n this.registerEventListeners();\n }\n\n registerEventListeners() {\n document.addEventListener('click', (e) => {\n const copyAction = e.target.closest('.action-copy');\n if (!copyAction) {\n return;\n }\n e.preventDefault(); // Stop. Hammer time.\n\n const url = new URL(copyAction.href);\n const params = new URLSearchParams(url.search);\n\n this.fetchCourseData(params.get('id'))\n .then(([course]) => this.createModal(course))\n .catch((error) => Notification.exception(error));\n });\n }\n\n fetchCourseData(courseid) {\n return ajax.call([{\n methodname: 'core_course_get_courses',\n args: {\n options: {\n ids: [courseid],\n },\n },\n }])[0];\n }\n\n submitBackupRequest(jsonformdata) {\n return ajax.call([{\n methodname: 'core_backup_submit_copy_form',\n args: {\n jsonformdata,\n },\n }])[0];\n }\n\n createModal(\n course,\n formdata = {},\n ) {\n const params = {\n jsonformdata: JSON.stringify(formdata),\n courseid: course.id,\n };\n\n // Create the Modal.\n return Modal.create({\n title: getString('copycoursetitle', 'backup', course.shortname),\n body: Fragment.loadFragment('course', 'new_base_form', this.contextid, params),\n large: true,\n show: true,\n removeOnClose: true,\n })\n .then((modal) => {\n // Explicitly handle form click events.\n modal.getRoot().on('click', '#id_submitreturn', (e) => {\n this.processModalForm(course, modal, e);\n });\n modal.getRoot().on('click', '#id_cancel', (e) => {\n e.preventDefault();\n modal.destroy();\n });\n modal.getRoot().on('click', '#id_submitdisplay', (e) => {\n e.formredirect = true;\n this.processModalForm(course, modal, e);\n\n });\n\n return modal;\n });\n }\n\n processModalForm(course, modal, e) {\n e.preventDefault(); // Stop modal from closing.\n\n // Form data.\n const copyform = modal.getRoot().find('form').serialize();\n const formjson = JSON.stringify(copyform);\n\n // Handle invalid form fields for better UX.\n const invalid = modal.getRoot()[0].querySelectorAll('[aria-invalid=\"true\"], .error');\n if (invalid.length) {\n invalid[0].focus();\n return;\n }\n\n modal.destroy();\n\n // Submit form via ajax.\n this.submitBackupRequest(formjson)\n .then(() => {\n if (e.formredirect == true) {\n // We are redirecting to copy progress display.\n const redirect = `${Config.wwwroot}/backup/copyprogress.php?id=${course.id}`;\n window.location.assign(redirect);\n }\n\n return;\n })\n .catch(() => {\n // Form submission failed server side, redisplay with errors.\n this.createModal(course, copyform);\n });\n }\n}\n"],"names":["CopyModal","context","constructor","contextid","registerEventListeners","document","addEventListener","e","copyAction","target","closest","preventDefault","url","URL","href","params","URLSearchParams","search","fetchCourseData","get","then","_ref","course","this","createModal","catch","error","Notification","exception","courseid","ajax","call","methodname","args","options","ids","submitBackupRequest","jsonformdata","formdata","JSON","stringify","id","Modal","create","title","shortname","body","Fragment","loadFragment","large","show","removeOnClose","modal","getRoot","on","processModalForm","destroy","formredirect","copyform","find","serialize","formjson","invalid","querySelectorAll","length","focus","redirect","Config","wwwroot","window","location","assign"],"mappings":";;;;;;;;;;0SAiCqBA,sBACLC,gBACD,IAAID,UAAUC,SAGzBC,YAAYD,cACHE,UAAYF,aAEZG,yBAGTA,yBACIC,SAASC,iBAAiB,SAAUC,UAC1BC,WAAaD,EAAEE,OAAOC,QAAQ,oBAC/BF,kBAGLD,EAAEI,uBAEIC,IAAM,IAAIC,IAAIL,WAAWM,MACzBC,OAAS,IAAIC,gBAAgBJ,IAAIK,aAElCC,gBAAgBH,OAAOI,IAAI,OAC/BC,MAAKC,WAAEC,oBAAYC,KAAKC,YAAYF,WACpCG,OAAOC,OAAUC,sBAAaC,UAAUF,YAIjDR,gBAAgBW,iBACLC,KAAKC,KAAK,CAAC,CACdC,WAAY,0BACZC,KAAM,CACFC,QAAS,CACLC,IAAK,CAACN,eAGd,GAGRO,oBAAoBC,qBACTP,KAAKC,KAAK,CAAC,CACdC,WAAY,+BACZC,KAAM,CACFI,aAAAA,iBAEJ,GAGRb,YACIF,YACAgB,gEAAW,SAELvB,OAAS,CACXsB,aAAcE,KAAKC,UAAUF,UAC7BT,SAAUP,OAAOmB,WAIdC,eAAMC,OAAO,CAChBC,OAAO,mBAAU,kBAAmB,SAAUtB,OAAOuB,WACrDC,KAAMC,SAASC,aAAa,SAAU,gBAAiBzB,KAAKpB,UAAWY,QACvEkC,OAAO,EACPC,MAAM,EACNC,eAAe,IAElB/B,MAAMgC,QAEHA,MAAMC,UAAUC,GAAG,QAAS,oBAAqB/C,SACxCgD,iBAAiBjC,OAAQ8B,MAAO7C,MAEzC6C,MAAMC,UAAUC,GAAG,QAAS,cAAe/C,IACvCA,EAAEI,iBACFyC,MAAMI,aAEVJ,MAAMC,UAAUC,GAAG,QAAS,qBAAsB/C,IAC9CA,EAAEkD,cAAe,OACZF,iBAAiBjC,OAAQ8B,MAAO7C,MAIlC6C,SAIfG,iBAAiBjC,OAAQ8B,MAAO7C,GAC5BA,EAAEI,uBAGI+C,SAAWN,MAAMC,UAAUM,KAAK,QAAQC,YACxCC,SAAWtB,KAAKC,UAAUkB,UAG1BI,QAAUV,MAAMC,UAAU,GAAGU,iBAAiB,iCAChDD,QAAQE,OACRF,QAAQ,GAAGG,SAIfb,MAAMI,eAGDpB,oBAAoByB,UACxBzC,MAAK,QACoB,GAAlBb,EAAEkD,aAAsB,OAElBS,mBAAcC,OAAOC,+CAAsC9C,OAAOmB,IACxE4B,OAAOC,SAASC,OAAOL,cAK9BzC,OAAM,UAEED,YAAYF,OAAQoC"}activitychooser.min.js.map000064400000061127152266416600011676 0ustar00{"version":3,"file":"activitychooser.min.js","sources":["../src/activitychooser.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 * A type of dialogue used as for choosing modules in a course.\n *\n * @module core_course/activitychooser\n * @copyright 2020 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as ChooserDialogue from 'core_course/local/activitychooser/dialogue';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport CustomEvents from 'core/custom_interaction_events';\nimport * as Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport Modal from 'core/modal';\nimport Pending from 'core/pending';\n\n// Set up some JS module wide constants that can be added to in the future.\n\n// Tab config options.\nconst ALLACTIVITIESRESOURCES = 0;\nconst ACTIVITIESRESOURCES = 2;\nconst ALLACTIVITIESRESOURCESREC = 3;\nconst ONLYALLREC = 4;\nconst ACTIVITIESRESOURCESREC = 5;\n\n\n// Module types.\nconst ACTIVITY = 0;\nconst RESOURCE = 1;\n\nlet initialized = false;\n\n/**\n * Set up the activity chooser.\n *\n * @method init\n * @param {Number} courseId Course ID to use later on in fetchModules()\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n */\nexport const init = (courseId, chooserConfig) => {\n const pendingPromise = new Pending();\n\n registerListenerEvents(courseId, chooserConfig);\n\n pendingPromise.resolve();\n};\n\n/**\n * Once a selection has been made make the modal & module information and pass it along\n *\n * @method registerListenerEvents\n * @param {Number} courseId\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n */\nconst registerListenerEvents = (courseId, chooserConfig) => {\n\n // Ensure we only add our listeners once.\n if (initialized) {\n return;\n }\n\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n\n const fetchModuleData = (() => {\n let innerPromises = new Map();\n\n return (sectionNum) => {\n if (innerPromises.has(sectionNum)) {\n return innerPromises.get(sectionNum);\n }\n\n innerPromises.set(\n sectionNum,\n new Promise((resolve) => {\n resolve(Repository.activityModules(courseId, sectionNum));\n })\n );\n return innerPromises.get(sectionNum);\n };\n })();\n\n const fetchFooterData = (() => {\n let footerInnerPromise = null;\n\n return (sectionNum) => {\n if (!footerInnerPromise) {\n footerInnerPromise = new Promise((resolve) => {\n resolve(Repository.fetchFooterData(courseId, sectionNum));\n });\n }\n\n return footerInnerPromise;\n };\n })();\n\n CustomEvents.define(document, events);\n\n // Display module chooser event listeners.\n events.forEach((event) => {\n document.addEventListener(event, async(e) => {\n if (e.target.closest(selectors.elements.sectionmodchooser)) {\n let caller;\n let sectionnum = null;\n let sectionid = null;\n // We need to know who called this.\n // Standard courses use the ID in the main section info.\n const sectionDiv = e.target.closest(selectors.elements.section);\n // Front page courses need some special handling.\n const button = e.target.closest(selectors.elements.sectionmodchooser);\n\n // If we don't have a section number use the fallback ID.\n // We always want the sectionDiv caller first as it keeps track of section number's after DnD changes.\n // The button attribute is always just a fallback for us as the section div is not always available.\n // A YUI change could be done maybe to only update the button attribute but we are going for minimal change here.\n if (sectionDiv !== null && sectionDiv.hasAttribute('data-number')) {\n // We check for attributes just in case of outdated contrib course formats.\n caller = sectionDiv;\n sectionnum = sectionDiv.getAttribute('data-number');\n sectionid = sectionDiv.getAttribute('data-id');\n } else {\n caller = button;\n if (caller.hasAttribute('data-sectionid')) {\n window.console.warn(\n 'The data-sectionid attribute has been deprecated. ' +\n 'Please update your code to use data-section-id passing the real section ID instead.'\n );\n caller.setAttribute('data-sectionnum', caller.dataset.sectionid);\n }\n sectionnum = caller.dataset.sectionnum;\n sectionid = caller.getAttribute('data-section-id');\n }\n\n // We want to show the modal instantly but loading whilst waiting for our data.\n let bodyPromiseResolver;\n const bodyPromise = new Promise(resolve => {\n bodyPromiseResolver = resolve;\n });\n\n const footerData = await fetchFooterData(sectionnum);\n const sectionModal = buildModal(bodyPromise, footerData);\n\n // Now we have a modal we should start fetching data.\n // If an error occurs while fetching the data, display the error within the modal.\n const data = await fetchModuleData(sectionnum).catch(async(e) => {\n const errorTemplateData = {\n 'errormessage': e.message\n };\n bodyPromiseResolver(await Templates.render('core_course/local/activitychooser/error', errorTemplateData));\n });\n\n // Early return if there is no module data.\n if (!data) {\n return;\n }\n\n // Apply the section num to all the module instance links.\n const builtModuleData = sectionMapper(\n data,\n sectionnum,\n caller.dataset.sectionreturnnum,\n caller.dataset.beforemod,\n sectionid\n );\n\n ChooserDialogue.displayChooser(\n sectionModal,\n builtModuleData,\n partiallyAppliedFavouriteManager(data, sectionnum, sectionid),\n footerData,\n );\n\n bodyPromiseResolver(await Templates.render(\n 'core_course/activitychooser',\n templateDataBuilder(builtModuleData, chooserConfig)\n ));\n }\n });\n });\n\n initialized = true;\n};\n\n/**\n * Given the web service data and an ID we want to make a deep copy\n * of the WS data then add on the section num to the addoption URL\n *\n * @method sectionMapper\n * @param {Object} webServiceData Our original data from the Web service call\n * @param {Number} num The number of the section we need to append to the links\n * @param {Number|null} sectionreturnnum The number of the section return we need to append to the links\n * @param {Number|null} beforemod The ID of the cm we need to append to the links\n * @param {Number|null} id The number of the section we need to append to the links\n * @return {Array} [modules] with URL's built\n */\nconst sectionMapper = (webServiceData, num, sectionreturnnum, beforemod, id = null) => {\n // We need to take a fresh deep copy of the original data as an object is a reference type.\n const newData = JSON.parse(JSON.stringify(webServiceData));\n let urlParams = '&beforemod=' + (beforemod ?? 0);\n if (id) {\n urlParams += `§ionid=${id}`;\n }\n urlParams += `§ion=${num}`;\n if (sectionreturnnum) {\n urlParams += `&sr=${sectionreturnnum}`;\n }\n newData.content_items.forEach((module) => {\n module.link += urlParams;\n });\n return newData.content_items;\n};\n\n/**\n * Given an array of modules we want to figure out where & how to place them into our template object\n *\n * @method templateDataBuilder\n * @param {Array} data our modules to manipulate into a Templatable object\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n * @return {Object} Our built object ready to render out\n */\nconst templateDataBuilder = (data, chooserConfig) => {\n // Setup of various bits and pieces we need to mutate before throwing it to the wolves.\n let activities = [];\n let resources = [];\n let showAll = true;\n let showActivities = false;\n let showResources = false;\n\n // Tab mode can be the following [All, Resources & Activities, All & Activities & Resources].\n const tabMode = parseInt(chooserConfig.tabmode);\n\n // Filter the incoming data to find favourite & recommended modules.\n const favourites = data.filter(mod => mod.favourite === true);\n const recommended = data.filter(mod => mod.recommended === true);\n\n // Whether the activities and resources tabs should be displayed or not.\n const showActivitiesAndResources = (tabMode) => {\n const acceptableModes = [\n ALLACTIVITIESRESOURCES,\n ALLACTIVITIESRESOURCESREC,\n ACTIVITIESRESOURCES,\n ACTIVITIESRESOURCESREC,\n ];\n\n return acceptableModes.indexOf(tabMode) !== -1;\n };\n\n // These modes need Activity & Resource tabs.\n if (showActivitiesAndResources(tabMode)) {\n // Filter the incoming data to find activities then resources.\n activities = data.filter(mod => mod.archetype === ACTIVITY);\n resources = data.filter(mod => mod.archetype === RESOURCE);\n showActivities = true;\n showResources = true;\n\n // We want all of the previous information but no 'All' tab.\n if (tabMode === ACTIVITIESRESOURCES || tabMode === ACTIVITIESRESOURCESREC) {\n showAll = false;\n }\n }\n\n const recommendedBeforeTabs = [\n ALLACTIVITIESRESOURCESREC,\n ONLYALLREC,\n ACTIVITIESRESOURCESREC,\n ];\n // Whether the recommended tab should be displayed before the All/Activities/Resources tabs.\n const recommendedBeginning = recommendedBeforeTabs.indexOf(tabMode) !== -1;\n\n // Given the results of the above filters lets figure out what tab to set active.\n // We have some favourites.\n const favouritesFirst = !!favourites.length;\n const recommendedFirst = favouritesFirst === false && recommendedBeginning === true && !!recommended.length;\n // We are in tabMode 2 without any favourites.\n const activitiesFirst = showAll === false && favouritesFirst === false && recommendedFirst === false;\n // We have nothing fallback to show all modules.\n const fallback = showAll === true && favouritesFirst === false && recommendedFirst === false;\n\n return {\n 'default': data,\n showAll: showAll,\n activities: activities,\n showActivities: showActivities,\n activitiesFirst: activitiesFirst,\n resources: resources,\n showResources: showResources,\n favourites: favourites,\n recommended: recommended,\n recommendedFirst: recommendedFirst,\n recommendedBeginning: recommendedBeginning,\n favouritesFirst: favouritesFirst,\n fallback: fallback,\n };\n};\n\n/**\n * Given an object we want to build a modal ready to show\n *\n * @method buildModal\n * @param {Promise} body\n * @param {String|Boolean} footer Either a footer to add or nothing\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = (body, footer) => Modal.create({\n body,\n title: getString('addresourceoractivity'),\n footer: footer.customfootertemplate,\n large: true,\n scrollable: false,\n templateContext: {\n classes: 'modchooser'\n },\n show: true,\n});\n\n/**\n * A small helper function to handle the case where there are no more favourites\n * and we need to mess a bit with the available tabs in the chooser\n *\n * @method nullFavouriteDomManager\n * @param {HTMLElement} favouriteTabNav Dom node of the favourite tab nav\n * @param {HTMLElement} modalBody Our current modals' body\n */\nconst nullFavouriteDomManager = (favouriteTabNav, modalBody) => {\n favouriteTabNav.tabIndex = -1;\n favouriteTabNav.classList.add('d-none');\n // Need to set active to an available tab.\n if (favouriteTabNav.classList.contains('active')) {\n favouriteTabNav.classList.remove('active');\n favouriteTabNav.setAttribute('aria-selected', 'false');\n const favouriteTab = modalBody.querySelector(selectors.regions.favouriteTab);\n favouriteTab.classList.remove('active');\n const defaultTabNav = modalBody.querySelector(selectors.regions.defaultTabNav);\n const activitiesTabNav = modalBody.querySelector(selectors.regions.activityTabNav);\n if (defaultTabNav.classList.contains('d-none') === false) {\n defaultTabNav.classList.add('active');\n defaultTabNav.setAttribute('aria-selected', 'true');\n defaultTabNav.tabIndex = 0;\n defaultTabNav.focus();\n const defaultTab = modalBody.querySelector(selectors.regions.defaultTab);\n defaultTab.classList.add('active');\n } else {\n activitiesTabNav.classList.add('active');\n activitiesTabNav.setAttribute('aria-selected', 'true');\n activitiesTabNav.tabIndex = 0;\n activitiesTabNav.focus();\n const activitiesTab = modalBody.querySelector(selectors.regions.activityTab);\n activitiesTab.classList.add('active');\n }\n\n }\n};\n\n/**\n * Export a curried function where the builtModules has been applied.\n * We have our array of modules so we can rerender the favourites area and have all of the items sorted.\n *\n * @method partiallyAppliedFavouriteManager\n * @param {Array} moduleData This is our raw WS data that we need to manipulate\n * @param {Number} sectionnum We need this to add the sectionnum to the URL's in the faves area after rerender\n * @param {Number|null} sectionid We need this to add the sectionid to the URL's in the faves area\n * Section ID is preferred over section number, as section numbers can change.\n * @return {Function} partially applied function so we can manipulate DOM nodes easily & update our internal array\n */\nconst partiallyAppliedFavouriteManager = (moduleData, sectionnum, sectionid = null) => {\n /**\n * Curried function that is being returned.\n *\n * @param {String} internal Internal name of the module to manage\n * @param {Boolean} favourite Is the caller adding a favourite or removing one?\n * @param {HTMLElement} modalBody What we need to update whilst we are here\n */\n return async(internal, favourite, modalBody) => {\n const favouriteArea = modalBody.querySelector(selectors.render.favourites);\n\n // eslint-disable-next-line max-len\n const favouriteButtons = modalBody.querySelectorAll(`[data-internal=\"${internal}\"] ${selectors.actions.optionActions.manageFavourite}`);\n const favouriteTabNav = modalBody.querySelector(selectors.regions.favouriteTabNav);\n const result = moduleData.content_items.find(({name}) => name === internal);\n const newFaves = {};\n if (result) {\n if (favourite) {\n result.favourite = true;\n\n // eslint-disable-next-line camelcase\n newFaves.content_items = moduleData.content_items.filter(mod => mod.favourite === true);\n\n const builtFaves = sectionMapper(newFaves, sectionnum, null, null, sectionid);\n\n const {html, js} = await Templates.renderForPromise('core_course/local/activitychooser/favourites',\n {favourites: builtFaves});\n\n await Templates.replaceNodeContents(favouriteArea, html, js);\n\n Array.from(favouriteButtons).forEach((element) => {\n element.classList.remove('text-muted');\n element.classList.add('text-primary');\n element.dataset.favourited = 'true';\n element.setAttribute('aria-pressed', true);\n element.firstElementChild.classList.remove('fa-regular');\n element.firstElementChild.classList.add('fa');\n });\n\n favouriteTabNav.classList.remove('d-none');\n } else {\n result.favourite = false;\n\n const nodeToRemove = favouriteArea.querySelector(`[data-internal=\"${internal}\"]`);\n\n nodeToRemove.parentNode.removeChild(nodeToRemove);\n\n Array.from(favouriteButtons).forEach((element) => {\n element.classList.add('text-muted');\n element.classList.remove('text-primary');\n element.dataset.favourited = 'false';\n element.setAttribute('aria-pressed', false);\n element.firstElementChild.classList.remove('fa');\n element.firstElementChild.classList.add('fa-regular');\n });\n const newFaves = moduleData.content_items.filter(mod => mod.favourite === true);\n\n if (newFaves.length === 0) {\n nullFavouriteDomManager(favouriteTabNav, modalBody);\n }\n }\n }\n };\n};\n"],"names":["initialized","courseId","chooserConfig","pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","fetchModuleData","innerPromises","Map","sectionNum","has","set","Promise","Repository","activityModules","get","fetchFooterData","footerInnerPromise","define","document","forEach","event","addEventListener","async","e","target","closest","selectors","elements","sectionmodchooser","caller","sectionnum","sectionid","sectionDiv","section","button","bodyPromiseResolver","hasAttribute","getAttribute","window","console","warn","setAttribute","dataset","bodyPromise","footerData","sectionModal","buildModal","data","catch","errorTemplateData","message","Templates","render","builtModuleData","sectionMapper","sectionreturnnum","beforemod","ChooserDialogue","displayChooser","partiallyAppliedFavouriteManager","templateDataBuilder","webServiceData","num","id","newData","JSON","parse","stringify","urlParams","content_items","module","link","activities","resources","showAll","showActivities","showResources","tabMode","parseInt","tabmode","favourites","filter","mod","favourite","recommended","indexOf","showActivitiesAndResources","archetype","recommendedBeginning","favouritesFirst","length","recommendedFirst","activitiesFirst","fallback","body","footer","Modal","create","title","customfootertemplate","large","scrollable","templateContext","classes","show","nullFavouriteDomManager","favouriteTabNav","modalBody","tabIndex","classList","add","contains","remove","querySelector","regions","favouriteTab","defaultTabNav","activitiesTabNav","activityTabNav","focus","defaultTab","activityTab","moduleData","internal","favouriteArea","favouriteButtons","querySelectorAll","actions","optionActions","manageFavourite","result","find","_ref","name","newFaves","builtFaves","html","js","renderForPromise","replaceNodeContents","Array","from","element","favourited","firstElementChild","nodeToRemove","parentNode","removeChild"],"mappings":";;;;;;;ubA8CIA,aAAc,gBASE,CAACC,SAAUC,uBACrBC,eAAiB,IAAIC,iBAE3BC,uBAAuBJ,SAAUC,eAEjCC,eAAeG,iBAUbD,uBAAyB,CAACJ,SAAUC,oBAGlCF,yBAIEO,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,kBAGlBC,gBAAkB,UAChBC,cAAgB,IAAIC,WAEhBC,aACAF,cAAcG,IAAID,aAItBF,cAAcI,IACVF,WACA,IAAIG,SAASX,UACTA,QAAQY,WAAWC,gBAAgBlB,SAAUa,iBAN1CF,cAAcQ,IAAIN,cALb,GAkBlBO,gBAAkB,UAChBC,mBAAqB,YAEjBR,aACCQ,qBACDA,mBAAqB,IAAIL,SAASX,UAC9BA,QAAQY,WAAWG,gBAAgBpB,SAAUa,iBAI9CQ,qBAVS,sCAcXC,OAAOC,SAAUjB,QAG9BA,OAAOkB,SAASC,QACZF,SAASG,iBAAiBD,OAAOE,MAAAA,OACzBC,EAAEC,OAAOC,QAAQC,mBAAUC,SAASC,mBAAoB,KACpDC,OACAC,WAAa,KACbC,UAAY,WAGVC,WAAaT,EAAEC,OAAOC,QAAQC,mBAAUC,SAASM,SAEjDC,OAASX,EAAEC,OAAOC,QAAQC,mBAAUC,SAASC,uBAyB/CO,oBAnBe,OAAfH,YAAuBA,WAAWI,aAAa,gBAE/CP,OAASG,WACTF,WAAaE,WAAWK,aAAa,eACrCN,UAAYC,WAAWK,aAAa,aAEpCR,OAASK,OACLL,OAAOO,aAAa,oBACpBE,OAAOC,QAAQC,KACX,yIAGJX,OAAOY,aAAa,kBAAmBZ,OAAOa,QAAQX,YAE1DD,WAAaD,OAAOa,QAAQZ,WAC5BC,UAAYF,OAAOQ,aAAa,0BAK9BM,YAAc,IAAIhC,SAAQX,UAC5BmC,oBAAsBnC,WAGpB4C,iBAAmB7B,gBAAgBe,YACnCe,aAAeC,WAAWH,YAAaC,YAIvCG,WAAa1C,gBAAgByB,YAAYkB,OAAM1B,MAAAA,UAC3C2B,kBAAoB,cACN1B,EAAE2B,SAEtBf,0BAA0BgB,UAAUC,OAAO,0CAA2CH,2BAIrFF,kBAKCM,gBAAkBC,cACpBP,KACAjB,WACAD,OAAOa,QAAQa,iBACf1B,OAAOa,QAAQc,UACfzB,WAGJ0B,gBAAgBC,eACZb,aACAQ,gBACAM,iCAAiCZ,KAAMjB,WAAYC,WACnDa,YAGJT,0BAA0BgB,UAAUC,OAChC,8BACAQ,oBAAoBP,gBAAiBzD,wBAMrDF,aAAc,GAeZ4D,cAAgB,SAACO,eAAgBC,IAAKP,iBAAkBC,eAAWO,0DAAK,WAEpEC,QAAUC,KAAKC,MAAMD,KAAKE,UAAUN,qBACtCO,UAAY,eAAiBZ,MAAAA,UAAAA,UAAa,UAC1CO,KACAK,gCAA2BL,KAE/BK,8BAAyBN,KACrBP,mBACAa,yBAAoBb,mBAExBS,QAAQK,cAAclD,SAASmD,SAC3BA,OAAOC,MAAQH,aAEZJ,QAAQK,eAWbT,oBAAsB,CAACb,KAAMnD,qBAE3B4E,WAAa,GACbC,UAAY,GACZC,SAAU,EACVC,gBAAiB,EACjBC,eAAgB,QAGdC,QAAUC,SAASlF,cAAcmF,SAGjCC,WAAajC,KAAKkC,QAAOC,MAAyB,IAAlBA,IAAIC,YACpCC,YAAcrC,KAAKkC,QAAOC,MAA2B,IAApBA,IAAIE,cAGPP,CAAAA,UAQa,IAPrB,CA7ND,EAEG,EADN,EAGG,GAgOAQ,QAAQR,SAI/BS,CAA2BT,WAE3BL,WAAazB,KAAKkC,QAAOC,KAlOhB,IAkOuBA,IAAIK,YACpCd,UAAY1B,KAAKkC,QAAOC,KAlOf,IAkOsBA,IAAIK,YACnCZ,gBAAiB,EACjBC,eAAgB,EA5OI,IA+OhBC,SA5OmB,IA4OgBA,UACnCH,SAAU,UAUZc,sBAAmE,IAN3C,CAnPA,EACf,EACY,GAuPwBH,QAAQR,SAIrDY,kBAAoBT,WAAWU,OAC/BC,kBAAuC,IAApBF,kBAAsD,IAAzBD,wBAAmCJ,YAAYM,aAM9F,SACQ3C,KACX2B,QAASA,QACTF,WAAYA,WACZG,eAAgBA,eAChBiB,iBATgC,IAAZlB,UAAyC,IAApBe,kBAAkD,IAArBE,iBAUtElB,UAAWA,UACXG,cAAeA,cACfI,WAAYA,WACZI,YAAaA,YACbO,iBAAkBA,iBAClBH,qBAAsBA,qBACtBC,gBAAiBA,gBACjBI,UAfyB,IAAZnB,UAAwC,IAApBe,kBAAkD,IAArBE,mBA2BhE7C,WAAa,CAACgD,KAAMC,SAAWC,eAAMC,OAAO,CAC9CH,KAAAA,KACAI,OAAO,kBAAU,yBACjBH,OAAQA,OAAOI,qBACfC,OAAO,EACPC,YAAY,EACZC,gBAAiB,CACbC,QAAS,cAEbC,MAAM,IAWJC,wBAA0B,CAACC,gBAAiBC,gBAC9CD,gBAAgBE,UAAY,EAC5BF,gBAAgBG,UAAUC,IAAI,UAE1BJ,gBAAgBG,UAAUE,SAAS,UAAW,CAC9CL,gBAAgBG,UAAUG,OAAO,UACjCN,gBAAgBjE,aAAa,gBAAiB,SACzBkE,UAAUM,cAAcvF,mBAAUwF,QAAQC,cAClDN,UAAUG,OAAO,gBACxBI,cAAgBT,UAAUM,cAAcvF,mBAAUwF,QAAQE,eAC1DC,iBAAmBV,UAAUM,cAAcvF,mBAAUwF,QAAQI,oBAChB,IAA/CF,cAAcP,UAAUE,SAAS,UAAqB,CACtDK,cAAcP,UAAUC,IAAI,UAC5BM,cAAc3E,aAAa,gBAAiB,QAC5C2E,cAAcR,SAAW,EACzBQ,cAAcG,QACKZ,UAAUM,cAAcvF,mBAAUwF,QAAQM,YAClDX,UAAUC,IAAI,cACtB,CACHO,iBAAiBR,UAAUC,IAAI,UAC/BO,iBAAiB5E,aAAa,gBAAiB,QAC/C4E,iBAAiBT,SAAW,EAC5BS,iBAAiBE,QACKZ,UAAUM,cAAcvF,mBAAUwF,QAAQO,aAClDZ,UAAUC,IAAI,aAiBlCnD,iCAAmC,SAAC+D,WAAY5F,gBAAYC,iEAAY,YAQnET,MAAMqG,SAAUxC,UAAWwB,mBACxBiB,cAAgBjB,UAAUM,cAAcvF,mBAAU0B,OAAO4B,YAGzD6C,iBAAmBlB,UAAUmB,2CAAoCH,uBAAcjG,mBAAUqG,QAAQC,cAAcC,kBAC/GvB,gBAAkBC,UAAUM,cAAcvF,mBAAUwF,QAAQR,iBAC5DwB,OAASR,WAAWrD,cAAc8D,MAAKC,WAACC,KAACA,kBAAUA,OAASV,YAC5DW,SAAW,MACbJ,UACI/C,UAAW,CACX+C,OAAO/C,WAAY,EAGnBmD,SAASjE,cAAgBqD,WAAWrD,cAAcY,QAAOC,MAAyB,IAAlBA,IAAIC,kBAE9DoD,WAAajF,cAAcgF,SAAUxG,WAAY,KAAM,KAAMC,YAE7DyG,KAACA,KAADC,GAAOA,UAAYtF,UAAUuF,iBAAiB,+CAChD,CAAC1D,WAAYuD,mBAEXpF,UAAUwF,oBAAoBf,cAAeY,KAAMC,IAEzDG,MAAMC,KAAKhB,kBAAkB1G,SAAS2H,UAClCA,QAAQjC,UAAUG,OAAO,cACzB8B,QAAQjC,UAAUC,IAAI,gBACtBgC,QAAQpG,QAAQqG,WAAa,OAC7BD,QAAQrG,aAAa,gBAAgB,GACrCqG,QAAQE,kBAAkBnC,UAAUG,OAAO,cAC3C8B,QAAQE,kBAAkBnC,UAAUC,IAAI,SAG5CJ,gBAAgBG,UAAUG,OAAO,cAC9B,CACHkB,OAAO/C,WAAY,QAEb8D,aAAerB,cAAcX,wCAAiCU,gBAEpEsB,aAAaC,WAAWC,YAAYF,cAEpCL,MAAMC,KAAKhB,kBAAkB1G,SAAS2H,UAClCA,QAAQjC,UAAUC,IAAI,cACtBgC,QAAQjC,UAAUG,OAAO,gBACzB8B,QAAQpG,QAAQqG,WAAa,QAC7BD,QAAQrG,aAAa,gBAAgB,GACrCqG,QAAQE,kBAAkBnC,UAAUG,OAAO,MAC3C8B,QAAQE,kBAAkBnC,UAAUC,IAAI,iBAIpB,IAFPY,WAAWrD,cAAcY,QAAOC,MAAyB,IAAlBA,IAAIC,YAE/CO,QACTe,wBAAwBC,gBAAiBC"}recommendations.min.js.map000064400000004426152266416600011645 0ustar00{"version":3,"file":"recommendations.min.js","sources":["../src/recommendations.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 * A javascript module to handle toggling activity chooser recommendations.\n *\n * @module core_course/recommendations\n * @copyright 2020 Adrian Greeve \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\n\n/**\n * Do an ajax call to toggle the recommendation\n *\n * @param {object} e The event\n * @return {void}\n */\nconst toggleRecommendation = (e) => {\n let data = {\n methodname: 'core_course_toggle_activity_recommendation',\n args: {\n area: e.currentTarget.dataset.area,\n id: e.currentTarget.dataset.id\n }\n };\n Ajax.call([data])[0].fail(Notification.exception);\n};\n\n/**\n * Initialisation function\n *\n * @return {void}\n */\nexport const init = () => {\n const checkboxelements = document.querySelectorAll(\"[data-area]\");\n checkboxelements.forEach((checkbox) => {\n checkbox.addEventListener('change', toggleRecommendation);\n });\n};\n"],"names":["toggleRecommendation","e","data","methodname","args","area","currentTarget","dataset","id","call","fail","Notification","exception","document","querySelectorAll","forEach","checkbox","addEventListener"],"mappings":";;;;;;;gLAgCMA,qBAAwBC,QACtBC,KAAO,CACPC,WAAY,6CACZC,KAAM,CACFC,KAAMJ,EAAEK,cAAcC,QAAQF,KAC9BG,GAAIP,EAAEK,cAAcC,QAAQC,mBAG/BC,KAAK,CAACP,OAAO,GAAGQ,KAAKC,sBAAaC,0BAQvB,KACSC,SAASC,iBAAiB,eAClCC,SAASC,WACtBA,SAASC,iBAAiB,SAAUjB"}actions.min.js.map000064400000022402152266416600010110 0ustar00{"version":3,"file":"actions.min.js","sources":["../src/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\n/**\n * Various actions on badges - enabling, disabling, etc.\n *\n * @module core_badges/actions\n * @copyright 2024 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from 'core_badges/selectors';\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport Ajax from 'core/ajax';\nimport Pending from 'core/pending';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {add as addToast} from 'core/toast';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n prefetchStrings('core_badges', [\n 'reviewconfirm',\n 'activatesuccess',\n 'deactivatesuccess',\n 'awardoncron',\n 'numawardstat',\n ]);\n prefetchStrings('core', [\n 'confirm',\n 'enable',\n ]);\n\n registerEventListeners();\n};\n\n/**\n * Register events for delete preset option in action menu.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const enableOption = event.target.closest(selectors.actions.enablebadge);\n\n if (enableOption) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const triggerElement = reportElement ? enableOption.closest('.dropdown').querySelector('.dropdown-toggle') : null;\n const badgeId = enableOption.dataset.badgeid;\n const badgeName = enableOption.dataset.badgename;\n\n Notification.saveCancelPromise(\n getString('confirm', 'core'),\n getString('reviewconfirm', 'core_badges', badgeName),\n getString('enable', 'core'),\n {triggerElement}\n ).then(() => {\n return enableBadge(badgeId, badgeName, reportElement);\n }).catch(() => {\n return;\n });\n }\n\n const disableOption = event.target.closest(selectors.actions.disablebadge);\n if (disableOption) {\n event.preventDefault();\n const badgeId = disableOption.dataset.badgeid;\n const badgeName = disableOption.dataset.badgename;\n const reportElement = event.target.closest(reportSelectors.regions.report);\n disableBadge(badgeId, badgeName, reportElement);\n }\n });\n};\n\n/**\n * Enable the badge.\n *\n * @param {Number} badgeId The id of the badge to enable.\n * @param {String} badgeName The name of the badge to enable.\n * @param {HTMLElement} reportElement the report element.\n */\nasync function enableBadge(badgeId, badgeName, reportElement) {\n var request = {\n methodname: 'core_badges_enable_badges',\n args: {\n badgeids: [badgeId],\n }\n };\n\n const pendingPromise = new Pending('core_badges/enable');\n try {\n const result = await Ajax.call([request])[0];\n if (reportElement) {\n showEnableResultToast(badgeName, result);\n // Report element is present, reload the table.\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n } else {\n // Report element is not present, add the parameters to the current page to display the message.\n const awards = result.result?.pop().awards;\n document.location = document.location.pathname + `?id=${badgeId}&awards=${awards}`;\n }\n } catch (error) {\n Notification.exception(error);\n }\n pendingPromise.resolve();\n}\n\n/**\n * Show the result of enabling a badge.\n *\n * @param {String} badgeName The name of the badge to enable.\n * @param {Object} result The result of enabling a badge.\n */\nfunction showEnableResultToast(badgeName, result) {\n if (result.result?.length > 0) {\n addToast(getString('activatesuccess', 'core_badges', badgeName), {type: 'success'});\n const awards = result.result?.pop().awards;\n if (awards == 'cron') {\n addToast(getString('awardoncron', 'core_badges', {badgename: badgeName}));\n } else if (awards > 0) {\n addToast(getString('numawardstat', 'core_badges', {badgename: badgeName, awards: awards}));\n }\n } else if (result.warnings.length > 0) {\n addToast(result.warnings[0].message, {type: 'danger'});\n }\n}\n\n/**\n * Disable the badge.\n *\n * @param {Number} badgeId The id of the badge to disable.\n * @param {String} badgeName The name of the badge to enable.\n * @param {HTMLElement} reportElement the report element.\n */\nasync function disableBadge(badgeId, badgeName, reportElement) {\n var request = {\n methodname: 'core_badges_disable_badges',\n args: {\n badgeids: [badgeId],\n }\n };\n\n try {\n const result = await Ajax.call([request])[0];\n if (reportElement) {\n // Report element is present, show the message in a toast and reload the table.\n showDisableResultToast(badgeName, result);\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n } else {\n // Report element is not present, the page should be reloaded.\n document.location = document.location.pathname + `?id=${badgeId}`;\n }\n } catch (error) {\n Notification.exception(error);\n }\n}\n\n/**\n * Show the result of disabling a badge.\n *\n * @param {String} badgeName The name of the badge to disable.\n * @param {Object} result The result of disabling a badge.\n */\nfunction showDisableResultToast(badgeName, result) {\n if (result.result) {\n addToast(\n getString('deactivatesuccess', 'core_badges', badgeName),\n {type: 'success'}\n );\n } else if (result.warnings.length > 0) {\n addToast(\n result.warnings[0].message,\n {type: 'danger'}\n );\n }\n}\n"],"names":["registerEventListeners","document","addEventListener","event","enableOption","target","closest","selectors","actions","enablebadge","preventDefault","reportElement","reportSelectors","regions","report","triggerElement","querySelector","badgeId","dataset","badgeid","badgeName","badgename","saveCancelPromise","then","request","methodname","args","badgeids","pendingPromise","Pending","result","Ajax","call","length","type","awards","_result$result3","pop","warnings","message","showEnableResultToast","reportEvents","tableReload","preservePagination","_result$result","location","pathname","error","exception","resolve","enableBadge","catch","disableOption","disablebadge","showDisableResultToast","disableBadge"],"mappings":";;;;;;;4XAqCoB,mCACA,cAAe,CAC3B,gBACA,kBACA,oBACA,cACA,+CAEY,OAAQ,CACpB,UACA,WAGJA,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,aAAeD,MAAME,OAAOC,QAAQC,mBAAUC,QAAQC,gBAExDL,aAAc,CACdD,MAAMO,uBAGAC,cAAgBR,MAAME,OAAOC,QAAQM,gBAAgBC,QAAQC,QAC7DC,eAAiBJ,cAAgBP,aAAaE,QAAQ,aAAaU,cAAc,oBAAsB,KACvGC,QAAUb,aAAac,QAAQC,QAC/BC,UAAYhB,aAAac,QAAQG,gCAE1BC,mBACT,kBAAU,UAAW,SACrB,kBAAU,gBAAiB,cAAeF,YAC1C,kBAAU,SAAU,QACpB,CAACL,eAAAA,iBACHQ,MAAK,mBAyBQN,QAASG,UAAWT,mBACvCa,QAAU,CACVC,WAAY,4BACZC,KAAM,CACFC,SAAU,CAACV,iBAIbW,eAAiB,IAAIC,iBAAQ,gCAEzBC,aAAeC,cAAKC,KAAK,CAACR,UAAU,MACtCb,wBAqBmBS,UAAWU,wDAClCA,OAAOA,yDAAQG,QAAS,EAAG,qCAClB,kBAAU,kBAAmB,cAAeb,WAAY,CAACc,KAAM,kBAClEC,+BAASL,OAAOA,yCAAPM,gBAAeC,MAAMF,OACtB,QAAVA,uBACS,kBAAU,cAAe,cAAe,CAACd,UAAWD,aACtDe,OAAS,mBACP,kBAAU,eAAgB,cAAe,CAACd,UAAWD,UAAWe,OAAQA,eAE9EL,OAAOQ,SAASL,OAAS,kBACvBH,OAAOQ,SAAS,GAAGC,QAAS,CAACL,KAAM,WA9BxCM,CAAsBpB,UAAWU,4CAEnBW,aAAaC,YAAa,CAACC,oBAAoB,GAAOhC,mBACjE,0BAEGwB,8BAASL,OAAOA,wCAAPc,eAAeP,MAAMF,OACpClC,SAAS4C,SAAW5C,SAAS4C,SAASC,uBAAkB7B,2BAAkBkB,SAEhF,MAAOY,6BACQC,UAAUD,OAE3BnB,eAAeqB,UA/CIC,CAAYjC,QAASG,UAAWT,iBACxCwC,OAAM,eAKPC,cAAgBjD,MAAME,OAAOC,QAAQC,mBAAUC,QAAQ6C,iBACzDD,cAAe,CACfjD,MAAMO,iCAqEUO,QAASG,UAAWT,mBACxCa,QAAU,CACVC,WAAY,6BACZC,KAAM,CACFC,SAAU,CAACV,qBAKTa,aAAeC,cAAKC,KAAK,CAACR,UAAU,GACtCb,yBAmBoBS,UAAWU,QACnCA,OAAOA,uBAEH,kBAAU,oBAAqB,cAAeV,WAC9C,CAACc,KAAM,YAEJJ,OAAOQ,SAASL,OAAS,kBAE5BH,OAAOQ,SAAS,GAAGC,QACnB,CAACL,KAAM,WA1BPoB,CAAuBlC,UAAWU,4CACpBW,aAAaC,YAAa,CAACC,oBAAoB,GAAOhC,gBAGpEV,SAAS4C,SAAW5C,SAAS4C,SAASC,uBAAkB7B,SAE9D,MAAO8B,6BACQC,UAAUD,QApFnBQ,CAHgBH,cAAclC,QAAQC,QACpBiC,cAAclC,QAAQG,UAClBlB,MAAME,OAAOC,QAAQM,gBAAgBC,QAAQC"}local/overview/overviewpage.min.js.map000064400000011643152266416600014120 0ustar00{"version":3,"file":"overviewpage.min.js","sources":["../../../src/local/overview/overviewpage.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 * Controls the fragment overview loadings.\n *\n * @module core_course/local/overview/overviewpage\n * @copyright 2025 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport CourseContent from 'core_courseformat/local/content';\nimport {eventTypes as collapsableSectionEventTypes} from 'core/local/collapsable_section/events';\nimport Fragment from 'core/fragment';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport Pending from 'core/pending';\nimport Templates from 'core/templates';\n\n/**\n * Initialize the overview page.\n *\n * @param {String} selector The selector where the overview page is located.\n */\nexport const init = async(selector) => {\n const pageElement = document.querySelector(selector);\n if (!pageElement) {\n throw new Error('No elements found with the selector: ' + selector);\n }\n\n pageElement.addEventListener(\n collapsableSectionEventTypes.shown,\n event => {\n const fragmentElement = getFragmentContainer(event.target);\n if (!fragmentElement) {\n return;\n }\n loadFragmentContent(fragmentElement);\n }\n );\n\n // The overview page is considered an alternative course view page so it must\n // include the course content component to capture any possible action. For example,\n // capturing manual completion toggles.\n return new CourseContent({\n element: pageElement,\n reactive: getCurrentCourseEditor(),\n });\n};\n\n/**\n * Load the fragment content.\n *\n * @private\n * @param {HTMLElement} element The element where the fragment content will be loaded.\n */\nconst loadFragmentContent = (element) => {\n if (element.dataset.loaded) {\n return;\n }\n\n const pendingReload = new Pending(`course_overviewtable_${element.dataset.modname}`);\n\n const promise = Fragment.loadFragment(\n 'core_course',\n 'course_overview',\n element.dataset.contextid,\n {\n courseid: element.dataset.courseid,\n modname: element.dataset.modname,\n }\n );\n\n promise.then(async(html, js) => {\n Templates.runTemplateJS(js);\n element.innerHTML = html;\n // Templates.replaceNode(element, html, js);\n element.dataset.loaded = true;\n pendingReload.resolve();\n return true;\n }).catch(() => {\n pendingReload.resolve();\n });\n};\n\n/**\n * Get the fragment container.\n *\n * @private\n * @param {HTMLElement} element The element where the fragment container is located.\n * @return {HTMLElement|null} The fragment container.\n */\nconst getFragmentContainer = (element) => {\n const result = element.querySelector('[data-region=\"loading-icon-container\"]');\n if (!result) {\n return null;\n }\n if (!result.dataset.contextid || !result.dataset.courseid || !result.dataset.modname) {\n throw new Error('The element is missing required data attributes.');\n }\n return result;\n};\n"],"names":["async","pageElement","document","querySelector","selector","Error","addEventListener","collapsableSectionEventTypes","shown","event","fragmentElement","getFragmentContainer","target","loadFragmentContent","CourseContent","element","reactive","dataset","loaded","pendingReload","Pending","modname","Fragment","loadFragment","contextid","courseid","then","html","js","runTemplateJS","innerHTML","resolve","catch","result"],"mappings":";;;;;;;8QAmCoBA,MAAAA,iBACVC,YAAcC,SAASC,cAAcC,cACtCH,kBACK,IAAII,MAAM,wCAA0CD,iBAG9DH,YAAYK,iBACRC,mBAA6BC,OAC7BC,cACUC,gBAAkBC,qBAAqBF,MAAMG,QAC9CF,iBAGLG,oBAAoBH,oBAOrB,IAAII,iBAAc,CACrBC,QAASd,YACTe,UAAU,mDAUZH,oBAAuBE,aACrBA,QAAQE,QAAQC,oBAIdC,cAAgB,IAAIC,gDAAgCL,QAAQE,QAAQI,UAE1DC,kBAASC,aACrB,cACA,kBACAR,QAAQE,QAAQO,UAChB,CACIC,SAAUV,QAAQE,QAAQQ,SAC1BJ,QAASN,QAAQE,QAAQI,UAIzBK,MAAK1B,MAAM2B,KAAMC,yBACXC,cAAcD,IACxBb,QAAQe,UAAYH,KAEpBZ,QAAQE,QAAQC,QAAS,EACzBC,cAAcY,WACP,KACRC,OAAM,KACLb,cAAcY,cAWhBpB,qBAAwBI,gBACpBkB,OAASlB,QAAQZ,cAAc,8CAChC8B,cACM,SAENA,OAAOhB,QAAQO,YAAcS,OAAOhB,QAAQQ,WAAaQ,OAAOhB,QAAQI,cACnE,IAAIhB,MAAM,2DAEb4B"}local/overview/overviewpage.min.js000064400000004171152266416600013342 0ustar00define("core_course/local/overview/overviewpage",["exports","core_courseformat/local/content","core/local/collapsable_section/events","core/fragment","core_courseformat/courseeditor","core/pending","core/templates"],(function(_exports,_content,_events,_fragment,_courseeditor,_pending,_templates){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Controls the fragment overview loadings. * * @module core_course/local/overview/overviewpage * @copyright 2025 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_content=_interopRequireDefault(_content),_fragment=_interopRequireDefault(_fragment),_pending=_interopRequireDefault(_pending),_templates=_interopRequireDefault(_templates);_exports.init=async selector=>{const pageElement=document.querySelector(selector);if(!pageElement)throw new Error("No elements found with the selector: "+selector);return pageElement.addEventListener(_events.eventTypes.shown,(event=>{const fragmentElement=getFragmentContainer(event.target);fragmentElement&&loadFragmentContent(fragmentElement)})),new _content.default({element:pageElement,reactive:(0,_courseeditor.getCurrentCourseEditor)()})};const loadFragmentContent=element=>{if(element.dataset.loaded)return;const pendingReload=new _pending.default("course_overviewtable_".concat(element.dataset.modname));_fragment.default.loadFragment("core_course","course_overview",element.dataset.contextid,{courseid:element.dataset.courseid,modname:element.dataset.modname}).then((async(html,js)=>(_templates.default.runTemplateJS(js),element.innerHTML=html,element.dataset.loaded=!0,pendingReload.resolve(),!0))).catch((()=>{pendingReload.resolve()}))},getFragmentContainer=element=>{const result=element.querySelector('[data-region="loading-icon-container"]');if(!result)return null;if(!result.dataset.contextid||!result.dataset.courseid||!result.dataset.modname)throw new Error("The element is missing required data attributes.");return result}})); //# sourceMappingURL=overviewpage.min.js.maplocal/activitychooser/repository.min.js000064400000002672152266416600014433 0ustar00define("core_course/local/activitychooser/repository",["exports","core/ajax"],(function(_exports,_ajax){var obj; /** * A javascript module to handle user AJAX actions. * * @module core_course/local/activitychooser/repository * @copyright 2019 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.unfavouriteModule=_exports.fetchFooterData=_exports.favouriteModule=_exports.activityModules=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.activityModules=(courseid,sectionnum)=>{const request={methodname:"core_course_get_course_content_items",args:{courseid:courseid,sectionnum:sectionnum}};return _ajax.default.call([request])[0]};_exports.favouriteModule=(modName,modID)=>{const request={methodname:"core_course_add_content_item_to_user_favourites",args:{componentname:modName,contentitemid:modID}};return _ajax.default.call([request])[0]};_exports.unfavouriteModule=(modName,modID)=>{const request={methodname:"core_course_remove_content_item_from_user_favourites",args:{componentname:modName,contentitemid:modID}};return _ajax.default.call([request])[0]};_exports.fetchFooterData=(courseid,sectionid)=>{const request={methodname:"core_course_get_activity_chooser_footer",args:{courseid:courseid,sectionid:sectionid}};return _ajax.default.call([request])[0]}})); //# sourceMappingURL=repository.min.js.maplocal/activitychooser/dialogue.min.js000064400000033175152266416600014007 0ustar00define("core_course/local/activitychooser/dialogue",["exports","jquery","core/custom_interaction_events","theme_boost/bootstrap/carousel","core/modal_events","core_course/local/activitychooser/selectors","core/templates","core/key_codes","core/loadingicon","core_course/local/activitychooser/repository","core/notification","core/utils","core/normalise"],(function(_exports,_jquery,_custom_interaction_events,_carousel,ModalEvents,_selectors,Templates,_key_codes,_loadingicon,Repository,_notification,_utils,_normalise){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.displayChooser=void 0,_jquery=_interopRequireDefault(_jquery),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_carousel=_interopRequireDefault(_carousel),ModalEvents=_interopRequireWildcard(ModalEvents),_selectors=_interopRequireDefault(_selectors),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),_notification=_interopRequireDefault(_notification);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const showModuleHelp=function(carousel,moduleData){let modal=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;null!==modal&&!0===moduleData.showFooter&&modal.setFooter(Templates.render("core_course/local/activitychooser/footer_partial",moduleData));const help=carousel.querySelector(_selectors.default.regions.help);help.innerHTML="",help.classList.add("m-auto");const spinnerPromise=(0,_loadingicon.addIconToContainer)(help);let transitionPromiseResolver=null;const transitionPromise=new Promise((resolve=>{transitionPromiseResolver=resolve})),contentPromise=Templates.renderForPromise("core_course/local/activitychooser/help",moduleData);Promise.all([contentPromise,spinnerPromise,transitionPromise]).then((_ref=>{let[{html:html,js:js}]=_ref;return Templates.replaceNodeContents(help,html,js)})).then((()=>(help.querySelector(_selectors.default.regions.chooserSummary.header).focus(),help))).catch(_notification.default.exception),carousel.addEventListener("slid.bs.carousel",(()=>{transitionPromiseResolver()}),{once:!0}),_carousel.default.getInstance(carousel).next()},registerListenerEvents=(modal,mappedModules,partialFavourite,footerData)=>{const modalBody=(0,_normalise.getFirst)(modal.getBody()),bodyClickListener=async e=>{if(e.target.closest(_selectors.default.actions.optionActions.showSummary)){const carousel=modalBody.querySelector(_selectors.default.regions.carousel),moduleName=e.target.closest(_selectors.default.regions.chooserOption.container).dataset.modname,moduleData=mappedModules.get(moduleName);moduleData.showFooter=modal.hasFooterContent(),showModuleHelp(carousel,moduleData,modal)}if(e.target.closest(_selectors.default.actions.optionActions.manageFavourite)){const caller=e.target.closest(_selectors.default.actions.optionActions.manageFavourite);await(async(modalBody,caller,partialFavourite)=>{const isFavourite=caller.dataset.favourited,id=caller.dataset.id,name=caller.dataset.name,internal=caller.dataset.internal;"true"===isFavourite?(await Repository.unfavouriteModule(name,id),partialFavourite(internal,!1,modalBody)):(await Repository.favouriteModule(name,id),partialFavourite(internal,!0,modalBody))})(modalBody,caller,partialFavourite);const activeSectionId=modalBody.querySelector(_selectors.default.elements.activetab).getAttribute("href"),sectionChooserOptions=modalBody.querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)),firstChooserOption=sectionChooserOptions.querySelector(_selectors.default.regions.chooserOption.container);toggleFocusableChooserOption(firstChooserOption,!0),initChooserOptionsKeyboardNavigation(modalBody,mappedModules,sectionChooserOptions,modal)}if(e.target.matches(_selectors.default.actions.closeOption)){const carousel=modalBody.querySelector(_selectors.default.regions.carousel);_carousel.default.getInstance(carousel).prev(),carousel.addEventListener("slid.bs.carousel",(()=>{modalBody.querySelector(_selectors.default.regions.modules).querySelector(_selectors.default.regions.getModuleSelector(e.target.dataset.modname)).focus()}))}if(e.target.closest(_selectors.default.actions.clearSearch)){const searchInput=modalBody.querySelector(_selectors.default.actions.search);searchInput.value="",searchInput.focus(),toggleSearchResultsView(modal,mappedModules,searchInput.value)}},footerClickListener=async e=>{if(!0===footerData.footer){const footerjs=await(pluginName=footerData.customfooterjs,"function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([pluginName],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(pluginName)):Promise.resolve(_systemImportTransformerGlobalIdentifier[pluginName]));await footerjs.footerClickListener(e,footerData,modal)}var pluginName};modal.getBodyPromise().then((body=>body[0])).then((body=>{const carousel=document.querySelector(_selectors.default.regions.carousel);return new _carousel.default(carousel,{interval:!1,pause:!0,keyboard:!1}),body})).then((body=>(body.addEventListener("click",bodyClickListener),body))).then((body=>{const searchInput=body.querySelector(_selectors.default.actions.search);return searchInput.addEventListener("input",(0,_utils.debounce)((()=>{toggleSearchResultsView(modal,mappedModules,searchInput.value)}),300)),body})).then((body=>{const activeSectionId=body.querySelector(_selectors.default.elements.activetab).getAttribute("href"),sectionChooserOptions=body.querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)),firstChooserOption=sectionChooserOptions.querySelector(_selectors.default.regions.chooserOption.container);return toggleFocusableChooserOption(firstChooserOption,!0),initChooserOptionsKeyboardNavigation(body,mappedModules,sectionChooserOptions,modal),body})).catch(_notification.default.exception),modal.getFooterPromise().then((footer=>footer[0])).then((footer=>(footer.addEventListener("click",footerClickListener),footer))).catch(_notification.default.exception)},initChooserOptionsKeyboardNavigation=function(body,mappedModules,chooserOptionsContainer){let modal=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;const chooserOptions=chooserOptionsContainer.querySelectorAll(_selectors.default.regions.chooserOption.container);Array.from(chooserOptions).forEach((element=>{const $element=(0,_jquery.default)(element);_custom_interaction_events.default.define($element,[_custom_interaction_events.default.events.next,_custom_interaction_events.default.events.previous,_custom_interaction_events.default.events.home,_custom_interaction_events.default.events.end]);const createNavHandler=resolver=>(e,data)=>{const currentOption=data.originalEvent.target.closest(_selectors.default.regions.chooserOption.container);if(null!==currentOption){const toFocusOption=resolver(currentOption);toFocusOption&&focusChooserOption(toFocusOption,currentOption)}};$element.on(_custom_interaction_events.default.events.next,createNavHandler((current=>current.nextElementSibling||chooserOptionsContainer.firstElementChild))),$element.on(_custom_interaction_events.default.events.previous,createNavHandler((current=>current.previousElementSibling||chooserOptionsContainer.lastElementChild))),$element.on(_custom_interaction_events.default.events.home,createNavHandler((()=>chooserOptionsContainer.firstElementChild))),$element.on(_custom_interaction_events.default.events.end,createNavHandler((()=>chooserOptionsContainer.lastElementChild))),element.addEventListener("keydown",(e=>{if((e.keyCode===_key_codes.enter||e.keyCode===_key_codes.space)&&e.target.matches(_selectors.default.actions.optionActions.showSummary)){e.preventDefault();const moduleName=e.target.closest(_selectors.default.regions.chooserOption.container).dataset.modname,moduleData=mappedModules.get(moduleName),carousel=document.querySelector(_selectors.default.regions.carousel);new _carousel.default({interval:!1,pause:!0,keyboard:!1}),moduleData.showFooter=modal.hasFooterContent(),showModuleHelp(carousel,moduleData,modal)}}))}))},focusChooserOption=function(currentChooserOption){let previousChooserOption=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;null!==previousChooserOption&&toggleFocusableChooserOption(previousChooserOption,!1),toggleFocusableChooserOption(currentChooserOption,!0),currentChooserOption.focus()},toggleFocusableChooserOption=(chooserOption,isFocusable)=>{const chooserOptionLink=chooserOption.querySelector(_selectors.default.actions.addChooser),chooserOptionHelp=chooserOption.querySelector(_selectors.default.actions.optionActions.showSummary),chooserOptionFavourite=chooserOption.querySelector(_selectors.default.actions.optionActions.manageFavourite);isFocusable?(chooserOption.tabIndex=0,chooserOptionLink.tabIndex=0,chooserOptionHelp.tabIndex=0,chooserOptionFavourite.tabIndex=0):(chooserOption.tabIndex=-1,chooserOptionLink.tabIndex=-1,chooserOptionHelp.tabIndex=-1,chooserOptionFavourite.tabIndex=-1)},toggleSearchResultsView=async(modal,mappedModules,searchQuery)=>{const modalBody=modal.getBody()[0],searchResultsContainer=modalBody.querySelector(_selectors.default.regions.searchResults),chooserContainer=modalBody.querySelector(_selectors.default.regions.chooser),clearSearchButton=modalBody.querySelector(_selectors.default.actions.clearSearch);if(searchQuery.length>0){const searchResultsData=searchModules(mappedModules,searchQuery);await(async(searchResultsContainer,searchResultsData)=>{const templateData={searchresultsnumber:searchResultsData.length,searchresults:searchResultsData},{html:html,js:js}=await Templates.renderForPromise("core_course/local/activitychooser/search_results",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)})(searchResultsContainer,searchResultsData);const searchResultItemsContainer=searchResultsContainer.querySelector(_selectors.default.regions.searchResultItems),firstSearchResultItem=searchResultItemsContainer.querySelector(_selectors.default.regions.chooserOption.container);firstSearchResultItem&&(toggleFocusableChooserOption(firstSearchResultItem,!0),initChooserOptionsKeyboardNavigation(modalBody,mappedModules,searchResultItemsContainer,modal)),clearSearchButton.classList.remove("d-none"),chooserContainer.setAttribute("hidden","hidden"),searchResultsContainer.removeAttribute("hidden")}else clearSearchButton.classList.add("d-none"),searchResultsContainer.setAttribute("hidden","hidden"),chooserContainer.removeAttribute("hidden")},searchModules=(modules,searchTerm)=>{if(""===searchTerm)return modules;searchTerm=searchTerm.toLowerCase();const searchResults=[];return modules.forEach((activity=>{const activityName=activity.title.toLowerCase(),activityDesc=activity.help.toLowerCase();(activityName.includes(searchTerm)||activityDesc.includes(searchTerm))&&searchResults.push(activity)})),searchResults},disableFocusAllChooserOptions=sectionChooserOptions=>{sectionChooserOptions.querySelectorAll(_selectors.default.regions.chooserOption.container).forEach((chooserOption=>{toggleFocusableChooserOption(chooserOption,!1)}))};_exports.displayChooser=(modalPromise,sectionModules,partialFavourite,footerData)=>{const mappedModules=new Map;sectionModules.forEach((module=>{mappedModules.set(module.componentname+"_"+module.link,module)})),modalPromise.then((modal=>(registerListenerEvents(modal,mappedModules,partialFavourite,footerData),((modal,mappedModules)=>{modal.getModal()[0].tabIndex=-1,modal.getBodyPromise().then((body=>{document.querySelectorAll(_selectors.default.elements.tab).forEach((tab=>{tab.addEventListener("shown.bs.tab",(e=>{const activeSectionId=e.target.getAttribute("href"),activeSectionChooserOptions=body[0].querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)),firstChooserOption=activeSectionChooserOptions.querySelector(_selectors.default.regions.chooserOption.container),prevActiveSectionId=e.relatedTarget.getAttribute("href"),prevActiveSectionChooserOptions=body[0].querySelector(_selectors.default.regions.getSectionChooserOptions(prevActiveSectionId));disableFocusAllChooserOptions(prevActiveSectionChooserOptions),toggleFocusableChooserOption(firstChooserOption,!0),initChooserOptionsKeyboardNavigation(body[0],mappedModules,activeSectionChooserOptions,modal)}))}))})).catch(_notification.default.exception)})(modal,mappedModules),modal.getRoot().on(ModalEvents.hidden,(()=>{modal.destroy()})),modal))).catch(_notification.default.exception)}})); //# sourceMappingURL=dialogue.min.js.maplocal/activitychooser/repository.min.js.map000064400000010212152266416600015174 0ustar00{"version":3,"file":"repository.min.js","sources":["../../../src/local/activitychooser/repository.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 * A javascript module to handle user AJAX actions.\n *\n * @module core_course/local/activitychooser/repository\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport ajax from 'core/ajax';\n\n/**\n * Fetch all the information on modules we'll need in the activity chooser.\n *\n * @method activityModules\n * @param {Number} courseid What course to fetch the modules for\n * @param {Number} sectionnum What course section to fetch the modules for\n * @return {object} jQuery promise\n */\nexport const activityModules = (courseid, sectionnum) => {\n const request = {\n methodname: 'core_course_get_course_content_items',\n args: {\n courseid: courseid,\n sectionnum: sectionnum,\n },\n };\n return ajax.call([request])[0];\n};\n\n/**\n * Given a module name, module ID & the current course we want to specify that the module\n * is a users' favourite.\n *\n * @method favouriteModule\n * @param {String} modName Frankenstyle name of the component to add favourite\n * @param {int} modID ID of the module. Mainly for LTI cases where they have same / similar names\n * @return {object} jQuery promise\n */\nexport const favouriteModule = (modName, modID) => {\n const request = {\n methodname: 'core_course_add_content_item_to_user_favourites',\n args: {\n componentname: modName,\n contentitemid: modID,\n },\n };\n return ajax.call([request])[0];\n};\n\n/**\n * Given a module name, module ID & the current course we want to specify that the module\n * is no longer a users' favourite.\n *\n * @method unfavouriteModule\n * @param {String} modName Frankenstyle name of the component to add favourite\n * @param {int} modID ID of the module. Mainly for LTI cases where they have same / similar names\n * @return {object} jQuery promise\n */\nexport const unfavouriteModule = (modName, modID) => {\n const request = {\n methodname: 'core_course_remove_content_item_from_user_favourites',\n args: {\n componentname: modName,\n contentitemid: modID,\n },\n };\n return ajax.call([request])[0];\n};\n\n/**\n * Fetch all the information on modules we'll need in the activity chooser.\n *\n * @method fetchFooterData\n * @param {Number} courseid What course to fetch the data for\n * @param {Number} sectionid What section to fetch the data for\n * @return {object} jQuery promise\n */\nexport const fetchFooterData = (courseid, sectionid) => {\n const request = {\n methodname: 'core_course_get_activity_chooser_footer',\n args: {\n courseid: courseid,\n sectionid: sectionid,\n },\n };\n return ajax.call([request])[0];\n};\n"],"names":["courseid","sectionnum","request","methodname","args","ajax","call","modName","modID","componentname","contentitemid","sectionid"],"mappings":";;;;;;;uPAgC+B,CAACA,SAAUC,oBAChCC,QAAU,CACZC,WAAY,uCACZC,KAAM,CACFJ,SAAUA,SACVC,WAAYA,oBAGbI,cAAKC,KAAK,CAACJ,UAAU,6BAYD,CAACK,QAASC,eAC/BN,QAAU,CACZC,WAAY,kDACZC,KAAM,CACFK,cAAeF,QACfG,cAAeF,eAGhBH,cAAKC,KAAK,CAACJ,UAAU,+BAYC,CAACK,QAASC,eACjCN,QAAU,CACZC,WAAY,uDACZC,KAAM,CACFK,cAAeF,QACfG,cAAeF,eAGhBH,cAAKC,KAAK,CAACJ,UAAU,6BAWD,CAACF,SAAUW,mBAChCT,QAAU,CACZC,WAAY,0CACZC,KAAM,CACFJ,SAAUA,SACVW,UAAWA,mBAGZN,cAAKC,KAAK,CAACJ,UAAU"}local/activitychooser/selectors.min.js.map000064400000013454152266416600014773 0ustar00{"version":3,"file":"selectors.min.js","sources":["../../../src/local/activitychooser/selectors.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 * Define all of the selectors we will be using on the grading interface.\n *\n * @module core_course/local/activitychooser/selectors\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 defaultTabNav: getDataSelector('region', 'default-tab-nav'),\n activityTabNav: getDataSelector('region', 'activity-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-bs-toggle=\"tab\"]',\n activetab: 'a[data-bs-toggle=\"tab\"][aria-selected=\"true\"]',\n visibletabs: 'a[data-bs-toggle=\"tab\"]:not(.d-none)'\n },\n};\n"],"names":["getDataSelector","name","value","regions","chooser","getSectionChooserOptions","containerid","chooserOption","container","actions","info","chooserSummary","content","header","carousel","help","modules","favouriteTabNav","defaultTabNav","activityTabNav","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":";;;;;;;;MA8BMA,gBAAkB,CAACC,KAAMC,wBACXD,kBAASC,yBAGd,CACXC,QAAS,CACLC,QAASJ,gBAAgB,SAAU,qBACnCK,yBAA0BC,uBAAkBA,wBAAeN,gBAAgB,SAAU,8BACrFO,cAAe,CACXC,UAAWR,gBAAgB,SAAU,4BACrCS,QAAST,gBAAgB,SAAU,oCACnCU,KAAMV,gBAAgB,SAAU,kCAEpCW,eAAgB,CACZH,UAAWR,gBAAgB,SAAU,oCACrCY,QAASZ,gBAAgB,SAAU,4CACnCa,OAAQb,gBAAgB,SAAU,kBAClCS,QAAST,gBAAgB,SAAU,6CAEvCc,SAAUd,gBAAgB,SAAU,YACpCe,KAAMf,gBAAgB,SAAU,QAChCgB,QAAShB,gBAAgB,SAAU,WACnCiB,gBAAiBjB,gBAAgB,SAAU,qBAC3CkB,cAAelB,gBAAgB,SAAU,mBACzCmB,eAAgBnB,gBAAgB,SAAU,oBAC1CoB,aAAcpB,gBAAgB,SAAU,cACxCqB,eAAgBrB,gBAAgB,SAAU,eAC1CsB,WAAYtB,gBAAgB,SAAU,WACtCuB,YAAavB,gBAAgB,SAAU,YACvCwB,YAAaxB,gBAAgB,SAAU,aACvCyB,kBAAmBC,mDAA8CA,cACjEC,cAAe3B,gBAAgB,SAAU,4BACzC4B,kBAAmB5B,gBAAgB,SAAU,kCAEjDS,QAAS,CACLoB,cAAe,CACXC,YAAa9B,gBAAgB,SAAU,uBACvC+B,gBAAiB/B,gBAAgB,SAAU,4BAE/CgC,WAAYhC,gBAAgB,SAAU,sBACtCiC,YAAajC,gBAAgB,SAAU,gCACvCkC,KAAMlC,gBAAgB,SAAU,QAChCmC,OAAQnC,gBAAgB,SAAU,UAClCoC,YAAapC,gBAAgB,SAAU,gBAE3CqC,OAAQ,CACJC,WAAYtC,gBAAgB,SAAU,oBAE1CuC,SAAU,CACNC,QAAS,WACTC,kBAAmB,iCACnBC,SAAU,wBACVC,UAAW,gBACXC,IAAK,0BACLC,UAAW,gDACXC,YAAa"}local/activitychooser/selectors.min.js000064400000005472152266416600014220 0ustar00define("core_course/local/activitychooser/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0; /** * Define all of the selectors we will be using on the grading interface. * * @module core_course/local/activitychooser/selectors * @copyright 2019 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ const getDataSelector=(name,value)=>"[data-".concat(name,'="').concat(value,'"]');var _default={regions:{chooser:getDataSelector("region","chooser-container"),getSectionChooserOptions:containerid=>"".concat(containerid," ").concat(getDataSelector("region","chooser-options-container")),chooserOption:{container:getDataSelector("region","chooser-option-container"),actions:getDataSelector("region","chooser-option-actions-container"),info:getDataSelector("region","chooser-option-info-container")},chooserSummary:{container:getDataSelector("region","chooser-option-summary-container"),content:getDataSelector("region","chooser-option-summary-content-container"),header:getDataSelector("region","summary-header"),actions:getDataSelector("region","chooser-option-summary-actions-container")},carousel:getDataSelector("region","carousel"),help:getDataSelector("region","help"),modules:getDataSelector("region","modules"),favouriteTabNav:getDataSelector("region","favourite-tab-nav"),defaultTabNav:getDataSelector("region","default-tab-nav"),activityTabNav:getDataSelector("region","activity-tab-nav"),favouriteTab:getDataSelector("region","favourites"),recommendedTab:getDataSelector("region","recommended"),defaultTab:getDataSelector("region","default"),activityTab:getDataSelector("region","activity"),resourceTab:getDataSelector("region","resources"),getModuleSelector:modname=>'[role="menuitem"][data-modname="'.concat(modname,'"]'),searchResults:getDataSelector("region","search-results-container"),searchResultItems:getDataSelector("region","search-result-items-container")},actions:{optionActions:{showSummary:getDataSelector("action","show-option-summary"),manageFavourite:getDataSelector("action","manage-module-favourite")},addChooser:getDataSelector("action","add-chooser-option"),closeOption:getDataSelector("action","close-chooser-option-summary"),hide:getDataSelector("action","hide"),search:getDataSelector("action","search"),clearSearch:getDataSelector("action","clearsearch")},render:{favourites:getDataSelector("render","favourites-area")},elements:{section:".section",sectionmodchooser:"button.section-modchooser-link",sitemenu:".block_site_main_menu",sitetopic:"div.sitetopic",tab:'a[data-bs-toggle="tab"]',activetab:'a[data-bs-toggle="tab"][aria-selected="true"]',visibletabs:'a[data-bs-toggle="tab"]:not(.d-none)'}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=selectors.min.js.maplocal/activitychooser/dialogue.min.js.map000064400000077306152266416600014567 0ustar00{"version":3,"file":"dialogue.min.js","sources":["../../../src/local/activitychooser/dialogue.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 * A type of dialogue used as for choosing options.\n *\n * @module core_course/local/activitychooser/dialogue\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 CustomEvents from 'core/custom_interaction_events';\nimport Carousel from 'theme_boost/bootstrap/carousel';\nimport * as ModalEvents from 'core/modal_events';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport * as Templates from 'core/templates';\nimport {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';\nimport {getFirst} from 'core/normalise';\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 {Element} 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.querySelector(selectors.regions.help);\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.addEventListener('slid.bs.carousel', () => {\n transitionPromiseResolver();\n }, {once: true});\n // Trigger the transition between 'pages'.\n Carousel.getInstance(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 modalBody = getFirst(modal.getBody());\n const bodyClickListener = async(e) => {\n if (e.target.closest(selectors.actions.optionActions.showSummary)) {\n const carousel = modalBody.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(modalBody, caller, partialFavourite);\n const activeSectionId = modalBody.querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = modalBody\n .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions\n .querySelector(selectors.regions.chooserOption.container);\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(modalBody, 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 = modalBody.querySelector(selectors.regions.carousel);\n\n // Trigger the transition between 'pages'.\n Carousel.getInstance(carousel).prev();\n carousel.addEventListener('slid.bs.carousel', () => {\n const allModules = modalBody.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 = modalBody.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 const carousel = document.querySelector(selectors.regions.carousel);\n new Carousel(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 initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions, modal);\n\n return body;\n })\n .catch(Notification.exception);\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(Notification.exception);\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 const $element = $(element);\n\n // Set up custom interaction events for RTL-aware keyboard navigation.\n CustomEvents.define($element, [\n CustomEvents.events.next,\n CustomEvents.events.previous,\n CustomEvents.events.home,\n CustomEvents.events.end,\n ]);\n\n // Handle focus move (automatically handles RTL).\n const createNavHandler = (resolver) => (e, data) => {\n const currentOption = data.originalEvent.target.closest(\n selectors.regions.chooserOption.container\n );\n if (currentOption !== null) {\n const toFocusOption = resolver(currentOption);\n if (toFocusOption) {\n focusChooserOption(toFocusOption, currentOption);\n }\n }\n };\n\n $element.on(\n CustomEvents.events.next,\n createNavHandler(\n (current) => current.nextElementSibling || chooserOptionsContainer.firstElementChild\n )\n );\n\n $element.on(\n CustomEvents.events.previous,\n createNavHandler(\n (current) => current.previousElementSibling || chooserOptionsContainer.lastElementChild\n )\n );\n\n $element.on(\n CustomEvents.events.home,\n createNavHandler(() => chooserOptionsContainer.firstElementChild)\n );\n\n $element.on(\n CustomEvents.events.end,\n createNavHandler(() => chooserOptionsContainer.lastElementChild)\n );\n\n 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 = document.querySelector(selectors.regions.carousel);\n new 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 });\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 * 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 document.querySelectorAll(selectors.elements.tab).forEach((tab) => {\n tab.addEventListener('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 });\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(Notification.exception);\n};\n"],"names":["showModuleHelp","carousel","moduleData","modal","showFooter","setFooter","Templates","render","help","querySelector","selectors","regions","innerHTML","classList","add","spinnerPromise","transitionPromiseResolver","transitionPromise","Promise","resolve","contentPromise","renderForPromise","all","then","_ref","html","js","replaceNodeContents","chooserSummary","header","focus","catch","Notification","exception","addEventListener","once","getInstance","next","registerListenerEvents","mappedModules","partialFavourite","footerData","modalBody","getBody","bodyClickListener","async","e","target","closest","actions","optionActions","showSummary","moduleName","chooserOption","container","dataset","modname","get","hasFooterContent","manageFavourite","caller","isFavourite","favourited","id","name","internal","Repository","unfavouriteModule","favouriteModule","manageFavouriteState","activeSectionId","elements","activetab","getAttribute","sectionChooserOptions","getSectionChooserOptions","firstChooserOption","toggleFocusableChooserOption","initChooserOptionsKeyboardNavigation","matches","closeOption","prev","modules","getModuleSelector","clearSearch","searchInput","search","value","toggleSearchResultsView","footerClickListener","footer","footerjs","pluginName","customfooterjs","getBodyPromise","body","document","Carousel","interval","pause","keyboard","getFooterPromise","chooserOptionsContainer","chooserOptions","querySelectorAll","Array","from","forEach","element","$element","define","CustomEvents","events","previous","home","end","createNavHandler","resolver","data","currentOption","originalEvent","toFocusOption","focusChooserOption","on","current","nextElementSibling","firstElementChild","previousElementSibling","lastElementChild","keyCode","enter","space","preventDefault","currentChooserOption","previousChooserOption","isFocusable","chooserOptionLink","addChooser","chooserOptionHelp","chooserOptionFavourite","tabIndex","searchQuery","searchResultsContainer","searchResults","chooserContainer","chooser","clearSearchButton","length","searchResultsData","searchModules","templateData","renderSearchResults","searchResultItemsContainer","searchResultItems","firstSearchResultItem","remove","setAttribute","removeAttribute","searchTerm","toLowerCase","activity","activityName","title","activityDesc","includes","push","disableFocusAllChooserOptions","modalPromise","sectionModules","Map","module","set","componentname","link","getModal","tab","activeSectionChooserOptions","prevActiveSectionId","relatedTarget","prevActiveSectionChooserOptions","setupKeyboardAccessibility","getRoot","ModalEvents","hidden","destroy"],"mappings":"ipEA6CMA,eAAiB,SAACC,SAAUC,gBAAYC,6DAAQ,KAEpC,OAAVA,QAA4C,IAA1BD,WAAWE,YAC7BD,MAAME,UAAUC,UAAUC,OAAO,mDAAoDL,mBAEnFM,KAAOP,SAASQ,cAAcC,mBAAUC,QAAQH,MACtDA,KAAKI,UAAY,GACjBJ,KAAKK,UAAUC,IAAI,gBAGbC,gBAAiB,mCAAmBP,UAGtCQ,0BAA4B,WAC1BC,kBAAoB,IAAIC,SAAQC,UAClCH,0BAA4BG,WAI1BC,eAAiBd,UAAUe,iBAAiB,yCAA0CnB,YAG5FgB,QAAQI,IAAI,CAACF,eAAgBL,eAAgBE,oBACxCM,MAAKC,YAAEC,KAACA,KAADC,GAAOA,iBAASpB,UAAUqB,oBAAoBnB,KAAMiB,KAAMC,OACjEH,MAAK,KACFf,KAAKC,cAAcC,mBAAUC,QAAQiB,eAAeC,QAAQC,QACrDtB,QAEVuB,MAAMC,sBAAaC,WAGxBhC,SAASiC,iBAAiB,oBAAoB,KAC1ClB,8BACD,CAACmB,MAAM,sBAEDC,YAAYnC,UAAUoC,QAuC7BC,uBAAyB,CAACnC,MAAOoC,cAAeC,iBAAkBC,oBAC9DC,WAAY,uBAASvC,MAAMwC,WAC3BC,kBAAoBC,MAAAA,OAClBC,EAAEC,OAAOC,QAAQtC,mBAAUuC,QAAQC,cAAcC,aAAc,OACzDlD,SAAWyC,UAAUjC,cAAcC,mBAAUC,QAAQV,UAGrDmD,WADSN,EAAEC,OAAOC,QAAQtC,mBAAUC,QAAQ0C,cAAcC,WACtCC,QAAQC,QAC5BtD,WAAaqC,cAAckB,IAAIL,YAErClD,WAAWE,WAAaD,MAAMuD,mBAC9B1D,eAAeC,SAAUC,WAAYC,UAGrC2C,EAAEC,OAAOC,QAAQtC,mBAAUuC,QAAQC,cAAcS,iBAAkB,OAC7DC,OAASd,EAAEC,OAAOC,QAAQtC,mBAAUuC,QAAQC,cAAcS,sBA1C/Cd,OAAMH,UAAWkB,OAAQpB,0BAC5CqB,YAAcD,OAAOL,QAAQO,WAC7BC,GAAKH,OAAOL,QAAQQ,GACpBC,KAAOJ,OAAOL,QAAQS,KACtBC,SAAWL,OAAOL,QAAQU,SAEZ,SAAhBJ,mBACMK,WAAWC,kBAAkBH,KAAMD,IAEzCvB,iBAAiByB,UAAU,EAAOvB,mBAE5BwB,WAAWE,gBAAgBJ,KAAMD,IAEvCvB,iBAAiByB,UAAU,EAAMvB,aA8BvB2B,CAAqB3B,UAAWkB,OAAQpB,wBACxC8B,gBAAkB5B,UAAUjC,cAAcC,mBAAU6D,SAASC,WAAWC,aAAa,QACrFC,sBAAwBhC,UACzBjC,cAAcC,mBAAUC,QAAQgE,yBAAyBL,kBACxDM,mBAAqBF,sBACtBjE,cAAcC,mBAAUC,QAAQ0C,cAAcC,WACnDuB,6BAA6BD,oBAAoB,GACjDE,qCAAqCpC,UAAWH,cAAemC,sBAAuBvE,UAItF2C,EAAEC,OAAOgC,QAAQrE,mBAAUuC,QAAQ+B,aAAc,OAC3C/E,SAAWyC,UAAUjC,cAAcC,mBAAUC,QAAQV,4BAGlDmC,YAAYnC,UAAUgF,OAC/BhF,SAASiC,iBAAiB,oBAAoB,KACvBQ,UAAUjC,cAAcC,mBAAUC,QAAQuE,SACnCzE,cAAcC,mBAAUC,QAAQwE,kBAAkBrC,EAAEC,OAAOQ,QAAQC,UACtF1B,cAKXgB,EAAEC,OAAOC,QAAQtC,mBAAUuC,QAAQmC,aAAc,OAE3CC,YAAc3C,UAAUjC,cAAcC,mBAAUuC,QAAQqC,QAC9DD,YAAYE,MAAQ,GACpBF,YAAYvD,QACZ0D,wBAAwBrF,MAAOoC,cAAe8C,YAAYE,SAQ5DE,oBAAsB5C,MAAAA,QACE,IAAtBJ,WAAWiD,OAAiB,OACtBC,eA3IAC,WA2I2BnD,WAAWoD,+NA3IjBD,4WAAAA,oBA4IrBD,SAASF,oBAAoB3C,EAAGL,WAAYtC,OA5I5CyF,IAAAA,YAgJdzF,MAAM2F,iBAGLvE,MAAKwE,MAAQA,KAAK,KAGlBxE,MAAKwE,aACI9F,SAAW+F,SAASvF,cAAcC,mBAAUC,QAAQV,qBACtDgG,kBAAShG,SAAU,CACfiG,UAAU,EACVC,OAAO,EACPC,UAAU,IAGXL,QAIVxE,MAAKwE,OACFA,KAAK7D,iBAAiB,QAASU,mBACxBmD,QAIVxE,MAAKwE,aACIV,YAAcU,KAAKtF,cAAcC,mBAAUuC,QAAQqC,eAEzDD,YAAYnD,iBAAiB,SAAS,oBAAS,KAE3CsD,wBAAwBrF,MAAOoC,cAAe8C,YAAYE,SAC3D,MACIQ,QAIVxE,MAAKwE,aAEIzB,gBAAkByB,KAAKtF,cAAcC,mBAAU6D,SAASC,WAAWC,aAAa,QAChFC,sBAAwBqB,KAAKtF,cAAcC,mBAAUC,QAAQgE,yBAAyBL,kBACtFM,mBAAqBF,sBAAsBjE,cAAcC,mBAAUC,QAAQ0C,cAAcC,kBAE/FuB,6BAA6BD,oBAAoB,GACjDE,qCAAqCiB,KAAMxD,cAAemC,sBAAuBvE,OAE1E4F,QAEVhE,MAAMC,sBAAaC,WAEpB9B,MAAMkG,mBAGL9E,MAAKmE,QAAUA,OAAO,KAEtBnE,MAAKmE,SACFA,OAAOxD,iBAAiB,QAASuD,qBAC1BC,UAEV3D,MAAMC,sBAAaC,YAYlB6C,qCAAuC,SAACiB,KAAMxD,cAAe+D,6BAAyBnG,6DAAQ,WAC1FoG,eAAiBD,wBAAwBE,iBAAiB9F,mBAAUC,QAAQ0C,cAAcC,WAEhGmD,MAAMC,KAAKH,gBAAgBI,SAASC,gBAC1BC,UAAW,mBAAED,4CAGNE,OAAOD,SAAU,CAC1BE,mCAAaC,OAAO3E,KACpB0E,mCAAaC,OAAOC,SACpBF,mCAAaC,OAAOE,KACpBH,mCAAaC,OAAOG,YAIlBC,iBAAoBC,UAAa,CAACvE,EAAGwE,cACjCC,cAAgBD,KAAKE,cAAczE,OAAOC,QAC5CtC,mBAAUC,QAAQ0C,cAAcC,cAEd,OAAlBiE,cAAwB,OAClBE,cAAgBJ,SAASE,eAC3BE,eACAC,mBAAmBD,cAAeF,iBAK9CV,SAASc,GACLZ,mCAAaC,OAAO3E,KACpB+E,kBACKQ,SAAYA,QAAQC,oBAAsBvB,wBAAwBwB,qBAI3EjB,SAASc,GACLZ,mCAAaC,OAAOC,SACpBG,kBACKQ,SAAYA,QAAQG,wBAA0BzB,wBAAwB0B,oBAI/EnB,SAASc,GACLZ,mCAAaC,OAAOE,KACpBE,kBAAiB,IAAMd,wBAAwBwB,qBAGnDjB,SAASc,GACLZ,mCAAaC,OAAOG,IACpBC,kBAAiB,IAAMd,wBAAwB0B,oBAGnDpB,QAAQ1E,iBAAiB,WAAYY,QAG7BA,EAAEmF,UAAYC,kBAASpF,EAAEmF,UAAYE,mBACjCrF,EAAEC,OAAOgC,QAAQrE,mBAAUuC,QAAQC,cAAcC,aAAc,CAC/DL,EAAEsF,uBAEIhF,WADSN,EAAEC,OAAOC,QAAQtC,mBAAUC,QAAQ0C,cAAcC,WACtCC,QAAQC,QAC5BtD,WAAaqC,cAAckB,IAAIL,YAC/BnD,SAAW+F,SAASvF,cAAcC,mBAAUC,QAAQV,cACtDgG,kBAAS,CACTC,UAAU,EACVC,OAAO,EACPC,UAAU,IAIdlG,WAAWE,WAAaD,MAAMuD,mBAC9B1D,eAAeC,SAAUC,WAAYC,eAcnDuH,mBAAqB,SAACW,0BAAsBC,6EAAwB,KACxC,OAA1BA,uBACAzD,6BAA6ByD,uBAAuB,GAGxDzD,6BAA6BwD,sBAAsB,GACnDA,qBAAqBvG,SAUnB+C,6BAA+B,CAACxB,cAAekF,qBAC3CC,kBAAoBnF,cAAc5C,cAAcC,mBAAUuC,QAAQwF,YAClEC,kBAAoBrF,cAAc5C,cAAcC,mBAAUuC,QAAQC,cAAcC,aAChFwF,uBAAyBtF,cAAc5C,cAAcC,mBAAUuC,QAAQC,cAAcS,iBAEvF4E,aAEAlF,cAAcuF,SAAW,EACzBJ,kBAAkBI,SAAW,EAC7BF,kBAAkBE,SAAW,EAC7BD,uBAAuBC,SAAW,IAGlCvF,cAAcuF,UAAY,EAC1BJ,kBAAkBI,UAAY,EAC9BF,kBAAkBE,UAAY,EAC9BD,uBAAuBC,UAAY,IA6BrCpD,wBAA0B3C,MAAM1C,MAAOoC,cAAesG,qBAClDnG,UAAYvC,MAAMwC,UAAU,GAC5BmG,uBAAyBpG,UAAUjC,cAAcC,mBAAUC,QAAQoI,eACnEC,iBAAmBtG,UAAUjC,cAAcC,mBAAUC,QAAQsI,SAC7DC,kBAAoBxG,UAAUjC,cAAcC,mBAAUuC,QAAQmC,gBAEhEyD,YAAYM,OAAS,EAAG,OAClBC,kBAAoBC,cAAc9G,cAAesG,kBAzBnChG,OAAMiG,uBAAwBM,2BAChDE,aAAe,qBACMF,kBAAkBD,qBACxBC,oBAGf3H,KAACA,KAADC,GAAOA,UAAYpB,UAAUe,iBAAiB,mDAAoDiI,oBAClGhJ,UAAUqB,oBAAoBmH,uBAAwBrH,KAAMC,KAmBxD6H,CAAoBT,uBAAwBM,yBAC5CI,2BAA6BV,uBAAuBrI,cAAcC,mBAAUC,QAAQ8I,mBACpFC,sBAAwBF,2BAA2B/I,cAAcC,mBAAUC,QAAQ0C,cAAcC,WACnGoG,wBAEA7E,6BAA6B6E,uBAAuB,GAEpD5E,qCAAqCpC,UAAWH,cAAeiH,2BAA4BrJ,QAG/F+I,kBAAkBrI,UAAU8I,OAAO,UAEnCX,iBAAiBY,aAAa,SAAU,UAExCd,uBAAuBe,gBAAgB,eAGvCX,kBAAkBrI,UAAUC,IAAI,UAEhCgI,uBAAuBc,aAAa,SAAU,UAE9CZ,iBAAiBa,gBAAgB,WAYnCR,cAAgB,CAACnE,QAAS4E,iBACT,KAAfA,kBACO5E,QAEX4E,WAAaA,WAAWC,oBAClBhB,cAAgB,UACtB7D,QAAQyB,SAASqD,iBACPC,aAAeD,SAASE,MAAMH,cAC9BI,aAAeH,SAASxJ,KAAKuJ,eAC/BE,aAAaG,SAASN,aAAeK,aAAaC,SAASN,cAC3Df,cAAcsB,KAAKL,aAIpBjB,eA0CLuB,8BAAiC5F,wBACTA,sBAAsB8B,iBAAiB9F,mBAAUC,QAAQ0C,cAAcC,WAC/EqD,SAAStD,gBACvBwB,6BAA6BxB,eAAe,+BAatB,CAACkH,aAAcC,eAAgBhI,iBAAkBC,oBAErEF,cAAgB,IAAIkI,IAC1BD,eAAe7D,SAAS+D,SACpBnI,cAAcoI,IAAID,OAAOE,cAAgB,IAAMF,OAAOG,KAAMH,WAIhEH,aAAahJ,MAAKpB,QACdmC,uBAAuBnC,MAAOoC,cAAeC,iBAAkBC,YAzDpC,EAACtC,MAAOoC,iBACvCpC,MAAM2K,WAAW,GAAGlC,UAAY,EAEhCzI,MAAM2F,iBAAiBvE,MAAKwE,OACxBC,SAASQ,iBAAiB9F,mBAAU6D,SAASwG,KAAKpE,SAASoE,MACvDA,IAAI7I,iBAAiB,gBAAiBY,UAC5BwB,gBAAkBxB,EAAEC,OAAO0B,aAAa,QACxCuG,4BAA8BjF,KAAK,GACpCtF,cAAcC,mBAAUC,QAAQgE,yBAAyBL,kBACxDM,mBAAqBoG,4BACtBvK,cAAcC,mBAAUC,QAAQ0C,cAAcC,WAC7C2H,oBAAsBnI,EAAEoI,cAAczG,aAAa,QACnD0G,gCAAkCpF,KAAK,GACxCtF,cAAcC,mBAAUC,QAAQgE,yBAAyBsG,sBAG9DX,8BAA8Ba,iCAE9BtG,6BAA6BD,oBAAoB,GACjDE,qCAAqCiB,KAAK,GAAIxD,cAAeyI,4BAA6B7K,gBAInG4B,MAAMC,sBAAaC,YAqClBmJ,CAA2BjL,MAAOoC,eAGlCpC,MAAMkL,UAAU1D,GAAG2D,YAAYC,QAAQ,KACnCpL,MAAMqL,aAGHrL,SACR4B,MAAMC,sBAAaC"}manual_completion_toggle.min.js000064400000007365152266416600012756 0ustar00define("core_course/manual_completion_toggle",["exports","core/templates","core/notification","core_course/repository","core_course/events","core/pending"],(function(_exports,_templates,_notification,_repository,CourseEvents,_pending){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Provides the functionality for toggling the manual completion state of a course module through * the manual completion button. * * @module core_course/manual_completion_toggle * @copyright 2021 Jun Pataleta * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending);const SELECTORS_MANUAL_TOGGLE="button[data-action=toggle-manual-completion]",TOGGLE_TYPES_TOGGLE_MARK_DONE="manual:mark-done";let registered=!1;_exports.init=()=>{registered||(document.addEventListener("click",(e=>{const toggleButton=e.target.closest(SELECTORS_MANUAL_TOGGLE);toggleButton&&(e.preventDefault(),toggleManualCompletionState(toggleButton).catch(_notification.default.exception))})),registered=!0)};const toggleManualCompletionState=async toggleButton=>{const pendingPromise=new _pending.default("core_course:toggleManualCompletionState"),originalInnerHtml=toggleButton.innerHTML;toggleButton.setAttribute("disabled","disabled");const toggleType=toggleButton.getAttribute("data-toggletype"),cmid=toggleButton.getAttribute("data-cmid"),activityname=toggleButton.getAttribute("data-activityname"),completed=toggleType===TOGGLE_TYPES_TOGGLE_MARK_DONE;_templates.default.renderForPromise("core/loading",{}).then((loadingHtml=>{_templates.default.replaceNodeContents(toggleButton,loadingHtml,"")})).catch((()=>{}));try{await(0,_repository.toggleManualCompletion)(cmid,completed);const templateContext={cmid:cmid,activityname:activityname,overallcomplete:completed,overallincomplete:!completed,istrackeduser:!0,normalbutton:!toggleButton.classList.contains("btn-sm")},renderObject=await _templates.default.renderForPromise("core_course/completion_manual",templateContext),newToggleButton=(await _templates.default.replaceNode(toggleButton,renderObject.html,renderObject.js)).pop(),withAvailability=toggleButton.getAttribute("data-withavailability"),toggledEvent=new CustomEvent(CourseEvents.manualCompletionToggled,{bubbles:!0,detail:{cmid:cmid,activityname:activityname,completed:completed,withAvailability:withAvailability}});newToggleButton.dispatchEvent(toggledEvent)}catch(exception){toggleButton.removeAttribute("disabled"),toggleButton.innerHTML=originalInnerHtml,_notification.default.exception(exception)}pendingPromise.resolve()}})); //# sourceMappingURL=manual_completion_toggle.min.js.maprepository.min.js.map000064400000007141152266416600010672 0ustar00{"version":3,"file":"repository.min.js","sources":["../src/repository.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 * Module to handle AJAX interactions.\n *\n * @module core_h5p/repository\n * @copyright 2023 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {call as fetchMany} from 'core/ajax';\nimport * as config from 'core/config';\n\n/**\n * Send a xAPI statement to LMS.\n *\n * @param {string} component\n * @param {Object} statements\n * @returns {Promise}\n */\nexport const postStatement = (component, statements) => fetchMany([{\n methodname: 'core_xapi_statement_post',\n args: {\n component,\n requestjson: JSON.stringify(statements),\n }\n}])[0];\n\n/**\n * Send a xAPI state to LMS.\n *\n * @param {string} component\n * @param {string} activityId\n * @param {Object} agent\n * @param {string} stateId\n * @param {string} stateData\n */\nexport const postState = (\n component,\n activityId,\n agent,\n stateId,\n stateData,\n) => {\n // Please note that we must use a Beacon send here.\n // The XHR is not guaranteed because it will be aborted on page transition.\n // https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API\n // Note: Moodle does not currently have a sendBeacon API endpoint.\n const requestUrl = new URL(`${config.wwwroot}/lib/ajax/service.php`);\n requestUrl.searchParams.set('sesskey', config.sesskey);\n\n navigator.sendBeacon(requestUrl, JSON.stringify([{\n index: 0,\n methodname: 'core_xapi_post_state',\n args: {\n component,\n activityId,\n agent: JSON.stringify(agent),\n stateId,\n stateData,\n }\n }]));\n};\n\n/**\n * Delete a xAPI state from LMS.\n *\n * @param {string} component\n * @param {string} activityId\n * @param {Object} agent\n * @param {string} stateId\n * @returns {Promise}\n */\nexport const deleteState = (\n component,\n activityId,\n agent,\n stateId,\n) => fetchMany([{\n methodname: 'core_xapi_delete_state',\n args: {\n component,\n activityId,\n agent: JSON.stringify(agent),\n stateId,\n },\n}])[0];\n"],"names":["component","statements","methodname","args","requestjson","JSON","stringify","activityId","agent","stateId","stateData","requestUrl","URL","config","wwwroot","searchParams","set","sesskey","navigator","sendBeacon","index"],"mappings":";;;;;;;qCAgC6B,CAACA,UAAWC,cAAe,cAAU,CAAC,CAC/DC,WAAY,2BACZC,KAAM,CACFH,UAAAA,UACAI,YAAaC,KAAKC,UAAUL,gBAEhC,sBAWqB,CACrBD,UACAO,WACAC,MACAC,QACAC,mBAMMC,WAAa,IAAIC,cAAOC,OAAOC,kCACrCH,WAAWI,aAAaC,IAAI,UAAWH,OAAOI,SAE9CC,UAAUC,WAAWR,WAAYN,KAAKC,UAAU,CAAC,CAC7Cc,MAAO,EACPlB,WAAY,uBACZC,KAAM,CACFH,UAAAA,UACAO,WAAAA,WACAC,MAAOH,KAAKC,UAAUE,OACtBC,QAAAA,QACAC,UAAAA,qCAce,CACvBV,UACAO,WACAC,MACAC,WACC,cAAU,CAAC,CACZP,WAAY,yBACZC,KAAM,CACFH,UAAAA,UACAO,WAAAA,WACAC,MAAOH,KAAKC,UAAUE,OACtBC,QAAAA,YAEJ"}events.min.js.map000064400000004420152266416600007754 0ustar00{"version":3,"file":"events.min.js","sources":["../src/events.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/ //\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript events for the `core_editor` subsystem.\n *\n * @module core_editor/events\n * @copyright 2021 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\n\n/**\n * Events for the `core_editor` subsystem.\n *\n * @constant\n * @property {String} editorContentRestored See {@link event:editorContentRestored}\n */\nexport const eventTypes = {\n /**\n * An event triggered when an editor restores auto-saved content.\n *\n * @event editorContentRestored\n */\n editorContentRestored: 'core_editor/contentRestored',\n};\n\n/**\n * Trigger an event to indicate that editor content was restored.\n *\n * @method notifyEditorContentRestored\n * @param {HTMLElement|null} editor The element that was modified\n * @returns {CustomEvent}\n * @fires editorContentRestored\n */\nexport const notifyEditorContentRestored = editor => {\n if (!editor) {\n window.console.warn(\n `The HTMLElement representing the editor that was modified should be provided to notifyEditorContentRestored.`\n );\n }\n return dispatchEvent(\n eventTypes.editorContentRestored,\n {},\n editor || document\n );\n};\n"],"names":["eventTypes","editorContentRestored","editor","window","console","warn","document"],"mappings":";;;;;;;;;MA+BaA,WAAa,CAMtBC,sBAAuB,mGAWgBC,SAClCA,QACDC,OAAOC,QAAQC,sHAIZ,mCACHL,WAAWC,sBACX,GACAC,QAAUI"}downloadcontent.min.js.map000064400000014560152266416600011660 0ustar00{"version":3,"file":"downloadcontent.min.js","sources":["../src/downloadcontent.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 * Functions related to downloading course content.\n *\n * @module core_course/downloadcontent\n * @copyright 2020 Michael Hawkins \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Config from 'core/config';\nimport CustomEvents from 'core/custom_interaction_events';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport jQuery from 'jquery';\nimport Pending from 'core/pending';\nimport {enter, space} from 'core/key_codes';\n\n/**\n * Set up listener to trigger the download course content modal.\n *\n * @return {void}\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n // Add event listeners for click and enter/space keys.\n jQuery('[data-downloadcourse]').on('click keydown', (e) => {\n if (e.type === 'click' || e.which === enter || e.which === space) {\n e.preventDefault();\n displayDownloadConfirmation(e.currentTarget);\n }\n });\n\n pendingPromise.resolve();\n};\n\n/**\n * Display the download course content modal.\n *\n * @method displayDownloadConfirmation\n * @param {Object} downloadModalTrigger The DOM element that triggered the download modal.\n * @return {void}\n */\nconst displayDownloadConfirmation = (downloadModalTrigger) => {\n return SaveCancelModal.create({\n title: downloadModalTrigger.dataset.downloadTitle,\n body: `

${downloadModalTrigger.dataset.downloadBody}

`,\n buttons: {\n save: downloadModalTrigger.dataset.downloadButtonText\n },\n templateContext: {\n classes: 'downloadcoursecontentmodal'\n }\n })\n .then((modal) => {\n // Display the modal.\n modal.show();\n\n const saveButton = document.querySelector('.modal .downloadcoursecontentmodal [data-action=\"save\"]');\n const cancelButton = document.querySelector('.modal .downloadcoursecontentmodal [data-action=\"cancel\"]');\n const modalContainer = document.querySelector('.modal[data-region=\"modal-container\"]');\n\n // Create listener to trigger the download when the \"Download\" button is pressed.\n jQuery(saveButton).on(CustomEvents.events.activate, (e) => downloadContent(e, downloadModalTrigger, modal));\n\n // Create listener to destroy the modal when closing modal by cancelling.\n jQuery(cancelButton).on(CustomEvents.events.activate, () => {\n modal.destroy();\n });\n\n // Create listener to destroy the modal when closing modal by clicking outside of it.\n if (modalContainer.querySelector('.downloadcoursecontentmodal')) {\n jQuery(modalContainer).on(CustomEvents.events.activate, () => {\n modal.destroy();\n });\n }\n\n return modal;\n });\n};\n\n/**\n * Trigger downloading of course content.\n *\n * @method downloadContent\n * @param {Event} e The event triggering the download.\n * @param {Object} downloadModalTrigger The DOM element that triggered the download modal.\n * @param {Object} modal The modal object.\n * @return {void}\n */\nconst downloadContent = (e, downloadModalTrigger, modal) => {\n e.preventDefault();\n\n // Create a form to submit the file download request, so we can avoid sending sesskey over GET.\n const downloadForm = document.createElement('form');\n downloadForm.action = downloadModalTrigger.dataset.downloadLink;\n downloadForm.method = 'POST';\n // Open download in a new tab, so current course view is not disrupted.\n downloadForm.target = '_blank';\n const downloadSesskey = document.createElement('input');\n downloadSesskey.name = 'sesskey';\n downloadSesskey.value = Config.sesskey;\n downloadForm.appendChild(downloadSesskey);\n downloadForm.style.display = 'none';\n\n document.body.appendChild(downloadForm);\n downloadForm.submit();\n document.body.removeChild(downloadForm);\n\n // Destroy the modal to prevent duplicates if reopened later.\n modal.destroy();\n};\n"],"names":["pendingPromise","Pending","on","e","type","which","enter","space","preventDefault","displayDownloadConfirmation","currentTarget","resolve","downloadModalTrigger","SaveCancelModal","create","title","dataset","downloadTitle","body","downloadBody","buttons","save","downloadButtonText","templateContext","classes","then","modal","show","saveButton","document","querySelector","cancelButton","modalContainer","CustomEvents","events","activate","downloadContent","destroy","downloadForm","createElement","action","downloadLink","method","target","downloadSesskey","name","value","Config","sesskey","appendChild","style","display","submit","removeChild"],"mappings":";;;;;;;sWAmCoB,WACVA,eAAiB,IAAIC,qCAGpB,yBAAyBC,GAAG,iBAAkBC,IAClC,UAAXA,EAAEC,MAAoBD,EAAEE,QAAUC,kBAASH,EAAEE,QAAUE,mBACvDJ,EAAEK,iBACFC,4BAA4BN,EAAEO,mBAItCV,eAAeW,iBAUbF,4BAA+BG,sBAC1BC,2BAAgBC,OAAO,CAC1BC,MAAOH,qBAAqBI,QAAQC,cACpCC,kBAAYN,qBAAqBI,QAAQG,qBACzCC,QAAS,CACLC,KAAMT,qBAAqBI,QAAQM,oBAEvCC,gBAAiB,CACbC,QAAS,gCAGhBC,MAAMC,QAEHA,MAAMC,aAEAC,WAAaC,SAASC,cAAc,2DACpCC,aAAeF,SAASC,cAAc,6DACtCE,eAAiBH,SAASC,cAAc,mEAGvCF,YAAY1B,GAAG+B,mCAAaC,OAAOC,UAAWhC,GAAMiC,gBAAgBjC,EAAGS,qBAAsBc,6BAG7FK,cAAc7B,GAAG+B,mCAAaC,OAAOC,UAAU,KAClDT,MAAMW,aAINL,eAAeF,cAAc,oDACtBE,gBAAgB9B,GAAG+B,mCAAaC,OAAOC,UAAU,KACpDT,MAAMW,aAIPX,SAaTU,gBAAkB,CAACjC,EAAGS,qBAAsBc,SAC9CvB,EAAEK,uBAGI8B,aAAeT,SAASU,cAAc,QAC5CD,aAAaE,OAAS5B,qBAAqBI,QAAQyB,aACnDH,aAAaI,OAAS,OAEtBJ,aAAaK,OAAS,eAChBC,gBAAkBf,SAASU,cAAc,SAC/CK,gBAAgBC,KAAO,UACvBD,gBAAgBE,MAAQC,gBAAOC,QAC/BV,aAAaW,YAAYL,iBACzBN,aAAaY,MAAMC,QAAU,OAE7BtB,SAASX,KAAK+B,YAAYX,cAC1BA,aAAac,SACbvB,SAASX,KAAKmC,YAAYf,cAG1BZ,MAAMW"}resetcourse.min.js000064400000004545152266416600010247 0ustar00define("core_course/resetcourse",["exports","core/notification","core/prefetch","core/str","core_form/changechecker"],(function(_exports,_notification,_prefetch,_str,FormChangeChecker){var obj; /** * Javascript module for resetting a course * * @module core_course/resetcourse * @copyright 2024 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj},FormChangeChecker=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(FormChangeChecker);const selectors_resetCoursetButton='[data-action="resetcourse"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("core",["resetcourseconfirm","resetcoursewarning","resetcourse"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const resetButton=event.target.closest(selectors_resetCoursetButton);resetButton&&(event.preventDefault(),resetCourseConfirm(resetButton))}))},resetCourseConfirm=async resetButton=>{const courseName=resetButton.dataset.coursename;_notification.default.deleteCancelPromise((0,_str.getString)("resetcourseconfirm"),(0,_str.getString)("resetcoursewarning","core",courseName),(0,_str.getString)("resetcourse")).then((()=>{FormChangeChecker.markFormSubmitted(resetButton.closest("form")),resetButton.closest("form").submit()})).catch((()=>{}))}})); //# sourceMappingURL=resetcourse.min.js.mapdownloadcontent.min.js000064400000005546152266416600011110 0ustar00define("core_course/downloadcontent",["exports","core/config","core/custom_interaction_events","core/modal_save_cancel","jquery","core/pending","core/key_codes"],(function(_exports,_config,_custom_interaction_events,_modal_save_cancel,_jquery,_pending,_key_codes){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Functions related to downloading course content. * * @module core_course/downloadcontent * @copyright 2020 Michael Hawkins * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_config=_interopRequireDefault(_config),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);_exports.init=()=>{const pendingPromise=new _pending.default;(0,_jquery.default)("[data-downloadcourse]").on("click keydown",(e=>{"click"!==e.type&&e.which!==_key_codes.enter&&e.which!==_key_codes.space||(e.preventDefault(),displayDownloadConfirmation(e.currentTarget))})),pendingPromise.resolve()};const displayDownloadConfirmation=downloadModalTrigger=>_modal_save_cancel.default.create({title:downloadModalTrigger.dataset.downloadTitle,body:"

".concat(downloadModalTrigger.dataset.downloadBody,"

"),buttons:{save:downloadModalTrigger.dataset.downloadButtonText},templateContext:{classes:"downloadcoursecontentmodal"}}).then((modal=>{modal.show();const saveButton=document.querySelector('.modal .downloadcoursecontentmodal [data-action="save"]'),cancelButton=document.querySelector('.modal .downloadcoursecontentmodal [data-action="cancel"]'),modalContainer=document.querySelector('.modal[data-region="modal-container"]');return(0,_jquery.default)(saveButton).on(_custom_interaction_events.default.events.activate,(e=>downloadContent(e,downloadModalTrigger,modal))),(0,_jquery.default)(cancelButton).on(_custom_interaction_events.default.events.activate,(()=>{modal.destroy()})),modalContainer.querySelector(".downloadcoursecontentmodal")&&(0,_jquery.default)(modalContainer).on(_custom_interaction_events.default.events.activate,(()=>{modal.destroy()})),modal})),downloadContent=(e,downloadModalTrigger,modal)=>{e.preventDefault();const downloadForm=document.createElement("form");downloadForm.action=downloadModalTrigger.dataset.downloadLink,downloadForm.method="POST",downloadForm.target="_blank";const downloadSesskey=document.createElement("input");downloadSesskey.name="sesskey",downloadSesskey.value=_config.default.sesskey,downloadForm.appendChild(downloadSesskey),downloadForm.style.display="none",document.body.appendChild(downloadForm),downloadForm.submit(),document.body.removeChild(downloadForm),modal.destroy()}})); //# sourceMappingURL=downloadcontent.min.js.mapmanual_completion_toggle.min.js.map000064400000016024152266416600013522 0ustar00{"version":3,"file":"manual_completion_toggle.min.js","sources":["../src/manual_completion_toggle.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 * Provides the functionality for toggling the manual completion state of a course module through\n * the manual completion button.\n *\n * @module core_course/manual_completion_toggle\n * @copyright 2021 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport {toggleManualCompletion} from 'core_course/repository';\nimport * as CourseEvents from 'core_course/events';\nimport Pending from 'core/pending';\n\n/**\n * Selectors in the manual completion template.\n *\n * @type {{MANUAL_TOGGLE: string}}\n */\nconst SELECTORS = {\n MANUAL_TOGGLE: 'button[data-action=toggle-manual-completion]',\n};\n\n/**\n * Toggle type values for the data-toggletype attribute in the core_course/completion_manual template.\n *\n * @type {{TOGGLE_UNDO: string, TOGGLE_MARK_DONE: string}}\n */\nconst TOGGLE_TYPES = {\n TOGGLE_MARK_DONE: 'manual:mark-done',\n TOGGLE_UNDO: 'manual:undo',\n};\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n/**\n * Registers the click event listener for the manual completion toggle button.\n */\nexport const init = () => {\n if (registered) {\n return;\n }\n document.addEventListener('click', (e) => {\n const toggleButton = e.target.closest(SELECTORS.MANUAL_TOGGLE);\n if (toggleButton) {\n e.preventDefault();\n toggleManualCompletionState(toggleButton).catch(Notification.exception);\n }\n });\n registered = true;\n};\n\n/**\n * Toggles the manual completion state of the module for the given user.\n *\n * @param {HTMLElement} toggleButton\n * @returns {Promise}\n */\nconst toggleManualCompletionState = async(toggleButton) => {\n const pendingPromise = new Pending('core_course:toggleManualCompletionState');\n // Make a copy of the original content of the button.\n const originalInnerHtml = toggleButton.innerHTML;\n\n // Disable the button to prevent double clicks.\n toggleButton.setAttribute('disabled', 'disabled');\n\n // Get button data.\n const toggleType = toggleButton.getAttribute('data-toggletype');\n const cmid = toggleButton.getAttribute('data-cmid');\n const activityname = toggleButton.getAttribute('data-activityname');\n // Get the target completion state.\n const completed = toggleType === TOGGLE_TYPES.TOGGLE_MARK_DONE;\n\n // Replace the button contents with the loading icon.\n Templates.renderForPromise('core/loading', {})\n .then((loadingHtml) => {\n Templates.replaceNodeContents(toggleButton, loadingHtml, '');\n return;\n }).catch(() => {});\n\n try {\n // Call the webservice to update the manual completion status.\n await toggleManualCompletion(cmid, completed);\n\n // All good so far. Refresh the manual completion button to reflect its new state by re-rendering the template.\n const templateContext = {\n cmid: cmid,\n activityname: activityname,\n overallcomplete: completed,\n overallincomplete: !completed,\n istrackeduser: true, // We know that we're tracking completion for this user given the presence of this button.\n normalbutton: !toggleButton.classList.contains('btn-sm'),\n };\n const renderObject = await Templates.renderForPromise('core_course/completion_manual', templateContext);\n\n // Replace the toggle button with the newly loaded template.\n const replacedNode = await Templates.replaceNode(toggleButton, renderObject.html, renderObject.js);\n const newToggleButton = replacedNode.pop();\n\n // Build manualCompletionToggled custom event.\n const withAvailability = toggleButton.getAttribute('data-withavailability');\n const toggledEvent = new CustomEvent(CourseEvents.manualCompletionToggled, {\n bubbles: true,\n detail: {\n cmid,\n activityname,\n completed,\n withAvailability,\n }\n });\n // Dispatch the manualCompletionToggled custom event.\n newToggleButton.dispatchEvent(toggledEvent);\n\n } catch (exception) {\n // In case of an error, revert the original state and appearance of the button.\n toggleButton.removeAttribute('disabled');\n toggleButton.innerHTML = originalInnerHtml;\n\n // Show the exception.\n Notification.exception(exception);\n }\n pendingPromise.resolve();\n};\n"],"names":["SELECTORS","TOGGLE_TYPES","registered","document","addEventListener","e","toggleButton","target","closest","preventDefault","toggleManualCompletionState","catch","Notification","exception","async","pendingPromise","Pending","originalInnerHtml","innerHTML","setAttribute","toggleType","getAttribute","cmid","activityname","completed","renderForPromise","then","loadingHtml","replaceNodeContents","templateContext","overallcomplete","overallincomplete","istrackeduser","normalbutton","classList","contains","renderObject","Templates","newToggleButton","replaceNode","html","js","pop","withAvailability","toggledEvent","CustomEvent","CourseEvents","manualCompletionToggled","bubbles","detail","dispatchEvent","removeAttribute","resolve"],"mappings":";;;;;;;;44BAmCMA,wBACa,+CAQbC,8BACgB,uBASlBC,YAAa,gBAKG,KACZA,aAGJC,SAASC,iBAAiB,SAAUC,UAC1BC,aAAeD,EAAEE,OAAOC,QAAQR,yBAClCM,eACAD,EAAEI,iBACFC,4BAA4BJ,cAAcK,MAAMC,sBAAaC,eAGrEX,YAAa,UASXQ,4BAA8BI,MAAAA,qBAC1BC,eAAiB,IAAIC,iBAAQ,2CAE7BC,kBAAoBX,aAAaY,UAGvCZ,aAAaa,aAAa,WAAY,kBAGhCC,WAAad,aAAae,aAAa,mBACvCC,KAAOhB,aAAae,aAAa,aACjCE,aAAejB,aAAae,aAAa,qBAEzCG,UAAYJ,aAAenB,iDAGvBwB,iBAAiB,eAAgB,IAC1CC,MAAMC,iCACOC,oBAAoBtB,aAAcqB,YAAa,OAE1DhB,OAAM,mBAIC,sCAAuBW,KAAME,iBAG7BK,gBAAkB,CACpBP,KAAMA,KACNC,aAAcA,aACdO,gBAAiBN,UACjBO,mBAAoBP,UACpBQ,eAAe,EACfC,cAAe3B,aAAa4B,UAAUC,SAAS,WAE7CC,mBAAqBC,mBAAUZ,iBAAiB,gCAAiCI,iBAIjFS,uBADqBD,mBAAUE,YAAYjC,aAAc8B,aAAaI,KAAMJ,aAAaK,KAC1DC,MAG/BC,iBAAmBrC,aAAae,aAAa,yBAC7CuB,aAAe,IAAIC,YAAYC,aAAaC,wBAAyB,CACvEC,SAAS,EACTC,OAAQ,CACJ3B,KAAAA,KACAC,aAAAA,aACAC,UAAAA,UACAmB,iBAAAA,oBAIRL,gBAAgBY,cAAcN,cAEhC,MAAO/B,WAELP,aAAa6C,gBAAgB,YAC7B7C,aAAaY,UAAYD,wCAGZJ,UAAUA,WAE3BE,eAAeqC"}events.min.js000064400000001611152266416600007177 0ustar00define("core_editor/events",["exports","core/event_dispatcher"],(function(_exports,_event_dispatcher){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.notifyEditorContentRestored=_exports.eventTypes=void 0; /** * Javascript events for the `core_editor` subsystem. * * @module core_editor/events * @copyright 2021 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.0 */ const eventTypes={editorContentRestored:"core_editor/contentRestored"};_exports.eventTypes=eventTypes;_exports.notifyEditorContentRestored=editor=>(editor||window.console.warn("The HTMLElement representing the editor that was modified should be provided to notifyEditorContentRestored."),(0,_event_dispatcher.dispatchEvent)(eventTypes.editorContentRestored,{},editor||document))})); //# sourceMappingURL=events.min.js.mapcopy_modal.min.js000064400000007635152266416600010035 0ustar00define("core_course/copy_modal",["exports","core/str","core/modal","core/ajax","core/fragment","core/notification","core/config"],(function(_exports,_str,_modal,ajax,Fragment,_notification,Config){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * This module provides the course copy modal from the course and * category management screen. * * @module core_course/copy_modal * @copyright 2020 onward The Moodle Users Association * @author Matt Porritt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.9 */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=_interopRequireDefault(_modal),ajax=_interopRequireWildcard(ajax),Fragment=_interopRequireWildcard(Fragment),_notification=_interopRequireDefault(_notification),Config=_interopRequireWildcard(Config);class CopyModal{static init(context){return new CopyModal(context)}constructor(context){this.contextid=context,this.registerEventListeners()}registerEventListeners(){document.addEventListener("click",(e=>{const copyAction=e.target.closest(".action-copy");if(!copyAction)return;e.preventDefault();const url=new URL(copyAction.href),params=new URLSearchParams(url.search);this.fetchCourseData(params.get("id")).then((_ref=>{let[course]=_ref;return this.createModal(course)})).catch((error=>_notification.default.exception(error)))}))}fetchCourseData(courseid){return ajax.call([{methodname:"core_course_get_courses",args:{options:{ids:[courseid]}}}])[0]}submitBackupRequest(jsonformdata){return ajax.call([{methodname:"core_backup_submit_copy_form",args:{jsonformdata:jsonformdata}}])[0]}createModal(course){let formdata=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const params={jsonformdata:JSON.stringify(formdata),courseid:course.id};return _modal.default.create({title:(0,_str.get_string)("copycoursetitle","backup",course.shortname),body:Fragment.loadFragment("course","new_base_form",this.contextid,params),large:!0,show:!0,removeOnClose:!0}).then((modal=>(modal.getRoot().on("click","#id_submitreturn",(e=>{this.processModalForm(course,modal,e)})),modal.getRoot().on("click","#id_cancel",(e=>{e.preventDefault(),modal.destroy()})),modal.getRoot().on("click","#id_submitdisplay",(e=>{e.formredirect=!0,this.processModalForm(course,modal,e)})),modal)))}processModalForm(course,modal,e){e.preventDefault();const copyform=modal.getRoot().find("form").serialize(),formjson=JSON.stringify(copyform),invalid=modal.getRoot()[0].querySelectorAll('[aria-invalid="true"], .error');invalid.length?invalid[0].focus():(modal.destroy(),this.submitBackupRequest(formjson).then((()=>{if(1==e.formredirect){const redirect="".concat(Config.wwwroot,"/backup/copyprogress.php?id=").concat(course.id);window.location.assign(redirect)}})).catch((()=>{this.createModal(course,copyform)})))}}return _exports.default=CopyModal,_exports.default})); //# sourceMappingURL=copy_modal.min.js.mapformatchooser.min.js000064400000001622152266416600010550 0ustar00define("core_course/formatchooser",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0; /** * Course format selection handler. * * @module core_course/formatchooser * @copyright 2022 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.0 */ const Selectors_fields={selector:'[data-formatchooser-field="selector"]',updateButton:'[data-formatchooser-field="updateButton"]'};_exports.init=()=>{document.querySelector(Selectors_fields.selector).addEventListener("change",(e=>{const form=e.target.closest("form"),updateButton=form.querySelector(Selectors_fields.updateButton),fieldset=updateButton.closest("fieldset"),url=new URL(form.action);url.hash=fieldset.id,form.action=url.toString(),updateButton.click()}))}})); //# sourceMappingURL=formatchooser.min.js.mapview.min.js.map000064400000006375152266416600007435 0ustar00{"version":3,"file":"view.min.js","sources":["../src/view.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 * Manage the quiz views.\n *\n * @module quizaccess_seb/view\n * @author Andrew Madden \n * @copyright 2021 Catalyst IT\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from \"core/notification\";\nimport * as Templates from \"core/templates\";\nimport * as Str from \"core/str\";\nimport ModalAlert from \"core/local/modal/alert\";\n\n/** @var SELECTOR List of CSS selectors. */\nconst SELECTOR = {\n MAIN: '#region-main',\n LOADING: '.seb-loading',\n};\n\n/** @var Template List of mustache templates. */\nconst TEMPLATE = {\n LOADING: 'quizaccess_seb/loading',\n};\n\n/**\n * Manages view when access has been granted.\n */\nexport const allowAccess = () => {\n window.location.reload();\n};\n\n/**\n * Add an alert to page to inform that Safe Exam Browser access is being checked.\n *\n * @return {Promise}\n */\nexport const addLoadingAlert = () => {\n return Templates.render(TEMPLATE.LOADING, {}).then((html, js) => {\n const alertRegion = window.document.querySelector(SELECTOR.MAIN);\n return Templates.prependNodeContents(alertRegion, html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Remove the Safe Exam Browser access check alert from the page.\n */\nexport const clearLoadingAlert = () => {\n const alert = window.document.querySelector(SELECTOR.LOADING);\n if (alert) {\n Templates.replaceNode(alert, '', '');\n }\n};\n\n/**\n * Display validation failed modal.\n */\nexport const showValidationFailedModal = () => {\n ModalAlert.create({\n title: Str.get_string('sebkeysvalidationfailed', 'quizaccess_seb'),\n body: Str.get_string('invalidkeys', 'quizaccess_seb'),\n large: false,\n show: true,\n }).catch(Notification.exception);\n};\n"],"names":["SELECTOR","TEMPLATE","window","location","reload","Templates","render","then","html","js","alertRegion","document","querySelector","prependNodeContents","catch","Notification","exception","alert","replaceNode","create","title","Str","get_string","body","large","show"],"mappings":";;;;;;;;8VA8BMA,cACI,eADJA,iBAEO,eAIPC,iBACO,8CAMc,KACvBC,OAAOC,SAASC,mCAQW,IACpBC,UAAUC,OAAOL,iBAAkB,IAAIM,MAAK,CAACC,KAAMC,YAChDC,YAAcR,OAAOS,SAASC,cAAcZ,sBAC3CK,UAAUQ,oBAAoBH,YAAaF,KAAMC,OACzDK,MAAMC,sBAAaC,sCAMO,WACvBC,MAAQf,OAAOS,SAASC,cAAcZ,kBACxCiB,OACAZ,UAAUa,YAAYD,MAAO,GAAI,wCAOA,oBAC1BE,OAAO,CACdC,MAAOC,IAAIC,WAAW,0BAA2B,kBACjDC,KAAMF,IAAIC,WAAW,cAAe,kBACpCE,OAAO,EACPC,MAAM,IACPX,MAAMC,sBAAaC"}resetcourse.min.js.map000064400000006162152266416600011020 0ustar00{"version":3,"file":"resetcourse.min.js","sources":["../src/resetcourse.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module for resetting a course\n *\n * @module core_course/resetcourse\n * @copyright 2024 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport * as FormChangeChecker from 'core_form/changechecker';\n\nconst selectors = {\n resetCoursetButton: '[data-action=\"resetcourse\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n prefetchStrings('core', [\n 'resetcourseconfirm',\n 'resetcoursewarning',\n 'resetcourse',\n ]);\n\n registerEventListeners();\n};\n\n/**\n * Register events for course reset button.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const resetButton = event.target.closest(selectors.resetCoursetButton);\n if (resetButton) {\n event.preventDefault();\n resetCourseConfirm(resetButton);\n }\n });\n};\n\n/**\n * Show the confirmation modal to reset the course.\n *\n * @param {HTMLElement} resetButton the element to delete.\n *\n */\nconst resetCourseConfirm = async(resetButton) => {\n const courseName = resetButton.dataset.coursename;\n\n Notification.deleteCancelPromise(\n getString('resetcourseconfirm'),\n getString('resetcoursewarning', 'core', courseName),\n getString('resetcourse'),\n ).then(() => {\n FormChangeChecker.markFormSubmitted(resetButton.closest('form'));\n resetButton.closest('form').submit();\n return;\n }).catch(() => {\n return;\n });\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","resetButton","target","closest","preventDefault","resetCourseConfirm","async","courseName","dataset","coursename","deleteCancelPromise","then","FormChangeChecker","markFormSubmitted","submit","catch"],"mappings":";;;;;;;imCA4BMA,6BACkB,4CAMJ,mCACA,OAAQ,CACpB,qBACA,qBACA,gBAGJC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,YAAcD,MAAME,OAAOC,QAAQP,8BACrCK,cACAD,MAAMI,iBACNC,mBAAmBJ,kBAWzBI,mBAAqBC,MAAAA,oBACjBC,WAAaN,YAAYO,QAAQC,iCAE1BC,qBACT,kBAAU,uBACV,kBAAU,qBAAsB,OAAQH,aACxC,kBAAU,gBACZI,MAAK,KACHC,kBAAkBC,kBAAkBZ,YAAYE,QAAQ,SACxDF,YAAYE,QAAQ,QAAQW,YAE7BC,OAAM"}formatchooser.min.js.map000064400000004254152266416600011330 0ustar00{"version":3,"file":"formatchooser.min.js","sources":["../src/formatchooser.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/ //\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 * Course format selection handler.\n *\n * @module core_course/formatchooser\n * @copyright 2022 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nconst Selectors = {\n fields: {\n selector: '[data-formatchooser-field=\"selector\"]',\n updateButton: '[data-formatchooser-field=\"updateButton\"]',\n },\n};\n\n/**\n * Initialise the format chooser.\n */\nexport const init = () => {\n document.querySelector(Selectors.fields.selector).addEventListener('change', e => {\n const form = e.target.closest('form');\n const updateButton = form.querySelector(Selectors.fields.updateButton);\n const fieldset = updateButton.closest('fieldset');\n\n const url = new URL(form.action);\n url.hash = fieldset.id;\n\n form.action = url.toString();\n updateButton.click();\n });\n};\n"],"names":["Selectors","selector","updateButton","document","querySelector","addEventListener","e","form","target","closest","fieldset","url","URL","action","hash","id","toString","click"],"mappings":";;;;;;;;;MAuBMA,iBACM,CACJC,SAAU,wCACVC,aAAc,2DAOF,KAChBC,SAASC,cAAcJ,iBAAiBC,UAAUI,iBAAiB,UAAUC,UACnEC,KAAOD,EAAEE,OAAOC,QAAQ,QACxBP,aAAeK,KAAKH,cAAcJ,iBAAiBE,cACnDQ,SAAWR,aAAaO,QAAQ,YAEhCE,IAAM,IAAIC,IAAIL,KAAKM,QACzBF,IAAIG,KAAOJ,SAASK,GAEpBR,KAAKM,OAASF,IAAIK,WAClBd,aAAae"}view.min.js000064400000005067152266416600006656 0ustar00define("quizaccess_seb/view",["exports","core/notification","core/templates","core/str","core/local/modal/alert"],(function(_exports,_notification,Templates,Str,_alert){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Manage the quiz views. * * @module quizaccess_seb/view * @author Andrew Madden * @copyright 2021 Catalyst IT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showValidationFailedModal=_exports.clearLoadingAlert=_exports.allowAccess=_exports.addLoadingAlert=void 0,_notification=_interopRequireDefault(_notification),Templates=_interopRequireWildcard(Templates),Str=_interopRequireWildcard(Str),_alert=_interopRequireDefault(_alert);const SELECTOR_MAIN="#region-main",SELECTOR_LOADING=".seb-loading",TEMPLATE_LOADING="quizaccess_seb/loading";_exports.allowAccess=()=>{window.location.reload()};_exports.addLoadingAlert=()=>Templates.render(TEMPLATE_LOADING,{}).then(((html,js)=>{const alertRegion=window.document.querySelector(SELECTOR_MAIN);return Templates.prependNodeContents(alertRegion,html,js)})).catch(_notification.default.exception);_exports.clearLoadingAlert=()=>{const alert=window.document.querySelector(SELECTOR_LOADING);alert&&Templates.replaceNode(alert,"","")};_exports.showValidationFailedModal=()=>{_alert.default.create({title:Str.get_string("sebkeysvalidationfailed","quizaccess_seb"),body:Str.get_string("invalidkeys","quizaccess_seb"),large:!1,show:!0}).catch(_notification.default.exception)}})); //# sourceMappingURL=view.min.js.mapactivitychooser.min.js000064400000022475152266416600011125 0ustar00define("core_course/activitychooser",["exports","core_course/local/activitychooser/dialogue","core_course/local/activitychooser/repository","core_course/local/activitychooser/selectors","core/custom_interaction_events","core/templates","core/str","core/modal","core/pending"],(function(_exports,ChooserDialogue,Repository,_selectors,_custom_interaction_events,Templates,_str,_modal,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} /** * A type of dialogue used as for choosing modules in a course. * * @module core_course/activitychooser * @copyright 2020 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,ChooserDialogue=_interopRequireWildcard(ChooserDialogue),Repository=_interopRequireWildcard(Repository),_selectors=_interopRequireDefault(_selectors),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),Templates=_interopRequireWildcard(Templates),_modal=_interopRequireDefault(_modal),_pending=_interopRequireDefault(_pending);let initialized=!1;_exports.init=(courseId,chooserConfig)=>{const pendingPromise=new _pending.default;registerListenerEvents(courseId,chooserConfig),pendingPromise.resolve()};const registerListenerEvents=(courseId,chooserConfig)=>{if(initialized)return;const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate],fetchModuleData=(()=>{let innerPromises=new Map;return sectionNum=>(innerPromises.has(sectionNum)||innerPromises.set(sectionNum,new Promise((resolve=>{resolve(Repository.activityModules(courseId,sectionNum))}))),innerPromises.get(sectionNum))})(),fetchFooterData=(()=>{let footerInnerPromise=null;return sectionNum=>(footerInnerPromise||(footerInnerPromise=new Promise((resolve=>{resolve(Repository.fetchFooterData(courseId,sectionNum))}))),footerInnerPromise)})();_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(async e=>{if(e.target.closest(_selectors.default.elements.sectionmodchooser)){let caller,sectionnum=null,sectionid=null;const sectionDiv=e.target.closest(_selectors.default.elements.section),button=e.target.closest(_selectors.default.elements.sectionmodchooser);let bodyPromiseResolver;null!==sectionDiv&§ionDiv.hasAttribute("data-number")?(caller=sectionDiv,sectionnum=sectionDiv.getAttribute("data-number"),sectionid=sectionDiv.getAttribute("data-id")):(caller=button,caller.hasAttribute("data-sectionid")&&(window.console.warn("The data-sectionid attribute has been deprecated. Please update your code to use data-section-id passing the real section ID instead."),caller.setAttribute("data-sectionnum",caller.dataset.sectionid)),sectionnum=caller.dataset.sectionnum,sectionid=caller.getAttribute("data-section-id"));const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve})),footerData=await fetchFooterData(sectionnum),sectionModal=buildModal(bodyPromise,footerData),data=await fetchModuleData(sectionnum).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_course/local/activitychooser/error",errorTemplateData))}));if(!data)return;const builtModuleData=sectionMapper(data,sectionnum,caller.dataset.sectionreturnnum,caller.dataset.beforemod,sectionid);ChooserDialogue.displayChooser(sectionModal,builtModuleData,partiallyAppliedFavouriteManager(data,sectionnum,sectionid),footerData),bodyPromiseResolver(await Templates.render("core_course/activitychooser",templateDataBuilder(builtModuleData,chooserConfig)))}}))})),initialized=!0},sectionMapper=function(webServiceData,num,sectionreturnnum,beforemod){let id=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;const newData=JSON.parse(JSON.stringify(webServiceData));let urlParams="&beforemod="+(null!=beforemod?beforemod:0);return id&&(urlParams+="§ionid=".concat(id)),urlParams+="§ion=".concat(num),sectionreturnnum&&(urlParams+="&sr=".concat(sectionreturnnum)),newData.content_items.forEach((module=>{module.link+=urlParams})),newData.content_items},templateDataBuilder=(data,chooserConfig)=>{let activities=[],resources=[],showAll=!0,showActivities=!1,showResources=!1;const tabMode=parseInt(chooserConfig.tabmode),favourites=data.filter((mod=>!0===mod.favourite)),recommended=data.filter((mod=>!0===mod.recommended));(tabMode=>-1!==[0,3,2,5].indexOf(tabMode))(tabMode)&&(activities=data.filter((mod=>0===mod.archetype)),resources=data.filter((mod=>1===mod.archetype)),showActivities=!0,showResources=!0,2!==tabMode&&5!==tabMode||(showAll=!1));const recommendedBeginning=-1!==[3,4,5].indexOf(tabMode),favouritesFirst=!!favourites.length,recommendedFirst=!1===favouritesFirst&&!0===recommendedBeginning&&!!recommended.length;return{default:data,showAll:showAll,activities:activities,showActivities:showActivities,activitiesFirst:!1===showAll&&!1===favouritesFirst&&!1===recommendedFirst,resources:resources,showResources:showResources,favourites:favourites,recommended:recommended,recommendedFirst:recommendedFirst,recommendedBeginning:recommendedBeginning,favouritesFirst:favouritesFirst,fallback:!0===showAll&&!1===favouritesFirst&&!1===recommendedFirst}},buildModal=(body,footer)=>_modal.default.create({body:body,title:(0,_str.getString)("addresourceoractivity"),footer:footer.customfootertemplate,large:!0,scrollable:!1,templateContext:{classes:"modchooser"},show:!0}),nullFavouriteDomManager=(favouriteTabNav,modalBody)=>{if(favouriteTabNav.tabIndex=-1,favouriteTabNav.classList.add("d-none"),favouriteTabNav.classList.contains("active")){favouriteTabNav.classList.remove("active"),favouriteTabNav.setAttribute("aria-selected","false");modalBody.querySelector(_selectors.default.regions.favouriteTab).classList.remove("active");const defaultTabNav=modalBody.querySelector(_selectors.default.regions.defaultTabNav),activitiesTabNav=modalBody.querySelector(_selectors.default.regions.activityTabNav);if(!1===defaultTabNav.classList.contains("d-none")){defaultTabNav.classList.add("active"),defaultTabNav.setAttribute("aria-selected","true"),defaultTabNav.tabIndex=0,defaultTabNav.focus();modalBody.querySelector(_selectors.default.regions.defaultTab).classList.add("active")}else{activitiesTabNav.classList.add("active"),activitiesTabNav.setAttribute("aria-selected","true"),activitiesTabNav.tabIndex=0,activitiesTabNav.focus();modalBody.querySelector(_selectors.default.regions.activityTab).classList.add("active")}}},partiallyAppliedFavouriteManager=function(moduleData,sectionnum){let sectionid=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;return async(internal,favourite,modalBody)=>{const favouriteArea=modalBody.querySelector(_selectors.default.render.favourites),favouriteButtons=modalBody.querySelectorAll('[data-internal="'.concat(internal,'"] ').concat(_selectors.default.actions.optionActions.manageFavourite)),favouriteTabNav=modalBody.querySelector(_selectors.default.regions.favouriteTabNav),result=moduleData.content_items.find((_ref=>{let{name:name}=_ref;return name===internal})),newFaves={};if(result)if(favourite){result.favourite=!0,newFaves.content_items=moduleData.content_items.filter((mod=>!0===mod.favourite));const builtFaves=sectionMapper(newFaves,sectionnum,null,null,sectionid),{html:html,js:js}=await Templates.renderForPromise("core_course/local/activitychooser/favourites",{favourites:builtFaves});await Templates.replaceNodeContents(favouriteArea,html,js),Array.from(favouriteButtons).forEach((element=>{element.classList.remove("text-muted"),element.classList.add("text-primary"),element.dataset.favourited="true",element.setAttribute("aria-pressed",!0),element.firstElementChild.classList.remove("fa-regular"),element.firstElementChild.classList.add("fa")})),favouriteTabNav.classList.remove("d-none")}else{result.favourite=!1;const nodeToRemove=favouriteArea.querySelector('[data-internal="'.concat(internal,'"]'));nodeToRemove.parentNode.removeChild(nodeToRemove),Array.from(favouriteButtons).forEach((element=>{element.classList.add("text-muted"),element.classList.remove("text-primary"),element.dataset.favourited="false",element.setAttribute("aria-pressed",!1),element.firstElementChild.classList.remove("fa"),element.firstElementChild.classList.add("fa-regular")}));0===moduleData.content_items.filter((mod=>!0===mod.favourite)).length&&nullFavouriteDomManager(favouriteTabNav,modalBody)}}}})); //# sourceMappingURL=activitychooser.min.js.mapmoodle-course-categoryexpander/moodle-course-categoryexpander-min.js000064400000012112152266437370022135 0ustar00YUI.add("moodle-course-categoryexpander",function(s,e){var a="collapse-all",p="disabled",i="loaded",g="notloaded",t="collapsed",_="with_children",h=".with_children",u=".with_children.loaded",l=".content",n=".category .info .categoryname",x=".categoryname",f=".category.with_children.collapsed",y=".category.with_children.loaded.collapsed",r=".category.with_children.loaded:not(.collapsed)",c=".collapseexpand",m=".coursebox",o=".coursebox .moreinfo",v=".info .moreinfo",b=".course_category_tree",C=".category",d=s.namespace("Moodle.course.categoryexpander"),w=M.cfg.wwwroot+"/course/category.ajax.php";d.init=function(){var e=s.one(s.config.doc);e.delegate("click",this.toggle_category_expansion,n,this),e.delegate("click",this.toggle_coursebox_expansion,o,this),e.delegate("click",this.collapse_expand_all,c,this),e.once("key",this.setup_keyboard_listeners,"tab",this)},d.setup_keyboard_listeners=function(){s.one(s.config.doc).all(n,o,c).setAttribute("tabindex","0"),s.one(s.config.doc).delegate("key",this.toggle_category_expansion,"enter",n,this),s.one(s.config.doc).delegate("key",this.toggle_coursebox_expansion,"enter",o,this),s.one(s.config.doc).delegate("key",this.collapse_expand_all,"enter",c,this)},d.expand_category=function(e){s.use("io-base","json-parse","moodle-core-notification","anim-node-plugin",function(){d.expand_category=d._expand_category,d.expand_category(e)})},d._expand_category=function(e){var n,o;e.hasClass(_)&&(e.hasClass(i)?this.run_expansion(e):(n=e.getData("categoryid"),o=e.getData("depth"),void 0!==n&&void 0!==o&&this._toggle_generic_expansion({parentnode:e,childnode:e.one(l),spinnerhandle:x,data:{categoryid:n,depth:o,showcourses:e.getData("showcourses"),type:0}})))},d.toggle_category_expansion=function(e){s.use("io-base","json-parse","moodle-core-notification","anim-node-plugin",function(){d.toggle_category_expansion=d._toggle_category_expansion,d.toggle_category_expansion(e)})},d.toggle_coursebox_expansion=function(e){s.use("io-base","json-parse","moodle-core-notification","anim-node-plugin",function(){d.toggle_coursebox_expansion=d._toggle_coursebox_expansion,d.toggle_coursebox_expansion(e)}),e.preventDefault()},d._toggle_coursebox_expansion=function(e){var n=e.target.ancestor(m,!0);e.preventDefault(),n.hasClass(i)?this.run_expansion(n):this._toggle_generic_expansion({parentnode:n,childnode:n.one(l),spinnerhandle:v,data:{courseid:n.getData("courseid"),type:1}})},d._toggle_category_expansion=function(e){var n,o;e.target.test("a")||e.target.test("img")||(e=e.target.ancestor(C,!0)).hasClass(_)&&(e.hasClass(i)?this.run_expansion(e):(n=e.getData("categoryid"),o=e.getData("depth"),void 0!==n&&void 0!==o&&this._toggle_generic_expansion({parentnode:e,childnode:e.one(l),spinnerhandle:x,data:{categoryid:n,depth:o,showcourses:e.getData("showcourses"),type:0}})))},d._toggle_generic_expansion=function(e){var n;e.spinnerhandle&&(n=M.util.add_spinner(s,e.parentnode.one(e.spinnerhandle)).show()),s.io(w,{method:"POST",context:this,on:{complete:this.process_results},data:e.data,arguments:{parentnode:e.parentnode,childnode:e.childnode,spinner:n}})},d.run_expansion=function(e){var n=e.one(l),o=this,a=e.ancestor(b);this.add_animation(n),e.hasClass(t)?(n.setStyle("height","0"),e.removeClass(t),e.setAttribute("aria-expanded","true"),n.fx.set("reverse",!1)):(n.fx.set("reverse",!0),n.fx.once("end",function(e,n){n.addClass(t),n.setAttribute("aria-expanded","false")},this,e)),n.fx.once("end",function(e,n){n.setStyles({height:"",opacity:""}),this.destroy(),o.update_collapsible_actions(a)},n.fx,n),n.fx.run()},d.collapse_expand_all=function(e){s.use("io-base","json-parse","moodle-core-notification","anim-node-plugin",function(){d.collapse_expand_all=d._collapse_expand_all,d.collapse_expand_all(e)}),e.preventDefault()},d._collapse_expand_all=function(e){e.preventDefault(),e.currentTarget.hasClass(p)||(e=e.currentTarget.ancestor(b))&&(e.one(c).hasClass(a)?this.collapse_all(e):this.expand_all(e),this.update_collapsible_actions(e))},d.expand_all=function(e){var n=[];e.all(f).each(function(e){e.ancestor(f)?(e.removeClass(t),e.all(h).removeClass(t)):n.push(e)},this),s.all(n).each(function(e){this.expand_category(e)},this)},d.collapse_all=function(e){var n=[];e.all(r).each(function(e){e.ancestor(r)?n.push(e):this.run_expansion(e)},this),s.all(n).each(function(e){e.addClass(t),e.all(u).addClass(t)},this)},d.update_collapsible_actions=function(e){var n=!1,o=e.one(c);o&&(e.all(r).each(function(e){return!e.ancestor(y)&&(n=!0)}),(n?o.setHTML(M.util.get_string("collapseall","moodle")).addClass(a):o.setHTML(M.util.get_string("expandall","moodle")).removeClass(a)).removeClass(p))},d.process_results=function(e,n,o){var a;try{if((a=s.JSON.parse(n.responseText)).error)return new M.core.ajaxException(a)}catch(t){return new M.core.exception(t)}n=s.Node.create(a),o.childnode.appendChild(n),o.parentnode.addClass(i).removeClass(g),this.run_expansion(o.parentnode),o.spinner&&o.spinner.hide().destroy()},d.add_animation=function(e){return"undefined"!=typeof e.fx||e.plug(s.Plugin.NodeFX,{from:{height:0,opacity:0},to:{height:function(e){return e.get("scrollHeight")},opacity:1},duration:.2}),e}},"@VERSION@",{requires:["node","event-key"]});moodle-course-categoryexpander/moodle-course-categoryexpander-debug.js000064400000040562152266437370022452 0ustar00YUI.add('moodle-course-categoryexpander', function (Y, NAME) { /** * Adds toggling of subcategory with automatic loading using AJAX. * * This also includes application of an animation to improve user experience. * * @module moodle-course-categoryexpander */ /** * The course category expander. * * @constructor * @class Y.Moodle.course.categoryexpander */ var CSS = { CONTENTNODE: 'content', COLLAPSEALL: 'collapse-all', DISABLED: 'disabled', LOADED: 'loaded', NOTLOADED: 'notloaded', SECTIONCOLLAPSED: 'collapsed', HASCHILDREN: 'with_children' }, SELECTORS = { WITHCHILDRENTREES: '.with_children', LOADEDTREES: '.with_children.loaded', CONTENTNODE: '.content', CATEGORYLISTENLINK: '.category .info .categoryname', CATEGORYSPINNERLOCATION: '.categoryname', CATEGORYWITHCOLLAPSEDCHILDREN: '.category.with_children.collapsed', CATEGORYWITHCOLLAPSEDLOADEDCHILDREN: '.category.with_children.loaded.collapsed', CATEGORYWITHMAXIMISEDLOADEDCHILDREN: '.category.with_children.loaded:not(.collapsed)', COLLAPSEEXPAND: '.collapseexpand', COURSEBOX: '.coursebox', COURSEBOXLISTENLINK: '.coursebox .moreinfo', COURSEBOXSPINNERLOCATION: '.info .moreinfo', COURSECATEGORYTREE: '.course_category_tree', PARENTWITHCHILDREN: '.category' }, NS = Y.namespace('Moodle.course.categoryexpander'), TYPE_CATEGORY = 0, TYPE_COURSE = 1, URL = M.cfg.wwwroot + '/course/category.ajax.php'; /** * Set up the category expander. * * No arguments are required. * * @method init */ NS.init = function() { var doc = Y.one(Y.config.doc); doc.delegate('click', this.toggle_category_expansion, SELECTORS.CATEGORYLISTENLINK, this); doc.delegate('click', this.toggle_coursebox_expansion, SELECTORS.COURSEBOXLISTENLINK, this); doc.delegate('click', this.collapse_expand_all, SELECTORS.COLLAPSEEXPAND, this); // Only set up they keybaord listeners when tab is first pressed - it // may never happen and modifying the DOM on a large number of nodes // can be very expensive. doc.once('key', this.setup_keyboard_listeners, 'tab', this); }; /** * Set up keyboard expansion for course content. * * This includes setting up the delegation but also adding the nodes to the * tabflow. * * @method setup_keyboard_listeners */ NS.setup_keyboard_listeners = function() { var doc = Y.one(Y.config.doc); Y.log('Setting the tabindex for all expandable course nodes', 'info', 'moodle-course-categoryexpander'); doc.all(SELECTORS.CATEGORYLISTENLINK, SELECTORS.COURSEBOXLISTENLINK, SELECTORS.COLLAPSEEXPAND).setAttribute('tabindex', '0'); Y.one(Y.config.doc).delegate('key', this.toggle_category_expansion, 'enter', SELECTORS.CATEGORYLISTENLINK, this); Y.one(Y.config.doc).delegate('key', this.toggle_coursebox_expansion, 'enter', SELECTORS.COURSEBOXLISTENLINK, this); Y.one(Y.config.doc).delegate('key', this.collapse_expand_all, 'enter', SELECTORS.COLLAPSEEXPAND, this); }; /** * Expand all categories. * * @method expand_category * @private * @param {Node} categorynode The node to expand */ NS.expand_category = function(categorynode) { // Load the actual dependencies now that we've been called. Y.use('io-base', 'json-parse', 'moodle-core-notification', 'anim-node-plugin', function() { // Overload the expand_category with the _expand_category function to ensure that // this function isn't called in the future, and call it for the first time. NS.expand_category = NS._expand_category; NS.expand_category(categorynode); }); }; NS._expand_category = function(categorynode) { var categoryid, depth; if (!categorynode.hasClass(CSS.HASCHILDREN)) { // Nothing to do here - this category has no children. return; } if (categorynode.hasClass(CSS.LOADED)) { // We've already loaded this content so we just need to toggle the view of it. this.run_expansion(categorynode); return; } // We use Data attributes to store the category. categoryid = categorynode.getData('categoryid'); depth = categorynode.getData('depth'); if (typeof categoryid === "undefined" || typeof depth === "undefined") { return; } this._toggle_generic_expansion({ parentnode: categorynode, childnode: categorynode.one(SELECTORS.CONTENTNODE), spinnerhandle: SELECTORS.CATEGORYSPINNERLOCATION, data: { categoryid: categoryid, depth: depth, showcourses: categorynode.getData('showcourses'), type: TYPE_CATEGORY } }); }; /** * Toggle the animation of the clicked category node. * * @method toggle_category_expansion * @private * @param {EventFacade} e */ NS.toggle_category_expansion = function(e) { // Load the actual dependencies now that we've been called. Y.use('io-base', 'json-parse', 'moodle-core-notification', 'anim-node-plugin', function() { // Overload the toggle_category_expansion with the _toggle_category_expansion function to ensure that // this function isn't called in the future, and call it for the first time. NS.toggle_category_expansion = NS._toggle_category_expansion; NS.toggle_category_expansion(e); }); }; /** * Toggle the animation of the clicked coursebox node. * * @method toggle_coursebox_expansion * @private * @param {EventFacade} e */ NS.toggle_coursebox_expansion = function(e) { // Load the actual dependencies now that we've been called. Y.use('io-base', 'json-parse', 'moodle-core-notification', 'anim-node-plugin', function() { // Overload the toggle_coursebox_expansion with the _toggle_coursebox_expansion function to ensure that // this function isn't called in the future, and call it for the first time. NS.toggle_coursebox_expansion = NS._toggle_coursebox_expansion; NS.toggle_coursebox_expansion(e); }); e.preventDefault(); }; NS._toggle_coursebox_expansion = function(e) { var courseboxnode; // Grab the parent category container - this is where the new content will be added. courseboxnode = e.target.ancestor(SELECTORS.COURSEBOX, true); e.preventDefault(); if (courseboxnode.hasClass(CSS.LOADED)) { // We've already loaded this content so we just need to toggle the view of it. this.run_expansion(courseboxnode); return; } this._toggle_generic_expansion({ parentnode: courseboxnode, childnode: courseboxnode.one(SELECTORS.CONTENTNODE), spinnerhandle: SELECTORS.COURSEBOXSPINNERLOCATION, data: { courseid: courseboxnode.getData('courseid'), type: TYPE_COURSE } }); }; NS._toggle_category_expansion = function(e) { var categorynode, categoryid, depth; if (e.target.test('a') || e.target.test('img')) { // Return early if either an anchor or an image were clicked. return; } // Grab the parent category container - this is where the new content will be added. categorynode = e.target.ancestor(SELECTORS.PARENTWITHCHILDREN, true); if (!categorynode.hasClass(CSS.HASCHILDREN)) { // Nothing to do here - this category has no children. return; } if (categorynode.hasClass(CSS.LOADED)) { // We've already loaded this content so we just need to toggle the view of it. this.run_expansion(categorynode); return; } // We use Data attributes to store the category. categoryid = categorynode.getData('categoryid'); depth = categorynode.getData('depth'); if (typeof categoryid === "undefined" || typeof depth === "undefined") { return; } this._toggle_generic_expansion({ parentnode: categorynode, childnode: categorynode.one(SELECTORS.CONTENTNODE), spinnerhandle: SELECTORS.CATEGORYSPINNERLOCATION, data: { categoryid: categoryid, depth: depth, showcourses: categorynode.getData('showcourses'), type: TYPE_CATEGORY } }); }; /** * Wrapper function to handle toggling of generic types. * * @method _toggle_generic_expansion * @private * @param {Object} config */ NS._toggle_generic_expansion = function(config) { var spinner; if (config.spinnerhandle) { // Add a spinner to give some feedback to the user. spinner = M.util.add_spinner(Y, config.parentnode.one(config.spinnerhandle)).show(); } // Fetch the data. Y.io(URL, { method: 'POST', context: this, on: { complete: this.process_results }, data: config.data, "arguments": { parentnode: config.parentnode, childnode: config.childnode, spinner: spinner } }); }; /** * Apply the animation on the supplied node. * * @method run_expansion * @private * @param {Node} categorynode The node to apply the animation to */ NS.run_expansion = function(categorynode) { var categorychildren = categorynode.one(SELECTORS.CONTENTNODE), self = this, ancestor = categorynode.ancestor(SELECTORS.COURSECATEGORYTREE); // Add our animation to the categorychildren. this.add_animation(categorychildren); // If we already have the class, remove it before showing otherwise we perform the // animation whilst the node is hidden. if (categorynode.hasClass(CSS.SECTIONCOLLAPSED)) { // To avoid a jump effect, we need to set the height of the children to 0 here before removing the SECTIONCOLLAPSED class. categorychildren.setStyle('height', '0'); categorynode.removeClass(CSS.SECTIONCOLLAPSED); categorynode.setAttribute('aria-expanded', 'true'); categorychildren.fx.set('reverse', false); } else { categorychildren.fx.set('reverse', true); categorychildren.fx.once('end', function(e, categorynode) { categorynode.addClass(CSS.SECTIONCOLLAPSED); categorynode.setAttribute('aria-expanded', 'false'); }, this, categorynode); } categorychildren.fx.once('end', function(e, categorychildren) { // Remove the styles that the animation has set. categorychildren.setStyles({ height: '', opacity: '' }); // To avoid memory gobbling, remove the animation. It will be added back if called again. this.destroy(); self.update_collapsible_actions(ancestor); }, categorychildren.fx, categorychildren); // Now that everything has been set up, run the animation. categorychildren.fx.run(); }; /** * Toggle collapsing of all nodes. * * @method collapse_expand_all * @private * @param {EventFacade} e */ NS.collapse_expand_all = function(e) { // Load the actual dependencies now that we've been called. Y.use('io-base', 'json-parse', 'moodle-core-notification', 'anim-node-plugin', function() { // Overload the collapse_expand_all with the _collapse_expand_all function to ensure that // this function isn't called in the future, and call it for the first time. NS.collapse_expand_all = NS._collapse_expand_all; NS.collapse_expand_all(e); }); e.preventDefault(); }; NS._collapse_expand_all = function(e) { // The collapse/expand button has no actual target but we need to prevent it's default // action to ensure we don't make the page reload/jump. e.preventDefault(); if (e.currentTarget.hasClass(CSS.DISABLED)) { // The collapse/expand is currently disabled. return; } var ancestor = e.currentTarget.ancestor(SELECTORS.COURSECATEGORYTREE); if (!ancestor) { return; } var collapseall = ancestor.one(SELECTORS.COLLAPSEEXPAND); if (collapseall.hasClass(CSS.COLLAPSEALL)) { this.collapse_all(ancestor); } else { this.expand_all(ancestor); } this.update_collapsible_actions(ancestor); }; NS.expand_all = function(ancestor) { var finalexpansions = []; ancestor.all(SELECTORS.CATEGORYWITHCOLLAPSEDCHILDREN) .each(function(c) { if (c.ancestor(SELECTORS.CATEGORYWITHCOLLAPSEDCHILDREN)) { // Expand the hidden children first without animation. c.removeClass(CSS.SECTIONCOLLAPSED); c.all(SELECTORS.WITHCHILDRENTREES).removeClass(CSS.SECTIONCOLLAPSED); } else { finalexpansions.push(c); } }, this); // Run the final expansion with animation on the visible items. Y.all(finalexpansions).each(function(c) { this.expand_category(c); }, this); }; NS.collapse_all = function(ancestor) { var finalcollapses = []; ancestor.all(SELECTORS.CATEGORYWITHMAXIMISEDLOADEDCHILDREN) .each(function(c) { if (c.ancestor(SELECTORS.CATEGORYWITHMAXIMISEDLOADEDCHILDREN)) { finalcollapses.push(c); } else { // Collapse the visible items first this.run_expansion(c); } }, this); // Run the final collapses now that the these are hidden hidden. Y.all(finalcollapses).each(function(c) { c.addClass(CSS.SECTIONCOLLAPSED); c.all(SELECTORS.LOADEDTREES).addClass(CSS.SECTIONCOLLAPSED); }, this); }; NS.update_collapsible_actions = function(ancestor) { var foundmaximisedchildren = false, // Grab the anchor for the collapseexpand all link. togglelink = ancestor.one(SELECTORS.COLLAPSEEXPAND); if (!togglelink) { // We should always have a togglelink but ensure. return; } // Search for any visibly expanded children. ancestor.all(SELECTORS.CATEGORYWITHMAXIMISEDLOADEDCHILDREN).each(function(n) { // If we can find any collapsed ancestors, skip. if (n.ancestor(SELECTORS.CATEGORYWITHCOLLAPSEDLOADEDCHILDREN)) { return false; } foundmaximisedchildren = true; return true; }); if (foundmaximisedchildren) { // At least one maximised child found. Show the collapseall. togglelink.setHTML(M.util.get_string('collapseall', 'moodle')) .addClass(CSS.COLLAPSEALL) .removeClass(CSS.DISABLED); } else { // No maximised children found but there are collapsed children. Show the expandall. togglelink.setHTML(M.util.get_string('expandall', 'moodle')) .removeClass(CSS.COLLAPSEALL) .removeClass(CSS.DISABLED); } }; /** * Process the data returned by Y.io. * This includes appending it to the relevant part of the DOM, and applying our animations. * * @method process_results * @private * @param {String} tid The Transaction ID * @param {Object} response The Reponse returned by Y.IO * @param {Object} ioargs The additional arguments provided by Y.IO */ NS.process_results = function(tid, response, args) { var newnode, data; try { data = Y.JSON.parse(response.responseText); if (data.error) { return new M.core.ajaxException(data); } } catch (e) { return new M.core.exception(e); } // Insert the returned data into a new Node. newnode = Y.Node.create(data); // Append to the existing child location. args.childnode.appendChild(newnode); // Now that we have content, we can swap the classes on the toggled container. args.parentnode .addClass(CSS.LOADED) .removeClass(CSS.NOTLOADED); // Toggle the open/close status of the node now that it's content has been loaded. this.run_expansion(args.parentnode); // Remove the spinner now that we've started to show the content. if (args.spinner) { args.spinner.hide().destroy(); } }; /** * Add our animation to the Node. * * @method add_animation * @private * @param {Node} childnode */ NS.add_animation = function(childnode) { if (typeof childnode.fx !== "undefined") { // The animation has already been plugged to this node. return childnode; } childnode.plug(Y.Plugin.NodeFX, { from: { height: 0, opacity: 0 }, to: { // This sets a dynamic height in case the node content changes. height: function(node) { // Get expanded height (offsetHeight may be zero). return node.get('scrollHeight'); }, opacity: 1 }, duration: 0.2 }); return childnode; }; }, '@VERSION@', {"requires": ["node", "event-key"]}); moodle-course-categoryexpander/moodle-course-categoryexpander.js000064400000040405152266437370021362 0ustar00YUI.add('moodle-course-categoryexpander', function (Y, NAME) { /** * Adds toggling of subcategory with automatic loading using AJAX. * * This also includes application of an animation to improve user experience. * * @module moodle-course-categoryexpander */ /** * The course category expander. * * @constructor * @class Y.Moodle.course.categoryexpander */ var CSS = { CONTENTNODE: 'content', COLLAPSEALL: 'collapse-all', DISABLED: 'disabled', LOADED: 'loaded', NOTLOADED: 'notloaded', SECTIONCOLLAPSED: 'collapsed', HASCHILDREN: 'with_children' }, SELECTORS = { WITHCHILDRENTREES: '.with_children', LOADEDTREES: '.with_children.loaded', CONTENTNODE: '.content', CATEGORYLISTENLINK: '.category .info .categoryname', CATEGORYSPINNERLOCATION: '.categoryname', CATEGORYWITHCOLLAPSEDCHILDREN: '.category.with_children.collapsed', CATEGORYWITHCOLLAPSEDLOADEDCHILDREN: '.category.with_children.loaded.collapsed', CATEGORYWITHMAXIMISEDLOADEDCHILDREN: '.category.with_children.loaded:not(.collapsed)', COLLAPSEEXPAND: '.collapseexpand', COURSEBOX: '.coursebox', COURSEBOXLISTENLINK: '.coursebox .moreinfo', COURSEBOXSPINNERLOCATION: '.info .moreinfo', COURSECATEGORYTREE: '.course_category_tree', PARENTWITHCHILDREN: '.category' }, NS = Y.namespace('Moodle.course.categoryexpander'), TYPE_CATEGORY = 0, TYPE_COURSE = 1, URL = M.cfg.wwwroot + '/course/category.ajax.php'; /** * Set up the category expander. * * No arguments are required. * * @method init */ NS.init = function() { var doc = Y.one(Y.config.doc); doc.delegate('click', this.toggle_category_expansion, SELECTORS.CATEGORYLISTENLINK, this); doc.delegate('click', this.toggle_coursebox_expansion, SELECTORS.COURSEBOXLISTENLINK, this); doc.delegate('click', this.collapse_expand_all, SELECTORS.COLLAPSEEXPAND, this); // Only set up they keybaord listeners when tab is first pressed - it // may never happen and modifying the DOM on a large number of nodes // can be very expensive. doc.once('key', this.setup_keyboard_listeners, 'tab', this); }; /** * Set up keyboard expansion for course content. * * This includes setting up the delegation but also adding the nodes to the * tabflow. * * @method setup_keyboard_listeners */ NS.setup_keyboard_listeners = function() { var doc = Y.one(Y.config.doc); doc.all(SELECTORS.CATEGORYLISTENLINK, SELECTORS.COURSEBOXLISTENLINK, SELECTORS.COLLAPSEEXPAND).setAttribute('tabindex', '0'); Y.one(Y.config.doc).delegate('key', this.toggle_category_expansion, 'enter', SELECTORS.CATEGORYLISTENLINK, this); Y.one(Y.config.doc).delegate('key', this.toggle_coursebox_expansion, 'enter', SELECTORS.COURSEBOXLISTENLINK, this); Y.one(Y.config.doc).delegate('key', this.collapse_expand_all, 'enter', SELECTORS.COLLAPSEEXPAND, this); }; /** * Expand all categories. * * @method expand_category * @private * @param {Node} categorynode The node to expand */ NS.expand_category = function(categorynode) { // Load the actual dependencies now that we've been called. Y.use('io-base', 'json-parse', 'moodle-core-notification', 'anim-node-plugin', function() { // Overload the expand_category with the _expand_category function to ensure that // this function isn't called in the future, and call it for the first time. NS.expand_category = NS._expand_category; NS.expand_category(categorynode); }); }; NS._expand_category = function(categorynode) { var categoryid, depth; if (!categorynode.hasClass(CSS.HASCHILDREN)) { // Nothing to do here - this category has no children. return; } if (categorynode.hasClass(CSS.LOADED)) { // We've already loaded this content so we just need to toggle the view of it. this.run_expansion(categorynode); return; } // We use Data attributes to store the category. categoryid = categorynode.getData('categoryid'); depth = categorynode.getData('depth'); if (typeof categoryid === "undefined" || typeof depth === "undefined") { return; } this._toggle_generic_expansion({ parentnode: categorynode, childnode: categorynode.one(SELECTORS.CONTENTNODE), spinnerhandle: SELECTORS.CATEGORYSPINNERLOCATION, data: { categoryid: categoryid, depth: depth, showcourses: categorynode.getData('showcourses'), type: TYPE_CATEGORY } }); }; /** * Toggle the animation of the clicked category node. * * @method toggle_category_expansion * @private * @param {EventFacade} e */ NS.toggle_category_expansion = function(e) { // Load the actual dependencies now that we've been called. Y.use('io-base', 'json-parse', 'moodle-core-notification', 'anim-node-plugin', function() { // Overload the toggle_category_expansion with the _toggle_category_expansion function to ensure that // this function isn't called in the future, and call it for the first time. NS.toggle_category_expansion = NS._toggle_category_expansion; NS.toggle_category_expansion(e); }); }; /** * Toggle the animation of the clicked coursebox node. * * @method toggle_coursebox_expansion * @private * @param {EventFacade} e */ NS.toggle_coursebox_expansion = function(e) { // Load the actual dependencies now that we've been called. Y.use('io-base', 'json-parse', 'moodle-core-notification', 'anim-node-plugin', function() { // Overload the toggle_coursebox_expansion with the _toggle_coursebox_expansion function to ensure that // this function isn't called in the future, and call it for the first time. NS.toggle_coursebox_expansion = NS._toggle_coursebox_expansion; NS.toggle_coursebox_expansion(e); }); e.preventDefault(); }; NS._toggle_coursebox_expansion = function(e) { var courseboxnode; // Grab the parent category container - this is where the new content will be added. courseboxnode = e.target.ancestor(SELECTORS.COURSEBOX, true); e.preventDefault(); if (courseboxnode.hasClass(CSS.LOADED)) { // We've already loaded this content so we just need to toggle the view of it. this.run_expansion(courseboxnode); return; } this._toggle_generic_expansion({ parentnode: courseboxnode, childnode: courseboxnode.one(SELECTORS.CONTENTNODE), spinnerhandle: SELECTORS.COURSEBOXSPINNERLOCATION, data: { courseid: courseboxnode.getData('courseid'), type: TYPE_COURSE } }); }; NS._toggle_category_expansion = function(e) { var categorynode, categoryid, depth; if (e.target.test('a') || e.target.test('img')) { // Return early if either an anchor or an image were clicked. return; } // Grab the parent category container - this is where the new content will be added. categorynode = e.target.ancestor(SELECTORS.PARENTWITHCHILDREN, true); if (!categorynode.hasClass(CSS.HASCHILDREN)) { // Nothing to do here - this category has no children. return; } if (categorynode.hasClass(CSS.LOADED)) { // We've already loaded this content so we just need to toggle the view of it. this.run_expansion(categorynode); return; } // We use Data attributes to store the category. categoryid = categorynode.getData('categoryid'); depth = categorynode.getData('depth'); if (typeof categoryid === "undefined" || typeof depth === "undefined") { return; } this._toggle_generic_expansion({ parentnode: categorynode, childnode: categorynode.one(SELECTORS.CONTENTNODE), spinnerhandle: SELECTORS.CATEGORYSPINNERLOCATION, data: { categoryid: categoryid, depth: depth, showcourses: categorynode.getData('showcourses'), type: TYPE_CATEGORY } }); }; /** * Wrapper function to handle toggling of generic types. * * @method _toggle_generic_expansion * @private * @param {Object} config */ NS._toggle_generic_expansion = function(config) { var spinner; if (config.spinnerhandle) { // Add a spinner to give some feedback to the user. spinner = M.util.add_spinner(Y, config.parentnode.one(config.spinnerhandle)).show(); } // Fetch the data. Y.io(URL, { method: 'POST', context: this, on: { complete: this.process_results }, data: config.data, "arguments": { parentnode: config.parentnode, childnode: config.childnode, spinner: spinner } }); }; /** * Apply the animation on the supplied node. * * @method run_expansion * @private * @param {Node} categorynode The node to apply the animation to */ NS.run_expansion = function(categorynode) { var categorychildren = categorynode.one(SELECTORS.CONTENTNODE), self = this, ancestor = categorynode.ancestor(SELECTORS.COURSECATEGORYTREE); // Add our animation to the categorychildren. this.add_animation(categorychildren); // If we already have the class, remove it before showing otherwise we perform the // animation whilst the node is hidden. if (categorynode.hasClass(CSS.SECTIONCOLLAPSED)) { // To avoid a jump effect, we need to set the height of the children to 0 here before removing the SECTIONCOLLAPSED class. categorychildren.setStyle('height', '0'); categorynode.removeClass(CSS.SECTIONCOLLAPSED); categorynode.setAttribute('aria-expanded', 'true'); categorychildren.fx.set('reverse', false); } else { categorychildren.fx.set('reverse', true); categorychildren.fx.once('end', function(e, categorynode) { categorynode.addClass(CSS.SECTIONCOLLAPSED); categorynode.setAttribute('aria-expanded', 'false'); }, this, categorynode); } categorychildren.fx.once('end', function(e, categorychildren) { // Remove the styles that the animation has set. categorychildren.setStyles({ height: '', opacity: '' }); // To avoid memory gobbling, remove the animation. It will be added back if called again. this.destroy(); self.update_collapsible_actions(ancestor); }, categorychildren.fx, categorychildren); // Now that everything has been set up, run the animation. categorychildren.fx.run(); }; /** * Toggle collapsing of all nodes. * * @method collapse_expand_all * @private * @param {EventFacade} e */ NS.collapse_expand_all = function(e) { // Load the actual dependencies now that we've been called. Y.use('io-base', 'json-parse', 'moodle-core-notification', 'anim-node-plugin', function() { // Overload the collapse_expand_all with the _collapse_expand_all function to ensure that // this function isn't called in the future, and call it for the first time. NS.collapse_expand_all = NS._collapse_expand_all; NS.collapse_expand_all(e); }); e.preventDefault(); }; NS._collapse_expand_all = function(e) { // The collapse/expand button has no actual target but we need to prevent it's default // action to ensure we don't make the page reload/jump. e.preventDefault(); if (e.currentTarget.hasClass(CSS.DISABLED)) { // The collapse/expand is currently disabled. return; } var ancestor = e.currentTarget.ancestor(SELECTORS.COURSECATEGORYTREE); if (!ancestor) { return; } var collapseall = ancestor.one(SELECTORS.COLLAPSEEXPAND); if (collapseall.hasClass(CSS.COLLAPSEALL)) { this.collapse_all(ancestor); } else { this.expand_all(ancestor); } this.update_collapsible_actions(ancestor); }; NS.expand_all = function(ancestor) { var finalexpansions = []; ancestor.all(SELECTORS.CATEGORYWITHCOLLAPSEDCHILDREN) .each(function(c) { if (c.ancestor(SELECTORS.CATEGORYWITHCOLLAPSEDCHILDREN)) { // Expand the hidden children first without animation. c.removeClass(CSS.SECTIONCOLLAPSED); c.all(SELECTORS.WITHCHILDRENTREES).removeClass(CSS.SECTIONCOLLAPSED); } else { finalexpansions.push(c); } }, this); // Run the final expansion with animation on the visible items. Y.all(finalexpansions).each(function(c) { this.expand_category(c); }, this); }; NS.collapse_all = function(ancestor) { var finalcollapses = []; ancestor.all(SELECTORS.CATEGORYWITHMAXIMISEDLOADEDCHILDREN) .each(function(c) { if (c.ancestor(SELECTORS.CATEGORYWITHMAXIMISEDLOADEDCHILDREN)) { finalcollapses.push(c); } else { // Collapse the visible items first this.run_expansion(c); } }, this); // Run the final collapses now that the these are hidden hidden. Y.all(finalcollapses).each(function(c) { c.addClass(CSS.SECTIONCOLLAPSED); c.all(SELECTORS.LOADEDTREES).addClass(CSS.SECTIONCOLLAPSED); }, this); }; NS.update_collapsible_actions = function(ancestor) { var foundmaximisedchildren = false, // Grab the anchor for the collapseexpand all link. togglelink = ancestor.one(SELECTORS.COLLAPSEEXPAND); if (!togglelink) { // We should always have a togglelink but ensure. return; } // Search for any visibly expanded children. ancestor.all(SELECTORS.CATEGORYWITHMAXIMISEDLOADEDCHILDREN).each(function(n) { // If we can find any collapsed ancestors, skip. if (n.ancestor(SELECTORS.CATEGORYWITHCOLLAPSEDLOADEDCHILDREN)) { return false; } foundmaximisedchildren = true; return true; }); if (foundmaximisedchildren) { // At least one maximised child found. Show the collapseall. togglelink.setHTML(M.util.get_string('collapseall', 'moodle')) .addClass(CSS.COLLAPSEALL) .removeClass(CSS.DISABLED); } else { // No maximised children found but there are collapsed children. Show the expandall. togglelink.setHTML(M.util.get_string('expandall', 'moodle')) .removeClass(CSS.COLLAPSEALL) .removeClass(CSS.DISABLED); } }; /** * Process the data returned by Y.io. * This includes appending it to the relevant part of the DOM, and applying our animations. * * @method process_results * @private * @param {String} tid The Transaction ID * @param {Object} response The Reponse returned by Y.IO * @param {Object} ioargs The additional arguments provided by Y.IO */ NS.process_results = function(tid, response, args) { var newnode, data; try { data = Y.JSON.parse(response.responseText); if (data.error) { return new M.core.ajaxException(data); } } catch (e) { return new M.core.exception(e); } // Insert the returned data into a new Node. newnode = Y.Node.create(data); // Append to the existing child location. args.childnode.appendChild(newnode); // Now that we have content, we can swap the classes on the toggled container. args.parentnode .addClass(CSS.LOADED) .removeClass(CSS.NOTLOADED); // Toggle the open/close status of the node now that it's content has been loaded. this.run_expansion(args.parentnode); // Remove the spinner now that we've started to show the content. if (args.spinner) { args.spinner.hide().destroy(); } }; /** * Add our animation to the Node. * * @method add_animation * @private * @param {Node} childnode */ NS.add_animation = function(childnode) { if (typeof childnode.fx !== "undefined") { // The animation has already been plugged to this node. return childnode; } childnode.plug(Y.Plugin.NodeFX, { from: { height: 0, opacity: 0 }, to: { // This sets a dynamic height in case the node content changes. height: function(node) { // Get expanded height (offsetHeight may be zero). return node.get('scrollHeight'); }, opacity: 1 }, duration: 0.2 }); return childnode; }; }, '@VERSION@', {"requires": ["node", "event-key"]}); moodle-course-util-section/moodle-course-util-section.js000064400000002343152266437370017507 0ustar00YUI.add('moodle-course-util-section', function (Y, NAME) { /** * A collection of utility classes for use with course sections. * * TODO: remove this module as part of MDL-83627. * * @module moodle-course-util * @submodule moodle-course-util-section */ Y.namespace('Moodle.core_course.util.section'); /** * A collection of utility classes for use with course sections. * * @class Moodle.core_course.util.section * @static */ Y.Moodle.core_course.util.section = { CONSTANTS: { SECTIONIDPREFIX: 'section-' }, /** * Determines the section ID for the provided section. * * @method getId * @param section {Node} The section to find an ID for. * @return {Number|false} The ID of the section in question or false if no ID was found. */ getId: function(section) { // We perform a simple substitution operation to get the ID. var id = section.get('id').replace( this.CONSTANTS.SECTIONIDPREFIX, ''); // Attempt to validate the ID. id = parseInt(id, 10); if (typeof id === 'number' && isFinite(id)) { return id; } return false; } }; }, '@VERSION@', {"requires": ["node", "moodle-course-util-base"]}); moodle-course-util-section/moodle-course-util-section-min.js000064400000000562152266437370020271 0ustar00YUI.add("moodle-course-util-section",function(e,o){e.namespace("Moodle.core_course.util.section"),e.Moodle.core_course.util.section={CONSTANTS:{SECTIONIDPREFIX:"section-"},getId:function(e){e=e.get("id").replace(this.CONSTANTS.SECTIONIDPREFIX,"");return!("number"!=typeof(e=parseInt(e,10))||!isFinite(e))&&e}}},"@VERSION@",{requires:["node","moodle-course-util-base"]});moodle-course-util-section/moodle-course-util-section-debug.js000064400000002603152266437370020572 0ustar00YUI.add('moodle-course-util-section', function (Y, NAME) { /** * A collection of utility classes for use with course sections. * * TODO: remove this module as part of MDL-83627. * * @module moodle-course-util * @submodule moodle-course-util-section */ Y.namespace('Moodle.core_course.util.section'); Y.log( 'YUI Moodle.core_course.util.cm is deprecated. Please, add support_components to your course format.', 'warn', 'moodle-course-coursebase' ); /** * A collection of utility classes for use with course sections. * * @class Moodle.core_course.util.section * @static */ Y.Moodle.core_course.util.section = { CONSTANTS: { SECTIONIDPREFIX: 'section-' }, /** * Determines the section ID for the provided section. * * @method getId * @param section {Node} The section to find an ID for. * @return {Number|false} The ID of the section in question or false if no ID was found. */ getId: function(section) { // We perform a simple substitution operation to get the ID. var id = section.get('id').replace( this.CONSTANTS.SECTIONIDPREFIX, ''); // Attempt to validate the ID. id = parseInt(id, 10); if (typeof id === 'number' && isFinite(id)) { return id; } return false; } }; }, '@VERSION@', {"requires": ["node", "moodle-course-util-base"]}); moodle-course-coursebase/moodle-course-coursebase-debug.js000064400000017125152266437370020027 0ustar00YUI.add('moodle-course-coursebase', function (Y, NAME) { /** * The coursebase class to provide shared functionality to Modules within * Moodle. * * TODO: remove this module as part of MDL-83627. * * @module moodle-course-coursebase */ var COURSEBASENAME = 'course-coursebase'; var COURSEBASE = function() { COURSEBASE.superclass.constructor.apply(this, arguments); }; Y.log( 'YUI moodle-course-coursebase is deprecated. Please, add support_components to your course format.', 'warn', 'moodle-course-coursebase' ); /** * The coursebase class to provide shared functionality to Modules within * Moodle. * * @class M.course.coursebase * @constructor */ Y.extend(COURSEBASE, Y.Base, { // Registered Modules registermodules: [], /** * Register a new Javascript Module * * @method register_module * @param {Object} The instantiated module to call functions on * @chainable */ register_module: function(object) { this.registermodules.push(object); return this; }, /** * Invoke the specified function in all registered modules with the given arguments * * @method invoke_function * @param {String} functionname The name of the function to call * @param {mixed} args The argument supplied to the function * @chainable */ invoke_function: function(functionname, args) { var module; for (module in this.registermodules) { if (functionname in this.registermodules[module]) { this.registermodules[module][functionname](args); } } return this; } }, { NAME: COURSEBASENAME, ATTRS: {} }); // Ensure that M.course exists and that coursebase is initialised correctly M.course = M.course || {}; M.course.coursebase = M.course.coursebase || new COURSEBASE(); // Abstract functions that needs to be defined per format (course/format/somename/format.js) M.course.format = M.course.format || {}; /** * Swap section (should be defined in format.js if requred) * * @method M.course.format.swap_sections * @param {YUI} Y YUI3 instance * @param {string} node1 node to swap to * @param {string} node2 node to swap with * @return {NodeList} section list */ M.course.format.swap_sections = M.course.format.swap_sections || function() { return null; }; /** * Process sections after ajax response (should be defined in format.js) * If some response is expected, we pass it over to format, as it knows better * hot to process it. * * @method M.course.format.process_sections * @param {YUI} Y YUI3 instance * @param {NodeList} list of sections * @param {array} response ajax response * @param {string} sectionfrom first affected section * @param {string} sectionto last affected section */ M.course.format.process_sections = M.course.format.process_sections || function() { return null; }; /** * Get sections config for this format, for examples see function definition * in the formats. * * @method M.course.format.get_config * @return {object} section list configuration */ M.course.format.get_config = M.course.format.get_config || function() { return { container_node: null, // compulsory container_class: null, // compulsory section_wrapper_node: null, // optional section_wrapper_class: null, // optional section_node: null, // compulsory section_class: null // compulsory }; }; /** * Get section list for this format (usually items inside container_node.container_class selector) * * @method M.course.format.get_section_selector * @param {YUI} Y YUI3 instance * @return {string} section selector */ M.course.format.get_section_selector = M.course.format.get_section_selector || function() { var config = M.course.format.get_config(); if (config.section_node && config.section_class) { return config.section_node + '.' + config.section_class; } Y.log('section_node and section_class are not defined in M.course.format.get_config', 'warn', 'moodle-course-coursebase'); return null; }; /** * Get section wraper for this format (only used in case when each * container_node.container_class node is wrapped in some other element). * * @method M.course.format.get_section_wrapper * @param {YUI} Y YUI3 instance * @return {string} section wrapper selector or M.course.format.get_section_selector * if section_wrapper_node and section_wrapper_class are not defined in the format config. */ M.course.format.get_section_wrapper = M.course.format.get_section_wrapper || function(Y) { var config = M.course.format.get_config(); if (config.section_wrapper_node && config.section_wrapper_class) { return config.section_wrapper_node + '.' + config.section_wrapper_class; } return M.course.format.get_section_selector(Y); }; /** * Get the tag of container node * * @method M.course.format.get_containernode * @return {string} tag of container node. */ M.course.format.get_containernode = M.course.format.get_containernode || function() { var config = M.course.format.get_config(); if (config.container_node) { return config.container_node; } else { Y.log('container_node is not defined in M.course.format.get_config', 'warn', 'moodle-course-coursebase'); } }; /** * Get the class of container node * * @method M.course.format.get_containerclass * @return {string} class of the container node. */ M.course.format.get_containerclass = M.course.format.get_containerclass || function() { var config = M.course.format.get_config(); if (config.container_class) { return config.container_class; } else { Y.log('container_class is not defined in M.course.format.get_config', 'warn', 'moodle-course-coursebase'); } }; /** * Get the tag of draggable node (section wrapper if exists, otherwise section) * * @method M.course.format.get_sectionwrappernode * @return {string} tag of the draggable node. */ M.course.format.get_sectionwrappernode = M.course.format.get_sectionwrappernode || function() { var config = M.course.format.get_config(); if (config.section_wrapper_node) { return config.section_wrapper_node; } else { return config.section_node; } }; /** * Get the class of draggable node (section wrapper if exists, otherwise section) * * @method M.course.format.get_sectionwrapperclass * @return {string} class of the draggable node. */ M.course.format.get_sectionwrapperclass = M.course.format.get_sectionwrapperclass || function() { var config = M.course.format.get_config(); if (config.section_wrapper_class) { return config.section_wrapper_class; } else { return config.section_class; } }; /** * Get the tag of section node * * @method M.course.format.get_sectionnode * @return {string} tag of section node. */ M.course.format.get_sectionnode = M.course.format.get_sectionnode || function() { var config = M.course.format.get_config(); if (config.section_node) { return config.section_node; } else { Y.log('section_node is not defined in M.course.format.get_config', 'warn', 'moodle-course-coursebase'); } }; /** * Get the class of section node * * @method M.course.format.get_sectionclass * @return {string} class of the section node. */ M.course.format.get_sectionclass = M.course.format.get_sectionclass || function() { var config = M.course.format.get_config(); if (config.section_class) { return config.section_class; } else { Y.log('section_class is not defined in M.course.format.get_config', 'warn', 'moodle-course-coursebase'); } }; }, '@VERSION@', {"requires": ["base", "node"]}); moodle-course-coursebase/moodle-course-coursebase.js000064400000015562152266437370016746 0ustar00YUI.add('moodle-course-coursebase', function (Y, NAME) { /** * The coursebase class to provide shared functionality to Modules within * Moodle. * * TODO: remove this module as part of MDL-83627. * * @module moodle-course-coursebase */ var COURSEBASENAME = 'course-coursebase'; var COURSEBASE = function() { COURSEBASE.superclass.constructor.apply(this, arguments); }; /** * The coursebase class to provide shared functionality to Modules within * Moodle. * * @class M.course.coursebase * @constructor */ Y.extend(COURSEBASE, Y.Base, { // Registered Modules registermodules: [], /** * Register a new Javascript Module * * @method register_module * @param {Object} The instantiated module to call functions on * @chainable */ register_module: function(object) { this.registermodules.push(object); return this; }, /** * Invoke the specified function in all registered modules with the given arguments * * @method invoke_function * @param {String} functionname The name of the function to call * @param {mixed} args The argument supplied to the function * @chainable */ invoke_function: function(functionname, args) { var module; for (module in this.registermodules) { if (functionname in this.registermodules[module]) { this.registermodules[module][functionname](args); } } return this; } }, { NAME: COURSEBASENAME, ATTRS: {} }); // Ensure that M.course exists and that coursebase is initialised correctly M.course = M.course || {}; M.course.coursebase = M.course.coursebase || new COURSEBASE(); // Abstract functions that needs to be defined per format (course/format/somename/format.js) M.course.format = M.course.format || {}; /** * Swap section (should be defined in format.js if requred) * * @method M.course.format.swap_sections * @param {YUI} Y YUI3 instance * @param {string} node1 node to swap to * @param {string} node2 node to swap with * @return {NodeList} section list */ M.course.format.swap_sections = M.course.format.swap_sections || function() { return null; }; /** * Process sections after ajax response (should be defined in format.js) * If some response is expected, we pass it over to format, as it knows better * hot to process it. * * @method M.course.format.process_sections * @param {YUI} Y YUI3 instance * @param {NodeList} list of sections * @param {array} response ajax response * @param {string} sectionfrom first affected section * @param {string} sectionto last affected section */ M.course.format.process_sections = M.course.format.process_sections || function() { return null; }; /** * Get sections config for this format, for examples see function definition * in the formats. * * @method M.course.format.get_config * @return {object} section list configuration */ M.course.format.get_config = M.course.format.get_config || function() { return { container_node: null, // compulsory container_class: null, // compulsory section_wrapper_node: null, // optional section_wrapper_class: null, // optional section_node: null, // compulsory section_class: null // compulsory }; }; /** * Get section list for this format (usually items inside container_node.container_class selector) * * @method M.course.format.get_section_selector * @param {YUI} Y YUI3 instance * @return {string} section selector */ M.course.format.get_section_selector = M.course.format.get_section_selector || function() { var config = M.course.format.get_config(); if (config.section_node && config.section_class) { return config.section_node + '.' + config.section_class; } return null; }; /** * Get section wraper for this format (only used in case when each * container_node.container_class node is wrapped in some other element). * * @method M.course.format.get_section_wrapper * @param {YUI} Y YUI3 instance * @return {string} section wrapper selector or M.course.format.get_section_selector * if section_wrapper_node and section_wrapper_class are not defined in the format config. */ M.course.format.get_section_wrapper = M.course.format.get_section_wrapper || function(Y) { var config = M.course.format.get_config(); if (config.section_wrapper_node && config.section_wrapper_class) { return config.section_wrapper_node + '.' + config.section_wrapper_class; } return M.course.format.get_section_selector(Y); }; /** * Get the tag of container node * * @method M.course.format.get_containernode * @return {string} tag of container node. */ M.course.format.get_containernode = M.course.format.get_containernode || function() { var config = M.course.format.get_config(); if (config.container_node) { return config.container_node; } else { } }; /** * Get the class of container node * * @method M.course.format.get_containerclass * @return {string} class of the container node. */ M.course.format.get_containerclass = M.course.format.get_containerclass || function() { var config = M.course.format.get_config(); if (config.container_class) { return config.container_class; } else { } }; /** * Get the tag of draggable node (section wrapper if exists, otherwise section) * * @method M.course.format.get_sectionwrappernode * @return {string} tag of the draggable node. */ M.course.format.get_sectionwrappernode = M.course.format.get_sectionwrappernode || function() { var config = M.course.format.get_config(); if (config.section_wrapper_node) { return config.section_wrapper_node; } else { return config.section_node; } }; /** * Get the class of draggable node (section wrapper if exists, otherwise section) * * @method M.course.format.get_sectionwrapperclass * @return {string} class of the draggable node. */ M.course.format.get_sectionwrapperclass = M.course.format.get_sectionwrapperclass || function() { var config = M.course.format.get_config(); if (config.section_wrapper_class) { return config.section_wrapper_class; } else { return config.section_class; } }; /** * Get the tag of section node * * @method M.course.format.get_sectionnode * @return {string} tag of section node. */ M.course.format.get_sectionnode = M.course.format.get_sectionnode || function() { var config = M.course.format.get_config(); if (config.section_node) { return config.section_node; } else { } }; /** * Get the class of section node * * @method M.course.format.get_sectionclass * @return {string} class of the section node. */ M.course.format.get_sectionclass = M.course.format.get_sectionclass || function() { var config = M.course.format.get_config(); if (config.section_class) { return config.section_class; } else { } }; }, '@VERSION@', {"requires": ["base", "node"]}); moodle-course-coursebase/moodle-course-coursebase-min.js000064400000004505152266437370017522 0ustar00YUI.add("moodle-course-coursebase",function(e,o){var r=function(){r.superclass.constructor.apply(this,arguments)};e.extend(r,e.Base,{registermodules:[],register_module:function(e){return this.registermodules.push(e),this},invoke_function:function(e,o){for(var r in this.registermodules)e in this.registermodules[r]&&this.registermodules[r][e](o);return this}},{NAME:"course-coursebase",ATTRS:{}}),M.course=M.course||{},M.course.coursebase=M.course.coursebase||new r,M.course.format=M.course.format||{},M.course.format.swap_sections=M.course.format.swap_sections||function(){return null},M.course.format.process_sections=M.course.format.process_sections||function(){return null},M.course.format.get_config=M.course.format.get_config||function(){return{container_node:null,container_class:null,section_wrapper_node:null,section_wrapper_class:null,section_node:null,section_class:null}},M.course.format.get_section_selector=M.course.format.get_section_selector||function(){var e=M.course.format.get_config();return e.section_node&&e.section_class?e.section_node+"."+e.section_class:null},M.course.format.get_section_wrapper=M.course.format.get_section_wrapper||function(e){var o=M.course.format.get_config();return o.section_wrapper_node&&o.section_wrapper_class?o.section_wrapper_node+"."+o.section_wrapper_class:M.course.format.get_section_selector(e)},M.course.format.get_containernode=M.course.format.get_containernode||function(){var e=M.course.format.get_config();if(e.container_node)return e.container_node},M.course.format.get_containerclass=M.course.format.get_containerclass||function(){var e=M.course.format.get_config();if(e.container_class)return e.container_class},M.course.format.get_sectionwrappernode=M.course.format.get_sectionwrappernode||function(){var e=M.course.format.get_config();return e.section_wrapper_node||e.section_node},M.course.format.get_sectionwrapperclass=M.course.format.get_sectionwrapperclass||function(){var e=M.course.format.get_config();return e.section_wrapper_class||e.section_class},M.course.format.get_sectionnode=M.course.format.get_sectionnode||function(){var e=M.course.format.get_config();if(e.section_node)return e.section_node},M.course.format.get_sectionclass=M.course.format.get_sectionclass||function(){var e=M.course.format.get_config();if(e.section_class)return e.section_class}},"@VERSION@",{requires:["base","node"]});moodle-course-util-cm/moodle-course-util-cm.js000064400000004335152266437370015400 0ustar00YUI.add('moodle-course-util-cm', function (Y, NAME) { /** * A collection of utility classes for use with course modules. * * TODO: remove this module as part of MDL-83627. * * @module moodle-course-util * @submodule moodle-course-util-cm */ Y.namespace('Moodle.core_course.util.cm'); /** * A collection of utility classes for use with course modules. * * @class Moodle.core_course.util.cm * @static */ Y.Moodle.core_course.util.cm = { CONSTANTS: { MODULEIDPREFIX: 'module-' }, SELECTORS: { COURSEMODULE: '.activity', INSTANCENAME: '.instancename' }, /** * Retrieve the course module item from one of it's child Nodes. * * @method getCourseModuleNodeFromComponent * @param coursemodulecomponent {Node} The component Node. * @return {Node|null} The Course Module Node. */ getCourseModuleFromComponent: function(coursemodulecomponent) { return Y.one(coursemodulecomponent).ancestor(this.SELECTORS.COURSEMODULE, true); }, /** * Determines the section ID for the provided section. * * @method getId * @param coursemodule {Node} The course module to find an ID for. * @return {Number|false} The ID of the course module in question or false if no ID was found. */ getId: function(coursemodule) { // We perform a simple substitution operation to get the ID. var id = coursemodule.get('id').replace( this.CONSTANTS.MODULEIDPREFIX, ''); // Attempt to validate the ID. id = parseInt(id, 10); if (typeof id === 'number' && isFinite(id)) { return id; } return false; }, /** * Determines the section ID for the provided section. * * @method getName * @param coursemodule {Node} The course module to find an ID for. * @return {Number|false} The ID of the course module in question or false if no ID was found. */ getName: function(coursemodule) { var instance = coursemodule.one(this.SELECTORS.INSTANCENAME); if (instance) { return instance.get('firstChild').get('data'); } return null; } }; }, '@VERSION@', {"requires": ["node", "moodle-course-util-base"]}); moodle-course-util-cm/moodle-course-util-cm-min.js000064400000001155152266437370016156 0ustar00YUI.add("moodle-course-util-cm",function(t,e){t.namespace("Moodle.core_course.util.cm"),t.Moodle.core_course.util.cm={CONSTANTS:{MODULEIDPREFIX:"module-"},SELECTORS:{COURSEMODULE:".activity",INSTANCENAME:".instancename"},getCourseModuleFromComponent:function(e){return t.one(e).ancestor(this.SELECTORS.COURSEMODULE,!0)},getId:function(e){e=e.get("id").replace(this.CONSTANTS.MODULEIDPREFIX,"");return!("number"!=typeof(e=parseInt(e,10))||!isFinite(e))&&e},getName:function(e){e=e.one(this.SELECTORS.INSTANCENAME);return e?e.get("firstChild").get("data"):null}}},"@VERSION@",{requires:["node","moodle-course-util-base"]});moodle-course-util-cm/moodle-course-util-cm-debug.js000064400000004575152266437370016472 0ustar00YUI.add('moodle-course-util-cm', function (Y, NAME) { /** * A collection of utility classes for use with course modules. * * TODO: remove this module as part of MDL-83627. * * @module moodle-course-util * @submodule moodle-course-util-cm */ Y.namespace('Moodle.core_course.util.cm'); Y.log( 'YUI Moodle.core_course.util.cm is deprecated. Please, add support_components to your course format.', 'warn', 'moodle-course-coursebase' ); /** * A collection of utility classes for use with course modules. * * @class Moodle.core_course.util.cm * @static */ Y.Moodle.core_course.util.cm = { CONSTANTS: { MODULEIDPREFIX: 'module-' }, SELECTORS: { COURSEMODULE: '.activity', INSTANCENAME: '.instancename' }, /** * Retrieve the course module item from one of it's child Nodes. * * @method getCourseModuleNodeFromComponent * @param coursemodulecomponent {Node} The component Node. * @return {Node|null} The Course Module Node. */ getCourseModuleFromComponent: function(coursemodulecomponent) { return Y.one(coursemodulecomponent).ancestor(this.SELECTORS.COURSEMODULE, true); }, /** * Determines the section ID for the provided section. * * @method getId * @param coursemodule {Node} The course module to find an ID for. * @return {Number|false} The ID of the course module in question or false if no ID was found. */ getId: function(coursemodule) { // We perform a simple substitution operation to get the ID. var id = coursemodule.get('id').replace( this.CONSTANTS.MODULEIDPREFIX, ''); // Attempt to validate the ID. id = parseInt(id, 10); if (typeof id === 'number' && isFinite(id)) { return id; } return false; }, /** * Determines the section ID for the provided section. * * @method getName * @param coursemodule {Node} The course module to find an ID for. * @return {Number|false} The ID of the course module in question or false if no ID was found. */ getName: function(coursemodule) { var instance = coursemodule.one(this.SELECTORS.INSTANCENAME); if (instance) { return instance.get('firstChild').get('data'); } return null; } }; }, '@VERSION@', {"requires": ["node", "moodle-course-util-base"]}); moodle-course-dragdrop/moodle-course-dragdrop-debug.js000064400000053057152266437370017151 0ustar00YUI.add('moodle-course-dragdrop', function (Y, NAME) { /* eslint-disable no-unused-vars */ /** * Drag and Drop for course sections and course modules. * * TODO: remove this module as part of MDL-83627. * * @module moodle-course-dragdrop */ Y.log( 'YUI moodle-course-dragdrop is deprecated. Please, add support_components to your course format.', 'warn', 'moodle-course-coursebase' ); var CSS = { ACTIONAREA: '.actions', ACTIVITY: 'activity', ACTIVITYINSTANCE: 'activityinstance', CONTENT: 'content', COURSECONTENT: 'course-content', EDITINGMOVE: 'editing_move', ICONCLASS: 'iconsmall', JUMPMENU: 'jumpmenu', LEFT: 'left', LIGHTBOX: 'lightbox', MOVEDOWN: 'movedown', MOVEUP: 'moveup', PAGECONTENT: 'page-content', RIGHT: 'right', SECTION: 'section', SECTIONADDMENUS: 'section_add_menus', SECTIONHANDLE: 'section-handle', SUMMARY: 'summary', SECTIONDRAGGABLE: 'sectiondraggable' }; M.course = M.course || {}; /** * Section drag and drop. * * TODO: remove this module as part of MDL-83627. * * @class M.course.dragdrop.section * @constructor * @extends M.core.dragdrop */ var DRAGSECTION = function() { DRAGSECTION.superclass.constructor.apply(this, arguments); }; Y.log( 'YUI M.course.dragdrop.section is deprecated. Please, add support_components to your course format.', 'warn', 'moodle-course-coursebase' ); Y.extend(DRAGSECTION, M.core.dragdrop, { sectionlistselector: null, initializer: function() { // Set group for parent class this.groups = [CSS.SECTIONDRAGGABLE]; this.samenodeclass = M.course.format.get_sectionwrapperclass(); this.parentnodeclass = M.course.format.get_containerclass(); // Detect the direction of travel. this.detectkeyboarddirection = true; // Check if we are in single section mode if (Y.Node.one('.' + CSS.JUMPMENU)) { return false; } // Initialise sections dragging this.sectionlistselector = M.course.format.get_section_wrapper(Y); if (this.sectionlistselector) { this.sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + this.sectionlistselector; this.setup_for_section(this.sectionlistselector); // Make each li element in the lists of sections draggable var del = new Y.DD.Delegate({ container: '.' + CSS.COURSECONTENT, nodes: '.' + CSS.SECTIONDRAGGABLE, target: true, handles: ['.' + CSS.LEFT], dragConfig: {groups: this.groups} }); del.dd.plug(Y.Plugin.DDProxy, { // Don't move the node at the end of the drag moveOnEnd: false }); del.dd.plug(Y.Plugin.DDConstrained, { // Keep it inside the .course-content constrain: '#' + CSS.PAGECONTENT, stickY: true }); del.dd.plug(Y.Plugin.DDWinScroll); } }, /** * Apply dragdrop features to the specified selector or node that refers to section(s) * * @method setup_for_section * @param {String} baseselector The CSS selector or node to limit scope to */ setup_for_section: function(baseselector) { Y.Node.all(baseselector).each(function(sectionnode) { // Determine the section ID var sectionid = Y.Moodle.core_course.util.section.getId(sectionnode); // We skip the top section as it is not draggable if (sectionid > 0) { // Remove move icons var movedown = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEDOWN); var moveup = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEUP); // Add dragger icon var title = M.util.get_string('movesection', 'moodle', sectionid); var cssleft = sectionnode.one('.' + CSS.LEFT); if ((movedown || moveup) && cssleft) { cssleft.setStyle('cursor', 'move'); cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true)); if (moveup) { if (moveup.previous('br')) { moveup.previous('br').remove(); } else if (moveup.next('br')) { moveup.next('br').remove(); } if (moveup.ancestor('.section_action_menu') && moveup.ancestor().get('nodeName').toLowerCase() == 'li') { moveup.ancestor().remove(); } else { moveup.remove(); } } if (movedown) { if (movedown.previous('br')) { movedown.previous('br').remove(); } else if (movedown.next('br')) { movedown.next('br').remove(); } var movedownParentType = movedown.ancestor().get('nodeName').toLowerCase(); if (movedown.ancestor('.section_action_menu') && movedownParentType == 'li') { movedown.ancestor().remove(); } else { movedown.remove(); } } // This section can be moved - add the class to indicate this to Y.DD. sectionnode.addClass(CSS.SECTIONDRAGGABLE); } } }, this); }, /* * Drag-dropping related functions */ drag_start: function(e) { // Get our drag object var drag = e.target; // This is the node that the user started to drag. var node = drag.get('node'); // This is the container node that will follow the mouse around, // or during a keyboard drag and drop the original node. var dragnode = drag.get('dragNode'); if (node === dragnode) { return; } // Creat a dummy structure of the outer elemnents for clean styles application var containernode = Y.Node.create('<' + M.course.format.get_containernode() + '>'); containernode.addClass(M.course.format.get_containerclass()); var sectionnode = Y.Node.create('<' + M.course.format.get_sectionwrappernode() + '>'); sectionnode.addClass(M.course.format.get_sectionwrapperclass()); sectionnode.setStyle('margin', 0); sectionnode.setContent(node.get('innerHTML')); containernode.appendChild(sectionnode); dragnode.setContent(containernode); dragnode.addClass(CSS.COURSECONTENT); }, drag_dropmiss: function(e) { // Missed the target, but we assume the user intended to drop it // on the last last ghost node location, e.drag and e.drop should be // prepared by global_drag_dropmiss parent so simulate drop_hit(e). this.drop_hit(e); }, get_section_index: function(node) { var sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + M.course.format.get_section_selector(Y), sectionList = Y.all(sectionlistselector), nodeIndex = sectionList.indexOf(node), zeroIndex = sectionList.indexOf(Y.one('#section-0')); return (nodeIndex - zeroIndex); }, drop_hit: function(e) { var drag = e.drag; // Get references to our nodes and their IDs. var dragnode = drag.get('node'), dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode), loopstart = dragnodeid, dropnodeindex = this.get_section_index(dragnode), loopend = dropnodeindex; if (dragnodeid === dropnodeindex) { Y.log("Skipping move - same location moving " + dragnodeid + " to " + dropnodeindex, 'debug', 'moodle-course-dragdrop'); return; } Y.log("Moving from position " + dragnodeid + " to position " + dropnodeindex, 'debug', 'moodle-course-dragdrop'); if (loopstart > loopend) { // If we're going up, we need to swap the loop order // because loops can't go backwards. loopstart = dropnodeindex; loopend = dragnodeid; } // Get the list of nodes. drag.get('dragNode').removeClass(CSS.COURSECONTENT); var sectionlist = Y.Node.all(this.sectionlistselector); // Add a lightbox if it's not there. var lightbox = M.util.add_lightbox(Y, dragnode); // Handle any variables which we must pass via AJAX. var params = {}, pageparams = this.get('config').pageparams, varname; for (varname in pageparams) { if (!pageparams.hasOwnProperty(varname)) { continue; } params[varname] = pageparams[varname]; } // Prepare request parameters params.sesskey = M.cfg.sesskey; params.courseId = this.get('courseid'); params['class'] = 'section'; params.field = 'move'; params.id = dragnodeid; params.value = dropnodeindex; // Perform the AJAX request. var uri = M.cfg.wwwroot + this.get('ajaxurl'); Y.io(uri, { method: 'POST', data: params, on: { start: function() { lightbox.show(); }, success: function(tid, response) { // Update section titles, we can't simply swap them as // they might have custom title try { var responsetext = Y.JSON.parse(response.responseText); if (responsetext.error) { new M.core.ajaxException(responsetext); } M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend); } catch (e) { // Ignore. } // Update all of the section IDs - first unset them, then set them // to avoid duplicates in the DOM. var index; // Classic bubble sort algorithm is applied to the section // nodes between original drag node location and the new one. var swapped = false; do { swapped = false; for (index = loopstart; index <= loopend; index++) { if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) > Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) { Y.log("Swapping " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) + " with " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index))); // Swap section id. var sectionid = sectionlist.item(index - 1).get('id'); sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id')); sectionlist.item(index).set('id', sectionid); // See what format needs to swap. M.course.format.swap_sections(Y, index - 1, index); // Update flag. swapped = true; } sectionlist.item(index).setAttribute('data-sectionid', Y.Moodle.core_course.util.section.getId(sectionlist.item(index))); } loopend = loopend - 1; } while (swapped); window.setTimeout(function() { lightbox.hide(); }, 250); // Update course state. M.course.coursebase.invoke_function('updateMovedSectionState'); }, failure: function(tid, response) { this.ajax_failure(response); lightbox.hide(); } }, context: this }); } }, { NAME: 'course-dragdrop-section', ATTRS: { courseid: { value: null }, ajaxurl: { value: 0 }, config: { value: 0 } } }); M.course = M.course || {}; M.course.init_section_dragdrop = function(params) { new DRAGSECTION(params); }; /** * Resource drag and drop. * * TODO: remove this module as part of MDL-83627. * * @class M.course.dragdrop.resource * @constructor * @extends M.core.dragdrop */ var DRAGRESOURCE = function() { DRAGRESOURCE.superclass.constructor.apply(this, arguments); }; Y.log( 'YUI M.course.dragdrop.resource is deprecated. Please, add support_components to your course format.', 'warn', 'moodle-course-coursebase' ); Y.extend(DRAGRESOURCE, M.core.dragdrop, { initializer: function() { // Set group for parent class this.groups = ['resource']; this.samenodeclass = CSS.ACTIVITY; this.parentnodeclass = CSS.SECTION; this.samenodelabel = { identifier: 'afterresource', component: 'moodle' }; this.parentnodelabel = { identifier: 'totopofsection', component: 'moodle' }; // Go through all sections var sectionlistselector = M.course.format.get_section_selector(Y); if (sectionlistselector) { sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + sectionlistselector; this.setup_for_section(sectionlistselector); // Initialise drag & drop for all resources/activities var nodeselector = sectionlistselector.slice(CSS.COURSECONTENT.length + 2) + ' li.' + CSS.ACTIVITY; var del = new Y.DD.Delegate({ container: '.' + CSS.COURSECONTENT, nodes: nodeselector, target: true, handles: ['.' + CSS.EDITINGMOVE], dragConfig: {groups: this.groups} }); del.dd.plug(Y.Plugin.DDProxy, { // Don't move the node at the end of the drag moveOnEnd: false, cloneNode: true }); del.dd.plug(Y.Plugin.DDConstrained, { // Keep it inside the .course-content constrain: '#' + CSS.PAGECONTENT }); del.dd.plug(Y.Plugin.DDWinScroll); M.course.coursebase.register_module(this); M.course.dragres = this; } }, /** * Apply dragdrop features to the specified selector or node that refers to section(s) * * @method setup_for_section * @param {String} baseselector The CSS selector or node to limit scope to */ setup_for_section: function(baseselector) { Y.Node.all(baseselector).each(function(sectionnode) { var resources = sectionnode.one('.' + CSS.CONTENT + ' ul.' + CSS.SECTION); // See if resources ul exists, if not create one if (!resources) { resources = Y.Node.create('
    '); resources.addClass(CSS.SECTION); sectionnode.one('.' + CSS.CONTENT + ' div.' + CSS.SUMMARY).insert(resources, 'after'); } resources.setAttribute('data-draggroups', this.groups.join(' ')); // Define empty ul as droptarget, so that item could be moved to empty list new Y.DD.Drop({ node: resources, groups: this.groups, padding: '20 0 20 0' }); // Initialise each resource/activity in this section this.setup_for_resource('#' + sectionnode.get('id') + ' li.' + CSS.ACTIVITY); }, this); }, /** * Apply dragdrop features to the specified selector or node that refers to resource(s) * * @method setup_for_resource * @param {String} baseselector The CSS selector or node to limit scope to */ setup_for_resource: function(baseselector) { Y.Node.all(baseselector).each(function(resourcesnode) { var draggroups = resourcesnode.getData('draggroups'); if (!draggroups) { // This Drop Node has not been set up. Configure it now. resourcesnode.setAttribute('data-draggroups', this.groups.join(' ')); // Define empty ul as droptarget, so that item could be moved to empty list new Y.DD.Drop({ node: resourcesnode, groups: this.groups, padding: '20 0 20 0' }); } // Replace move icons var move = resourcesnode.one('a.' + CSS.EDITINGMOVE); if (move) { var sr = move.getData('sectionreturn'); move.replace(this.get_drag_handle(M.util.get_string('movecoursemodule', 'moodle'), CSS.EDITINGMOVE, CSS.ICONCLASS, true).setAttribute('data-sectionreturn', sr)); } }, this); }, drag_start: function(e) { // Get our drag object var drag = e.target; if (drag.get('dragNode') === drag.get('node')) { // We do not want to modify the contents of the real node. // They will be the same during a keyboard drag and drop. return; } drag.get('dragNode').setContent(drag.get('node').get('innerHTML')); drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline'); }, drag_dropmiss: function(e) { // Missed the target, but we assume the user intended to drop it // on the last last ghost node location, e.drag and e.drop should be // prepared by global_drag_dropmiss parent so simulate drop_hit(e). this.drop_hit(e); }, drop_hit: function(e) { var drag = e.drag; // Get a reference to our drag node var dragnode = drag.get('node'); var dropnode = e.drop.get('node'); // Add spinner if it not there var actionarea = dragnode.one(CSS.ACTIONAREA); var spinner = M.util.add_spinner(Y, actionarea); var params = {}; // Handle any variables which we must pass back through to var pageparams = this.get('config').pageparams; var varname; for (varname in pageparams) { params[varname] = pageparams[varname]; } // Variables needed to update the course state. var cmid = Number(Y.Moodle.core_course.util.cm.getId(dragnode)); var beforeid = null; // Prepare request parameters params.sesskey = M.cfg.sesskey; params.courseId = this.get('courseid'); params['class'] = 'resource'; params.field = 'move'; params.id = cmid; params.sectionId = Y.Moodle.core_course.util.section.getId(dropnode.ancestor(M.course.format.get_section_wrapper(Y), true)); if (dragnode.next()) { beforeid = Number(Y.Moodle.core_course.util.cm.getId(dragnode.next())); params.beforeId = beforeid; } // Do AJAX request var uri = M.cfg.wwwroot + this.get('ajaxurl'); Y.io(uri, { method: 'POST', data: params, on: { start: function() { this.lock_drag_handle(drag, CSS.EDITINGMOVE); spinner.show(); }, success: function(tid, response) { var responsetext = Y.JSON.parse(response.responseText); // Update course state. M.course.coursebase.invoke_function( 'updateMovedCmState', { cmid: cmid, beforeid: beforeid, visible: responsetext.visible, } ); // Set visibility in course content. var params = {element: dragnode, visible: responsetext.visible}; M.course.coursebase.invoke_function('set_visibility_resource_ui', params); this.unlock_drag_handle(drag, CSS.EDITINGMOVE); window.setTimeout(function() { spinner.hide(); }, 250); }, failure: function(tid, response) { this.ajax_failure(response); this.unlock_drag_handle(drag, CSS.SECTIONHANDLE); spinner.hide(); // TODO: revert nodes location } }, context: this }); } }, { NAME: 'course-dragdrop-resource', ATTRS: { courseid: { value: null }, ajaxurl: { value: 0 }, config: { value: 0 } } }); M.course = M.course || {}; M.course.init_resource_dragdrop = function(params) { new DRAGRESOURCE(params); }; }, '@VERSION@', { "requires": [ "base", "node", "io", "dom", "dd", "dd-scroll", "moodle-core-dragdrop", "moodle-core-notification", "moodle-course-coursebase", "moodle-course-util" ] }); moodle-course-dragdrop/moodle-course-dragdrop.js000064400000051144152266437370016060 0ustar00YUI.add('moodle-course-dragdrop', function (Y, NAME) { /* eslint-disable no-unused-vars */ /** * Drag and Drop for course sections and course modules. * * TODO: remove this module as part of MDL-83627. * * @module moodle-course-dragdrop */ var CSS = { ACTIONAREA: '.actions', ACTIVITY: 'activity', ACTIVITYINSTANCE: 'activityinstance', CONTENT: 'content', COURSECONTENT: 'course-content', EDITINGMOVE: 'editing_move', ICONCLASS: 'iconsmall', JUMPMENU: 'jumpmenu', LEFT: 'left', LIGHTBOX: 'lightbox', MOVEDOWN: 'movedown', MOVEUP: 'moveup', PAGECONTENT: 'page-content', RIGHT: 'right', SECTION: 'section', SECTIONADDMENUS: 'section_add_menus', SECTIONHANDLE: 'section-handle', SUMMARY: 'summary', SECTIONDRAGGABLE: 'sectiondraggable' }; M.course = M.course || {}; /** * Section drag and drop. * * TODO: remove this module as part of MDL-83627. * * @class M.course.dragdrop.section * @constructor * @extends M.core.dragdrop */ var DRAGSECTION = function() { DRAGSECTION.superclass.constructor.apply(this, arguments); }; Y.extend(DRAGSECTION, M.core.dragdrop, { sectionlistselector: null, initializer: function() { // Set group for parent class this.groups = [CSS.SECTIONDRAGGABLE]; this.samenodeclass = M.course.format.get_sectionwrapperclass(); this.parentnodeclass = M.course.format.get_containerclass(); // Detect the direction of travel. this.detectkeyboarddirection = true; // Check if we are in single section mode if (Y.Node.one('.' + CSS.JUMPMENU)) { return false; } // Initialise sections dragging this.sectionlistselector = M.course.format.get_section_wrapper(Y); if (this.sectionlistselector) { this.sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + this.sectionlistselector; this.setup_for_section(this.sectionlistselector); // Make each li element in the lists of sections draggable var del = new Y.DD.Delegate({ container: '.' + CSS.COURSECONTENT, nodes: '.' + CSS.SECTIONDRAGGABLE, target: true, handles: ['.' + CSS.LEFT], dragConfig: {groups: this.groups} }); del.dd.plug(Y.Plugin.DDProxy, { // Don't move the node at the end of the drag moveOnEnd: false }); del.dd.plug(Y.Plugin.DDConstrained, { // Keep it inside the .course-content constrain: '#' + CSS.PAGECONTENT, stickY: true }); del.dd.plug(Y.Plugin.DDWinScroll); } }, /** * Apply dragdrop features to the specified selector or node that refers to section(s) * * @method setup_for_section * @param {String} baseselector The CSS selector or node to limit scope to */ setup_for_section: function(baseselector) { Y.Node.all(baseselector).each(function(sectionnode) { // Determine the section ID var sectionid = Y.Moodle.core_course.util.section.getId(sectionnode); // We skip the top section as it is not draggable if (sectionid > 0) { // Remove move icons var movedown = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEDOWN); var moveup = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEUP); // Add dragger icon var title = M.util.get_string('movesection', 'moodle', sectionid); var cssleft = sectionnode.one('.' + CSS.LEFT); if ((movedown || moveup) && cssleft) { cssleft.setStyle('cursor', 'move'); cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true)); if (moveup) { if (moveup.previous('br')) { moveup.previous('br').remove(); } else if (moveup.next('br')) { moveup.next('br').remove(); } if (moveup.ancestor('.section_action_menu') && moveup.ancestor().get('nodeName').toLowerCase() == 'li') { moveup.ancestor().remove(); } else { moveup.remove(); } } if (movedown) { if (movedown.previous('br')) { movedown.previous('br').remove(); } else if (movedown.next('br')) { movedown.next('br').remove(); } var movedownParentType = movedown.ancestor().get('nodeName').toLowerCase(); if (movedown.ancestor('.section_action_menu') && movedownParentType == 'li') { movedown.ancestor().remove(); } else { movedown.remove(); } } // This section can be moved - add the class to indicate this to Y.DD. sectionnode.addClass(CSS.SECTIONDRAGGABLE); } } }, this); }, /* * Drag-dropping related functions */ drag_start: function(e) { // Get our drag object var drag = e.target; // This is the node that the user started to drag. var node = drag.get('node'); // This is the container node that will follow the mouse around, // or during a keyboard drag and drop the original node. var dragnode = drag.get('dragNode'); if (node === dragnode) { return; } // Creat a dummy structure of the outer elemnents for clean styles application var containernode = Y.Node.create('<' + M.course.format.get_containernode() + '>'); containernode.addClass(M.course.format.get_containerclass()); var sectionnode = Y.Node.create('<' + M.course.format.get_sectionwrappernode() + '>'); sectionnode.addClass(M.course.format.get_sectionwrapperclass()); sectionnode.setStyle('margin', 0); sectionnode.setContent(node.get('innerHTML')); containernode.appendChild(sectionnode); dragnode.setContent(containernode); dragnode.addClass(CSS.COURSECONTENT); }, drag_dropmiss: function(e) { // Missed the target, but we assume the user intended to drop it // on the last last ghost node location, e.drag and e.drop should be // prepared by global_drag_dropmiss parent so simulate drop_hit(e). this.drop_hit(e); }, get_section_index: function(node) { var sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + M.course.format.get_section_selector(Y), sectionList = Y.all(sectionlistselector), nodeIndex = sectionList.indexOf(node), zeroIndex = sectionList.indexOf(Y.one('#section-0')); return (nodeIndex - zeroIndex); }, drop_hit: function(e) { var drag = e.drag; // Get references to our nodes and their IDs. var dragnode = drag.get('node'), dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode), loopstart = dragnodeid, dropnodeindex = this.get_section_index(dragnode), loopend = dropnodeindex; if (dragnodeid === dropnodeindex) { return; } if (loopstart > loopend) { // If we're going up, we need to swap the loop order // because loops can't go backwards. loopstart = dropnodeindex; loopend = dragnodeid; } // Get the list of nodes. drag.get('dragNode').removeClass(CSS.COURSECONTENT); var sectionlist = Y.Node.all(this.sectionlistselector); // Add a lightbox if it's not there. var lightbox = M.util.add_lightbox(Y, dragnode); // Handle any variables which we must pass via AJAX. var params = {}, pageparams = this.get('config').pageparams, varname; for (varname in pageparams) { if (!pageparams.hasOwnProperty(varname)) { continue; } params[varname] = pageparams[varname]; } // Prepare request parameters params.sesskey = M.cfg.sesskey; params.courseId = this.get('courseid'); params['class'] = 'section'; params.field = 'move'; params.id = dragnodeid; params.value = dropnodeindex; // Perform the AJAX request. var uri = M.cfg.wwwroot + this.get('ajaxurl'); Y.io(uri, { method: 'POST', data: params, on: { start: function() { lightbox.show(); }, success: function(tid, response) { // Update section titles, we can't simply swap them as // they might have custom title try { var responsetext = Y.JSON.parse(response.responseText); if (responsetext.error) { new M.core.ajaxException(responsetext); } M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend); } catch (e) { // Ignore. } // Update all of the section IDs - first unset them, then set them // to avoid duplicates in the DOM. var index; // Classic bubble sort algorithm is applied to the section // nodes between original drag node location and the new one. var swapped = false; do { swapped = false; for (index = loopstart; index <= loopend; index++) { if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) > Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) { // Swap section id. var sectionid = sectionlist.item(index - 1).get('id'); sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id')); sectionlist.item(index).set('id', sectionid); // See what format needs to swap. M.course.format.swap_sections(Y, index - 1, index); // Update flag. swapped = true; } sectionlist.item(index).setAttribute('data-sectionid', Y.Moodle.core_course.util.section.getId(sectionlist.item(index))); } loopend = loopend - 1; } while (swapped); window.setTimeout(function() { lightbox.hide(); }, 250); // Update course state. M.course.coursebase.invoke_function('updateMovedSectionState'); }, failure: function(tid, response) { this.ajax_failure(response); lightbox.hide(); } }, context: this }); } }, { NAME: 'course-dragdrop-section', ATTRS: { courseid: { value: null }, ajaxurl: { value: 0 }, config: { value: 0 } } }); M.course = M.course || {}; M.course.init_section_dragdrop = function(params) { new DRAGSECTION(params); }; /** * Resource drag and drop. * * TODO: remove this module as part of MDL-83627. * * @class M.course.dragdrop.resource * @constructor * @extends M.core.dragdrop */ var DRAGRESOURCE = function() { DRAGRESOURCE.superclass.constructor.apply(this, arguments); }; Y.extend(DRAGRESOURCE, M.core.dragdrop, { initializer: function() { // Set group for parent class this.groups = ['resource']; this.samenodeclass = CSS.ACTIVITY; this.parentnodeclass = CSS.SECTION; this.samenodelabel = { identifier: 'afterresource', component: 'moodle' }; this.parentnodelabel = { identifier: 'totopofsection', component: 'moodle' }; // Go through all sections var sectionlistselector = M.course.format.get_section_selector(Y); if (sectionlistselector) { sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + sectionlistselector; this.setup_for_section(sectionlistselector); // Initialise drag & drop for all resources/activities var nodeselector = sectionlistselector.slice(CSS.COURSECONTENT.length + 2) + ' li.' + CSS.ACTIVITY; var del = new Y.DD.Delegate({ container: '.' + CSS.COURSECONTENT, nodes: nodeselector, target: true, handles: ['.' + CSS.EDITINGMOVE], dragConfig: {groups: this.groups} }); del.dd.plug(Y.Plugin.DDProxy, { // Don't move the node at the end of the drag moveOnEnd: false, cloneNode: true }); del.dd.plug(Y.Plugin.DDConstrained, { // Keep it inside the .course-content constrain: '#' + CSS.PAGECONTENT }); del.dd.plug(Y.Plugin.DDWinScroll); M.course.coursebase.register_module(this); M.course.dragres = this; } }, /** * Apply dragdrop features to the specified selector or node that refers to section(s) * * @method setup_for_section * @param {String} baseselector The CSS selector or node to limit scope to */ setup_for_section: function(baseselector) { Y.Node.all(baseselector).each(function(sectionnode) { var resources = sectionnode.one('.' + CSS.CONTENT + ' ul.' + CSS.SECTION); // See if resources ul exists, if not create one if (!resources) { resources = Y.Node.create('
      '); resources.addClass(CSS.SECTION); sectionnode.one('.' + CSS.CONTENT + ' div.' + CSS.SUMMARY).insert(resources, 'after'); } resources.setAttribute('data-draggroups', this.groups.join(' ')); // Define empty ul as droptarget, so that item could be moved to empty list new Y.DD.Drop({ node: resources, groups: this.groups, padding: '20 0 20 0' }); // Initialise each resource/activity in this section this.setup_for_resource('#' + sectionnode.get('id') + ' li.' + CSS.ACTIVITY); }, this); }, /** * Apply dragdrop features to the specified selector or node that refers to resource(s) * * @method setup_for_resource * @param {String} baseselector The CSS selector or node to limit scope to */ setup_for_resource: function(baseselector) { Y.Node.all(baseselector).each(function(resourcesnode) { var draggroups = resourcesnode.getData('draggroups'); if (!draggroups) { // This Drop Node has not been set up. Configure it now. resourcesnode.setAttribute('data-draggroups', this.groups.join(' ')); // Define empty ul as droptarget, so that item could be moved to empty list new Y.DD.Drop({ node: resourcesnode, groups: this.groups, padding: '20 0 20 0' }); } // Replace move icons var move = resourcesnode.one('a.' + CSS.EDITINGMOVE); if (move) { var sr = move.getData('sectionreturn'); move.replace(this.get_drag_handle(M.util.get_string('movecoursemodule', 'moodle'), CSS.EDITINGMOVE, CSS.ICONCLASS, true).setAttribute('data-sectionreturn', sr)); } }, this); }, drag_start: function(e) { // Get our drag object var drag = e.target; if (drag.get('dragNode') === drag.get('node')) { // We do not want to modify the contents of the real node. // They will be the same during a keyboard drag and drop. return; } drag.get('dragNode').setContent(drag.get('node').get('innerHTML')); drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline'); }, drag_dropmiss: function(e) { // Missed the target, but we assume the user intended to drop it // on the last last ghost node location, e.drag and e.drop should be // prepared by global_drag_dropmiss parent so simulate drop_hit(e). this.drop_hit(e); }, drop_hit: function(e) { var drag = e.drag; // Get a reference to our drag node var dragnode = drag.get('node'); var dropnode = e.drop.get('node'); // Add spinner if it not there var actionarea = dragnode.one(CSS.ACTIONAREA); var spinner = M.util.add_spinner(Y, actionarea); var params = {}; // Handle any variables which we must pass back through to var pageparams = this.get('config').pageparams; var varname; for (varname in pageparams) { params[varname] = pageparams[varname]; } // Variables needed to update the course state. var cmid = Number(Y.Moodle.core_course.util.cm.getId(dragnode)); var beforeid = null; // Prepare request parameters params.sesskey = M.cfg.sesskey; params.courseId = this.get('courseid'); params['class'] = 'resource'; params.field = 'move'; params.id = cmid; params.sectionId = Y.Moodle.core_course.util.section.getId(dropnode.ancestor(M.course.format.get_section_wrapper(Y), true)); if (dragnode.next()) { beforeid = Number(Y.Moodle.core_course.util.cm.getId(dragnode.next())); params.beforeId = beforeid; } // Do AJAX request var uri = M.cfg.wwwroot + this.get('ajaxurl'); Y.io(uri, { method: 'POST', data: params, on: { start: function() { this.lock_drag_handle(drag, CSS.EDITINGMOVE); spinner.show(); }, success: function(tid, response) { var responsetext = Y.JSON.parse(response.responseText); // Update course state. M.course.coursebase.invoke_function( 'updateMovedCmState', { cmid: cmid, beforeid: beforeid, visible: responsetext.visible, } ); // Set visibility in course content. var params = {element: dragnode, visible: responsetext.visible}; M.course.coursebase.invoke_function('set_visibility_resource_ui', params); this.unlock_drag_handle(drag, CSS.EDITINGMOVE); window.setTimeout(function() { spinner.hide(); }, 250); }, failure: function(tid, response) { this.ajax_failure(response); this.unlock_drag_handle(drag, CSS.SECTIONHANDLE); spinner.hide(); // TODO: revert nodes location } }, context: this }); } }, { NAME: 'course-dragdrop-resource', ATTRS: { courseid: { value: null }, ajaxurl: { value: 0 }, config: { value: 0 } } }); M.course = M.course || {}; M.course.init_resource_dragdrop = function(params) { new DRAGRESOURCE(params); }; }, '@VERSION@', { "requires": [ "base", "node", "io", "dom", "dd", "dd-scroll", "moodle-core-dragdrop", "moodle-core-notification", "moodle-course-coursebase", "moodle-course-util" ] }); moodle-course-dragdrop/moodle-course-dragdrop-min.js000064400000015734152266437370016646 0ustar00YUI.add("moodle-course-dragdrop",function(g,e){var o,t,l=".actions",s="activity",r="content",p="course-content",u="editing_move",a="iconsmall",d="jumpmenu",i="left",f="movedown",m="moveup",_="page-content",h="right",n="section",v="section-handle",b="summary",c="sectiondraggable";M.course=M.course||{},g.extend(o=function(){o.superclass.constructor.apply(this,arguments)},M.core.dragdrop,{sectionlistselector:null,initializer:function(){if(this.groups=[c],this.samenodeclass=M.course.format.get_sectionwrapperclass(),this.parentnodeclass=M.course.format.get_containerclass(),this.detectkeyboarddirection=!0,g.Node.one("."+d))return!1;var e;this.sectionlistselector=M.course.format.get_section_wrapper(g),this.sectionlistselector&&(this.sectionlistselector="."+p+" "+this.sectionlistselector,this.setup_for_section(this.sectionlistselector),(e=new g.DD.Delegate({container:"."+p,nodes:"."+c,target:!0,handles:["."+i],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+_,stickY:!0}),e.dd.plug(g.Plugin.DDWinScroll))},setup_for_section:function(e){g.Node.all(e).each(function(e){var o,t,s,r=g.Moodle.core_course.util.section.getId(e);0")).addClass(M.course.format.get_containerclass()),(t=g.Node.create("<"+M.course.format.get_sectionwrappernode()+">")).addClass(M.course.format.get_sectionwrapperclass()),t.setStyle("margin",0),t.setContent(s.get("innerHTML")),o.appendChild(t),e.setContent(o),e.addClass(p))},drag_dropmiss:function(e){this.drop_hit(e)},get_section_index:function(e){var o="."+p+" "+M.course.format.get_section_selector(g),o=g.all(o);return o.indexOf(e)-o.indexOf(g.one("#section-0"))},drop_hit:function(n){var c,a,e,i,o,t=n.drag,l=t.get("node"),s=g.Moodle.core_course.util.section.getId(l),d=s,r=this.get_section_index(l),u=r;if(s!==r){for(o in ug.Moodle.core_course.util.section.getId(c.item(s))&&(i=c.item(s-1).get("id"),c.item(s-1).set("id",c.item(s).get("id")),c.item(s).set("id",i),M.course.format.swap_sections(g,s-1,s),r=!0),c.item(s).setAttribute("data-sectionid",g.Moodle.core_course.util.section.getId(c.item(s)))}while(u-=1,r);window.setTimeout(function(){a.hide()},250),M.course.coursebase.invoke_function("updateMovedSectionState")},failure:function(e,o){this.ajax_failure(o),a.hide()}},context:this})}}},{NAME:"course-dragdrop-section",ATTRS:{courseid:{value:null},ajaxurl:{value:0},config:{value:0}}}),M.course=M.course||{},M.course.init_section_dragdrop=function(e){new o(e)},g.extend(t=function(){t.superclass.constructor.apply(this,arguments)},M.core.dragdrop,{initializer:function(){var e;this.groups=["resource"],this.samenodeclass=s,this.parentnodeclass=n,this.samenodelabel={identifier:"afterresource",component:"moodle"},this.parentnodelabel={identifier:"totopofsection",component:"moodle"},(e=M.course.format.get_section_selector(g))&&(e="."+p+" "+e,this.setup_for_section(e),e=e.slice(p.length+2)+" li."+s,(e=new g.DD.Delegate({container:"."+p,nodes:e,target:!0,handles:["."+u],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1,cloneNode:!0}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+_}),e.dd.plug(g.Plugin.DDWinScroll),M.course.coursebase.register_module(this),M.course.dragres=this)},setup_for_section:function(e){g.Node.all(e).each(function(e){var o=e.one("."+r+" ul."+n);o||((o=g.Node.create("
        ")).addClass(n),e.one("."+r+" div."+b).insert(o,"after")),o.setAttribute("data-draggroups",this.groups.join(" ")),new g.DD.Drop({node:o,groups:this.groups,padding:"20 0 20 0"}),this.setup_for_resource("#"+e.get("id")+" li."+s)},this)},setup_for_resource:function(e){g.Node.all(e).each(function(e){var o=e.getData("draggroups");o||(e.setAttribute("data-draggroups",this.groups.join(" ")),new g.DD.Drop({node:e,groups:this.groups,padding:"20 0 20 0"})),(o=e.one("a."+u))&&(e=o.getData("sectionreturn"),o.replace(this.get_drag_handle(M.util.get_string("movecoursemodule","moodle"),u,a,!0).setAttribute("data-sectionreturn",e)))},this)},drag_start:function(e){e=e.target;e.get("dragNode")!==e.get("node")&&(e.get("dragNode").setContent(e.get("node").get("innerHTML")),e.get("dragNode").all("img.iconsmall").setStyle("vertical-align","baseline"))},drag_dropmiss:function(e){this.drop_hit(e)},drop_hit:function(e){var o,t,s,r=e.drag,i=r.get("node"),e=e.drop.get("node"),n=i.one(l),c=M.util.add_spinner(g,n),a={},d=this.get("config").pageparams;for(o in d)a[o]=d[o];t=Number(g.Moodle.core_course.util.cm.getId(i)),s=null,a.sesskey=M.cfg.sesskey,a.courseId=this.get( "courseid"),a["class"]="resource",a.field="move",a.id=t,a.sectionId=g.Moodle.core_course.util.section.getId(e.ancestor(M.course.format.get_section_wrapper(g),!0)),i.next()&&(s=Number(g.Moodle.core_course.util.cm.getId(i.next())),a.beforeId=s),n=M.cfg.wwwroot+this.get("ajaxurl"),g.io(n,{method:"POST",data:a,on:{start:function(){this.lock_drag_handle(r,u),c.show()},success:function(e,o){var o=g.JSON.parse(o.responseText);M.course.coursebase.invoke_function("updateMovedCmState",{cmid:t,beforeid:s,visible:o.visible}),o={element:i,visible:o.visible},M.course.coursebase.invoke_function("set_visibility_resource_ui",o),this.unlock_drag_handle(r,u),window.setTimeout(function(){c.hide()},250)},failure:function(e,o){this.ajax_failure(o),this.unlock_drag_handle(r,v),c.hide()}},context:this})}},{NAME:"course-dragdrop-resource",ATTRS:{courseid:{value:null},ajaxurl:{value:0},config:{value:0}}}),M.course=M.course||{},M.course.init_resource_dragdrop=function(e){new t(e)}},"@VERSION@",{requires:["base","node","io","dom","dd","dd-scroll","moodle-core-dragdrop","moodle-core-notification","moodle-course-coursebase","moodle-course-util"]});moodle-course-management/moodle-course-management-min.js000064400000045143152266437370017467 0ustar00YUI.add("moodle-course-management",function(s,e){var i,o,r,t,n=function(){n.superclass.constructor.apply(this,arguments)};n.NAME="moodle-course-management",n.CSS_PREFIX="management",n.ATTRS={element:{setter:function(e){return e="string"==typeof e?s.one("#"+e):e}},categorylisting:{value:null},courselisting:{value:null},coursedetails:{value:null},activecategoryid:{value:null},activecourseid:{value:null},categories:{setter:function(e,t){if(s.Lang.isArray(e))return e;t=this.get(t);return t.push(e),t},value:[]},courses:{validator:function(e){return s.Lang.isArray(e)},value:[]},page:{getter:function(e,t){return null===e&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},totalpages:{getter:function(e,t){return null===e&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},totalcourses:{getter:function(e,t){return null===e&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},ajaxurl:{getter:function(e){return e=null===e?M.cfg.wwwroot+"/course/ajax/management.php":e},value:null},dragdrop:{value:null}},s.extend(n,s.Base,n.prototype={categoriesinit:!1,initializer:function(){this.set("element","coursecat-management");var e=this.get("element"),t=e.one("#category-listing"),i=e.one("#course-listing"),o=null,n=null;t&&(o=t.one('.listitem[data-selected="1"]')),i&&(n=i.one('.listitem[data-selected="1"]')),this.set("categorylisting",t),this.set("courselisting",i),this.set("coursedetails",e.one("#course-detail")),o&&this.set("activecategoryid",o.getData("id")),n&&this.set("activecourseid",n.getData("id")),this.initialiseCategories(t),this.initialiseCourses(),i&&this.set("dragdrop",new r({console:this}))},initialiseCategories:function(e){var t;if(!e)return!1;(t=e.one("#menumovecategoriesto"))&&t.setAttribute("disabled",!0),(t=e.one("#menuresortcategoriesby"))&&t.setAttribute("disabled",!0),(t=e.one("#menuresortcoursesby"))&&t.setAttribute("disabled",!0),e.all(".listitem[data-id]").each(function(e){this.set("categories",new i({node:e,console:this})),0},this),this.categoriesinit||(this.get("categorylisting").delegate("click",this.handleCategoryDelegation,"a[data-action]",this),this.get("categorylisting").delegate("click",this.handleCategoryDelegation,'input[name="bcat[]"]',this),this.get("categorylisting").delegate("change",this.handleBulkSortByaction,"#menuselectsortby",this),this.categoriesinit=!0)},initialiseCourses:function(){var e,t=this.getCategoryById(this.get("activecategoryid")),i=this.get("courselisting");if(!i)return!1;(e=i.one("#menumovecoursesto"))&&e.setAttribute("disabled",!0),i.all(".listitem[data-id]").each(function(e){this.registerCourse(new o({node:e,console:this,category:t})),0},this),i.delegate("click",this.handleCourseDelegation,"a[data-action]",this),i.delegate("click",this.handleCourseDelegation,'input[name="bc[]"]',this)},registerCourse:function(e){var t=this.get("courses");t.push(e),this.set("courses",t)},handleCourseDelegation:function(e){var t=e.currentTarget,i=t.getData("action"),t=t.ancestor(".listitem").getData("id"),t=this.getCourseById(t);t&&t.handle(i,e)},handleCategoryDelegation:function(e){var t=e.currentTarget,i=t.getData("action"),t=t.ancestor(".listitem").getData("id"),t=this.getCategoryById(t);t&&t.handle(i,e)},isCourseSelected:function(e){var t,i,o,n=!1;if(e&&e.get("checked"))n=!0;else for(o=(i=this.get("courses")).length,t=0;t .course-listing"),t=t?t.one("ul.category-list"):null,o=i?i.one("ul.course-list"):null,i=!!i&&i.getData("canmoveoutof"),n=i?e:o;if(!o)return!1;for(;0===n.get("scrollHeight")&&!n.compareTo(window.document.body);)n=n.get("parentNode");o.all("> li").each(function(e){this.initCourseListing(e,n)},this),o.setData("dd",new s.DD.Drop({node:o})),i&&t&&t.all("li > div").each(function(e){this.initCategoryListitem(e)},this),s.DD.DDM.on("drag:start",this.dragStart, this),s.DD.DDM.on("drag:end",this.dragEnd,this),s.DD.DDM.on("drag:drag",this.dragDrag,this),s.DD.DDM.on("drop:over",this.dropOver,this),s.DD.DDM.on("drop:enter",this.dropEnter,this),s.DD.DDM.on("drop:exit",this.dropExit,this),s.DD.DDM.on("drop:hit",this.dropHit,this)},initCourseListing:function(e,t){e.setData("dd",new s.DD.Drag({node:e,target:{padding:"0 0 0 20"}}).addHandle(".drag-handle").plug(s.Plugin.DDProxy,{moveOnEnd:!1,borderStyle:!1}).plug(s.Plugin.DDConstrained,{constrain2node:t}))},initCategoryListitem:function(e){e.setData("dd",new s.DD.Drop({node:e}))},dragStart:function(e){var e=e.target,t=e.get("node"),e=e.get("dragNode");t.addClass("course-being-dragged"),e.addClass("course-being-dragged-proxy").set("innerHTML",t.one("a.coursename").get("innerHTML")),this.previoussibling=t.get("previousSibling")},dragEnd:function(e){e.target.get("node").removeClass("course-being-dragged"),this.get("console").get("element").all("#category-listing li.highlight").removeClass("highlight")},dragDrag:function(e){e=e.target.lastXY[1];e div a.action-moveup"),n=e.one(" > div a.action-movedown"),i&&n||(o=e.one(" > div a.action-moveup"),t=t.one(" > div a.action-movedown"),i||n?n||o.insert(t,"after"):(i=s.Node.create(' '),t.replace(i),o.replace(t),i.replace(o),i.destroy())),(o=e.one(" > div a.action-moveup"))?o.focus():(n=e.one(" > div a.action-movedown"))&&n.focus(),this.updated(!0)):window.location.reload()},movedown:function(e,t,i){var o,n;if(!1===this.checkAjaxResponse(e,t,i))return!1;(t=(e=this.get("node")).next(".listitem"))?(e.insert(t,"before"),i=t.one(" > div a.action-movedown"),o=e.one(" > div a.action-moveup"),i&&o||(t=t.one(" > div a.action-moveup"),n=e.one(" > div a.action-movedown"),i||o?o||n.insert(t,"before"):(i=s.Node.create(' '),t.replace(i),n.replace(t),i.replace(n),i.destroy())),(n=e.one(" > div a.action-movedown"))?n.focus():(o=e.one(" > div a.action-moveup"))&&o.focus(),this.updated(!0)):window.location.reload()},show:function(e,t,i){if(!1===this.checkAjaxResponse(e,t,i))return!1;this.markVisible(),(e=this.get("node").one("a[data-action=hide]"))&&e.focus(),this.updated()},markVisible:function(){return this.get("node").setAttribute("data-visible","1"),!0},hide:function(e,t,i){if(!1===this.checkAjaxResponse(e,t,i))return!1;this.markHidden(),(e=this.get("node").one("a[data-action=show]"))&&e.focus(),this.updated()},markHidden:function(){return this.get("node").setAttribute("data-visible","0"),!0},updated:function(e){e&&this.highlight()},highlight:function(){var e=this.get("node");e.siblings(".highlight").removeClass("highlight"),e.addClass("highlight"),this.highlighttimeout&&window.clearTimeout(this.highlighttimeout),this.highlighttimeout=window.setTimeout(function(){e.removeClass("highlight")},2500)}}),(i=function(){i.superclass.constructor.apply(this,arguments)}).NAME="moodle-course-management-category",i.CSS_PREFIX="management-category",i.ATTRS={categoryid:{getter:function(e,t){return null===e&&(e=this.get("node").getData("id"),this.set(t,e)),e},value:null,writeOnce:!0},selected:{getter:function(e,t){return null===e&&(e=this.get("node").getData(t),this.set(t,e=null===e?!1:e)),e},value:null},courses:{validator:function(e){return s.Lang.isArray(e)},value:[]}},i.prototype={initializer:function(){this.set("itemname","category")},getName:function(){return this.get("node").one("a.categoryname").get("innerHTML")},registerCourse:function(e){var t=this.get("courses");t.push(e),this.set("courses",t)},handle:function(e,t){var i,o,n,s,r={categoryid:this.get("categoryid")},a=this.get("console").get("activecategoryid");switch(a&&a!==r.categoryid&&(r.selectedcategory=a),e){case"moveup":t.preventDefault(),this.get("console").performAjaxAction("movecategoryup",r,this.moveup,this);break;case"movedown":t.preventDefault(),this.get("console").performAjaxAction("movecategorydown",r,this.movedown,this);break;case"show":t.preventDefault(),this.get("console").performAjaxAction("showcategory",r,this.show,this);break;case"hide":if(t.preventDefault(),"0"===(n=this.get("node").getData("course-count"))){this.get("console").performAjaxAction("hidecategory",r,this.hide, this);break}i="1"===n?"hidecategoryone":"hidecategorymany",o={category:this.get("node").getData("category-name"),coursecount:n},require(["core/notification","core/str"],function(e,t){e.saveCancelPromise(t.get_string("hidecategory"),t.get_string(i,"core",o),t.get_string("hide")).then(function(){this.get("console").performAjaxAction("hidecategory",r,this.hide,this)}.bind(this))["catch"](function(){})}.bind(this));break;case"expand":t.preventDefault(),"0"===this.get("node").getData("expanded")&&(this.get("node").setAttribute("data-expanded","1").setData("expanded","true"),this.get("console").performAjaxAction("getsubcategorieshtml",r,this.loadSubcategories,this)),this.expand();break;case"collapse":t.preventDefault(),this.collapse();break;case"select":(s=(n=this.get("console")).get("categorylisting").one("#menumovecategoriesto"))&&(n.isCategorySelected(t.currentTarget)&&1 .course-listing'), categoryul = (categorylisting) ? categorylisting.one('ul.category-list') : null, courseul = (courselisting) ? courselisting.one('ul.course-list') : null, canmoveoutof = (courselisting) ? courselisting.getData('canmoveoutof') : false, contstraint = (canmoveoutof) ? container : courseul; if (!courseul) { // No course listings found. return false; } while (contstraint.get('scrollHeight') === 0 && !contstraint.compareTo(window.document.body)) { contstraint = contstraint.get('parentNode'); } courseul.all('> li').each(function(li) { this.initCourseListing(li, contstraint); }, this); courseul.setData('dd', new Y.DD.Drop({ node: courseul })); if (canmoveoutof && categoryul) { // Category UL may not be there if viewmode is just courses. categoryul.all('li > div').each(function(div) { this.initCategoryListitem(div); }, this); } Y.DD.DDM.on('drag:start', this.dragStart, this); Y.DD.DDM.on('drag:end', this.dragEnd, this); Y.DD.DDM.on('drag:drag', this.dragDrag, this); Y.DD.DDM.on('drop:over', this.dropOver, this); Y.DD.DDM.on('drop:enter', this.dropEnter, this); Y.DD.DDM.on('drop:exit', this.dropExit, this); Y.DD.DDM.on('drop:hit', this.dropHit, this); }, /** * Initialises a course listing. * @method initCourseListing * @param Node */ initCourseListing: function(node, contstraint) { node.setData('dd', new Y.DD.Drag({ node: node, target: { padding: '0 0 0 20' } }).addHandle( '.drag-handle' ).plug(Y.Plugin.DDProxy, { moveOnEnd: false, borderStyle: false }).plug(Y.Plugin.DDConstrained, { constrain2node: contstraint })); }, /** * Initialises a category listing. * @method initCategoryListitem * @param Node */ initCategoryListitem: function(node) { node.setData('dd', new Y.DD.Drop({ node: node })); }, /** * Dragging has started. * @method dragStart * @private * @param {EventFacade} e */ dragStart: function(e) { var drag = e.target, node = drag.get('node'), dragnode = drag.get('dragNode'); node.addClass('course-being-dragged'); dragnode.addClass('course-being-dragged-proxy').set('innerHTML', node.one('a.coursename').get('innerHTML')); this.previoussibling = node.get('previousSibling'); }, /** * Dragging has ended. * @method dragEnd * @private * @param {EventFacade} e */ dragEnd: function(e) { var drag = e.target, node = drag.get('node'); node.removeClass('course-being-dragged'); this.get('console').get('element').all('#category-listing li.highlight').removeClass('highlight'); }, /** * Dragging in progress. * @method dragDrag * @private * @param {EventFacade} e */ dragDrag: function(e) { var y = e.target.lastXY[1]; if (y < this.lasty) { this.goingup = true; } else { this.goingup = false; } this.lasty = y; }, /** * The course has been dragged over a drop target. * @method dropOver * @private * @param {EventFacade} e */ dropOver: function(e) { // Get a reference to our drag and drop nodes var drag = e.drag.get('node'), drop = e.drop.get('node'), tag = drop.get('tagName').toLowerCase(); if (tag === 'li' && drop.hasClass('listitem-course')) { if (!this.goingup) { drop = drop.get('nextSibling'); if (!drop) { drop = e.drop.get('node'); drop.get('parentNode').append(drag); return false; } } drop.get('parentNode').insertBefore(drag, drop); e.drop.sizeShim(); } }, /** * The course has been dragged over a drop target. * @method dropEnter * @private * @param {EventFacade} e */ dropEnter: function(e) { var drop = e.drop.get('node'), tag = drop.get('tagName').toLowerCase(); if (tag === 'div') { drop.ancestor('li.listitem-category').addClass('highlight'); } }, /** * The course has been dragged off a drop target. * @method dropExit * @private * @param {EventFacade} e */ dropExit: function(e) { var drop = e.drop.get('node'), tag = drop.get('tagName').toLowerCase(); if (tag === 'div') { drop.ancestor('li.listitem-category').removeClass('highlight'); } }, /** * The course has been dropped on a target. * @method dropHit * @private * @param {EventFacade} e */ dropHit: function(e) { var drag = e.drag.get('node'), drop = e.drop.get('node'), iscategory = (drop.ancestor('.listitem-category') !== null), iscourse = !iscategory && (drop.test('.listitem-course')), managementconsole = this.get('console'), categoryid, category, courseid, course, aftercourseid, previoussibling, previousid; if (!drag.test('.listitem-course')) { Y.log('It was not a course being dragged.', 'warn', 'moodle-course-management'); return false; } courseid = drag.getData('id'); if (iscategory) { categoryid = drop.ancestor('.listitem-category').getData('id'); Y.log('Course ' + courseid + ' dragged into category ' + categoryid); category = managementconsole.getCategoryById(categoryid); if (category) { course = managementconsole.getCourseById(courseid); if (course) { category.moveCourseTo(course); } } } else if (iscourse || drop.ancestor('#course-listing')) { course = managementconsole.getCourseById(courseid); previoussibling = drag.get('previousSibling'); aftercourseid = (previoussibling) ? previoussibling.getData('id') || 0 : 0; previousid = (this.previoussibling) ? this.previoussibling.getData('id') : 0; if (aftercourseid !== previousid) { course.moveAfter(aftercourseid, previousid); } } else { Y.log('Course dropped over unhandled target.', 'info', 'moodle-course-management'); } } }; Y.extend(DragDrop, Y.Base, DragDrop.prototype); /** * A managed course. * * @namespace M.course.management * @class Item * @constructor * @extends Base */ Item = function() { Item.superclass.constructor.apply(this, arguments); }; Item.NAME = 'moodle-course-management-item'; Item.CSS_PREFIX = 'management-item'; Item.ATTRS = { /** * The node for this item. * @attribute node * @type Node */ node: {}, /** * The management console. * @attribute console * @type Console */ console: {}, /** * Describes the type of this item. Should be set by the extending class. * @attribute itemname * @type {String} * @default item */ itemname: { value: 'item' } }; Item.prototype = { /** * The highlight timeout for this item if there is one. * @property highlighttimeout * @protected * @type Timeout * @default null */ highlighttimeout: null, /** * Checks and parses an AJAX response for an item. * * @method checkAjaxResponse * @protected * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Object|Boolean} */ checkAjaxResponse: function(transactionid, response, args) { if (response.status !== 200) { Y.log('Error: AJAX response resulted in non 200 status.', 'error', 'Item.checkAjaxResponse'); return false; } if (transactionid === null || args === null) { Y.log('Error: Invalid AJAX response details provided.', 'error', 'Item.checkAjaxResponse'); return false; } var outcome = Y.JSON.parse(response.responseText); if (outcome.error !== false) { new M.core.exception(outcome); } if (outcome.outcome === false) { return false; } return outcome; }, /** * Moves an item up by one. * * @method moveup * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ moveup: function(transactionid, response, args) { var node, nodeup, nodedown, previous, previousup, previousdown, tmpnode, outcome = this.checkAjaxResponse(transactionid, response, args); if (outcome === false) { Y.log('AJAX request to move ' + this.get('itemname') + ' up failed by outcome.', 'warn', 'moodle-course-management'); return false; } node = this.get('node'); previous = node.previous('.listitem'); if (previous) { previous.insert(node, 'before'); previousup = previous.one(' > div a.action-moveup'); nodedown = node.one(' > div a.action-movedown'); if (!previousup || !nodedown) { // We can have two situations here: // 1. previousup is not set and nodedown is not set. This happens when there are only two courses. // 2. nodedown is not set. This happens when they are moving the bottom course up. // node up and previous down should always be there. They would be required to trigger the action. nodeup = node.one(' > div a.action-moveup'); previousdown = previous.one(' > div a.action-movedown'); if (!previousup && !nodedown) { // Ok, must be two courses. We need to switch the up and down icons. tmpnode = Y.Node.create(' '); previousdown.replace(tmpnode); nodeup.replace(previousdown); tmpnode.replace(nodeup); tmpnode.destroy(); } else if (!nodedown) { // previous down needs to be given to node. nodeup.insert(previousdown, 'after'); } } nodeup = node.one(' > div a.action-moveup'); if (nodeup) { // Try to re-focus on up. nodeup.focus(); } else { // If we can't focus up we're at the bottom, try to focus on up. nodedown = node.one(' > div a.action-movedown'); if (nodedown) { nodedown.focus(); } } this.updated(true); Y.log('Success: ' + this.get('itemname') + ' moved up by AJAX.', 'info', 'moodle-course-management'); } else { // Aha it succeeded but this is the top item in the list. Pagination is in play! // Refresh to update the state of things. Y.log(this.get('itemname') + ' cannot be moved up as its the top item on this page.', 'info', 'moodle-course-management'); window.location.reload(); } }, /** * Moves an item down by one. * * @method movedown * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ movedown: function(transactionid, response, args) { var node, next, nodeup, nodedown, nextup, nextdown, tmpnode, outcome = this.checkAjaxResponse(transactionid, response, args); if (outcome === false) { Y.log('AJAX request to move ' + this.get('itemname') + ' down failed by outcome.', 'warn', 'moodle-course-management'); return false; } node = this.get('node'); next = node.next('.listitem'); if (next) { node.insert(next, 'before'); nextdown = next.one(' > div a.action-movedown'); nodeup = node.one(' > div a.action-moveup'); if (!nextdown || !nodeup) { // next up and node down should always be there. They would be required to trigger the action. nextup = next.one(' > div a.action-moveup'); nodedown = node.one(' > div a.action-movedown'); if (!nextdown && !nodeup) { // We can have two situations here: // 1. nextdown is not set and nodeup is not set. This happens when there are only two courses. // 2. nodeup is not set. This happens when we are moving the first course down. // Ok, must be two courses. We need to switch the up and down icons. tmpnode = Y.Node.create(' '); nextup.replace(tmpnode); nodedown.replace(nextup); tmpnode.replace(nodedown); tmpnode.destroy(); } else if (!nodeup) { // next up needs to be given to node. nodedown.insert(nextup, 'before'); } } nodedown = node.one(' > div a.action-movedown'); if (nodedown) { // Try to ensure the up is focused again. nodedown.focus(); } else { // If we can't focus up we're at the top, try to focus on down. nodeup = node.one(' > div a.action-moveup'); if (nodeup) { nodeup.focus(); } } this.updated(true); Y.log('Success: ' + this.get('itemname') + ' moved down by AJAX.', 'info', 'moodle-course-management'); } else { // Aha it succeeded but this is the bottom item in the list. Pagination is in play! // Refresh to update the state of things. Y.log(this.get('itemname') + ' cannot be moved down as its the top item on this page.', 'info', 'moodle-course-management'); window.location.reload(); } }, /** * Makes an item visible. * * @method show * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ show: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), hidebtn; if (outcome === false) { Y.log('AJAX request to show ' + this.get('itemname') + ' by outcome.', 'warn', 'moodle-course-management'); return false; } this.markVisible(); hidebtn = this.get('node').one('a[data-action=hide]'); if (hidebtn) { hidebtn.focus(); } this.updated(); Y.log('Success: ' + this.get('itemname') + ' made visible by AJAX.', 'info', 'moodle-course-management'); }, /** * Marks the item as visible * @method markVisible */ markVisible: function() { this.get('node').setAttribute('data-visible', '1'); Y.log('Marked ' + this.get('itemname') + ' as visible', 'info', 'moodle-course-management'); return true; }, /** * Hides an item. * * @method hide * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ hide: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), showbtn; if (outcome === false) { Y.log('AJAX request to hide ' + this.get('itemname') + ' by outcome.', 'warn', 'moodle-course-management'); return false; } this.markHidden(); showbtn = this.get('node').one('a[data-action=show]'); if (showbtn) { showbtn.focus(); } this.updated(); Y.log('Success: ' + this.get('itemname') + ' made hidden by AJAX.', 'info', 'moodle-course-management'); }, /** * Marks the item as hidden. * @method makeHidden */ markHidden: function() { this.get('node').setAttribute('data-visible', '0'); Y.log('Marked ' + this.get('itemname') + ' as hidden', 'info', 'moodle-course-management'); return true; }, /** * Called when ever a node is updated. * * @method updated * @param {Boolean} moved True if this item was moved. */ updated: function(moved) { if (moved) { this.highlight(); } }, /** * Highlights this option for a breif time. * * @method highlight */ highlight: function() { var node = this.get('node'); node.siblings('.highlight').removeClass('highlight'); node.addClass('highlight'); if (this.highlighttimeout) { window.clearTimeout(this.highlighttimeout); } this.highlighttimeout = window.setTimeout(function() { node.removeClass('highlight'); }, 2500); } }; Y.extend(Item, Y.Base, Item.prototype); /** * A managed category. * * @namespace M.course.management * @class Category * @constructor * @extends Item */ Category = function() { Category.superclass.constructor.apply(this, arguments); }; Category.NAME = 'moodle-course-management-category'; Category.CSS_PREFIX = 'management-category'; Category.ATTRS = { /** * The category ID relating to this category. * @attribute categoryid * @type Number * @writeOnce * @default null */ categoryid: { getter: function(value, name) { if (value === null) { value = this.get('node').getData('id'); this.set(name, value); } return value; }, value: null, writeOnce: true }, /** * True if this category is the currently selected category. * @attribute selected * @type Boolean * @default null */ selected: { getter: function(value, name) { if (value === null) { value = this.get('node').getData(name); if (value === null) { value = false; } this.set(name, value); } return value; }, value: null }, /** * An array of courses belonging to this category. * @attribute courses * @type Course[] * @default Array */ courses: { validator: function(val) { return Y.Lang.isArray(val); }, value: [] } }; Category.prototype = { /** * Initialises an instance of a Category. * @method initializer */ initializer: function() { this.set('itemname', 'category'); }, /** * Returns the name of the category. * @method getName * @return {String} */ getName: function() { return this.get('node').one('a.categoryname').get('innerHTML'); }, /** * Registers a course as belonging to this category. * @method registerCourse * @param {Course} course */ registerCourse: function(course) { var courses = this.get('courses'); courses.push(course); this.set('courses', courses); }, /** * Handles a category related event. * * @method handle * @param {String} action * @param {EventFacade} e * @return {Boolean} */ handle: function(action, e) { var catarg = {categoryid: this.get('categoryid')}, selected = this.get('console').get('activecategoryid'); if (selected && selected !== catarg.categoryid) { catarg.selectedcategory = selected; } switch (action) { case 'moveup': e.preventDefault(); this.get('console').performAjaxAction('movecategoryup', catarg, this.moveup, this); break; case 'movedown': e.preventDefault(); this.get('console').performAjaxAction('movecategorydown', catarg, this.movedown, this); break; case 'show': e.preventDefault(); this.get('console').performAjaxAction('showcategory', catarg, this.show, this); break; case 'hide': e.preventDefault(); var courseCount = this.get('node').getData('course-count'); if (courseCount === '0') { this.get('console').performAjaxAction('hidecategory', catarg, this.hide, this); break; } var warningStr = courseCount === '1' ? 'hidecategoryone' : 'hidecategorymany'; var warningParams = { category: this.get('node').getData('category-name'), coursecount: courseCount, }; require(['core/notification', 'core/str'], function(Notification, Str) { Notification.saveCancelPromise( Str.get_string('hidecategory'), Str.get_string(warningStr, 'core', warningParams), Str.get_string('hide') ).then(function() { this.get('console').performAjaxAction('hidecategory', catarg, this.hide, this); }.bind(this) ).catch(function() { // User cancelled, no action needed. }); }.bind(this)); break; case 'expand': e.preventDefault(); if (this.get('node').getData('expanded') === '0') { this.get('node').setAttribute('data-expanded', '1').setData('expanded', 'true'); this.get('console').performAjaxAction('getsubcategorieshtml', catarg, this.loadSubcategories, this); } this.expand(); break; case 'collapse': e.preventDefault(); this.collapse(); break; case 'select': var c = this.get('console'), movecategoryto = c.get('categorylisting').one('#menumovecategoriesto'); // If any category is selected and there are more then one categories. if (movecategoryto) { if (c.isCategorySelected(e.currentTarget) && c.get('categories').length > 1) { movecategoryto.removeAttribute('disabled'); } else { movecategoryto.setAttribute('disabled', true); } c.handleBulkSortByaction(); } break; default: Y.log('Invalid AJAX action requested of managed category.', 'warn', 'moodle-course-management'); return false; } }, /** * Expands the category making its sub categories visible. * @method expand */ expand: function() { var node = this.get('node'), action = node.one('a[data-action=expand]'), ul = node.one('ul[role=group]'); node.removeClass('collapsed').setAttribute('aria-expanded', 'true'); action.setAttribute('data-action', 'collapse').setAttrs({ title: M.util.get_string('collapsecategory', 'moodle', this.getName()) }); require(['core/str', 'core/templates', 'core/notification'], function(Str, Templates, Notification) { Str.get_string('collapse', 'core') .then(function(string) { return Templates.renderPix('t/switch_minus', 'core', string); }) .then(function(html) { html = Y.Node.create(html).addClass('tree-icon').getDOMNode().outerHTML; return action.set('innerHTML', html); }).fail(Notification.exception); }); if (ul) { ul.setAttribute('aria-hidden', 'false'); } this.get('console').performAjaxAction('expandcategory', {categoryid: this.get('categoryid')}, null, this); }, /** * Collapses the category making its sub categories hidden. * @method collapse */ collapse: function() { var node = this.get('node'), action = node.one('a[data-action=collapse]'), ul = node.one('ul[role=group]'); node.addClass('collapsed').setAttribute('aria-expanded', 'false'); action.setAttribute('data-action', 'expand').setAttrs({ title: M.util.get_string('expandcategory', 'moodle', this.getName()) }); require(['core/str', 'core/templates', 'core/notification'], function(Str, Templates, Notification) { Str.get_string('expand', 'core') .then(function(string) { return Templates.renderPix('t/switch_plus', 'core', string); }) .then(function(html) { html = Y.Node.create(html).addClass('tree-icon').getDOMNode().outerHTML; return action.set('innerHTML', html); }).fail(Notification.exception); }); if (ul) { ul.setAttribute('aria-hidden', 'true'); } this.get('console').performAjaxAction('collapsecategory', {categoryid: this.get('categoryid')}, null, this); }, /** * Loads sub categories provided by an AJAX request.. * * @method loadSubcategories * @protected * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} Returns true on success - false otherwise. */ loadSubcategories: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), node = this.get('node'), managementconsole = this.get('console'), ul, actionnode; if (outcome === false) { Y.log('AJAX failed to load sub categories for ' + this.get('itemname'), 'warn', 'moodle-course-management'); return false; } Y.log('AJAX loaded subcategories for ' + this.get('itemname'), 'info', 'moodle-course-management'); node.append(outcome.html); managementconsole.initialiseCategories(node); if (M.core && M.core.actionmenu && M.core.actionmenu.newDOMNode) { M.core.actionmenu.newDOMNode(node); } ul = node.one('ul[role=group]'); actionnode = node.one('a[data-action=collapse]'); if (ul && actionnode) { actionnode.setAttribute('aria-controls', ul.generateID()); } return true; }, /** * Moves the course to this category. * * @method moveCourseTo * @param {Course} course */ moveCourseTo: function(course) { require(['core/notification'], function(Notification) { Notification.saveCancelPromise( M.util.get_string('confirmation', 'admin'), M.util.get_string('confirmcoursemove', 'moodle', { course: course.getName(), category: this.getName(), }), M.util.get_string('move', 'moodle') ).then(function() { this.get('console').performAjaxAction('movecourseintocategory', { courseid: course.get('courseid'), categoryid: this.get('categoryid'), }, this.completeMoveCourse, this); return; }.bind(this)).catch(function() { // User cancelled. }); }.bind(this)); }, /** * Completes moving a course to this category. * @method completeMoveCourse * @protected * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ completeMoveCourse: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), managementconsole = this.get('console'), category, course, totals; if (outcome === false) { Y.log('AJAX failed to move courses into this category: ' + this.get('itemname'), 'warn', 'moodle-course-management'); return false; } course = managementconsole.getCourseById(args.courseid); if (!course) { Y.log('Course was moved but the course listing could not be found to reflect this', 'warn', 'moodle-course-management'); return false; } Y.log('Moved the course (' + course.getName() + ') into this category (' + this.getName() + ')', 'debug', 'moodle-course-management'); this.highlight(); if (course) { if (outcome.paginationtotals) { totals = managementconsole.get('courselisting').one('.listing-pagination-totals'); if (totals) { totals.set('innerHTML', outcome.paginationtotals); } } if (outcome.totalcatcourses !== 'undefined') { totals = this.get('node').one('.course-count span'); if (totals) { totals.set('innerHTML', totals.get('innerHTML').replace(/^\d+/, outcome.totalcatcourses)); } } if (typeof outcome.fromcatcoursecount !== 'undefined') { category = managementconsole.get('activecategoryid'); category = managementconsole.getCategoryById(category); if (category) { totals = category.get('node').one('.course-count span'); if (totals) { totals.set('innerHTML', totals.get('innerHTML').replace(/^\d+/, outcome.fromcatcoursecount)); } } } course.remove(); } return true; }, /** * Makes an item visible. * * @method show * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ show: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), hidebtn; if (outcome === false) { Y.log('AJAX request to show ' + this.get('itemname') + ' by outcome.', 'warn', 'moodle-course-management'); return false; } this.markVisible(); hidebtn = this.get('node').one('a[data-action=hide]'); if (hidebtn) { hidebtn.focus(); } if (outcome.categoryvisibility) { this.updateChildVisibility(outcome.categoryvisibility); } if (outcome.coursevisibility) { this.updateCourseVisiblity(outcome.coursevisibility); } this.updated(); Y.log('Success: category made visible by AJAX.', 'info', 'moodle-course-management'); }, /** * Hides an item. * * @method hide * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ hide: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), showbtn; if (outcome === false) { Y.log('AJAX request to hide ' + this.get('itemname') + ' by outcome.', 'warn', 'moodle-course-management'); return false; } this.markHidden(); showbtn = this.get('node').one('a[data-action=show]'); if (showbtn) { showbtn.focus(); } if (outcome.categoryvisibility) { this.updateChildVisibility(outcome.categoryvisibility); } if (outcome.coursevisibility) { this.updateCourseVisiblity(outcome.coursevisibility); } this.updated(); Y.log('Success: ' + this.get('itemname') + ' made hidden by AJAX.', 'info', 'moodle-course-management'); }, /** * Updates the visibility of child courses if required. * @method updateCourseVisiblity * @chainable * @param courses */ updateCourseVisiblity: function(courses) { var managementconsole = this.get('console'), key, course; Y.log('Changing categories course visibility', 'info', 'moodle-course-management'); try { for (key in courses) { if (typeof courses[key] === 'object') { course = managementconsole.getCourseById(courses[key].id); if (course) { if (courses[key].visible === "1") { course.markVisible(); } else { course.markHidden(); } } } } } catch (err) { Y.log('Error trying to update course visibility: ' + err.message, 'warn', 'moodle-course-management'); } return this; }, /** * Updates the visibility of subcategories if required. * @method updateChildVisibility * @chainable * @param categories */ updateChildVisibility: function(categories) { var managementconsole = this.get('console'), key, category; Y.log('Changing categories subcategory visibility', 'info', 'moodle-course-management'); try { for (key in categories) { if (typeof categories[key] === 'object') { category = managementconsole.getCategoryById(categories[key].id); if (category) { if (categories[key].visible === "1") { category.markVisible(); } else { category.markHidden(); } } } } } catch (err) { Y.log('Error trying to update category visibility: ' + err.message, 'warn', 'moodle-course-management'); } return this; } }; Y.extend(Category, Item, Category.prototype); /** * A managed course. * * @namespace M.course.management * @class Course * @constructor * @extends Item */ Course = function() { Course.superclass.constructor.apply(this, arguments); }; Course.NAME = 'moodle-course-management-course'; Course.CSS_PREFIX = 'management-course'; Course.ATTRS = { /** * The course ID of this course. * @attribute courseid * @type Number */ courseid: {}, /** * True if this is the selected course. * @attribute selected * @type Boolean * @default null */ selected: { getter: function(value, name) { if (value === null) { value = this.get('node').getData(name); this.set(name, value); } return value; }, value: null }, node: { }, /** * The management console tracking this course. * @attribute console * @type Console * @writeOnce */ console: { writeOnce: 'initOnly' }, /** * The category this course belongs to. * @attribute category * @type Category * @writeOnce */ category: { writeOnce: 'initOnly' } }; Course.prototype = { /** * Initialises the new course instance. * @method initializer */ initializer: function() { var node = this.get('node'), category = this.get('category'); this.set('courseid', node.getData('id')); if (category && category.registerCourse) { category.registerCourse(this); } this.set('itemname', 'course'); }, /** * Returns the name of the course. * @method getName * @return {String} */ getName: function() { return this.get('node').one('a.coursename').get('innerHTML'); }, /** * Handles an event relating to this course. * @method handle * @param {String} action * @param {EventFacade} e * @return {Boolean} */ handle: function(action, e) { var managementconsole = this.get('console'), args = {courseid: this.get('courseid')}; switch (action) { case 'moveup': e.halt(); managementconsole.performAjaxAction('movecourseup', args, this.moveup, this); break; case 'movedown': e.halt(); managementconsole.performAjaxAction('movecoursedown', args, this.movedown, this); break; case 'show': e.halt(); managementconsole.performAjaxAction('showcourse', args, this.show, this); break; case 'hide': e.halt(); managementconsole.performAjaxAction('hidecourse', args, this.hide, this); break; case 'select': var c = this.get('console'), movetonode = c.get('courselisting').one('#menumovecoursesto'); if (movetonode) { if (c.isCourseSelected(e.currentTarget)) { movetonode.removeAttribute('disabled'); } else { movetonode.setAttribute('disabled', true); } } break; default: Y.log('Invalid AJAX action requested of managed course.', 'warn', 'moodle-course-management'); return false; } }, /** * Removes this course. * @method remove */ remove: function() { this.get('console').removeCourseById(this.get('courseid')); this.get('node').remove(); }, /** * Moves this course after another course. * * @method moveAfter * @param {Number} moveaftercourse The course to move after or 0 to put it at the top. * @param {Number} previousid the course it was previously after in case we need to revert. */ moveAfter: function(moveaftercourse, previousid) { var managementconsole = this.get('console'), args = { courseid: this.get('courseid'), moveafter: moveaftercourse, previous: previousid }; managementconsole.performAjaxAction('movecourseafter', args, this.moveAfterResponse, this); }, /** * Performs the actual move. * * @method moveAfterResponse * @protected * @param {Number} transactionid The transaction ID for the request. * @param {Object} response The response to the request. * @param {Objects} args The arguments that were given with the request. * @return {Boolean} */ moveAfterResponse: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), node = this.get('node'), previous; if (outcome === false) { previous = node.ancestor('ul').one('li[data-id=' + args.previous + ']'); Y.log('AJAX failed to move this course after the requested course', 'warn', 'moodle-course-management'); if (previous) { // After the last previous. previous.insertAfter(node, 'after'); } else { // Start of the list. node.ancestor('ul').one('li').insert(node, 'before'); } return false; } Y.log('AJAX successfully moved course (' + this.getName() + ')', 'info', 'moodle-course-management'); this.highlight(); } }; Y.extend(Course, Item, Course.prototype); }, '@VERSION@', { "requires": [ "base", "node", "io-base", "moodle-core-notification-exception", "json-parse", "dd-constrain", "dd-proxy", "dd-drop", "dd-delegate", "node-event-delegate" ] }); moodle-course-management/moodle-course-management.js000064400000156351152266437370016712 0ustar00YUI.add('moodle-course-management', function (Y, NAME) { var Category; var Console; var Course; var DragDrop; var Item; /** * Provides drop down menus for list of action links. * * @module moodle-course-management */ /** * Management JS console. * * Provides the organisation for course and category management JS. * * @namespace M.course.management * @class Console * @constructor * @extends Base */ Console = function() { Console.superclass.constructor.apply(this, arguments); }; Console.NAME = 'moodle-course-management'; Console.CSS_PREFIX = 'management'; Console.ATTRS = { /** * The HTML element containing the management interface. * @attribute element * @type Node */ element: { setter: function(node) { if (typeof node === 'string') { node = Y.one('#' + node); } return node; } }, /** * The category listing container node. * @attribute categorylisting * @type Node * @default null */ categorylisting: { value: null }, /** * The course listing container node. * @attribute courselisting * @type Node * @default null */ courselisting: { value: null }, /** * The course details container node. * @attribute coursedetails * @type Node|null * @default null */ coursedetails: { value: null }, /** * The id of the currently active category. * @attribute activecategoryid * @type Number * @default null */ activecategoryid: { value: null }, /** * The id of the currently active course. * @attribute activecourseid * @type Number * @default Null */ activecourseid: { value: null }, /** * The categories that are currently available through the management interface. * @attribute categories * @type Array * @default [] */ categories: { setter: function(item, name) { if (Y.Lang.isArray(item)) { return item; } var items = this.get(name); items.push(item); return items; }, value: [] }, /** * The courses that are currently available through the management interface. * @attribute courses * @type Course[] * @default Array */ courses: { validator: function(val) { return Y.Lang.isArray(val); }, value: [] }, /** * The currently displayed page of courses. * @attribute page * @type Number * @default null */ page: { getter: function(value, name) { if (value === null) { value = this.get('element').getData(name); this.set(name, value); } return value; }, value: null }, /** * The total pages of courses that can be shown for this category. * @attribute totalpages * @type Number * @default null */ totalpages: { getter: function(value, name) { if (value === null) { value = this.get('element').getData(name); this.set(name, value); } return value; }, value: null }, /** * The total number of courses belonging to this category. * @attribute totalcourses * @type Number * @default null */ totalcourses: { getter: function(value, name) { if (value === null) { value = this.get('element').getData(name); this.set(name, value); } return value; }, value: null }, /** * The URL to use for AJAX actions/requests. * @attribute ajaxurl * @type String * @default /course/ajax/management.php */ ajaxurl: { getter: function(value) { if (value === null) { value = M.cfg.wwwroot + '/course/ajax/management.php'; } return value; }, value: null }, /** * The drag drop handler * @attribute dragdrop * @type DragDrop * @default null */ dragdrop: { value: null } }; Console.prototype = { /** * Gets set to true once the first categories have been initialised. * @property categoriesinit * @private * @type {boolean} */ categoriesinit: false, /** * Initialises a new instance of the Console. * @method initializer */ initializer: function() { this.set('element', 'coursecat-management'); var element = this.get('element'), categorylisting = element.one('#category-listing'), courselisting = element.one('#course-listing'), selectedcategory = null, selectedcourse = null; if (categorylisting) { selectedcategory = categorylisting.one('.listitem[data-selected="1"]'); } if (courselisting) { selectedcourse = courselisting.one('.listitem[data-selected="1"]'); } this.set('categorylisting', categorylisting); this.set('courselisting', courselisting); this.set('coursedetails', element.one('#course-detail')); if (selectedcategory) { this.set('activecategoryid', selectedcategory.getData('id')); } if (selectedcourse) { this.set('activecourseid', selectedcourse.getData('id')); } this.initialiseCategories(categorylisting); this.initialiseCourses(); if (courselisting) { // No need for dragdrop if we don't have a course listing. this.set('dragdrop', new DragDrop({console: this})); } }, /** * Initialises all the categories being shown. * @method initialiseCategories * @private * @return {boolean} */ initialiseCategories: function(listing) { var count = 0; if (!listing) { return false; } // Disable category bulk actions as nothing will be selected on initialise. var menumovecatto = listing.one('#menumovecategoriesto'); if (menumovecatto) { menumovecatto.setAttribute('disabled', true); } var menuresortcategoriesby = listing.one('#menuresortcategoriesby'); if (menuresortcategoriesby) { menuresortcategoriesby.setAttribute('disabled', true); } var menuresortcoursesby = listing.one('#menuresortcoursesby'); if (menuresortcoursesby) { menuresortcoursesby.setAttribute('disabled', true); } listing.all('.listitem[data-id]').each(function(node) { this.set('categories', new Category({ node: node, console: this })); count++; }, this); if (!this.categoriesinit) { this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'a[data-action]', this); this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'input[name="bcat[]"]', this); this.get('categorylisting').delegate('change', this.handleBulkSortByaction, '#menuselectsortby', this); this.categoriesinit = true; } else { } }, /** * Initialises all the categories being shown. * @method initialiseCourses * @private * @return {boolean} */ initialiseCourses: function() { var category = this.getCategoryById(this.get('activecategoryid')), listing = this.get('courselisting'), count = 0; if (!listing) { return false; } // Disable course move to bulk action as nothing will be selected on initialise. var menumovecoursesto = listing.one('#menumovecoursesto'); if (menumovecoursesto) { menumovecoursesto.setAttribute('disabled', true); } listing.all('.listitem[data-id]').each(function(node) { this.registerCourse(new Course({ node: node, console: this, category: category })); count++; }, this); listing.delegate('click', this.handleCourseDelegation, 'a[data-action]', this); listing.delegate('click', this.handleCourseDelegation, 'input[name="bc[]"]', this); }, /** * Registers a course within the management display. * @method registerCourse * @param {Course} course */ registerCourse: function(course) { var courses = this.get('courses'); courses.push(course); this.set('courses', courses); }, /** * Handles the event fired by a delegated course listener. * * @method handleCourseDelegation * @protected * @param {EventFacade} e */ handleCourseDelegation: function(e) { var target = e.currentTarget, action = target.getData('action'), courseid = target.ancestor('.listitem').getData('id'), course = this.getCourseById(courseid); if (course) { course.handle(action, e); } else { } }, /** * Handles the event fired by a delegated course listener. * * @method handleCategoryDelegation * @protected * @param {EventFacade} e */ handleCategoryDelegation: function(e) { var target = e.currentTarget, action = target.getData('action'), categoryid = target.ancestor('.listitem').getData('id'), category = this.getCategoryById(categoryid); if (category) { category.handle(action, e); } else { } }, /** * Check if any course is selected. * * @method isCourseSelected * @param {Node} checkboxnode Checkbox node on which action happened. * @return bool */ isCourseSelected: function(checkboxnode) { var selected = false; // If any course selected then show move to category select box. if (checkboxnode && checkboxnode.get('checked')) { selected = true; } else { var i, course, courses = this.get('courses'), length = courses.length; for (i = 0; i < length; i++) { if (courses.hasOwnProperty(i)) { course = courses[i]; if (course.get('node').one('input[name="bc[]"]').get('checked')) { selected = true; break; } } } } return selected; }, /** * Check if any category is selected. * * @method isCategorySelected * @param {Node} checkboxnode Checkbox node on which action happened. * @return bool */ isCategorySelected: function(checkboxnode) { var selected = false; // If any category selected then show move to category select box. if (checkboxnode && checkboxnode.get('checked')) { selected = true; } else { var i, category, categories = this.get('categories'), length = categories.length; for (i = 0; i < length; i++) { if (categories.hasOwnProperty(i)) { category = categories[i]; if (category.get('node').one('input[name="bcat[]"]').get('checked')) { selected = true; break; } } } } return selected; }, /** * Handle bulk sort action. * * @method handleBulkSortByaction * @protected * @param {EventFacade} e */ handleBulkSortByaction: function(e) { var sortcategoryby = this.get('categorylisting').one('#menuresortcategoriesby'), sortcourseby = this.get('categorylisting').one('#menuresortcoursesby'), sortbybutton = this.get('categorylisting').one('input[name="bulksort"]'), sortby = e; if (!sortby) { sortby = this.get('categorylisting').one('#menuselectsortby'); } else { if (e && e.currentTarget) { sortby = e.currentTarget; } } // If no sortby select found then return as we can't do anything. if (!sortby) { return; } if ((this.get('categories').length <= 1) || (!this.isCategorySelected() && (sortby.get("options").item(sortby.get('selectedIndex')).getAttribute('value') === 'selectedcategories'))) { if (sortcategoryby) { sortcategoryby.setAttribute('disabled', true); } if (sortcourseby) { sortcourseby.setAttribute('disabled', true); } if (sortbybutton) { sortbybutton.setAttribute('disabled', true); } } else { if (sortcategoryby) { sortcategoryby.removeAttribute('disabled'); } if (sortcourseby) { sortcourseby.removeAttribute('disabled'); } if (sortbybutton) { sortbybutton.removeAttribute('disabled'); } } }, /** * Returns the category with the given ID. * @method getCategoryById * @param {Number} id * @return {Category|Boolean} The category or false if it can't be found. */ getCategoryById: function(id) { var i, category, categories = this.get('categories'), length = categories.length; for (i = 0; i < length; i++) { if (categories.hasOwnProperty(i)) { category = categories[i]; if (category.get('categoryid') === id) { return category; } } } return false; }, /** * Returns the course with the given id. * @method getCourseById * @param {Number} id * @return {Course|Boolean} The course or false if not found/ */ getCourseById: function(id) { var i, course, courses = this.get('courses'), length = courses.length; for (i = 0; i < length; i++) { if (courses.hasOwnProperty(i)) { course = courses[i]; if (course.get('courseid') === id) { return course; } } } return false; }, /** * Removes the course with the given ID. * @method removeCourseById * @param {Number} id */ removeCourseById: function(id) { var courses = this.get('courses'), length = courses.length, course, i; for (i = 0; i < length; i++) { course = courses[i]; if (course.get('courseid') === id) { courses.splice(i, 1); break; } } }, /** * Performs an AJAX action. * * @method performAjaxAction * @param {String} action The action to perform. * @param {Object} args The arguments to pass through with teh request. * @param {Function} callback The function to call when all is done. * @param {Object} context The object to use as the context for the callback. */ performAjaxAction: function(action, args, callback, context) { var io = new Y.IO(); args.action = action; args.ajax = '1'; args.sesskey = M.cfg.sesskey; if (callback === null) { callback = function() { }; } io.send(this.get('ajaxurl'), { method: 'POST', on: { complete: callback }, context: context, data: args, 'arguments': args }); } }; Y.extend(Console, Y.Base, Console.prototype); M.course = M.course || {}; M.course.management = M.course.management || {}; M.course.management.console = null; /** * Initalises the course management console. * * @method M.course.management.init * @static * @param {Object} config */ M.course.management.init = function(config) { M.course.management.console = new Console(config); }; /** * Drag and Drop handler * * @namespace M.course.management * @class DragDrop * @constructor * @extends Base */ DragDrop = function(config) { Console.superclass.constructor.apply(this, [config]); }; DragDrop.NAME = 'moodle-course-management-dd'; DragDrop.CSS_PREFIX = 'management-dd'; DragDrop.ATTRS = { /** * The management console this drag and drop has been set up for. * @attribute console * @type Console * @writeOnce */ console: { writeOnce: 'initOnly' } }; DragDrop.prototype = { /** * True if the user is dragging a course upwards. * @property goingup * @protected * @default false */ goingup: false, /** * The last Y position of the course being dragged * @property lasty * @protected * @default null */ lasty: null, /** * The sibling above the course being dragged currently (tracking its original position). * * @property previoussibling * @protected * @default false */ previoussibling: null, /** * Initialises the DragDrop instance. * @method initializer */ initializer: function() { var managementconsole = this.get('console'), container = managementconsole.get('element'), categorylisting = container.one('#category-listing'), courselisting = container.one('#course-listing > .course-listing'), categoryul = (categorylisting) ? categorylisting.one('ul.category-list') : null, courseul = (courselisting) ? courselisting.one('ul.course-list') : null, canmoveoutof = (courselisting) ? courselisting.getData('canmoveoutof') : false, contstraint = (canmoveoutof) ? container : courseul; if (!courseul) { // No course listings found. return false; } while (contstraint.get('scrollHeight') === 0 && !contstraint.compareTo(window.document.body)) { contstraint = contstraint.get('parentNode'); } courseul.all('> li').each(function(li) { this.initCourseListing(li, contstraint); }, this); courseul.setData('dd', new Y.DD.Drop({ node: courseul })); if (canmoveoutof && categoryul) { // Category UL may not be there if viewmode is just courses. categoryul.all('li > div').each(function(div) { this.initCategoryListitem(div); }, this); } Y.DD.DDM.on('drag:start', this.dragStart, this); Y.DD.DDM.on('drag:end', this.dragEnd, this); Y.DD.DDM.on('drag:drag', this.dragDrag, this); Y.DD.DDM.on('drop:over', this.dropOver, this); Y.DD.DDM.on('drop:enter', this.dropEnter, this); Y.DD.DDM.on('drop:exit', this.dropExit, this); Y.DD.DDM.on('drop:hit', this.dropHit, this); }, /** * Initialises a course listing. * @method initCourseListing * @param Node */ initCourseListing: function(node, contstraint) { node.setData('dd', new Y.DD.Drag({ node: node, target: { padding: '0 0 0 20' } }).addHandle( '.drag-handle' ).plug(Y.Plugin.DDProxy, { moveOnEnd: false, borderStyle: false }).plug(Y.Plugin.DDConstrained, { constrain2node: contstraint })); }, /** * Initialises a category listing. * @method initCategoryListitem * @param Node */ initCategoryListitem: function(node) { node.setData('dd', new Y.DD.Drop({ node: node })); }, /** * Dragging has started. * @method dragStart * @private * @param {EventFacade} e */ dragStart: function(e) { var drag = e.target, node = drag.get('node'), dragnode = drag.get('dragNode'); node.addClass('course-being-dragged'); dragnode.addClass('course-being-dragged-proxy').set('innerHTML', node.one('a.coursename').get('innerHTML')); this.previoussibling = node.get('previousSibling'); }, /** * Dragging has ended. * @method dragEnd * @private * @param {EventFacade} e */ dragEnd: function(e) { var drag = e.target, node = drag.get('node'); node.removeClass('course-being-dragged'); this.get('console').get('element').all('#category-listing li.highlight').removeClass('highlight'); }, /** * Dragging in progress. * @method dragDrag * @private * @param {EventFacade} e */ dragDrag: function(e) { var y = e.target.lastXY[1]; if (y < this.lasty) { this.goingup = true; } else { this.goingup = false; } this.lasty = y; }, /** * The course has been dragged over a drop target. * @method dropOver * @private * @param {EventFacade} e */ dropOver: function(e) { // Get a reference to our drag and drop nodes var drag = e.drag.get('node'), drop = e.drop.get('node'), tag = drop.get('tagName').toLowerCase(); if (tag === 'li' && drop.hasClass('listitem-course')) { if (!this.goingup) { drop = drop.get('nextSibling'); if (!drop) { drop = e.drop.get('node'); drop.get('parentNode').append(drag); return false; } } drop.get('parentNode').insertBefore(drag, drop); e.drop.sizeShim(); } }, /** * The course has been dragged over a drop target. * @method dropEnter * @private * @param {EventFacade} e */ dropEnter: function(e) { var drop = e.drop.get('node'), tag = drop.get('tagName').toLowerCase(); if (tag === 'div') { drop.ancestor('li.listitem-category').addClass('highlight'); } }, /** * The course has been dragged off a drop target. * @method dropExit * @private * @param {EventFacade} e */ dropExit: function(e) { var drop = e.drop.get('node'), tag = drop.get('tagName').toLowerCase(); if (tag === 'div') { drop.ancestor('li.listitem-category').removeClass('highlight'); } }, /** * The course has been dropped on a target. * @method dropHit * @private * @param {EventFacade} e */ dropHit: function(e) { var drag = e.drag.get('node'), drop = e.drop.get('node'), iscategory = (drop.ancestor('.listitem-category') !== null), iscourse = !iscategory && (drop.test('.listitem-course')), managementconsole = this.get('console'), categoryid, category, courseid, course, aftercourseid, previoussibling, previousid; if (!drag.test('.listitem-course')) { return false; } courseid = drag.getData('id'); if (iscategory) { categoryid = drop.ancestor('.listitem-category').getData('id'); category = managementconsole.getCategoryById(categoryid); if (category) { course = managementconsole.getCourseById(courseid); if (course) { category.moveCourseTo(course); } } } else if (iscourse || drop.ancestor('#course-listing')) { course = managementconsole.getCourseById(courseid); previoussibling = drag.get('previousSibling'); aftercourseid = (previoussibling) ? previoussibling.getData('id') || 0 : 0; previousid = (this.previoussibling) ? this.previoussibling.getData('id') : 0; if (aftercourseid !== previousid) { course.moveAfter(aftercourseid, previousid); } } else { } } }; Y.extend(DragDrop, Y.Base, DragDrop.prototype); /** * A managed course. * * @namespace M.course.management * @class Item * @constructor * @extends Base */ Item = function() { Item.superclass.constructor.apply(this, arguments); }; Item.NAME = 'moodle-course-management-item'; Item.CSS_PREFIX = 'management-item'; Item.ATTRS = { /** * The node for this item. * @attribute node * @type Node */ node: {}, /** * The management console. * @attribute console * @type Console */ console: {}, /** * Describes the type of this item. Should be set by the extending class. * @attribute itemname * @type {String} * @default item */ itemname: { value: 'item' } }; Item.prototype = { /** * The highlight timeout for this item if there is one. * @property highlighttimeout * @protected * @type Timeout * @default null */ highlighttimeout: null, /** * Checks and parses an AJAX response for an item. * * @method checkAjaxResponse * @protected * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Object|Boolean} */ checkAjaxResponse: function(transactionid, response, args) { if (response.status !== 200) { return false; } if (transactionid === null || args === null) { return false; } var outcome = Y.JSON.parse(response.responseText); if (outcome.error !== false) { new M.core.exception(outcome); } if (outcome.outcome === false) { return false; } return outcome; }, /** * Moves an item up by one. * * @method moveup * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ moveup: function(transactionid, response, args) { var node, nodeup, nodedown, previous, previousup, previousdown, tmpnode, outcome = this.checkAjaxResponse(transactionid, response, args); if (outcome === false) { return false; } node = this.get('node'); previous = node.previous('.listitem'); if (previous) { previous.insert(node, 'before'); previousup = previous.one(' > div a.action-moveup'); nodedown = node.one(' > div a.action-movedown'); if (!previousup || !nodedown) { // We can have two situations here: // 1. previousup is not set and nodedown is not set. This happens when there are only two courses. // 2. nodedown is not set. This happens when they are moving the bottom course up. // node up and previous down should always be there. They would be required to trigger the action. nodeup = node.one(' > div a.action-moveup'); previousdown = previous.one(' > div a.action-movedown'); if (!previousup && !nodedown) { // Ok, must be two courses. We need to switch the up and down icons. tmpnode = Y.Node.create(' '); previousdown.replace(tmpnode); nodeup.replace(previousdown); tmpnode.replace(nodeup); tmpnode.destroy(); } else if (!nodedown) { // previous down needs to be given to node. nodeup.insert(previousdown, 'after'); } } nodeup = node.one(' > div a.action-moveup'); if (nodeup) { // Try to re-focus on up. nodeup.focus(); } else { // If we can't focus up we're at the bottom, try to focus on up. nodedown = node.one(' > div a.action-movedown'); if (nodedown) { nodedown.focus(); } } this.updated(true); } else { // Aha it succeeded but this is the top item in the list. Pagination is in play! // Refresh to update the state of things. window.location.reload(); } }, /** * Moves an item down by one. * * @method movedown * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ movedown: function(transactionid, response, args) { var node, next, nodeup, nodedown, nextup, nextdown, tmpnode, outcome = this.checkAjaxResponse(transactionid, response, args); if (outcome === false) { return false; } node = this.get('node'); next = node.next('.listitem'); if (next) { node.insert(next, 'before'); nextdown = next.one(' > div a.action-movedown'); nodeup = node.one(' > div a.action-moveup'); if (!nextdown || !nodeup) { // next up and node down should always be there. They would be required to trigger the action. nextup = next.one(' > div a.action-moveup'); nodedown = node.one(' > div a.action-movedown'); if (!nextdown && !nodeup) { // We can have two situations here: // 1. nextdown is not set and nodeup is not set. This happens when there are only two courses. // 2. nodeup is not set. This happens when we are moving the first course down. // Ok, must be two courses. We need to switch the up and down icons. tmpnode = Y.Node.create(' '); nextup.replace(tmpnode); nodedown.replace(nextup); tmpnode.replace(nodedown); tmpnode.destroy(); } else if (!nodeup) { // next up needs to be given to node. nodedown.insert(nextup, 'before'); } } nodedown = node.one(' > div a.action-movedown'); if (nodedown) { // Try to ensure the up is focused again. nodedown.focus(); } else { // If we can't focus up we're at the top, try to focus on down. nodeup = node.one(' > div a.action-moveup'); if (nodeup) { nodeup.focus(); } } this.updated(true); } else { // Aha it succeeded but this is the bottom item in the list. Pagination is in play! // Refresh to update the state of things. window.location.reload(); } }, /** * Makes an item visible. * * @method show * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ show: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), hidebtn; if (outcome === false) { return false; } this.markVisible(); hidebtn = this.get('node').one('a[data-action=hide]'); if (hidebtn) { hidebtn.focus(); } this.updated(); }, /** * Marks the item as visible * @method markVisible */ markVisible: function() { this.get('node').setAttribute('data-visible', '1'); return true; }, /** * Hides an item. * * @method hide * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ hide: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), showbtn; if (outcome === false) { return false; } this.markHidden(); showbtn = this.get('node').one('a[data-action=show]'); if (showbtn) { showbtn.focus(); } this.updated(); }, /** * Marks the item as hidden. * @method makeHidden */ markHidden: function() { this.get('node').setAttribute('data-visible', '0'); return true; }, /** * Called when ever a node is updated. * * @method updated * @param {Boolean} moved True if this item was moved. */ updated: function(moved) { if (moved) { this.highlight(); } }, /** * Highlights this option for a breif time. * * @method highlight */ highlight: function() { var node = this.get('node'); node.siblings('.highlight').removeClass('highlight'); node.addClass('highlight'); if (this.highlighttimeout) { window.clearTimeout(this.highlighttimeout); } this.highlighttimeout = window.setTimeout(function() { node.removeClass('highlight'); }, 2500); } }; Y.extend(Item, Y.Base, Item.prototype); /** * A managed category. * * @namespace M.course.management * @class Category * @constructor * @extends Item */ Category = function() { Category.superclass.constructor.apply(this, arguments); }; Category.NAME = 'moodle-course-management-category'; Category.CSS_PREFIX = 'management-category'; Category.ATTRS = { /** * The category ID relating to this category. * @attribute categoryid * @type Number * @writeOnce * @default null */ categoryid: { getter: function(value, name) { if (value === null) { value = this.get('node').getData('id'); this.set(name, value); } return value; }, value: null, writeOnce: true }, /** * True if this category is the currently selected category. * @attribute selected * @type Boolean * @default null */ selected: { getter: function(value, name) { if (value === null) { value = this.get('node').getData(name); if (value === null) { value = false; } this.set(name, value); } return value; }, value: null }, /** * An array of courses belonging to this category. * @attribute courses * @type Course[] * @default Array */ courses: { validator: function(val) { return Y.Lang.isArray(val); }, value: [] } }; Category.prototype = { /** * Initialises an instance of a Category. * @method initializer */ initializer: function() { this.set('itemname', 'category'); }, /** * Returns the name of the category. * @method getName * @return {String} */ getName: function() { return this.get('node').one('a.categoryname').get('innerHTML'); }, /** * Registers a course as belonging to this category. * @method registerCourse * @param {Course} course */ registerCourse: function(course) { var courses = this.get('courses'); courses.push(course); this.set('courses', courses); }, /** * Handles a category related event. * * @method handle * @param {String} action * @param {EventFacade} e * @return {Boolean} */ handle: function(action, e) { var catarg = {categoryid: this.get('categoryid')}, selected = this.get('console').get('activecategoryid'); if (selected && selected !== catarg.categoryid) { catarg.selectedcategory = selected; } switch (action) { case 'moveup': e.preventDefault(); this.get('console').performAjaxAction('movecategoryup', catarg, this.moveup, this); break; case 'movedown': e.preventDefault(); this.get('console').performAjaxAction('movecategorydown', catarg, this.movedown, this); break; case 'show': e.preventDefault(); this.get('console').performAjaxAction('showcategory', catarg, this.show, this); break; case 'hide': e.preventDefault(); var courseCount = this.get('node').getData('course-count'); if (courseCount === '0') { this.get('console').performAjaxAction('hidecategory', catarg, this.hide, this); break; } var warningStr = courseCount === '1' ? 'hidecategoryone' : 'hidecategorymany'; var warningParams = { category: this.get('node').getData('category-name'), coursecount: courseCount, }; require(['core/notification', 'core/str'], function(Notification, Str) { Notification.saveCancelPromise( Str.get_string('hidecategory'), Str.get_string(warningStr, 'core', warningParams), Str.get_string('hide') ).then(function() { this.get('console').performAjaxAction('hidecategory', catarg, this.hide, this); }.bind(this) ).catch(function() { // User cancelled, no action needed. }); }.bind(this)); break; case 'expand': e.preventDefault(); if (this.get('node').getData('expanded') === '0') { this.get('node').setAttribute('data-expanded', '1').setData('expanded', 'true'); this.get('console').performAjaxAction('getsubcategorieshtml', catarg, this.loadSubcategories, this); } this.expand(); break; case 'collapse': e.preventDefault(); this.collapse(); break; case 'select': var c = this.get('console'), movecategoryto = c.get('categorylisting').one('#menumovecategoriesto'); // If any category is selected and there are more then one categories. if (movecategoryto) { if (c.isCategorySelected(e.currentTarget) && c.get('categories').length > 1) { movecategoryto.removeAttribute('disabled'); } else { movecategoryto.setAttribute('disabled', true); } c.handleBulkSortByaction(); } break; default: return false; } }, /** * Expands the category making its sub categories visible. * @method expand */ expand: function() { var node = this.get('node'), action = node.one('a[data-action=expand]'), ul = node.one('ul[role=group]'); node.removeClass('collapsed').setAttribute('aria-expanded', 'true'); action.setAttribute('data-action', 'collapse').setAttrs({ title: M.util.get_string('collapsecategory', 'moodle', this.getName()) }); require(['core/str', 'core/templates', 'core/notification'], function(Str, Templates, Notification) { Str.get_string('collapse', 'core') .then(function(string) { return Templates.renderPix('t/switch_minus', 'core', string); }) .then(function(html) { html = Y.Node.create(html).addClass('tree-icon').getDOMNode().outerHTML; return action.set('innerHTML', html); }).fail(Notification.exception); }); if (ul) { ul.setAttribute('aria-hidden', 'false'); } this.get('console').performAjaxAction('expandcategory', {categoryid: this.get('categoryid')}, null, this); }, /** * Collapses the category making its sub categories hidden. * @method collapse */ collapse: function() { var node = this.get('node'), action = node.one('a[data-action=collapse]'), ul = node.one('ul[role=group]'); node.addClass('collapsed').setAttribute('aria-expanded', 'false'); action.setAttribute('data-action', 'expand').setAttrs({ title: M.util.get_string('expandcategory', 'moodle', this.getName()) }); require(['core/str', 'core/templates', 'core/notification'], function(Str, Templates, Notification) { Str.get_string('expand', 'core') .then(function(string) { return Templates.renderPix('t/switch_plus', 'core', string); }) .then(function(html) { html = Y.Node.create(html).addClass('tree-icon').getDOMNode().outerHTML; return action.set('innerHTML', html); }).fail(Notification.exception); }); if (ul) { ul.setAttribute('aria-hidden', 'true'); } this.get('console').performAjaxAction('collapsecategory', {categoryid: this.get('categoryid')}, null, this); }, /** * Loads sub categories provided by an AJAX request.. * * @method loadSubcategories * @protected * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} Returns true on success - false otherwise. */ loadSubcategories: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), node = this.get('node'), managementconsole = this.get('console'), ul, actionnode; if (outcome === false) { return false; } node.append(outcome.html); managementconsole.initialiseCategories(node); if (M.core && M.core.actionmenu && M.core.actionmenu.newDOMNode) { M.core.actionmenu.newDOMNode(node); } ul = node.one('ul[role=group]'); actionnode = node.one('a[data-action=collapse]'); if (ul && actionnode) { actionnode.setAttribute('aria-controls', ul.generateID()); } return true; }, /** * Moves the course to this category. * * @method moveCourseTo * @param {Course} course */ moveCourseTo: function(course) { require(['core/notification'], function(Notification) { Notification.saveCancelPromise( M.util.get_string('confirmation', 'admin'), M.util.get_string('confirmcoursemove', 'moodle', { course: course.getName(), category: this.getName(), }), M.util.get_string('move', 'moodle') ).then(function() { this.get('console').performAjaxAction('movecourseintocategory', { courseid: course.get('courseid'), categoryid: this.get('categoryid'), }, this.completeMoveCourse, this); return; }.bind(this)).catch(function() { // User cancelled. }); }.bind(this)); }, /** * Completes moving a course to this category. * @method completeMoveCourse * @protected * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ completeMoveCourse: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), managementconsole = this.get('console'), category, course, totals; if (outcome === false) { return false; } course = managementconsole.getCourseById(args.courseid); if (!course) { return false; } this.highlight(); if (course) { if (outcome.paginationtotals) { totals = managementconsole.get('courselisting').one('.listing-pagination-totals'); if (totals) { totals.set('innerHTML', outcome.paginationtotals); } } if (outcome.totalcatcourses !== 'undefined') { totals = this.get('node').one('.course-count span'); if (totals) { totals.set('innerHTML', totals.get('innerHTML').replace(/^\d+/, outcome.totalcatcourses)); } } if (typeof outcome.fromcatcoursecount !== 'undefined') { category = managementconsole.get('activecategoryid'); category = managementconsole.getCategoryById(category); if (category) { totals = category.get('node').one('.course-count span'); if (totals) { totals.set('innerHTML', totals.get('innerHTML').replace(/^\d+/, outcome.fromcatcoursecount)); } } } course.remove(); } return true; }, /** * Makes an item visible. * * @method show * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ show: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), hidebtn; if (outcome === false) { return false; } this.markVisible(); hidebtn = this.get('node').one('a[data-action=hide]'); if (hidebtn) { hidebtn.focus(); } if (outcome.categoryvisibility) { this.updateChildVisibility(outcome.categoryvisibility); } if (outcome.coursevisibility) { this.updateCourseVisiblity(outcome.coursevisibility); } this.updated(); }, /** * Hides an item. * * @method hide * @param {Number} transactionid The transaction ID of the AJAX request (unique) * @param {Object} response The response from the AJAX request. * @param {Object} args The arguments given to the request. * @return {Boolean} */ hide: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), showbtn; if (outcome === false) { return false; } this.markHidden(); showbtn = this.get('node').one('a[data-action=show]'); if (showbtn) { showbtn.focus(); } if (outcome.categoryvisibility) { this.updateChildVisibility(outcome.categoryvisibility); } if (outcome.coursevisibility) { this.updateCourseVisiblity(outcome.coursevisibility); } this.updated(); }, /** * Updates the visibility of child courses if required. * @method updateCourseVisiblity * @chainable * @param courses */ updateCourseVisiblity: function(courses) { var managementconsole = this.get('console'), key, course; try { for (key in courses) { if (typeof courses[key] === 'object') { course = managementconsole.getCourseById(courses[key].id); if (course) { if (courses[key].visible === "1") { course.markVisible(); } else { course.markHidden(); } } } } } catch (err) { } return this; }, /** * Updates the visibility of subcategories if required. * @method updateChildVisibility * @chainable * @param categories */ updateChildVisibility: function(categories) { var managementconsole = this.get('console'), key, category; try { for (key in categories) { if (typeof categories[key] === 'object') { category = managementconsole.getCategoryById(categories[key].id); if (category) { if (categories[key].visible === "1") { category.markVisible(); } else { category.markHidden(); } } } } } catch (err) { } return this; } }; Y.extend(Category, Item, Category.prototype); /** * A managed course. * * @namespace M.course.management * @class Course * @constructor * @extends Item */ Course = function() { Course.superclass.constructor.apply(this, arguments); }; Course.NAME = 'moodle-course-management-course'; Course.CSS_PREFIX = 'management-course'; Course.ATTRS = { /** * The course ID of this course. * @attribute courseid * @type Number */ courseid: {}, /** * True if this is the selected course. * @attribute selected * @type Boolean * @default null */ selected: { getter: function(value, name) { if (value === null) { value = this.get('node').getData(name); this.set(name, value); } return value; }, value: null }, node: { }, /** * The management console tracking this course. * @attribute console * @type Console * @writeOnce */ console: { writeOnce: 'initOnly' }, /** * The category this course belongs to. * @attribute category * @type Category * @writeOnce */ category: { writeOnce: 'initOnly' } }; Course.prototype = { /** * Initialises the new course instance. * @method initializer */ initializer: function() { var node = this.get('node'), category = this.get('category'); this.set('courseid', node.getData('id')); if (category && category.registerCourse) { category.registerCourse(this); } this.set('itemname', 'course'); }, /** * Returns the name of the course. * @method getName * @return {String} */ getName: function() { return this.get('node').one('a.coursename').get('innerHTML'); }, /** * Handles an event relating to this course. * @method handle * @param {String} action * @param {EventFacade} e * @return {Boolean} */ handle: function(action, e) { var managementconsole = this.get('console'), args = {courseid: this.get('courseid')}; switch (action) { case 'moveup': e.halt(); managementconsole.performAjaxAction('movecourseup', args, this.moveup, this); break; case 'movedown': e.halt(); managementconsole.performAjaxAction('movecoursedown', args, this.movedown, this); break; case 'show': e.halt(); managementconsole.performAjaxAction('showcourse', args, this.show, this); break; case 'hide': e.halt(); managementconsole.performAjaxAction('hidecourse', args, this.hide, this); break; case 'select': var c = this.get('console'), movetonode = c.get('courselisting').one('#menumovecoursesto'); if (movetonode) { if (c.isCourseSelected(e.currentTarget)) { movetonode.removeAttribute('disabled'); } else { movetonode.setAttribute('disabled', true); } } break; default: return false; } }, /** * Removes this course. * @method remove */ remove: function() { this.get('console').removeCourseById(this.get('courseid')); this.get('node').remove(); }, /** * Moves this course after another course. * * @method moveAfter * @param {Number} moveaftercourse The course to move after or 0 to put it at the top. * @param {Number} previousid the course it was previously after in case we need to revert. */ moveAfter: function(moveaftercourse, previousid) { var managementconsole = this.get('console'), args = { courseid: this.get('courseid'), moveafter: moveaftercourse, previous: previousid }; managementconsole.performAjaxAction('movecourseafter', args, this.moveAfterResponse, this); }, /** * Performs the actual move. * * @method moveAfterResponse * @protected * @param {Number} transactionid The transaction ID for the request. * @param {Object} response The response to the request. * @param {Objects} args The arguments that were given with the request. * @return {Boolean} */ moveAfterResponse: function(transactionid, response, args) { var outcome = this.checkAjaxResponse(transactionid, response, args), node = this.get('node'), previous; if (outcome === false) { previous = node.ancestor('ul').one('li[data-id=' + args.previous + ']'); if (previous) { // After the last previous. previous.insertAfter(node, 'after'); } else { // Start of the list. node.ancestor('ul').one('li').insert(node, 'before'); } return false; } this.highlight(); } }; Y.extend(Course, Item, Course.prototype); }, '@VERSION@', { "requires": [ "base", "node", "io-base", "moodle-core-notification-exception", "json-parse", "dd-constrain", "dd-proxy", "dd-drop", "dd-delegate", "node-event-delegate" ] }); moodle-course-util-base/moodle-course-util-base-debug.js000064400000001104152266437370017301 0ustar00YUI.add('moodle-course-util-base', function (Y, NAME) { /** * The Moodle.core_course.util classes provide course-related utility functions. * * TODO: remove this module as part of MDL-83627. * * @module moodle-course-util * @main */ Y.namespace('Moodle.core_course.util'); Y.log( 'YUI Moodle.core_course.util is deprecated. Please, add support_components to your course format.', 'warn', 'moodle-course-coursebase' ); /** * A collection of general utility functions for use in course. * * @class Moodle.core_course.util * @static */ }, '@VERSION@'); moodle-course-util-base/moodle-course-util-base.js000064400000000647152266437370016230 0ustar00YUI.add('moodle-course-util-base', function (Y, NAME) { /** * The Moodle.core_course.util classes provide course-related utility functions. * * TODO: remove this module as part of MDL-83627. * * @module moodle-course-util * @main */ Y.namespace('Moodle.core_course.util'); /** * A collection of general utility functions for use in course. * * @class Moodle.core_course.util * @static */ }, '@VERSION@'); moodle-course-util-base/moodle-course-util-base-min.js000064400000000145152266437370017002 0ustar00YUI.add("moodle-course-util-base",function(e,o){e.namespace("Moodle.core_course.util")},"@VERSION@");content_select.min.js000064400000001335152266453450010713 0ustar00define("enrol_lti/content_select",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0;_exports.init=()=>{document.addEventListener("change",(e=>{if(e.target.matches("input[type='checkbox'][name^='modules']")){const value=e.target.value,gradecheckbox=document.querySelector("input[type='checkbox'][name^='grades'][value='"+value+"']");gradecheckbox&&(gradecheckbox.checked=e.target.checked)}if(e.target.matches("input[type='checkbox'][name^='grades']")){const value=e.target.value,modcheckbox=document.querySelector("input[type='checkbox'][name^='modules'][value='"+value+"']");e.target.checked&&(modcheckbox.checked=!0)}}))}})); //# sourceMappingURL=content_select.min.js.maptool_endpoints.min.js000064400000001474152266453450010746 0ustar00define("enrol_lti/tool_endpoints",["exports","core/copy_to_clipboard"],(function(_exports,_copy_to_clipboard){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0; /** * Module supporting the dynamic and manual registration URLs in the tool registration admin setting. * * @module enrol_lti/tool_endpoints * @copyright 2021 Jake Dallimore * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ const SELECTORS_URL_VALUE='[id^="lti_tool_endpoint_url_"]',focusURLHandler=event=>{const triggerElement=event.target.closest(SELECTORS_URL_VALUE);null!==triggerElement&&(event.preventDefault(),triggerElement.select())};_exports.init=()=>{document.addEventListener("focusin",focusURLHandler)}})); //# sourceMappingURL=tool_endpoints.min.js.maptool_endpoints.min.js.map000064400000004270152266453450011517 0ustar00{"version":3,"file":"tool_endpoints.min.js","sources":["../src/tool_endpoints.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 * Module supporting the dynamic and manual registration URLs in the tool registration admin setting.\n *\n * @module enrol_lti/tool_endpoints\n * @copyright 2021 Jake Dallimore \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport 'core/copy_to_clipboard';\n\n/**\n * DOM Selectors.\n * @type {{URL_VALUE: string}}\n */\nconst SELECTORS = {\n URL_VALUE: '[id^=\"lti_tool_endpoint_url_\"]',\n};\n\n/**\n * Focus handler for the registration URL field, enabling auto select of text on click.\n *\n * @param {Event} event a click event.\n */\nconst focusURLHandler = (event) => {\n const triggerElement = event.target.closest(SELECTORS.URL_VALUE);\n if (triggerElement === null) {\n return;\n }\n event.preventDefault();\n\n triggerElement.select();\n};\n\n/**\n * Initialise the tool registration page, attaching handlers, etc.\n */\nexport const init = () => {\n // Event delegation supporting the select on focus behaviour (with text selection permitted on subsequent clicks).\n document.addEventListener('focusin', focusURLHandler);\n};\n"],"names":["SELECTORS","focusURLHandler","event","triggerElement","target","closest","preventDefault","select","document","addEventListener"],"mappings":";;;;;;;;MA2BMA,oBACS,iCAQTC,gBAAmBC,cACfC,eAAiBD,MAAME,OAAOC,QAAQL,qBACrB,OAAnBG,iBAGJD,MAAMI,iBAENH,eAAeI,yBAMC,KAEhBC,SAASC,iBAAiB,UAAWR"}content_select.min.js.map000064400000005105152266453450011466 0ustar00{"version":3,"file":"content_select.min.js","sources":["../src/content_select.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 * Module providing checkbox autoselection behaviour to the table on the select content deep linking view, launch_deeplink.php.\n *\n * @module enrol_lti/content_select\n * @copyright 2021 Jake Dallimore \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n// Register the checkbox change events allowing the automatic selection/deselection of the\n// 'add to gradebook' and 'add to course' checkboxes when selecting an activity/resource.\nconst registerEventHandlers = () => {\n document.addEventListener('change', e => {\n if (e.target.matches(\"input[type='checkbox'][name^='modules']\")) {\n const value = e.target.value;\n const gradecheckbox = document.querySelector(\"input[type='checkbox'][name^='grades'][value='\" + value + \"']\");\n if (gradecheckbox) {\n gradecheckbox.checked = e.target.checked;\n }\n }\n\n if (e.target.matches(\"input[type='checkbox'][name^='grades']\")) {\n const value = e.target.value;\n const modcheckbox = document.querySelector(\"input[type='checkbox'][name^='modules'][value='\" + value + \"']\");\n if (e.target.checked) {\n modcheckbox.checked = true;\n }\n }\n });\n};\n\nexport const init = () => {\n registerEventHandlers();\n};\n"],"names":["document","addEventListener","e","target","matches","value","gradecheckbox","querySelector","checked","modcheckbox"],"mappings":"6JA2CoB,KAnBhBA,SAASC,iBAAiB,UAAUC,OAC5BA,EAAEC,OAAOC,QAAQ,2CAA4C,OACvDC,MAAQH,EAAEC,OAAOE,MACjBC,cAAgBN,SAASO,cAAc,iDAAmDF,MAAQ,MACpGC,gBACAA,cAAcE,QAAUN,EAAEC,OAAOK,YAIrCN,EAAEC,OAAOC,QAAQ,0CAA2C,OACtDC,MAAQH,EAAEC,OAAOE,MACjBI,YAAcT,SAASO,cAAc,kDAAoDF,MAAQ,MACnGH,EAAEC,OAAOK,UACTC,YAAYD,SAAU"}gateways_modal.min.js000064400000011240152266470730010676 0ustar00define("core_payment/gateways_modal",["exports","core/templates","core/str","./repository","./selectors","core/modal_events","core_payment/events","core/toast","core/notification","./modal_gateways"],(function(_exports,_templates,_str,_repository,_selectors,_modal_events,_events,_toast,_notification,_modal_gateways){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_selectors=_interopRequireDefault(_selectors),_modal_events=_interopRequireDefault(_modal_events),_events=_interopRequireDefault(_events),_notification=_interopRequireDefault(_notification),_modal_gateways=_interopRequireDefault(_modal_gateways);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const show=async function(rootNode){let{focusOnClose:focusOnClose=null}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const body=await _templates.default.render("core_payment/gateways_modal",{}),modal=await _modal_gateways.default.create({title:(0,_str.getString)("selectpaymenttype","core_payment"),body:body,show:!0,removeOnClose:!0}),rootElement=modal.getRoot()[0];(0,_toast.addToastRegion)(rootElement),modal.getRoot().on(_modal_events.default.hidden,(()=>{null==focusOnClose||focusOnClose.focus()})),modal.getRoot().on(_events.default.proceed,(async e=>{e.preventDefault();const gateway=(rootElement.querySelector(_selectors.default.values.gateway)||{value:""}).value;gateway?processPayment(gateway,rootNode.dataset.component,rootNode.dataset.paymentarea,rootNode.dataset.itemid,rootNode.dataset.description).then((message=>{modal.hide(),_notification.default.addNotification({message:message,type:"success"}),location.href=rootNode.dataset.successurl})).catch((message=>_notification.default.alert("",message))):(0,_toast.add)((0,_str.getString)("nogatewayselected","core_payment"),{type:"warning"})})),rootElement.addEventListener("change",(e=>{e.target.matches(_selectors.default.elements.gateways)&&updateCostRegion(rootElement,rootNode.dataset.cost)}));const gateways=await(0,_repository.getAvailableGateways)(rootNode.dataset.component,rootNode.dataset.paymentarea,rootNode.dataset.itemid),context={gateways:gateways},{html:html,js:js}=await _templates.default.renderForPromise("core_payment/gateways",context);_templates.default.replaceNodeContents(rootElement.querySelector(_selectors.default.regions.gatewaysContainer),html,js),selectSingleGateway(rootElement),await updateCostRegion(rootElement,rootNode.dataset.cost)},selectSingleGateway=root=>{const gateways=root.querySelectorAll(_selectors.default.elements.gateways);1==gateways.length&&(gateways[0].checked=!0)},updateCostRegion=async function(root){let defaultCost=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";const gatewayElement=root.querySelector(_selectors.default.values.gateway),surcharge=parseInt((gatewayElement||{dataset:{surcharge:0}}).dataset.surcharge),cost=(gatewayElement||{dataset:{cost:defaultCost}}).dataset.cost,valueStr=surcharge?await(0,_str.getString)("feeincludesurcharge","core_payment",{fee:cost,surcharge:surcharge}):cost,surchargeStr=await(0,_str.getString)("labelvalue","core",{label:await(0,_str.getString)("cost","core"),value:valueStr}),{html:html,js:js}=await _templates.default.renderForPromise("core_payment/fee_breakdown",{surchargestr:surchargeStr});_templates.default.replaceNodeContents(root.querySelector(_selectors.default.regions.costContainer),html,js)},processPayment=async(gateway,component,paymentArea,itemId,description)=>(await("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require(["paygw_".concat(gateway,"/gateways_modal")],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require("paygw_".concat(gateway,"/gateways_modal"))):Promise.resolve(_systemImportTransformerGlobalIdentifier["paygw_".concat(gateway,"/gateways_modal")]))).process(component,paymentArea,itemId,description),init=()=>{init.initialised||(init.initialised=!0,document.addEventListener("click",(e=>{const gatewayTrigger=e.target.closest('[data-action="core_payment/triggerPayment"]');gatewayTrigger&&(e.preventDefault(),show(gatewayTrigger,{focusOnClose:e.target}))})))};_exports.init=init,init.initialised=!1})); //# sourceMappingURL=gateways_modal.min.js.mapmodal_gateways.min.js000064400000003603152266470730010702 0ustar00define("core_payment/modal_gateways",["exports","jquery","core/custom_interaction_events","core/modal","core/modal_events","core_payment/events"],(function(_exports,_jquery,_custom_interaction_events,_modal,_modal_events,_events){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,_jquery=_interopRequireDefault(_jquery),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_modal=_interopRequireDefault(_modal),_modal_events=_interopRequireDefault(_modal_events),_events=_interopRequireDefault(_events);const SELECTORS_PROCEED_BUTTON='[data-action="proceed"]',SELECTORS_CANCEL_BUTTON='[data-action="cancel"]';class ModalGateways extends _modal.default{constructor(root){super(root)}registerEventListeners(){super.registerEventListeners(),this.getModal().on(_custom_interaction_events.default.events.activate,SELECTORS_PROCEED_BUTTON,((e,data)=>{var proceedEvent=_jquery.default.Event(_events.default.proceed);this.getRoot().trigger(proceedEvent,this),proceedEvent.isDefaultPrevented()||(this.hide(),data.originalEvent.preventDefault())})),this.getModal().on(_custom_interaction_events.default.events.activate,SELECTORS_CANCEL_BUTTON,((e,data)=>{var cancelEvent=_jquery.default.Event(_modal_events.default.cancel);this.getRoot().trigger(cancelEvent,this),cancelEvent.isDefaultPrevented()||(this.hide(),data.originalEvent.preventDefault())}))}}return _exports.default=ModalGateways,_defineProperty(ModalGateways,"TYPE","core_payment-modal_gateways"),_defineProperty(ModalGateways,"TEMPLATE","core_payment/modal_gateways"),ModalGateways.registerModalType(),_exports.default})); //# sourceMappingURL=modal_gateways.min.js.mapgateways_modal.min.js.map000064400000023576152266470730011471 0ustar00{"version":3,"file":"gateways_modal.min.js","sources":["../src/gateways_modal.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 * Contain the logic for the gateways modal.\n *\n * @module core_payment/gateways_modal\n * @copyright 2019 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport {getAvailableGateways} from './repository';\nimport Selectors from './selectors';\nimport ModalEvents from 'core/modal_events';\nimport PaymentEvents from 'core_payment/events';\nimport {add as addToast, addToastRegion} from 'core/toast';\nimport Notification from 'core/notification';\nimport ModalGateways from './modal_gateways';\n\n/**\n * Register event listeners for the module.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const gatewayTrigger = e.target.closest('[data-action=\"core_payment/triggerPayment\"]');\n if (gatewayTrigger) {\n e.preventDefault();\n\n show(gatewayTrigger, {focusOnClose: e.target});\n }\n });\n};\n\n/**\n * Shows the gateway selector modal.\n *\n * @param {HTMLElement} rootNode\n * @param {Object} options - Additional options\n * @param {HTMLElement} options.focusOnClose The element to focus on when the modal is closed.\n */\nconst show = async(rootNode, {\n focusOnClose = null,\n} = {}) => {\n\n // Load upfront, so we don't try to inject the internal content into a possibly-not-yet-resolved promise.\n const body = await Templates.render('core_payment/gateways_modal', {});\n\n const modal = await ModalGateways.create({\n title: getString('selectpaymenttype', 'core_payment'),\n body: body,\n show: true,\n removeOnClose: true,\n });\n\n const rootElement = modal.getRoot()[0];\n addToastRegion(rootElement);\n\n modal.getRoot().on(ModalEvents.hidden, () => {\n focusOnClose?.focus();\n });\n\n modal.getRoot().on(PaymentEvents.proceed, async(e) => {\n e.preventDefault();\n const gateway = (rootElement.querySelector(Selectors.values.gateway) || {value: ''}).value;\n\n if (gateway) {\n processPayment(\n gateway,\n rootNode.dataset.component,\n rootNode.dataset.paymentarea,\n rootNode.dataset.itemid,\n rootNode.dataset.description\n ).then((message) => {\n modal.hide();\n Notification.addNotification({\n message,\n type: 'success',\n });\n location.href = rootNode.dataset.successurl;\n\n return;\n }).catch(message => Notification.alert('', message));\n } else {\n // We cannot use await in the following line.\n // The reason is that we are preventing the default action of the save event being triggered,\n // therefore we cannot define the event handler function asynchronous.\n addToast(getString('nogatewayselected', 'core_payment'), {\n type: 'warning',\n });\n }\n });\n\n // Re-calculate the cost when gateway is changed.\n rootElement.addEventListener('change', e => {\n if (e.target.matches(Selectors.elements.gateways)) {\n updateCostRegion(rootElement, rootNode.dataset.cost);\n }\n });\n\n const gateways = await getAvailableGateways(rootNode.dataset.component, rootNode.dataset.paymentarea, rootNode.dataset.itemid);\n const context = {\n gateways\n };\n\n const {html, js} = await Templates.renderForPromise('core_payment/gateways', context);\n Templates.replaceNodeContents(rootElement.querySelector(Selectors.regions.gatewaysContainer), html, js);\n selectSingleGateway(rootElement);\n await updateCostRegion(rootElement, rootNode.dataset.cost);\n};\n\n/**\n * Auto-select the gateway if there is only one gateway.\n *\n * @param {HTMLElement} root An HTMLElement that contains the cost region\n */\nconst selectSingleGateway = root => {\n const gateways = root.querySelectorAll(Selectors.elements.gateways);\n\n if (gateways.length == 1) {\n gateways[0].checked = true;\n }\n};\n\n/**\n * Shows the cost of the item the user is purchasing in the cost region.\n *\n * @param {HTMLElement} root An HTMLElement that contains the cost region\n * @param {string} defaultCost The default cost that is going to be displayed if no gateway is selected\n * @returns {Promise}\n */\nconst updateCostRegion = async(root, defaultCost = '') => {\n const gatewayElement = root.querySelector(Selectors.values.gateway);\n const surcharge = parseInt((gatewayElement || {dataset: {surcharge: 0}}).dataset.surcharge);\n const cost = (gatewayElement || {dataset: {cost: defaultCost}}).dataset.cost;\n const valueStr = surcharge ? await getString('feeincludesurcharge', 'core_payment', {fee: cost, surcharge: surcharge}) : cost;\n\n const surchargeStr = await getString('labelvalue', 'core',\n {\n label: await getString('cost', 'core'),\n value: valueStr\n }\n );\n\n const {html, js} = await Templates.renderForPromise('core_payment/fee_breakdown', {surchargestr: surchargeStr});\n Templates.replaceNodeContents(root.querySelector(Selectors.regions.costContainer), html, js);\n};\n\n/**\n * Process payment using the selected gateway.\n *\n * @param {string} gateway The gateway to be used for payment\n * @param {string} component Name of the component that the itemId belongs to\n * @param {string} paymentArea Name of the area in the component that the itemId belongs to\n * @param {number} itemId An internal identifier that is used by the component\n * @param {string} description Description of the payment\n * @returns {Promise}\n */\nconst processPayment = async(gateway, component, paymentArea, itemId, description) => {\n const paymentMethod = await import(`paygw_${gateway}/gateways_modal`);\n return paymentMethod.process(component, paymentArea, itemId, description);\n};\n\n/**\n * Set up the payment actions.\n */\nexport const init = () => {\n if (!init.initialised) {\n // Event listeners should only be registered once.\n init.initialised = true;\n registerEventListeners();\n }\n};\n\n/**\n * Whether the init function was called before.\n *\n * @static\n * @type {boolean}\n */\ninit.initialised = false;\n"],"names":["show","async","rootNode","focusOnClose","body","Templates","render","modal","ModalGateways","create","title","removeOnClose","rootElement","getRoot","on","ModalEvents","hidden","focus","PaymentEvents","proceed","e","preventDefault","gateway","querySelector","Selectors","values","value","processPayment","dataset","component","paymentarea","itemid","description","then","message","hide","addNotification","type","location","href","successurl","catch","Notification","alert","addEventListener","target","matches","elements","gateways","updateCostRegion","cost","context","html","js","renderForPromise","replaceNodeContents","regions","gatewaysContainer","selectSingleGateway","root","querySelectorAll","length","checked","defaultCost","gatewayElement","surcharge","parseInt","valueStr","fee","surchargeStr","label","surchargestr","costContainer","paymentArea","itemId","process","init","initialised","document","gatewayTrigger","closest"],"mappings":"y5BAsDMA,KAAOC,eAAMC,cAAUC,aACzBA,aAAe,6DACf,SAGMC,WAAaC,mBAAUC,OAAO,8BAA+B,IAE7DC,YAAcC,wBAAcC,OAAO,CACrCC,OAAO,kBAAU,oBAAqB,gBACtCN,KAAMA,KACNJ,MAAM,EACNW,eAAe,IAGbC,YAAcL,MAAMM,UAAU,6BACrBD,aAEfL,MAAMM,UAAUC,GAAGC,sBAAYC,QAAQ,KACnCb,MAAAA,cAAAA,aAAcc,WAGlBV,MAAMM,UAAUC,GAAGI,gBAAcC,SAASlB,MAAAA,IACtCmB,EAAEC,uBACIC,SAAWV,YAAYW,cAAcC,mBAAUC,OAAOH,UAAY,CAACI,MAAO,KAAKA,MAEjFJ,QACAK,eACIL,QACApB,SAAS0B,QAAQC,UACjB3B,SAAS0B,QAAQE,YACjB5B,SAAS0B,QAAQG,OACjB7B,SAAS0B,QAAQI,aACnBC,MAAMC,UACJ3B,MAAM4B,6BACOC,gBAAgB,CACzBF,QAAAA,QACAG,KAAM,YAEVC,SAASC,KAAOrC,SAAS0B,QAAQY,cAGlCC,OAAMP,SAAWQ,sBAAaC,MAAM,GAAIT,2BAKlC,kBAAU,oBAAqB,gBAAiB,CACrDG,KAAM,eAMlBzB,YAAYgC,iBAAiB,UAAUxB,IAC/BA,EAAEyB,OAAOC,QAAQtB,mBAAUuB,SAASC,WACpCC,iBAAiBrC,YAAaV,SAAS0B,QAAQsB,eAIjDF,eAAiB,oCAAqB9C,SAAS0B,QAAQC,UAAW3B,SAAS0B,QAAQE,YAAa5B,SAAS0B,QAAQG,QACjHoB,QAAU,CACZH,SAAAA,WAGEI,KAACA,KAADC,GAAOA,UAAYhD,mBAAUiD,iBAAiB,wBAAyBH,4BACnEI,oBAAoB3C,YAAYW,cAAcC,mBAAUgC,QAAQC,mBAAoBL,KAAMC,IACpGK,oBAAoB9C,mBACdqC,iBAAiBrC,YAAaV,SAAS0B,QAAQsB,OAQnDQ,oBAAsBC,aAClBX,SAAWW,KAAKC,iBAAiBpC,mBAAUuB,SAASC,UAEnC,GAAnBA,SAASa,SACTb,SAAS,GAAGc,SAAU,IAWxBb,iBAAmBhD,eAAM0D,UAAMI,mEAAc,SACzCC,eAAiBL,KAAKpC,cAAcC,mBAAUC,OAAOH,SACrD2C,UAAYC,UAAUF,gBAAkB,CAACpC,QAAS,CAACqC,UAAW,KAAKrC,QAAQqC,WAC3Ef,MAAQc,gBAAkB,CAACpC,QAAS,CAACsB,KAAMa,eAAenC,QAAQsB,KAClEiB,SAAWF,gBAAkB,kBAAU,sBAAuB,eAAgB,CAACG,IAAKlB,KAAMe,UAAWA,YAAcf,KAEnHmB,mBAAqB,kBAAU,aAAc,OAC/C,CACIC,YAAa,kBAAU,OAAQ,QAC/B5C,MAAOyC,YAITf,KAACA,KAADC,GAAOA,UAAYhD,mBAAUiD,iBAAiB,6BAA8B,CAACiB,aAAcF,kCACvFd,oBAAoBI,KAAKpC,cAAcC,mBAAUgC,QAAQgB,eAAgBpB,KAAMC,KAavF1B,eAAiB1B,MAAMqB,QAASO,UAAW4C,YAAaC,OAAQ1C,qPACtBV,sUAAAA,sGAAAA,+BACvBqD,QAAQ9C,UAAW4C,YAAaC,OAAQ1C,aAMpD4C,KAAO,KACXA,KAAKC,cAEND,KAAKC,aAAc,EAjJvBC,SAASlC,iBAAiB,SAASxB,UACzB2D,eAAiB3D,EAAEyB,OAAOmC,QAAQ,+CACpCD,iBACA3D,EAAEC,iBAEFrB,KAAK+E,eAAgB,CAAC5E,aAAciB,EAAEyB,kCAuJlD+B,KAAKC,aAAc"}selectors.min.js.map000064400000005065152266470730010465 0ustar00{"version":3,"file":"selectors.min.js","sources":["../src/selectors.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 * Define all of the selectors we will be using on the grading interface.\n *\n * @module gradereport_singleview/selectors\n * @copyright 2022 Ilya Tregubov \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 actions: {\n bulkaction: getDataSelector('role', 'bulkaction'),\n },\n elements: {\n override: 'input[type=checkbox][name^=override]',\n exclude: 'input[type=checkbox][name^=exclude]',\n modalsave: '[data-action=\"save\"]',\n warningcheckbox: 'input[type=\"checkbox\"]',\n modalformdata: '.formdata',\n modalradio: 'input[type=\"radio\"]',\n modalinput: 'input[type=\"text\"]',\n modalradiochecked: 'input[type=\"radio\"]:checked',\n enablebulkinsert: 'input[type=\"checkbox\"][name^=bulk]',\n formradio: 'select[name^=bulk]',\n modalgrade: '.form-control',\n formgrade: 'input[type=\"text\"][name^=bulk]',\n formsave: 'input[type=\"submit\"]',\n },\n};\n"],"names":["name","value","actions","bulkaction","elements","override","exclude","modalsave","warningcheckbox","modalformdata","modalradio","modalinput","modalradiochecked","enablebulkinsert","formradio","modalgrade","formgrade","formsave"],"mappings":"8JA8ByBA,KAAMC,eAIhB,CACXC,QAAS,CACLC,YANiBH,KAMW,OANLC,MAMa,6BALxBD,kBAASC,cAOzBG,SAAU,CACNC,SAAU,uCACVC,QAAS,sCACTC,UAAW,uBACXC,gBAAiB,yBACjBC,cAAe,YACfC,WAAY,sBACZC,WAAY,qBACZC,kBAAmB,8BACnBC,iBAAkB,qCAClBC,UAAW,qBACXC,WAAY,gBACZC,UAAW,iCACXC,SAAU"}selectors.min.js000064400000001561152266470730007706 0ustar00define("gradereport_singleview/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;var name,value,_default={actions:{bulkaction:(name="role",value="bulkaction","[data-".concat(name,'="').concat(value,'"]'))},elements:{override:"input[type=checkbox][name^=override]",exclude:"input[type=checkbox][name^=exclude]",modalsave:'[data-action="save"]',warningcheckbox:'input[type="checkbox"]',modalformdata:".formdata",modalradio:'input[type="radio"]',modalinput:'input[type="text"]',modalradiochecked:'input[type="radio"]:checked',enablebulkinsert:'input[type="checkbox"][name^=bulk]',formradio:"select[name^=bulk]",modalgrade:".form-control",formgrade:'input[type="text"][name^=bulk]',formsave:'input[type="submit"]'}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=selectors.min.js.mapmodal_gateways.min.js.map000064400000007035152266470730011461 0ustar00{"version":3,"file":"modal_gateways.min.js","sources":["../src/modal_gateways.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 * Contain the logic for the gateways modal: A modal with proceed and cancel buttons.\n *\n * @module core_payment/modal_gateways\n * @copyright 2020 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport CustomEvents from 'core/custom_interaction_events';\nimport Modal from 'core/modal';\nimport ModalEvents from 'core/modal_events';\nimport PaymentEvents from 'core_payment/events';\n\nconst SELECTORS = {\n PROCEED_BUTTON: '[data-action=\"proceed\"]',\n CANCEL_BUTTON: '[data-action=\"cancel\"]',\n};\n\nexport default class ModalGateways extends Modal {\n static TYPE = 'core_payment-modal_gateways';\n static TEMPLATE = 'core_payment/modal_gateways';\n\n /**\n * Constructor for the Modal.\n *\n * @param {object} root The root jQuery element for the modal\n */\n constructor(root) {\n super(root);\n }\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n registerEventListeners() {\n // Apply parent event listeners.\n super.registerEventListeners();\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.PROCEED_BUTTON, (e, data) => {\n var proceedEvent = $.Event(PaymentEvents.proceed);\n this.getRoot().trigger(proceedEvent, this);\n\n if (!proceedEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n });\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.CANCEL_BUTTON, (e, data) => {\n var cancelEvent = $.Event(ModalEvents.cancel);\n this.getRoot().trigger(cancelEvent, this);\n\n if (!cancelEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n });\n }\n}\n\nModalGateways.registerModalType();\n"],"names":["SELECTORS","ModalGateways","Modal","constructor","root","registerEventListeners","getModal","on","CustomEvents","events","activate","e","data","proceedEvent","$","Event","PaymentEvents","proceed","getRoot","trigger","this","isDefaultPrevented","hide","originalEvent","preventDefault","cancelEvent","ModalEvents","cancel","registerModalType"],"mappings":"0yBA6BMA,yBACc,0BADdA,wBAEa,+BAGEC,sBAAsBC,eASvCC,YAAYC,YACFA,MAQVC,+BAEUA,8BAEDC,WAAWC,GAAGC,mCAAaC,OAAOC,SAAUV,0BAA0B,CAACW,EAAGC,YACvEC,aAAeC,gBAAEC,MAAMC,gBAAcC,cACpCC,UAAUC,QAAQN,aAAcO,MAEhCP,aAAaQ,4BACTC,OACLV,KAAKW,cAAcC,0BAItBlB,WAAWC,GAAGC,mCAAaC,OAAOC,SAAUV,yBAAyB,CAACW,EAAGC,YACtEa,YAAcX,gBAAEC,MAAMW,sBAAYC,aACjCT,UAAUC,QAAQM,YAAaL,MAE/BK,YAAYJ,4BACRC,OACLV,KAAKW,cAAcC,4EAtCdvB,qBACH,+CADGA,yBAEC,+BA0CtBA,cAAc2B"}edit.min.js000064400000002111152266471740006622 0ustar00define("core_block/edit",["exports","core_form/modalform"],(function(_exports,_modalform){var obj; /** * Javascript module for editing blocks * * @module core_block/edit * @copyright 2022 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=(obj=_modalform)&&obj.__esModule?obj:{default:obj};const SELECTORS_EDITBLOCK='[data-action="editblock"][data-blockid][data-blockform]';_exports.init=pagehash=>{document.addEventListener("click",(e=>{const target=e.target.closest(SELECTORS_EDITBLOCK);if(!target||!target.getAttribute("data-blockform"))return;e.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:target.getAttribute("data-header")},args:{blockid:target.getAttribute("data-blockid"),pagehash:pagehash},formClass:target.getAttribute("data-blockform"),returnFocus:target});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>{location.reload()})),modalForm.show()}))}})); //# sourceMappingURL=edit.min.js.mapedit.min.js.map000064400000005266152266471740007414 0ustar00{"version":3,"file":"edit.min.js","sources":["../src/edit.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module for editing blocks\n *\n * @module core_block/edit\n * @copyright 2022 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\n\nconst SELECTORS = {\n EDITBLOCK: '[data-action=\"editblock\"][data-blockid][data-blockform]',\n};\n\n/**\n * Initialize module\n * @param {String} pagehash\n */\nexport const init = (pagehash) => {\n document.addEventListener('click', e => {\n const target = e.target.closest(SELECTORS.EDITBLOCK);\n if (!target || !target.getAttribute('data-blockform')) {\n return;\n }\n e.preventDefault();\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: target.getAttribute('data-header'),\n },\n args: {blockid: target.getAttribute('data-blockid'), pagehash},\n formClass: target.getAttribute('data-blockform'),\n returnFocus: target,\n });\n\n // Reload the page when the form is submitted, there is no possibility\n // currently to request contents update of just one block on the page.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => {\n location.reload();\n });\n\n modalForm.show();\n });\n};\n"],"names":["SELECTORS","pagehash","document","addEventListener","e","target","closest","getAttribute","preventDefault","modalForm","ModalForm","modalConfig","title","args","blockid","formClass","returnFocus","events","FORM_SUBMITTED","location","reload","show"],"mappings":";;;;;;;sJAyBMA,oBACS,wEAOMC,WACjBC,SAASC,iBAAiB,SAASC,UACzBC,OAASD,EAAEC,OAAOC,QAAQN,yBAC3BK,SAAWA,OAAOE,aAAa,yBAGpCH,EAAEI,uBAEIC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,MAAOP,OAAOE,aAAa,gBAE/BM,KAAM,CAACC,QAAST,OAAOE,aAAa,gBAAiBN,SAAAA,UACrDc,UAAWV,OAAOE,aAAa,kBAC/BS,YAAaX,SAKjBI,UAAUN,iBAAiBM,UAAUQ,OAAOC,gBAAgB,KACxDC,SAASC,YAGbX,UAAUY"}add_modal.min.js.map000064400000015512152266471740010366 0ustar00{"version":3,"file":"add_modal.min.js","sources":["../src/add_modal.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 * Show an add block modal instead of doing it on a separate page.\n *\n * @module core_block/add_modal\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport Ajax from 'core/ajax';\nimport ModalForm from \"core_form/modalform\";\nimport CancelModal from 'core/modal_cancel';\n\nconst SELECTORS = {\n ADD_BLOCK: '[data-key=\"addblock\"]',\n SHOW_BLOCK_FORM: '[data-action=\"showaddblockform\"][data-blockname][data-blockform]'\n};\n\n// Ensure we only add our listeners once.\nlet listenerEventsRegistered = false;\n\n/**\n * Register related event listeners.\n *\n * @method registerListenerEvents\n * @param {String|null} addBlockUrl The add block URL\n * @param {String} pagehash\n */\nconst registerListenerEvents = (addBlockUrl, pagehash) => {\n let addBlockModal = null;\n document.addEventListener('click', e => {\n\n const showAddBlockForm = e.target.closest(SELECTORS.SHOW_BLOCK_FORM);\n if (showAddBlockForm) {\n e.preventDefault();\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('addblock', 'core_block',\n showAddBlockForm.getAttribute('data-blocktitle')),\n },\n args: {blockname: showAddBlockForm.getAttribute('data-blockname'), pagehash,\n blockregion: showAddBlockForm.getAttribute('data-blockregion')},\n formClass: showAddBlockForm.getAttribute('data-blockform'),\n returnFocus: showAddBlockForm,\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => {\n addBlockModal.destroy();\n window.location.reload();\n });\n\n modalForm.show();\n }\n\n const addBlock = e.target.closest(SELECTORS.ADD_BLOCK);\n if (addBlock) {\n e.preventDefault();\n\n let addBlockModalUrl = addBlockUrl ?? addBlock.dataset.url;\n\n buildAddBlockModal()\n .then(modal => {\n addBlockModal = modal;\n const modalBody = renderBlocks(addBlockModalUrl, pagehash,\n addBlock.getAttribute('data-blockregion'));\n modal.setBody(modalBody);\n modal.show();\n\n return modalBody;\n })\n .catch(() => {\n addBlockModal.destroy();\n });\n }\n });\n};\n\n/**\n * Method that creates the 'add block' modal.\n *\n * @method buildAddBlockModal\n * @returns {Promise} The modal promise (modal's body will be rendered later).\n */\nconst buildAddBlockModal = () => CancelModal.create({\n title: getString('addblock'),\n});\n\n/**\n * Method that renders the list of available blocks.\n *\n * @method renderBlocks\n * @param {String} addBlockUrl The add block URL\n * @param {String} pagehash\n * @param {String} region\n * @return {Promise}\n */\nconst renderBlocks = async(addBlockUrl, pagehash, region) => {\n // Fetch all addable blocks in the given page.\n const blocks = await getAddableBlocks(pagehash);\n\n return Templates.render('core/add_block_body', {\n blocks: blocks,\n url: addBlockUrl,\n blockregion: region,\n pagehash\n });\n};\n\n/**\n * Method that fetches all addable blocks in a given page.\n *\n * @method getAddableBlocks\n * @param {String} pagehash\n * @return {Promise}\n */\nconst getAddableBlocks = async(pagehash) => {\n const request = {\n methodname: 'core_block_fetch_addable_blocks',\n args: {\n pagecontextid: 0,\n pagetype: '',\n pagelayout: '',\n subpage: '',\n pagehash: pagehash,\n },\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Set up the actions.\n *\n * @method init\n * @param {String} addBlockUrl The add block URL\n * @param {String} pagehash\n */\nexport const init = (addBlockUrl = null, pagehash = '') => {\n if (!listenerEventsRegistered) {\n registerListenerEvents(addBlockUrl, pagehash);\n listenerEventsRegistered = true;\n }\n};\n"],"names":["SELECTORS","listenerEventsRegistered","registerListenerEvents","addBlockUrl","pagehash","addBlockModal","document","addEventListener","e","showAddBlockForm","target","closest","preventDefault","modalForm","ModalForm","modalConfig","title","getAttribute","args","blockname","blockregion","formClass","returnFocus","events","FORM_SUBMITTED","destroy","window","location","reload","show","addBlock","addBlockModalUrl","dataset","url","buildAddBlockModal","then","modal","modalBody","renderBlocks","setBody","catch","CancelModal","create","async","region","blocks","getAddableBlocks","Templates","render","request","methodname","pagecontextid","pagetype","pagelayout","subpage","Ajax","call"],"mappings":";;;;;;;4QA6BMA,oBACS,wBADTA,0BAEe,uEAIjBC,0BAA2B,QASzBC,uBAAyB,CAACC,YAAaC,gBACrCC,cAAgB,KACpBC,SAASC,iBAAiB,SAASC,UAEzBC,iBAAmBD,EAAEE,OAAOC,QAAQX,8BACtCS,iBAAkB,CAClBD,EAAEI,uBAEIC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,WAAY,aACzBP,iBAAiBQ,aAAa,qBAEtCC,KAAM,CAACC,UAAWV,iBAAiBQ,aAAa,kBAAmBb,SAAAA,SAC/DgB,YAAaX,iBAAiBQ,aAAa,qBAC/CI,UAAWZ,iBAAiBQ,aAAa,kBACzCK,YAAab,mBAGjBI,UAAUN,iBAAiBM,UAAUU,OAAOC,gBAAgB,KACxDnB,cAAcoB,UACdC,OAAOC,SAASC,YAGpBf,UAAUgB,aAGRC,SAAWtB,EAAEE,OAAOC,QAAQX,wBAC9B8B,SAAU,CACVtB,EAAEI,qBAEEmB,iBAAmB5B,MAAAA,YAAAA,YAAe2B,SAASE,QAAQC,IAEvDC,qBACKC,MAAKC,QACF/B,cAAgB+B,YACVC,UAAYC,aAAaP,iBAAkB3B,SAC7C0B,SAASb,aAAa,4BAC1BmB,MAAMG,QAAQF,WACdD,MAAMP,OAECQ,aAEVG,OAAM,KACHnC,cAAcoB,kBAY5BS,mBAAqB,IAAMO,sBAAYC,OAAO,CAChD1B,OAAO,kBAAU,cAYfsB,aAAeK,MAAMxC,YAAaC,SAAUwC,gBAExCC,aAAeC,iBAAiB1C,iBAE/B2C,mBAAUC,OAAO,sBAAuB,CAC3CH,OAAQA,OACRZ,IAAK9B,YACLiB,YAAawB,OACbxC,SAAAA,YAWF0C,iBAAmBH,MAAAA,iBACfM,QAAU,CACZC,WAAY,kCACZhC,KAAM,CACFiC,cAAe,EACfC,SAAU,GACVC,WAAY,GACZC,QAAS,GACTlD,SAAUA,kBAIXmD,cAAKC,KAAK,CAACP,UAAU,kBAUZ,eAAC9C,mEAAc,KAAMC,gEAAW,GAC3CH,2BACDC,uBAAuBC,YAAaC,UACpCH,0BAA2B"}add_modal.min.js000064400000005614152266471740007614 0ustar00define("core_block/add_modal",["exports","core/templates","core/str","core/ajax","core_form/modalform","core/modal_cancel"],(function(_exports,_templates,_str,_ajax,_modalform,_modal_cancel){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Show an add block modal instead of doing it on a separate page. * * @module core_block/add_modal * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_ajax=_interopRequireDefault(_ajax),_modalform=_interopRequireDefault(_modalform),_modal_cancel=_interopRequireDefault(_modal_cancel);const SELECTORS_ADD_BLOCK='[data-key="addblock"]',SELECTORS_SHOW_BLOCK_FORM='[data-action="showaddblockform"][data-blockname][data-blockform]';let listenerEventsRegistered=!1;const registerListenerEvents=(addBlockUrl,pagehash)=>{let addBlockModal=null;document.addEventListener("click",(e=>{const showAddBlockForm=e.target.closest(SELECTORS_SHOW_BLOCK_FORM);if(showAddBlockForm){e.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("addblock","core_block",showAddBlockForm.getAttribute("data-blocktitle"))},args:{blockname:showAddBlockForm.getAttribute("data-blockname"),pagehash:pagehash,blockregion:showAddBlockForm.getAttribute("data-blockregion")},formClass:showAddBlockForm.getAttribute("data-blockform"),returnFocus:showAddBlockForm});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>{addBlockModal.destroy(),window.location.reload()})),modalForm.show()}const addBlock=e.target.closest(SELECTORS_ADD_BLOCK);if(addBlock){e.preventDefault();let addBlockModalUrl=null!=addBlockUrl?addBlockUrl:addBlock.dataset.url;buildAddBlockModal().then((modal=>{addBlockModal=modal;const modalBody=renderBlocks(addBlockModalUrl,pagehash,addBlock.getAttribute("data-blockregion"));return modal.setBody(modalBody),modal.show(),modalBody})).catch((()=>{addBlockModal.destroy()}))}}))},buildAddBlockModal=()=>_modal_cancel.default.create({title:(0,_str.getString)("addblock")}),renderBlocks=async(addBlockUrl,pagehash,region)=>{const blocks=await getAddableBlocks(pagehash);return _templates.default.render("core/add_block_body",{blocks:blocks,url:addBlockUrl,blockregion:region,pagehash:pagehash})},getAddableBlocks=async pagehash=>{const request={methodname:"core_block_fetch_addable_blocks",args:{pagecontextid:0,pagetype:"",pagelayout:"",subpage:"",pagehash:pagehash}};return _ajax.default.call([request])[0]};_exports.init=function(){let addBlockUrl=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,pagehash=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";listenerEventsRegistered||(registerListenerEvents(addBlockUrl,pagehash),listenerEventsRegistered=!0)}})); //# sourceMappingURL=add_modal.min.js.mapbulkactions.min.js.map000064400000027147152266557020011003 0ustar00{"version":3,"file":"bulkactions.min.js","sources":["../src/bulkactions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module for bulk actions.\n *\n * @module gradereport_singleview/bulkactions\n * @copyright 2022 Ilya Tregubov \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport Templates from 'core/templates';\nimport ModalEvents from 'core/modal_events';\nimport * as Str from 'core/str';\nimport Notification from 'core/notification';\nimport selectors from 'gradereport_singleview/selectors';\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n};\n\n/**\n * Register bulk actions related event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n // Register events.\n events.forEach((event) => {\n document.addEventListener(event, async(e) => {\n const trigger = e.target.closest(selectors.actions.bulkaction);\n\n if (!trigger) {\n return;\n }\n if ((trigger.dataset.action === 'overrideallgrades') || (trigger.dataset.action === 'overridenonegrades')) {\n const override = document.querySelectorAll(selectors.elements.override);\n\n if (trigger.dataset.action === 'overridenonegrades') {\n // Alert for removing all grade overrides on page.\n Str.get_strings([\n {key: 'removeoverride', component: 'gradereport_singleview'},\n {key: 'overridenoneconfirm', component: 'gradereport_singleview'},\n {key: 'removeoverridesave', component: 'gradereport_singleview'},\n {key: 'cancel', component: 'moodle'},\n ])\n .then((strings) => {\n return Notification.confirm(\n strings[0],\n strings[1],\n strings[2],\n strings[3],\n () => {\n // Uncheck each override checkbox - this will make grade and feedback input fields disabled.\n override.forEach((el) => {\n if (el.checked) {\n el.click();\n }\n });\n });\n })\n .catch(Notification.exception);\n\n } else {\n // Check each override checkbox - this will make grade and feedback input fields enabled.\n override.forEach((el) => {\n if (!el.checked) {\n el.click();\n }\n });\n }\n } else if ((trigger.dataset.action === 'excludeallgrades') || (trigger.dataset.action === 'excludenonegrades')) {\n const exclude = document.querySelectorAll(selectors.elements.exclude);\n const checked = (trigger.dataset.action === 'excludeallgrades');\n // Uncheck or check each exclude checkbox.\n exclude.forEach((el) => {\n el.checked = checked;\n });\n } else if (trigger.dataset.action === 'bulklegend') {\n // Modal for bulk insert grades.\n Str.get_strings([\n {key: 'bulklegend', component: 'gradereport_singleview'},\n {key: 'save', component: 'moodle'},\n ])\n .then((strings) => {\n return ModalSaveCancel.create({\n body: Templates.render('gradereport_singleview/bulkinsert', {\n id: 'bulkinsertmodal',\n name: 'bulkinsertmodal'\n }),\n title: strings[0],\n buttons: {\n save: strings[1],\n },\n removeOnClose: true,\n show: true,\n });\n })\n .then((modal) => {\n modal.getFooter().find(selectors.elements.modalsave).attr('disabled', true);\n\n // We need to acknowledge that we understand risks of loosing data.\n // Only when acknowledge checkbox is checked we allow selecting insert options.\n modal.getRoot().on('change', selectors.elements.warningcheckbox,\n (e) => {\n e.preventDefault();\n if (e.target.checked) {\n modal.getRoot().find(selectors.elements.modalformdata).removeClass('dimmed_text');\n modal.getRoot().find(selectors.elements.modalradio).removeAttr('disabled');\n modal.getRoot().find(selectors.elements.modalinput).removeAttr('disabled');\n\n const formRadioData = modal.getRoot().find(selectors.elements.modalradiochecked).val();\n // We allow saving grades only when all needed data present on form.\n if (formRadioData) {\n modal.getFooter().find(selectors.elements.modalsave).removeAttr('disabled');\n }\n } else {\n modal.getRoot().find(selectors.elements.modalformdata).addClass('dimmed_text');\n modal.getRoot().find(selectors.elements.modalradio).attr('disabled', true);\n modal.getRoot().find(selectors.elements.modalinput).attr('disabled', true);\n modal.getFooter().find(selectors.elements.modalsave).attr('disabled', true);\n }\n });\n\n // We allow saving grades only when all needed data present on form.\n modal.getRoot().on('change', selectors.elements.modalradio, (e) => {\n e.preventDefault();\n modal.getFooter().find(selectors.elements.modalsave).removeAttr('disabled');\n });\n\n modal.getRoot().on(ModalEvents.save, () => {\n // When save button is clicked in modal form we insert data from modal\n // into preexisted hidden bulk insert form and Save button for table form.\n document.querySelector(selectors.elements.enablebulkinsert).checked = true;\n const formRadioData = modal.getRoot().find(selectors.elements.modalradiochecked).val();\n const $select = document.querySelector(selectors.elements.formradio);\n $select.value = formRadioData;\n\n const formData = modal.getRoot().find(selectors.elements.modalgrade).val();\n document.querySelector(selectors.elements.formgrade).value = formData;\n document.querySelector(selectors.elements.formsave).click();\n });\n\n return modal;\n })\n .catch(Notification.exception);\n }\n });\n });\n};\n"],"names":["pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","define","document","forEach","event","addEventListener","async","trigger","e","target","closest","selectors","actions","bulkaction","dataset","action","override","querySelectorAll","elements","Str","get_strings","key","component","then","strings","Notification","confirm","el","checked","click","catch","exception","exclude","ModalSaveCancel","create","body","Templates","render","id","name","title","buttons","save","removeOnClose","show","modal","getFooter","find","modalsave","attr","getRoot","on","warningcheckbox","preventDefault","modalformdata","removeClass","modalradio","removeAttr","modalinput","modalradiochecked","val","addClass","ModalEvents","querySelector","enablebulkinsert","formRadioData","formradio","value","formData","modalgrade","formgrade","formsave"],"mappings":";;;;;;;gnCAmCoB,WACVA,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,iBAQbD,uBAAyB,WACrBE,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,qDAEXC,OAAOC,SAAUL,QAG9BA,OAAOM,SAASC,QACZF,SAASG,iBAAiBD,OAAOE,MAAAA,UACvBC,QAAUC,EAAEC,OAAOC,QAAQC,mBAAUC,QAAQC,eAE9CN,WAG2B,sBAA3BA,QAAQO,QAAQC,QAA+D,uBAA3BR,QAAQO,QAAQC,OAAkC,OACjGC,SAAWd,SAASe,iBAAiBN,mBAAUO,SAASF,UAE/B,uBAA3BT,QAAQO,QAAQC,OAEhBI,IAAIC,YAAY,CACZ,CAACC,IAAK,iBAAkBC,UAAW,0BACnC,CAACD,IAAK,sBAAuBC,UAAW,0BACxC,CAACD,IAAK,qBAAsBC,UAAW,0BACvC,CAACD,IAAK,SAAUC,UAAW,YAE9BC,MAAMC,SACIC,sBAAaC,QAChBF,QAAQ,GACRA,QAAQ,GACRA,QAAQ,GACRA,QAAQ,IACR,KAEIR,SAASb,SAASwB,KACVA,GAAGC,SACHD,GAAGE,gBAKtBC,MAAML,sBAAaM,WAIpBf,SAASb,SAASwB,KACTA,GAAGC,SACJD,GAAGE,gBAIZ,GAAgC,qBAA3BtB,QAAQO,QAAQC,QAA8D,sBAA3BR,QAAQO,QAAQC,OAAiC,OACtGiB,QAAU9B,SAASe,iBAAiBN,mBAAUO,SAASc,SACvDJ,QAAsC,qBAA3BrB,QAAQO,QAAQC,OAEjCiB,QAAQ7B,SAASwB,KACbA,GAAGC,QAAUA,eAEiB,eAA3BrB,QAAQO,QAAQC,QAEvBI,IAAIC,YAAY,CACZ,CAACC,IAAK,aAAcC,UAAW,0BAC/B,CAACD,IAAK,OAAQC,UAAW,YAE5BC,MAAMC,SACIS,2BAAgBC,OAAO,CAC1BC,KAAMC,mBAAUC,OAAO,oCAAqC,CACxDC,GAAI,kBACJC,KAAM,oBAEVC,MAAOhB,QAAQ,GACfiB,QAAS,CACLC,KAAMlB,QAAQ,IAElBmB,eAAe,EACfC,MAAM,MAGbrB,MAAMsB,QACHA,MAAMC,YAAYC,KAAKpC,mBAAUO,SAAS8B,WAAWC,KAAK,YAAY,GAItEJ,MAAMK,UAAUC,GAAG,SAAUxC,mBAAUO,SAASkC,iBAC3C5C,OACGA,EAAE6C,iBACE7C,EAAEC,OAAOmB,QAAS,CAClBiB,MAAMK,UAAUH,KAAKpC,mBAAUO,SAASoC,eAAeC,YAAY,eACnEV,MAAMK,UAAUH,KAAKpC,mBAAUO,SAASsC,YAAYC,WAAW,YAC/DZ,MAAMK,UAAUH,KAAKpC,mBAAUO,SAASwC,YAAYD,WAAW,YAEzCZ,MAAMK,UAAUH,KAAKpC,mBAAUO,SAASyC,mBAAmBC,OAG7Ef,MAAMC,YAAYC,KAAKpC,mBAAUO,SAAS8B,WAAWS,WAAW,iBAGpEZ,MAAMK,UAAUH,KAAKpC,mBAAUO,SAASoC,eAAeO,SAAS,eAChEhB,MAAMK,UAAUH,KAAKpC,mBAAUO,SAASsC,YAAYP,KAAK,YAAY,GACrEJ,MAAMK,UAAUH,KAAKpC,mBAAUO,SAASwC,YAAYT,KAAK,YAAY,GACrEJ,MAAMC,YAAYC,KAAKpC,mBAAUO,SAAS8B,WAAWC,KAAK,YAAY,MAKlFJ,MAAMK,UAAUC,GAAG,SAAUxC,mBAAUO,SAASsC,YAAahD,IACrDA,EAAE6C,iBACFR,MAAMC,YAAYC,KAAKpC,mBAAUO,SAAS8B,WAAWS,WAAW,eAGxEZ,MAAMK,UAAUC,GAAGW,sBAAYpB,MAAM,KAGjCxC,SAAS6D,cAAcpD,mBAAUO,SAAS8C,kBAAkBpC,SAAU,QAChEqC,cAAgBpB,MAAMK,UAAUH,KAAKpC,mBAAUO,SAASyC,mBAAmBC,MACjE1D,SAAS6D,cAAcpD,mBAAUO,SAASgD,WAClDC,MAAQF,oBAEVG,SAAWvB,MAAMK,UAAUH,KAAKpC,mBAAUO,SAASmD,YAAYT,MACrE1D,SAAS6D,cAAcpD,mBAAUO,SAASoD,WAAWH,MAAQC,SAC7DlE,SAAS6D,cAAcpD,mBAAUO,SAASqD,UAAU1C,WAGjDgB,SAEVf,MAAML,sBAAaM"}group.min.js.map000064400000006505152266557020007614 0ustar00{"version":3,"file":"group.min.js","sources":["../src/group.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 * Allow the user to search for groups within the singleview report.\n *\n * @module gradereport_singleview/group\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @deprecated since Moodle 4.5 - please use core_course/actionbar/group instead.\n * @todo Final deprecation in Moodle 6.0. See MDL-82116.\n */\nimport GroupSearch from 'core_group/comboboxsearch/group';\nimport Url from 'core/url';\n\nexport default class Group extends GroupSearch {\n\n courseID;\n\n item = null;\n\n /**\n * Construct the class.\n *\n * @param {string} item The page type we are currently on.\n */\n constructor(item) {\n window.console.warn(\n 'The gradereport_singleview/group module has been deprecated since Moodle 4.5.' +\n ' Please use core_course/actionbar/group instead.',\n );\n\n super();\n this.item = item;\n\n // Define our standard lookups.\n this.selectors = {...this.selectors,\n courseid: '[data-region=\"courseid\"]',\n };\n const component = document.querySelector(this.componentSelector());\n this.courseID = component.querySelector(this.selectors.courseid).dataset.courseid;\n }\n\n /**\n * Allow the class to be invoked via PHP.\n *\n * @param {string} item The page type we are currently on.\n * @returns {Group}\n */\n static init(item) {\n return new Group(item);\n }\n\n /**\n * Build up the link that is dedicated to a particular result.\n *\n * @param {Number} groupID The ID of the group selected.\n * @returns {string|*}\n */\n selectOneLink(groupID) {\n return Url.relativeUrl('/grade/report/singleview/index.php', {\n id: this.courseID,\n groupsearchvalue: this.getSearchTerm(),\n group: groupID,\n item: this.item\n }, false);\n }\n}\n"],"names":["Group","GroupSearch","constructor","item","window","console","warn","selectors","this","courseid","component","document","querySelector","componentSelector","courseID","dataset","selectOneLink","groupID","Url","relativeUrl","id","groupsearchvalue","getSearchTerm","group"],"mappings":"mhBA2BqBA,cAAcC,eAW/BC,YAAYC,MACRC,OAAOC,QAAQC,KACX,6MATD,WAcEH,KAAOA,UAGPI,UAAY,IAAIC,KAAKD,UACtBE,SAAU,kCAERC,UAAYC,SAASC,cAAcJ,KAAKK,0BACzCC,SAAWJ,UAAUE,cAAcJ,KAAKD,UAAUE,UAAUM,QAAQN,qBASjEN,aACD,IAAIH,MAAMG,MASrBa,cAAcC,gBACHC,aAAIC,YAAY,qCAAsC,CACzDC,GAAIZ,KAAKM,SACTO,iBAAkBb,KAAKc,gBACvBC,MAAON,QACPd,KAAMK,KAAKL,OACZ"}bulkactions.min.js000064400000013107152266557020010216 0ustar00define("gradereport_singleview/bulkactions",["exports","core/pending","core/custom_interaction_events","core/modal_save_cancel","core/templates","core/modal_events","core/str","core/notification","gradereport_singleview/selectors"],(function(_exports,_pending,_custom_interaction_events,_modal_save_cancel,_templates,_modal_events,Str,_notification,_selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Javascript module for bulk actions. * * @module gradereport_singleview/bulkactions * @copyright 2022 Ilya Tregubov * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_templates=_interopRequireDefault(_templates),_modal_events=_interopRequireDefault(_modal_events),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str),_notification=_interopRequireDefault(_notification),_selectors=_interopRequireDefault(_selectors);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest(_selectors.default.actions.bulkaction);if(trigger)if("overrideallgrades"===trigger.dataset.action||"overridenonegrades"===trigger.dataset.action){const override=document.querySelectorAll(_selectors.default.elements.override);"overridenonegrades"===trigger.dataset.action?Str.get_strings([{key:"removeoverride",component:"gradereport_singleview"},{key:"overridenoneconfirm",component:"gradereport_singleview"},{key:"removeoverridesave",component:"gradereport_singleview"},{key:"cancel",component:"moodle"}]).then((strings=>_notification.default.confirm(strings[0],strings[1],strings[2],strings[3],(()=>{override.forEach((el=>{el.checked&&el.click()}))})))).catch(_notification.default.exception):override.forEach((el=>{el.checked||el.click()}))}else if("excludeallgrades"===trigger.dataset.action||"excludenonegrades"===trigger.dataset.action){const exclude=document.querySelectorAll(_selectors.default.elements.exclude),checked="excludeallgrades"===trigger.dataset.action;exclude.forEach((el=>{el.checked=checked}))}else"bulklegend"===trigger.dataset.action&&Str.get_strings([{key:"bulklegend",component:"gradereport_singleview"},{key:"save",component:"moodle"}]).then((strings=>_modal_save_cancel.default.create({body:_templates.default.render("gradereport_singleview/bulkinsert",{id:"bulkinsertmodal",name:"bulkinsertmodal"}),title:strings[0],buttons:{save:strings[1]},removeOnClose:!0,show:!0}))).then((modal=>(modal.getFooter().find(_selectors.default.elements.modalsave).attr("disabled",!0),modal.getRoot().on("change",_selectors.default.elements.warningcheckbox,(e=>{if(e.preventDefault(),e.target.checked){modal.getRoot().find(_selectors.default.elements.modalformdata).removeClass("dimmed_text"),modal.getRoot().find(_selectors.default.elements.modalradio).removeAttr("disabled"),modal.getRoot().find(_selectors.default.elements.modalinput).removeAttr("disabled");modal.getRoot().find(_selectors.default.elements.modalradiochecked).val()&&modal.getFooter().find(_selectors.default.elements.modalsave).removeAttr("disabled")}else modal.getRoot().find(_selectors.default.elements.modalformdata).addClass("dimmed_text"),modal.getRoot().find(_selectors.default.elements.modalradio).attr("disabled",!0),modal.getRoot().find(_selectors.default.elements.modalinput).attr("disabled",!0),modal.getFooter().find(_selectors.default.elements.modalsave).attr("disabled",!0)})),modal.getRoot().on("change",_selectors.default.elements.modalradio,(e=>{e.preventDefault(),modal.getFooter().find(_selectors.default.elements.modalsave).removeAttr("disabled")})),modal.getRoot().on(_modal_events.default.save,(()=>{document.querySelector(_selectors.default.elements.enablebulkinsert).checked=!0;const formRadioData=modal.getRoot().find(_selectors.default.elements.modalradiochecked).val();document.querySelector(_selectors.default.elements.formradio).value=formRadioData;const formData=modal.getRoot().find(_selectors.default.elements.modalgrade).val();document.querySelector(_selectors.default.elements.formgrade).value=formData,document.querySelector(_selectors.default.elements.formsave).click()})),modal))).catch(_notification.default.exception)}))}))}})); //# sourceMappingURL=bulkactions.min.js.mapgrade.min.js000064400000002050152266557020006755 0ustar00define("gradereport_singleview/grade",["exports","core_grades/comboboxsearch/grade"],(function(_exports,_grade){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_grade=(obj=_grade)&&obj.__esModule?obj:{default:obj};const selectors_component=".grade-search",selectors_courseid='[data-region="courseid"]',component=document.querySelector(selectors_component);class GradeItems extends _grade.default{constructor(baseUrl){super(),function(obj,key,value){key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}(this,"courseID",component.querySelector(selectors_courseid).dataset.courseid),this.baseUrl=baseUrl}static init(baseUrl){return new GradeItems(baseUrl)}selectOneLink(gradeID){const url=new URL(this.baseUrl);return url.searchParams.set("gradesearchvalue",this.getSearchTerm()),url.searchParams.set("item","grade"),url.searchParams.set("itemid",gradeID),url.toString()}}return _exports.default=GradeItems,_exports.default})); //# sourceMappingURL=grade.min.js.mapuser.min.js.map000064400000007431152266557020007435 0ustar00{"version":3,"file":"user.min.js","sources":["../src/user.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 UserSearch from 'core_user/comboboxsearch/user';\nimport * as Repository from 'mod_assign/repository';\n\n// Define our standard lookups.\nconst selectors = {\n component: '.user-search',\n groupid: '[data-region=\"groupid\"]',\n instance: '[data-region=\"instance\"]',\n currentvalue: '[data-region=\"currentvalue\"]',\n};\nconst component = document.querySelector(selectors.component);\nconst groupID = parseInt(component.querySelector(selectors.groupid).dataset.groupid, 10);\nconst assignID = parseInt(component.querySelector(selectors.instance).dataset.instance, 10);\n\n/**\n * Allow the user to search for users in the action bar.\n *\n * @module mod_assign/user\n * @copyright 2024 Ilya Tregubov \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class User extends UserSearch {\n\n /**\n * Construct the class.\n *\n * @param {string} baseUrl The base URL for the page.\n */\n constructor(baseUrl) {\n super();\n this.baseUrl = baseUrl;\n }\n\n /**\n * Allow the class to be invoked via PHP.\n *\n * @param {string} baseUrl The base URL for the page.\n * @returns {User}\n */\n static init(baseUrl) {\n return new User(baseUrl);\n }\n\n /**\n * Build up the view all link.\n *\n * @returns {string|*}\n */\n selectAllResultsLink() {\n const url = new URL(this.baseUrl);\n url.searchParams.set('search', this.getSearchTerm());\n\n return url.toString();\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {Promise<*>}\n */\n fetchDataset() {\n return Repository.userFetch(assignID, groupID).then((r) => r);\n }\n\n /**\n * Build up the link that is dedicated to a particular result.\n *\n * @param {Number} userID The ID of the user selected.\n * @returns {string|*}\n */\n selectOneLink(userID) {\n const url = new URL(this.baseUrl);\n url.searchParams.set('search', this.getSearchTerm());\n url.searchParams.set('userid', userID.toString());\n\n return url.toString();\n }\n}\n"],"names":["selectors","component","document","querySelector","groupID","parseInt","dataset","groupid","assignID","instance","User","UserSearch","constructor","baseUrl","selectAllResultsLink","url","URL","this","searchParams","set","getSearchTerm","toString","fetchDataset","Repository","userFetch","then","r","selectOneLink","userID"],"mappings":"2sCAmBMA,oBACS,eADTA,kBAEO,0BAFPA,mBAGQ,2BAGRC,UAAYC,SAASC,cAAcH,qBACnCI,QAAUC,SAASJ,UAAUE,cAAcH,mBAAmBM,QAAQC,QAAS,IAC/EC,SAAWH,SAASJ,UAAUE,cAAcH,oBAAoBM,QAAQG,SAAU;;;;;;;;MASnEC,aAAaC,cAO9BC,YAAYC,sBAEHA,QAAUA,oBASPA,gBACD,IAAIH,KAAKG,SAQpBC,6BACUC,IAAM,IAAIC,IAAIC,KAAKJ,gBACzBE,IAAIG,aAAaC,IAAI,SAAUF,KAAKG,iBAE7BL,IAAIM,WAQfC,sBACWC,WAAWC,UAAUhB,SAAUJ,SAASqB,MAAMC,GAAMA,IAS/DC,cAAcC,cACJb,IAAM,IAAIC,IAAIC,KAAKJ,gBACzBE,IAAIG,aAAaC,IAAI,SAAUF,KAAKG,iBACpCL,IAAIG,aAAaC,IAAI,SAAUS,OAAOP,YAE/BN,IAAIM"}user.min.js000064400000004546152266557020006665 0ustar00define("mod_assign/user",["exports","core_user/comboboxsearch/user","mod_assign/repository"],(function(_exports,_user,Repository){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_user=(obj=_user)&&obj.__esModule?obj:{default:obj},Repository=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Repository);const selectors_component=".user-search",selectors_groupid='[data-region="groupid"]',selectors_instance='[data-region="instance"]',component=document.querySelector(selectors_component),groupID=parseInt(component.querySelector(selectors_groupid).dataset.groupid,10),assignID=parseInt(component.querySelector(selectors_instance).dataset.instance,10); /** * Allow the user to search for users in the action bar. * * @module mod_assign/user * @copyright 2024 Ilya Tregubov * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class User extends _user.default{constructor(baseUrl){super(),this.baseUrl=baseUrl}static init(baseUrl){return new User(baseUrl)}selectAllResultsLink(){const url=new URL(this.baseUrl);return url.searchParams.set("search",this.getSearchTerm()),url.toString()}fetchDataset(){return Repository.userFetch(assignID,groupID).then((r=>r))}selectOneLink(userID){const url=new URL(this.baseUrl);return url.searchParams.set("search",this.getSearchTerm()),url.searchParams.set("userid",userID.toString()),url.toString()}}return _exports.default=User,_exports.default})); //# sourceMappingURL=user.min.js.mapgroup.min.js000064400000002511152266557020007031 0ustar00define("gradereport_singleview/group",["exports","core_group/comboboxsearch/group","core/url"],(function(_exports,_group,_url){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,_group=_interopRequireDefault(_group),_url=_interopRequireDefault(_url);class Group extends _group.default{constructor(item){window.console.warn("The gradereport_singleview/group module has been deprecated since Moodle 4.5. Please use core_course/actionbar/group instead."),super(),_defineProperty(this,"courseID",void 0),_defineProperty(this,"item",null),this.item=item,this.selectors={...this.selectors,courseid:'[data-region="courseid"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid}static init(item){return new Group(item)}selectOneLink(groupID){return _url.default.relativeUrl("/grade/report/singleview/index.php",{id:this.courseID,groupsearchvalue:this.getSearchTerm(),group:groupID,item:this.item},!1)}}return _exports.default=Group,_exports.default})); //# sourceMappingURL=group.min.js.mapgrade.min.js.map000064400000005277152266557020007547 0ustar00{"version":3,"file":"grade.min.js","sources":["../src/grade.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 * Allow the user to search for grades within the singleview report.\n *\n * @module gradereport_singleview/grade\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport GradeItemSearch from 'core_grades/comboboxsearch/grade';\n\n// Define our standard lookups.\nconst selectors = {\n component: '.grade-search',\n courseid: '[data-region=\"courseid\"]',\n};\nconst component = document.querySelector(selectors.component);\n\nexport default class GradeItems extends GradeItemSearch {\n\n courseID = component.querySelector(selectors.courseid).dataset.courseid;\n\n /**\n * Construct the class.\n *\n * @param {string} baseUrl The base URL for the page.\n */\n constructor(baseUrl) {\n super();\n this.baseUrl = baseUrl;\n }\n\n static init(baseUrl) {\n return new GradeItems(baseUrl);\n }\n\n /**\n * Build up the link that is dedicated to a particular result.\n *\n * @param {Number} gradeID The ID of the grade item selected.\n * @returns {string|*}\n */\n selectOneLink(gradeID) {\n const url = new URL(this.baseUrl);\n url.searchParams.set('gradesearchvalue', this.getSearchTerm());\n url.searchParams.set('item', 'grade');\n url.searchParams.set('itemid', gradeID);\n return url.toString();\n }\n}\n"],"names":["selectors","component","document","querySelector","GradeItems","GradeItemSearch","constructor","baseUrl","dataset","courseid","selectOneLink","gradeID","url","URL","this","searchParams","set","getSearchTerm","toString"],"mappings":"oQAyBMA,oBACS,gBADTA,mBAEQ,2BAERC,UAAYC,SAASC,cAAcH,2BAEpBI,mBAAmBC,eASpCC,YAAYC,2KAPDN,UAAUE,cAAcH,oBAAoBQ,QAAQC,eAStDF,QAAUA,oBAGPA,gBACD,IAAIH,WAAWG,SAS1BG,cAAcC,eACJC,IAAM,IAAIC,IAAIC,KAAKP,gBACzBK,IAAIG,aAAaC,IAAI,mBAAoBF,KAAKG,iBAC9CL,IAAIG,aAAaC,IAAI,OAAQ,SAC7BJ,IAAIG,aAAaC,IAAI,SAAUL,SACxBC,IAAIM"}grading_popup.min.js000064400000005202152266565620010540 0ustar00/** * Module to enable inline editing of a comptency grade. * * @module report_competency/grading_popup * @copyright 2015 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define("report_competency/grading_popup",["jquery","core/notification","core/str","core/ajax","core/log","core/templates","tool_lp/dialogue"],(function($,notification,str,ajax,log,templates,Dialogue){var GradingPopup=function(regionSelector,userCompetencySelector){this._regionSelector=regionSelector,this._userCompetencySelector=userCompetencySelector,$(this._regionSelector).on("click",this._userCompetencySelector,this._handleClick.bind(this))};return GradingPopup.prototype._handleClick=function(e){var cell=$(e.target).closest(this._userCompetencySelector),competencyId=$(cell).data("competencyid"),courseId=$(cell).data("courseid"),userId=$(cell).data("userid");log.debug("Clicked on cell: competencyId="+competencyId+", courseId="+courseId+", userId="+userId);var requests=ajax.call([{methodname:"tool_lp_data_for_user_competency_summary_in_course",args:{userid:userId,competencyid:competencyId,courseid:courseId}},{methodname:"core_competency_user_competency_viewed_in_course",args:{userid:userId,competencyid:competencyId,courseid:courseId}}]);$.when(requests[0],requests[1]).then(this._contextLoaded.bind(this)).catch(notification.exception)},GradingPopup.prototype._contextLoaded=function(context){return context.displayuser=!0,M.util.js_pending("report_competency/grading_popup:_contextLoaded"),$.when(str.get_string("usercompetencysummary","report_competency"),templates.render("tool_lp/user_competency_summary_in_course",context)).then(function(title,templateData){return new Dialogue(title,templateData[0],(function(){templates.runTemplateJS(templateData[1]),M.util.js_complete("report_competency/grading_popup:_contextLoaded")}),this._refresh.bind(this),!0)}.bind(this))},GradingPopup.prototype._refresh=function(){var region=$(this._regionSelector),courseId=region.data("courseid"),moduleId=region.data("moduleid"),userId=region.data("userid");return""===moduleId&&(moduleId=0),ajax.call([{methodname:"report_competency_data_for_report",args:{courseid:courseId,userid:userId,moduleid:moduleId},done:this._pageContextLoaded.bind(this),fail:notification.exception}])},GradingPopup.prototype._pageContextLoaded=function(context){templates.render("report_competency/report",context).then(function(html,js){templates.replaceNode(this._regionSelector,html,js)}.bind(this)).catch(notification.exception)},GradingPopup.prototype._regionSelector=null,GradingPopup.prototype._userCompetencySelector=null,GradingPopup})); //# sourceMappingURL=grading_popup.min.js.mapuser_course_navigation.min.js.map000064400000011045152266565620013235 0ustar00{"version":3,"file":"user_course_navigation.min.js","sources":["../src/user_course_navigation.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 * Module to navigation between users in a course.\n *\n * @module report_competency/user_course_navigation\n * @copyright 2015 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n /**\n * UserCourseNavigation\n *\n * @class report_competency/user_course_navigation\n * @param {String} userSelector The selector of the user element.\n * @param {String} moduleSelector The selector of the module element.\n * @param {String} baseUrl The base url for the page (no params).\n * @param {Number} userId The course id\n * @param {Number} courseId The user id\n * @param {Number} moduleId The activity module (filter)\n */\n var UserCourseNavigation = function(userSelector, moduleSelector, baseUrl, userId, courseId, moduleId) {\n this._baseUrl = baseUrl;\n this._userId = userId + '';\n this._courseId = courseId;\n this._moduleId = moduleId;\n\n $(userSelector).on('change', this._userChanged.bind(this));\n $(moduleSelector).on('change', this._moduleChanged.bind(this));\n };\n\n /**\n * The user was changed in the select list.\n *\n * @method _userChanged\n * @param {Event} e the event\n */\n UserCourseNavigation.prototype._userChanged = function(e) {\n // Note: This change causes a page reload and is intentionally not paired with a js_complete call.\n M.util.js_pending('report_competency/user_course_navigation:_userChanged');\n var newUserId = $(e.target).val();\n var queryStr = '?user=' + newUserId + '&id=' + this._courseId + '&mod=' + this._moduleId;\n document.location = this._baseUrl + queryStr;\n };\n\n /**\n * The module was changed in the select list.\n *\n * @method _moduleChanged\n * @param {Event} e the event\n */\n UserCourseNavigation.prototype._moduleChanged = function(e) {\n // Note: This change causes a page reload and is intentionally not paired with a js_complete call.\n M.util.js_pending('report_competency/user_course_navigation:_moduleChanged');\n var newModuleId = $(e.target).val();\n var queryStr = '?mod=' + newModuleId + '&id=' + this._courseId + '&user=' + this._userId;\n document.location = this._baseUrl + queryStr;\n };\n\n /** @property {Number} The id of the user. */\n UserCourseNavigation.prototype._userId = null;\n /** @property {Number} The id of the module. */\n UserCourseNavigation.prototype._moduleId = null;\n /** @property {Number} The id of the course. */\n UserCourseNavigation.prototype._courseId = null;\n /** @property {String} Plugin base url. */\n UserCourseNavigation.prototype._baseUrl = null;\n\n return UserCourseNavigation;\n});\n"],"names":["define","$","UserCourseNavigation","userSelector","moduleSelector","baseUrl","userId","courseId","moduleId","_baseUrl","_userId","_courseId","_moduleId","on","this","_userChanged","bind","_moduleChanged","prototype","e","M","util","js_pending","queryStr","target","val","document","location"],"mappings":";;;;;;;AAuBAA,kDAAO,CAAC,WAAW,SAASC,OAapBC,qBAAuB,SAASC,aAAcC,eAAgBC,QAASC,OAAQC,SAAUC,eACpFC,SAAWJ,aACXK,QAAUJ,OAAS,QACnBK,UAAYJ,cACZK,UAAYJ,SAEjBP,EAAEE,cAAcU,GAAG,SAAUC,KAAKC,aAAaC,KAAKF,OACpDb,EAAEG,gBAAgBS,GAAG,SAAUC,KAAKG,eAAeD,KAAKF,eAS5DZ,qBAAqBgB,UAAUH,aAAe,SAASI,GAEnDC,EAAEC,KAAKC,WAAW,6DAEdC,SAAW,SADCtB,EAAEkB,EAAEK,QAAQC,MACU,OAASX,KAAKH,UAAY,QAAUG,KAAKF,UAC/Ec,SAASC,SAAWb,KAAKL,SAAWc,UASxCrB,qBAAqBgB,UAAUD,eAAiB,SAASE,GAErDC,EAAEC,KAAKC,WAAW,+DAEdC,SAAW,QADGtB,EAAEkB,EAAEK,QAAQC,MACS,OAASX,KAAKH,UAAY,SAAWG,KAAKJ,QACjFgB,SAASC,SAAWb,KAAKL,SAAWc,UAIxCrB,qBAAqBgB,UAAUR,QAAU,KAEzCR,qBAAqBgB,UAAUN,UAAY,KAE3CV,qBAAqBgB,UAAUP,UAAY,KAE3CT,qBAAqBgB,UAAUT,SAAW,KAEnCP"}user_course_navigation.min.js000064400000002555152266565620012467 0ustar00/** * Module to navigation between users in a course. * * @module report_competency/user_course_navigation * @copyright 2015 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define("report_competency/user_course_navigation",["jquery"],(function($){var UserCourseNavigation=function(userSelector,moduleSelector,baseUrl,userId,courseId,moduleId){this._baseUrl=baseUrl,this._userId=userId+"",this._courseId=courseId,this._moduleId=moduleId,$(userSelector).on("change",this._userChanged.bind(this)),$(moduleSelector).on("change",this._moduleChanged.bind(this))};return UserCourseNavigation.prototype._userChanged=function(e){M.util.js_pending("report_competency/user_course_navigation:_userChanged");var queryStr="?user="+$(e.target).val()+"&id="+this._courseId+"&mod="+this._moduleId;document.location=this._baseUrl+queryStr},UserCourseNavigation.prototype._moduleChanged=function(e){M.util.js_pending("report_competency/user_course_navigation:_moduleChanged");var queryStr="?mod="+$(e.target).val()+"&id="+this._courseId+"&user="+this._userId;document.location=this._baseUrl+queryStr},UserCourseNavigation.prototype._userId=null,UserCourseNavigation.prototype._moduleId=null,UserCourseNavigation.prototype._courseId=null,UserCourseNavigation.prototype._baseUrl=null,UserCourseNavigation})); //# sourceMappingURL=user_course_navigation.min.js.mapgrading_popup.min.js.map000064400000017022152266565620011317 0ustar00{"version":3,"file":"grading_popup.min.js","sources":["../src/grading_popup.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 * Module to enable inline editing of a comptency grade.\n *\n * @module report_competency/grading_popup\n * @copyright 2015 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/notification', 'core/str', 'core/ajax', 'core/log', 'core/templates', 'tool_lp/dialogue'],\n function($, notification, str, ajax, log, templates, Dialogue) {\n\n /**\n * GradingPopup\n *\n * @class report_competency/grading_popup\n * @param {String} regionSelector The regionSelector\n * @param {String} userCompetencySelector The userCompetencySelector\n */\n var GradingPopup = function(regionSelector, userCompetencySelector) {\n this._regionSelector = regionSelector;\n this._userCompetencySelector = userCompetencySelector;\n\n $(this._regionSelector).on('click', this._userCompetencySelector, this._handleClick.bind(this));\n };\n\n /**\n * Get the data from the clicked cell and open the popup.\n *\n * @method _handleClick\n * @param {Event} e The event\n */\n GradingPopup.prototype._handleClick = function(e) {\n var cell = $(e.target).closest(this._userCompetencySelector);\n var competencyId = $(cell).data('competencyid');\n var courseId = $(cell).data('courseid');\n var userId = $(cell).data('userid');\n\n log.debug('Clicked on cell: competencyId=' + competencyId + ', courseId=' + courseId + ', userId=' + userId);\n\n var requests = ajax.call([{\n methodname: 'tool_lp_data_for_user_competency_summary_in_course',\n args: {userid: userId, competencyid: competencyId, courseid: courseId},\n }, {\n methodname: 'core_competency_user_competency_viewed_in_course',\n args: {userid: userId, competencyid: competencyId, courseid: courseId},\n }]);\n\n $.when(requests[0], requests[1])\n .then(this._contextLoaded.bind(this))\n .catch(notification.exception);\n };\n\n /**\n * We loaded the context, now render the template.\n *\n * @method _contextLoaded\n * @param {Object} context\n * @returns {Promise}\n */\n GradingPopup.prototype._contextLoaded = function(context) {\n // We have to display user info in popup.\n context.displayuser = true;\n\n M.util.js_pending('report_competency/grading_popup:_contextLoaded');\n\n return $.when(\n str.get_string('usercompetencysummary', 'report_competency'),\n templates.render('tool_lp/user_competency_summary_in_course', context)\n )\n .then(function(title, templateData) {\n return new Dialogue(\n title,\n templateData[0],\n function() {\n templates.runTemplateJS(templateData[1]);\n M.util.js_complete('report_competency/grading_popup:_contextLoaded');\n },\n this._refresh.bind(this),\n true\n );\n }.bind(this));\n };\n\n /**\n * Refresh the page.\n *\n * @method _refresh\n * @returns {Promise}\n */\n GradingPopup.prototype._refresh = function() {\n var region = $(this._regionSelector);\n var courseId = region.data('courseid');\n var moduleId = region.data('moduleid');\n var userId = region.data('userid');\n\n // The module id is expected to be an integer, so don't pass empty string.\n if (moduleId === '') {\n moduleId = 0;\n }\n\n return ajax.call([{\n methodname: 'report_competency_data_for_report',\n args: {courseid: courseId, userid: userId, moduleid: moduleId},\n done: this._pageContextLoaded.bind(this),\n fail: notification.exception\n }]);\n };\n\n /**\n * We loaded the context, now render the template.\n *\n * @method _pageContextLoaded\n * @param {Object} context\n */\n GradingPopup.prototype._pageContextLoaded = function(context) {\n templates.render('report_competency/report', context)\n .then(function(html, js) {\n templates.replaceNode(this._regionSelector, html, js);\n\n return;\n }.bind(this))\n .catch(notification.exception);\n };\n\n /** @property {String} The selector for the region with the user competencies */\n GradingPopup.prototype._regionSelector = null;\n /** @property {String} The selector for the region with a single user competencies */\n GradingPopup.prototype._userCompetencySelector = null;\n\n return GradingPopup;\n});\n"],"names":["define","$","notification","str","ajax","log","templates","Dialogue","GradingPopup","regionSelector","userCompetencySelector","_regionSelector","_userCompetencySelector","this","on","_handleClick","bind","prototype","e","cell","target","closest","competencyId","data","courseId","userId","debug","requests","call","methodname","args","userid","competencyid","courseid","when","then","_contextLoaded","catch","exception","context","displayuser","M","util","js_pending","get_string","render","title","templateData","runTemplateJS","js_complete","_refresh","region","moduleId","moduleid","done","_pageContextLoaded","fail","html","js","replaceNode"],"mappings":";;;;;;;AAuBAA,yCAAO,CAAC,SAAU,oBAAqB,WAAY,YAAa,WAAY,iBAAkB,qBACvF,SAASC,EAAGC,aAAcC,IAAKC,KAAMC,IAAKC,UAAWC,cASpDC,aAAe,SAASC,eAAgBC,6BACnCC,gBAAkBF,oBAClBG,wBAA0BF,uBAE/BT,EAAEY,KAAKF,iBAAiBG,GAAG,QAASD,KAAKD,wBAAyBC,KAAKE,aAAaC,KAAKH,eAS7FL,aAAaS,UAAUF,aAAe,SAASG,OACvCC,KAAOlB,EAAEiB,EAAEE,QAAQC,QAAQR,KAAKD,yBAChCU,aAAerB,EAAEkB,MAAMI,KAAK,gBAC5BC,SAAWvB,EAAEkB,MAAMI,KAAK,YACxBE,OAASxB,EAAEkB,MAAMI,KAAK,UAE1BlB,IAAIqB,MAAM,iCAAmCJ,aAAe,cAAgBE,SAAW,YAAcC,YAEjGE,SAAWvB,KAAKwB,KAAK,CAAC,CACtBC,WAAY,qDACZC,KAAM,CAACC,OAAQN,OAAQO,aAAcV,aAAcW,SAAUT,WAC9D,CACCK,WAAY,mDACZC,KAAM,CAACC,OAAQN,OAAQO,aAAcV,aAAcW,SAAUT,aAGjEvB,EAAEiC,KAAKP,SAAS,GAAIA,SAAS,IAC5BQ,KAAKtB,KAAKuB,eAAepB,KAAKH,OAC9BwB,MAAMnC,aAAaoC,YAUxB9B,aAAaS,UAAUmB,eAAiB,SAASG,gBAE7CA,QAAQC,aAAc,EAEtBC,EAAEC,KAAKC,WAAW,kDAEX1C,EAAEiC,KACL/B,IAAIyC,WAAW,wBAAyB,qBACxCtC,UAAUuC,OAAO,4CAA6CN,UAEjEJ,KAAK,SAASW,MAAOC,qBACX,IAAIxC,SACPuC,MACAC,aAAa,IACb,WACIzC,UAAU0C,cAAcD,aAAa,IACrCN,EAAEC,KAAKO,YAAY,oDAEvBpC,KAAKqC,SAASlC,KAAKH,OACnB,IAENG,KAAKH,QASXL,aAAaS,UAAUiC,SAAW,eAC1BC,OAASlD,EAAEY,KAAKF,iBAChBa,SAAW2B,OAAO5B,KAAK,YACvB6B,SAAWD,OAAO5B,KAAK,YACvBE,OAAS0B,OAAO5B,KAAK,gBAGR,KAAb6B,WACAA,SAAW,GAGRhD,KAAKwB,KAAK,CAAC,CACdC,WAAY,oCACZC,KAAM,CAACG,SAAUT,SAAUO,OAAQN,OAAQ4B,SAAUD,UACrDE,KAAMzC,KAAK0C,mBAAmBvC,KAAKH,MACnC2C,KAAMtD,aAAaoC,cAU3B9B,aAAaS,UAAUsC,mBAAqB,SAAShB,SACjDjC,UAAUuC,OAAO,2BAA4BN,SAC5CJ,KAAK,SAASsB,KAAMC,IACjBpD,UAAUqD,YAAY9C,KAAKF,gBAAiB8C,KAAMC,KAGpD1C,KAAKH,OACNwB,MAAMnC,aAAaoC,YAIxB9B,aAAaS,UAAUN,gBAAkB,KAEzCH,aAAaS,UAAUL,wBAA0B,KAE1CJ"}moodle-gradereport_history-userselector/moodle-gradereport_history-userselector.js000064400000063241152266570030025300 0ustar00YUI.add('moodle-gradereport_history-userselector', function (Y, NAME) { // 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 . /** * The User Selector for the grade history report. * * @module moodle-gradereport_history-userselector * @package gradereport_history * @copyright 2013 NetSpot Pty Ltd (https://www.netspot.com.au) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @main moodle-gradereport_history-userselector */ /** * @module moodle-gradereport_history-userselector */ var COMPONENT = 'gradereport_history'; var USP = { AJAXURL: 'ajaxurl', BASE: 'base', CHECKBOX_NAME_PREFIX: 'usp-u', COURSEID: 'courseid', DIALOGUE_PREFIX: 'moodle-dialogue', NAME: 'gradereport_history_usp', PAGE: 'page', PARAMS: 'params', PERPAGE: 'perPage', SEARCH: 'search', SEARCHBTN: 'searchbtn', SELECTEDUSERS: 'selectedUsers', URL: 'url', USERCOUNT: 'userCount' }; var CSS = { ACCESSHIDE: 'accesshide', AJAXCONTENT: 'usp-ajax-content', CHECKBOX: 'usp-checkbox', CLOSE: 'close', CLOSEBTN: 'usp-finish', CONTENT: 'usp-content', DETAILS: 'details', EXTRAFIELDS: 'extrafields', FIRSTADDED: 'usp-first-added', FULLNAME: 'fullname', HEADER: 'usp-header', HIDDEN: 'hidden', LIGHTBOX: 'usp-loading-lightbox', LOADINGICON: 'loading-icon icon', MORERESULTS: 'usp-more-results', OPTIONS: 'options', PICTURE: 'usp-picture', RESULTSCOUNT: 'usp-results-count', SEARCH: 'usp-search', SEARCHBTN: 'usp-search-btn', SEARCHFIELD: 'usp-search-field', SEARCHRESULTS: 'usp-search-results', SELECTED: 'selected', USER: 'usp-user', USERS: 'usp-users', WRAP: 'usp-wrap' }; var SELECTORS = { AJAXCONTENT: '.' + CSS.AJAXCONTENT, FINISHBTN: '.' + CSS.CLOSEBTN + ' input', FIRSTADDED: '.' + CSS.FIRSTADDED, FULLNAME: '.' + CSS.FULLNAME + ' label', LIGHTBOX: '.' + CSS.LIGHTBOX, MORERESULTS: '.' + CSS.MORERESULTS, OPTIONS: '.' + CSS.OPTIONS, PICTURE: '.' + CSS.USER + ' .userpicture', RESULTSCOUNT: '.' + CSS.RESULTSCOUNT, RESULTSUSERS: '.' + CSS.SEARCHRESULTS + ' .' + CSS.USERS, SEARCHBTN: '.' + CSS.SEARCHBTN, SEARCHFIELD: '.' + CSS.SEARCHFIELD, SELECTEDNAMES: '.felement .selectednames', TRIGGER: '.gradereport_history_plugin input.selectortrigger', USER: '.' + CSS.USER, USERFULLNAMES: 'input[name="userfullnames"]', USERIDS: 'input[name="userids"]', USERSELECT: '.' + CSS.CHECKBOX + ' input[type=checkbox]' }; /** * User Selector. * * @namespace M.gradereport_history * @class UserSelector * @constructor */ var USERSELECTOR = function() { USERSELECTOR.superclass.constructor.apply(this, arguments); }; Y.namespace('M.gradereport_history').UserSelector = Y.extend(USERSELECTOR, M.core.dialogue, { /** * Whether or not this is the first time the user displays the dialogue within that request. * * @property _firstDisplay * @type Boolean * @private */ _firstDisplay: true, /** * The list of all the users selected while the dialogue is open. * * @type Object * @property _usersBufferList * @private */ _usersBufferList: null, /** * The Node on which the focus is set. * * @property _userTabFocus * @type Node * @private */ _userTabFocus: null, /** * Compiled template function for a user node. * * @property _userTemplate * @type Function * @private */ _userTemplate: null, initializer: function() { var bb = this.get('boundingBox'), content, params, tpl; tpl = Y.Handlebars.compile( '
        ' + '
        ' + '' + '
        ' + '
        ' + '
        ' + '
        ' + '
        ' + '{{get_string ' + '
        ' + '
        ' + '' + '
        ' + '
        ' + '
        ' + '
        '); content = Y.Node.create( tpl({ COMPONENT: COMPONENT, CSS: CSS, loadingIcon: M.util.image_url('i/loading', 'moodle') }) ); // Set the title and content. this.getStdModNode(Y.WidgetStdMod.HEADER).prepend(Y.Node.create('

        ' + this.get('title') + '

        ')); this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE); // Use standard dialogue class name. This removes the default styling of the footer. this.get('boundingBox').one('.moodle-dialogue-wrap').addClass('moodle-dialogue-content'); // Add the event on the button that opens the dialogue. Y.one(SELECTORS.TRIGGER).on('click', this.show, this); // The button to finalize the selection. bb.one(SELECTORS.FINISHBTN).on('click', this.finishSelectingUsers, this); // Delegate the keyboard navigation in the users list. bb.delegate('key', this.userKeyboardNavigation, 'down:38,40', SELECTORS.AJAXCONTENT, this); // Delegate the action to select a user. Y.delegate('click', this.selectUser, SELECTORS.AJAXCONTENT, SELECTORS.USERSELECT, this); Y.delegate('click', this.selectUser, SELECTORS.AJAXCONTENT, SELECTORS.PICTURE, this); params = this.get(USP.PARAMS); params.id = this.get(USP.COURSEID); this.set(USP.PARAMS, params); bb.one(SELECTORS.SEARCHBTN).on('click', this.search, this, false); }, /** * Display the dialogue. * * @method show */ show: function(e) { var bb; this._usersBufferList = Y.clone(this.get(USP.SELECTEDUSERS)); if (this._firstDisplay) { // Load the default list of users when the dialogue is loaded for the first time. this._firstDisplay = false; this.search(e, false); } else { // Leave the content as is, but reset the selection. bb = this.get('boundingBox'); // Remove all the selected users. bb.all(SELECTORS.USER).each(function(node) { this.markUserNode(node, false); }, this); // Select the users. Y.Object.each(this._usersBufferList, function(v, k) { var user = bb.one(SELECTORS.USER + '[data-userid="' + k + '"]'); if (user) { this.markUserNode(user, true); } }, this); // Reset the tab focus. this.setUserTabFocus(bb.one(SELECTORS.USER)); } return Y.namespace('M.gradereport_history.UserSelector').superclass.show.call(this); }, /** * Search for users. * * @method search * @param {EventFacade} e The event. * @param {Boolean} append Whether we want to append the results to the current results or not. */ search: function(e, append) { if (e) { e.preventDefault(); } var params; if (append) { this.set(USP.PAGE, this.get(USP.PAGE) + 1); } else { this.set(USP.USERCOUNT, 0); this.set(USP.PAGE, 0); } params = this.get(USP.PARAMS); params.sesskey = M.cfg.sesskey; params.action = 'searchusers'; params.search = this.get('boundingBox').one(SELECTORS.SEARCHFIELD).get('value'); params.page = this.get(USP.PAGE); params.perpage = this.get(USP.PERPAGE); Y.io(M.cfg.wwwroot + this.get(USP.AJAXURL), { method: 'POST', data: window.build_querystring(params), on: { start: this.preSearch, complete: this.processSearchResults, end: this.postSearch }, context: this, "arguments": { // Quoted because this is a reserved keyword. append: append } }); }, /** * Pre search callback. * * @method preSearch * @param {String} transactionId The transaction ID. * @param {Object} args The arguments passed from YUI.io() */ preSearch: function(unused, args) { var bb = this.get('boundingBox'); // Display the lightbox. bb.one(SELECTORS.LIGHTBOX).removeClass(CSS.HIDDEN); // Set the number of results to 'loading...'. if (!args.append) { bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('loading', 'admin')); } }, /** * Post search callback. * * @method postSearch * @param {String} transactionId The transaction ID. * @param {Object} args The arguments passed from YUI.io() */ postSearch: function(transactionId, args) { var bb = this.get('boundingBox'), firstAdded = bb.one(SELECTORS.FIRSTADDED), firstUser; // Hide the lightbox. bb.one(SELECTORS.LIGHTBOX).addClass(CSS.HIDDEN); if (args.append && firstAdded) { // Sets the focus on the newly added user if we are appending results. this.setUserTabFocus(firstAdded); firstAdded.one(SELECTORS.USERSELECT).focus(); } else { // New search result, set the tab focus on the first user returned. firstUser = bb.one(SELECTORS.USER); if (firstUser) { this.setUserTabFocus(firstUser); } } }, /** * Process and display the search results. * * @method processSearchResults * @param {String} tid The transaction ID. * @param {Object} outcome The response object. * @param {Object} args The arguments passed from YUI.io(). */ processSearchResults: function(tid, outcome, args) { var result = false, error = false, bb = this.get('boundingBox'), users, userTemplate, count, selected, i, firstAdded = true, node, content, fetchmore, totalUsers; // Decodes the result. try { result = Y.JSON.parse(outcome.responseText); if (!result.success || result.error) { error = true; } } catch (e) { error = true; } // There was an error. if (error) { this.setContent(''); bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('errajaxsearch', COMPONENT)); return; } // Create the div containing the users when it is a fresh search. if (!args.append) { users = Y.Node.create('
        '); } else { users = bb.one(SELECTORS.RESULTSUSERS); } // Compile the template for each user node. if (!this._userTemplate) { this._userTemplate = Y.Handlebars.compile( '
        ' + '
        ' + '' + '
        ' + '
        {{{picture}}}
        ' + '
        ' + '
        ' + '' + '
        ' + '
        {{{extrafields}}}
        ' + '
        ' + '
        ' ); } userTemplate = this._userTemplate; // Append the users one by one. count = this.get(USP.USERCOUNT); selected = ''; var user; for (i in result.response.users) { count++; user = result.response.users[i]; // If already selected. if (Y.Object.hasKey(this._usersBufferList, user.userid)) { selected = true; } else { selected = false; } node = Y.Node.create(userTemplate({ checkboxId: Y.guid(), COMPONENT: COMPONENT, count: count, CSS: CSS, extrafields: user.extrafields, extraFieldsId: Y.guid(), fullname: user.fullname, picture: user.picture, userId: user.userid, USP: USP })); this.markUserNode(node, selected); // Noting the first user that was when adding more results. if (args.append && firstAdded) { users.all(SELECTORS.FIRSTADDED).removeClass(CSS.FIRSTADDED); node.addClass(CSS.FIRSTADDED); firstAdded = false; } users.append(node); } this.set(USP.USERCOUNT, count); // Update the count of users, and add a button to load more if need be. totalUsers = parseInt(result.response.totalusers, 10); if (!args.append) { if (totalUsers === 0) { bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('noresults', 'moodle')); content = ''; } else { if (totalUsers === 1) { bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('foundoneuser', COMPONENT)); } else { bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('foundnusers', COMPONENT, totalUsers)); } content = Y.Node.create('
        ') .append(users); if (result.response.totalusers > (this.get(USP.PAGE) + 1) * this.get(USP.PERPAGE)) { fetchmore = Y.Node.create(''); fetchmore.one('a').on('click', this.search, this, true); fetchmore.one('a').on('key', this.search, 'space', this, true); content.append(fetchmore); } } this.setContent(content); } else { if (totalUsers <= (this.get(USP.PAGE) + 1) * this.get(USP.PERPAGE)) { bb.one(SELECTORS.MORERESULTS).remove(); } } }, /** * When the user has finished selecting users. * * @method finishSelectingUsers * @param {EventFacade} e The event. */ finishSelectingUsers: function(e) { e.preventDefault(); this.applySelection(); this.hide(); }, /** * Apply the selection made. * * @method applySelection * @param {EventFacade} e The event. */ applySelection: function() { var userIds = Y.Object.keys(this._usersBufferList); this.set(USP.SELECTEDUSERS, Y.clone(this._usersBufferList)) .setNameDisplay(); Y.one(SELECTORS.USERIDS).set('value', userIds.join()); }, /** * Select a user. * * @method SelectUser * @param {EventFacade} e The event. */ selectUser: function(e) { var user = e.currentTarget.ancestor(SELECTORS.USER), checkbox = user.one(SELECTORS.USERSELECT), fullname = user.one(SELECTORS.FULLNAME).get('innerHTML'), checked = checkbox.get('checked'), userId = user.getData('userid'); if (e.currentTarget !== checkbox) { // We triggered the selection from another node, so we need to change the checkbox value. checked = !checked; } if (checked) { // Selecting the user. this._usersBufferList[userId] = fullname; } else { // De-selecting the user. delete this._usersBufferList[userId]; delete this._usersBufferList[parseInt(userId, 10)]; // Also remove numbered keys. } this.markUserNode(user, checked); }, /** * Mark a user node as selected or not. * * This only takes care of the DOM side of things, not the internal mechanism * storing what users have been selected or not. * * @param {Node} node The user node. * @param {Boolean} selected True to mark as selected. * @chainable */ markUserNode: function(node, selected) { if (selected) { node.addClass(CSS.SELECTED) .set('aria-selected', true) .one(SELECTORS.USERSELECT) .set('checked', true); } else { node.removeClass(CSS.SELECTED) .set('aria-selected', false) .one(SELECTORS.USERSELECT) .set('checked', false); } return this; }, /** * Set the content of the dialogue. * * @method setContent * @param {String} content The content. * @chainable */ setContent: function(content) { this.get('boundingBox').one(SELECTORS.AJAXCONTENT).setHTML(content); return this; }, /** * Display the names of the selected users in the form. * * @method setNameDisplay */ setNameDisplay: function() { var namelist = Y.Object.values(this.get(USP.SELECTEDUSERS)); Y.one(SELECTORS.SELECTEDNAMES).set('innerHTML', namelist.join(', ')); Y.one(SELECTORS.USERFULLNAMES).set('value', namelist.join()); }, /** * User keyboard navigation. * * @method userKeyboardNavigation */ userKeyboardNavigation: function(e) { var bb = this.get('boundingBox'), users = bb.all(SELECTORS.USER), direction = 1, user, current = e.target.ancestor(SELECTORS.USER, true); if (e.keyCode === 38) { direction = -1; } user = this.findFocusableUser(users, current, direction); if (user) { e.preventDefault(); user.one(SELECTORS.USERSELECT).focus(); this.setUserTabFocus(user); } }, /** * Find the next or previous focusable node. * * @param {NodeList} users The list of users. * @param {Node} user The user to start with. * @param {Number} direction The direction in which to go. * @return {Node|null} A user node, or null if not found. * @method findFocusableUser */ findFocusableUser: function(users, user, direction) { var index = users.indexOf(user); if (users.size() < 1) { return null; } if (index < 0) { return users.item(0); } index += direction; // Wrap the navigation when reaching the top of the bottom. if (index < 0) { index = users.size() - 1; } else if (index >= users.size()) { index = 0; } return users.item(index); }, /** * Set the user tab focus. * * @param {Node} user The user node. * @method setUserTabFocus */ setUserTabFocus: function(user) { if (this._userTabFocus) { this._userTabFocus.setAttribute('tabindex', '-1'); } if (!user) { // We were not passed a user, there is apparently none in the dialogue. Nothing to do here \\\o/. return; } this._userTabFocus = user.one(SELECTORS.USERSELECT); this._userTabFocus.setAttribute('tabindex', '0'); this.get('boundingBox').one(SELECTORS.RESULTSUSERS).setAttribute('aria-activedescendant', this._userTabFocus.generateID()); } }, { NAME: USP.NAME, CSS_PREFIX: USP.CSS_PREFIX, ATTRS: { /** * The header. * * @attribute title * @default selectusers language string. * @type String */ title: { validator: Y.Lang.isString, valueFn: function() { return M.util.get_string('selectusers', COMPONENT); } }, /** * The current page URL. * * @attribute url * @default null * @type String */ url: { validator: Y.Lang.isString, value: null }, /** * The URL to the Ajax file. * * @attribute ajaxurl * @default null * @type String */ ajaxurl: { validator: Y.Lang.isString, value: null }, /** * The names of the selected users. * * The keys are the user IDs, the values are their fullname. * * @attribute selectedUsers * @default null * @type Object */ selectedUsers: { validator: Y.Lang.isObject, value: null, getter: function(v) { if (v === null) { return {}; } return v; } }, /** * The course ID. * * @attribute courseid * @default null * @type Number */ courseid: { value: null }, /** * Array of parameters. * * @attribute params * @default [] * @type Array */ params: { validator: Y.Lang.isArray, value: [] }, /** * The page we are on. * * @attribute page * @default 0 * @type Number */ page: { validator: Y.Lang.isNumber, value: 0 }, /** * The number of users displayed. * * @attribute userCount * @default 0 * @type Number */ userCount: { value: 0, validator: Y.Lang.isNumber }, /** * The number of results per page. * * @attribute perPage * @default 25 * @type Number */ perPage: { value: 25, Validator: Y.Lang.isNumber } } }); Y.Base.modifyAttrs(Y.namespace('M.gradereport_history.UserSelector'), { /** * List of extra classes. * * @attribute extraClasses * @default ['gradereport_history_usp'] * @type Array */ extraClasses: { value: [ 'gradereport_history_usp' ] }, /** * Whether to focus on the target that caused the Widget to be shown. * * @attribute focusOnPreviousTargetAfterHide * @default true * @type Node */ focusOnPreviousTargetAfterHide: { value: true }, /** * * Width. * * @attribute width * @default '500px' * @type String|Number */ width: { value: '500px' }, /** * Boolean indicating whether or not the Widget is visible. * * @attribute visible * @default false * @type Boolean */ visible: { value: false }, /** * Whether the widget should be modal or not. * * @attribute modal * @type Boolean * @default true */ modal: { value: true }, /** * Whether the widget should be draggable or not. * * @attribute draggable * @type Boolean * @default true */ draggable: { value: true } }); Y.namespace('M.gradereport_history.UserSelector').init = function(cfg) { return new USERSELECTOR(cfg); }; }, '@VERSION@', { "requires": [ "escape", "event-delegate", "event-key", "handlebars", "io-base", "json-parse", "moodle-core-notification-dialogue" ] }); moodle-gradereport_history-userselector/moodle-gradereport_history-userselector-debug.js000064400000063472152266570030026372 0ustar00YUI.add('moodle-gradereport_history-userselector', function (Y, NAME) { // 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 . /** * The User Selector for the grade history report. * * @module moodle-gradereport_history-userselector * @package gradereport_history * @copyright 2013 NetSpot Pty Ltd (https://www.netspot.com.au) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @main moodle-gradereport_history-userselector */ /** * @module moodle-gradereport_history-userselector */ var COMPONENT = 'gradereport_history'; var USP = { AJAXURL: 'ajaxurl', BASE: 'base', CHECKBOX_NAME_PREFIX: 'usp-u', COURSEID: 'courseid', DIALOGUE_PREFIX: 'moodle-dialogue', NAME: 'gradereport_history_usp', PAGE: 'page', PARAMS: 'params', PERPAGE: 'perPage', SEARCH: 'search', SEARCHBTN: 'searchbtn', SELECTEDUSERS: 'selectedUsers', URL: 'url', USERCOUNT: 'userCount' }; var CSS = { ACCESSHIDE: 'accesshide', AJAXCONTENT: 'usp-ajax-content', CHECKBOX: 'usp-checkbox', CLOSE: 'close', CLOSEBTN: 'usp-finish', CONTENT: 'usp-content', DETAILS: 'details', EXTRAFIELDS: 'extrafields', FIRSTADDED: 'usp-first-added', FULLNAME: 'fullname', HEADER: 'usp-header', HIDDEN: 'hidden', LIGHTBOX: 'usp-loading-lightbox', LOADINGICON: 'loading-icon icon', MORERESULTS: 'usp-more-results', OPTIONS: 'options', PICTURE: 'usp-picture', RESULTSCOUNT: 'usp-results-count', SEARCH: 'usp-search', SEARCHBTN: 'usp-search-btn', SEARCHFIELD: 'usp-search-field', SEARCHRESULTS: 'usp-search-results', SELECTED: 'selected', USER: 'usp-user', USERS: 'usp-users', WRAP: 'usp-wrap' }; var SELECTORS = { AJAXCONTENT: '.' + CSS.AJAXCONTENT, FINISHBTN: '.' + CSS.CLOSEBTN + ' input', FIRSTADDED: '.' + CSS.FIRSTADDED, FULLNAME: '.' + CSS.FULLNAME + ' label', LIGHTBOX: '.' + CSS.LIGHTBOX, MORERESULTS: '.' + CSS.MORERESULTS, OPTIONS: '.' + CSS.OPTIONS, PICTURE: '.' + CSS.USER + ' .userpicture', RESULTSCOUNT: '.' + CSS.RESULTSCOUNT, RESULTSUSERS: '.' + CSS.SEARCHRESULTS + ' .' + CSS.USERS, SEARCHBTN: '.' + CSS.SEARCHBTN, SEARCHFIELD: '.' + CSS.SEARCHFIELD, SELECTEDNAMES: '.felement .selectednames', TRIGGER: '.gradereport_history_plugin input.selectortrigger', USER: '.' + CSS.USER, USERFULLNAMES: 'input[name="userfullnames"]', USERIDS: 'input[name="userids"]', USERSELECT: '.' + CSS.CHECKBOX + ' input[type=checkbox]' }; /** * User Selector. * * @namespace M.gradereport_history * @class UserSelector * @constructor */ var USERSELECTOR = function() { USERSELECTOR.superclass.constructor.apply(this, arguments); }; Y.namespace('M.gradereport_history').UserSelector = Y.extend(USERSELECTOR, M.core.dialogue, { /** * Whether or not this is the first time the user displays the dialogue within that request. * * @property _firstDisplay * @type Boolean * @private */ _firstDisplay: true, /** * The list of all the users selected while the dialogue is open. * * @type Object * @property _usersBufferList * @private */ _usersBufferList: null, /** * The Node on which the focus is set. * * @property _userTabFocus * @type Node * @private */ _userTabFocus: null, /** * Compiled template function for a user node. * * @property _userTemplate * @type Function * @private */ _userTemplate: null, initializer: function() { var bb = this.get('boundingBox'), content, params, tpl; tpl = Y.Handlebars.compile( '
        ' + '
        ' + '' + '
        ' + '
        ' + '
        ' + '
        ' + '
        ' + '{{get_string ' + '
        ' + '
        ' + '' + '
        ' + '
        ' + '
        ' + '
        '); content = Y.Node.create( tpl({ COMPONENT: COMPONENT, CSS: CSS, loadingIcon: M.util.image_url('i/loading', 'moodle') }) ); // Set the title and content. this.getStdModNode(Y.WidgetStdMod.HEADER).prepend(Y.Node.create('

        ' + this.get('title') + '

        ')); this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE); // Use standard dialogue class name. This removes the default styling of the footer. this.get('boundingBox').one('.moodle-dialogue-wrap').addClass('moodle-dialogue-content'); // Add the event on the button that opens the dialogue. Y.one(SELECTORS.TRIGGER).on('click', this.show, this); // The button to finalize the selection. bb.one(SELECTORS.FINISHBTN).on('click', this.finishSelectingUsers, this); // Delegate the keyboard navigation in the users list. bb.delegate('key', this.userKeyboardNavigation, 'down:38,40', SELECTORS.AJAXCONTENT, this); // Delegate the action to select a user. Y.delegate('click', this.selectUser, SELECTORS.AJAXCONTENT, SELECTORS.USERSELECT, this); Y.delegate('click', this.selectUser, SELECTORS.AJAXCONTENT, SELECTORS.PICTURE, this); params = this.get(USP.PARAMS); params.id = this.get(USP.COURSEID); this.set(USP.PARAMS, params); bb.one(SELECTORS.SEARCHBTN).on('click', this.search, this, false); }, /** * Display the dialogue. * * @method show */ show: function(e) { var bb; this._usersBufferList = Y.clone(this.get(USP.SELECTEDUSERS)); if (this._firstDisplay) { // Load the default list of users when the dialogue is loaded for the first time. this._firstDisplay = false; this.search(e, false); } else { // Leave the content as is, but reset the selection. bb = this.get('boundingBox'); // Remove all the selected users. bb.all(SELECTORS.USER).each(function(node) { this.markUserNode(node, false); }, this); // Select the users. Y.Object.each(this._usersBufferList, function(v, k) { var user = bb.one(SELECTORS.USER + '[data-userid="' + k + '"]'); if (user) { this.markUserNode(user, true); } }, this); // Reset the tab focus. this.setUserTabFocus(bb.one(SELECTORS.USER)); } return Y.namespace('M.gradereport_history.UserSelector').superclass.show.call(this); }, /** * Search for users. * * @method search * @param {EventFacade} e The event. * @param {Boolean} append Whether we want to append the results to the current results or not. */ search: function(e, append) { if (e) { e.preventDefault(); } var params; if (append) { this.set(USP.PAGE, this.get(USP.PAGE) + 1); } else { this.set(USP.USERCOUNT, 0); this.set(USP.PAGE, 0); } params = this.get(USP.PARAMS); params.sesskey = M.cfg.sesskey; params.action = 'searchusers'; params.search = this.get('boundingBox').one(SELECTORS.SEARCHFIELD).get('value'); params.page = this.get(USP.PAGE); params.perpage = this.get(USP.PERPAGE); Y.io(M.cfg.wwwroot + this.get(USP.AJAXURL), { method: 'POST', data: window.build_querystring(params), on: { start: this.preSearch, complete: this.processSearchResults, end: this.postSearch }, context: this, "arguments": { // Quoted because this is a reserved keyword. append: append } }); }, /** * Pre search callback. * * @method preSearch * @param {String} transactionId The transaction ID. * @param {Object} args The arguments passed from YUI.io() */ preSearch: function(unused, args) { var bb = this.get('boundingBox'); // Display the lightbox. bb.one(SELECTORS.LIGHTBOX).removeClass(CSS.HIDDEN); // Set the number of results to 'loading...'. if (!args.append) { bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('loading', 'admin')); } }, /** * Post search callback. * * @method postSearch * @param {String} transactionId The transaction ID. * @param {Object} args The arguments passed from YUI.io() */ postSearch: function(transactionId, args) { var bb = this.get('boundingBox'), firstAdded = bb.one(SELECTORS.FIRSTADDED), firstUser; // Hide the lightbox. bb.one(SELECTORS.LIGHTBOX).addClass(CSS.HIDDEN); if (args.append && firstAdded) { // Sets the focus on the newly added user if we are appending results. this.setUserTabFocus(firstAdded); firstAdded.one(SELECTORS.USERSELECT).focus(); } else { // New search result, set the tab focus on the first user returned. firstUser = bb.one(SELECTORS.USER); if (firstUser) { this.setUserTabFocus(firstUser); } } }, /** * Process and display the search results. * * @method processSearchResults * @param {String} tid The transaction ID. * @param {Object} outcome The response object. * @param {Object} args The arguments passed from YUI.io(). */ processSearchResults: function(tid, outcome, args) { var result = false, error = false, bb = this.get('boundingBox'), users, userTemplate, count, selected, i, firstAdded = true, node, content, fetchmore, totalUsers; // Decodes the result. try { result = Y.JSON.parse(outcome.responseText); if (!result.success || result.error) { error = true; } } catch (e) { error = true; } // There was an error. if (error) { this.setContent(''); bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('errajaxsearch', COMPONENT)); return; } // Create the div containing the users when it is a fresh search. if (!args.append) { users = Y.Node.create('
        '); } else { users = bb.one(SELECTORS.RESULTSUSERS); } // Compile the template for each user node. if (!this._userTemplate) { this._userTemplate = Y.Handlebars.compile( '
        ' + '
        ' + '' + '
        ' + '
        {{{picture}}}
        ' + '
        ' + '
        ' + '' + '
        ' + '
        {{{extrafields}}}
        ' + '
        ' + '
        ' ); } userTemplate = this._userTemplate; // Append the users one by one. count = this.get(USP.USERCOUNT); selected = ''; var user; for (i in result.response.users) { count++; user = result.response.users[i]; // If already selected. if (Y.Object.hasKey(this._usersBufferList, user.userid)) { selected = true; } else { selected = false; } node = Y.Node.create(userTemplate({ checkboxId: Y.guid(), COMPONENT: COMPONENT, count: count, CSS: CSS, extrafields: user.extrafields, extraFieldsId: Y.guid(), fullname: user.fullname, picture: user.picture, userId: user.userid, USP: USP })); this.markUserNode(node, selected); // Noting the first user that was when adding more results. if (args.append && firstAdded) { users.all(SELECTORS.FIRSTADDED).removeClass(CSS.FIRSTADDED); node.addClass(CSS.FIRSTADDED); firstAdded = false; } users.append(node); } this.set(USP.USERCOUNT, count); // Update the count of users, and add a button to load more if need be. totalUsers = parseInt(result.response.totalusers, 10); if (!args.append) { if (totalUsers === 0) { bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('noresults', 'moodle')); content = ''; } else { if (totalUsers === 1) { bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('foundoneuser', COMPONENT)); } else { bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('foundnusers', COMPONENT, totalUsers)); } content = Y.Node.create('
        ') .append(users); if (result.response.totalusers > (this.get(USP.PAGE) + 1) * this.get(USP.PERPAGE)) { fetchmore = Y.Node.create(''); fetchmore.one('a').on('click', this.search, this, true); fetchmore.one('a').on('key', this.search, 'space', this, true); content.append(fetchmore); } } this.setContent(content); } else { if (totalUsers <= (this.get(USP.PAGE) + 1) * this.get(USP.PERPAGE)) { bb.one(SELECTORS.MORERESULTS).remove(); } } }, /** * When the user has finished selecting users. * * @method finishSelectingUsers * @param {EventFacade} e The event. */ finishSelectingUsers: function(e) { e.preventDefault(); this.applySelection(); this.hide(); }, /** * Apply the selection made. * * @method applySelection * @param {EventFacade} e The event. */ applySelection: function() { var userIds = Y.Object.keys(this._usersBufferList); this.set(USP.SELECTEDUSERS, Y.clone(this._usersBufferList)) .setNameDisplay(); Y.one(SELECTORS.USERIDS).set('value', userIds.join()); }, /** * Select a user. * * @method SelectUser * @param {EventFacade} e The event. */ selectUser: function(e) { var user = e.currentTarget.ancestor(SELECTORS.USER), checkbox = user.one(SELECTORS.USERSELECT), fullname = user.one(SELECTORS.FULLNAME).get('innerHTML'), checked = checkbox.get('checked'), userId = user.getData('userid'); if (e.currentTarget !== checkbox) { // We triggered the selection from another node, so we need to change the checkbox value. checked = !checked; } if (checked) { // Selecting the user. this._usersBufferList[userId] = fullname; } else { // De-selecting the user. delete this._usersBufferList[userId]; delete this._usersBufferList[parseInt(userId, 10)]; // Also remove numbered keys. } this.markUserNode(user, checked); }, /** * Mark a user node as selected or not. * * This only takes care of the DOM side of things, not the internal mechanism * storing what users have been selected or not. * * @param {Node} node The user node. * @param {Boolean} selected True to mark as selected. * @chainable */ markUserNode: function(node, selected) { if (selected) { node.addClass(CSS.SELECTED) .set('aria-selected', true) .one(SELECTORS.USERSELECT) .set('checked', true); } else { node.removeClass(CSS.SELECTED) .set('aria-selected', false) .one(SELECTORS.USERSELECT) .set('checked', false); } return this; }, /** * Set the content of the dialogue. * * @method setContent * @param {String} content The content. * @chainable */ setContent: function(content) { this.get('boundingBox').one(SELECTORS.AJAXCONTENT).setHTML(content); return this; }, /** * Display the names of the selected users in the form. * * @method setNameDisplay */ setNameDisplay: function() { var namelist = Y.Object.values(this.get(USP.SELECTEDUSERS)); Y.one(SELECTORS.SELECTEDNAMES).set('innerHTML', namelist.join(', ')); Y.one(SELECTORS.USERFULLNAMES).set('value', namelist.join()); }, /** * User keyboard navigation. * * @method userKeyboardNavigation */ userKeyboardNavigation: function(e) { var bb = this.get('boundingBox'), users = bb.all(SELECTORS.USER), direction = 1, user, current = e.target.ancestor(SELECTORS.USER, true); if (e.keyCode === 38) { direction = -1; } user = this.findFocusableUser(users, current, direction); if (user) { e.preventDefault(); user.one(SELECTORS.USERSELECT).focus(); this.setUserTabFocus(user); } }, /** * Find the next or previous focusable node. * * @param {NodeList} users The list of users. * @param {Node} user The user to start with. * @param {Number} direction The direction in which to go. * @return {Node|null} A user node, or null if not found. * @method findFocusableUser */ findFocusableUser: function(users, user, direction) { var index = users.indexOf(user); if (users.size() < 1) { Y.log('The users list is empty', 'debug', COMPONENT); return null; } if (index < 0) { Y.log('Unable to find the user in the list of users', 'debug', COMPONENT); return users.item(0); } index += direction; // Wrap the navigation when reaching the top of the bottom. if (index < 0) { index = users.size() - 1; } else if (index >= users.size()) { index = 0; } return users.item(index); }, /** * Set the user tab focus. * * @param {Node} user The user node. * @method setUserTabFocus */ setUserTabFocus: function(user) { if (this._userTabFocus) { this._userTabFocus.setAttribute('tabindex', '-1'); } if (!user) { // We were not passed a user, there is apparently none in the dialogue. Nothing to do here \\\o/. return; } this._userTabFocus = user.one(SELECTORS.USERSELECT); this._userTabFocus.setAttribute('tabindex', '0'); this.get('boundingBox').one(SELECTORS.RESULTSUSERS).setAttribute('aria-activedescendant', this._userTabFocus.generateID()); } }, { NAME: USP.NAME, CSS_PREFIX: USP.CSS_PREFIX, ATTRS: { /** * The header. * * @attribute title * @default selectusers language string. * @type String */ title: { validator: Y.Lang.isString, valueFn: function() { return M.util.get_string('selectusers', COMPONENT); } }, /** * The current page URL. * * @attribute url * @default null * @type String */ url: { validator: Y.Lang.isString, value: null }, /** * The URL to the Ajax file. * * @attribute ajaxurl * @default null * @type String */ ajaxurl: { validator: Y.Lang.isString, value: null }, /** * The names of the selected users. * * The keys are the user IDs, the values are their fullname. * * @attribute selectedUsers * @default null * @type Object */ selectedUsers: { validator: Y.Lang.isObject, value: null, getter: function(v) { if (v === null) { return {}; } return v; } }, /** * The course ID. * * @attribute courseid * @default null * @type Number */ courseid: { value: null }, /** * Array of parameters. * * @attribute params * @default [] * @type Array */ params: { validator: Y.Lang.isArray, value: [] }, /** * The page we are on. * * @attribute page * @default 0 * @type Number */ page: { validator: Y.Lang.isNumber, value: 0 }, /** * The number of users displayed. * * @attribute userCount * @default 0 * @type Number */ userCount: { value: 0, validator: Y.Lang.isNumber }, /** * The number of results per page. * * @attribute perPage * @default 25 * @type Number */ perPage: { value: 25, Validator: Y.Lang.isNumber } } }); Y.Base.modifyAttrs(Y.namespace('M.gradereport_history.UserSelector'), { /** * List of extra classes. * * @attribute extraClasses * @default ['gradereport_history_usp'] * @type Array */ extraClasses: { value: [ 'gradereport_history_usp' ] }, /** * Whether to focus on the target that caused the Widget to be shown. * * @attribute focusOnPreviousTargetAfterHide * @default true * @type Node */ focusOnPreviousTargetAfterHide: { value: true }, /** * * Width. * * @attribute width * @default '500px' * @type String|Number */ width: { value: '500px' }, /** * Boolean indicating whether or not the Widget is visible. * * @attribute visible * @default false * @type Boolean */ visible: { value: false }, /** * Whether the widget should be modal or not. * * @attribute modal * @type Boolean * @default true */ modal: { value: true }, /** * Whether the widget should be draggable or not. * * @attribute draggable * @type Boolean * @default true */ draggable: { value: true } }); Y.namespace('M.gradereport_history.UserSelector').init = function(cfg) { return new USERSELECTOR(cfg); }; }, '@VERSION@', { "requires": [ "escape", "event-delegate", "event-key", "handlebars", "io-base", "json-parse", "moodle-core-notification-dialogue" ] }); moodle-gradereport_history-userselector/moodle-gradereport_history-userselector-min.js000064400000023206152266570030026056 0ustar00YUI.add("moodle-gradereport_history-userselector",function(T,e){var g="gradereport_history",p={AJAXURL:"ajaxurl",BASE:"base",CHECKBOX_NAME_PREFIX:"usp-u",COURSEID:"courseid",DIALOGUE_PREFIX:"moodle-dialogue",NAME:"gradereport_history_usp",PAGE:"page",PARAMS:"params",PERPAGE:"perPage",SEARCH:"search",SEARCHBTN:"searchbtn",SELECTEDUSERS:"selectedUsers",URL:"url",USERCOUNT:"userCount"},R={ACCESSHIDE:"accesshide",AJAXCONTENT:"usp-ajax-content",CHECKBOX:"usp-checkbox",CLOSE:"close",CLOSEBTN:"usp-finish",CONTENT:"usp-content",DETAILS:"details",EXTRAFIELDS:"extrafields",FIRSTADDED:"usp-first-added",FULLNAME:"fullname",HEADER:"usp-header",HIDDEN:"hidden",LIGHTBOX:"usp-loading-lightbox",LOADINGICON:"loading-icon icon",MORERESULTS:"usp-more-results",OPTIONS:"options",PICTURE:"usp-picture",RESULTSCOUNT:"usp-results-count",SEARCH:"usp-search",SEARCHBTN:"usp-search-btn",SEARCHFIELD:"usp-search-field",SEARCHRESULTS:"usp-search-results",SELECTED:"selected",USER:"usp-user",USERS:"usp-users",WRAP:"usp-wrap"},C={AJAXCONTENT:"."+R.AJAXCONTENT,FINISHBTN:"."+R.CLOSEBTN+" input",FIRSTADDED:"."+R.FIRSTADDED,FULLNAME:"."+R.FULLNAME+" label",LIGHTBOX:"."+R.LIGHTBOX,MORERESULTS:"."+R.MORERESULTS,OPTIONS:"."+R.OPTIONS,PICTURE:"."+R.USER+" .userpicture",RESULTSCOUNT:"."+R.RESULTSCOUNT,RESULTSUSERS:"."+R.SEARCHRESULTS+" ."+R.USERS,SEARCHBTN:"."+R.SEARCHBTN,SEARCHFIELD:"."+R.SEARCHFIELD,SELECTEDNAMES:".felement .selectednames",TRIGGER:".gradereport_history_plugin input.selectortrigger",USER:"."+R.USER,USERFULLNAMES:'input[name="userfullnames"]',USERIDS:'input[name="userids"]',USERSELECT:"."+R.CHECKBOX+" input[type=checkbox]"},s=function(){s.superclass.constructor.apply(this,arguments)};T.namespace("M.gradereport_history").UserSelector=T.extend(s,M.core.dialogue,{_firstDisplay:!0,_usersBufferList:null,_userTabFocus:null,_userTemplate:null,initializer:function(){var e=this.get("boundingBox"),s=T.Handlebars.compile('
        {{get_string
        '),s=T.Node.create(s({COMPONENT:g,CSS:R,loadingIcon:M.util.image_url("i/loading","moodle")}));this.getStdModNode(T.WidgetStdMod.HEADER).prepend(T.Node.create("

        "+this.get("title")+"

        ")),this.setStdModContent(T.WidgetStdMod.BODY,s,T.WidgetStdMod.REPLACE),this.get("boundingBox").one(".moodle-dialogue-wrap").addClass("moodle-dialogue-content"),T.one(C.TRIGGER).on("click",this.show,this),e.one(C.FINISHBTN).on("click",this.finishSelectingUsers,this),e.delegate("key",this.userKeyboardNavigation,"down:38,40",C.AJAXCONTENT,this),T.delegate("click",this.selectUser,C.AJAXCONTENT,C.USERSELECT,this),T.delegate("click",this.selectUser,C.AJAXCONTENT,C.PICTURE,this),(s=this.get(p.PARAMS)).id=this.get(p.COURSEID),this.set(p.PARAMS,s),e.one(C.SEARCHBTN).on("click",this.search,this,!1)},show:function(e){var t;return this._usersBufferList=T.clone(this.get(p.SELECTEDUSERS)),this._firstDisplay?(this._firstDisplay=!1,this.search(e,!1)):((t=this.get("boundingBox")).all(C.USER).each(function(e){this.markUserNode(e,!1)},this),T.Object.each(this._usersBufferList,function(e,s){s=t.one(C.USER+'[data-userid="'+s+'"]');s&&this.markUserNode(s,!0)},this),this.setUserTabFocus(t.one(C.USER))),T.namespace("M.gradereport_history.UserSelector").superclass.show.call(this)},search:function(e,s){e&&e.preventDefault(),s?this.set(p.PAGE,this.get(p.PAGE)+1):(this.set(p.USERCOUNT,0),this.set(p.PAGE,0)),(e=this.get(p.PARAMS)).sesskey=M.cfg.sesskey,e.action="searchusers",e.search=this.get("boundingBox").one(C.SEARCHFIELD).get("value"),e.page=this.get(p.PAGE),e.perpage=this.get(p.PERPAGE),T.io(M.cfg.wwwroot+this.get(p.AJAXURL),{method:"POST",data:window.build_querystring(e),on:{start:this.preSearch,complete:this.processSearchResults,end:this.postSearch},context:this,arguments:{append:s}})},preSearch:function(e,s){var t=this.get("boundingBox");t.one(C.LIGHTBOX).removeClass(R.HIDDEN),s.append||t.one(C.RESULTSCOUNT).setHTML(M.util.get_string("loading","admin"))},postSearch:function(e,s){var t=this.get("boundingBox"),i=t.one(C.FIRSTADDED);t.one(C.LIGHTBOX).addClass(R.HIDDEN),s.append&&i?(this.setUserTabFocus(i),i.one(C.USERSELECT).focus()):(s=t.one(C.USER))&&this.setUserTabFocus(s)},processSearchResults:function(S,e,s){var t,d,i,r,E,a,n,o=!1,u=!1,l=this.get("boundingBox"),c=!0;try{(o=T.JSON.parse(e.responseText)).success&&!o.error||(u=!0)}catch(h){u=!0}if(u)return this.setContent(""),void l.one(C.RESULTSCOUNT).setHTML(M.util.get_string("errajaxsearch",g));for(E in t=s.append?l.one(C.RESULTSUSERS):T.Node.create('
        '),this._userTemplate||(this._userTemplate=T.Handlebars.compile('
        {{{picture}}}
        {{{extrafields}}}
        ')),d=this._userTemplate,i=this.get(p.USERCOUNT),r="",o.response.users)i++, n=o.response.users[E],r=!!T.Object.hasKey(this._usersBufferList,n.userid),n=T.Node.create(d({checkboxId:T.guid(),COMPONENT:g,count:i,CSS:R,extrafields:n.extrafields,extraFieldsId:T.guid(),fullname:n.fullname,picture:n.picture,userId:n.userid,USP:p})),this.markUserNode(n,r),s.append&&c&&(t.all(C.FIRSTADDED).removeClass(R.FIRSTADDED),n.addClass(R.FIRSTADDED),c=!1),t.append(n);this.set(p.USERCOUNT,i),e=parseInt(o.response.totalusers,10),s.append?e<=(this.get(p.PAGE)+1)*this.get(p.PERPAGE)&&l.one(C.MORERESULTS).remove():(0===e?(l.one(C.RESULTSCOUNT).setHTML(M.util.get_string("noresults","moodle")),a=""):(1===e?l.one(C.RESULTSCOUNT).setHTML(M.util.get_string("foundoneuser",g)):l.one(C.RESULTSCOUNT).setHTML(M.util.get_string("foundnusers",g,e)),a=T.Node.create('
        ').append(t),o.response.totalusers>(this.get(p.PAGE)+1)*this.get(p.PERPAGE)&&((u=T.Node.create('")).one("a").on("click",this.search,this,!0),u.one("a").on("key",this.search,"space",this,!0),a.append(u))),this.setContent(a))},finishSelectingUsers:function(e){e.preventDefault(),this.applySelection(),this.hide()},applySelection:function(){var e=T.Object.keys(this._usersBufferList);this.set(p.SELECTEDUSERS,T.clone(this._usersBufferList)).setNameDisplay(),T.one(C.USERIDS).set("value",e.join())},selectUser:function(e){var s=e.currentTarget.ancestor(C.USER),t=s.one(C.USERSELECT),i=s.one(C.FULLNAME).get("innerHTML"),r=t.get("checked"),a=s.getData("userid");(r=e.currentTarget!==t?!r:r)?this._usersBufferList[a]=i:(delete this._usersBufferList[a],delete this._usersBufferList[parseInt(a,10)]),this.markUserNode(s,r)},markUserNode:function(e,s){return s?e.addClass(R.SELECTED).set("aria-selected",!0).one(C.USERSELECT).set("checked",!0):e.removeClass(R.SELECTED).set("aria-selected",!1).one(C.USERSELECT).set("checked",!1),this},setContent:function(e){return this.get("boundingBox").one(C.AJAXCONTENT).setHTML(e),this},setNameDisplay:function(){var e=T.Object.values(this.get(p.SELECTEDUSERS));T.one(C.SELECTEDNAMES).set("innerHTML",e.join(", ")),T.one(C.USERFULLNAMES).set("value",e.join())},userKeyboardNavigation:function(e){var s=this.get("boundingBox").all(C.USER),t=1,i=e.target.ancestor(C.USER,!0);38===e.keyCode&&(t=-1),(s=this.findFocusableUser(s,i,t))&&(e.preventDefault(),s.one(C.USERSELECT).focus(),this.setUserTabFocus(s))},findFocusableUser:function(e,s,t){s=e.indexOf(s);return e.size()<1?null:s<0?e.item(0):((s+=t)<0?s=e.size()-1:s>=e.size()&&(s=0),e.item(s))},setUserTabFocus:function(e){this._userTabFocus&&this._userTabFocus.setAttribute("tabindex","-1"),e&&(this._userTabFocus=e.one(C.USERSELECT),this._userTabFocus.setAttribute("tabindex","0"),this.get("boundingBox").one(C.RESULTSUSERS).setAttribute("aria-activedescendant",this._userTabFocus.generateID()))}},{NAME:p.NAME,CSS_PREFIX:p.CSS_PREFIX,ATTRS:{title:{validator:T.Lang.isString,valueFn:function(){return M.util.get_string("selectusers",g)}},url:{validator:T.Lang.isString,value:null},ajaxurl:{validator:T.Lang.isString,value:null},selectedUsers:{validator:T.Lang.isObject,value:null,getter:function(e){return null===e?{}:e}},courseid:{value:null},params:{validator:T.Lang.isArray,value:[]},page:{validator:T.Lang.isNumber,value:0},userCount:{value:0,validator:T.Lang.isNumber},perPage:{value:25,Validator:T.Lang.isNumber}}}),T.Base.modifyAttrs(T.namespace("M.gradereport_history.UserSelector"),{extraClasses:{value:["gradereport_history_usp"]},focusOnPreviousTargetAfterHide:{value:!0},width:{value:"500px"},visible:{value:!1},modal:{value:!0},draggable:{value:!0}}),T.namespace("M.gradereport_history.UserSelector").init=function(e){return new s(e)}},"@VERSION@",{requires:["escape","event-delegate","event-key","handlebars","io-base","json-parse","moodle-core-notification-dialogue"]});enrol_page.min.js000064400000002456152266577450010031 0ustar00define("enrol_self/enrol_page",["exports","core_form/modalform","core/str","core/prefetch","core/url"],(function(_exports,_modalform,_str,_prefetch,_url){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Functions for the enrol_self plugin * * @module enrol_self/enrol_page * @copyright Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initEnrol=function(instanceId){(0,_prefetch.prefetchStrings)("enrol_self",["enrolme"]);const button=document.querySelector('button[type="submit"][data-instance="'+instanceId+'"]');button&&button.addEventListener("click",(e=>{e.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:button.dataset.title,large:!1},formClass:button.dataset.form,args:{id:button.dataset.id,instance:instanceId},saveButtonText:(0,_str.getString)("enrolme","enrol_self"),returnFocus:button});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{window.location.href=event.detail?event.detail:_url.default.relativeUrl("/course/view.php",{id:button.dataset.id})})),modalForm.show()}))},_modalform=_interopRequireDefault(_modalform),_url=_interopRequireDefault(_url)})); //# sourceMappingURL=enrol_page.min.js.mapenrol_page.min.js.map000064400000006327152266577450010606 0ustar00{"version":3,"file":"enrol_page.min.js","sources":["../src/enrol_page.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 * Functions for the enrol_self plugin\n *\n * @module enrol_self/enrol_page\n * @copyright Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport {getString} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport Url from 'core/url';\n\n/**\n * Initialise widget on the course enrolment page - clicking on the button should submit the form\n *\n * @param {Number} instanceId\n */\nexport function initEnrol(instanceId) {\n prefetchStrings('enrol_self', [\n 'enrolme',\n ]);\n\n const button = document.querySelector('button[type=\"submit\"][data-instance=\"' + instanceId + '\"]');\n if (button) {\n button.addEventListener('click', (e) => {\n e.preventDefault();\n const modalForm = new ModalForm({\n modalConfig: {\n title: button.dataset.title,\n large: false, // This is a very small form that does not need a large popup.\n },\n formClass: button.dataset.form,\n args: {id: button.dataset.id, instance: instanceId},\n saveButtonText: getString('enrolme', 'enrol_self'),\n returnFocus: button,\n });\n\n // Redirect to the course page when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n window.location.href = event.detail ? event.detail :\n Url.relativeUrl('/course/view.php', {id: button.dataset.id});\n });\n\n modalForm.show();\n });\n }\n}\n"],"names":["instanceId","button","document","querySelector","addEventListener","e","preventDefault","modalForm","ModalForm","modalConfig","title","dataset","large","formClass","form","args","id","instance","saveButtonText","returnFocus","events","FORM_SUBMITTED","event","window","location","href","detail","Url","relativeUrl","show"],"mappings":";;;;;;;yFAiC0BA,0CACN,aAAc,CAC1B,kBAGEC,OAASC,SAASC,cAAc,wCAA0CH,WAAa,MACzFC,QACAA,OAAOG,iBAAiB,SAAUC,IAC9BA,EAAEC,uBACIC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,MAAOT,OAAOU,QAAQD,MACtBE,OAAO,GAEXC,UAAWZ,OAAOU,QAAQG,KAC1BC,KAAM,CAACC,GAAIf,OAAOU,QAAQK,GAAIC,SAAUjB,YACxCkB,gBAAgB,kBAAU,UAAW,cACrCC,YAAalB,SAIjBM,UAAUH,iBAAiBG,UAAUa,OAAOC,gBAAgBC,QACxDC,OAAOC,SAASC,KAAOH,MAAMI,OAASJ,MAAMI,OACxCC,aAAIC,YAAY,mBAAoB,CAACZ,GAAIf,OAAOU,QAAQK,QAGhET,UAAUsB"}files_tree.min.js000064400000001146152266602000010006 0ustar00define("block_private_files/files_tree",["exports","core/tree"],(function(_exports,_tree){var obj; /** * Changes the display of directories and files into a tree. * * @module block_private_files/files_tree * @copyright 2021 Shamim Rezaie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_tree=(obj=_tree)&&obj.__esModule?obj:{default:obj};_exports.init=blockId=>{new _tree.default("#".concat(blockId,' [role="tree"]'))}})); //# sourceMappingURL=files_tree.min.js.mapfiles_tree.min.js.map000064400000002552152266602000010564 0ustar00{"version":3,"file":"files_tree.min.js","sources":["../src/files_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 * Changes the display of directories and files into a tree.\n *\n * @module block_private_files/files_tree\n * @copyright 2021 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Tree from 'core/tree';\n\n/**\n * The init function that does the job.\n * It changes the display of directories and files into a tree.\n *\n * @param {string} blockId\n */\nexport const init = (blockId) => {\n new Tree(`#${blockId} [role=\"tree\"]`);\n};\n"],"names":["blockId","Tree"],"mappings":";;;;;;;oJA8BqBA,cACbC,yBAASD"}modform.min.js000064400000003634152267044310007341 0ustar00/** * Additional javascript for the Workshop module form. * * @module mod_workshop/modform * @copyright The Open University 2018 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define("mod_workshop/modform",["jquery"],(function($){var submissionTypes={text:{available:null,required:null,requiredHidden:null},file:{available:null,required:null,requiredHidden:null}};function checkAvailability(checkUnavailable,checkAvailable){checkUnavailable.available.prop("checked")||(checkUnavailable.required.prop("disabled",!0),checkUnavailable.required.prop("checked",!1),checkAvailable.available.prop("checked")&&(checkAvailable.required.prop("disabled",!0),checkAvailable.required.prop("checked",!0),checkAvailable.requiredHidden.val(1)))}function enableRequired(submissionType){submissionType.required.prop("disabled",!1),submissionType.required.prop("checked",!1),submissionType.requiredHidden.val(0)}function submissionTypeChanged(){checkAvailability(submissionTypes.file,submissionTypes.text),checkAvailability(submissionTypes.text,submissionTypes.file),submissionTypes.text.available.prop("checked")&&submissionTypes.file.available.prop("checked")&&(enableRequired(submissionTypes.text),enableRequired(submissionTypes.file))}return{init:function(){submissionTypes.text.available=$("#id_submissiontypetextavailable"),submissionTypes.text.required=$("#id_submissiontypetextrequired"),submissionTypes.text.requiredHidden=$('input[name="submissiontypetextrequired"][type="hidden"]'),submissionTypes.file.available=$("#id_submissiontypefileavailable"),submissionTypes.file.required=$("#id_submissiontypefilerequired"),submissionTypes.file.requiredHidden=$('input[name="submissiontypefilerequired"][type="hidden"]'),submissionTypes.text.available.on("change",submissionTypeChanged),submissionTypes.file.available.on("change",submissionTypeChanged),submissionTypeChanged()}}})); //# sourceMappingURL=modform.min.js.mapworkshopview.min.js000064400000001403152267044310010435 0ustar00/** * Sets the equal height to the user plan widget boxes. * * @module mod_workshop/workshopview * @category output * @copyright Loc Nguyen * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define("mod_workshop/workshopview",["jquery"],(function($){function equalHeight(group){var tallest=0;group.height("auto"),group.each((function(){var thisHeight=$(this).height();thisHeight>tallest&&(tallest=thisHeight)})),group.height(tallest)}return{init:function(){var $dt=$(".path-mod-workshop .userplan dt"),$dd=$(".path-mod-workshop .userplan dd");equalHeight($dt),equalHeight($dd),$(window).on("resize",(function(){equalHeight($dt),equalHeight($dd)}))}}})); //# sourceMappingURL=workshopview.min.js.mapworkshopview.min.js.map000064400000004571152267044310011222 0ustar00{"version":3,"file":"workshopview.min.js","sources":["../src/workshopview.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 * Sets the equal height to the user plan widget boxes.\n *\n * @module mod_workshop/workshopview\n * @category output\n * @copyright Loc Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'], function($) {\n\n /**\n * Sets the equal height to all elements in the group.\n *\n * @param {jQuery} group List of nodes.\n */\n function equalHeight(group) {\n var tallest = 0;\n group.height('auto');\n group.each(function() {\n var thisHeight = $(this).height();\n if (thisHeight > tallest) {\n tallest = thisHeight;\n }\n });\n group.height(tallest);\n }\n\n return /** @alias module:mod_workshop/workshopview */ {\n init: function() {\n var $dt = $('.path-mod-workshop .userplan dt');\n var $dd = $('.path-mod-workshop .userplan dd');\n equalHeight($dt);\n equalHeight($dd);\n $(window).on(\"resize\", function() {\n equalHeight($dt);\n equalHeight($dd);\n });\n }\n };\n});\n"],"names":["define","$","equalHeight","group","tallest","height","each","thisHeight","this","init","$dt","$dd","window","on"],"mappings":";;;;;;;;AAuBAA,mCAAO,CAAC,WAAW,SAASC,YAOfC,YAAYC,WACbC,QAAU,EACdD,MAAME,OAAO,QACbF,MAAMG,MAAK,eACHC,WAAaN,EAAEO,MAAMH,SACrBE,WAAaH,UACbA,QAAUG,eAGlBJ,MAAME,OAAOD,eAGqC,CAClDK,KAAM,eACEC,IAAMT,EAAE,mCACRU,IAAMV,EAAE,mCACZC,YAAYQ,KACZR,YAAYS,KACZV,EAAEW,QAAQC,GAAG,UAAU,WACnBX,YAAYQ,KACZR,YAAYS"}modform.min.js.map000064400000012605152267044310010113 0ustar00{"version":3,"file":"modform.min.js","sources":["../src/modform.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 * Additional javascript for the Workshop module form.\n *\n * @module mod_workshop/modform\n * @copyright The Open University 2018\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'], function($) {\n\n var submissionTypes = {\n text: {\n available: null,\n required: null,\n requiredHidden: null\n },\n file: {\n available: null,\n required: null,\n requiredHidden: null\n }\n };\n\n /**\n * Determine whether one of the submission types has been marked as not available.\n *\n * If it has been marked not available, clear and disable its required checkbox. Then determine if the other submission\n * type is available, and if it is, check and disable its required checkbox.\n *\n * @param {Object} checkUnavailable\n * @param {Object} checkAvailable\n */\n function checkAvailability(checkUnavailable, checkAvailable) {\n if (!checkUnavailable.available.prop('checked')) {\n checkUnavailable.required.prop('disabled', true);\n checkUnavailable.required.prop('checked', false);\n if (checkAvailable.available.prop('checked')) {\n checkAvailable.required.prop('disabled', true);\n checkAvailable.required.prop('checked', true);\n // Also set the checkbox's hidden field to 1 so a 'required' value is submitted for the submission type.\n checkAvailable.requiredHidden.val(1);\n }\n }\n }\n\n /**\n * Enable the submission type's required checkbox and uncheck it.\n *\n * @param {Object} submissionType\n */\n function enableRequired(submissionType) {\n submissionType.required.prop('disabled', false);\n submissionType.required.prop('checked', false);\n submissionType.requiredHidden.val(0);\n }\n\n /**\n * Check which submission types have been marked as available, and disable required checkboxes as necessary.\n */\n function submissionTypeChanged() {\n checkAvailability(submissionTypes.file, submissionTypes.text);\n checkAvailability(submissionTypes.text, submissionTypes.file);\n if (submissionTypes.text.available.prop('checked') && submissionTypes.file.available.prop('checked')) {\n enableRequired(submissionTypes.text);\n enableRequired(submissionTypes.file);\n }\n }\n\n return /** @alias module:mod_workshop/modform */ {\n /**\n * Find all the required fields, set up event listeners, and set the initial state of required checkboxes.\n */\n init: function() {\n submissionTypes.text.available = $('#id_submissiontypetextavailable');\n submissionTypes.text.required = $('#id_submissiontypetextrequired');\n submissionTypes.text.requiredHidden = $('input[name=\"submissiontypetextrequired\"][type=\"hidden\"]');\n submissionTypes.file.available = $('#id_submissiontypefileavailable');\n submissionTypes.file.required = $('#id_submissiontypefilerequired');\n submissionTypes.file.requiredHidden = $('input[name=\"submissiontypefilerequired\"][type=\"hidden\"]');\n submissionTypes.text.available.on('change', submissionTypeChanged);\n submissionTypes.file.available.on('change', submissionTypeChanged);\n submissionTypeChanged();\n }\n };\n});\n"],"names":["define","$","submissionTypes","text","available","required","requiredHidden","file","checkAvailability","checkUnavailable","checkAvailable","prop","val","enableRequired","submissionType","submissionTypeChanged","init","on"],"mappings":";;;;;;;AAsBAA,8BAAO,CAAC,WAAW,SAASC,OAEpBC,gBAAkB,CAClBC,KAAM,CACFC,UAAW,KACXC,SAAU,KACVC,eAAgB,MAEpBC,KAAM,CACFH,UAAW,KACXC,SAAU,KACVC,eAAgB,gBAafE,kBAAkBC,iBAAkBC,gBACpCD,iBAAiBL,UAAUO,KAAK,aACjCF,iBAAiBJ,SAASM,KAAK,YAAY,GAC3CF,iBAAiBJ,SAASM,KAAK,WAAW,GACtCD,eAAeN,UAAUO,KAAK,aAC9BD,eAAeL,SAASM,KAAK,YAAY,GACzCD,eAAeL,SAASM,KAAK,WAAW,GAExCD,eAAeJ,eAAeM,IAAI,cAUrCC,eAAeC,gBACpBA,eAAeT,SAASM,KAAK,YAAY,GACzCG,eAAeT,SAASM,KAAK,WAAW,GACxCG,eAAeR,eAAeM,IAAI,YAM7BG,wBACLP,kBAAkBN,gBAAgBK,KAAML,gBAAgBC,MACxDK,kBAAkBN,gBAAgBC,KAAMD,gBAAgBK,MACpDL,gBAAgBC,KAAKC,UAAUO,KAAK,YAAcT,gBAAgBK,KAAKH,UAAUO,KAAK,aACtFE,eAAeX,gBAAgBC,MAC/BU,eAAeX,gBAAgBK,aAIU,CAI7CS,KAAM,WACFd,gBAAgBC,KAAKC,UAAYH,EAAE,mCACnCC,gBAAgBC,KAAKE,SAAWJ,EAAE,kCAClCC,gBAAgBC,KAAKG,eAAiBL,EAAE,2DACxCC,gBAAgBK,KAAKH,UAAYH,EAAE,mCACnCC,gBAAgBK,KAAKF,SAAWJ,EAAE,kCAClCC,gBAAgBK,KAAKD,eAAiBL,EAAE,2DACxCC,gBAAgBC,KAAKC,UAAUa,GAAG,SAAUF,uBAC5Cb,gBAAgBK,KAAKH,UAAUa,GAAG,SAAUF,uBAC5CA"}templateseditor.min.js.map000064400000015036152267105120011653 0ustar00{"version":3,"file":"templateseditor.min.js","sources":["../src/templateseditor.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module to control the template editor.\n *\n * @module mod_data/templateseditor\n * @copyright 2021 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getString} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport {relativeUrl} from 'core/url';\nimport {saveCancel, deleteCancel} from 'core/notification';\nimport Templates from 'core/templates';\n\nprefetchStrings('admin', ['confirmation']);\nprefetchStrings('mod_data', [\n 'resettemplateconfirmtitle',\n 'enabletemplateeditorcheck',\n 'editorenable'\n]);\nprefetchStrings('core', [\n 'reset',\n]);\n\n/**\n * Template editor constants.\n */\nconst selectors = {\n toggleTemplateEditor: 'input[name=\"useeditor\"]',\n resetTemplateAction: '[data-action=\"resettemplate\"]',\n resetTemplate: 'input[name=\"defaultform\"]',\n resetAllTemplates: 'input[name=\"resetall\"]',\n resetAllCheck: 'input[name=\"resetallcheck\"]',\n editForm: '#edittemplateform',\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {Number} instanceId The database ID\n * @param {string} mode The template mode\n */\nconst registerEventListeners = (instanceId, mode) => {\n registerResetButton(mode);\n registerEditorToggler(instanceId, mode);\n};\n\nconst registerResetButton = (mode) => {\n const editForm = document.querySelector(selectors.editForm);\n const resetTemplate = document.querySelector(selectors.resetTemplate);\n const resetAllTemplates = document.querySelector(selectors.resetAllTemplates);\n const resetTemplateAction = document.querySelector(selectors.resetTemplateAction);\n\n if (!resetTemplateAction || !resetTemplate || !editForm) {\n return;\n }\n prefetchStrings('mod_data', [\n mode\n ]);\n resetTemplateAction.addEventListener('click', async(event) => {\n event.preventDefault();\n const params = {\n resetallname: \"resetallcheck\",\n templatename: await getString(mode, 'mod_data'),\n };\n deleteCancel(\n getString('resettemplateconfirmtitle', 'mod_data'),\n Templates.render('mod_data/template_editor_resetmodal', params),\n getString('reset', 'core'),\n () => {\n resetTemplate.value = \"true\";\n editForm.submit();\n },\n null,\n {triggerElement: event.target}\n );\n });\n\n // The reset all checkbox is inside a modal so we need to capture at document level.\n if (!resetAllTemplates) {\n return;\n }\n document.addEventListener('change', (event) => {\n if (event.target.matches(selectors.resetAllCheck)) {\n resetAllTemplates.value = (event.target.checked) ? \"true\" : \"\";\n }\n });\n};\n\nconst registerEditorToggler = (instanceId, mode) => {\n const toggleTemplateEditor = document.querySelector(selectors.toggleTemplateEditor);\n\n if (!toggleTemplateEditor) {\n return;\n }\n\n toggleTemplateEditor.addEventListener('click', async(event) => {\n event.preventDefault();\n // Whether the event action attempts to enable or disable the template editor.\n const enableTemplateEditor = event.target.checked;\n\n if (enableTemplateEditor) {\n // Display a confirmation dialog before enabling the template editor.\n saveCancel(\n getString('confirmation', 'admin'),\n getString('enabletemplateeditorcheck', 'mod_data'),\n getString('editorenable', 'mod_data'),\n () => {\n window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: true});\n },\n null,\n {triggerElement: event.target}\n );\n } else {\n window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: false});\n }\n });\n};\n\n/**\n * Initialize the module.\n *\n * @param {int} instanceId The database ID\n * @param {string} mode The template mode\n */\nexport const init = (instanceId, mode) => {\n registerEventListeners(instanceId, mode);\n};\n"],"names":["selectors","registerResetButton","mode","editForm","document","querySelector","resetTemplate","resetAllTemplates","resetTemplateAction","addEventListener","async","event","preventDefault","params","resetallname","templatename","Templates","render","value","submit","triggerElement","target","matches","checked","registerEditorToggler","instanceId","toggleTemplateEditor","window","location","d","useeditor","registerEventListeners"],"mappings":";;;;;;;8KA6BgB,QAAS,CAAC,+CACV,WAAY,CACxB,4BACA,4BACA,+CAEY,OAAQ,CACpB,gBAMEA,+BACoB,0BADpBA,8BAEmB,gCAFnBA,wBAGa,4BAHbA,4BAIiB,yBAJjBA,wBAKa,8BALbA,mBAMQ,oBAcRC,oBAAuBC,aACnBC,SAAWC,SAASC,cAAcL,oBAClCM,cAAgBF,SAASC,cAAcL,yBACvCO,kBAAoBH,SAASC,cAAcL,6BAC3CQ,oBAAsBJ,SAASC,cAAcL,+BAE9CQ,qBAAwBF,eAAkBH,yCAG/B,WAAY,CACxBD,OAEJM,oBAAoBC,iBAAiB,SAASC,MAAAA,QAC1CC,MAAMC,uBACAC,OAAS,CACXC,aAAc,gBACdC,mBAAoB,kBAAUb,KAAM,6CAGpC,kBAAU,4BAA6B,YACvCc,mBAAUC,OAAO,sCAAuCJ,SACxD,kBAAU,QAAS,SACnB,KACIP,cAAcY,MAAQ,OACtBf,SAASgB,WAEb,KACA,CAACC,eAAgBT,MAAMU,YAK1Bd,mBAGLH,SAASK,iBAAiB,UAAWE,QAC7BA,MAAMU,OAAOC,QAAQtB,2BACrBO,kBAAkBW,MAASP,MAAMU,OAAOE,QAAW,OAAS,SAKlEC,sBAAwB,CAACC,WAAYvB,cACjCwB,qBAAuBtB,SAASC,cAAcL,gCAE/C0B,sBAILA,qBAAqBjB,iBAAiB,SAASC,MAAAA,QAC3CC,MAAMC,iBAEuBD,MAAMU,OAAOE,sCAKlC,kBAAU,eAAgB,UAC1B,kBAAU,4BAA6B,aACvC,kBAAU,eAAgB,aAC1B,KACII,OAAOC,UAAW,oBAAY,0BAA2B,CAACC,EAAGJ,WAAYvB,KAAMA,KAAM4B,WAAW,MAEpG,KACA,CAACV,eAAgBT,MAAMU,SAG3BM,OAAOC,UAAW,oBAAY,0BAA2B,CAACC,EAAGJ,WAAYvB,KAAMA,KAAM4B,WAAW,sBAWxF,CAACL,WAAYvB,QAnFF,EAACuB,WAAYvB,QACxCD,oBAAoBC,MACpBsB,sBAAsBC,WAAYvB,OAkFlC6B,CAAuBN,WAAYvB"}editpreset.min.js000064400000003242152267105120010036 0ustar00define("mod_data/editpreset",["exports","core_form/modalform","core/notification","core/str"],(function(_exports,_modalform,_notification,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Javascript module for editing a database preset. * * @module mod_data/editpreset * @copyright 2022 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_editPresetButton='[data-action="editpreset"]';_exports.init=()=>{registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const editAction=event.target.closest(selectors_editPresetButton);editAction&&(event.preventDefault(),showEditPresetModal(editAction))}))},showEditPresetModal=editAction=>{const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("editpreset","mod_data")},formClass:"mod_data\\form\\save_as_preset",args:{d:editAction.getAttribute("data-dataid"),action:editAction.getAttribute("data-action"),presetname:editAction.getAttribute("data-presetname"),presetdescription:editAction.getAttribute("data-presetdescription")},saveButtonText:(0,_str.getString)("save"),returnFocus:editAction});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.reload():_notification.default.addNotification({type:"error",message:event.detail.errors.join("
        ")})})),modalForm.show()}})); //# sourceMappingURL=editpreset.min.js.mapselectpreset.min.js000064400000003237152267105120010374 0ustar00define("mod_data/selectpreset",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0; /** * Javascript module to control the form responsible for selecting a preset. * * @module mod_data/selectpreset * @copyright 2021 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ const selectors_presetRadioButton='input[name="fullname"]',selectors_selectPresetButton='input[name="selectpreset"]',selectors_selectedPresetRadioButton='input[name="fullname"]:checked';_exports.init=()=>{const radioButton=document.querySelectorAll(selectors_presetRadioButton);disableUsePresetButton(),radioButton.forEach((elem=>{elem.addEventListener("change",(function(event){event.preventDefault(),disableUsePresetButton()}))}))};const disableUsePresetButton=()=>{let selectPresetButton=document.querySelector(selectors_selectPresetButton);const selectedRadioButton=document.querySelector(selectors_selectedPresetRadioButton);selectedRadioButton?(selectPresetButton.removeAttribute("disabled"),selectPresetButton.classList.remove("btn-secondary"),selectPresetButton.classList.add("btn-primary"),selectPresetButton.setAttribute("data-presetname",selectedRadioButton.getAttribute("value")),selectPresetButton.setAttribute("data-cmid",selectedRadioButton.getAttribute("data-cmid"))):(selectPresetButton.setAttribute("disabled",!0),selectPresetButton.classList.remove("btn-primary"),selectPresetButton.classList.add("btn-secondary"),selectPresetButton.removeAttribute("data-presetname"),selectPresetButton.removeAttribute("data-cmid"))}})); //# sourceMappingURL=selectpreset.min.js.mapdeletepreset.min.js000064400000003530152267105120010353 0ustar00define("mod_data/deletepreset",["exports","core/notification","core/prefetch","core/str","core/ajax","core/url"],(function(_exports,_notification,_prefetch,_str,_ajax,_url){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Javascript module for deleting a database as a preset. * * @module mod_data/deletepreset * @copyright 2022 Amaia Anabitarte * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_url=_interopRequireDefault(_url);const selectors_deletePresetButton='[data-action="deletepreset"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("mod_data",["deleteconfirm","deletewarning"]),(0,_prefetch.prefetchStrings)("core",["delete"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const deleteOption=event.target.closest(selectors_deletePresetButton);deleteOption&&(event.preventDefault(),deletePresetConfirm(deleteOption))}))},deletePresetConfirm=deleteOption=>{const presetName=deleteOption.getAttribute("data-presetname"),dataId=deleteOption.getAttribute("data-dataid");_notification.default.deleteCancelPromise((0,_str.getString)("deleteconfirm","mod_data",presetName),(0,_str.getString)("deletewarning","mod_data")).then((()=>async function(dataId,presetName){var request={methodname:"mod_data_delete_saved_preset",args:{dataid:dataId,presetnames:{presetname:presetName}}};try{await _ajax.default.call([request])[0],window.location.href=_url.default.relativeUrl("mod/data/preset.php",{d:dataId},!1)}catch(error){_notification.default.exception(error)}}(dataId,presetName))).catch((()=>{}))}})); //# sourceMappingURL=deletepreset.min.js.mapselectpreset.min.js.map000064400000007631152267105120011152 0ustar00{"version":3,"file":"selectpreset.min.js","sources":["../src/selectpreset.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module to control the form responsible for selecting a preset.\n *\n * @module mod_data/selectpreset\n * @copyright 2021 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst selectors = {\n presetRadioButton: 'input[name=\"fullname\"]',\n selectPresetButton: 'input[name=\"selectpreset\"]',\n selectedPresetRadioButton: 'input[name=\"fullname\"]:checked',\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const radioButton = document.querySelectorAll(selectors.presetRadioButton);\n\n // Initialize the \"Use a preset\" button properly.\n disableUsePresetButton();\n\n radioButton.forEach((elem) => {\n elem.addEventListener('change', function(event) {\n event.preventDefault();\n // Enable the \"Use a preset\" button when any of the radio buttons in the presets list is checked.\n disableUsePresetButton();\n });\n });\n\n};\n\n/**\n * Decide whether to disable or not the \"Use a preset\" button.\n * When there is no preset selected, the button should be displayed disabled; otherwise, it will appear enabled as a primary button.\n *\n * @method\n * @private\n */\nconst disableUsePresetButton = () => {\n let selectPresetButton = document.querySelector(selectors.selectPresetButton);\n const selectedRadioButton = document.querySelector(selectors.selectedPresetRadioButton);\n\n if (selectedRadioButton) {\n // There is one preset selected, so the button should be enabled.\n selectPresetButton.removeAttribute('disabled');\n selectPresetButton.classList.remove('btn-secondary');\n selectPresetButton.classList.add('btn-primary');\n selectPresetButton.setAttribute('data-presetname', selectedRadioButton.getAttribute('value'));\n selectPresetButton.setAttribute('data-cmid', selectedRadioButton.getAttribute('data-cmid'));\n } else {\n // There is no any preset selected, so the button should be disabled.\n selectPresetButton.setAttribute('disabled', true);\n selectPresetButton.classList.remove('btn-primary');\n selectPresetButton.classList.add('btn-secondary');\n selectPresetButton.removeAttribute('data-presetname');\n selectPresetButton.removeAttribute('data-cmid');\n }\n};\n"],"names":["selectors","radioButton","document","querySelectorAll","disableUsePresetButton","forEach","elem","addEventListener","event","preventDefault","selectPresetButton","querySelector","selectedRadioButton","removeAttribute","classList","remove","add","setAttribute","getAttribute"],"mappings":";;;;;;;;MAuBMA,4BACiB,yBADjBA,6BAEkB,6BAFlBA,oCAGyB,+CAMX,WACVC,YAAcC,SAASC,iBAAiBH,6BAG9CI,yBAEAH,YAAYI,SAASC,OACjBA,KAAKC,iBAAiB,UAAU,SAASC,OACrCA,MAAMC,iBAENL,sCAaNA,uBAAyB,SACvBM,mBAAqBR,SAASS,cAAcX,oCAC1CY,oBAAsBV,SAASS,cAAcX,qCAE/CY,qBAEAF,mBAAmBG,gBAAgB,YACnCH,mBAAmBI,UAAUC,OAAO,iBACpCL,mBAAmBI,UAAUE,IAAI,eACjCN,mBAAmBO,aAAa,kBAAmBL,oBAAoBM,aAAa,UACpFR,mBAAmBO,aAAa,YAAaL,oBAAoBM,aAAa,gBAG9ER,mBAAmBO,aAAa,YAAY,GAC5CP,mBAAmBI,UAAUC,OAAO,eACpCL,mBAAmBI,UAAUE,IAAI,iBACjCN,mBAAmBG,gBAAgB,mBACnCH,mBAAmBG,gBAAgB"}importpresets.min.js000064400000002662152267105120010613 0ustar00define("mod_data/importpresets",["exports","core_form/modalform","core/notification","core/str"],(function(_exports,_modalform,_notification,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Javascript module for importing presets. * * @module mod_data/importpresets * @copyright 2022 Laurent David * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_importPresetButton='[data-action="importpresets"]';_exports.init=()=>{document.addEventListener("click",(event=>{const importPresetButton=event.target.closest(selectors_importPresetButton);if(!importPresetButton)return;event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("importpreset","mod_data")},formClass:"mod_data\\form\\import_presets",args:{cmid:importPresetButton.dataset.dataid},saveButtonText:(0,_str.getString)("importandapply","mod_data")});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.assign(event.detail.url):_notification.default.addNotification({type:"error",message:event.detail.errors.join("
        ")})})),modalForm.show()}))}})); //# sourceMappingURL=importpresets.min.js.mapeditpreset.min.js.map000064400000007316152267105120010620 0ustar00{"version":3,"file":"editpreset.min.js","sources":["../src/editpreset.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module for editing a database preset.\n *\n * @module mod_data/editpreset\n * @copyright 2022 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\n\nconst selectors = {\n editPresetButton: '[data-action=\"editpreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n registerEventListeners();\n};\n\n/**\n * Register events for update/delete links.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const editAction = event.target.closest(selectors.editPresetButton);\n if (editAction) {\n event.preventDefault();\n showEditPresetModal(editAction);\n }\n });\n};\n\n/**\n * Show the edit preset modal.\n *\n * @param {HTMLElement} editAction the edit action element.\n */\nconst showEditPresetModal = (editAction) => {\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('editpreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\save_as_preset',\n args: {\n d: editAction.getAttribute('data-dataid'),\n action: editAction.getAttribute('data-action'),\n presetname: editAction.getAttribute('data-presetname'),\n presetdescription: editAction.getAttribute('data-presetdescription')\n },\n saveButtonText: getString('save'),\n returnFocus: editAction,\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('
        ')\n });\n }\n });\n\n modalForm.show();\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","editAction","target","closest","preventDefault","showEditPresetModal","modalForm","ModalForm","modalConfig","title","formClass","args","d","getAttribute","action","presetname","presetdescription","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","window","location","reload","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,2BACgB,2CAMF,KAChBC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,WAAaD,MAAME,OAAOC,QAAQP,4BACpCK,aACAD,MAAMI,iBACNC,oBAAoBJ,iBAU1BI,oBAAuBJ,mBACnBK,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,aAAc,aAEnCC,UAAW,iCACXC,KAAM,CACFC,EAAGX,WAAWY,aAAa,eAC3BC,OAAQb,WAAWY,aAAa,eAChCE,WAAYd,WAAWY,aAAa,mBACpCG,kBAAmBf,WAAWY,aAAa,2BAE/CI,gBAAgB,kBAAU,QAC1BC,YAAajB,aAGjBK,UAAUP,iBAAiBO,UAAUa,OAAOC,gBAAgBpB,QACpDA,MAAMqB,OAAOC,OACbC,OAAOC,SAASC,+BAEHC,gBAAgB,CACzBC,KAAM,QACNC,QAAS5B,MAAMqB,OAAOQ,OAAOC,KAAK,aAK9CxB,UAAUyB"}importmappingdialogue.min.js000064400000005120152267105120012263 0ustar00define("mod_data/importmappingdialogue",["exports","core/notification","core/ajax","core/url","core/templates","core/modal","core/prefetch","core/str"],(function(_exports,_notification,_ajax,_url,_templates,_modal,_prefetch,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Javascript module for deleting a database as a preset. * * @module mod_data/importmappingdialogue * @copyright 2022 Amaia Anabitarte * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_url=_interopRequireDefault(_url),_templates=_interopRequireDefault(_templates),_modal=_interopRequireDefault(_modal),(0,_prefetch.prefetchStrings)("mod_data",["mapping:dialogtitle:usepreset"]);const selectors_selectPreset='[data-action="selectpreset"]';_exports.init=()=>{registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const preset=event.target.closest(selectors_selectPreset);preset&&(event.preventDefault(),showMappingDialogue(preset))}))},showMappingDialogue=usepreset=>{const presetName=usepreset.dataset.presetname,cmId=usepreset.dataset.cmid;getMappingInformation(cmId,presetName).then((result=>(result.data&&result.data.needsmapping?buildModal({title:(0,_str.getString)("mapping:dialogtitle:usepreset","mod_data",result.data.presetname),body:_templates.default.render("mod_data/fields_mapping_body",result.data),footer:_templates.default.render("mod_data/fields_mapping_footer",getMappingButtons(cmId,presetName)),large:!0,show:!0}):window.location.href=_url.default.relativeUrl("mod/data/field.php",{id:cmId,mode:"usepreset",fullname:presetName},!1),!0))).catch(_notification.default.exception)},buildModal=params=>_modal.default.create({...params}).then((modal=>(modal.showFooter(),modal.registerCloseOnCancel(),modal))).catch(_notification.default.exception),getMappingButtons=(cmId,presetName)=>{const data={};return data.mapfieldsbutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"select"},!1),data.applybutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"notmapping"},!1),data},getMappingInformation=(cmId,presetName)=>{const request={methodname:"mod_data_get_mapping_information",args:{cmid:cmId,importedpreset:presetName}};return _ajax.default.call([request])[0]}})); //# sourceMappingURL=importmappingdialogue.min.js.mapdeletepreset.min.js.map000064400000010311152267105120011122 0ustar00{"version":3,"file":"deletepreset.min.js","sources":["../src/deletepreset.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module for deleting a database as a preset.\n *\n * @module mod_data/deletepreset\n * @copyright 2022 Amaia Anabitarte \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport Ajax from 'core/ajax';\nimport Url from 'core/url';\n\nconst selectors = {\n deletePresetButton: '[data-action=\"deletepreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n prefetchStrings('mod_data', [\n 'deleteconfirm',\n 'deletewarning',\n ]);\n prefetchStrings('core', [\n 'delete',\n ]);\n\n registerEventListeners();\n};\n\n/**\n * Register events for delete preset option in action menu.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const deleteOption = event.target.closest(selectors.deletePresetButton);\n if (deleteOption) {\n event.preventDefault();\n deletePresetConfirm(deleteOption);\n }\n });\n};\n\n/**\n * Show the confirmation modal to delete the preset.\n *\n * @param {HTMLElement} deleteOption the element to delete.\n */\nconst deletePresetConfirm = (deleteOption) => {\n const presetName = deleteOption.getAttribute('data-presetname');\n const dataId = deleteOption.getAttribute('data-dataid');\n\n Notification.deleteCancelPromise(\n getString('deleteconfirm', 'mod_data', presetName),\n getString('deletewarning', 'mod_data'),\n ).then(() => {\n return deletePreset(dataId, presetName);\n }).catch(() => {\n return;\n });\n};\n\n/**\n * Delete site user preset.\n *\n * @param {int} dataId The id of the current database activity.\n * @param {string} presetName The preset name to delete.\n * @return {promise} Resolved with the result and warnings of deleting a preset.\n */\nasync function deletePreset(dataId, presetName) {\n var request = {\n methodname: 'mod_data_delete_saved_preset',\n args: {\n dataid: dataId,\n presetnames: {presetname: presetName},\n }\n };\n try {\n await Ajax.call([request])[0];\n window.location.href = Url.relativeUrl(\n 'mod/data/preset.php',\n {\n d: dataId,\n },\n false\n );\n } catch (error) {\n Notification.exception(error);\n }\n}\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","deleteOption","target","closest","preventDefault","deletePresetConfirm","presetName","getAttribute","dataId","deleteCancelPromise","then","request","methodname","args","dataid","presetnames","presetname","Ajax","call","window","location","href","Url","relativeUrl","d","error","exception","deletePreset","catch"],"mappings":";;;;;;;kNA6BMA,6BACkB,6CAMJ,mCACA,WAAY,CACxB,gBACA,gDAEY,OAAQ,CACpB,WAGJC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,aAAeD,MAAME,OAAOC,QAAQP,8BACtCK,eACAD,MAAMI,iBACNC,oBAAoBJ,mBAU1BI,oBAAuBJ,qBACnBK,WAAaL,aAAaM,aAAa,mBACvCC,OAASP,aAAaM,aAAa,qCAE5BE,qBACT,kBAAU,gBAAiB,WAAYH,aACvC,kBAAU,gBAAiB,aAC7BI,MAAK,mBAciBF,OAAQF,gBAC5BK,QAAU,CACVC,WAAY,+BACZC,KAAM,CACFC,OAAQN,OACRO,YAAa,CAACC,WAAYV,wBAIxBW,cAAKC,KAAK,CAACP,UAAU,GAC3BQ,OAAOC,SAASC,KAAOC,aAAIC,YACvB,sBACA,CACIC,EAAGhB,SAEP,GAEN,MAAOiB,6BACQC,UAAUD,QA/BhBE,CAAanB,OAAQF,cAC7BsB,OAAM"}resetalltemplates.min.js000064400000002570152267105120011423 0ustar00define("mod_data/resetalltemplates",["exports","core/notification","core/prefetch","core/str"],(function(_exports,_notification,_prefetch,_str){var obj; /** * Javascript module for reseting all templates. * * @module mod_data/resetalltemplates * @copyright 2022 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};const selectors_resetAllTemplatesAction='[data-action="resetalltemplates"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("mod_data",["resetalltemplatesconfirmtitle","resetalltemplatesconfirm"]),(0,_prefetch.prefetchStrings)("core",["reset"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const actionLink=event.target.closest(selectors_resetAllTemplatesAction);actionLink&&(event.preventDefault(),resetAllTemplatesConfirm(actionLink))}))},resetAllTemplatesConfirm=async actionLink=>{try{await _notification.default.deleteCancelPromise((0,_str.getString)("resetalltemplatesconfirmtitle","mod_data"),(0,_str.getString)("resetalltemplatesconfirm","mod_data"),(0,_str.getString)("reset","core")),window.location=actionLink.href}catch(error){return}}})); //# sourceMappingURL=resetalltemplates.min.js.mapresetalltemplates.min.js.map000064400000005743152267105120012204 0ustar00{"version":3,"file":"resetalltemplates.min.js","sources":["../src/resetalltemplates.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module for reseting all templates.\n *\n * @module mod_data/resetalltemplates\n * @copyright 2022 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\n\nconst selectors = {\n resetAllTemplatesAction: '[data-action=\"resetalltemplates\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n prefetchStrings('mod_data', [\n 'resetalltemplatesconfirmtitle',\n 'resetalltemplatesconfirm',\n ]);\n prefetchStrings('core', [\n 'reset',\n ]);\n registerEventListeners();\n};\n\n/**\n * Register events for option in action menu.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const actionLink = event.target.closest(selectors.resetAllTemplatesAction);\n if (actionLink) {\n event.preventDefault();\n resetAllTemplatesConfirm(actionLink);\n }\n });\n};\n\n/**\n * Show the confirmation modal to reset all the templates.\n *\n * @param {HTMLElement} actionLink the element that triggers the action.\n */\nconst resetAllTemplatesConfirm = async(actionLink) => {\n try {\n await Notification.deleteCancelPromise(\n getString('resetalltemplatesconfirmtitle', 'mod_data'),\n getString('resetalltemplatesconfirm', 'mod_data'),\n getString('reset', 'core'),\n );\n window.location = actionLink.href;\n } catch (error) {\n return;\n }\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","actionLink","target","closest","preventDefault","resetAllTemplatesConfirm","async","Notification","deleteCancelPromise","window","location","href","error"],"mappings":";;;;;;;4JA2BMA,kCACuB,kDAMT,mCACA,WAAY,CACxB,gCACA,2DAEY,OAAQ,CACpB,UAEJC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,WAAaD,MAAME,OAAOC,QAAQP,mCACpCK,aACAD,MAAMI,iBACNC,yBAAyBJ,iBAU/BI,yBAA2BC,MAAAA,uBAEnBC,sBAAaC,qBACf,kBAAU,gCAAiC,aAC3C,kBAAU,2BAA4B,aACtC,kBAAU,QAAS,SAEvBC,OAAOC,SAAWT,WAAWU,KAC/B,MAAOC"}importmappingdialogue.min.js.map000064400000015107152267105120013045 0ustar00{"version":3,"file":"importmappingdialogue.min.js","sources":["../src/importmappingdialogue.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module for deleting a database as a preset.\n *\n * @module mod_data/importmappingdialogue\n * @copyright 2022 Amaia Anabitarte \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport Ajax from 'core/ajax';\nimport Url from 'core/url';\nimport Templates from 'core/templates';\nimport Modal from 'core/modal';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\n\n// Load global strings.\nprefetchStrings('mod_data', ['mapping:dialogtitle:usepreset']);\n\nconst selectors = {\n selectPreset: '[data-action=\"selectpreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n registerEventListeners();\n};\n\n/**\n * Register events.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const preset = event.target.closest(selectors.selectPreset);\n if (preset) {\n event.preventDefault();\n showMappingDialogue(preset);\n }\n });\n};\n\n/**\n * Show the confirmation modal for uploading a preset.\n *\n * @param {HTMLElement} usepreset the preset to import.\n */\nconst showMappingDialogue = (usepreset) => {\n const presetName = usepreset.dataset.presetname;\n const cmId = usepreset.dataset.cmid;\n\n getMappingInformation(cmId, presetName).then((result) => {\n if (result.data && result.data.needsmapping) {\n buildModal({\n title: getString('mapping:dialogtitle:usepreset', 'mod_data', result.data.presetname),\n body: Templates.render('mod_data/fields_mapping_body', result.data),\n footer: Templates.render('mod_data/fields_mapping_footer', getMappingButtons(cmId, presetName)),\n large: true,\n show: true,\n });\n } else {\n window.location.href = Url.relativeUrl(\n 'mod/data/field.php',\n {\n id: cmId,\n mode: 'usepreset',\n fullname: presetName,\n },\n false\n );\n }\n return true;\n }).catch(Notification.exception);\n};\n\n/**\n * Given an object we want to build a modal ready to show\n *\n * @method buildModal\n * @param {Object} params the modal params\n * @param {Promise} params.title\n * @param {Promise} params.body\n * @param {Promise} params.footer\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = (params) => {\n return Modal.create({\n ...params,\n }).then(modal => {\n modal.showFooter();\n modal.registerCloseOnCancel();\n return modal;\n }).catch(Notification.exception);\n};\n\n/**\n * Add buttons to render on mapping modal.\n *\n * @param {int} cmId The id of the current database activity.\n * @param {string} presetName The preset name to delete.\n * @return {array} Same data with buttons.\n */\nconst getMappingButtons = (cmId, presetName) => {\n const data = {};\n\n data.mapfieldsbutton = Url.relativeUrl(\n 'mod/data/field.php',\n {\n id: cmId,\n fullname: presetName,\n mode: 'usepreset',\n action: 'select',\n },\n false\n );\n\n data.applybutton = Url.relativeUrl(\n 'mod/data/field.php',\n {\n id: cmId,\n fullname: presetName,\n mode: 'usepreset',\n action: 'notmapping'\n },\n false\n );\n\n return data;\n};\n\n/**\n * Check whether we should show the mapping dialogue or not.\n *\n * @param {int} cmId The id of the current database activity.\n * @param {string} presetName The preset name to delete.\n * @return {promise} Resolved with the result and warnings of deleting a preset.\n */\nconst getMappingInformation = (cmId, presetName) => {\n const request = {\n methodname: 'mod_data_get_mapping_information',\n args: {\n cmid: cmId,\n importedpreset: presetName,\n }\n };\n return Ajax.call([request])[0];\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","preset","target","closest","preventDefault","showMappingDialogue","usepreset","presetName","dataset","presetname","cmId","cmid","getMappingInformation","then","result","data","needsmapping","buildModal","title","body","Templates","render","footer","getMappingButtons","large","show","window","location","href","Url","relativeUrl","id","mode","fullname","catch","Notification","exception","params","Modal","create","modal","showFooter","registerCloseOnCancel","mapfieldsbutton","action","applybutton","request","methodname","args","importedpreset","Ajax","call"],"mappings":";;;;;;;8TAgCgB,WAAY,CAAC,wCAEvBA,uBACY,6CAME,KAChBC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,OAASD,MAAME,OAAOC,QAAQP,wBAChCK,SACAD,MAAMI,iBACNC,oBAAoBJ,aAU1BI,oBAAuBC,kBACnBC,WAAaD,UAAUE,QAAQC,WAC/BC,KAAOJ,UAAUE,QAAQG,KAE/BC,sBAAsBF,KAAMH,YAAYM,MAAMC,SACtCA,OAAOC,MAAQD,OAAOC,KAAKC,aAC3BC,WAAW,CACPC,OAAO,kBAAU,gCAAiC,WAAYJ,OAAOC,KAAKN,YAC1EU,KAAMC,mBAAUC,OAAO,+BAAgCP,OAAOC,MAC9DO,OAAQF,mBAAUC,OAAO,iCAAkCE,kBAAkBb,KAAMH,aACnFiB,OAAO,EACPC,MAAM,IAGVC,OAAOC,SAASC,KAAOC,aAAIC,YACvB,qBACA,CACIC,GAAIrB,KACJsB,KAAM,YACNC,SAAU1B,aAEd,IAGD,KACR2B,MAAMC,sBAAaC,YAapBnB,WAAcoB,QACTC,eAAMC,OAAO,IACbF,SACJxB,MAAK2B,QACJA,MAAMC,aACND,MAAME,wBACCF,SACRN,MAAMC,sBAAaC,WAUpBb,kBAAoB,CAACb,KAAMH,oBACvBQ,KAAO,UAEbA,KAAK4B,gBAAkBd,aAAIC,YACvB,qBACA,CACIC,GAAIrB,KACJuB,SAAU1B,WACVyB,KAAM,YACNY,OAAQ,WAEZ,GAGJ7B,KAAK8B,YAAchB,aAAIC,YACnB,qBACA,CACIC,GAAIrB,KACJuB,SAAU1B,WACVyB,KAAM,YACNY,OAAQ,eAEZ,GAGG7B,MAULH,sBAAwB,CAACF,KAAMH,oBAC3BuC,QAAU,CACZC,WAAY,mCACZC,KAAM,CACFrC,KAAMD,KACNuC,eAAgB1C,oBAGjB2C,cAAKC,KAAK,CAACL,UAAU"}saveaspreset.min.js.map000064400000006244152267105120011154 0ustar00{"version":3,"file":"saveaspreset.min.js","sources":["../src/saveaspreset.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module for saving a database as a preset.\n *\n * @module mod_data/saveaspreset\n * @copyright 2021 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\n\nconst selectors = {\n saveAsPresetButton: '[data-action=\"saveaspreset\"]',\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n\n document.addEventListener('click', (event) => {\n const saveAsPresetButton = event.target.closest(selectors.saveAsPresetButton);\n\n if (!saveAsPresetButton) {\n return;\n }\n\n event.preventDefault();\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('savedataaspreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\save_as_preset',\n args: {d: saveAsPresetButton.dataset.dataid},\n saveButtonText: getString('save'),\n returnFocus: saveAsPresetButton,\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('
        ')\n });\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["selectors","document","addEventListener","event","saveAsPresetButton","target","closest","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","args","d","dataset","dataid","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","window","location","reload","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,6BACkB,6CAMJ,KAEhBC,SAASC,iBAAiB,SAAUC,cAC1BC,mBAAqBD,MAAME,OAAOC,QAAQN,kCAE3CI,0BAILD,MAAMI,uBACAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,mBAAoB,aAEzCC,UAAW,iCACXC,KAAM,CAACC,EAAGV,mBAAmBW,QAAQC,QACrCC,gBAAgB,kBAAU,QAC1BC,YAAad,qBAIjBI,UAAUN,iBAAiBM,UAAUW,OAAOC,gBAAgBjB,QACpDA,MAAMkB,OAAOC,OACbC,OAAOC,SAASC,+BAEHC,gBAAgB,CACzBC,KAAM,QACNC,QAAUzB,MAAMkB,OAAOQ,OAAOC,KAAK,aAK/CtB,UAAUuB"}saveaspreset.min.js000064400000002663152267105120010401 0ustar00define("mod_data/saveaspreset",["exports","core_form/modalform","core/notification","core/str"],(function(_exports,_modalform,_notification,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Javascript module for saving a database as a preset. * * @module mod_data/saveaspreset * @copyright 2021 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_saveAsPresetButton='[data-action="saveaspreset"]';_exports.init=()=>{document.addEventListener("click",(event=>{const saveAsPresetButton=event.target.closest(selectors_saveAsPresetButton);if(!saveAsPresetButton)return;event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("savedataaspreset","mod_data")},formClass:"mod_data\\form\\save_as_preset",args:{d:saveAsPresetButton.dataset.dataid},saveButtonText:(0,_str.getString)("save"),returnFocus:saveAsPresetButton});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.reload():_notification.default.addNotification({type:"error",message:event.detail.errors.join("
        ")})})),modalForm.show()}))}})); //# sourceMappingURL=saveaspreset.min.js.maptemplateseditor.min.js000064400000005717152267105120011104 0ustar00define("mod_data/templateseditor",["exports","core/str","core/prefetch","core/url","core/notification","core/templates"],(function(_exports,_str,_prefetch,_url,_notification,_templates){var obj; /** * Javascript module to control the template editor. * * @module mod_data/templateseditor * @copyright 2021 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj},(0,_prefetch.prefetchStrings)("admin",["confirmation"]),(0,_prefetch.prefetchStrings)("mod_data",["resettemplateconfirmtitle","enabletemplateeditorcheck","editorenable"]),(0,_prefetch.prefetchStrings)("core",["reset"]);const selectors_toggleTemplateEditor='input[name="useeditor"]',selectors_resetTemplateAction='[data-action="resettemplate"]',selectors_resetTemplate='input[name="defaultform"]',selectors_resetAllTemplates='input[name="resetall"]',selectors_resetAllCheck='input[name="resetallcheck"]',selectors_editForm="#edittemplateform",registerResetButton=mode=>{const editForm=document.querySelector(selectors_editForm),resetTemplate=document.querySelector(selectors_resetTemplate),resetAllTemplates=document.querySelector(selectors_resetAllTemplates),resetTemplateAction=document.querySelector(selectors_resetTemplateAction);resetTemplateAction&&resetTemplate&&editForm&&((0,_prefetch.prefetchStrings)("mod_data",[mode]),resetTemplateAction.addEventListener("click",(async event=>{event.preventDefault();const params={resetallname:"resetallcheck",templatename:await(0,_str.getString)(mode,"mod_data")};(0,_notification.deleteCancel)((0,_str.getString)("resettemplateconfirmtitle","mod_data"),_templates.default.render("mod_data/template_editor_resetmodal",params),(0,_str.getString)("reset","core"),(()=>{resetTemplate.value="true",editForm.submit()}),null,{triggerElement:event.target})})),resetAllTemplates&&document.addEventListener("change",(event=>{event.target.matches(selectors_resetAllCheck)&&(resetAllTemplates.value=event.target.checked?"true":"")})))},registerEditorToggler=(instanceId,mode)=>{const toggleTemplateEditor=document.querySelector(selectors_toggleTemplateEditor);toggleTemplateEditor&&toggleTemplateEditor.addEventListener("click",(async event=>{event.preventDefault();event.target.checked?(0,_notification.saveCancel)((0,_str.getString)("confirmation","admin"),(0,_str.getString)("enabletemplateeditorcheck","mod_data"),(0,_str.getString)("editorenable","mod_data"),(()=>{window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:instanceId,mode:mode,useeditor:!0})}),null,{triggerElement:event.target}):window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:instanceId,mode:mode,useeditor:!1})}))};_exports.init=(instanceId,mode)=>{((instanceId,mode)=>{registerResetButton(mode),registerEditorToggler(instanceId,mode)})(instanceId,mode)}})); //# sourceMappingURL=templateseditor.min.js.mapimportpresets.min.js.map000064400000006125152267105120011365 0ustar00{"version":3,"file":"importpresets.min.js","sources":["../src/importpresets.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript module for importing presets.\n *\n * @module mod_data/importpresets\n * @copyright 2022 Laurent David \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\n\nconst selectors = {\n importPresetButton: '[data-action=\"importpresets\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n document.addEventListener('click', (event) => {\n const importPresetButton = event.target.closest(selectors.importPresetButton);\n\n if (!importPresetButton) {\n return;\n }\n\n event.preventDefault();\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('importpreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\import_presets',\n args: {cmid: importPresetButton.dataset.dataid},\n saveButtonText: getString('importandapply', 'mod_data'),\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.assign(event.detail.url);\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('
        ')\n });\n }\n });\n modalForm.show();\n });\n};\n"],"names":["selectors","document","addEventListener","event","importPresetButton","target","closest","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","args","cmid","dataset","dataid","saveButtonText","events","FORM_SUBMITTED","detail","result","window","location","assign","url","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,6BACkB,8CAMJ,KAChBC,SAASC,iBAAiB,SAAUC,cAC1BC,mBAAqBD,MAAME,OAAOC,QAAQN,kCAE3CI,0BAILD,MAAMI,uBACAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,eAAgB,aAErCC,UAAW,iCACXC,KAAM,CAACC,KAAMV,mBAAmBW,QAAQC,QACxCC,gBAAgB,kBAAU,iBAAkB,cAGhDT,UAAUN,iBAAiBM,UAAUU,OAAOC,gBAAgBhB,QACpDA,MAAMiB,OAAOC,OACbC,OAAOC,SAASC,OAAOrB,MAAMiB,OAAOK,2BAEvBC,gBAAgB,CACzBC,KAAM,QACNC,QAASzB,MAAMiB,OAAOS,OAAOC,KAAK,aAI9CtB,UAAUuB"}external_registration.min.js.map000064400000067261152267106260013077 0ustar00{"version":3,"file":"external_registration.min.js","sources":["../src/external_registration.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 * Encapsules the behavior for creating a tool type and tool proxy from a\n * registration url in Moodle.\n *\n * Manages the UI while operations are occuring, including rendering external\n * registration page within the iframe.\n *\n * See template: mod_lti/external_registration\n *\n * @module mod_lti/external_registration\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/events',\n 'mod_lti/tool_proxy', 'mod_lti/tool_type', 'mod_lti/keys', 'core/str'],\n function($, ajax, notification, templates, ltiEvents, toolProxy, toolType, KEYS, str) {\n\n var SELECTORS = {\n EXTERNAL_REGISTRATION_CONTAINER: '#external-registration-page-container',\n EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER: '#external-registration-template-container',\n EXTERNAL_REGISTRATION_CANCEL_BUTTON: '#cancel-external-registration',\n TOOL_TYPE_CAPABILITIES_CONTAINER: '#tool-type-capabilities-container',\n TOOL_TYPE_CAPABILITIES_TEMPLATE_CONTAINER: '#tool-type-capabilities-template-container',\n CAPABILITIES_AGREE_CONTAINER: '.capabilities-container',\n };\n\n /**\n * Return the external registration cancel button element. This button is\n * the cancel button that appears while the iframe is rendered.\n *\n * @method getExternalRegistrationCancelButton\n * @private\n * @return {JQuery} jQuery object\n */\n var getExternalRegistrationCancelButton = function() {\n return $(SELECTORS.EXTERNAL_REGISTRATION_CANCEL_BUTTON);\n };\n\n /**\n * Return the container that holds all elements for the external registration, including\n * the cancel button and the iframe.\n *\n * @method getExternalRegistrationContainer\n * @private\n * @return {JQuery} jQuery object\n */\n var getExternalRegistrationContainer = function() {\n return $(SELECTORS.EXTERNAL_REGISTRATION_CONTAINER);\n };\n\n /**\n * Return the container that holds the external registration page template. It should\n * be the iframe.\n *\n * @method getExternalRegistrationTemplateContainer\n * @private\n * @return {JQuery} jQuery object\n */\n var getExternalRegistrationTemplateContainer = function() {\n return $(SELECTORS.EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER);\n };\n\n /**\n * Return the container that holds the elements for displaying the list of capabilities\n * that this tool type requires. This container wraps the loading indicator and the template\n * container.\n *\n * @method getToolTypeCapabilitiesContainer\n * @private\n * @return {JQuery} jQuery object\n */\n var getToolTypeCapabilitiesContainer = function() {\n return $(SELECTORS.TOOL_TYPE_CAPABILITIES_CONTAINER);\n };\n\n /**\n * Return the container that holds the template that lists the capabilities that the\n * tool type will require.\n *\n * @method getToolTypeCapabilitiesTemplateContainer\n * @private\n * @return {JQuery} jQuery object\n */\n var getToolTypeCapabilitiesTemplateContainer = function() {\n return $(SELECTORS.TOOL_TYPE_CAPABILITIES_TEMPLATE_CONTAINER);\n };\n\n /**\n * Triggers a visual indicator to show that the capabilities section is loading.\n *\n * @method startLoadingCapabilitiesContainer\n * @private\n */\n var startLoadingCapabilitiesContainer = function() {\n getToolTypeCapabilitiesContainer().addClass('loading');\n };\n\n /**\n * Removes the visual indicator that shows the capabilities section is loading.\n *\n * @method stopLoadingCapabilitiesContainer\n * @private\n */\n var stopLoadingCapabilitiesContainer = function() {\n getToolTypeCapabilitiesContainer().removeClass('loading');\n };\n\n /**\n * Adds a visual indicator that shows the cancel button is loading.\n *\n * @method startLoadingCancel\n * @private\n */\n var startLoadingCancel = function() {\n getExternalRegistrationCancelButton().addClass('loading');\n };\n\n /**\n * Adds a visual indicator that shows the cancel button is loading.\n *\n * @method startLoadingCancel\n * @private\n */\n var stopLoadingCancel = function() {\n getExternalRegistrationCancelButton().removeClass('loading');\n };\n\n /**\n * Stops displaying the tool type capabilities container.\n *\n * @method hideToolTypeCapabilitiesContainer\n * @private\n */\n var hideToolTypeCapabilitiesContainer = function() {\n getToolTypeCapabilitiesContainer().addClass('hidden');\n };\n\n /**\n * Displays the tool type capabilities container.\n *\n * @method showToolTypeCapabilitiesContainer\n * @private\n */\n var showToolTypeCapabilitiesContainer = function() {\n getToolTypeCapabilitiesContainer().removeClass('hidden');\n };\n\n /**\n * Stops displaying the external registration content.\n *\n * @method hideExternalRegistrationContent\n * @private\n */\n var hideExternalRegistrationContent = function() {\n getExternalRegistrationContainer().addClass('hidden');\n };\n\n /**\n * Displays the external registration content.\n *\n * @method showExternalRegistrationContent\n * @private\n */\n var showExternalRegistrationContent = function() {\n getExternalRegistrationContainer().removeClass('hidden');\n };\n\n /**\n * Save the given tool proxy id on the DOM.\n *\n * @method setToolProxyId\n * @private\n * @param {Integer} id Tool proxy ID\n */\n var setToolProxyId = function(id) {\n var button = getExternalRegistrationCancelButton();\n button.attr('data-tool-proxy-id', id);\n };\n\n /**\n * Return the saved tool proxy id.\n *\n * @method getToolProxyId\n * @private\n * @return {String} Tool proxy ID\n */\n var getToolProxyId = function() {\n var button = getExternalRegistrationCancelButton();\n return button.attr('data-tool-proxy-id');\n };\n\n /**\n * Remove the saved tool proxy id.\n *\n * @method clearToolProxyId\n * @private\n */\n var clearToolProxyId = function() {\n var button = getExternalRegistrationCancelButton();\n button.removeAttr('data-tool-proxy-id');\n };\n\n /**\n * Returns true if a tool proxy id has been recorded.\n *\n * @method hasToolProxyId\n * @private\n * @return {Boolean}\n */\n var hasToolProxyId = function() {\n return getToolProxyId() ? true : false;\n };\n\n /**\n * Checks if this process has created a tool proxy within\n * Moodle yet.\n *\n * @method hasCreatedToolProxy\n * @private\n * @return {Boolean}\n */\n var hasCreatedToolProxy = function() {\n var button = getExternalRegistrationCancelButton();\n return button.attr('data-tool-proxy-new') && hasToolProxyId();\n };\n\n /**\n * Records that this process has created a tool proxy.\n *\n * @method setProxyAsNew\n * @private\n * @return {Boolean}\n */\n var setProxyAsNew = function() {\n var button = getExternalRegistrationCancelButton();\n return button.attr('data-tool-proxy-new', \"new\");\n };\n\n /**\n * Records that this process has not created a tool proxy.\n *\n * @method setProxyAsOld\n * @private\n * @return {Boolean}\n */\n var setProxyAsOld = function() {\n var button = getExternalRegistrationCancelButton();\n return button.removeAttr('data-tool-proxy-new');\n };\n\n /**\n * Gets the external registration request required to be sent to the external\n * registration page using a form.\n *\n * See mod_lti/tool_proxy_registration_form template.\n *\n * @method getRegistrationRequest\n * @private\n * @param {Integer} id Tool Proxy ID\n * @return {Promise} jQuery Deferred object\n */\n var getRegistrationRequest = function(id) {\n var request = {\n methodname: 'mod_lti_get_tool_proxy_registration_request',\n args: {\n id: id\n }\n };\n\n return ajax.call([request])[0];\n };\n\n /**\n * Cancel an in progress external registration. This will perform any necessary\n * clean up of tool proxies and return the page section back to the home section.\n *\n * @method cancelRegistration\n * @private\n * @return {Promise} jQuery Deferred object\n */\n var cancelRegistration = function() {\n startLoadingCancel();\n var promise = $.Deferred();\n\n // If we've created a proxy as part of this process then\n // we need to delete it to clean up the data in the back end.\n if (hasCreatedToolProxy()) {\n var id = getToolProxyId();\n toolProxy.delete(id).done(function() {\n promise.resolve();\n }).fail(function(failure) {\n promise.reject(failure);\n });\n } else {\n promise.resolve();\n }\n\n promise.done(function() {\n // Return to the original page.\n finishExternalRegistration();\n stopLoadingCancel();\n }).fail(function(failure) {\n notification.exception(failure);\n finishExternalRegistration();\n stopLoadingCancel();\n str.get_string('failedtodeletetoolproxy', 'mod_lti').done(function(s) {\n var feedback = {\n message: s,\n error: true\n };\n $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, feedback);\n }).fail(notification.exception);\n });\n\n return promise;\n };\n\n /**\n * Load the external registration template and render it in the DOM and display it.\n *\n * @method renderExternalRegistrationWindow\n * @private\n * @param {Object} registrationRequest\n * @return {Promise} jQuery Deferred object\n */\n var renderExternalRegistrationWindow = function(registrationRequest) {\n var promise = templates.render('mod_lti/tool_proxy_registration_form', registrationRequest);\n\n promise.done(function(html, js) {\n // Show the external registration page in an iframe.\n var container = getExternalRegistrationTemplateContainer();\n container.append(html);\n templates.runTemplateJS(js);\n\n container.find('form').submit();\n showExternalRegistrationContent();\n }).fail(notification.exception);\n\n return promise;\n };\n\n /**\n * Send a request to Moodle server to set the state of the tool type to configured (active).\n *\n * @method setTypeStatusActive\n * @private\n * @param {Object} typeData A set of data representing a type, as returned by a request to get a type\n * from the Moodle server.\n * @return {Promise} jQuery Deferred object\n */\n var setTypeStatusActive = function(typeData) {\n return toolType.update({\n id: typeData.id,\n state: toolType.constants.state.configured\n });\n };\n\n /**\n * Render and display an agreement page for the user to acknowledge the list of capabilities\n * (groups of data) that the external tool requires in order to work. If the user agrees then\n * we will activate the tool so that it is immediately available. If they don't agree then\n * the tool remains in a pending state within Moodle until agreement is given.\n *\n * @method promptForToolTypeCapabilitiesAgreement\n * @private\n * @param {Object} typeData A set of data representing a type, as returned by a request to get a type\n * from the Moodle server.\n * @return {Promise} jQuery Deferred object\n */\n var promptForToolTypeCapabilitiesAgreement = function(typeData) {\n var promise = $.Deferred();\n\n templates.render('mod_lti/tool_type_capabilities_agree', typeData).done(function(html, js) {\n var container = getToolTypeCapabilitiesTemplateContainer();\n\n hideExternalRegistrationContent();\n showToolTypeCapabilitiesContainer();\n\n templates.replaceNodeContents(container, html, js);\n\n var choiceContainer = container.find(SELECTORS.CAPABILITIES_AGREE_CONTAINER);\n\n // The user agrees to allow the tool to use the groups of data so we can go\n // ahead and activate it for them so that it can be used straight away.\n choiceContainer.on(ltiEvents.CAPABILITIES_AGREE, function() {\n startLoadingCapabilitiesContainer();\n setTypeStatusActive(typeData).always(function() {\n stopLoadingCapabilitiesContainer();\n container.empty();\n promise.resolve();\n });\n });\n\n // The user declines to let the tool use the data. In this case we leave\n // the tool as pending and they can delete it using the main screen if they\n // wish.\n choiceContainer.on(ltiEvents.CAPABILITIES_DECLINE, function() {\n container.empty();\n promise.resolve();\n });\n }).fail(promise.reject);\n\n promise.done(function() {\n hideToolTypeCapabilitiesContainer();\n }).fail(notification.exception);\n\n return promise;\n };\n\n /**\n * Send a request to the Moodle server to create a tool proxy using the registration URL the user\n * has provided. The proxy is required for the external registration page to work correctly.\n *\n * After the proxy is created the external registration page is rendered within an iframe for the user\n * to complete the registration in the external page.\n *\n * If the tool proxy creation fails then we redirect the page section back to the home section and\n * display the error, rather than rendering the external registration page.\n *\n * @method createAndRegisterToolProxy\n * @private\n * @param {String} url Tool registration URL to register\n * @return {Promise} jQuery Deferred object\n */\n var createAndRegisterToolProxy = function(url) {\n var promise = $.Deferred();\n\n if (!url || url === \"\") {\n // No URL has been input so do nothing.\n promise.resolve();\n } else {\n // A tool proxy needs to exist before the external page is rendered because\n // the external page sends requests back to Moodle for information that is stored\n // in the proxy.\n toolProxy.create({regurl: url})\n .done(function(result) {\n // Note that it's a new proxy so we will always clean it up.\n setProxyAsNew();\n promise = registerProxy(result.id);\n })\n .fail(function(exception) {\n // Clean up.\n cancelRegistration();\n // Let the user know what the error is.\n var feedback = {\n message: exception.message,\n error: true\n };\n $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, feedback);\n promise.reject(exception);\n });\n }\n\n return promise;\n };\n\n /**\n * Loads the window to register a proxy, given an ID.\n *\n * @method registerProxy\n * @private\n * @param {Integer} id Proxy id to register\n * @return {Promise} jQuery Deferred object to fail or resolve\n */\n var registerProxy = function(id) {\n var promise = $.Deferred();\n // Save the id on the DOM to cleanup later.\n setToolProxyId(id);\n\n // There is a specific set of data needed to send to the external registration page\n // in a form, so let's get it from our server.\n getRegistrationRequest(id)\n .done(function(registrationRequest) {\n renderExternalRegistrationWindow(registrationRequest)\n .done(function() {\n promise.resolve();\n })\n .fail(promise.fail);\n })\n .fail(promise.fail);\n\n return promise;\n };\n\n /**\n * Complete the registration process, clean up any left over data and\n * trigger the appropriate events.\n *\n * @method finishExternalRegistration\n * @private\n */\n var finishExternalRegistration = function() {\n if (hasToolProxyId()) {\n clearToolProxyId();\n }\n setProxyAsOld(false);\n\n hideExternalRegistrationContent();\n var container = getExternalRegistrationTemplateContainer();\n container.empty();\n\n $(document).trigger(ltiEvents.STOP_EXTERNAL_REGISTRATION);\n };\n\n /**\n * Sets up the listeners for user interaction on the page.\n *\n * @method registerEventListeners\n * @private\n */\n var registerEventListeners = function() {\n\n $(document).on(ltiEvents.START_EXTERNAL_REGISTRATION, function(event, data) {\n if (!data) {\n return;\n }\n if (data.url) {\n createAndRegisterToolProxy(data.url);\n }\n if (data.proxyid) {\n registerProxy(data.proxyid);\n }\n });\n\n var cancelExternalRegistrationButton = getExternalRegistrationCancelButton();\n cancelExternalRegistrationButton.click(function(e) {\n e.preventDefault();\n cancelRegistration();\n });\n cancelExternalRegistrationButton.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n e.preventDefault();\n cancelRegistration();\n }\n }\n });\n\n // This is gross but necessary due to isolated jQuery scopes between\n // child iframe and parent windows. There is no other way to communicate.\n //\n // This function gets called by the moodle page that received the redirect\n // from the external registration page and handles the external page's returned\n // parameters.\n //\n // See AMD module mod_lti/external_registration_return.\n window.triggerExternalRegistrationComplete = function(data) {\n var promise = $.Deferred();\n var feedback = {\n message: \"\",\n error: false\n };\n\n if (data.status == \"success\") {\n str.get_string('successfullycreatedtooltype', 'mod_lti').done(function(s) {\n feedback.message = s;\n }).fail(notification.exception);\n\n // Trigger appropriate events when we've completed the necessary requests.\n promise.done(function() {\n finishExternalRegistration();\n $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, feedback);\n $(document).trigger(ltiEvents.NEW_TOOL_TYPE);\n }).fail(notification.exception);\n\n // We should have created a tool proxy by this point.\n if (hasCreatedToolProxy()) {\n var proxyId = getToolProxyId();\n\n // We need the list of types that are linked to this proxy. We're assuming it'll\n // only be one because this process creates a one-to-one type->proxy.\n toolType.getFromToolProxyId(proxyId).done(function(types) {\n if (types && types.length) {\n // There should only be one result.\n var typeData = types[0];\n\n // Check if the external tool required access to any Moodle data (users, courses etc).\n if (typeData.hascapabilitygroups) {\n // If it did then we ask the user to agree to those groups before the type is\n // activated (i.e. can be used in Moodle).\n promptForToolTypeCapabilitiesAgreement(typeData).always(function() {\n promise.resolve();\n });\n } else {\n promise.resolve();\n }\n } else {\n promise.resolve();\n }\n }).fail(function() {\n promise.resolve();\n });\n }\n } else {\n // Anything other than success is failure.\n feedback.message = data.error;\n feedback.error = true;\n\n // Cancel registration to clean up any proxies and tools that were\n // created.\n promise.done(function() {\n cancelRegistration().always(function() {\n $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, feedback);\n });\n }).fail(notification.exception);\n\n promise.resolve();\n }\n\n return promise;\n };\n };\n\n return {\n\n /**\n * Initialise this module.\n */\n init: function() {\n registerEventListeners();\n }\n };\n});\n"],"names":["define","$","ajax","notification","templates","ltiEvents","toolProxy","toolType","KEYS","str","SELECTORS","getExternalRegistrationCancelButton","getExternalRegistrationContainer","getExternalRegistrationTemplateContainer","getToolTypeCapabilitiesContainer","stopLoadingCancel","removeClass","hideExternalRegistrationContent","addClass","getToolProxyId","attr","hasToolProxyId","hasCreatedToolProxy","cancelRegistration","promise","Deferred","id","delete","done","resolve","fail","failure","reject","finishExternalRegistration","exception","get_string","s","feedback","message","error","document","trigger","REGISTRATION_FEEDBACK","renderExternalRegistrationWindow","registrationRequest","render","html","js","container","append","runTemplateJS","find","submit","promptForToolTypeCapabilitiesAgreement","typeData","replaceNodeContents","choiceContainer","on","CAPABILITIES_AGREE","update","state","constants","configured","setTypeStatusActive","always","empty","CAPABILITIES_DECLINE","createAndRegisterToolProxy","url","create","regurl","result","registerProxy","setToolProxyId","request","methodname","args","call","getRegistrationRequest","removeAttr","STOP_EXTERNAL_REGISTRATION","init","START_EXTERNAL_REGISTRATION","event","data","proxyid","cancelExternalRegistrationButton","click","e","preventDefault","keypress","metaKey","shiftKey","altKey","ctrlKey","keyCode","ENTER","SPACE","window","triggerExternalRegistrationComplete","status","NEW_TOOL_TYPE","proxyId","getFromToolProxyId","types","length","hascapabilitygroups","registerEventListeners"],"mappings":";;;;;;;;;;;;;;AA6BAA,uCAAO,CAAC,SAAU,YAAa,oBAAqB,iBAAkB,iBAC9D,qBAAsB,oBAAqB,eAAgB,aAC3D,SAASC,EAAGC,KAAMC,aAAcC,UAAWC,UAAWC,UAAWC,SAAUC,KAAMC,SAEjFC,0CACiC,wCADjCA,mDAE0C,4CAF1CA,8CAGqC,gCAHrCA,2CAIkC,oCAJlCA,oDAK2C,6CAL3CA,uCAM8B,0BAW9BC,oCAAsC,kBAC/BV,EAAES,gDAWTE,iCAAmC,kBAC5BX,EAAES,4CAWTG,yCAA2C,kBACpCZ,EAAES,qDAYTI,iCAAmC,kBAC5Bb,EAAES,6CAmDTK,kBAAoB,WACpBJ,sCAAsCK,YAAY,YA6BlDC,gCAAkC,WAClCL,mCAAmCM,SAAS,WAgC5CC,eAAiB,kBACJR,sCACCS,KAAK,uBAqBnBC,eAAiB,mBACVF,kBAWPG,oBAAsB,kBACTX,sCACCS,KAAK,wBAA0BC,kBAyD7CE,mBAAqB,WAtKrBZ,sCAAsCO,SAAS,eAwK3CM,QAAUvB,EAAEwB,cAIZH,sBAAuB,KACnBI,GAAKP,iBACTb,UAAUqB,OAAOD,IAAIE,MAAK,WACtBJ,QAAQK,aACTC,MAAK,SAASC,SACbP,QAAQQ,OAAOD,iBAGnBP,QAAQK,iBAGZL,QAAQI,MAAK,WAETK,6BACAlB,uBACDe,MAAK,SAASC,SACb5B,aAAa+B,UAAUH,SACvBE,6BACAlB,oBACAN,IAAI0B,WAAW,0BAA2B,WAAWP,MAAK,SAASQ,OAC3DC,SAAW,CACXC,QAASF,EACTG,OAAO,GAEXtC,EAAEuC,UAAUC,QAAQpC,UAAUqC,sBAAuBL,aACtDP,KAAK3B,aAAa+B,cAGlBV,SAWPmB,iCAAmC,SAASC,yBACxCpB,QAAUpB,UAAUyC,OAAO,uCAAwCD,4BAEvEpB,QAAQI,MAAK,SAASkB,KAAMC,QAEpBC,UAAYnC,2CAChBmC,UAAUC,OAAOH,MACjB1C,UAAU8C,cAAcH,IAExBC,UAAUG,KAAK,QAAQC,SA1K3BxC,mCAAmCI,YAAY,aA4K5Cc,KAAK3B,aAAa+B,WAEdV,SA+BP6B,uCAAyC,SAASC,cAC9C9B,QAAUvB,EAAEwB,kBAEhBrB,UAAUyC,OAAO,uCAAwCS,UAAU1B,MAAK,SAASkB,KAAMC,QAC/EC,UAjSD/C,EAAES,qDAmSLO,kCAvOJH,mCAAmCE,YAAY,UA0O3CZ,UAAUmD,oBAAoBP,UAAWF,KAAMC,QAE3CS,gBAAkBR,UAAUG,KAAKzC,wCAIrC8C,gBAAgBC,GAAGpD,UAAUqD,oBAAoB,WAlSrD5C,mCAAmCI,SAAS,WAgQtB,SAASoC,iBACxB/C,SAASoD,OAAO,CACnBjC,GAAI4B,SAAS5B,GACbkC,MAAOrD,SAASsD,UAAUD,MAAME,aAiC5BC,CAAoBT,UAAUU,QAAO,WA1R7ClD,mCAAmCE,YAAY,WA4RnCgC,UAAUiB,QACVzC,QAAQK,gBAOhB2B,gBAAgBC,GAAGpD,UAAU6D,sBAAsB,WAC/ClB,UAAUiB,QACVzC,QAAQK,gBAEbC,KAAKN,QAAQQ,QAEhBR,QAAQI,MAAK,WA5Qbd,mCAAmCI,SAAS,aA8QzCY,KAAK3B,aAAa+B,WAEdV,SAkBP2C,2BAA6B,SAASC,SAClC5C,QAAUvB,EAAEwB,kBAEX2C,KAAe,KAARA,IAOR9D,UAAU+D,OAAO,CAACC,OAAQF,MACrBxC,MAAK,SAAS2C,QAzMV5D,sCACCS,KAAK,sBAAuB,OA2M1BI,QAAUgD,cAAcD,OAAO7C,OAEtCI,MAAK,SAASI,WAEPX,yBAEIc,SAAW,CACXC,QAASJ,UAAUI,QACnBC,OAAO,GAEXtC,EAAEuC,UAAUC,QAAQpC,UAAUqC,sBAAuBL,UACrDb,QAAQQ,OAAOE,cApB3BV,QAAQK,UAwBLL,SAWPgD,cAAgB,SAAS9C,QACrBF,QAAUvB,EAAEwB,kBAnSC,SAASC,IACbf,sCACNS,KAAK,qBAAsBM,IAmSlC+C,CAAe/C,IA9MU,SAASA,QAC9BgD,QAAU,CACVC,WAAY,8CACZC,KAAM,CACFlD,GAAIA,YAILxB,KAAK2E,KAAK,CAACH,UAAU,GA0M5BI,CAAuBpD,IAClBE,MAAK,SAASgB,qBACPD,iCAAiCC,qBAC5BhB,MAAK,WACEJ,QAAQK,aAEfC,KAAKN,QAAQM,SAEzBA,KAAKN,QAAQM,MAEXN,SAUPS,2BAA6B,WACzBZ,kBAtSSV,sCACNoE,WAAW,sBA+CLpE,sCACCoE,WAAW,uBA0PzB9D,kCACgBJ,2CACNoD,QAEVhE,EAAEuC,UAAUC,QAAQpC,UAAU2E,mCAgH3B,CAKHC,KAAM,YA5GmB,WAEzBhF,EAAEuC,UAAUiB,GAAGpD,UAAU6E,6BAA6B,SAASC,MAAOC,MACzDA,OAGDA,KAAKhB,KACLD,2BAA2BiB,KAAKhB,KAEhCgB,KAAKC,SACLb,cAAcY,KAAKC,iBAI3BC,iCAAmC3E,sCACvC2E,iCAAiCC,OAAM,SAASC,GAC5CA,EAAEC,iBACFlE,wBAEJ+D,iCAAiCI,UAAS,SAASF,GAC1CA,EAAEG,SAAYH,EAAEI,UAAaJ,EAAEK,QAAWL,EAAEM,SACzCN,EAAEO,SAAWvF,KAAKwF,OAASR,EAAEO,SAAWvF,KAAKyF,QAC7CT,EAAEC,iBACFlE,yBAaZ2E,OAAOC,oCAAsC,SAASf,UAC9C5D,QAAUvB,EAAEwB,WACZY,SAAW,CACXC,QAAS,GACTC,OAAO,MAGQ,WAAf6C,KAAKgB,WACL3F,IAAI0B,WAAW,8BAA+B,WAAWP,MAAK,SAASQ,GACnEC,SAASC,QAAUF,KACpBN,KAAK3B,aAAa+B,WAGrBV,QAAQI,MAAK,WACTK,6BACAhC,EAAEuC,UAAUC,QAAQpC,UAAUqC,sBAAuBL,UACrDpC,EAAEuC,UAAUC,QAAQpC,UAAUgG,kBAC/BvE,KAAK3B,aAAa+B,WAGjBZ,sBAAuB,KACnBgF,QAAUnF,iBAIdZ,SAASgG,mBAAmBD,SAAS1E,MAAK,SAAS4E,UAC3CA,OAASA,MAAMC,OAAQ,KAEnBnD,SAAWkD,MAAM,GAGjBlD,SAASoD,oBAGTrD,uCAAuCC,UAAUU,QAAO,WACpDxC,QAAQK,aAGZL,QAAQK,eAGZL,QAAQK,aAEbC,MAAK,WACJN,QAAQK,mBAKhBQ,SAASC,QAAU8C,KAAK7C,MACxBF,SAASE,OAAQ,EAIjBf,QAAQI,MAAK,WACTL,qBAAqByC,QAAO,WACxB/D,EAAEuC,UAAUC,QAAQpC,UAAUqC,sBAAuBL,gBAE1DP,KAAK3B,aAAa+B,WAErBV,QAAQK,iBAGLL,SAUPmF"}contentitem.min.js000064400000011507152267106260010230 0ustar00/** * Launches the modal dialogue that contains the iframe that sends the Content-Item selection request to an * LTI tool provider that supports Content-Item type message. * * See template: mod_lti/contentitem * * @module mod_lti/contentitem * @copyright 2016 Jun Pataleta * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.2 */ define("mod_lti/contentitem",["jquery","core/notification","core/str","core/templates","mod_lti/form-field","core/modal","core/modal_events"],(function($,notification,str,templates,FormField,Modal,ModalEvents){var dialogue,doneCallback,contentItem={init:function(url,toolUrl,postData,cb){doneCallback=cb;var context={url:url,toolUrl:toolUrl,postData:postData},bodyPromise=templates.render("mod_lti/contentitem",context);if(dialogue)return dialogue.setBody(bodyPromise),void dialogue.show();str.get_string("selectcontent","lti").then((function(title){return Modal.create({title:title,body:bodyPromise,large:!0,show:!0})})).then((function(modal){dialogue=modal,modal.getRoot().on(ModalEvents.hidden,(function(){modal.setBody(""),notification.fetchNotifications()}))})).catch(notification.exception)}},ltiFormFields=[new FormField("name",FormField.TYPES.TEXT,!1,""),new FormField("introeditor",FormField.TYPES.EDITOR,!1,""),new FormField("toolurl",FormField.TYPES.TEXT,!0,""),new FormField("securetoolurl",FormField.TYPES.TEXT,!0,""),new FormField("instructorchoiceacceptgrades",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorchoicesendname",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorchoicesendemailaddr",FormField.TYPES.CHECKBOX,!0,!0),new FormField("instructorcustomparameters",FormField.TYPES.TEXT,!0,""),new FormField("icon",FormField.TYPES.TEXT,!0,""),new FormField("secureicon",FormField.TYPES.TEXT,!0,""),new FormField("launchcontainer",FormField.TYPES.SELECT,!0,0),new FormField("grade_modgrade_point",FormField.TYPES.TEXT,!1,""),new FormField("lineitemresourceid",FormField.TYPES.TEXT,!0,""),new FormField("lineitemtag",FormField.TYPES.TEXT,!0,""),new FormField("lineitemsubreviewurl",FormField.TYPES.TEXT,!0,""),new FormField("lineitemsubreviewparams",FormField.TYPES.TEXT,!0,"")];const hideElement=e=>{e.setAttribute("hidden","true"),e.setAttribute("aria-hidden","true"),e.setAttribute("tab-index","-1")},showElement=e=>{e.removeAttribute("hidden"),e.setAttribute("aria-hidden","false"),e.setAttribute("tab-index","1")};return window.processContentItemReturnData=function(returnData){var index;if(dialogue&&dialogue.hide(),returnData.multiple){for(index in ltiFormFields)ltiFormFields[index].setFieldValue("name"===ltiFormFields[index].name?"item":null);var variants=[];returnData.multiple.forEach((function(v){variants.push((config=>{const variant={};return["name","toolurl","securetoolurl","instructorcustomparameters","icon","secureicon","launchcontainer","lineitemresourceid","lineitemtag","lineitemsubreviewurl","lineitemsubreviewparams"].forEach((function(name){variant[name]=config[name]||""})),variant["introeditor[text]"]=config.introeditor?config.introeditor.text:"",variant["introeditor[format]"]=config.introeditor?config.introeditor.format:"",1===config.instructorchoiceacceptgrades?(variant.instructorchoiceacceptgrades="1",variant["grade[modgrade_point]"]=config.grade_modgrade_point||"100"):variant.instructorchoiceacceptgrades="0",variant})(v))})),async function(items){const form=document.querySelector("#region-main-box form"),toolArea=form.querySelector('[data-attribute="dynamic-import"]'),buttonGroup=form.querySelector("#fgroup_id_buttonar"),submitAndLaunch=form.querySelector("#id_submitbutton");Array.from(form.children).forEach(hideElement),hideElement(submitAndLaunch);const{html:html,js:js}=await templates.renderForPromise("mod_lti/tool_deeplinking_results",{items:items});await templates.replaceNodeContents(toolArea,html,js),showElement(toolArea),showElement(buttonGroup)}(returnData.multiple);const submitAndCourse=document.querySelector("#id_submitbutton2");submitAndCourse.onclick=e=>{e.preventDefault(),submitAndCourse.disabled=!0;const fd=new FormData(document.querySelector("#region-main-box form")),backToCourse=()=>{document.querySelector("#id_cancel").click()};variants.reduce(((promise,variant)=>{Object.entries(variant).forEach((entry=>fd.set(entry[0],entry[1])));const body=new URLSearchParams(fd),doPost=()=>fetch(document.location.pathname,{method:"post",body:body});return promise.then(doPost).catch(doPost)}),Promise.resolve()).then(backToCourse).catch(backToCourse)}}else{for(index in ltiFormFields){var field=ltiFormFields[index],value=null;void 0!==returnData[field.name]&&(value=returnData[field.name]),field.setFieldValue(value)}field.setFieldValue(value),document.querySelector("#id_selectcontentindicator").innerHTML=returnData.selectcontentindicator}doneCallback&&doneCallback(returnData)},contentItem})); //# sourceMappingURL=contentitem.min.js.maptool_type.min.js.map000064400000013612152267106260010470 0ustar00{"version":3,"file":"tool_type.min.js","sources":["../src/tool_type.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 * Provides an interface for a tool type in the Moodle server.\n *\n * @module mod_lti/tool_type\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['core/ajax', 'core/notification'], function(ajax, notification) {\n return {\n /**\n * Get a list of tool types from Moodle for the given\n * search args.\n *\n * See also:\n * mod/lti/classes/external.php get_tool_types_parameters()\n *\n * @method query\n * @public\n * @param {Object} args Search parameters\n * @return {Promise} jQuery Deferred object\n */\n query: function(args) {\n var request = {\n methodname: 'mod_lti_get_tool_types',\n args: args || {}\n };\n\n var promise = ajax.call([request])[0];\n\n promise.fail(notification.exception);\n\n return promise;\n },\n\n /**\n * Create a tool type in Moodle.\n *\n * The promise will fail if the URL is not a cartridge, so you must handle the fail result.\n *\n * See also:\n * mod/lti/classes/external.php create_tool_type_parameters()\n *\n * @method create\n * @public\n * @param {Object} args Tool type properties\n * @return {Promise} jQuery Deferred object\n */\n create: function(args) {\n var request = {\n methodname: 'mod_lti_create_tool_type',\n args: args\n };\n\n var promise = ajax.call([request])[0];\n\n return promise;\n },\n\n /**\n * Update a tool type in Moodle.\n *\n * See also:\n * mod/lti/classes/external.php update_tool_type_parameters()\n *\n * @method update\n * @public\n * @param {Object} args Tool type properties\n * @return {Promise} jQuery Deferred object\n */\n update: function(args) {\n var request = {\n methodname: 'mod_lti_update_tool_type',\n args: args\n };\n\n var promise = ajax.call([request])[0];\n\n promise.fail(notification.exception);\n\n return promise;\n },\n\n /**\n * Delete a tool type from Moodle.\n *\n * @method delete\n * @public\n * @param {Integer} id Tool type ID\n * @return {Promise} jQuery Deferred object\n */\n 'delete': function(id) {\n var request = {\n methodname: 'mod_lti_delete_tool_type',\n args: {\n id: id\n }\n };\n\n var promise = ajax.call([request])[0];\n\n promise.fail(notification.exception);\n\n return promise;\n },\n\n /**\n * Get a list of tool types from Moodle for the given\n * tool proxy id.\n *\n * @method query\n * @public\n * @param {Integer} id Tool type ID\n * @return {Promise} jQuery Deferred object\n */\n getFromToolProxyId: function(id) {\n return this.query({toolproxyid: id});\n },\n\n /**\n * Check if the given URL is a cartridge URL.\n *\n * The promise will fail if the URL is unreachable, so you must handle the fail result.\n *\n * @method isCartridge\n * @public\n * @param {String} url\n * @return {Promise} jQuery Deferred object\n */\n isCartridge: function(url) {\n var request = {\n methodname: 'mod_lti_is_cartridge',\n args: {\n url: url\n }\n };\n\n var promise = ajax.call([request])[0];\n\n return promise;\n },\n\n /**\n * Tool type constants.\n */\n constants: {\n state: {\n configured: 1,\n pending: 2,\n rejected: 3\n },\n }\n };\n});\n"],"names":["define","ajax","notification","query","args","request","methodname","promise","call","fail","exception","create","update","id","getFromToolProxyId","this","toolproxyid","isCartridge","url","constants","state","configured","pending","rejected"],"mappings":";;;;;;;;AAuBAA,2BAAO,CAAC,YAAa,sBAAsB,SAASC,KAAMC,oBAC/C,CAaHC,MAAO,SAASC,UACRC,QAAU,CACVC,WAAY,yBACZF,KAAMA,MAAQ,IAGdG,QAAUN,KAAKO,KAAK,CAACH,UAAU,UAEnCE,QAAQE,KAAKP,aAAaQ,WAEnBH,SAgBXI,OAAQ,SAASP,UACTC,QAAU,CACVC,WAAY,2BACZF,KAAMA,aAGIH,KAAKO,KAAK,CAACH,UAAU,IAgBvCO,OAAQ,SAASR,UACTC,QAAU,CACVC,WAAY,2BACZF,KAAMA,MAGNG,QAAUN,KAAKO,KAAK,CAACH,UAAU,UAEnCE,QAAQE,KAAKP,aAAaQ,WAEnBH,gBAWD,SAASM,QACXR,QAAU,CACVC,WAAY,2BACZF,KAAM,CACFS,GAAIA,KAIRN,QAAUN,KAAKO,KAAK,CAACH,UAAU,UAEnCE,QAAQE,KAAKP,aAAaQ,WAEnBH,SAYXO,mBAAoB,SAASD,WAClBE,KAAKZ,MAAM,CAACa,YAAaH,MAapCI,YAAa,SAASC,SACdb,QAAU,CACVC,WAAY,uBACZF,KAAM,CACFc,IAAKA,aAICjB,KAAKO,KAAK,CAACH,UAAU,IAQvCc,UAAW,CACPC,MAAO,CACHC,WAAY,EACZC,QAAS,EACTC,SAAU"}mod_form.min.js000064400000002506152267106260007500 0ustar00define("mod_lti/mod_form",["exports","mod_lti/contentitem"],(function(_exports,_contentitem){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_contentitem=(obj=_contentitem)&&obj.__esModule?obj:{default:obj};var _default={init:(courseId,toolUrl)=>{const contentItemButton=document.querySelector('[name="selectcontent"]');contentItemButton&&contentItemButton.addEventListener("click",(()=>{const contentItemUrl=contentItemButton.getAttribute("data-contentitemurl"),contentItemId=document.querySelector("#hidden_typeid").value;if(contentItemId){const title=document.querySelector("#id_name").value.trim(),text=document.querySelector("#id_introeditor").value.trim(),postData={id:contentItemId,course:courseId,title:title,text:text};_contentitem.default.init(contentItemUrl,toolUrl,postData,(returnData=>{if(!returnData.multiple){const allowGrades=document.querySelector("#id_instructorchoiceacceptgrades");let allowGradesChangeEvent=new Event("change");if(allowGrades.dispatchEvent(allowGradesChangeEvent),allowGrades.checked){const gradeType=document.querySelector("#id_grade_modgrade_type");gradeType.value="point";let gradeTypeChangeEvent=new Event("change");gradeType.dispatchEvent(gradeTypeChangeEvent)}}}))}}))}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=mod_form.min.js.mapcartridge_registration_form.min.js000064400000005065152267106260013462 0ustar00/** * Encapsules the behavior for creating a tool type from a cartridge URL * in Moodle. Manages the UI while operations are occuring. * * See template: mod_lti/cartridge_registration_form * * @module mod_lti/cartridge_registration_form * @copyright 2015 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_lti/cartridge_registration_form",["jquery","core/ajax","core/notification","mod_lti/tool_type","mod_lti/events","mod_lti/keys","core/str"],(function($,ajax,notification,toolType,ltiEvents,KEYS,str){var SELECTORS_CONSUMER_KEY="#registration-key",SELECTORS_SHARED_SECRET="#registration-secret",SELECTORS_REGISTRATION_FORM="#cartridge-registration-form",SELECTORS_REGISTRATION_SUBMIT_BUTTON="#cartridge-registration-submit",SELECTORS_REGISTRATION_CANCEL_BUTTON="#cartridge-registration-cancel",getSubmitButton=function(){return $(SELECTORS_REGISTRATION_SUBMIT_BUTTON)},submitCartridgeURL=function(){if(getSubmitButton().hasClass("loading"))return!1;var url=$(SELECTORS_REGISTRATION_FORM).attr("data-cartridge-url");if(""===url)return!1;getSubmitButton().addClass("loading");var consumerKey=$(SELECTORS_CONSUMER_KEY).val(),sharedSecret=$(SELECTORS_SHARED_SECRET).val(),promise=toolType.create({cartridgeurl:url,key:consumerKey,secret:sharedSecret});return promise.done((function(){str.get_string("successfullycreatedtooltype","mod_lti").done((function(s){$(document).trigger(ltiEvents.NEW_TOOL_TYPE),$(document).trigger(ltiEvents.STOP_CARTRIDGE_REGISTRATION),$(document).trigger(ltiEvents.REGISTRATION_FEEDBACK,{message:s})})).fail(notification.exception)})).fail((function(){str.get_string("failedtocreatetooltype","mod_lti").done((function(s){$(document).trigger(ltiEvents.NEW_TOOL_TYPE),$(document).trigger(ltiEvents.STOP_CARTRIDGE_REGISTRATION),$(document).trigger(ltiEvents.REGISTRATION_FEEDBACK,{message:s,error:!0})})).fail(notification.exception)})).always((function(){getSubmitButton().removeClass("loading")})),promise},registerEventListeners=function(){$(SELECTORS_REGISTRATION_FORM).submit((function(e){e.preventDefault(),submitCartridgeURL()}));var cancelButton=$(SELECTORS_REGISTRATION_CANCEL_BUTTON);cancelButton.click((function(e){e.preventDefault(),$(document).trigger(ltiEvents.STOP_CARTRIDGE_REGISTRATION)})),cancelButton.keypress((function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||e.keyCode!=KEYS.ENTER&&e.keyCode!=KEYS.SPACE||(e.preventDefault(),cancelButton.click())}))};return{init:function(){registerEventListeners()}}})); //# sourceMappingURL=cartridge_registration_form.min.js.mapexternal_registration_return.min.js.map000064400000005007152267106260014464 0ustar00{"version":3,"file":"external_registration_return.min.js","sources":["../src/external_registration_return.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 * Handles the return params from the external registration page after it\n * redirects back to Moodle.\n *\n * See also: mod/lti/externalregistrationreturn.php\n *\n * @module mod_lti/external_registration_return\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine([], function() {\n return {\n\n /**\n * If this was rendered in an iframe then trigger the external registration\n * complete behaviour in the parent page and provide the params returned from\n * the external registration page.\n *\n * @param {String} message The registration message from the external registration page\n * @param {String} error The registration error message from the external registration page, if\n * there was an error.\n * @param {Integer} id The tool proxy id for the external registration.\n * @param {String} status Whether the external registration was successful or not.\n */\n init: function(message, error, id, status) {\n if (window.parent) {\n window.parent.triggerExternalRegistrationComplete({\n message: message,\n error: error,\n id: id,\n status: status\n });\n }\n }\n };\n});\n"],"names":["define","init","message","error","id","status","window","parent","triggerExternalRegistrationComplete"],"mappings":";;;;;;;;;;;AA0BAA,8CAAO,IAAI,iBACA,CAaHC,KAAM,SAASC,QAASC,MAAOC,GAAIC,QAC3BC,OAAOC,QACPD,OAAOC,OAAOC,oCAAoC,CAC9CN,QAASA,QACTC,MAAOA,MACPC,GAAIA,GACJC,OAAQA"}cartridge_registration_form.min.js.map000064400000021226152267106260014233 0ustar00{"version":3,"file":"cartridge_registration_form.min.js","sources":["../src/cartridge_registration_form.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 * Encapsules the behavior for creating a tool type from a cartridge URL\n * in Moodle. Manages the UI while operations are occuring.\n *\n * See template: mod_lti/cartridge_registration_form\n *\n * @module mod_lti/cartridge_registration_form\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/ajax', 'core/notification', 'mod_lti/tool_type', 'mod_lti/events', 'mod_lti/keys', 'core/str'],\n function($, ajax, notification, toolType, ltiEvents, KEYS, str) {\n\n var SELECTORS = {\n CARTRIDGE_URL: '#cartridge-url',\n CONSUMER_KEY: '#registration-key',\n SHARED_SECRET: '#registration-secret',\n REGISTRATION_FORM: '#cartridge-registration-form',\n REGISTRATION_SUBMIT_BUTTON: '#cartridge-registration-submit',\n REGISTRATION_CANCEL_BUTTON: '#cartridge-registration-cancel',\n };\n\n /**\n * Return the URL the user entered for the cartridge.\n *\n * @method getCartridgeURL\n * @private\n * @return {String}\n */\n var getCartridgeURL = function() {\n return $(SELECTORS.REGISTRATION_FORM).attr('data-cartridge-url');\n };\n\n /**\n * Return the submit button element.\n *\n * @method getSubmitButton\n * @private\n * @return {JQuery} jQuery object\n */\n var getSubmitButton = function() {\n return $(SELECTORS.REGISTRATION_SUBMIT_BUTTON);\n };\n\n /**\n * Return the cancel button element.\n *\n * @method getCancelButton\n * @private\n * @return {JQuery} jQuery object\n */\n var getCancelButton = function() {\n return $(SELECTORS.REGISTRATION_CANCEL_BUTTON);\n };\n\n /**\n * Return the value that the user entered for the consumer key.\n *\n * @method getConsumerKey\n * @private\n * @return {String} the value entered for consumer key.\n */\n var getConsumerKey = function() {\n return $(SELECTORS.CONSUMER_KEY).val();\n };\n\n /**\n * Return the value that the user entered for the shared secret.\n *\n * @method getSharedSecret\n * @private\n * @return {String} the value entered for shared secret\n */\n var getSharedSecret = function() {\n return $(SELECTORS.SHARED_SECRET).val();\n };\n\n /**\n * Trigger a visual loading indicator.\n *\n * @method startLoading\n * @private\n */\n var startLoading = function() {\n getSubmitButton().addClass('loading');\n };\n\n /**\n * Stop the visual loading indicator.\n *\n * @method stopLoading\n * @private\n */\n var stopLoading = function() {\n getSubmitButton().removeClass('loading');\n };\n\n /**\n * Check if the page is currently loading.\n *\n * @method isLoading\n * @private\n * @return {Boolean}\n */\n var isLoading = function() {\n return getSubmitButton().hasClass('loading');\n };\n\n /**\n * Create a tool type from the cartridge URL that the user input. This will\n * send an ajax request to the Moodle server to create the Type. The request will\n * include the consumer key and secret, if any.\n *\n * On success the page will be re-rendered to take the user back to the original\n * page with the list of tools and an alert notifying them of success.\n *\n * @method submitCartridgeURL\n * @private\n * @return {Promise} jQuery Deferred object\n */\n var submitCartridgeURL = function() {\n if (isLoading()) {\n return false;\n }\n\n var url = getCartridgeURL();\n // No URL? Do nothing.\n if (url === \"\") {\n return false;\n }\n\n startLoading();\n var consumerKey = getConsumerKey();\n var sharedSecret = getSharedSecret();\n var promise = toolType.create({cartridgeurl: url, key: consumerKey, secret: sharedSecret});\n\n promise.done(function() {\n str.get_string('successfullycreatedtooltype', 'mod_lti').done(function(s) {\n $(document).trigger(ltiEvents.NEW_TOOL_TYPE);\n $(document).trigger(ltiEvents.STOP_CARTRIDGE_REGISTRATION);\n $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, {\n message: s\n });\n }).fail(notification.exception);\n }).fail(function() {\n str.get_string('failedtocreatetooltype', 'mod_lti').done(function(s) {\n $(document).trigger(ltiEvents.NEW_TOOL_TYPE);\n $(document).trigger(ltiEvents.STOP_CARTRIDGE_REGISTRATION);\n $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, {\n message: s,\n error: true\n });\n }).fail(notification.exception);\n }).always(function() {\n stopLoading();\n });\n\n return promise;\n };\n\n /**\n * Sets up the listeners for user interaction on the page.\n *\n * @method registerEventListeners\n * @private\n */\n var registerEventListeners = function() {\n var form = $(SELECTORS.REGISTRATION_FORM);\n form.submit(function(e) {\n e.preventDefault();\n submitCartridgeURL();\n });\n\n var cancelButton = getCancelButton();\n cancelButton.click(function(e) {\n e.preventDefault();\n $(document).trigger(ltiEvents.STOP_CARTRIDGE_REGISTRATION);\n });\n cancelButton.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n e.preventDefault();\n cancelButton.click();\n }\n }\n });\n };\n\n return /** @alias module:mod_lti/cartridge_registration_form */ {\n\n /**\n * Initialise this module.\n */\n init: function() {\n registerEventListeners();\n }\n };\n});\n"],"names":["define","$","ajax","notification","toolType","ltiEvents","KEYS","str","SELECTORS","getSubmitButton","submitCartridgeURL","hasClass","url","attr","addClass","consumerKey","val","sharedSecret","promise","create","cartridgeurl","key","secret","done","get_string","s","document","trigger","NEW_TOOL_TYPE","STOP_CARTRIDGE_REGISTRATION","REGISTRATION_FEEDBACK","message","fail","exception","error","always","removeClass","registerEventListeners","submit","e","preventDefault","cancelButton","click","keypress","metaKey","shiftKey","altKey","ctrlKey","keyCode","ENTER","SPACE","init"],"mappings":";;;;;;;;;;;AA0BAA,6CAAO,CAAC,SAAU,YAAa,oBAAqB,oBAAqB,iBAAkB,eAAgB,aACnG,SAASC,EAAGC,KAAMC,aAAcC,SAAUC,UAAWC,KAAMC,SAE3DC,uBAEc,oBAFdA,wBAGe,uBAHfA,4BAImB,+BAJnBA,qCAK4B,iCAL5BA,qCAM4B,iCAqB5BC,gBAAkB,kBACXR,EAAEO,uCA+ETE,mBAAqB,cAfdD,kBAAkBE,SAAS,kBAiBvB,MAGPC,IA/FGX,EAAEO,6BAA6BK,KAAK,yBAiG/B,KAARD,WACO,EA5CXH,kBAAkBK,SAAS,eAgDvBC,YArEGd,EAAEO,wBAAwBQ,MAsE7BC,aA3DGhB,EAAEO,yBAAyBQ,MA4D9BE,QAAUd,SAASe,OAAO,CAACC,aAAcR,IAAKS,IAAKN,YAAaO,OAAQL,sBAE5EC,QAAQK,MAAK,WACThB,IAAIiB,WAAW,8BAA+B,WAAWD,MAAK,SAASE,GACnExB,EAAEyB,UAAUC,QAAQtB,UAAUuB,eAC9B3B,EAAEyB,UAAUC,QAAQtB,UAAUwB,6BAC9B5B,EAAEyB,UAAUC,QAAQtB,UAAUyB,sBAAuB,CACjDC,QAASN,OAEdO,KAAK7B,aAAa8B,cACtBD,MAAK,WACJzB,IAAIiB,WAAW,yBAA0B,WAAWD,MAAK,SAASE,GAC9DxB,EAAEyB,UAAUC,QAAQtB,UAAUuB,eAC9B3B,EAAEyB,UAAUC,QAAQtB,UAAUwB,6BAC9B5B,EAAEyB,UAAUC,QAAQtB,UAAUyB,sBAAuB,CACjDC,QAASN,EACTS,OAAO,OAEZF,KAAK7B,aAAa8B,cACtBE,QAAO,WA3DV1B,kBAAkB2B,YAAY,cA+DvBlB,SASPmB,uBAAyB,WACdpC,EAAEO,6BACR8B,QAAO,SAASC,GACjBA,EAAEC,iBACF9B,4BAGA+B,aAzHGxC,EAAEO,sCA0HTiC,aAAaC,OAAM,SAASH,GACxBA,EAAEC,iBACFvC,EAAEyB,UAAUC,QAAQtB,UAAUwB,gCAElCY,aAAaE,UAAS,SAASJ,GACtBA,EAAEK,SAAYL,EAAEM,UAAaN,EAAEO,QAAWP,EAAEQ,SACzCR,EAAES,SAAW1C,KAAK2C,OAASV,EAAES,SAAW1C,KAAK4C,QAC7CX,EAAEC,iBACFC,aAAaC,mBAMmC,CAK5DS,KAAM,WACFd"}tool_configure_controller.min.js.map000064400000053654152267106260013745 0ustar00{"version":3,"file":"tool_configure_controller.min.js","sources":["../src/tool_configure_controller.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 * Standard Ajax wrapper for Moodle. It calls the central Ajax script,\n * which can call any existing webservice using the current session.\n * In addition, it can batch multiple requests and return multiple responses.\n *\n * @module mod_lti/tool_configure_controller\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/ajax', 'core/paged_content_factory', 'core/notification', 'core/templates', 'mod_lti/events',\n 'mod_lti/keys', 'mod_lti/tool_types_and_proxies', 'mod_lti/tool_type', 'mod_lti/tool_proxy', 'core/str', 'core/config'],\n function($, ajax,\n pagedContentFactory, notification, templates, ltiEvents, KEYS,\n toolTypesAndProxies, toolType, toolProxy, str, config) {\n\n var SELECTORS = {\n EXTERNAL_REGISTRATION_CONTAINER: '#external-registration-container',\n EXTERNAL_REGISTRATION_PAGE_CONTAINER: '#external-registration-page-container',\n EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER: '#external-registration-template-container',\n CARTRIDGE_REGISTRATION_CONTAINER: '#cartridge-registration-container',\n CARTRIDGE_REGISTRATION_FORM: '#cartridge-registration-form',\n ADD_TOOL_FORM: '#add-tool-form',\n TOOL_CARD_CONTAINER: '#tool-card-container',\n TOOL_LIST_CONTAINER: '#tool-list-container',\n TOOL_CREATE_BUTTON: '#tool-create-button',\n TOOL_CREATE_LTILEGACY_BUTTON: '#tool-createltilegacy-button',\n REGISTRATION_CHOICE_CONTAINER: '#registration-choice-container',\n TOOL_URL: '#tool-url'\n };\n\n /**\n * Get the tool list container element.\n *\n * @method getToolListContainer\n * @private\n * @return {Object} jQuery object\n */\n var getToolListContainer = function() {\n return $(SELECTORS.TOOL_LIST_CONTAINER);\n };\n\n /**\n * Get the tool card container element.\n *\n * @method getToolCardContainer\n * @private\n * @return {Object} jQuery object\n */\n const getToolCardContainer = function() {\n return $(SELECTORS.TOOL_CARD_CONTAINER);\n };\n\n /**\n * Get the external registration container element.\n *\n * @method getExternalRegistrationContainer\n * @private\n * @return {Object} jQuery object\n */\n var getExternalRegistrationContainer = function() {\n return $(SELECTORS.EXTERNAL_REGISTRATION_CONTAINER);\n };\n\n /**\n * Get the cartridge registration container element.\n *\n * @method getCartridgeRegistrationContainer\n * @private\n * @return {Object} jQuery object\n */\n var getCartridgeRegistrationContainer = function() {\n return $(SELECTORS.CARTRIDGE_REGISTRATION_CONTAINER);\n };\n\n /**\n * Get the registration choice container element.\n *\n * @method getRegistrationChoiceContainer\n * @private\n * @return {Object} jQuery object\n */\n var getRegistrationChoiceContainer = function() {\n return $(SELECTORS.REGISTRATION_CHOICE_CONTAINER);\n };\n\n /**\n * Close the LTI Advantage Registration IFrame.\n *\n * @private\n * @param {Object} e post message event sent from the registration frame.\n */\n var closeLTIAdvRegistration = function(e) {\n if (e.data && 'org.imsglobal.lti.close' === e.data.subject) {\n $(SELECTORS.EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER).empty();\n hideExternalRegistration();\n showRegistrationChoices();\n showToolList();\n showRegistrationChoices();\n reloadToolList();\n }\n };\n\n /**\n * Load the external registration template and render it in the DOM and display it.\n *\n * @method initiateRegistration\n * @private\n * @param {String} url where to send the registration request\n */\n var initiateRegistration = function(url) {\n // Show the external registration page in an iframe.\n $(SELECTORS.EXTERNAL_REGISTRATION_PAGE_CONTAINER).removeClass('hidden');\n var container = $(SELECTORS.EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER);\n container.append($(\"\"));\n showExternalRegistration();\n window.addEventListener(\"message\", closeLTIAdvRegistration, false);\n };\n\n /**\n * Get the tool type URL.\n *\n * @method getToolURL\n * @private\n * @return {String} the tool type url\n */\n var getToolURL = function() {\n return $(SELECTORS.TOOL_URL).val();\n };\n\n /**\n * Hide the external registration container.\n *\n * @method hideExternalRegistration\n * @private\n */\n var hideExternalRegistration = function() {\n getExternalRegistrationContainer().addClass('hidden');\n };\n\n /**\n * Hide the cartridge registration container.\n *\n * @method hideCartridgeRegistration\n * @private\n */\n var hideCartridgeRegistration = function() {\n getCartridgeRegistrationContainer().addClass('hidden');\n };\n\n /**\n * Hide the registration choice container.\n *\n * @method hideRegistrationChoices\n * @private\n */\n var hideRegistrationChoices = function() {\n getRegistrationChoiceContainer().addClass('hidden');\n };\n\n /**\n * Display the external registration panel and hides the other\n * panels.\n *\n * @method showExternalRegistration\n * @private\n */\n var showExternalRegistration = function() {\n hideCartridgeRegistration();\n hideRegistrationChoices();\n getExternalRegistrationContainer().removeClass('hidden');\n screenReaderAnnounce(getExternalRegistrationContainer());\n };\n\n /**\n * Display the cartridge registration panel and hides the other\n * panels.\n *\n * @method showCartridgeRegistration\n * @param {String} url\n * @private\n */\n var showCartridgeRegistration = function(url) {\n hideExternalRegistration();\n hideRegistrationChoices();\n // Don't save the key and secret from the last tool.\n var container = getCartridgeRegistrationContainer();\n container.find('input').val('');\n container.removeClass('hidden');\n container.find(SELECTORS.CARTRIDGE_REGISTRATION_FORM).attr('data-cartridge-url', url);\n screenReaderAnnounce(container);\n };\n\n /**\n * Display the registration choices panel and hides the other\n * panels.\n *\n * @method showRegistrationChoices\n * @private\n */\n var showRegistrationChoices = function() {\n hideExternalRegistration();\n hideCartridgeRegistration();\n getRegistrationChoiceContainer().removeClass('hidden');\n screenReaderAnnounce(getRegistrationChoiceContainer());\n };\n\n /**\n * JAWS does not notice visibility changes with aria-live.\n * Remove and add the content back to force it to read it out.\n * This function can be removed once JAWS supports visibility.\n *\n * @method screenReaderAnnounce\n * @param {Object} element\n * @private\n */\n var screenReaderAnnounce = function(element) {\n var children = element.children().detach();\n children.appendTo(element);\n };\n\n /**\n * Hides the list of tool types.\n *\n * @method hideToolList\n * @private\n */\n var hideToolList = function() {\n getToolListContainer().addClass('hidden');\n };\n\n /**\n * Display the list of tool types.\n *\n * @method hideToolList\n * @private\n */\n var showToolList = function() {\n getToolListContainer().removeClass('hidden');\n };\n\n /**\n * Display the registration feedback alert and hide the other panels.\n *\n * @method showRegistrationFeedback\n * @param {Object} data\n * @private\n */\n var showRegistrationFeedback = function(data) {\n var type = data.error ? 'error' : 'success';\n notification.addNotification({\n message: data.message,\n type: type\n });\n };\n\n /**\n * Show the loading animation\n *\n * @method startLoading\n * @private\n * @param {Object} element jQuery object\n */\n var startLoading = function(element) {\n element.addClass(\"loading\");\n };\n\n /**\n * Hide the loading animation\n *\n * @method stopLoading\n * @private\n * @param {Object} element jQuery object\n */\n var stopLoading = function(element) {\n element.removeClass(\"loading\");\n };\n\n /**\n * Refresh the list of tool types and render the new ones.\n *\n * @method reloadToolList\n * @private\n */\n var reloadToolList = function() {\n // Behat tests should wait for the tool list to load.\n M.util.js_pending('reloadToolList');\n\n const cardContainer = getToolCardContainer();\n const listContainer = getToolListContainer();\n const limit = 60;\n // Get initial data with zero limit and offset.\n fetchToolCount().done(function(data) {\n pagedContentFactory.createWithTotalAndLimit(\n data.count,\n limit,\n function(pagesData) {\n return pagesData.map(function(pageData) {\n return fetchToolData(pageData.limit, pageData.offset)\n .then(function(data) {\n return renderToolData(data);\n });\n });\n },\n {\n 'showFirstLast': true\n })\n .done(function(html, js) {\n // Add the paged content into the page.\n templates.replaceNodeContents(cardContainer, html, js);\n })\n .always(function() {\n stopLoading(listContainer);\n M.util.js_complete('reloadToolList');\n });\n });\n startLoading(listContainer);\n };\n\n /**\n * Fetch the count of tool type and proxy datasets.\n *\n * @return {*|void}\n */\n const fetchToolCount = function() {\n return toolTypesAndProxies.count({'orphanedonly': true})\n .done(function(data) {\n return data;\n }).catch(function(error) {\n // Add debug message, then return empty data.\n notification.exception(error);\n return {\n 'count': 0\n };\n });\n };\n\n /**\n * Fetch the data for tool type and proxy cards.\n *\n * @param {number} limit Maximum number of datasets to get.\n * @param {number} offset Offset count for fetching the data.\n * @return {*|void}\n */\n const fetchToolData = function(limit, offset) {\n const args = {'orphanedonly': true};\n // Only add limit and offset to args if they are integers and not null, otherwise defaults will be used.\n if (limit !== null && !Number.isNaN(limit)) {\n args.limit = limit;\n }\n if (offset !== null && !Number.isNaN(offset)) {\n args.offset = offset;\n }\n return toolTypesAndProxies.query(args)\n .done(function(data) {\n return data;\n }).catch(function(error) {\n // Add debug message, then return empty data.\n notification.exception(error);\n return {\n 'types': [],\n 'proxies': [],\n 'limit': limit,\n 'offset': offset\n };\n });\n };\n\n /**\n * Render Tool and Proxy cards from data.\n *\n * @param {Object} data Contains arrays of data objects to populate cards.\n * @return {*}\n */\n const renderToolData = function(data) {\n const context = {\n tools: data.types,\n proxies: data.proxies,\n };\n return templates.render('mod_lti/tool_list', context)\n .done(function(html, js) {\n return {html, js};\n }\n );\n };\n\n /**\n * Start the LTI Advantage registration.\n *\n * @method addLTIAdvTool\n * @private\n */\n var addLTIAdvTool = function() {\n var url = getToolURL().trim();\n\n if (url) {\n $(SELECTORS.TOOL_URL).val('');\n hideToolList();\n initiateRegistration(url);\n }\n\n };\n\n /**\n * Trigger appropriate registration process process for the user input\n * URL. It can either be a cartridge or a registration url.\n *\n * @method addLTILegacyTool\n * @private\n * @return {Promise} jQuery Deferred object\n */\n var addLTILegacyTool = function() {\n var url = getToolURL().trim();\n\n if (url === \"\") {\n return $.Deferred().resolve();\n }\n var toolButton = $(SELECTORS.TOOL_CREATE_LTILEGACY_BUTTON);\n startLoading(toolButton);\n\n var promise = toolType.isCartridge(url);\n\n promise.always(function() {\n stopLoading(toolButton);\n });\n\n promise.done(function(result) {\n if (result.iscartridge) {\n $(SELECTORS.TOOL_URL).val('');\n $(document).trigger(ltiEvents.START_CARTRIDGE_REGISTRATION, url);\n } else {\n $(document).trigger(ltiEvents.START_EXTERNAL_REGISTRATION, {url: url});\n }\n });\n\n promise.fail(function() {\n str.get_string('errorbadurl', 'mod_lti')\n .done(function(s) {\n $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, {\n message: s,\n error: true\n });\n })\n .fail(notification.exception);\n });\n\n return promise;\n };\n\n /**\n * Sets up the listeners for user interaction on the page.\n *\n * @method registerEventListeners\n * @private\n */\n var registerEventListeners = function() {\n\n // These are events fired by the registration processes. Either\n // the cartridge registration or the external registration url.\n $(document).on(ltiEvents.NEW_TOOL_TYPE, function() {\n reloadToolList();\n });\n\n $(document).on(ltiEvents.START_EXTERNAL_REGISTRATION, function() {\n showExternalRegistration();\n $(SELECTORS.TOOL_URL).val('');\n hideToolList();\n });\n\n $(document).on(ltiEvents.STOP_EXTERNAL_REGISTRATION, function() {\n showToolList();\n showRegistrationChoices();\n });\n\n $(document).on(ltiEvents.START_CARTRIDGE_REGISTRATION, function(event, url) {\n showCartridgeRegistration(url);\n });\n\n $(document).on(ltiEvents.STOP_CARTRIDGE_REGISTRATION, function() {\n getCartridgeRegistrationContainer().find(SELECTORS.CARTRIDGE_REGISTRATION_FORM).removeAttr('data-cartridge-url');\n showRegistrationChoices();\n });\n\n $(document).on(ltiEvents.REGISTRATION_FEEDBACK, function(event, data) {\n showRegistrationFeedback(data);\n });\n\n var addLegacyButton = $(SELECTORS.TOOL_CREATE_LTILEGACY_BUTTON);\n addLegacyButton.click(function(e) {\n e.preventDefault();\n addLTILegacyTool();\n });\n\n var addLTIButton = $(SELECTORS.TOOL_CREATE_BUTTON);\n addLTIButton.click(function(e) {\n e.preventDefault();\n addLTIAdvTool();\n });\n\n };\n\n return /** @alias module:mod_lti/cartridge_registration_form */ {\n\n /**\n * Initialise this module.\n */\n init: function() {\n registerEventListeners();\n reloadToolList();\n }\n };\n});\n"],"names":["define","$","ajax","pagedContentFactory","notification","templates","ltiEvents","KEYS","toolTypesAndProxies","toolType","toolProxy","str","config","SELECTORS","getToolListContainer","getExternalRegistrationContainer","getCartridgeRegistrationContainer","getRegistrationChoiceContainer","closeLTIAdvRegistration","e","data","subject","empty","hideExternalRegistration","showRegistrationChoices","showToolList","reloadToolList","getToolURL","val","addClass","hideCartridgeRegistration","hideRegistrationChoices","showExternalRegistration","removeClass","screenReaderAnnounce","element","children","detach","appendTo","hideToolList","startLoading","stopLoading","M","util","js_pending","cardContainer","listContainer","fetchToolCount","done","createWithTotalAndLimit","count","pagesData","map","pageData","fetchToolData","limit","offset","then","renderToolData","html","js","replaceNodeContents","always","js_complete","catch","error","exception","args","Number","isNaN","query","context","tools","types","proxies","render","addLTIAdvTool","url","trim","append","encodeURIComponent","sesskey","window","addEventListener","initiateRegistration","registerEventListeners","document","on","NEW_TOOL_TYPE","START_EXTERNAL_REGISTRATION","STOP_EXTERNAL_REGISTRATION","START_CARTRIDGE_REGISTRATION","event","container","find","attr","showCartridgeRegistration","STOP_CARTRIDGE_REGISTRATION","removeAttr","REGISTRATION_FEEDBACK","type","addNotification","message","showRegistrationFeedback","click","preventDefault","Deferred","resolve","toolButton","promise","isCartridge","result","iscartridge","trigger","fail","get_string","s","addLTILegacyTool","init"],"mappings":";;;;;;;;;;AAyBAA,2CAAO,CAAC,SAAU,YAAa,6BAA8B,oBAAqB,iBAAkB,iBAC5F,eAAgB,iCAAkC,oBAAqB,qBAAsB,WAAY,gBACzG,SAASC,EAAGC,KACHC,oBAAqBC,aAAcC,UAAWC,UAAWC,KACzDC,oBAAqBC,SAAUC,UAAWC,IAAKC,YAExDC,0CACiC,mCADjCA,+CAEsC,wCAFtCA,mDAG0C,4CAH1CA,2CAIkC,oCAJlCA,sCAK6B,+BAL7BA,8BAOqB,uBAPrBA,8BAQqB,uBARrBA,6BASoB,sBATpBA,uCAU8B,+BAV9BA,wCAW+B,iCAX/BA,mBAYU,YAUVC,qBAAuB,kBAChBb,EAAEY,oCAqBTE,iCAAmC,kBAC5Bd,EAAEY,4CAUTG,kCAAoC,kBAC7Bf,EAAEY,6CAUTI,+BAAiC,kBAC1BhB,EAAEY,0CASTK,wBAA0B,SAASC,GAC/BA,EAAEC,MAAQ,4BAA8BD,EAAEC,KAAKC,UAC/CpB,EAAEY,oDAAoDS,QACtDC,2BACAC,0BACAC,eACAD,0BACAE,mBA4BJC,WAAa,kBACN1B,EAAEY,oBAAoBe,OAS7BL,yBAA2B,WAC3BR,mCAAmCc,SAAS,WAS5CC,0BAA4B,WAC5Bd,oCAAoCa,SAAS,WAS7CE,wBAA0B,WAC1Bd,iCAAiCY,SAAS,WAU1CG,yBAA2B,WAC3BF,4BACAC,0BACAhB,mCAAmCkB,YAAY,UAC/CC,qBAAqBnB,qCA6BrBS,wBAA0B,WAC1BD,2BACAO,4BACAb,iCAAiCgB,YAAY,UAC7CC,qBAAqBjB,mCAYrBiB,qBAAuB,SAASC,SACjBA,QAAQC,WAAWC,SACzBC,SAASH,UASlBI,aAAe,WACfzB,uBAAuBe,SAAS,WAShCJ,aAAe,WACfX,uBAAuBmB,YAAY,WAyBnCO,aAAe,SAASL,SACxBA,QAAQN,SAAS,YAUjBY,YAAc,SAASN,SACvBA,QAAQF,YAAY,YASpBP,eAAiB,WAEjBgB,EAAEC,KAAKC,WAAW,wBAEZC,cA/OC5C,EAAEY,+BAgPHiC,cAAgBhC,uBAGtBiC,iBAAiBC,MAAK,SAAS5B,MAC3BjB,oBAAoB8C,wBAChB7B,KAAK8B,MAJC,IAMN,SAASC,kBACEA,UAAUC,KAAI,SAASC,iBACnBC,cAAcD,SAASE,MAAOF,SAASG,QACzCC,MAAK,SAASrC,aACJsC,eAAetC,cAItC,gBACqB,IAEpB4B,MAAK,SAASW,KAAMC,IAErBvD,UAAUwD,oBAAoBhB,cAAec,KAAMC,OAElDE,QAAO,WACJrB,YAAYK,eACZJ,EAAEC,KAAKoB,YAAY,wBAG/BvB,aAAaM,sBAQXC,eAAiB,kBACZvC,oBAAoB0C,MAAM,eAAiB,IAC7CF,MAAK,SAAS5B,aACJA,QACR4C,OAAM,SAASC,cAEd7D,aAAa8D,UAAUD,OAChB,OACM,OAYnBX,cAAgB,SAASC,MAAOC,cAC5BW,KAAO,eAAiB,UAEhB,OAAVZ,OAAmBa,OAAOC,MAAMd,SAChCY,KAAKZ,MAAQA,OAEF,OAAXC,QAAoBY,OAAOC,MAAMb,UACjCW,KAAKX,OAASA,QAEXhD,oBAAoB8D,MAAMH,MAC5BnB,MAAK,SAAS5B,aACJA,QACR4C,OAAM,SAASC,cAEd7D,aAAa8D,UAAUD,OAChB,OACM,WACE,SACFV,aACCC,YAWpBE,eAAiB,SAAStC,YACtBmD,QAAU,CACZC,MAAOpD,KAAKqD,MACZC,QAAStD,KAAKsD,gBAEXrE,UAAUsE,OAAO,oBAAqBJ,SACxCvB,MAAK,SAASW,KAAMC,UACN,CAACD,KAAAA,KAAMC,GAAAA,YAW1BgB,cAAgB,eACZC,IAAMlD,aAAamD,OAEnBD,MACA5E,EAAEY,oBAAoBe,IAAI,IAC1BW,eAhSmB,SAASsC,KAEhC5E,EAAEY,gDAAgDoB,YAAY,UAC9ChC,EAAEY,oDACRkE,OAAO9E,EAAE,gDACA+E,mBAAmBH,KAAO,YAAcjE,OAAOqE,QAAU,gBAC5EjD,2BACAkD,OAAOC,iBAAiB,UAAWjE,yBAAyB,GA0RxDkE,CAAqBP,OAyDzBQ,uBAAyB,WAIzBpF,EAAEqF,UAAUC,GAAGjF,UAAUkF,eAAe,WACpC9D,oBAGJzB,EAAEqF,UAAUC,GAAGjF,UAAUmF,6BAA6B,WAClDzD,2BACA/B,EAAEY,oBAAoBe,IAAI,IAC1BW,kBAGJtC,EAAEqF,UAAUC,GAAGjF,UAAUoF,4BAA4B,WACjDjE,eACAD,6BAGJvB,EAAEqF,UAAUC,GAAGjF,UAAUqF,8BAA8B,SAASC,MAAOf,MApS3C,SAASA,KACrCtD,2BACAQ,8BAEI8D,UAAY7E,oCAChB6E,UAAUC,KAAK,SAASlE,IAAI,IAC5BiE,UAAU5D,YAAY,UACtB4D,UAAUC,KAAKjF,uCAAuCkF,KAAK,qBAAsBlB,KACjF3C,qBAAqB2D,WA6RjBG,CAA0BnB,QAG9B5E,EAAEqF,UAAUC,GAAGjF,UAAU2F,6BAA6B,WAClDjF,oCAAoC8E,KAAKjF,uCAAuCqF,WAAW,sBAC3F1E,6BAGJvB,EAAEqF,UAAUC,GAAGjF,UAAU6F,uBAAuB,SAASP,MAAOxE,OA3OrC,SAASA,UAChCgF,KAAOhF,KAAK6C,MAAQ,QAAU,UAClC7D,aAAaiG,gBAAgB,CACzBC,QAASlF,KAAKkF,QACdF,KAAMA,OAwONG,CAAyBnF,SAGPnB,EAAEY,wCACR2F,OAAM,SAASrF,GAC3BA,EAAEsF,iBA9Ea,eACf5B,IAAMlD,aAAamD,UAEX,KAARD,WACO5E,EAAEyG,WAAWC,cAEpBC,WAAa3G,EAAEY,wCACnB2B,aAAaoE,gBAETC,QAAUpG,SAASqG,YAAYjC,KAEnCgC,QAAQ/C,QAAO,WACbrB,YAAYmE,eAGdC,QAAQ7D,MAAK,SAAS+D,QACdA,OAAOC,aACP/G,EAAEY,oBAAoBe,IAAI,IAC1B3B,EAAEqF,UAAU2B,QAAQ3G,UAAUqF,6BAA8Bd,MAE5D5E,EAAEqF,UAAU2B,QAAQ3G,UAAUmF,4BAA6B,CAACZ,IAAKA,SAIzEgC,QAAQK,MAAK,WACTvG,IAAIwG,WAAW,cAAe,WACzBnE,MAAK,SAASoE,GACPnH,EAAEqF,UAAU2B,QAAQ3G,UAAU6F,sBAAuB,CAC7CG,QAASc,EACTnD,OAAO,OAGtBiD,KAAK9G,aAAa8D,cA+CvBmD,MAGepH,EAAEY,8BACR2F,OAAM,SAASrF,GACxBA,EAAEsF,iBACF7B,0BAKwD,CAK5D0C,KAAM,WACFjC,yBACA3D"}tool_proxy.min.js000064400000001530152267106260010110 0ustar00/** * Provides an interface for a tool proxy in the Moodle server. * * @module mod_lti/tool_proxy * @copyright 2015 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_lti/tool_proxy",["core/ajax","core/notification"],(function(ajax,notification){return{query:function(args){var request={methodname:"mod_lti_get_tool_proxies",args:args||{}},promise=ajax.call([request])[0];return promise.fail(notification.exception),promise},delete:function(id){var request={methodname:"mod_lti_delete_tool_proxy",args:{id:id}},promise=ajax.call([request])[0];return promise.fail(notification.exception),promise},create:function(args){var request={methodname:"mod_lti_create_tool_proxy",args:args};return ajax.call([request])[0]}}})); //# sourceMappingURL=tool_proxy.min.js.maptool_types_and_proxies.min.js000064400000001633152267106260012472 0ustar00define("mod_lti/tool_types_and_proxies",["exports","core/ajax"],(function(_exports,_ajax){var obj; /** * Provides an interface for external tools in the Moodle server. * * @module mod_lti/tool_types_and_proxies * @class tool_types_and_proxies * @copyright 2020 Andrew Madden * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.0 */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.query=_exports.count=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.query=args=>{const request={methodname:"mod_lti_get_tool_types_and_proxies",args:args||{}};return _ajax.default.call([request])[0]};_exports.count=args=>{const request={methodname:"mod_lti_get_tool_types_and_proxies_count",args:args||{}};return _ajax.default.call([request])[0]}})); //# sourceMappingURL=tool_types_and_proxies.min.js.mapexternal_registration_return.min.js000064400000001214152267106260013704 0ustar00/** * Handles the return params from the external registration page after it * redirects back to Moodle. * * See also: mod/lti/externalregistrationreturn.php * * @module mod_lti/external_registration_return * @copyright 2015 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_lti/external_registration_return",[],(function(){return{init:function(message,error,id,status){window.parent&&window.parent.triggerExternalRegistrationComplete({message:message,error:error,id:id,status:status})}}})); //# sourceMappingURL=external_registration_return.min.js.maptool_type.min.js000064400000002355152267106260007716 0ustar00/** * Provides an interface for a tool type in the Moodle server. * * @module mod_lti/tool_type * @copyright 2015 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_lti/tool_type",["core/ajax","core/notification"],(function(ajax,notification){return{query:function(args){var request={methodname:"mod_lti_get_tool_types",args:args||{}},promise=ajax.call([request])[0];return promise.fail(notification.exception),promise},create:function(args){var request={methodname:"mod_lti_create_tool_type",args:args};return ajax.call([request])[0]},update:function(args){var request={methodname:"mod_lti_update_tool_type",args:args},promise=ajax.call([request])[0];return promise.fail(notification.exception),promise},delete:function(id){var request={methodname:"mod_lti_delete_tool_type",args:{id:id}},promise=ajax.call([request])[0];return promise.fail(notification.exception),promise},getFromToolProxyId:function(id){return this.query({toolproxyid:id})},isCartridge:function(url){var request={methodname:"mod_lti_is_cartridge",args:{url:url}};return ajax.call([request])[0]},constants:{state:{configured:1,pending:2,rejected:3}}}})); //# sourceMappingURL=tool_type.min.js.mapform-field.min.js000064400000002416152267106260007722 0ustar00/** * A module that enables the setting of form field values on the client side. * * @module mod_lti/form-field * @copyright 2016 Jun Pataleta * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.2 */ define("mod_lti/form-field",["jquery"],(function($){var FormField=function(name,type,resetIfUndefined,defaultValue){this.name=name,this.id="id_"+this.name,this.selector="#"+this.id,this.type=type,this.resetIfUndefined=resetIfUndefined,this.defaultValue=defaultValue};return FormField.TYPES={TEXT:1,SELECT:2,CHECKBOX:3,EDITOR:4},FormField.prototype.setFieldValue=function(value){if(null===value){if(!this.resetIfUndefined)return;value=this.defaultValue}switch(this.type){case FormField.TYPES.CHECKBOX:value?$(this.selector).prop("checked",!0):$(this.selector).prop("checked",!1);break;case FormField.TYPES.EDITOR:if(void 0!==value.text){var attoEditor=$(this.selector+"editable");attoEditor.length?attoEditor.html(value.text):"undefined"!=typeof tinyMCE&&("3"==tinyMCE.majorVersion?tinyMCE.execInstanceCommand(this.id,"mceInsertContent",!1,value.text):tinyMCE.get(this.id).setContent(value.text)),$(this.selector).val(value.text)}break;default:$(this.selector).val(value)}},FormField})); //# sourceMappingURL=form-field.min.js.mapform-field.min.js.map000064400000012623152267106260010477 0ustar00{"version":3,"file":"form-field.min.js","sources":["../src/form-field.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 * A module that enables the setting of form field values on the client side.\n *\n * @module mod_lti/form-field\n * @copyright 2016 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.2\n */\ndefine(['jquery'],\n function($) {\n /**\n * Form field class.\n *\n * @param {string} name Field name.\n * @param {number} type The field type.\n * @param {boolean} resetIfUndefined Flag to reset the field to the default value if undefined in the return data.\n * @param {string|number|boolean} defaultValue The default value to use for the field.\n * @constructor\n */\n var FormField = function(name, type, resetIfUndefined, defaultValue) {\n this.name = name;\n this.id = 'id_' + this.name;\n this.selector = '#' + this.id;\n this.type = type;\n this.resetIfUndefined = resetIfUndefined;\n this.defaultValue = defaultValue;\n };\n\n /**\n * Form field types.\n *\n * @type {{TEXT: number, SELECT: number, CHECKBOX: number, EDITOR: number}}\n */\n FormField.TYPES = {\n TEXT: 1,\n SELECT: 2,\n CHECKBOX: 3,\n EDITOR: 4\n };\n\n /**\n * Sets the values for a form field.\n *\n * @param {string|boolean|number} value The value to be set into the field.\n */\n FormField.prototype.setFieldValue = function(value) {\n if (value === null) {\n if (this.resetIfUndefined) {\n value = this.defaultValue;\n } else {\n // No need set the field value if value is null and there's no need to reset the field.\n return;\n }\n }\n\n switch (this.type) {\n case FormField.TYPES.CHECKBOX:\n if (value) {\n $(this.selector).prop('checked', true);\n } else {\n $(this.selector).prop('checked', false);\n }\n break;\n case FormField.TYPES.EDITOR:\n if (typeof value.text !== 'undefined') {\n /* global tinyMCE:false */\n\n // Set text in editor's editable content, if applicable.\n // Check if it is an Atto editor.\n var attoEditor = $(this.selector + 'editable');\n if (attoEditor.length) {\n attoEditor.html(value.text);\n } else if (typeof tinyMCE !== 'undefined') {\n // If the editor is not Atto, try to fallback to TinyMCE.\n if (tinyMCE.majorVersion == \"3\") {\n // Tiny 3.\n tinyMCE.execInstanceCommand(this.id, 'mceInsertContent', false, value.text);\n } else {\n // Tiny 4+.\n tinyMCE.get(this.id).setContent(value.text);\n }\n }\n\n // Set text to actual editor text area.\n $(this.selector).val(value.text);\n }\n break;\n default:\n $(this.selector).val(value);\n break;\n }\n };\n\n return FormField;\n }\n);\n"],"names":["define","$","FormField","name","type","resetIfUndefined","defaultValue","id","this","selector","TYPES","TEXT","SELECT","CHECKBOX","EDITOR","prototype","setFieldValue","value","prop","text","attoEditor","length","html","tinyMCE","majorVersion","execInstanceCommand","get","setContent","val"],"mappings":";;;;;;;;AAuBAA,4BAAO,CAAC,WACJ,SAASC,OAUDC,UAAY,SAASC,KAAMC,KAAMC,iBAAkBC,mBAC9CH,KAAOA,UACPI,GAAK,MAAQC,KAAKL,UAClBM,SAAW,IAAMD,KAAKD,QACtBH,KAAOA,UACPC,iBAAmBA,sBACnBC,aAAeA,qBAQxBJ,UAAUQ,MAAQ,CACdC,KAAM,EACNC,OAAQ,EACRC,SAAU,EACVC,OAAQ,GAQZZ,UAAUa,UAAUC,cAAgB,SAASC,UAC3B,OAAVA,MAAgB,KACZT,KAAKH,wBACLY,MAAQT,KAAKF,oBAObE,KAAKJ,WACJF,UAAUQ,MAAMG,SACbI,MACAhB,EAAEO,KAAKC,UAAUS,KAAK,WAAW,GAEjCjB,EAAEO,KAAKC,UAAUS,KAAK,WAAW,cAGpChB,UAAUQ,MAAMI,eACS,IAAfG,MAAME,KAAsB,KAK/BC,WAAanB,EAAEO,KAAKC,SAAW,YAC/BW,WAAWC,OACXD,WAAWE,KAAKL,MAAME,MACI,oBAAZI,UAEc,KAAxBA,QAAQC,aAERD,QAAQE,oBAAoBjB,KAAKD,GAAI,oBAAoB,EAAOU,MAAME,MAGtEI,QAAQG,IAAIlB,KAAKD,IAAIoB,WAAWV,MAAME,OAK9ClB,EAAEO,KAAKC,UAAUmB,IAAIX,MAAME,oBAI/BlB,EAAEO,KAAKC,UAAUmB,IAAIX,SAK1Bf"}tool_proxy.min.js.map000064400000007123152267106260010670 0ustar00{"version":3,"file":"tool_proxy.min.js","sources":["../src/tool_proxy.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 * Provides an interface for a tool proxy in the Moodle server.\n *\n * @module mod_lti/tool_proxy\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['core/ajax', 'core/notification'], function(ajax, notification) {\n return {\n /**\n * Get a list of tool types from Moodle for the given\n * search args.\n *\n * See also:\n * mod/lti/classes/external.php get_tool_types_parameters()\n *\n * @method query\n * @public\n * @param {Object} args Search parameters\n * @return {Promise} jQuery Deferred object\n */\n query: function(args) {\n var request = {\n methodname: 'mod_lti_get_tool_proxies',\n args: args || {}\n };\n\n var promise = ajax.call([request])[0];\n\n promise.fail(notification.exception);\n\n return promise;\n },\n /**\n * Delete a tool proxy from Moodle.\n *\n * @method delete\n * @public\n * @param {Integer} id Tool proxy ID\n * @return {Promise} jQuery Deferred object\n */\n 'delete': function(id) {\n var request = {\n methodname: 'mod_lti_delete_tool_proxy',\n args: {\n id: id\n }\n };\n\n var promise = ajax.call([request])[0];\n\n promise.fail(notification.exception);\n\n return promise;\n },\n\n /**\n * Create a tool proxy in Moodle.\n *\n * The promise will fail if the proxy cannot be created, so you must handle the fail result.\n *\n * See mod/lti/classes/external.php create_tool_proxy_parameters\n *\n * @method create\n * @public\n * @param {Object} args Tool proxy properties\n * @return {Promise} jQuery Deferred object\n */\n create: function(args) {\n var request = {\n methodname: 'mod_lti_create_tool_proxy',\n args: args\n };\n\n var promise = ajax.call([request])[0];\n\n return promise;\n }\n };\n});\n"],"names":["define","ajax","notification","query","args","request","methodname","promise","call","fail","exception","id","create"],"mappings":";;;;;;;;AAuBAA,4BAAO,CAAC,YAAa,sBAAsB,SAASC,KAAMC,oBAC/C,CAaHC,MAAO,SAASC,UACRC,QAAU,CACVC,WAAY,2BACZF,KAAMA,MAAQ,IAGdG,QAAUN,KAAKO,KAAK,CAACH,UAAU,UAEnCE,QAAQE,KAAKP,aAAaQ,WAEnBH,gBAUD,SAASI,QACXN,QAAU,CACVC,WAAY,4BACZF,KAAM,CACFO,GAAIA,KAIRJ,QAAUN,KAAKO,KAAK,CAACH,UAAU,UAEnCE,QAAQE,KAAKP,aAAaQ,WAEnBH,SAeXK,OAAQ,SAASR,UACTC,QAAU,CACVC,WAAY,4BACZF,KAAMA,aAGIH,KAAKO,KAAK,CAACH,UAAU"}external_registration.min.js000064400000016126152267106260012315 0ustar00/** * Encapsules the behavior for creating a tool type and tool proxy from a * registration url in Moodle. * * Manages the UI while operations are occuring, including rendering external * registration page within the iframe. * * See template: mod_lti/external_registration * * @module mod_lti/external_registration * @copyright 2015 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_lti/external_registration",["jquery","core/ajax","core/notification","core/templates","mod_lti/events","mod_lti/tool_proxy","mod_lti/tool_type","mod_lti/keys","core/str"],(function($,ajax,notification,templates,ltiEvents,toolProxy,toolType,KEYS,str){var SELECTORS_EXTERNAL_REGISTRATION_CONTAINER="#external-registration-page-container",SELECTORS_EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER="#external-registration-template-container",SELECTORS_EXTERNAL_REGISTRATION_CANCEL_BUTTON="#cancel-external-registration",SELECTORS_TOOL_TYPE_CAPABILITIES_CONTAINER="#tool-type-capabilities-container",SELECTORS_TOOL_TYPE_CAPABILITIES_TEMPLATE_CONTAINER="#tool-type-capabilities-template-container",SELECTORS_CAPABILITIES_AGREE_CONTAINER=".capabilities-container",getExternalRegistrationCancelButton=function(){return $(SELECTORS_EXTERNAL_REGISTRATION_CANCEL_BUTTON)},getExternalRegistrationContainer=function(){return $(SELECTORS_EXTERNAL_REGISTRATION_CONTAINER)},getExternalRegistrationTemplateContainer=function(){return $(SELECTORS_EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER)},getToolTypeCapabilitiesContainer=function(){return $(SELECTORS_TOOL_TYPE_CAPABILITIES_CONTAINER)},stopLoadingCancel=function(){getExternalRegistrationCancelButton().removeClass("loading")},hideExternalRegistrationContent=function(){getExternalRegistrationContainer().addClass("hidden")},getToolProxyId=function(){return getExternalRegistrationCancelButton().attr("data-tool-proxy-id")},hasToolProxyId=function(){return!!getToolProxyId()},hasCreatedToolProxy=function(){return getExternalRegistrationCancelButton().attr("data-tool-proxy-new")&&hasToolProxyId()},cancelRegistration=function(){getExternalRegistrationCancelButton().addClass("loading");var promise=$.Deferred();if(hasCreatedToolProxy()){var id=getToolProxyId();toolProxy.delete(id).done((function(){promise.resolve()})).fail((function(failure){promise.reject(failure)}))}else promise.resolve();return promise.done((function(){finishExternalRegistration(),stopLoadingCancel()})).fail((function(failure){notification.exception(failure),finishExternalRegistration(),stopLoadingCancel(),str.get_string("failedtodeletetoolproxy","mod_lti").done((function(s){var feedback={message:s,error:!0};$(document).trigger(ltiEvents.REGISTRATION_FEEDBACK,feedback)})).fail(notification.exception)})),promise},renderExternalRegistrationWindow=function(registrationRequest){var promise=templates.render("mod_lti/tool_proxy_registration_form",registrationRequest);return promise.done((function(html,js){var container=getExternalRegistrationTemplateContainer();container.append(html),templates.runTemplateJS(js),container.find("form").submit(),getExternalRegistrationContainer().removeClass("hidden")})).fail(notification.exception),promise},promptForToolTypeCapabilitiesAgreement=function(typeData){var promise=$.Deferred();return templates.render("mod_lti/tool_type_capabilities_agree",typeData).done((function(html,js){var container=$(SELECTORS_TOOL_TYPE_CAPABILITIES_TEMPLATE_CONTAINER);hideExternalRegistrationContent(),getToolTypeCapabilitiesContainer().removeClass("hidden"),templates.replaceNodeContents(container,html,js);var choiceContainer=container.find(SELECTORS_CAPABILITIES_AGREE_CONTAINER);choiceContainer.on(ltiEvents.CAPABILITIES_AGREE,(function(){getToolTypeCapabilitiesContainer().addClass("loading"),function(typeData){return toolType.update({id:typeData.id,state:toolType.constants.state.configured})}(typeData).always((function(){getToolTypeCapabilitiesContainer().removeClass("loading"),container.empty(),promise.resolve()}))})),choiceContainer.on(ltiEvents.CAPABILITIES_DECLINE,(function(){container.empty(),promise.resolve()}))})).fail(promise.reject),promise.done((function(){getToolTypeCapabilitiesContainer().addClass("hidden")})).fail(notification.exception),promise},createAndRegisterToolProxy=function(url){var promise=$.Deferred();return url&&""!==url?toolProxy.create({regurl:url}).done((function(result){getExternalRegistrationCancelButton().attr("data-tool-proxy-new","new"),promise=registerProxy(result.id)})).fail((function(exception){cancelRegistration();var feedback={message:exception.message,error:!0};$(document).trigger(ltiEvents.REGISTRATION_FEEDBACK,feedback),promise.reject(exception)})):promise.resolve(),promise},registerProxy=function(id){var promise=$.Deferred();return function(id){getExternalRegistrationCancelButton().attr("data-tool-proxy-id",id)}(id),function(id){var request={methodname:"mod_lti_get_tool_proxy_registration_request",args:{id:id}};return ajax.call([request])[0]}(id).done((function(registrationRequest){renderExternalRegistrationWindow(registrationRequest).done((function(){promise.resolve()})).fail(promise.fail)})).fail(promise.fail),promise},finishExternalRegistration=function(){hasToolProxyId()&&getExternalRegistrationCancelButton().removeAttr("data-tool-proxy-id"),getExternalRegistrationCancelButton().removeAttr("data-tool-proxy-new"),hideExternalRegistrationContent(),getExternalRegistrationTemplateContainer().empty(),$(document).trigger(ltiEvents.STOP_EXTERNAL_REGISTRATION)};return{init:function(){!function(){$(document).on(ltiEvents.START_EXTERNAL_REGISTRATION,(function(event,data){data&&(data.url&&createAndRegisterToolProxy(data.url),data.proxyid&®isterProxy(data.proxyid))}));var cancelExternalRegistrationButton=getExternalRegistrationCancelButton();cancelExternalRegistrationButton.click((function(e){e.preventDefault(),cancelRegistration()})),cancelExternalRegistrationButton.keypress((function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||e.keyCode!=KEYS.ENTER&&e.keyCode!=KEYS.SPACE||(e.preventDefault(),cancelRegistration())})),window.triggerExternalRegistrationComplete=function(data){var promise=$.Deferred(),feedback={message:"",error:!1};if("success"==data.status){if(str.get_string("successfullycreatedtooltype","mod_lti").done((function(s){feedback.message=s})).fail(notification.exception),promise.done((function(){finishExternalRegistration(),$(document).trigger(ltiEvents.REGISTRATION_FEEDBACK,feedback),$(document).trigger(ltiEvents.NEW_TOOL_TYPE)})).fail(notification.exception),hasCreatedToolProxy()){var proxyId=getToolProxyId();toolType.getFromToolProxyId(proxyId).done((function(types){if(types&&types.length){var typeData=types[0];typeData.hascapabilitygroups?promptForToolTypeCapabilitiesAgreement(typeData).always((function(){promise.resolve()})):promise.resolve()}else promise.resolve()})).fail((function(){promise.resolve()}))}}else feedback.message=data.error,feedback.error=!0,promise.done((function(){cancelRegistration().always((function(){$(document).trigger(ltiEvents.REGISTRATION_FEEDBACK,feedback)}))})).fail(notification.exception),promise.resolve();return promise}}()}}})); //# sourceMappingURL=external_registration.min.js.mapcontentitem_return.min.js.map000064400000003744152267106260012407 0ustar00{"version":3,"file":"contentitem_return.min.js","sources":["../src/contentitem_return.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 * Processes the result of LTI tool creation from a Content-Item message type.\n *\n * @module mod_lti/contentitem_return\n * @copyright 2016 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.2\n */\ndefine(['jquery'], function($) {\n return {\n /**\n * Init function.\n *\n * @param {string} returnData The returned data.\n */\n init: function(returnData) {\n // Make sure the window has loaded before we perform processing.\n $(window).ready(function() {\n if (window != top) {\n // Send return data to be processed by the parent window.\n parent.processContentItemReturnData(returnData);\n } else {\n window.processContentItemReturnData(returnData);\n }\n });\n }\n };\n});\n"],"names":["define","$","init","returnData","window","ready","top","parent","processContentItemReturnData"],"mappings":";;;;;;;;AAuBAA,oCAAO,CAAC,WAAW,SAASC,SACjB,CAMHC,KAAM,SAASC,YAEXF,EAAEG,QAAQC,OAAM,WACRD,QAAUE,IAEVC,OAAOC,6BAA6BL,YAEpCC,OAAOI,6BAA6BL"}tool_proxy_card_controller.min.js000064400000006203152267106260013346 0ustar00/** * Controls all of the behaviour and interaction with a tool type card. These are * listed on the LTI tool type management page. * * See template: mod_lti/tool_proxy_card * * @module mod_lti/tool_proxy_card_controller * @copyright 2016 John Okely * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_lti/tool_proxy_card_controller",["jquery","core/ajax","core/notification","core/templates","mod_lti/tool_proxy","mod_lti/events","mod_lti/keys","core/str"],(function($,ajax,notification,templates,toolProxy,ltiEvents,KEYS,str){var SELECTORS_DELETE_BUTTON=".delete",SELECTORS_ACTIVATE_BUTTON=".tool-card-footer a.activate",getTypeId=function(element){return element.attr("data-proxy-id")},clearAllAnnouncements=function(element){element.removeClass("announcement loading success fail capabilities")},stopLoading=function(element){element.removeClass("announcement loading")},deleteType=function(element){var promise=$.Deferred(),typeId=getTypeId(element);return function(element){clearAllAnnouncements(element),element.addClass("announcement loading")}(element),""===typeId?$.Deferred().resolve():(str.get_strings([{key:"delete",component:"mod_lti"},{key:"delete_confirmation",component:"mod_lti"},{key:"delete",component:"mod_lti"},{key:"cancel",component:"core"}]).done((function(strs){notification.confirm(strs[0],strs[1],strs[2],strs[3],(function(){toolProxy.delete(typeId).done((function(){stopLoading(element),function(element){var promise=$.Deferred();return clearAllAnnouncements(element),element.addClass("announcement success"),setTimeout((function(){element.removeClass("announcement success"),promise.resolve()}),2e3),promise}(element).done((function(){element.remove(),promise.resolve()})).fail(notification.exception)})).fail((function(error){!function(element){var promise=$.Deferred();clearAllAnnouncements(element),element.addClass("announcement fail"),setTimeout((function(){element.removeClass("announcement fail"),promise.resolve()}),2e3)}(element),promise.reject(error)}))}),(function(){stopLoading(element),promise.resolve()}))})).fail((function(error){stopLoading(element),notification.exception(error),promise.reject(error)})),promise)},registerEventListeners=function(element){var deleteButton=function(element){return element.find(SELECTORS_DELETE_BUTTON)}(element);deleteButton.click((function(e){e.preventDefault(),deleteType(element)})),deleteButton.keypress((function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||e.keyCode!=KEYS.ENTER&&e.keyCode!=KEYS.SPACE||(e.preventDefault(),deleteButton.click())}));var activateButton=function(element){return element.find(SELECTORS_ACTIVATE_BUTTON)}(element);activateButton.click((function(e){e.preventDefault(),function(element){var data={proxyid:getTypeId(element)};$(document).trigger(ltiEvents.START_EXTERNAL_REGISTRATION,data)}(element)})),activateButton.keypress((function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||e.keyCode!=KEYS.ENTER&&e.keyCode!=KEYS.SPACE||(e.preventDefault(),activateButton.click())}))};return{init:function(element){registerEventListeners(element)}}})); //# sourceMappingURL=tool_proxy_card_controller.min.js.mapkeys.min.js000064400000000567152267106260006656 0ustar00/** * A list of keys and their keycodes that are used by the LTI modules. * * @module mod_lti/keys * @class keys * @copyright 2015 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_lti/keys",[],(function(){return{ENTER:13,SPACE:32}})); //# sourceMappingURL=keys.min.js.mapcourse_tools_list.min.js000064400000006677152267106260011466 0ustar00define("mod_lti/course_tools_list",["exports","core/notification","core/pending","core/ajax","core/toast","core/str","core_table/dynamic","core_table/local/dynamic/selectors","./repository"],(function(_exports,_notification,_pending,_ajax,_toast,_str,_dynamic,Selectors,_repository){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_ajax=_interopRequireDefault(_ajax),Selectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Selectors);_exports.init=()=>{document.addEventListener("click",(event=>{const courseToolDelete=event.target.closest('[data-action="course-tool-delete"]');if(courseToolDelete){event.preventDefault();const deleteBodyStringId=courseToolDelete.dataset.courseToolUsage>0?"deletecoursetoolwithusageconfirm":"deletecoursetoolconfirm",requiredStrings=[{key:"deletecoursetool",component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:deleteBodyStringId,component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:"delete",component:"core",param:courseToolDelete.dataset.courseToolName},{key:"coursetooldeleted",component:"mod_lti",param:courseToolDelete.dataset.courseToolName}],triggerElement=courseToolDelete.closest(".dropdown").querySelector(".dropdown-toggle");(0,_str.getStrings)(requiredStrings).then((_ref=>{let[modalTitle,modalBody,deleteLabel]=_ref;return _notification.default.deleteCancelPromise(modalTitle,modalBody,deleteLabel,{triggerElement:triggerElement})})).then((()=>{const pendingPromise=new _pending.default("mod_lti/course_tools:delete"),request={methodname:"mod_lti_delete_course_tool_type",args:{tooltypeid:courseToolDelete.dataset.courseToolId}};return _ajax.default.call([request])[0].then((0,_toast.add)((0,_str.getString)("coursetooldeleted","mod_lti",courseToolDelete.dataset.courseToolName))).then((()=>{const tableRoot=triggerElement.closest(Selectors.main.region);return(0,_dynamic.refreshTableContent)(tableRoot)})).then(pendingPromise.resolve).catch(_notification.default.exception)})).catch((()=>{}))}const courseShowInActivityChooser=event.target.closest('[data-action="showinactivitychooser-toggle"]');if(courseShowInActivityChooser){const showInActivityChooserStateToggle="0"===courseShowInActivityChooser.dataset.state?1:0;return(0,_repository.toggleShowInActivityChooser)(courseShowInActivityChooser.dataset.id,courseShowInActivityChooser.dataset.courseid,showInActivityChooserStateToggle)}}))}})); //# sourceMappingURL=course_tools_list.min.js.maptool_proxy_card_controller.min.js.map000064400000030020152267106260014114 0ustar00{"version":3,"file":"tool_proxy_card_controller.min.js","sources":["../src/tool_proxy_card_controller.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 * Controls all of the behaviour and interaction with a tool type card. These are\n * listed on the LTI tool type management page.\n *\n * See template: mod_lti/tool_proxy_card\n *\n * @module mod_lti/tool_proxy_card_controller\n * @copyright 2016 John Okely \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/tool_proxy', 'mod_lti/events', 'mod_lti/keys',\n 'core/str'],\n function($, ajax, notification, templates, toolProxy, ltiEvents, KEYS, str) {\n\n var SELECTORS = {\n DELETE_BUTTON: '.delete',\n CAPABILITIES_CONTAINER: '.capabilities-container',\n ACTIVATE_BUTTON: '.tool-card-footer a.activate',\n };\n\n // Timeout in seconds.\n var ANNOUNCEMENT_TIMEOUT = 2000;\n\n /**\n * Return the delete button element.\n *\n * @method getDeleteButton\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {JQuery} jQuery object\n */\n var getDeleteButton = function(element) {\n return element.find(SELECTORS.DELETE_BUTTON);\n };\n\n /**\n * Return the activate button for the type.\n *\n * @method getActivateButton\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {JQuery} jQuery object\n */\n var getActivateButton = function(element) {\n return element.find(SELECTORS.ACTIVATE_BUTTON);\n };\n\n /**\n * Get the type id.\n *\n * @method getTypeId\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {String} Type ID\n */\n var getTypeId = function(element) {\n return element.attr('data-proxy-id');\n };\n\n /**\n * Stop any announcement currently visible on the card.\n *\n * @method clearAllAnnouncements\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var clearAllAnnouncements = function(element) {\n element.removeClass('announcement loading success fail capabilities');\n };\n\n /**\n * Show the loading announcement.\n *\n * @method startLoading\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var startLoading = function(element) {\n clearAllAnnouncements(element);\n element.addClass('announcement loading');\n };\n\n /**\n * Hide the loading announcement.\n *\n * @method stopLoading\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var stopLoading = function(element) {\n element.removeClass('announcement loading');\n };\n\n /**\n * Show the success announcement. The announcement is only\n * visible for 2 seconds.\n *\n * @method announceSuccess\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var announceSuccess = function(element) {\n var promise = $.Deferred();\n\n clearAllAnnouncements(element);\n element.addClass('announcement success');\n setTimeout(function() {\n element.removeClass('announcement success');\n promise.resolve();\n }, ANNOUNCEMENT_TIMEOUT);\n\n return promise;\n };\n\n /**\n * Show the failure announcement. The announcement is only\n * visible for 2 seconds.\n *\n * @method announceFailure\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var announceFailure = function(element) {\n var promise = $.Deferred();\n\n clearAllAnnouncements(element);\n element.addClass('announcement fail');\n setTimeout(function() {\n element.removeClass('announcement fail');\n promise.resolve();\n }, ANNOUNCEMENT_TIMEOUT);\n\n return promise;\n };\n\n /**\n * Delete the tool type from the Moodle server. Triggers a success\n * or failure announcement depending on the result.\n *\n * @method deleteType\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var deleteType = function(element) {\n var promise = $.Deferred();\n var typeId = getTypeId(element);\n startLoading(element);\n\n if (typeId === \"\") {\n return $.Deferred().resolve();\n }\n\n str.get_strings([\n {\n key: 'delete',\n component: 'mod_lti'\n },\n {\n key: 'delete_confirmation',\n component: 'mod_lti'\n },\n {\n key: 'delete',\n component: 'mod_lti'\n },\n {\n key: 'cancel',\n component: 'core'\n },\n ])\n .done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n toolProxy.delete(typeId)\n .done(function() {\n stopLoading(element);\n announceSuccess(element)\n .done(function() {\n element.remove();\n promise.resolve();\n })\n .fail(notification.exception);\n })\n .fail(function(error) {\n announceFailure(element);\n promise.reject(error);\n });\n }, function() {\n stopLoading(element);\n promise.resolve();\n });\n })\n .fail(function(error) {\n stopLoading(element);\n notification.exception(error);\n promise.reject(error);\n });\n\n return promise;\n };\n\n /**\n * The user wishes to activate this tool so show them the capabilities that\n * they need to agree to or if there are none then set the tool type's state\n * to active.\n *\n * @method activateToolType\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var activateToolType = function(element) {\n var data = {proxyid: getTypeId(element)};\n $(document).trigger(ltiEvents.START_EXTERNAL_REGISTRATION, data);\n };\n\n /**\n * Sets up the listeners for user interaction on this tool type card.\n *\n * @method registerEventListeners\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var registerEventListeners = function(element) {\n var deleteButton = getDeleteButton(element);\n deleteButton.click(function(e) {\n e.preventDefault();\n deleteType(element);\n });\n deleteButton.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n e.preventDefault();\n deleteButton.click();\n }\n }\n });\n\n var activateButton = getActivateButton(element);\n activateButton.click(function(e) {\n e.preventDefault();\n activateToolType(element);\n });\n activateButton.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n e.preventDefault();\n activateButton.click();\n }\n }\n });\n };\n\n return /** @alias module:mod_lti/tool_card_controller */ {\n\n /**\n * Initialise this module.\n *\n * @param {JQuery} element jQuery object representing the tool card.\n */\n init: function(element) {\n registerEventListeners(element);\n }\n };\n});\n"],"names":["define","$","ajax","notification","templates","toolProxy","ltiEvents","KEYS","str","SELECTORS","getTypeId","element","attr","clearAllAnnouncements","removeClass","stopLoading","deleteType","promise","Deferred","typeId","addClass","startLoading","resolve","get_strings","key","component","done","strs","confirm","delete","setTimeout","announceSuccess","remove","fail","exception","error","announceFailure","reject","registerEventListeners","deleteButton","find","getDeleteButton","click","e","preventDefault","keypress","metaKey","shiftKey","altKey","ctrlKey","keyCode","ENTER","SPACE","activateButton","getActivateButton","data","proxyid","document","trigger","START_EXTERNAL_REGISTRATION","activateToolType","init"],"mappings":";;;;;;;;;;;AA0BAA,4CAAO,CAAC,SAAU,YAAa,oBAAqB,iBAAkB,qBAAsB,iBAAkB,eACtG,aACA,SAASC,EAAGC,KAAMC,aAAcC,UAAWC,UAAWC,UAAWC,KAAMC,SAEvEC,wBACe,UADfA,0BAGiB,+BAsCjBC,UAAY,SAASC,gBACdA,QAAQC,KAAK,kBAUpBC,sBAAwB,SAASF,SACjCA,QAAQG,YAAY,mDAsBpBC,YAAc,SAASJ,SACvBA,QAAQG,YAAY,yBAwDpBE,WAAa,SAASL,aAClBM,QAAUhB,EAAEiB,WACZC,OAAST,UAAUC,gBAvER,SAASA,SACxBE,sBAAsBF,SACtBA,QAAQS,SAAS,wBAsEjBC,CAAaV,SAEE,KAAXQ,OACOlB,EAAEiB,WAAWI,WAGxBd,IAAIe,YAAY,CACR,CACIC,IAAK,SACLC,UAAW,WAEf,CACID,IAAK,sBACLC,UAAW,WAEf,CACID,IAAK,SACLC,UAAW,WAEf,CACID,IAAK,SACLC,UAAW,UAGlBC,MAAK,SAASC,MACPxB,aAAayB,QAAQD,KAAK,GAAIA,KAAK,GAAIA,KAAK,GAAIA,KAAK,IAAI,WACjDtB,UAAUwB,OAAOV,QACZO,MAAK,WACEX,YAAYJ,SA3E1B,SAASA,aACvBM,QAAUhB,EAAEiB,kBAEhBL,sBAAsBF,SACtBA,QAAQS,SAAS,wBACjBU,YAAW,WACPnB,QAAQG,YAAY,wBACpBG,QAAQK,YAxFW,KA2FhBL,QAkEyBc,CAAgBpB,SACXe,MAAK,WACEf,QAAQqB,SACRf,QAAQK,aAEfW,KAAK9B,aAAa+B,cAE9BD,MAAK,SAASE,QA7DrB,SAASxB,aACvBM,QAAUhB,EAAEiB,WAEhBL,sBAAsBF,SACtBA,QAAQS,SAAS,qBACjBU,YAAW,WACHnB,QAAQG,YAAY,qBACpBG,QAAQK,YA9GO,KAqKSc,CAAgBzB,SAChBM,QAAQoB,OAAOF,aAEhC,WACKpB,YAAYJ,SACZM,QAAQK,gBAGvBW,MAAK,SAASE,OACPpB,YAAYJ,SACZR,aAAa+B,UAAUC,OACvBlB,QAAQoB,OAAOF,UAGpBlB,UAwBPqB,uBAAyB,SAAS3B,aAC9B4B,aAlMc,SAAS5B,gBACpBA,QAAQ6B,KAAK/B,yBAiMDgC,CAAgB9B,SACnC4B,aAAaG,OAAM,SAASC,GACpBA,EAAEC,iBACF5B,WAAWL,YAEnB4B,aAAaM,UAAS,SAASF,GAClBA,EAAEG,SAAYH,EAAEI,UAAaJ,EAAEK,QAAWL,EAAEM,SACzCN,EAAEO,SAAW3C,KAAK4C,OAASR,EAAEO,SAAW3C,KAAK6C,QAC7CT,EAAEC,iBACFL,aAAaG,gBAKzBW,eApMgB,SAAS1C,gBACtBA,QAAQ6B,KAAK/B,2BAmMC6C,CAAkB3C,SACvC0C,eAAeX,OAAM,SAASC,GACtBA,EAAEC,iBA7BS,SAASjC,aACxB4C,KAAO,CAACC,QAAS9C,UAAUC,UAC/BV,EAAEwD,UAAUC,QAAQpD,UAAUqD,4BAA6BJ,MA4BnDK,CAAiBjD,YAEzB0C,eAAeR,UAAS,SAASF,GACpBA,EAAEG,SAAYH,EAAEI,UAAaJ,EAAEK,QAAWL,EAAEM,SACzCN,EAAEO,SAAW3C,KAAK4C,OAASR,EAAEO,SAAW3C,KAAK6C,QAC7CT,EAAEC,iBACFS,eAAeX,mBAMsB,CAOrDmB,KAAM,SAASlD,SACX2B,uBAAuB3B"}coursecategory.min.js000064400000003610152267106260010731 0ustar00/** * Select course categories for LTI tool. * * @module mod_lti/coursecategory * @copyright 2023 Jackson D'souza * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.3 */ define("mod_lti/coursecategory",[],(function(){function getParents(elem){const parents=[];for(;elem&&elem!==document;elem=elem.parentNode)elem.classList.contains("accordion-group")&&parents.push(elem);return parents}return document.addEventListener("click",(event=>{const checkedbox=event.target.closest(".lticoursecategories");if(checkedbox){const checkboxstatus=checkedbox.checked,categorycontainer=document.querySelector("#collapse"+checkedbox.value);if(categorycontainer){const categorycontainercheckbox=categorycontainer.querySelectorAll('input[type="checkbox"]');for(let i=0;i.\n\n/**\n * Provides an interface for external tools in the Moodle server.\n *\n * @module mod_lti/tool_types_and_proxies\n * @class tool_types_and_proxies\n * @copyright 2020 Andrew Madden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\nimport ajax from 'core/ajax';\n\n/**\n * Get a list of LTI tool types and tool proxies from Moodle for the given\n * search args.\n *\n * See also:\n * mod/lti/classes/external.php get_tool_types_and_proxies()\n *\n * @method query\n * @public\n * @param {Object} args Search parameters\n * @return {Promise} Promise that will be resolved when the ajax call returns.\n */\nexport const query = (args) => {\n const request = {\n methodname: 'mod_lti_get_tool_types_and_proxies',\n args: args || {}\n };\n\n return ajax.call([request])[0];\n};\n\n/**\n * Get a count of LTI tool types and tool proxies from Moodle for the given\n * search args.\n *\n * See also:\n * mod/lti/classes/external.php get_tool_types_and_proxies_count()\n *\n * @method count\n * @public\n * @param {Object} args Search parameters\n * @return {Promise} Promise that will be resolved when the ajax call returns.\n */\nexport const count = (args) => {\n const request = {\n methodname: 'mod_lti_get_tool_types_and_proxies_count',\n args: args || {}\n };\n\n return ajax.call([request])[0];\n};\n"],"names":["args","request","methodname","ajax","call"],"mappings":";;;;;;;;;qKAsCsBA,aACZC,QAAU,CACZC,WAAY,qCACZF,KAAMA,MAAQ,WAGXG,cAAKC,KAAK,CAACH,UAAU,mBAeVD,aACZC,QAAU,CACZC,WAAY,2CACZF,KAAMA,MAAQ,WAGXG,cAAKC,KAAK,CAACH,UAAU"}mod_form.min.js.map000064400000010125152267106260010250 0ustar00{"version":3,"file":"mod_form.min.js","sources":["../src/mod_form.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 * Event handlers for the mod_lti mod_form.\n *\n * @module mod_lti/mod_form\n * @copyright 2023 Jake Dallimore \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport ContentItem from 'mod_lti/contentitem';\n\n/**\n * Initialise module.\n *\n * @param {int} courseId the course id.\n * @param {string} toolUrl the external tool url.\n */\nconst init = (courseId, toolUrl) => {\n const contentItemButton = document.querySelector('[name=\"selectcontent\"]');\n\n if (!contentItemButton) {\n return;\n }\n\n contentItemButton.addEventListener('click', () => {\n const contentItemUrl = contentItemButton.getAttribute('data-contentitemurl');\n const contentItemId = document.querySelector('#hidden_typeid').value;\n if (contentItemId) {\n const title = document.querySelector('#id_name').value.trim();\n const text = document.querySelector('#id_introeditor').value.trim();\n const postData = {\n id: contentItemId,\n course: courseId,\n title: title,\n text: text\n };\n\n // The callback below is called after the content item has been returned and processed.\n ContentItem.init(contentItemUrl, toolUrl, postData, (returnData) => {\n if (!returnData.multiple) {\n // The state of the grade checkbox has already been set by processContentItemReturnData() but that\n // hasn't fired the click/change event required by formslib to show/hide the dependent grade fields.\n // Fire it now.\n const allowGrades = document.querySelector('#id_instructorchoiceacceptgrades');\n let allowGradesChangeEvent = new Event('change');\n allowGrades.dispatchEvent(allowGradesChangeEvent);\n\n // If the tool is set to accept grades, make sure \"Point\" is selected.\n if (allowGrades.checked) {\n const gradeType = document.querySelector('#id_grade_modgrade_type');\n gradeType.value = \"point\";\n let gradeTypeChangeEvent = new Event('change');\n gradeType.dispatchEvent(gradeTypeChangeEvent);\n }\n }\n });\n }\n });\n};\n\nexport default {\n init: init\n};\n"],"names":["init","courseId","toolUrl","contentItemButton","document","querySelector","addEventListener","contentItemUrl","getAttribute","contentItemId","value","title","trim","text","postData","id","course","returnData","multiple","allowGrades","allowGradesChangeEvent","Event","dispatchEvent","checked","gradeType","gradeTypeChangeEvent"],"mappings":"oQA4Ee,CACXA,KA5CS,CAACC,SAAUC,iBACdC,kBAAoBC,SAASC,cAAc,0BAE5CF,mBAILA,kBAAkBG,iBAAiB,SAAS,WAClCC,eAAiBJ,kBAAkBK,aAAa,uBAChDC,cAAgBL,SAASC,cAAc,kBAAkBK,SAC3DD,cAAe,OACTE,MAAQP,SAASC,cAAc,YAAYK,MAAME,OACjDC,KAAOT,SAASC,cAAc,mBAAmBK,MAAME,OACvDE,SAAW,CACbC,GAAIN,cACJO,OAAQf,SACRU,MAAOA,MACPE,KAAMA,2BAIEb,KAAKO,eAAgBL,QAASY,UAAWG,iBAC5CA,WAAWC,SAAU,OAIhBC,YAAcf,SAASC,cAAc,wCACvCe,uBAAyB,IAAIC,MAAM,aACvCF,YAAYG,cAAcF,wBAGtBD,YAAYI,QAAS,OACfC,UAAYpB,SAASC,cAAc,2BACzCmB,UAAUd,MAAQ,YACde,qBAAuB,IAAIJ,MAAM,UACrCG,UAAUF,cAAcG"}tool_card_controller.min.js000064400000021151152267106260012104 0ustar00/** * Controls all of the behaviour and interaction with a tool type card. These are * listed on the LTI tool type management page. * * See template: mod_lti/tool_card * * @module mod_lti/tool_card_controller * @copyright 2015 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_lti/tool_card_controller",["jquery","core/ajax","core/notification","core/templates","core/modal","mod_lti/tool_type","mod_lti/events","mod_lti/keys","core/str"],(function($,ajax,notification,templates,Modal,toolType,ltiEvents,KEYS,str){var SELECTORS_DELETE_BUTTON=".delete",SELECTORS_NAME_ELEMENT=".name",SELECTORS_DESCRIPTION_ELEMENT=".description",SELECTORS_CAPABILITIES_CONTAINER=".capabilities-container",SELECTORS_ACTIVATE_BUTTON=".tool-card-footer a.activate",getNameElement=function(element){return element.find(SELECTORS_NAME_ELEMENT)},getDescriptionElement=function(element){return element.find(SELECTORS_DESCRIPTION_ELEMENT)},getActivateButton=function(element){return element.find(SELECTORS_ACTIVATE_BUTTON)},getCapabilitiesContainer=function(element){return element.find(SELECTORS_CAPABILITIES_CONTAINER)},hasCapabilitiesContainer=function(element){return!!getCapabilitiesContainer(element).length},getTypeId=function(element){return element.attr("data-type-id")},clearAllAnnouncements=function(element){element.removeClass("announcement loading success fail capabilities")},startLoading=function(element){clearAllAnnouncements(element),element.addClass("announcement loading")},stopLoading=function(element){element.removeClass("announcement loading")},announceSuccess=function(element){var promise=$.Deferred();return clearAllAnnouncements(element),element.addClass("announcement success"),setTimeout((function(){element.removeClass("announcement success"),promise.resolve()}),2e3),promise},announceFailure=function(element){var promise=$.Deferred();return clearAllAnnouncements(element),element.addClass("announcement fail"),setTimeout((function(){element.removeClass("announcement fail"),promise.resolve()}),2e3),promise},setValueSnapshot=function(element,value){element.attr("data-val-snapshot",value)},getValueSnapshot=function(element){return element.attr("data-val-snapshot")},setStatusActive=function(element){var id=getTypeId(element);if(""===id)return $.Deferred().resolve();startLoading(element);var promise=toolType.update({id:id,state:toolType.constants.state.configured});return promise.then((function(toolTypeData){return stopLoading(element),announceSuccess(element),toolTypeData})).then((function(toolTypeData){return templates.render("mod_lti/tool_card",toolTypeData)})).then((function(html,js){templates.replaceNode(element,html,js)})).catch((function(){stopLoading(element),announceFailure(element)})),promise},registerEventListeners=function(element){var deleteButton=function(element){return element.find(SELECTORS_DELETE_BUTTON)}(element);deleteButton.click((function(e){e.preventDefault(),function(element){var promise=$.Deferred(),typeId=getTypeId(element);startLoading(element),""===typeId?$.Deferred().resolve():str.get_strings([{key:"delete",component:"mod_lti"},{key:"delete_confirmation",component:"mod_lti"},{key:"delete",component:"mod_lti"},{key:"cancel",component:"core"}]).done((function(strs){notification.confirm(strs[0],strs[1],strs[2],strs[3],(function(){toolType.delete(typeId).done((function(){stopLoading(element),announceSuccess(element).done((function(){element.remove()})).fail(notification.exception).always((function(){promise.resolve()}))})).fail((function(error){announceFailure(element),promise.reject(error)}))}),(function(){stopLoading(element),promise.resolve()}))})).fail((function(error){stopLoading(element),notification.exception(error),promise.reject(error)}))}(element)})),deleteButton.keypress((function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||e.keyCode!=KEYS.ENTER&&e.keyCode!=KEYS.SPACE||(e.preventDefault(),deleteButton.click())}));var descriptionElement=getDescriptionElement(element);descriptionElement.focus((function(e){e.preventDefault(),function(element){var descriptionElement=getDescriptionElement(element);if(!descriptionElement.hasClass("loading")){var description=descriptionElement.text().trim();setValueSnapshot(descriptionElement,description)}}(element)})),descriptionElement.blur((function(e){e.preventDefault(),function(element){var typeId=getTypeId(element);if(""===typeId)return $.Deferred().resolve();var descriptionElement=getDescriptionElement(element);if(descriptionElement.hasClass("loading"))return $.Deferred().resolve();var description=descriptionElement.text().trim();if(getValueSnapshot(descriptionElement)==description)return $.Deferred().resolve();descriptionElement.addClass("loading");var promise=toolType.update({id:typeId,description:description});promise.done((function(type){descriptionElement.removeClass("loading"),descriptionElement.text(type.description)})).fail(notification.exception),promise.fail((function(){descriptionElement.removeClass("loading")}))}(element)})),descriptionElement.keypress((function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||e.keyCode==KEYS.ENTER&&(e.preventDefault(),descriptionElement.blur())}));var nameElement=getNameElement(element);if(nameElement.focus((function(e){e.preventDefault(),function(element){var nameElement=getNameElement(element);if(!nameElement.hasClass("loading")){var name=nameElement.text().trim();setValueSnapshot(nameElement,name)}}(element)})),nameElement.blur((function(e){e.preventDefault(),function(element){var typeId=getTypeId(element);if(""===typeId)return $.Deferred().resolve();var nameElement=getNameElement(element);if(nameElement.hasClass("loading"))return $.Deferred().resolve();var name=nameElement.text().trim();if(getValueSnapshot(nameElement)==name)return $.Deferred().resolve();nameElement.addClass("loading");var promise=toolType.update({id:typeId,name:name});promise.done((function(type){nameElement.removeClass("loading"),nameElement.text(type.name)})),promise.fail((function(){nameElement.removeClass("loading")}))}(element)})),nameElement.keypress((function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||e.keyCode==KEYS.ENTER&&(e.preventDefault(),nameElement.blur())})),function(element){return!!getActivateButton(element).length}(element)){var activateButton=getActivateButton(element);activateButton.click((function(e){e.preventDefault(),function(element){hasCapabilitiesContainer(element)?function(element){element.addClass("announcement capabilities")}(element):setStatusActive(element)}(element)})),activateButton.keypress((function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||e.keyCode!=KEYS.ENTER&&e.keyCode!=KEYS.SPACE||(e.preventDefault(),activateButton.click())}))}if(hasCapabilitiesContainer(element)){var capabilitiesContainer=getCapabilitiesContainer(element);capabilitiesContainer.on(ltiEvents.CAPABILITIES_AGREE,(function(){setStatusActive(element)})),capabilitiesContainer.on(ltiEvents.CAPABILITIES_DECLINE,(function(){!function(element){element.removeClass("announcement capabilities")}(element)}))}};return{init:function(element){registerEventListeners(element),function(element){const configurationLink=element.find("#"+element.data("uniqid")+"-"+element.data("deploymentid"));if(!configurationLink.length)return;configurationLink.get(0).addEventListener("click",(e=>{e.preventDefault();var context={uniqid:element.data("uniqid"),platformid:element.data("platformid"),clientid:element.data("clientid"),deploymentid:element.data("deploymentid"),urls:{publickeyset:element.data("publickeyseturl"),accesstoken:element.data("accesstokenurl"),authrequest:element.data("authrequesturl")}},bodyPromise=templates.render("mod_lti/tool_config_modal_body",context);context={mailto:"mailto:?subject="+encodeURIComponent(element.data("mailtosubject"))+"&body="+encodeURIComponent(element.data("platformidstr"))+":%20"+encodeURIComponent(element.data("platformid"))+"%0D%0A"+encodeURIComponent(element.data("clientidstr"))+":%20"+encodeURIComponent(element.data("clientid"))+"%0D%0A"+encodeURIComponent(element.data("deploymentidstr"))+":%20"+encodeURIComponent(element.data("deploymentid"))+"%0D%0A"+encodeURIComponent(element.data("publickeyseturlstr"))+":%20"+encodeURIComponent(element.data("publickeyseturl"))+"%0D%0A"+encodeURIComponent(element.data("accesstokenurlstr"))+":%20"+encodeURIComponent(element.data("accesstokenurl"))+"%0D%0A"+encodeURIComponent(element.data("authrequesturlstr"))+":%20"+encodeURIComponent(element.data("authrequesturl"))+"%0D%0A"};var footerPromise=templates.render("mod_lti/tool_config_modal_footer",context);Modal.create({large:!0,title:element.data("modaltitle"),body:bodyPromise,footer:footerPromise,show:!0})}))}(element)}}})); //# sourceMappingURL=tool_card_controller.min.js.mapcourse_tools_list.min.js.map000064400000013566152267106260012235 0ustar00{"version":3,"file":"course_tools_list.min.js","sources":["../src/course_tools_list.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 * Course LTI External tools list management.\n *\n * @module mod_lti/course_tools_list\n * @copyright 2023 Jake Dallimore \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Ajax from 'core/ajax';\nimport {add as addToast} from 'core/toast';\nimport {getString, getStrings} from 'core/str';\nimport {refreshTableContent} from 'core_table/dynamic';\nimport * as Selectors from 'core_table/local/dynamic/selectors';\nimport {toggleShowInActivityChooser} from \"./repository\";\n\n/**\n * Initialise module.\n */\nexport const init = () => {\n document.addEventListener('click', event => {\n\n const courseToolDelete = event.target.closest('[data-action=\"course-tool-delete\"]');\n if (courseToolDelete) {\n event.preventDefault();\n\n // A different message is used in the modal if the tool has usages within the course.\n const usage = courseToolDelete.dataset.courseToolUsage;\n const deleteBodyStringId = usage > 0 ? 'deletecoursetoolwithusageconfirm' : 'deletecoursetoolconfirm';\n const requiredStrings = [\n {key: 'deletecoursetool', component: 'mod_lti', param: courseToolDelete.dataset.courseToolName},\n {key: deleteBodyStringId, component: 'mod_lti', param: courseToolDelete.dataset.courseToolName},\n {key: 'delete', component: 'core', param: courseToolDelete.dataset.courseToolName},\n {key: 'coursetooldeleted', component: 'mod_lti', param: courseToolDelete.dataset.courseToolName}\n ];\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = courseToolDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n\n getStrings(requiredStrings).then(([modalTitle, modalBody, deleteLabel]) => {\n return Notification.deleteCancelPromise(\n modalTitle,\n modalBody,\n deleteLabel,\n {triggerElement});\n }).then(() => {\n const pendingPromise = new Pending('mod_lti/course_tools:delete');\n\n const request = {\n methodname: 'mod_lti_delete_course_tool_type',\n args: {tooltypeid: courseToolDelete.dataset.courseToolId}\n };\n return Ajax.call([request])[0]\n .then(addToast(getString('coursetooldeleted', 'mod_lti', courseToolDelete.dataset.courseToolName)))\n .then(() => {\n const tableRoot = triggerElement.closest(Selectors.main.region);\n return refreshTableContent(tableRoot);\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n\n const courseShowInActivityChooser = event.target.closest('[data-action=\"showinactivitychooser-toggle\"]');\n if (courseShowInActivityChooser) {\n const showInActivityChooserStateToggle = courseShowInActivityChooser.dataset.state === \"0\" ? 1 : 0;\n return toggleShowInActivityChooser(\n courseShowInActivityChooser.dataset.id,\n courseShowInActivityChooser.dataset.courseid,\n showInActivityChooserStateToggle,\n );\n }\n });\n};\n"],"names":["document","addEventListener","event","courseToolDelete","target","closest","preventDefault","deleteBodyStringId","dataset","courseToolUsage","requiredStrings","key","component","param","courseToolName","triggerElement","querySelector","then","_ref","modalTitle","modalBody","deleteLabel","Notification","deleteCancelPromise","pendingPromise","Pending","request","methodname","args","tooltypeid","courseToolId","Ajax","call","tableRoot","Selectors","main","region","resolve","catch","exception","courseShowInActivityChooser","showInActivityChooserStateToggle","state","id","courseid"],"mappings":"+/CAqCoB,KAChBA,SAASC,iBAAiB,SAASC,cAEzBC,iBAAmBD,MAAME,OAAOC,QAAQ,yCAC1CF,iBAAkB,CAClBD,MAAMI,uBAIAC,mBADQJ,iBAAiBK,QAAQC,gBACJ,EAAI,mCAAqC,0BACtEC,gBAAkB,CACpB,CAACC,IAAK,mBAAoBC,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,gBAChF,CAACH,IAAKJ,mBAAoBK,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,gBAChF,CAACH,IAAK,SAAUC,UAAW,OAAQC,MAAOV,iBAAiBK,QAAQM,gBACnE,CAACH,IAAK,oBAAqBC,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,iBAG/EC,eAAiBZ,iBAAiBE,QAAQ,aAAaW,cAAc,wCAEhEN,iBAAiBO,MAAKC,WAAEC,WAAYC,UAAWC,yBAC/CC,sBAAaC,oBAChBJ,WACAC,UACAC,YACA,CAACN,eAAAA,oBACNE,MAAK,WACEO,eAAiB,IAAIC,iBAAQ,+BAE7BC,QAAU,CACZC,WAAY,kCACZC,KAAM,CAACC,WAAY1B,iBAAiBK,QAAQsB,sBAEzCC,cAAKC,KAAK,CAACN,UAAU,GACvBT,MAAK,eAAS,kBAAU,oBAAqB,UAAWd,iBAAiBK,QAAQM,kBACjFG,MAAK,WACIgB,UAAYlB,eAAeV,QAAQ6B,UAAUC,KAAKC,eACjD,gCAAoBH,cAE9BhB,KAAKO,eAAea,SACpBC,MAAMhB,sBAAaiB,cACzBD,OAAM,eAKPE,4BAA8BtC,MAAME,OAAOC,QAAQ,mDACrDmC,4BAA6B,OACvBC,iCAAiF,MAA9CD,4BAA4BhC,QAAQkC,MAAgB,EAAI,SAC1F,2CACHF,4BAA4BhC,QAAQmC,GACpCH,4BAA4BhC,QAAQoC,SACpCH"}tool_card_controller.min.js.map000064400000074666152267106260012703 0ustar00{"version":3,"file":"tool_card_controller.min.js","sources":["../src/tool_card_controller.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 * Controls all of the behaviour and interaction with a tool type card. These are\n * listed on the LTI tool type management page.\n *\n * See template: mod_lti/tool_card\n *\n * @module mod_lti/tool_card_controller\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\n define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'core/modal',\n 'mod_lti/tool_type', 'mod_lti/events', 'mod_lti/keys',\n 'core/str'],\n function($, ajax, notification, templates, Modal, toolType, ltiEvents, KEYS, str) {\n\n var SELECTORS = {\n DELETE_BUTTON: '.delete',\n NAME_ELEMENT: '.name',\n DESCRIPTION_ELEMENT: '.description',\n CAPABILITIES_CONTAINER: '.capabilities-container',\n ACTIVATE_BUTTON: '.tool-card-footer a.activate',\n };\n\n // Timeout in seconds.\n var ANNOUNCEMENT_TIMEOUT = 2000;\n\n /**\n * Return the delete button element.\n *\n * @method getDeleteButton\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {JQuery} jQuery object\n */\n var getDeleteButton = function(element) {\n return element.find(SELECTORS.DELETE_BUTTON);\n };\n\n /**\n * Return the element representing the tool type name.\n *\n * @method getNameElement\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {JQuery} jQuery object\n */\n var getNameElement = function(element) {\n return element.find(SELECTORS.NAME_ELEMENT);\n };\n\n /**\n * Return the element representing the tool type description.\n *\n * @method getDescriptionElement\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {JQuery} jQuery object\n */\n var getDescriptionElement = function(element) {\n return element.find(SELECTORS.DESCRIPTION_ELEMENT);\n };\n\n /**\n * Return the activate button for the type.\n *\n * @method getActivateButton\n * @private\n * @param {Object} element jQuery object representing the tool card.\n * @return {Object} jQuery object\n */\n var getActivateButton = function(element) {\n return element.find(SELECTORS.ACTIVATE_BUTTON);\n };\n\n /**\n * Checks if the type card has an activate button.\n *\n * @method hasActivateButton\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Boolean} true if has active buton\n */\n var hasActivateButton = function(element) {\n return getActivateButton(element).length ? true : false;\n };\n\n /**\n * Return the element that contains the capabilities approval for\n * the user.\n *\n * @method getCapabilitiesContainer\n * @private\n * @param {Object} element jQuery object representing the tool card.\n * @return {Object} The element\n */\n var getCapabilitiesContainer = function(element) {\n return element.find(SELECTORS.CAPABILITIES_CONTAINER);\n };\n\n /**\n * Checks if the tool type has capabilities that need approval. If it\n * does then the container will be present.\n *\n * @method hasCapabilitiesContainer\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Boolean} true if has capbilities.\n */\n var hasCapabilitiesContainer = function(element) {\n return getCapabilitiesContainer(element).length ? true : false;\n };\n\n /**\n * Get the type id.\n *\n * @method getTypeId\n * @private\n * @param {Object} element jQuery object representing the tool card.\n * @return {String} Type ID\n */\n var getTypeId = function(element) {\n return element.attr('data-type-id');\n };\n\n /**\n * Stop any announcement currently visible on the card.\n *\n * @method clearAllAnnouncements\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var clearAllAnnouncements = function(element) {\n element.removeClass('announcement loading success fail capabilities');\n };\n\n /**\n * Show the loading announcement.\n *\n * @method startLoading\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var startLoading = function(element) {\n clearAllAnnouncements(element);\n element.addClass('announcement loading');\n };\n\n /**\n * Hide the loading announcement.\n *\n * @method stopLoading\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var stopLoading = function(element) {\n element.removeClass('announcement loading');\n };\n\n /**\n * Show the success announcement. The announcement is only\n * visible for 2 seconds.\n *\n * @method announceSuccess\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var announceSuccess = function(element) {\n var promise = $.Deferred();\n\n clearAllAnnouncements(element);\n element.addClass('announcement success');\n setTimeout(function() {\n element.removeClass('announcement success');\n promise.resolve();\n }, ANNOUNCEMENT_TIMEOUT);\n\n return promise;\n };\n\n /**\n * Show the failure announcement. The announcement is only\n * visible for 2 seconds.\n *\n * @method announceFailure\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var announceFailure = function(element) {\n var promise = $.Deferred();\n\n clearAllAnnouncements(element);\n element.addClass('announcement fail');\n setTimeout(function() {\n element.removeClass('announcement fail');\n promise.resolve();\n }, ANNOUNCEMENT_TIMEOUT);\n\n return promise;\n };\n\n /**\n * Delete the tool type from the Moodle server. Triggers a success\n * or failure announcement depending on the result.\n *\n * @method deleteType\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var deleteType = function(element) {\n var promise = $.Deferred();\n var typeId = getTypeId(element);\n startLoading(element);\n\n if (typeId === \"\") {\n return $.Deferred().resolve();\n }\n\n str.get_strings([\n {\n key: 'delete',\n component: 'mod_lti'\n },\n {\n key: 'delete_confirmation',\n component: 'mod_lti'\n },\n {\n key: 'delete',\n component: 'mod_lti'\n },\n {\n key: 'cancel',\n component: 'core'\n },\n ])\n .done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n toolType.delete(typeId)\n .done(function() {\n stopLoading(element);\n announceSuccess(element)\n .done(function() {\n element.remove();\n })\n .fail(notification.exception)\n .always(function() {\n // Always resolve because even if the announcement fails the type was deleted.\n promise.resolve();\n });\n })\n .fail(function(error) {\n announceFailure(element);\n promise.reject(error);\n });\n }, function() {\n stopLoading(element);\n promise.resolve();\n });\n })\n .fail(function(error) {\n stopLoading(element);\n notification.exception(error);\n promise.reject(error);\n });\n\n return promise;\n };\n\n /**\n * Save a given value in a data attribute on the element.\n *\n * @method setValueSnapshot\n * @private\n * @param {JQuery} element jQuery object representing the element.\n * @param {String} value to be saved.\n */\n var setValueSnapshot = function(element, value) {\n element.attr('data-val-snapshot', value);\n };\n\n /**\n * Return the saved value from the element.\n *\n * @method getValueSnapshot\n * @private\n * @param {JQuery} element jQuery object representing the element.\n * @return {String} the saved value.\n */\n var getValueSnapshot = function(element) {\n return element.attr('data-val-snapshot');\n };\n\n /**\n * Save the current value of the tool description.\n *\n * @method snapshotDescription\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var snapshotDescription = function(element) {\n var descriptionElement = getDescriptionElement(element);\n\n if (descriptionElement.hasClass('loading')) {\n return;\n }\n\n var description = descriptionElement.text().trim();\n setValueSnapshot(descriptionElement, description);\n };\n\n /**\n * Send a request to update the description value for this tool\n * in the Moodle server.\n *\n * @method updateDescription\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var updateDescription = function(element) {\n var typeId = getTypeId(element);\n\n // Return early if we don't have an id because it's\n // required to save the changes.\n if (typeId === \"\") {\n return $.Deferred().resolve();\n }\n\n var descriptionElement = getDescriptionElement(element);\n\n // Return early if we're already saving a value.\n if (descriptionElement.hasClass('loading')) {\n return $.Deferred().resolve();\n }\n\n var description = descriptionElement.text().trim();\n var snapshotVal = getValueSnapshot(descriptionElement);\n\n // If the value hasn't change then don't bother sending the\n // update request.\n if (snapshotVal == description) {\n return $.Deferred().resolve();\n }\n\n descriptionElement.addClass('loading');\n\n var promise = toolType.update({id: typeId, description: description});\n\n promise.done(function(type) {\n descriptionElement.removeClass('loading');\n // Make sure the text is updated with the description from the\n // server, just in case the update didn't work.\n descriptionElement.text(type.description);\n }).fail(notification.exception);\n\n // Probably need to handle failures better so that we can revert\n // the value in the input for the user.\n promise.fail(function() {\n descriptionElement.removeClass('loading');\n });\n\n return promise;\n };\n\n /**\n * Save the current value of the tool name.\n *\n * @method snapshotName\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var snapshotName = function(element) {\n var nameElement = getNameElement(element);\n\n if (nameElement.hasClass('loading')) {\n return;\n }\n\n var name = nameElement.text().trim();\n setValueSnapshot(nameElement, name);\n };\n\n /**\n * Send a request to update the name value for this tool\n * in the Moodle server.\n *\n * @method updateName\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var updateName = function(element) {\n var typeId = getTypeId(element);\n\n // Return if we don't have an id.\n if (typeId === \"\") {\n return $.Deferred().resolve();\n }\n\n var nameElement = getNameElement(element);\n\n // Return if we're already saving.\n if (nameElement.hasClass('loading')) {\n return $.Deferred().resolve();\n }\n\n var name = nameElement.text().trim();\n var snapshotVal = getValueSnapshot(nameElement);\n\n // If the value hasn't change then don't bother sending the\n // update request.\n if (snapshotVal == name) {\n return $.Deferred().resolve();\n }\n\n nameElement.addClass('loading');\n var promise = toolType.update({id: typeId, name: name});\n\n promise.done(function(type) {\n nameElement.removeClass('loading');\n // Make sure the text is updated with the name from the\n // server, just in case the update didn't work.\n nameElement.text(type.name);\n });\n\n // Probably need to handle failures better so that we can revert\n // the value in the input for the user.\n promise.fail(function() {\n nameElement.removeClass('loading');\n });\n\n return promise;\n };\n\n /**\n * Send a request to update the state for this tool to be configured (active)\n * in the Moodle server. A success or failure announcement is triggered depending\n * on the result.\n *\n * @method setStatusActive\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var setStatusActive = function(element) {\n var id = getTypeId(element);\n\n // Return if we don't have an id.\n if (id === \"\") {\n return $.Deferred().resolve();\n }\n\n startLoading(element);\n\n var promise = toolType.update({\n id: id,\n state: toolType.constants.state.configured\n });\n\n promise.then(function(toolTypeData) {\n stopLoading(element);\n announceSuccess(element);\n return toolTypeData;\n }).then(function(toolTypeData) {\n return templates.render('mod_lti/tool_card', toolTypeData);\n }).then(function(html, js) {\n templates.replaceNode(element, html, js);\n return;\n }).catch(function() {\n stopLoading(element);\n announceFailure(element);\n });\n\n return promise;\n };\n\n /**\n * Show the capabilities approval screen to show which groups of data this\n * type requires access to in Moodle (if any).\n *\n * @method displayCapabilitiesApproval\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var displayCapabilitiesApproval = function(element) {\n element.addClass('announcement capabilities');\n };\n\n /**\n * Hide the capabilities approval screen.\n *\n * @method hideCapabilitiesApproval\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var hideCapabilitiesApproval = function(element) {\n element.removeClass('announcement capabilities');\n };\n\n /**\n * The user wishes to activate this tool so show them the capabilities that\n * they need to agree to or if there are none then set the tool type's state\n * to active.\n *\n * @method activateToolType\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var activateToolType = function(element) {\n if (hasCapabilitiesContainer(element)) {\n displayCapabilitiesApproval(element);\n } else {\n setStatusActive(element);\n }\n };\n\n /**\n * Sets up the listeners for user interaction on this tool type card.\n *\n * @method registerEventListeners\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var registerEventListeners = function(element) {\n var deleteButton = getDeleteButton(element);\n deleteButton.click(function(e) {\n e.preventDefault();\n deleteType(element);\n });\n deleteButton.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n e.preventDefault();\n deleteButton.click();\n }\n }\n });\n\n var descriptionElement = getDescriptionElement(element);\n descriptionElement.focus(function(e) {\n e.preventDefault();\n // Save a copy of the current value for the description so that\n // we can check if the user has changed it before sending a request to\n // the server.\n snapshotDescription(element);\n });\n descriptionElement.blur(function(e) {\n e.preventDefault();\n updateDescription(element);\n });\n descriptionElement.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER) {\n e.preventDefault();\n descriptionElement.blur();\n }\n }\n });\n\n var nameElement = getNameElement(element);\n nameElement.focus(function(e) {\n e.preventDefault();\n // Save a copy of the current value for the name so that\n // we can check if the user has changed it before sending a request to\n // the server.\n snapshotName(element);\n });\n nameElement.blur(function(e) {\n e.preventDefault();\n updateName(element);\n });\n nameElement.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER) {\n e.preventDefault();\n nameElement.blur();\n }\n }\n });\n\n // Only pending tool type cards have an activate button.\n if (hasActivateButton(element)) {\n var activateButton = getActivateButton(element);\n activateButton.click(function(e) {\n e.preventDefault();\n activateToolType(element);\n });\n activateButton.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n e.preventDefault();\n activateButton.click();\n }\n }\n });\n }\n\n if (hasCapabilitiesContainer(element)) {\n var capabilitiesContainer = getCapabilitiesContainer(element);\n\n capabilitiesContainer.on(ltiEvents.CAPABILITIES_AGREE, function() {\n setStatusActive(element);\n });\n\n capabilitiesContainer.on(ltiEvents.CAPABILITIES_DECLINE, function() {\n hideCapabilitiesApproval(element);\n });\n }\n };\n\n /**\n * Sets up the templates for the tool configuration modal on this tool type card.\n *\n * @method registerModal\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var registerModal = function(element) {\n const configurationLink = element.find('#' + element.data('uniqid') + '-' + element.data('deploymentid'));\n if (!configurationLink.length) {\n return;\n }\n const trigger = configurationLink.get(0);\n trigger.addEventListener('click', (e) => {\n e.preventDefault();\n var context = {\n 'uniqid': element.data('uniqid'),\n 'platformid': element.data('platformid'),\n 'clientid': element.data('clientid'),\n 'deploymentid': element.data('deploymentid'),\n 'urls': {\n 'publickeyset': element.data('publickeyseturl'),\n 'accesstoken': element.data('accesstokenurl'),\n 'authrequest': element.data('authrequesturl')\n }\n };\n var bodyPromise = templates.render('mod_lti/tool_config_modal_body', context);\n var mailTo = 'mailto:?subject=' + encodeURIComponent(element.data('mailtosubject')) +\n '&body=' + encodeURIComponent(element.data('platformidstr')) + ':%20' +\n encodeURIComponent(element.data('platformid')) + '%0D%0A' +\n encodeURIComponent(element.data('clientidstr')) + ':%20' +\n encodeURIComponent(element.data('clientid')) + '%0D%0A' +\n encodeURIComponent(element.data('deploymentidstr')) + ':%20' +\n encodeURIComponent(element.data('deploymentid')) + '%0D%0A' +\n encodeURIComponent(element.data('publickeyseturlstr')) + ':%20' +\n encodeURIComponent(element.data('publickeyseturl')) + '%0D%0A' +\n encodeURIComponent(element.data('accesstokenurlstr')) + ':%20' +\n encodeURIComponent(element.data('accesstokenurl')) + '%0D%0A' +\n encodeURIComponent(element.data('authrequesturlstr')) + ':%20' +\n encodeURIComponent(element.data('authrequesturl')) + '%0D%0A';\n context = {\n 'mailto': mailTo\n };\n var footerPromise = templates.render('mod_lti/tool_config_modal_footer', context);\n Modal.create({\n large: true,\n title: element.data('modaltitle'),\n body: bodyPromise,\n footer: footerPromise,\n show: true\n });\n });\n };\n\n return /** @alias module:mod_lti/tool_card_controller */ {\n\n /**\n * Initialise this module.\n *\n * @param {JQuery} element jQuery object representing the tool card.\n */\n init: function(element) {\n registerEventListeners(element);\n registerModal(element);\n }\n };\n});\n"],"names":["define","$","ajax","notification","templates","Modal","toolType","ltiEvents","KEYS","str","SELECTORS","getNameElement","element","find","getDescriptionElement","getActivateButton","getCapabilitiesContainer","hasCapabilitiesContainer","length","getTypeId","attr","clearAllAnnouncements","removeClass","startLoading","addClass","stopLoading","announceSuccess","promise","Deferred","setTimeout","resolve","announceFailure","setValueSnapshot","value","getValueSnapshot","setStatusActive","id","update","state","constants","configured","then","toolTypeData","render","html","js","replaceNode","catch","registerEventListeners","deleteButton","getDeleteButton","click","e","preventDefault","typeId","get_strings","key","component","done","strs","confirm","delete","remove","fail","exception","always","error","reject","deleteType","keypress","metaKey","shiftKey","altKey","ctrlKey","keyCode","ENTER","SPACE","descriptionElement","focus","hasClass","description","text","trim","snapshotDescription","blur","type","updateDescription","nameElement","name","snapshotName","updateName","hasActivateButton","activateButton","displayCapabilitiesApproval","activateToolType","capabilitiesContainer","on","CAPABILITIES_AGREE","CAPABILITIES_DECLINE","hideCapabilitiesApproval","init","configurationLink","data","get","addEventListener","context","bodyPromise","encodeURIComponent","footerPromise","create","large","title","body","footer","show","registerModal"],"mappings":";;;;;;;;;;;AA0BCA,sCAAO,CAAC,SAAU,YAAa,oBAAqB,iBAAkB,aAC/D,oBAAqB,iBAAkB,eACvC,aACA,SAASC,EAAGC,KAAMC,aAAcC,UAAWC,MAAOC,SAAUC,UAAWC,KAAMC,SAE7EC,wBACe,UADfA,uBAEc,QAFdA,8BAGqB,eAHrBA,iCAIwB,0BAJxBA,0BAKiB,+BA0BjBC,eAAiB,SAASC,gBACnBA,QAAQC,KAAKH,yBAWpBI,sBAAwB,SAASF,gBAC1BA,QAAQC,KAAKH,gCAWpBK,kBAAoB,SAASH,gBACtBA,QAAQC,KAAKH,4BAwBpBM,yBAA2B,SAASJ,gBAC7BA,QAAQC,KAAKH,mCAYpBO,yBAA2B,SAASL,iBAC7BI,yBAAyBJ,SAASM,QAWzCC,UAAY,SAASP,gBACdA,QAAQQ,KAAK,iBAUpBC,sBAAwB,SAAST,SACjCA,QAAQU,YAAY,mDAUpBC,aAAe,SAASX,SACxBS,sBAAsBT,SACtBA,QAAQY,SAAS,yBAUjBC,YAAc,SAASb,SACvBA,QAAQU,YAAY,yBAYpBI,gBAAkB,SAASd,aACvBe,QAAU1B,EAAE2B,kBAEhBP,sBAAsBT,SACtBA,QAAQY,SAAS,wBACjBK,YAAW,WACPjB,QAAQU,YAAY,wBACpBK,QAAQG,YAtJW,KAyJhBH,SAYPI,gBAAkB,SAASnB,aACvBe,QAAU1B,EAAE2B,kBAEhBP,sBAAsBT,SACtBA,QAAQY,SAAS,qBACjBK,YAAW,WACPjB,QAAQU,YAAY,qBACpBK,QAAQG,YA5KW,KA+KhBH,SAgFPK,iBAAmB,SAASpB,QAASqB,OACrCrB,QAAQQ,KAAK,oBAAqBa,QAWlCC,iBAAmB,SAAStB,gBACrBA,QAAQQ,KAAK,sBA2JpBe,gBAAkB,SAASvB,aACvBwB,GAAKjB,UAAUP,YAGR,KAAPwB,UACOnC,EAAE2B,WAAWE,UAGxBP,aAAaX,aAETe,QAAUrB,SAAS+B,OAAO,CAC1BD,GAAIA,GACJE,MAAOhC,SAASiC,UAAUD,MAAME,oBAGpCb,QAAQc,MAAK,SAASC,qBAClBjB,YAAYb,SACZc,gBAAgBd,SACT8B,gBACRD,MAAK,SAASC,qBACNtC,UAAUuC,OAAO,oBAAqBD,iBAC9CD,MAAK,SAASG,KAAMC,IACnBzC,UAAU0C,YAAYlC,QAASgC,KAAMC,OAEtCE,OAAM,WACLtB,YAAYb,SACZmB,gBAAgBnB,YAGbe,SAkDPqB,uBAAyB,SAASpC,aAC9BqC,aA7ec,SAASrC,gBACpBA,QAAQC,KAAKH,yBA4eDwC,CAAgBtC,SACnCqC,aAAaE,OAAM,SAASC,GACxBA,EAAEC,iBA9TO,SAASzC,aAClBe,QAAU1B,EAAE2B,WACZ0B,OAASnC,UAAUP,SACvBW,aAAaX,SAEE,KAAX0C,OACOrD,EAAE2B,WAAWE,UAGxBrB,IAAI8C,YAAY,CACR,CACIC,IAAK,SACLC,UAAW,WAEf,CACID,IAAK,sBACLC,UAAW,WAEf,CACID,IAAK,SACLC,UAAW,WAEf,CACID,IAAK,SACLC,UAAW,UAGlBC,MAAK,SAASC,MACPxD,aAAayD,QAAQD,KAAK,GAAIA,KAAK,GAAIA,KAAK,GAAIA,KAAK,IAAI,WACjDrD,SAASuD,OAAOP,QACXI,MAAK,WACEjC,YAAYb,SACZc,gBAAgBd,SACX8C,MAAK,WACE9C,QAAQkD,YAEfC,KAAK5D,aAAa6D,WAClBC,QAAO,WAEAtC,QAAQG,gBAG3BiC,MAAK,SAASG,OACPnC,gBAAgBnB,SAChBe,QAAQwC,OAAOD,aAE5B,WACKzC,YAAYb,SACZe,QAAQG,gBAG3BiC,MAAK,SAASG,OACPzC,YAAYb,SACZT,aAAa6D,UAAUE,OACvBvC,QAAQwC,OAAOD,UAyQvBE,CAAWxD,YAEfqC,aAAaoB,UAAS,SAASjB,GACtBA,EAAEkB,SAAYlB,EAAEmB,UAAanB,EAAEoB,QAAWpB,EAAEqB,SACzCrB,EAAEsB,SAAWlE,KAAKmE,OAASvB,EAAEsB,SAAWlE,KAAKoE,QAC7CxB,EAAEC,iBACFJ,aAAaE,gBAKrB0B,mBAAqB/D,sBAAsBF,SAC/CiE,mBAAmBC,OAAM,SAAS1B,GAC9BA,EAAEC,iBAjPgB,SAASzC,aAC3BiE,mBAAqB/D,sBAAsBF,aAE3CiE,mBAAmBE,SAAS,gBAI5BC,YAAcH,mBAAmBI,OAAOC,OAC5ClD,iBAAiB6C,mBAAoBG,cA6OjCG,CAAoBvE,YAExBiE,mBAAmBO,MAAK,SAAShC,GAC7BA,EAAEC,iBApOc,SAASzC,aACzB0C,OAASnC,UAAUP,YAIR,KAAX0C,cACOrD,EAAE2B,WAAWE,cAGpB+C,mBAAqB/D,sBAAsBF,YAG3CiE,mBAAmBE,SAAS,kBACrB9E,EAAE2B,WAAWE,cAGpBkD,YAAcH,mBAAmBI,OAAOC,UAC1BhD,iBAAiB2C,qBAIhBG,mBACR/E,EAAE2B,WAAWE,UAGxB+C,mBAAmBrD,SAAS,eAExBG,QAAUrB,SAAS+B,OAAO,CAACD,GAAIkB,OAAQ0B,YAAaA,cAExDrD,QAAQ+B,MAAK,SAAS2B,MAClBR,mBAAmBvD,YAAY,WAG/BuD,mBAAmBI,KAAKI,KAAKL,gBAC9BjB,KAAK5D,aAAa6D,WAIrBrC,QAAQoC,MAAK,WACXc,mBAAmBvD,YAAY,cA8L7BgE,CAAkB1E,YAEtBiE,mBAAmBR,UAAS,SAASjB,GAC5BA,EAAEkB,SAAYlB,EAAEmB,UAAanB,EAAEoB,QAAWpB,EAAEqB,SACzCrB,EAAEsB,SAAWlE,KAAKmE,QAClBvB,EAAEC,iBACFwB,mBAAmBO,eAK3BG,YAAc5E,eAAeC,YACjC2E,YAAYT,OAAM,SAAS1B,GACvBA,EAAEC,iBA9LS,SAASzC,aACpB2E,YAAc5E,eAAeC,aAE7B2E,YAAYR,SAAS,gBAIrBS,KAAOD,YAAYN,OAAOC,OAC9BlD,iBAAiBuD,YAAaC,OA0L1BC,CAAa7E,YAEjB2E,YAAYH,MAAK,SAAShC,GACtBA,EAAEC,iBAjLO,SAASzC,aAClB0C,OAASnC,UAAUP,YAGR,KAAX0C,cACOrD,EAAE2B,WAAWE,cAGpByD,YAAc5E,eAAeC,YAG7B2E,YAAYR,SAAS,kBACd9E,EAAE2B,WAAWE,cAGpB0D,KAAOD,YAAYN,OAAOC,UACZhD,iBAAiBqD,cAIhBC,YACRvF,EAAE2B,WAAWE,UAGxByD,YAAY/D,SAAS,eACjBG,QAAUrB,SAAS+B,OAAO,CAACD,GAAIkB,OAAQkC,KAAMA,OAEjD7D,QAAQ+B,MAAK,SAAS2B,MAClBE,YAAYjE,YAAY,WAGxBiE,YAAYN,KAAKI,KAAKG,SAK1B7D,QAAQoC,MAAK,WACXwB,YAAYjE,YAAY,cA6ItBoE,CAAW9E,YAEf2E,YAAYlB,UAAS,SAASjB,GACrBA,EAAEkB,SAAYlB,EAAEmB,UAAanB,EAAEoB,QAAWpB,EAAEqB,SACzCrB,EAAEsB,SAAWlE,KAAKmE,QAClBvB,EAAEC,iBACFkC,YAAYH,WAhfJ,SAASxE,iBACtBG,kBAAkBH,SAASM,OAqf9ByE,CAAkB/E,SAAU,KACxBgF,eAAiB7E,kBAAkBH,SACvCgF,eAAezC,OAAM,SAASC,GAC1BA,EAAEC,iBA5ES,SAASzC,SACxBK,yBAAyBL,SAzBC,SAASA,SACvCA,QAAQY,SAAS,6BAyBbqE,CAA4BjF,SAE5BuB,gBAAgBvB,SAyEZkF,CAAiBlF,YAErBgF,eAAevB,UAAS,SAASjB,GACxBA,EAAEkB,SAAYlB,EAAEmB,UAAanB,EAAEoB,QAAWpB,EAAEqB,SACzCrB,EAAEsB,SAAWlE,KAAKmE,OAASvB,EAAEsB,SAAWlE,KAAKoE,QAC7CxB,EAAEC,iBACFuC,eAAezC,eAM3BlC,yBAAyBL,SAAU,KAC/BmF,sBAAwB/E,yBAAyBJ,SAErDmF,sBAAsBC,GAAGzF,UAAU0F,oBAAoB,WACnD9D,gBAAgBvB,YAGpBmF,sBAAsBC,GAAGzF,UAAU2F,sBAAsB,YA7GlC,SAAStF,SACpCA,QAAQU,YAAY,6BA6GZ6E,CAAyBvF,oBA2DoB,CAOrDwF,KAAM,SAASxF,SACXoC,uBAAuBpC,SAvDX,SAASA,eACnByF,kBAAoBzF,QAAQC,KAAK,IAAMD,QAAQ0F,KAAK,UAAY,IAAM1F,QAAQ0F,KAAK,qBACpFD,kBAAkBnF,cAGPmF,kBAAkBE,IAAI,GAC9BC,iBAAiB,SAAUpD,IAC/BA,EAAEC,qBACEoD,QAAU,QACA7F,QAAQ0F,KAAK,qBACT1F,QAAQ0F,KAAK,uBACf1F,QAAQ0F,KAAK,yBACT1F,QAAQ0F,KAAK,qBACrB,cACY1F,QAAQ0F,KAAK,+BACd1F,QAAQ0F,KAAK,8BACb1F,QAAQ0F,KAAK,oBAGhCI,YAActG,UAAUuC,OAAO,iCAAkC8D,SAcrEA,QAAU,QAbG,mBAAqBE,mBAAmB/F,QAAQ0F,KAAK,kBAC9D,SAAWK,mBAAmB/F,QAAQ0F,KAAK,kBAAoB,OAC/DK,mBAAmB/F,QAAQ0F,KAAK,eAAiB,SACjDK,mBAAmB/F,QAAQ0F,KAAK,gBAAkB,OAClDK,mBAAmB/F,QAAQ0F,KAAK,aAAe,SAC/CK,mBAAmB/F,QAAQ0F,KAAK,oBAAsB,OACtDK,mBAAmB/F,QAAQ0F,KAAK,iBAAmB,SACnDK,mBAAmB/F,QAAQ0F,KAAK,uBAAyB,OACzDK,mBAAmB/F,QAAQ0F,KAAK,oBAAsB,SACtDK,mBAAmB/F,QAAQ0F,KAAK,sBAAwB,OACxDK,mBAAmB/F,QAAQ0F,KAAK,mBAAqB,SACrDK,mBAAmB/F,QAAQ0F,KAAK,sBAAwB,OACxDK,mBAAmB/F,QAAQ0F,KAAK,mBAAqB,cAIrDM,cAAgBxG,UAAUuC,OAAO,mCAAoC8D,SACzEpG,MAAMwG,OAAO,CACTC,OAAO,EACPC,MAAOnG,QAAQ0F,KAAK,cACpBU,KAAMN,YACNO,OAAQL,cACRM,MAAM,OAcVC,CAAcvG"}keys.min.js.map000064400000002416152267106260007425 0ustar00{"version":3,"file":"keys.min.js","sources":["../src/keys.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 * A list of keys and their keycodes that are used by the LTI modules.\n *\n * @module mod_lti/keys\n * @class keys\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine([], function() {\n return /** @alias module:mod_lti/keys */ {\n ENTER: 13,\n SPACE: 32\n };\n});\n"],"names":["define","ENTER","SPACE"],"mappings":";;;;;;;;;AAwBAA,sBAAO,IAAI,iBACkC,CACrCC,MAAO,GACPC,MAAO"}coursecategory.min.js.map000064400000014230152267106260011505 0ustar00{"version":3,"file":"coursecategory.min.js","sources":["../src/coursecategory.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 * Select course categories for LTI tool.\n *\n * @module mod_lti/coursecategory\n * @copyright 2023 Jackson D'souza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.3\n */\n\ndefine([], function() {\n\n document.addEventListener('click', event => {\n const checkedbox = event.target.closest(\".lticoursecategories\");\n\n if (checkedbox) {\n // Get checkbox status.\n const checkboxstatus = checkedbox.checked;\n\n // Check / Uncheck all child category checkboxes based on selected checkbox status.\n const categorycontainer = document.querySelector('#collapse' + checkedbox.value);\n if (categorycontainer) {\n const categorycontainercheckbox = categorycontainer.querySelectorAll('input[type=\"checkbox\"]');\n for (let i = 0; i < categorycontainercheckbox.length; i++) {\n categorycontainercheckbox[i].checked = checkboxstatus;\n }\n }\n\n const lticategorytree = document.querySelector('.modltitree');\n const ltitreecheckbox = lticategorytree.querySelectorAll('input[type=\"checkbox\"]');\n let listvalue = '';\n for (let i = 0; i < ltitreecheckbox.length; i++) {\n if (ltitreecheckbox[i].checked) {\n if (listvalue.length == 0) {\n listvalue = ltitreecheckbox[i].value;\n } else {\n listvalue = listvalue + ',' + ltitreecheckbox[i].value;\n }\n }\n }\n document.querySelector('input[name=\"lti_coursecategories\"]').value = listvalue;\n }\n });\n\n /**\n * Get parent elements with class = accordion.\n *\n * @method getParents\n * @private\n * @param {string} elem DOM element.\n * @return {array} Parent elements.\n */\n function getParents(elem) {\n // Set up a parent array\n const parents = [];\n\n // Push each parent element to the array\n for (; elem && elem !== document; elem = elem.parentNode) {\n if (elem.classList.contains('accordion-group')) {\n parents.push(elem);\n }\n }\n\n // Return our parent array\n return parents;\n }\n\n return /** @alias module:mod_lti/coursecategory */ {\n\n /**\n * Initialise this module.\n * Loop through checkbox form elements starting with #cat-{N} and set it to checked\n * if {N} is found in the Selected category(s) list. Show / Hide the parent UL element.\n *\n * @param {string} selectedcategories Selected category(s).\n */\n init: function(selectedcategories) {\n if (selectedcategories.length) {\n const separator = \",\";\n const values = selectedcategories.split(separator);\n\n for (let i = 0; i < values.length; i++) {\n const categoryid = document.getElementById(\"cat-\" + values[i]);\n if (categoryid.value !== 0) {\n categoryid.checked = true;\n }\n const parents = getParents(categoryid);\n parents.forEach(function(element) {\n const elem = element.querySelector('a.accordion-toggle');\n const elembody = element.querySelector('.accordion-body');\n\n if (elem && elem.classList.contains('collapsed')) {\n elem.classList.remove('collapsed');\n }\n if (elembody) {\n elembody.classList.remove('collapse');\n elembody.classList.add('show');\n }\n });\n }\n }\n }\n };\n});"],"names":["define","getParents","elem","parents","document","parentNode","classList","contains","push","addEventListener","event","checkedbox","target","closest","checkboxstatus","checked","categorycontainer","querySelector","value","categorycontainercheckbox","querySelectorAll","i","length","ltitreecheckbox","listvalue","init","selectedcategories","separator","values","split","categoryid","getElementById","forEach","element","elembody","remove","add"],"mappings":";;;;;;;;AAwBAA,gCAAO,IAAI,oBA0CEC,WAAWC,YAEVC,QAAU,QAGTD,MAAQA,OAASE,SAAUF,KAAOA,KAAKG,WACtCH,KAAKI,UAAUC,SAAS,oBACxBJ,QAAQK,KAAKN,aAKdC,eApDXC,SAASK,iBAAiB,SAASC,cACzBC,WAAaD,MAAME,OAAOC,QAAQ,2BAEpCF,WAAY,OAENG,eAAiBH,WAAWI,QAG5BC,kBAAoBZ,SAASa,cAAc,YAAcN,WAAWO,UACtEF,kBAAmB,OACbG,0BAA4BH,kBAAkBI,iBAAiB,8BAChE,IAAIC,EAAI,EAAGA,EAAIF,0BAA0BG,OAAQD,IAClDF,0BAA0BE,GAAGN,QAAUD,qBAKzCS,gBADkBnB,SAASa,cAAc,eACPG,iBAAiB,8BACrDI,UAAY,OACX,IAAIH,EAAI,EAAGA,EAAIE,gBAAgBD,OAAQD,IACpCE,gBAAgBF,GAAGN,UAEfS,UADoB,GAApBA,UAAUF,OACEC,gBAAgBF,GAAGH,MAEnBM,UAAY,IAAMD,gBAAgBF,GAAGH,OAI7Dd,SAASa,cAAc,sCAAsCC,MAAQM,cA2B1B,CAS/CC,KAAM,SAASC,uBACPA,mBAAmBJ,OAAQ,OACrBK,UAAY,IACZC,OAASF,mBAAmBG,MAAMF,eAEnC,IAAIN,EAAI,EAAGA,EAAIO,OAAON,OAAQD,IAAK,OAC9BS,WAAa1B,SAAS2B,eAAe,OAASH,OAAOP,IAClC,IAArBS,WAAWZ,QACXY,WAAWf,SAAU,GAETd,WAAW6B,YACnBE,SAAQ,SAASC,eACf/B,KAAO+B,QAAQhB,cAAc,sBAC7BiB,SAAWD,QAAQhB,cAAc,mBAEnCf,MAAQA,KAAKI,UAAUC,SAAS,cAChCL,KAAKI,UAAU6B,OAAO,aAEtBD,WACAA,SAAS5B,UAAU6B,OAAO,YAC1BD,SAAS5B,UAAU8B,IAAI"}tool_configure_controller.min.js000064400000016071152267106260013161 0ustar00/** * Standard Ajax wrapper for Moodle. It calls the central Ajax script, * which can call any existing webservice using the current session. * In addition, it can batch multiple requests and return multiple responses. * * @module mod_lti/tool_configure_controller * @copyright 2015 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_lti/tool_configure_controller",["jquery","core/ajax","core/paged_content_factory","core/notification","core/templates","mod_lti/events","mod_lti/keys","mod_lti/tool_types_and_proxies","mod_lti/tool_type","mod_lti/tool_proxy","core/str","core/config"],(function($,ajax,pagedContentFactory,notification,templates,ltiEvents,KEYS,toolTypesAndProxies,toolType,toolProxy,str,config){var SELECTORS_EXTERNAL_REGISTRATION_CONTAINER="#external-registration-container",SELECTORS_EXTERNAL_REGISTRATION_PAGE_CONTAINER="#external-registration-page-container",SELECTORS_EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER="#external-registration-template-container",SELECTORS_CARTRIDGE_REGISTRATION_CONTAINER="#cartridge-registration-container",SELECTORS_CARTRIDGE_REGISTRATION_FORM="#cartridge-registration-form",SELECTORS_TOOL_CARD_CONTAINER="#tool-card-container",SELECTORS_TOOL_LIST_CONTAINER="#tool-list-container",SELECTORS_TOOL_CREATE_BUTTON="#tool-create-button",SELECTORS_TOOL_CREATE_LTILEGACY_BUTTON="#tool-createltilegacy-button",SELECTORS_REGISTRATION_CHOICE_CONTAINER="#registration-choice-container",SELECTORS_TOOL_URL="#tool-url",getToolListContainer=function(){return $(SELECTORS_TOOL_LIST_CONTAINER)};var getExternalRegistrationContainer=function(){return $(SELECTORS_EXTERNAL_REGISTRATION_CONTAINER)},getCartridgeRegistrationContainer=function(){return $(SELECTORS_CARTRIDGE_REGISTRATION_CONTAINER)},getRegistrationChoiceContainer=function(){return $(SELECTORS_REGISTRATION_CHOICE_CONTAINER)},closeLTIAdvRegistration=function(e){e.data&&"org.imsglobal.lti.close"===e.data.subject&&($(SELECTORS_EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER).empty(),hideExternalRegistration(),showRegistrationChoices(),showToolList(),showRegistrationChoices(),reloadToolList())},getToolURL=function(){return $(SELECTORS_TOOL_URL).val()},hideExternalRegistration=function(){getExternalRegistrationContainer().addClass("hidden")},hideCartridgeRegistration=function(){getCartridgeRegistrationContainer().addClass("hidden")},hideRegistrationChoices=function(){getRegistrationChoiceContainer().addClass("hidden")},showExternalRegistration=function(){hideCartridgeRegistration(),hideRegistrationChoices(),getExternalRegistrationContainer().removeClass("hidden"),screenReaderAnnounce(getExternalRegistrationContainer())},showRegistrationChoices=function(){hideExternalRegistration(),hideCartridgeRegistration(),getRegistrationChoiceContainer().removeClass("hidden"),screenReaderAnnounce(getRegistrationChoiceContainer())},screenReaderAnnounce=function(element){element.children().detach().appendTo(element)},hideToolList=function(){getToolListContainer().addClass("hidden")},showToolList=function(){getToolListContainer().removeClass("hidden")},startLoading=function(element){element.addClass("loading")},stopLoading=function(element){element.removeClass("loading")},reloadToolList=function(){M.util.js_pending("reloadToolList");const cardContainer=$(SELECTORS_TOOL_CARD_CONTAINER),listContainer=getToolListContainer();fetchToolCount().done((function(data){pagedContentFactory.createWithTotalAndLimit(data.count,60,(function(pagesData){return pagesData.map((function(pageData){return fetchToolData(pageData.limit,pageData.offset).then((function(data){return renderToolData(data)}))}))}),{showFirstLast:!0}).done((function(html,js){templates.replaceNodeContents(cardContainer,html,js)})).always((function(){stopLoading(listContainer),M.util.js_complete("reloadToolList")}))})),startLoading(listContainer)};const fetchToolCount=function(){return toolTypesAndProxies.count({orphanedonly:!0}).done((function(data){return data})).catch((function(error){return notification.exception(error),{count:0}}))},fetchToolData=function(limit,offset){const args={orphanedonly:!0};return null===limit||Number.isNaN(limit)||(args.limit=limit),null===offset||Number.isNaN(offset)||(args.offset=offset),toolTypesAndProxies.query(args).done((function(data){return data})).catch((function(error){return notification.exception(error),{types:[],proxies:[],limit:limit,offset:offset}}))},renderToolData=function(data){const context={tools:data.types,proxies:data.proxies};return templates.render("mod_lti/tool_list",context).done((function(html,js){return{html:html,js:js}}))};var addLTIAdvTool=function(){var url=getToolURL().trim();url&&($(SELECTORS_TOOL_URL).val(""),hideToolList(),function(url){$(SELECTORS_EXTERNAL_REGISTRATION_PAGE_CONTAINER).removeClass("hidden"),$(SELECTORS_EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER).append($("")),showExternalRegistration(),window.addEventListener("message",closeLTIAdvRegistration,!1)}(url))},registerEventListeners=function(){$(document).on(ltiEvents.NEW_TOOL_TYPE,(function(){reloadToolList()})),$(document).on(ltiEvents.START_EXTERNAL_REGISTRATION,(function(){showExternalRegistration(),$(SELECTORS_TOOL_URL).val(""),hideToolList()})),$(document).on(ltiEvents.STOP_EXTERNAL_REGISTRATION,(function(){showToolList(),showRegistrationChoices()})),$(document).on(ltiEvents.START_CARTRIDGE_REGISTRATION,(function(event,url){!function(url){hideExternalRegistration(),hideRegistrationChoices();var container=getCartridgeRegistrationContainer();container.find("input").val(""),container.removeClass("hidden"),container.find(SELECTORS_CARTRIDGE_REGISTRATION_FORM).attr("data-cartridge-url",url),screenReaderAnnounce(container)}(url)})),$(document).on(ltiEvents.STOP_CARTRIDGE_REGISTRATION,(function(){getCartridgeRegistrationContainer().find(SELECTORS_CARTRIDGE_REGISTRATION_FORM).removeAttr("data-cartridge-url"),showRegistrationChoices()})),$(document).on(ltiEvents.REGISTRATION_FEEDBACK,(function(event,data){!function(data){var type=data.error?"error":"success";notification.addNotification({message:data.message,type:type})}(data)})),$(SELECTORS_TOOL_CREATE_LTILEGACY_BUTTON).click((function(e){e.preventDefault(),function(){var url=getToolURL().trim();if(""===url)return $.Deferred().resolve();var toolButton=$(SELECTORS_TOOL_CREATE_LTILEGACY_BUTTON);startLoading(toolButton);var promise=toolType.isCartridge(url);promise.always((function(){stopLoading(toolButton)})),promise.done((function(result){result.iscartridge?($(SELECTORS_TOOL_URL).val(""),$(document).trigger(ltiEvents.START_CARTRIDGE_REGISTRATION,url)):$(document).trigger(ltiEvents.START_EXTERNAL_REGISTRATION,{url:url})})),promise.fail((function(){str.get_string("errorbadurl","mod_lti").done((function(s){$(document).trigger(ltiEvents.REGISTRATION_FEEDBACK,{message:s,error:!0})})).fail(notification.exception)}))}()})),$(SELECTORS_TOOL_CREATE_BUTTON).click((function(e){e.preventDefault(),addLTIAdvTool()}))};return{init:function(){registerEventListeners(),reloadToolList()}}})); //# sourceMappingURL=tool_configure_controller.min.js.mapcontentitem_return.min.js000064400000001062152267106260011622 0ustar00/** * Processes the result of LTI tool creation from a Content-Item message type. * * @module mod_lti/contentitem_return * @copyright 2016 Jun Pataleta * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.2 */ define("mod_lti/contentitem_return",["jquery"],(function($){return{init:function(returnData){$(window).ready((function(){window!=top?parent.processContentItemReturnData(returnData):window.processContentItemReturnData(returnData)}))}}})); //# sourceMappingURL=contentitem_return.min.js.mapcontentitem.min.js.map000064400000036555152267106260011016 0ustar00{"version":3,"file":"contentitem.min.js","sources":["../src/contentitem.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 * Launches the modal dialogue that contains the iframe that sends the Content-Item selection request to an\n * LTI tool provider that supports Content-Item type message.\n *\n * See template: mod_lti/contentitem\n *\n * @module mod_lti/contentitem\n * @copyright 2016 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.2\n */\ndefine(\n [\n 'jquery',\n 'core/notification',\n 'core/str',\n 'core/templates',\n 'mod_lti/form-field',\n 'core/modal',\n 'core/modal_events'\n ],\n function($, notification, str, templates, FormField, Modal, ModalEvents) {\n var dialogue;\n var doneCallback;\n var contentItem = {\n /**\n * Init function.\n *\n * @param {string} url The URL for the content item selection.\n * @param {string} toolUrl The URL of the LTI tool.\n * @param {object} postData The data to be sent for the content item selection request.\n * @param {Function} cb The callback to run once the content item has been processed.\n */\n init: function(url, toolUrl, postData, cb) {\n doneCallback = cb;\n var context = {\n url: url,\n toolUrl: toolUrl,\n postData: postData\n };\n var bodyPromise = templates.render('mod_lti/contentitem', context);\n\n if (dialogue) {\n // Set dialogue body.\n dialogue.setBody(bodyPromise);\n // Display the dialogue.\n dialogue.show();\n return;\n }\n\n str.get_string('selectcontent', 'lti').then(function(title) {\n return Modal.create({\n title: title,\n body: bodyPromise,\n large: true,\n show: true,\n });\n }).then(function(modal) {\n dialogue = modal;\n // On hide handler.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Empty modal contents when it's hidden.\n modal.setBody('');\n\n // Fetch notifications.\n notification.fetchNotifications();\n });\n return;\n }).catch(notification.exception);\n }\n };\n\n /**\n * Array of form fields for LTI tool configuration.\n */\n var ltiFormFields = [\n new FormField('name', FormField.TYPES.TEXT, false, ''),\n new FormField('introeditor', FormField.TYPES.EDITOR, false, ''),\n new FormField('toolurl', FormField.TYPES.TEXT, true, ''),\n new FormField('securetoolurl', FormField.TYPES.TEXT, true, ''),\n new FormField('instructorchoiceacceptgrades', FormField.TYPES.CHECKBOX, true, true),\n new FormField('instructorchoicesendname', FormField.TYPES.CHECKBOX, true, true),\n new FormField('instructorchoicesendemailaddr', FormField.TYPES.CHECKBOX, true, true),\n new FormField('instructorcustomparameters', FormField.TYPES.TEXT, true, ''),\n new FormField('icon', FormField.TYPES.TEXT, true, ''),\n new FormField('secureicon', FormField.TYPES.TEXT, true, ''),\n new FormField('launchcontainer', FormField.TYPES.SELECT, true, 0),\n new FormField('grade_modgrade_point', FormField.TYPES.TEXT, false, ''),\n new FormField('lineitemresourceid', FormField.TYPES.TEXT, true, ''),\n new FormField('lineitemtag', FormField.TYPES.TEXT, true, ''),\n new FormField('lineitemsubreviewurl', FormField.TYPES.TEXT, true, ''),\n new FormField('lineitemsubreviewparams', FormField.TYPES.TEXT, true, '')\n ];\n\n /**\n * Hide the element, including aria and tab index.\n * @param {HTMLElement} e the element to be hidden.\n */\n const hideElement = (e) => {\n e.setAttribute('hidden', 'true');\n e.setAttribute('aria-hidden', 'true');\n e.setAttribute('tab-index', '-1');\n };\n\n /**\n * Show the element, including aria and tab index (set to 1).\n * @param {HTMLElement} e the element to be shown.\n */\n const showElement = (e) => {\n e.removeAttribute('hidden');\n e.setAttribute('aria-hidden', 'false');\n e.setAttribute('tab-index', '1');\n };\n\n /**\n * When more than one item needs to be added, the UI is simplified\n * to just list the items to be added. Form is hidden and the only\n * options is (save and return to course) or cancel.\n * This function injects the summary to the form page, and hides\n * the unneeded elements.\n * @param {Object[]} items items to be added to the course.\n */\n const showMultipleSummaryAndHideForm = async function(items) {\n const form = document.querySelector('#region-main-box form');\n const toolArea = form.querySelector('[data-attribute=\"dynamic-import\"]');\n const buttonGroup = form.querySelector('#fgroup_id_buttonar');\n const submitAndLaunch = form.querySelector('#id_submitbutton');\n Array.from(form.children).forEach(hideElement);\n hideElement(submitAndLaunch);\n const {html, js} = await templates.renderForPromise('mod_lti/tool_deeplinking_results',\n {items: items});\n\n await templates.replaceNodeContents(toolArea, html, js);\n showElement(toolArea);\n showElement(buttonGroup);\n };\n\n /**\n * Transforms config values aimed at populating the lti mod form to JSON variant\n * which are used to insert more than one activity modules in one submit\n * by applying variation to the submitted form.\n * See /course/modedit.php.\n * @private\n * @param {Object} config transforms a config to an actual form data to be posted.\n * @return {Object} variant that will be used to modify form values on submit.\n */\n var configToVariant = (config) => {\n const variant = {};\n ['name', 'toolurl', 'securetoolurl', 'instructorcustomparameters', 'icon', 'secureicon',\n 'launchcontainer', 'lineitemresourceid', 'lineitemtag', 'lineitemsubreviewurl',\n 'lineitemsubreviewparams'].forEach(\n function(name) {\n variant[name] = config[name] || '';\n }\n );\n variant['introeditor[text]'] = config.introeditor ? config.introeditor.text : '';\n variant['introeditor[format]'] = config.introeditor ? config.introeditor.format : '';\n if (config.instructorchoiceacceptgrades === 1) {\n variant.instructorchoiceacceptgrades = '1';\n variant['grade[modgrade_point]'] = config.grade_modgrade_point || '100';\n } else {\n variant.instructorchoiceacceptgrades = '0';\n }\n return variant;\n };\n\n /**\n * Window function that can be called from mod_lti/contentitem_return to close the dialogue and process the return data.\n * If the return data contains more than one item, the form will not be populated with item data\n * but rather hidden, and the item data will be added to a single input field used to create multiple\n * instances in one request.\n *\n * @param {object} returnData The fetched configuration data from the Content-Item selection dialogue.\n */\n window.processContentItemReturnData = function(returnData) {\n if (dialogue) {\n dialogue.hide();\n }\n var index;\n if (returnData.multiple) {\n for (index in ltiFormFields) {\n // Name is required, so putting a placeholder as it will not be used\n // in multi-items add.\n ltiFormFields[index].setFieldValue(ltiFormFields[index].name === 'name' ? 'item' : null);\n }\n var variants = [];\n returnData.multiple.forEach(function(v) {\n variants.push(configToVariant(v));\n });\n showMultipleSummaryAndHideForm(returnData.multiple);\n const submitAndCourse = document.querySelector('#id_submitbutton2');\n submitAndCourse.onclick = (e) => {\n e.preventDefault();\n submitAndCourse.disabled = true;\n const fd = new FormData(document.querySelector('#region-main-box form'));\n const postVariant = (promise, variant) => {\n Object.entries(variant).forEach((entry) => fd.set(entry[0], entry[1]));\n const body = new URLSearchParams(fd);\n const doPost = () => fetch(document.location.pathname, {method: 'post', body});\n return promise.then(doPost).catch(doPost);\n };\n const backToCourse = () => {\n document.querySelector(\"#id_cancel\").click();\n };\n variants.reduce(postVariant, Promise.resolve()).then(backToCourse).catch(backToCourse);\n };\n } else {\n // Populate LTI configuration fields from return data.\n for (index in ltiFormFields) {\n var field = ltiFormFields[index];\n var value = null;\n if (typeof returnData[field.name] !== 'undefined') {\n value = returnData[field.name];\n }\n field.setFieldValue(value);\n }\n field.setFieldValue(value);\n\n // Update the UI element which signifies content has been selected.\n document.querySelector(\"#id_selectcontentindicator\").innerHTML = returnData.selectcontentindicator;\n }\n\n if (doneCallback) {\n doneCallback(returnData);\n }\n };\n\n return contentItem;\n }\n);\n"],"names":["define","$","notification","str","templates","FormField","Modal","ModalEvents","dialogue","doneCallback","contentItem","init","url","toolUrl","postData","cb","context","bodyPromise","render","setBody","show","get_string","then","title","create","body","large","modal","getRoot","on","hidden","fetchNotifications","catch","exception","ltiFormFields","TYPES","TEXT","EDITOR","CHECKBOX","SELECT","hideElement","e","setAttribute","showElement","removeAttribute","window","processContentItemReturnData","returnData","index","hide","multiple","setFieldValue","name","variants","forEach","v","push","config","variant","introeditor","text","format","instructorchoiceacceptgrades","grade_modgrade_point","configToVariant","async","items","form","document","querySelector","toolArea","buttonGroup","submitAndLaunch","Array","from","children","html","js","renderForPromise","replaceNodeContents","showMultipleSummaryAndHideForm","submitAndCourse","onclick","preventDefault","disabled","fd","FormData","backToCourse","click","reduce","promise","Object","entries","entry","set","URLSearchParams","doPost","fetch","location","pathname","method","Promise","resolve","field","value","innerHTML","selectcontentindicator"],"mappings":";;;;;;;;;;;AA0BAA,6BACI,CACI,SACA,oBACA,WACA,iBACA,qBACA,aACA,sBAEJ,SAASC,EAAGC,aAAcC,IAAKC,UAAWC,UAAWC,MAAOC,iBACpDC,SACAC,aACAC,YAAc,CASdC,KAAM,SAASC,IAAKC,QAASC,SAAUC,IACnCN,aAAeM,OACXC,QAAU,CACVJ,IAAKA,IACLC,QAASA,QACTC,SAAUA,UAEVG,YAAcb,UAAUc,OAAO,sBAAuBF,YAEtDR,gBAEAA,SAASW,QAAQF,kBAEjBT,SAASY,OAIbjB,IAAIkB,WAAW,gBAAiB,OAAOC,MAAK,SAASC,cAC1CjB,MAAMkB,OAAO,CAChBD,MAAOA,MACPE,KAAMR,YACNS,OAAO,EACPN,MAAM,OAEXE,MAAK,SAASK,OACbnB,SAAWmB,MAEXA,MAAMC,UAAUC,GAAGtB,YAAYuB,QAAQ,WAEnCH,MAAMR,QAAQ,IAGdjB,aAAa6B,2BAGlBC,MAAM9B,aAAa+B,aAO1BC,cAAgB,CAChB,IAAI7B,UAAU,OAAQA,UAAU8B,MAAMC,MAAM,EAAO,IACnD,IAAI/B,UAAU,cAAeA,UAAU8B,MAAME,QAAQ,EAAO,IAC5D,IAAIhC,UAAU,UAAWA,UAAU8B,MAAMC,MAAM,EAAM,IACrD,IAAI/B,UAAU,gBAAiBA,UAAU8B,MAAMC,MAAM,EAAM,IAC3D,IAAI/B,UAAU,+BAAgCA,UAAU8B,MAAMG,UAAU,GAAM,GAC9E,IAAIjC,UAAU,2BAA4BA,UAAU8B,MAAMG,UAAU,GAAM,GAC1E,IAAIjC,UAAU,gCAAiCA,UAAU8B,MAAMG,UAAU,GAAM,GAC/E,IAAIjC,UAAU,6BAA8BA,UAAU8B,MAAMC,MAAM,EAAM,IACxE,IAAI/B,UAAU,OAAQA,UAAU8B,MAAMC,MAAM,EAAM,IAClD,IAAI/B,UAAU,aAAcA,UAAU8B,MAAMC,MAAM,EAAM,IACxD,IAAI/B,UAAU,kBAAmBA,UAAU8B,MAAMI,QAAQ,EAAM,GAC/D,IAAIlC,UAAU,uBAAwBA,UAAU8B,MAAMC,MAAM,EAAO,IACnE,IAAI/B,UAAU,qBAAsBA,UAAU8B,MAAMC,MAAM,EAAM,IAChE,IAAI/B,UAAU,cAAeA,UAAU8B,MAAMC,MAAM,EAAM,IACzD,IAAI/B,UAAU,uBAAwBA,UAAU8B,MAAMC,MAAM,EAAM,IAClE,IAAI/B,UAAU,0BAA2BA,UAAU8B,MAAMC,MAAM,EAAM,WAOnEI,YAAeC,IACjBA,EAAEC,aAAa,SAAU,QACzBD,EAAEC,aAAa,cAAe,QAC9BD,EAAEC,aAAa,YAAa,OAO1BC,YAAeF,IACjBA,EAAEG,gBAAgB,UAClBH,EAAEC,aAAa,cAAe,SAC9BD,EAAEC,aAAa,YAAa,aA+DhCG,OAAOC,6BAA+B,SAASC,gBAIvCC,SAHAxC,UACAA,SAASyC,OAGTF,WAAWG,SAAU,KAChBF,SAASd,cAGVA,cAAcc,OAAOG,cAA4C,SAA9BjB,cAAcc,OAAOI,KAAkB,OAAS,UAEnFC,SAAW,GACfN,WAAWG,SAASI,SAAQ,SAASC,GACjCF,SAASG,KAzCEC,CAAAA,eACbC,QAAU,UACf,OAAQ,UAAW,gBAAiB,6BAA8B,OAAQ,aACvE,kBAAmB,qBAAsB,cAAe,uBACxD,2BAA2BJ,SAC3B,SAASF,MACLM,QAAQN,MAAQK,OAAOL,OAAS,MAGxCM,QAAQ,qBAAuBD,OAAOE,YAAcF,OAAOE,YAAYC,KAAO,GAC9EF,QAAQ,uBAAyBD,OAAOE,YAAcF,OAAOE,YAAYE,OAAS,GACtC,IAAxCJ,OAAOK,8BACPJ,QAAQI,6BAA+B,IACvCJ,QAAQ,yBAA2BD,OAAOM,sBAAwB,OAElEL,QAAQI,6BAA+B,IAEpCJ,SAwBeM,CAAgBT,OAjEHU,eAAeC,aAC5CC,KAAOC,SAASC,cAAc,yBAC9BC,SAAWH,KAAKE,cAAc,qCAC9BE,YAAcJ,KAAKE,cAAc,uBACjCG,gBAAkBL,KAAKE,cAAc,oBAC3CI,MAAMC,KAAKP,KAAKQ,UAAUrB,QAAQd,aAClCA,YAAYgC,uBACNI,KAACA,KAADC,GAAOA,UAAYzE,UAAU0E,iBAAiB,mCAChD,CAACZ,MAAOA,cAEN9D,UAAU2E,oBAAoBT,SAAUM,KAAMC,IACpDlC,YAAY2B,UACZ3B,YAAY4B,aAuDRS,CAA+BjC,WAAWG,gBACpC+B,gBAAkBb,SAASC,cAAc,qBAC/CY,gBAAgBC,QAAWzC,IACvBA,EAAE0C,iBACFF,gBAAgBG,UAAW,QACrBC,GAAK,IAAIC,SAASlB,SAASC,cAAc,0BAOzCkB,aAAe,KACjBnB,SAASC,cAAc,cAAcmB,SAEzCnC,SAASoC,QATW,CAACC,QAAShC,WAC1BiC,OAAOC,QAAQlC,SAASJ,SAASuC,OAAUR,GAAGS,IAAID,MAAM,GAAIA,MAAM,YAC5DpE,KAAO,IAAIsE,gBAAgBV,IAC3BW,OAAS,IAAMC,MAAM7B,SAAS8B,SAASC,SAAU,CAACC,OAAQ,OAAQ3E,KAAAA,cACjEiE,QAAQpE,KAAK0E,QAAQhE,MAAMgE,UAKTK,QAAQC,WAAWhF,KAAKiE,cAAcvD,MAAMuD,mBAE1E,KAEEvC,SAASd,cAAe,KACrBqE,MAAQrE,cAAcc,OACtBwD,MAAQ,UAC0B,IAA3BzD,WAAWwD,MAAMnD,QACxBoD,MAAQzD,WAAWwD,MAAMnD,OAE7BmD,MAAMpD,cAAcqD,OAExBD,MAAMpD,cAAcqD,OAGpBpC,SAASC,cAAc,8BAA8BoC,UAAY1D,WAAW2D,uBAG5EjG,cACAA,aAAasC,aAIdrC"}participant_selector.min.js000064400000003573152267113500012114 0ustar00/** * Custom auto-complete adapter to load users from the assignment list_participants webservice. * * @module mod_assign/participant_selector * @copyright 2015 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define("mod_assign/participant_selector",["core/ajax","jquery","core/templates"],(function(ajax,$,templates){return{processResults:function(selector,data){return data},transport:function(selector,query,success,failure){var assignmentid=$(selector).attr("data-assignmentid"),groupid=$(selector).attr("data-groupid"),filters=$('[data-region="configure-filters"] input[type="checkbox"]'),filterstrings=[];filters.each((function(index,element){filterstrings[$(element).attr("name")]=$(element).prop("checked")})),ajax.call([{methodname:"mod_assign_list_participants",args:{assignid:assignmentid,groupid:groupid,filter:query,limit:30,includeenrolments:!1,tablesort:!0}}])[0].then((function(results){var promises=[],identityfields=$("[data-showuseridentity]").data("showuseridentity").split(",");return $.each(results,(function(index,user){var ctx=user,identity=[],show=!0;filterstrings.filter_submitted&&!user.submitted&&(show=!1),filterstrings.filter_notsubmitted&&user.submitted&&(show=!1),filterstrings.filter_requiregrading&&!user.requiregrading&&(show=!1),filterstrings.filter_grantedextension&&!user.grantedextension&&(show=!1),show&&($.each(identityfields,(function(i,k){void 0!==user[k]&&""!==user[k]&&(ctx.hasidentity=!0,identity.push(user[k]))})),ctx.identity=identity.join(", "),promises.push(templates.render("mod_assign/list_participant_user_summary",ctx).then((function(html){return{value:user.id,label:html}}))))})),$.when.apply($,promises)})).then((function(){var users=[];arguments[0]&&(users=Array.prototype.slice.call(arguments)),success(users)})).catch(failure)}}})); //# sourceMappingURL=participant_selector.min.js.mapgrading_panel.min.js.map000064400000057623152267113500011251 0ustar00{"version":3,"file":"grading_panel.min.js","sources":["../src/grading_panel.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript controller for the \"Grading\" panel at the right of the page.\n *\n * @module mod_assign/grading_panel\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine([\n 'jquery',\n 'core/yui',\n 'core/notification',\n 'core/templates',\n 'core/fragment',\n 'core/ajax',\n 'core/str',\n 'mod_assign/grading_form_change_checker',\n 'mod_assign/grading_events',\n 'core_form/events',\n 'core/toast',\n 'core_form/changechecker',\n], function(\n $,\n Y,\n notification,\n templates,\n fragment,\n ajax,\n str,\n checker,\n GradingEvents,\n FormEvents,\n Toast,\n FormChangeChecker\n) {\n\n /**\n * GradingPanel class.\n *\n * @class mod_assign/grading_panel\n * @param {String} selector The selector for the page region containing the user navigation.\n */\n var GradingPanel = function(selector) {\n this._regionSelector = selector;\n this._region = $(selector);\n this._userCache = [];\n\n this.registerEventListeners();\n };\n\n /** @property {String} Selector for the page region containing the user navigation. */\n GradingPanel.prototype._regionSelector = null;\n\n /** @property {Integer} Remember the last user id to prevent unnessecary reloads. */\n GradingPanel.prototype._lastUserId = 0;\n\n /** @property {Integer} Remember the last attempt number to prevent unnessecary reloads. */\n GradingPanel.prototype._lastAttemptNumber = -1;\n\n /** @property {JQuery} JQuery node for the page region containing the user navigation. */\n GradingPanel.prototype._region = null;\n\n /** @property {Integer} The id of the next user in the grading list */\n GradingPanel.prototype.nextUserId = null;\n\n /** @property {Boolean} Next user exists in the grading list */\n GradingPanel.prototype.nextUser = false;\n\n /**\n * Fade the dom node out, update it, and fade it back.\n *\n * @private\n * @method _niceReplaceNodeContents\n * @param {JQuery} node\n * @param {String} html\n * @param {String} js\n * @return {Deferred} promise resolved when the animations are complete.\n */\n GradingPanel.prototype._niceReplaceNodeContents = function(node, html, js) {\n var promise = $.Deferred();\n\n node.fadeOut(\"fast\", function() {\n templates.replaceNodeContents(node, html, js);\n node.fadeIn(\"fast\", function() {\n promise.resolve();\n });\n });\n\n return promise.promise();\n };\n\n /**\n * Make sure all form fields have the latest saved state.\n * @private\n * @method _saveFormState\n */\n GradingPanel.prototype._saveFormState = function() {\n // Copy data from notify students checkbox which was moved out of the form.\n var checked = $('[data-region=\"grading-actions-form\"] [name=\"sendstudentnotifications\"]').prop(\"checked\");\n $('.gradeform [name=\"sendstudentnotifications\"]').val(checked);\n };\n\n /**\n * Make form submit via ajax.\n *\n * @private\n * @param {Object} event\n * @param {Integer} nextUserId\n * @param {Boolean} nextUser optional. Load next user in the grading list.\n * @method _submitForm\n * @fires event:formSubmittedByJavascript\n */\n GradingPanel.prototype._submitForm = function(event, nextUserId, nextUser) {\n // If the form has data in comment-area, then we need to save that comment\n var commentAreaElement = document.querySelector('.comment-area');\n if (commentAreaElement) {\n var commentTextAreaElement = commentAreaElement.querySelector('.db > textarea');\n if (commentTextAreaElement.value !== '') {\n var commentActionPostElement = commentAreaElement.querySelector('.fd a[id^=\"comment-action-post-\"]');\n commentActionPostElement.click();\n }\n }\n\n // The form was submitted - send it via ajax instead.\n var form = $(this._region.find('form.gradeform'));\n\n $('[data-region=\"overlay\"]').show();\n\n // Mark the form as submitted in the change checker.\n FormChangeChecker.markFormSubmitted(form[0]);\n\n // We call this, so other modules can update the form with the latest state.\n form.trigger('save-form-state');\n\n // Tell all form fields we are about to submit the form.\n FormEvents.notifyFormSubmittedByJavascript(form[0]);\n\n // Now we get all the current values from the form.\n var data = form.serialize();\n var assignmentid = this._region.attr('data-assignmentid');\n\n // Now we can continue...\n ajax.call([{\n methodname: 'mod_assign_submit_grading_form',\n args: {assignmentid: assignmentid, userid: this._lastUserId, jsonformdata: JSON.stringify(data)},\n done: this._handleFormSubmissionResponse.bind(this, data, nextUserId, nextUser),\n fail: notification.exception\n }]);\n };\n\n /**\n * Handle form submission response.\n *\n * @private\n * @method _handleFormSubmissionResponse\n * @param {Array} formdata - submitted values\n * @param {Number} [nextUserId] The id of the user to load after the form is saved\n * @param {Boolean} [nextUser] - Whether to switch to next user in the grading list.\n * @param {Array} response List of errors.\n */\n GradingPanel.prototype._handleFormSubmissionResponse = function(formdata, nextUserId, nextUser, response) {\n if (typeof nextUserId === \"undefined\") {\n nextUserId = this._lastUserId;\n }\n if (response.length) {\n str.get_string('errorgradechangessaveddetail', 'mod_assign')\n .then(function(str) {\n Toast.add(str, {type: 'danger', delay: 4000});\n return str;\n })\n .catch(notification.exception);\n\n // There was an error saving the grade. Re-render the form using the submitted data so we can show\n // validation errors.\n $(document).trigger('reset', [this._lastUserId, formdata, true]);\n } else {\n str.get_string('gradechangessaveddetail', 'mod_assign')\n .then(function(str) {\n Toast.add(str);\n return str;\n })\n .catch(notification.exception);\n\n // Reset the form state.\n var form = $(this._region.find('form.gradeform'));\n FormChangeChecker.resetFormDirtyState(form[0]);\n\n if (nextUserId == this._lastUserId) {\n $(document).trigger('reset', nextUserId);\n } else if (nextUser) {\n $(document).trigger('done-saving-show-next', true);\n } else {\n $(document).trigger('user-changed', nextUserId);\n }\n }\n $('[data-region=\"overlay\"]').hide();\n };\n\n /**\n * Refresh form with default values.\n *\n * @private\n * @method _resetForm\n * @param {Event} e\n * @param {Integer} userid\n * @param {Array} formdata\n * @param {Boolean} unresolvederror\n */\n GradingPanel.prototype._resetForm = function(e, userid, formdata, unresolvederror) {\n // The form was cancelled - refresh with default values.\n var event = $.Event(\"custom\");\n if (typeof userid == \"undefined\") {\n userid = this._lastUserId;\n }\n this._lastUserId = 0;\n this._refreshGradingPanel(event, userid, formdata, -1, unresolvederror);\n };\n\n /**\n * Open a picker to choose an older attempt.\n *\n * @private\n * @param {Object} e\n * @method _chooseAttempt\n */\n GradingPanel.prototype._chooseAttempt = function(e) {\n // Show a dialog.\n\n // The form is in the element pointed to by data-submissions.\n var link = $(e.target);\n var submissionsId = link.data('submissions');\n var submissionsform = $(document.getElementById(submissionsId));\n var formcopy = submissionsform.clone();\n var formhtml = formcopy.wrap($('
        ')).html();\n\n str.get_strings([\n {key: 'viewadifferentattempt', component: 'mod_assign'},\n {key: 'view', component: 'core'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], formhtml, strs[1], strs[2], function() {\n var attemptnumber = $(\"input:radio[name='select-attemptnumber']:checked\").val();\n\n this._refreshGradingPanel(null, this._lastUserId, '', attemptnumber);\n }.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Add popout buttons\n *\n * @private\n * @method _addPopoutButtons\n * @param {JQuery} selector The region selector to add popout buttons to.\n */\n GradingPanel.prototype._addPopoutButtons = function(selector) {\n var region = $(selector);\n\n templates.render('mod_assign/popout_button', {}).done(function(html) {\n var parents = region.find('[data-fieldtype=\"filemanager\"],[data-fieldtype=\"editor\"],[data-fieldtype=\"grading\"]')\n .closest('.fitem');\n parents.addClass('has-popout').find('label:first').parent().append(html);\n\n region.on('click', '[data-region=\"popout-button\"]', this._togglePopout.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Make a div \"popout\" or \"popback\".\n *\n * @private\n * @method _togglePopout\n * @param {Event} event\n */\n GradingPanel.prototype._togglePopout = function(event) {\n event.preventDefault();\n var container = $(event.target).closest('.fitem');\n if (container.hasClass('popout')) {\n $('.popout').removeClass('popout');\n } else {\n $('.popout').removeClass('popout');\n container.addClass('popout');\n container.addClass('moodle-has-zindex');\n }\n };\n\n /**\n * Get the user context - re-render the template in the page.\n *\n * @private\n * @method _refreshGradingPanel\n * @param {Event} event\n * @param {Number} userid\n * @param {String} submissiondata serialised submission data.\n * @param {Integer} attemptnumber\n * @param {Boolean} unresolvederror\n */\n GradingPanel.prototype._refreshGradingPanel = function(event, userid, submissiondata, attemptnumber, unresolvederror) {\n var contextid = this._region.attr('data-contextid');\n if (typeof submissiondata === 'undefined') {\n submissiondata = '';\n }\n if (typeof attemptnumber === 'undefined') {\n attemptnumber = -1;\n }\n if (typeof unresolvederror === 'undefined') {\n unresolvederror = false;\n }\n // Skip reloading if it is the same user.\n if (this._lastUserId == userid && this._lastAttemptNumber == attemptnumber && submissiondata === '') {\n return;\n }\n this._lastUserId = userid;\n this._lastAttemptNumber = attemptnumber;\n $(document).trigger('start-loading-user');\n // Tell behat to back off too.\n window.M.util.js_pending('mod-assign-loading-user');\n // First insert the loading template.\n templates.render('mod_assign/loading', {}).done(function(html, js) {\n // Update the page.\n this._niceReplaceNodeContents(this._region, html, js).done(function() {\n if (userid > 0) {\n this._region.show();\n // Reload the grading form \"fragment\" for this user.\n var params = {userid: userid, attemptnumber: attemptnumber, jsonformdata: JSON.stringify(submissiondata)};\n fragment.loadFragment('mod_assign', 'gradingpanel', contextid, params).done(function(html, js) {\n\n // Reset whole grading page when there is a failure in retrieving the html\n // i.e. user no longer under \"requires grading\" filter when graded\n if (html === '') {\n $(document).trigger('reset-table', true);\n }\n\n this._niceReplaceNodeContents(this._region, html, js)\n .done(function() {\n checker.saveFormState('[data-region=\"grade-panel\"] .gradeform');\n $(document).on('editor-content-restored', function() {\n // If the editor has some content that has been restored\n // then save the form state again for comparison.\n checker.saveFormState('[data-region=\"grade-panel\"] .gradeform');\n });\n $('[data-region=\"attempt-chooser\"]').on('click', this._chooseAttempt.bind(this));\n this._addPopoutButtons('[data-region=\"grade-panel\"] .gradeform');\n if (unresolvederror) {\n $('[data-region=\"grade-panel\"] .gradeform').data('unresolved-error', true);\n }\n $(document).trigger('finish-loading-user');\n // Tell behat we are friends again.\n window.M.util.js_complete('mod-assign-loading-user');\n }.bind(this))\n .fail(notification.exception);\n }.bind(this)).fail(notification.exception);\n $('[data-region=\"review-panel\"]').show();\n } else {\n this._region.hide();\n $('[data-region=\"review-panel\"]').hide();\n $(document).trigger('finish-loading-user');\n // Tell behat we are friends again.\n window.M.util.js_complete('mod-assign-loading-user');\n }\n }.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Get next user data and store it in global variables\n *\n * @private\n * @method _getNextUser\n * @param {Event} event\n * @param {Object} data Next user's data\n */\n GradingPanel.prototype._getNextUser = function(event, data) {\n this.nextUserId = data.nextUserId;\n this.nextUser = data.nextUser;\n };\n\n /**\n * Handle the save-and-show-next event\n *\n * @private\n * @method _handleSaveAndShowNext\n */\n GradingPanel.prototype._handleSaveAndShowNext = function() {\n this._submitForm(null, this.nextUserId, this.nextUser);\n };\n\n /**\n * Get the grade panel element.\n *\n * @method getPanelElement\n * @return {jQuery}\n */\n GradingPanel.prototype.getPanelElement = function() {\n return $('[data-region=\"grade-panel\"]');\n };\n\n /**\n * Hide the grade panel.\n *\n * @method collapsePanel\n */\n GradingPanel.prototype.collapsePanel = function() {\n this.getPanelElement().addClass('collapsed');\n };\n\n /**\n * Show the grade panel.\n *\n * @method expandPanel\n */\n GradingPanel.prototype.expandPanel = function() {\n this.getPanelElement().removeClass('collapsed');\n };\n\n /**\n * Register event listeners for the grade panel.\n *\n * @method registerEventListeners\n */\n GradingPanel.prototype.registerEventListeners = function() {\n var docElement = $(document);\n var region = $(this._region);\n // Add an event listener to prevent form submission when pressing enter key.\n region.on('submit', 'form', function(e) {\n e.preventDefault();\n });\n\n docElement.on('next-user', this._getNextUser.bind(this));\n docElement.on('user-changed', this._refreshGradingPanel.bind(this));\n docElement.on('save-changes', this._submitForm.bind(this));\n docElement.on('save-and-show-next', this._handleSaveAndShowNext.bind(this));\n docElement.on('reset', this._resetForm.bind(this));\n\n docElement.on('save-form-state', this._saveFormState.bind(this));\n\n docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL, function() {\n this.collapsePanel();\n }.bind(this));\n\n // We should expand if the review panel is collapsed.\n docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL, function() {\n this.expandPanel();\n }.bind(this));\n\n docElement.on(GradingEvents.EXPAND_GRADE_PANEL, function() {\n this.expandPanel();\n }.bind(this));\n };\n\n return GradingPanel;\n});\n"],"names":["define","$","Y","notification","templates","fragment","ajax","str","checker","GradingEvents","FormEvents","Toast","FormChangeChecker","GradingPanel","selector","_regionSelector","_region","_userCache","registerEventListeners","prototype","_lastUserId","_lastAttemptNumber","nextUserId","nextUser","_niceReplaceNodeContents","node","html","js","promise","Deferred","fadeOut","replaceNodeContents","fadeIn","resolve","_saveFormState","checked","prop","val","_submitForm","event","commentAreaElement","document","querySelector","value","click","form","this","find","show","markFormSubmitted","trigger","notifyFormSubmittedByJavascript","data","serialize","assignmentid","attr","call","methodname","args","userid","jsonformdata","JSON","stringify","done","_handleFormSubmissionResponse","bind","fail","exception","formdata","response","length","get_string","then","add","type","delay","catch","resetFormDirtyState","hide","_resetForm","e","unresolvederror","Event","_refreshGradingPanel","_chooseAttempt","submissionsId","target","formhtml","getElementById","clone","wrap","get_strings","key","component","strs","confirm","attemptnumber","_addPopoutButtons","region","render","closest","addClass","parent","append","on","_togglePopout","preventDefault","container","hasClass","removeClass","submissiondata","contextid","window","M","util","js_pending","params","loadFragment","saveFormState","js_complete","_getNextUser","_handleSaveAndShowNext","getPanelElement","collapsePanel","expandPanel","docElement","COLLAPSE_GRADE_PANEL","COLLAPSE_REVIEW_PANEL","EXPAND_GRADE_PANEL"],"mappings":";;;;;;;;AAuBAA,kCAAO,CACH,SACA,WACA,oBACA,iBACA,gBACA,YACA,WACA,yCACA,4BACA,mBACA,aACA,4BACD,SACCC,EACAC,EACAC,aACAC,UACAC,SACAC,KACAC,IACAC,QACAC,cACAC,WACAC,MACAC,uBASIC,aAAe,SAASC,eACnBC,gBAAkBD,cAClBE,QAAUf,EAAEa,eACZG,WAAa,QAEbC,iCAITL,aAAaM,UAAUJ,gBAAkB,KAGzCF,aAAaM,UAAUC,YAAc,EAGrCP,aAAaM,UAAUE,oBAAsB,EAG7CR,aAAaM,UAAUH,QAAU,KAGjCH,aAAaM,UAAUG,WAAa,KAGpCT,aAAaM,UAAUI,UAAW,EAYlCV,aAAaM,UAAUK,yBAA2B,SAASC,KAAMC,KAAMC,QAC/DC,QAAU3B,EAAE4B,kBAEhBJ,KAAKK,QAAQ,QAAQ,WACjB1B,UAAU2B,oBAAoBN,KAAMC,KAAMC,IAC1CF,KAAKO,OAAO,QAAQ,WAChBJ,QAAQK,gBAITL,QAAQA,WAQnBf,aAAaM,UAAUe,eAAiB,eAEhCC,QAAUlC,EAAE,0EAA0EmC,KAAK,WAC/FnC,EAAE,gDAAgDoC,IAAIF,UAa1DtB,aAAaM,UAAUmB,YAAc,SAASC,MAAOjB,WAAYC,cAEzDiB,mBAAqBC,SAASC,cAAc,iBAC5CF,qBAEqC,KADRA,mBAAmBE,cAAc,kBACnCC,OACQH,mBAAmBE,cAAc,qCACvCE,aAK7BC,KAAO5C,EAAE6C,KAAK9B,QAAQ+B,KAAK,mBAE/B9C,EAAE,2BAA2B+C,OAG7BpC,kBAAkBqC,kBAAkBJ,KAAK,IAGzCA,KAAKK,QAAQ,mBAGbxC,WAAWyC,gCAAgCN,KAAK,QAG5CO,KAAOP,KAAKQ,YACZC,aAAeR,KAAK9B,QAAQuC,KAAK,qBAGrCjD,KAAKkD,KAAK,CAAC,CACPC,WAAY,iCACZC,KAAM,CAACJ,aAAcA,aAAcK,OAAQb,KAAK1B,YAAawC,aAAcC,KAAKC,UAAUV,OAC1FW,KAAMjB,KAAKkB,8BAA8BC,KAAKnB,KAAMM,KAAM9B,WAAYC,UACtE2C,KAAM/D,aAAagE,cAc3BtD,aAAaM,UAAU6C,8BAAgC,SAASI,SAAU9C,WAAYC,SAAU8C,kBAClE,IAAf/C,aACPA,WAAawB,KAAK1B,aAElBiD,SAASC,OACT/D,IAAIgE,WAAW,+BAAgC,cAC1CC,MAAK,SAASjE,YACXI,MAAM8D,IAAIlE,IAAK,CAACmE,KAAM,SAAUC,MAAO,MAChCpE,OAEVqE,MAAMzE,aAAagE,WAIxBlE,EAAEwC,UAAUS,QAAQ,QAAS,CAACJ,KAAK1B,YAAagD,UAAU,QACvD,CACH7D,IAAIgE,WAAW,0BAA2B,cACzCC,MAAK,SAASjE,YACXI,MAAM8D,IAAIlE,KACHA,OAEVqE,MAAMzE,aAAagE,eAGhBtB,KAAO5C,EAAE6C,KAAK9B,QAAQ+B,KAAK,mBAC/BnC,kBAAkBiE,oBAAoBhC,KAAK,IAEvCvB,YAAcwB,KAAK1B,YACnBnB,EAAEwC,UAAUS,QAAQ,QAAS5B,YACtBC,SACPtB,EAAEwC,UAAUS,QAAQ,yBAAyB,GAE7CjD,EAAEwC,UAAUS,QAAQ,eAAgB5B,YAG5CrB,EAAE,2BAA2B6E,QAajCjE,aAAaM,UAAU4D,WAAa,SAASC,EAAGrB,OAAQS,SAAUa,qBAE1D1C,MAAQtC,EAAEiF,MAAM,eACC,IAAVvB,SACPA,OAASb,KAAK1B,kBAEbA,YAAc,OACd+D,qBAAqB5C,MAAOoB,OAAQS,UAAW,EAAGa,kBAU3DpE,aAAaM,UAAUiE,eAAiB,SAASJ,OAKzCK,cADOpF,EAAE+E,EAAEM,QACUlC,KAAK,eAG1BmC,SAFkBtF,EAAEwC,SAAS+C,eAAeH,gBACjBI,QACPC,KAAKzF,EAAE,YAAYyB,OAE3CnB,IAAIoF,YAAY,CACZ,CAACC,IAAK,wBAAyBC,UAAW,cAC1C,CAACD,IAAK,OAAQC,UAAW,QACzB,CAACD,IAAK,SAAUC,UAAW,UAC5B9B,KAAK,SAAS+B,MACb3F,aAAa4F,QAAQD,KAAK,GAAIP,SAAUO,KAAK,GAAIA,KAAK,GAAI,eAClDE,cAAgB/F,EAAE,oDAAoDoC,WAErE8C,qBAAqB,KAAMrC,KAAK1B,YAAa,GAAI4E,gBACxD/B,KAAKnB,QACTmB,KAAKnB,OAAOoB,KAAK/D,aAAagE,YAUpCtD,aAAaM,UAAU8E,kBAAoB,SAASnF,cAC5CoF,OAASjG,EAAEa,UAEfV,UAAU+F,OAAO,2BAA4B,IAAIpC,KAAK,SAASrC,MAC7CwE,OAAOnD,KAAK,uFACjBqD,QAAQ,UACTC,SAAS,cAActD,KAAK,eAAeuD,SAASC,OAAO7E,MAEnEwE,OAAOM,GAAG,QAAS,gCAAiC1D,KAAK2D,cAAcxC,KAAKnB,QAC9EmB,KAAKnB,OAAOoB,KAAK/D,aAAagE,YAUpCtD,aAAaM,UAAUsF,cAAgB,SAASlE,OAC5CA,MAAMmE,qBACFC,UAAY1G,EAAEsC,MAAM+C,QAAQc,QAAQ,UACpCO,UAAUC,SAAS,UACnB3G,EAAE,WAAW4G,YAAY,WAEzB5G,EAAE,WAAW4G,YAAY,UACzBF,UAAUN,SAAS,UACnBM,UAAUN,SAAS,uBAe3BxF,aAAaM,UAAUgE,qBAAuB,SAAS5C,MAAOoB,OAAQmD,eAAgBd,cAAef,qBAC7F8B,UAAYjE,KAAK9B,QAAQuC,KAAK,uBACJ,IAAnBuD,iBACPA,eAAiB,SAEQ,IAAlBd,gBACPA,eAAiB,QAEU,IAApBf,kBACPA,iBAAkB,GAGlBnC,KAAK1B,aAAeuC,QAAUb,KAAKzB,oBAAsB2E,eAAoC,KAAnBc,sBAGzE1F,YAAcuC,YACdtC,mBAAqB2E,cAC1B/F,EAAEwC,UAAUS,QAAQ,sBAEpB8D,OAAOC,EAAEC,KAAKC,WAAW,2BAEzB/G,UAAU+F,OAAO,qBAAsB,IAAIpC,KAAK,SAASrC,KAAMC,SAEtDH,yBAAyBsB,KAAK9B,QAASU,KAAMC,IAAIoC,KAAK,cACnDJ,OAAS,EAAG,MACP3C,QAAQgC,WAEToE,OAAS,CAACzD,OAAQA,OAAQqC,cAAeA,cAAepC,aAAcC,KAAKC,UAAUgD,iBACzFzG,SAASgH,aAAa,aAAc,eAAgBN,UAAWK,QAAQrD,KAAK,SAASrC,KAAMC,IAI1E,KAATD,MACAzB,EAAEwC,UAAUS,QAAQ,eAAe,QAGlC1B,yBAAyBsB,KAAK9B,QAASU,KAAMC,IACjDoC,KAAK,WACFvD,QAAQ8G,cAAc,0CACtBrH,EAAEwC,UAAU+D,GAAG,2BAA2B,WAGtChG,QAAQ8G,cAAc,6CAE1BrH,EAAE,mCAAmCuG,GAAG,QAAS1D,KAAKsC,eAAenB,KAAKnB,YACrEmD,kBAAkB,0CACnBhB,iBACAhF,EAAE,0CAA0CmD,KAAK,oBAAoB,GAEzEnD,EAAEwC,UAAUS,QAAQ,uBAEpB8D,OAAOC,EAAEC,KAAKK,YAAY,4BAC5BtD,KAAKnB,OACNoB,KAAK/D,aAAagE,YACrBF,KAAKnB,OAAOoB,KAAK/D,aAAagE,WAChClE,EAAE,gCAAgC+C,iBAE7BhC,QAAQ8D,OACb7E,EAAE,gCAAgC6E,OAClC7E,EAAEwC,UAAUS,QAAQ,uBAEpB8D,OAAOC,EAAEC,KAAKK,YAAY,4BAEhCtD,KAAKnB,QACTmB,KAAKnB,OAAOoB,KAAK/D,aAAagE,aAWpCtD,aAAaM,UAAUqG,aAAe,SAASjF,MAAOa,WAC7C9B,WAAa8B,KAAK9B,gBAClBC,SAAW6B,KAAK7B,UASzBV,aAAaM,UAAUsG,uBAAyB,gBACvCnF,YAAY,KAAMQ,KAAKxB,WAAYwB,KAAKvB,WASjDV,aAAaM,UAAUuG,gBAAkB,kBAC9BzH,EAAE,gCAQbY,aAAaM,UAAUwG,cAAgB,gBAC9BD,kBAAkBrB,SAAS,cAQpCxF,aAAaM,UAAUyG,YAAc,gBAC5BF,kBAAkBb,YAAY,cAQvChG,aAAaM,UAAUD,uBAAyB,eACxC2G,WAAa5H,EAAEwC,UACNxC,EAAE6C,KAAK9B,SAEbwF,GAAG,SAAU,QAAQ,SAASxB,GACjCA,EAAE0B,oBAGNmB,WAAWrB,GAAG,YAAa1D,KAAK0E,aAAavD,KAAKnB,OAClD+E,WAAWrB,GAAG,eAAgB1D,KAAKqC,qBAAqBlB,KAAKnB,OAC7D+E,WAAWrB,GAAG,eAAgB1D,KAAKR,YAAY2B,KAAKnB,OACpD+E,WAAWrB,GAAG,qBAAsB1D,KAAK2E,uBAAuBxD,KAAKnB,OACrE+E,WAAWrB,GAAG,QAAS1D,KAAKiC,WAAWd,KAAKnB,OAE5C+E,WAAWrB,GAAG,kBAAmB1D,KAAKZ,eAAe+B,KAAKnB,OAE1D+E,WAAWrB,GAAG/F,cAAcqH,qBAAsB,gBACzCH,iBACP1D,KAAKnB,OAGP+E,WAAWrB,GAAG/F,cAAcsH,sBAAuB,gBAC1CH,eACP3D,KAAKnB,OAEP+E,WAAWrB,GAAG/F,cAAcuH,mBAAoB,gBACvCJ,eACP3D,KAAKnB,QAGJjC"}actionbar/grading/extra_filters_dropdown.min.js.map000064400000016242152267113500016613 0ustar00{"version":3,"file":"extra_filters_dropdown.min.js","sources":["../../../src/actionbar/grading/extra_filters_dropdown.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 {getDropdownDialog} from 'core/local/dropdown/dialog';\nimport {getUserPreference} from 'core_user/repository';\n\n/**\n * Module for the extra filters dropdown on the submissions page.\n *\n * @module mod_assign/actionbar/grading/extra_filters_dropdown\n * @copyright 2024 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n extraFiltersDropdown: '.dropdown.extrafilters',\n extraFiltersClose: 'a[data-action=\"close\"]',\n workflowFilterElement: 'select[name=\"workflowfilter\"]',\n markerFilterElement: 'select[name=\"markingallocationfilter\"]',\n suspendedParticipantsFilterCheckbox: 'input[type=\"checkbox\"][name=\"suspendedparticipantsfilter\"]',\n suspendedParticipantsFilterHidden: 'input[type=\"hidden\"][name=\"suspendedparticipantsfilter\"]'\n};\n\n/**\n * Register event listeners for the extra filters dropdown.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst registerEventListeners = (extraFiltersDropdown) => {\n // Click event listener to the extra filters dropdown element.\n extraFiltersDropdown.getElement().addEventListener('click', e => {\n // The target is the 'Close' button.\n if (e.target.closest(Selectors.extraFiltersClose)) {\n e.preventDefault();\n extraFiltersDropdown.setVisible(false);\n }\n });\n\n // Change event listener to the extra filters dropdown element.\n extraFiltersDropdown.getElement().addEventListener('change', e => {\n const suspendedParticipantsFilterCheckbox = e.target.closest(Selectors.suspendedParticipantsFilterCheckbox);\n // The target is the 'Suspended participants' filter checkbox.\n if (suspendedParticipantsFilterCheckbox) {\n // The 'Suspended participants' filter uses a hidden input and a checkbox. The hidden input is used to\n // submit '0' as a workaround when the checkbox is unchecked since unchecked checkboxes are not submitted\n // with the form. Therefore, we need to enable or disable the hidden input based on the checkbox state.\n const suspendedParticipantsFilterHidden = suspendedParticipantsFilterCheckbox.parentNode\n .querySelector(Selectors.suspendedParticipantsFilterHidden);\n suspendedParticipantsFilterHidden.disabled = suspendedParticipantsFilterCheckbox.checked;\n }\n });\n\n // Event listener triggered upon hiding of the dropdown.\n extraFiltersDropdown.getElement().addEventListener('hide.bs.dropdown', () => {\n // Restore the filters to their stored preference values once the dropdown is closed.\n restoreAppliedWorkflowFilter(extraFiltersDropdown);\n restoreAppliedMarkerFilter(extraFiltersDropdown);\n restoreAppliedSuspendedParticipantsFilter(extraFiltersDropdown);\n });\n};\n\n/**\n * Restores the currently applied workflow filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedWorkflowFilter = async(extraFiltersDropdown) => {\n const workflowFilterSelect = extraFiltersDropdown.getElement().querySelector(Selectors.workflowFilterElement);\n if (workflowFilterSelect) {\n workflowFilterSelect.value = await getUserPreference('assign_workflowfilter');\n }\n};\n\n/**\n * Restores the currently applied marker filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedMarkerFilter = async(extraFiltersDropdown) => {\n const markerFilterSelect = extraFiltersDropdown.getElement().querySelector(Selectors.markerFilterElement);\n if (markerFilterSelect) {\n markerFilterSelect.value = await getUserPreference('assign_markerfilter');\n }\n};\n\n/**\n * Restores the currently suspended participants filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedSuspendedParticipantsFilter = async(extraFiltersDropdown) => {\n const suspendedParticipantsFilterCheckbox = extraFiltersDropdown.getElement()\n .querySelector(Selectors.suspendedParticipantsFilterCheckbox);\n if (suspendedParticipantsFilterCheckbox) {\n const suspendedParticipantsFilterHidden = suspendedParticipantsFilterCheckbox.parentNode\n .querySelector(Selectors.suspendedParticipantsFilterHidden);\n const showOnlyActiveParticipants = await getUserPreference('grade_report_showonlyactiveenrol');\n suspendedParticipantsFilterCheckbox.checked = !showOnlyActiveParticipants;\n suspendedParticipantsFilterHidden.disabled = !showOnlyActiveParticipants;\n }\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const extraFiltersDropdown = getDropdownDialog(Selectors.extraFiltersDropdown);\n if (extraFiltersDropdown) {\n registerEventListeners(extraFiltersDropdown);\n }\n};\n"],"names":["Selectors","restoreAppliedWorkflowFilter","async","workflowFilterSelect","extraFiltersDropdown","getElement","querySelector","value","restoreAppliedMarkerFilter","markerFilterSelect","restoreAppliedSuspendedParticipantsFilter","suspendedParticipantsFilterCheckbox","suspendedParticipantsFilterHidden","parentNode","showOnlyActiveParticipants","checked","disabled","addEventListener","e","target","closest","preventDefault","setVisible","registerEventListeners"],"mappings":";;;;;;;;MA2BMA,+BACoB,yBADpBA,4BAEiB,yBAFjBA,gCAGqB,gCAHrBA,8BAImB,yCAJnBA,8CAKmC,6DALnCA,4CAMiC,2DA8CjCC,6BAA+BC,MAAAA,6BAC3BC,qBAAuBC,qBAAqBC,aAAaC,cAAcN,iCACzEG,uBACAA,qBAAqBI,YAAc,iCAAkB,2BASvDC,2BAA6BN,MAAAA,6BACzBO,mBAAqBL,qBAAqBC,aAAaC,cAAcN,+BACvES,qBACAA,mBAAmBF,YAAc,iCAAkB,yBASrDG,0CAA4CR,MAAAA,6BACxCS,oCAAsCP,qBAAqBC,aAC5DC,cAAcN,kDACfW,oCAAqC,OAC/BC,kCAAoCD,oCAAoCE,WACzEP,cAAcN,6CACbc,iCAAmC,iCAAkB,oCAC3DH,oCAAoCI,SAAWD,2BAC/CF,kCAAkCI,UAAYF,2CAOlC,WACVV,sBAAuB,6BAAkBJ,gCAC3CI,sBA/EwBA,CAAAA,uBAE5BA,qBAAqBC,aAAaY,iBAAiB,SAASC,IAEpDA,EAAEC,OAAOC,QAAQpB,+BACjBkB,EAAEG,iBACFjB,qBAAqBkB,YAAW,OAKxClB,qBAAqBC,aAAaY,iBAAiB,UAAUC,UACnDP,oCAAsCO,EAAEC,OAAOC,QAAQpB,+CAEzDW,sCAI0CA,oCAAoCE,WACzEP,cAAcN,6CACegB,SAAWL,oCAAoCI,YAKzFX,qBAAqBC,aAAaY,iBAAiB,oBAAoB,KAEnEhB,6BAA6BG,sBAC7BI,2BAA2BJ,sBAC3BM,0CAA0CN,0BAmD1CmB,CAAuBnB"}actionbar/grading/extra_filters_dropdown.min.js000064400000006204152267113500016034 0ustar00define("mod_assign/actionbar/grading/extra_filters_dropdown",["exports","core/local/dropdown/dialog","core_user/repository"],(function(_exports,_dialog,_repository){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0; /** * Module for the extra filters dropdown on the submissions page. * * @module mod_assign/actionbar/grading/extra_filters_dropdown * @copyright 2024 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ const Selectors_extraFiltersDropdown=".dropdown.extrafilters",Selectors_extraFiltersClose='a[data-action="close"]',Selectors_workflowFilterElement='select[name="workflowfilter"]',Selectors_markerFilterElement='select[name="markingallocationfilter"]',Selectors_suspendedParticipantsFilterCheckbox='input[type="checkbox"][name="suspendedparticipantsfilter"]',Selectors_suspendedParticipantsFilterHidden='input[type="hidden"][name="suspendedparticipantsfilter"]',restoreAppliedWorkflowFilter=async extraFiltersDropdown=>{const workflowFilterSelect=extraFiltersDropdown.getElement().querySelector(Selectors_workflowFilterElement);workflowFilterSelect&&(workflowFilterSelect.value=await(0,_repository.getUserPreference)("assign_workflowfilter"))},restoreAppliedMarkerFilter=async extraFiltersDropdown=>{const markerFilterSelect=extraFiltersDropdown.getElement().querySelector(Selectors_markerFilterElement);markerFilterSelect&&(markerFilterSelect.value=await(0,_repository.getUserPreference)("assign_markerfilter"))},restoreAppliedSuspendedParticipantsFilter=async extraFiltersDropdown=>{const suspendedParticipantsFilterCheckbox=extraFiltersDropdown.getElement().querySelector(Selectors_suspendedParticipantsFilterCheckbox);if(suspendedParticipantsFilterCheckbox){const suspendedParticipantsFilterHidden=suspendedParticipantsFilterCheckbox.parentNode.querySelector(Selectors_suspendedParticipantsFilterHidden),showOnlyActiveParticipants=await(0,_repository.getUserPreference)("grade_report_showonlyactiveenrol");suspendedParticipantsFilterCheckbox.checked=!showOnlyActiveParticipants,suspendedParticipantsFilterHidden.disabled=!showOnlyActiveParticipants}};_exports.init=()=>{const extraFiltersDropdown=(0,_dialog.getDropdownDialog)(Selectors_extraFiltersDropdown);extraFiltersDropdown&&(extraFiltersDropdown=>{extraFiltersDropdown.getElement().addEventListener("click",(e=>{e.target.closest(Selectors_extraFiltersClose)&&(e.preventDefault(),extraFiltersDropdown.setVisible(!1))})),extraFiltersDropdown.getElement().addEventListener("change",(e=>{const suspendedParticipantsFilterCheckbox=e.target.closest(Selectors_suspendedParticipantsFilterCheckbox);suspendedParticipantsFilterCheckbox&&(suspendedParticipantsFilterCheckbox.parentNode.querySelector(Selectors_suspendedParticipantsFilterHidden).disabled=suspendedParticipantsFilterCheckbox.checked)})),extraFiltersDropdown.getElement().addEventListener("hide.bs.dropdown",(()=>{restoreAppliedWorkflowFilter(extraFiltersDropdown),restoreAppliedMarkerFilter(extraFiltersDropdown),restoreAppliedSuspendedParticipantsFilter(extraFiltersDropdown)}))})(extraFiltersDropdown)}})); //# sourceMappingURL=extra_filters_dropdown.min.js.mapgrading_navigation_user_info.min.js.map000064400000023406152267113500014352 0ustar00{"version":3,"file":"grading_navigation_user_info.min.js","sources":["../src/grading_navigation_user_info.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript controller for the \"User summary\" panel at the top of the page.\n *\n * @module mod_assign/grading_navigation_user_info\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/notification', 'core/ajax', 'core/templates'], function($, notification, ajax, templates) {\n\n /**\n * UserInfo class.\n *\n * @class mod_assign/grading_navigation_user_info\n * @param {String} selector The selector for the page region containing the user navigation.\n */\n var UserInfo = function(selector) {\n this._regionSelector = selector;\n this._region = $(selector);\n this._userCache = {};\n\n $(document).on('user-changed', this._refreshUserInfo.bind(this));\n };\n\n /** @property {String} Selector for the page region containing the user navigation. */\n UserInfo.prototype._regionSelector = null;\n\n /** @property {Array} Cache of user info contexts. */\n UserInfo.prototype._userCache = null;\n\n /** @property {JQuery} JQuery node for the page region containing the user navigation. */\n UserInfo.prototype._region = null;\n\n /** @property {Integer} Remember the last user id to prevent unnecessary reloads. */\n UserInfo.prototype._lastUserId = 0;\n\n /**\n * Get the assignment id\n *\n * @private\n * @method _getAssignmentId\n * @return {Integer} assignment id\n */\n UserInfo.prototype._getAssignmentId = function() {\n return this._region.attr('data-assignmentid');\n };\n\n /**\n * Get the user context - re-render the template in the page.\n *\n * @private\n * @method _refreshUserInfo\n * @param {Event} event\n * @param {Number} userid\n */\n UserInfo.prototype._refreshUserInfo = function(event, userid) {\n var promise = $.Deferred();\n\n // Put the current user ID in the DOM so yui can access it.\n this._region.attr('data-userid', userid);\n\n // Skip reloading if it is the same user.\n if (this._lastUserId == userid) {\n return;\n }\n this._lastUserId = userid;\n\n // First insert the loading template.\n templates.render('mod_assign/loading', {}).done(function(html, js) {\n // Update the page.\n this._region.fadeOut(\"fast\", function() {\n templates.replaceNodeContents(this._region, html, js);\n this._region.fadeIn(\"fast\");\n }.bind(this));\n\n if (userid < 0) {\n // Render the template.\n templates.render('mod_assign/grading_navigation_no_users', {}).done(function(html, js) {\n if (userid == this._lastUserId) {\n // Update the page.\n this._region.fadeOut(\"fast\", function() {\n templates.replaceNodeContents(this._region, html, js);\n this._region.fadeIn(\"fast\");\n }.bind(this));\n }\n }.bind(this)).fail(notification.exception);\n return;\n }\n\n if (typeof this._userCache[userid] !== \"undefined\") {\n promise.resolve(this._userCache[userid]);\n } else {\n // Load context from ajax.\n var assignmentId = this._getAssignmentId();\n var requests = ajax.call([{\n methodname: 'mod_assign_get_participant',\n args: {\n userid: userid,\n assignid: assignmentId,\n embeduser: true\n }\n }]);\n\n requests[0].done(function(participant) {\n if (!participant.hasOwnProperty('id')) {\n promise.reject('No users');\n } else {\n this._userCache[userid] = participant;\n promise.resolve(this._userCache[userid]);\n }\n }.bind(this)).fail(notification.exception);\n }\n\n promise.done(function(context) {\n var identityfields = $('[data-showuseridentity]').data('showuseridentity').split(','),\n identity = [];\n // Render the template.\n context.courseid = $('[data-region=\"grading-navigation-panel\"]').attr('data-courseid');\n\n if (context.user) {\n // Build a string for the visible identity fields listed in showuseridentity config setting.\n $.each(identityfields, function(i, k) {\n if (typeof context.user[k] !== 'undefined' && context.user[k] !== '') {\n context.hasidentity = true;\n identity.push(context.user[k]);\n }\n });\n context.identity = identity.join(', ');\n\n // Add profile image url to context.\n if (context.user.profileimageurl) {\n context.profileimageurl = context.user.profileimageurl;\n }\n }\n\n templates.render('mod_assign/grading_navigation_user_summary', context).done(function(html, js) {\n // Update the page.\n if (userid == this._lastUserId) {\n this._region.fadeOut(\"fast\", function() {\n templates.replaceNodeContents(this._region, html, js);\n this._region.fadeIn(\"fast\");\n }.bind(this));\n }\n }.bind(this)).fail(notification.exception);\n }.bind(this)).fail(function() {\n // Render the template.\n templates.render('mod_assign/grading_navigation_no_users', {}).done(function(html, js) {\n // Update the page.\n this._region.fadeOut(\"fast\", function() {\n templates.replaceNodeContents(this._region, html, js);\n this._region.fadeIn(\"fast\");\n }.bind(this));\n }.bind(this)).fail(notification.exception);\n }\n .bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n return UserInfo;\n});\n"],"names":["define","$","notification","ajax","templates","UserInfo","selector","_regionSelector","_region","_userCache","document","on","this","_refreshUserInfo","bind","prototype","_lastUserId","_getAssignmentId","attr","event","userid","promise","Deferred","render","done","html","js","fadeOut","replaceNodeContents","fadeIn","fail","exception","resolve","assignmentId","call","methodname","args","assignid","embeduser","participant","hasOwnProperty","reject","context","identityfields","data","split","identity","courseid","user","each","i","k","hasidentity","push","join","profileimageurl"],"mappings":";;;;;;;;AAuBAA,iDAAO,CAAC,SAAU,oBAAqB,YAAa,mBAAmB,SAASC,EAAGC,aAAcC,KAAMC,eAQ/FC,SAAW,SAASC,eACfC,gBAAkBD,cAClBE,QAAUP,EAAEK,eACZG,WAAa,GAElBR,EAAES,UAAUC,GAAG,eAAgBC,KAAKC,iBAAiBC,KAAKF,eAI9DP,SAASU,UAAUR,gBAAkB,KAGrCF,SAASU,UAAUN,WAAa,KAGhCJ,SAASU,UAAUP,QAAU,KAG7BH,SAASU,UAAUC,YAAc,EASjCX,SAASU,UAAUE,iBAAmB,kBAC3BL,KAAKJ,QAAQU,KAAK,sBAW7Bb,SAASU,UAAUF,iBAAmB,SAASM,MAAOC,YAC9CC,QAAUpB,EAAEqB,gBAGXd,QAAQU,KAAK,cAAeE,QAG7BR,KAAKI,aAAeI,cAGnBJ,YAAcI,OAGnBhB,UAAUmB,OAAO,qBAAsB,IAAIC,KAAK,SAASC,KAAMC,YAEtDlB,QAAQmB,QAAQ,OAAQ,WACzBvB,UAAUwB,oBAAoBhB,KAAKJ,QAASiB,KAAMC,SAC7ClB,QAAQqB,OAAO,SACtBf,KAAKF,OAEHQ,OAAS,EAEThB,UAAUmB,OAAO,yCAA0C,IAAIC,KAAK,SAASC,KAAMC,IAC3EN,QAAUR,KAAKI,kBAEVR,QAAQmB,QAAQ,OAAQ,WACzBvB,UAAUwB,oBAAoBhB,KAAKJ,QAASiB,KAAMC,SAC7ClB,QAAQqB,OAAO,SACtBf,KAAKF,QAEbE,KAAKF,OAAOkB,KAAK5B,aAAa6B,wBAIG,IAA5BnB,KAAKH,WAAWW,QACvBC,QAAQW,QAAQpB,KAAKH,WAAWW,aAC7B,KAECa,aAAerB,KAAKK,mBACTd,KAAK+B,KAAK,CAAC,CACtBC,WAAY,6BACZC,KAAM,CACFhB,OAAQA,OACRiB,SAAUJ,aACVK,WAAW,MAIV,GAAGd,KAAK,SAASe,aACjBA,YAAYC,eAAe,YAGvB/B,WAAWW,QAAUmB,YAC1BlB,QAAQW,QAAQpB,KAAKH,WAAWW,UAHhCC,QAAQoB,OAAO,aAKrB3B,KAAKF,OAAOkB,KAAK5B,aAAa6B,WAGpCV,QAAQG,KAAK,SAASkB,aACdC,eAAiB1C,EAAE,2BAA2B2C,KAAK,oBAAoBC,MAAM,KAC7EC,SAAW,GAEfJ,QAAQK,SAAW9C,EAAE,4CAA4CiB,KAAK,iBAElEwB,QAAQM,OAER/C,EAAEgD,KAAKN,gBAAgB,SAASO,EAAGC,QACA,IAApBT,QAAQM,KAAKG,IAA0C,KAApBT,QAAQM,KAAKG,KACvDT,QAAQU,aAAc,EACtBN,SAASO,KAAKX,QAAQM,KAAKG,QAGnCT,QAAQI,SAAWA,SAASQ,KAAK,MAG7BZ,QAAQM,KAAKO,kBACbb,QAAQa,gBAAkBb,QAAQM,KAAKO,kBAI/CnD,UAAUmB,OAAO,6CAA8CmB,SAASlB,KAAK,SAASC,KAAMC,IAEpFN,QAAUR,KAAKI,kBACVR,QAAQmB,QAAQ,OAAQ,WACzBvB,UAAUwB,oBAAoBhB,KAAKJ,QAASiB,KAAMC,SAC7ClB,QAAQqB,OAAO,SACtBf,KAAKF,QAEbE,KAAKF,OAAOkB,KAAK5B,aAAa6B,YAClCjB,KAAKF,OAAOkB,KAAK,WAEf1B,UAAUmB,OAAO,yCAA0C,IAAIC,KAAK,SAASC,KAAMC,SAE1ElB,QAAQmB,QAAQ,OAAQ,WACzBvB,UAAUwB,oBAAoBhB,KAAKJ,QAASiB,KAAMC,SAC7ClB,QAAQqB,OAAO,SACtBf,KAAKF,QACTE,KAAKF,OAAOkB,KAAK5B,aAAa6B,YAEnCjB,KAAKF,SACRE,KAAKF,OAAOkB,KAAK5B,aAAa6B,aAG7B1B"}grading_actions.min.js000064400000010015152267113500011016 0ustar00/** * Javascript controller for the "Actions" panel at the bottom of the page. * * @module mod_assign/grading_actions * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_assign/grading_actions",["jquery","mod_assign/grading_events"],(function($,GradingEvents){var GradingActions=function(selector){this._regionSelector=selector,this._region=$(selector),this.registerEventListeners()};return GradingActions.prototype._regionSelector=null,GradingActions.prototype._lastUserId=0,GradingActions.prototype._region=null,GradingActions.prototype._showActionsForm=function(event,userid){var form=this._region.find("[data-region=grading-actions-form]");userid!=this._lastUserId&&userid>0&&(this._lastUserId=userid),userid>0?form.removeClass("hide"):form.addClass("hide")},GradingActions.prototype._trigger=function(action){$(document).trigger(action)},GradingActions.prototype.getReviewPanelElement=function(){return $('[data-region="review-panel"]')},GradingActions.prototype.hasReviewPanelElement=function(){return this.getReviewPanelElement().length>0},GradingActions.prototype.getCollapseGradePanelButton=function(){return $('[data-region="grade-actions"] .collapse-grade-panel')},GradingActions.prototype.getCollapseReviewPanelButton=function(){return $('[data-region="grade-actions"] .collapse-review-panel')},GradingActions.prototype.getExpandAllPanelsButton=function(){return $('[data-region="grade-actions"] .collapse-none')},GradingActions.prototype.resetLayoutButtons=function(){this.getCollapseGradePanelButton().removeClass("active"),this.getCollapseReviewPanelButton().removeClass("active"),this.getExpandAllPanelsButton().removeClass("active")},GradingActions.prototype.collapseReviewPanel=function(){$(document).trigger(GradingEvents.COLLAPSE_REVIEW_PANEL),$(document).trigger(GradingEvents.EXPAND_GRADE_PANEL),this.resetLayoutButtons(),this.getCollapseReviewPanelButton().addClass("active")},GradingActions.prototype.collapseGradePanel=function(){$(document).trigger(GradingEvents.COLLAPSE_GRADE_PANEL),$(document).trigger(GradingEvents.EXPAND_REVIEW_PANEL),this.resetLayoutButtons(),this.getCollapseGradePanelButton().addClass("active")},GradingActions.prototype.expandAllPanels=function(){$(document).trigger(GradingEvents.EXPAND_GRADE_PANEL),$(document).trigger(GradingEvents.EXPAND_REVIEW_PANEL),this.resetLayoutButtons(),this.getExpandAllPanelsButton().addClass("active")},GradingActions.prototype.registerEventListeners=function(){if(this.hasReviewPanelElement()){var collapseReviewPanelButton=this.getCollapseReviewPanelButton();collapseReviewPanelButton.click(function(e){this.collapseReviewPanel(),e.preventDefault()}.bind(this)),collapseReviewPanelButton.keydown(function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||13!==e.keyCode&&32!==e.keyCode||(this.collapseReviewPanel(),e.preventDefault())}.bind(this));var collapseGradePanelButton=this.getCollapseGradePanelButton();collapseGradePanelButton.click(function(e){this.collapseGradePanel(),e.preventDefault()}.bind(this)),collapseGradePanelButton.keydown(function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||13!==e.keyCode&&32!==e.keyCode||(this.collapseGradePanel(),e.preventDefault())}.bind(this));var expandAllPanelsButton=this.getExpandAllPanelsButton();expandAllPanelsButton.click(function(e){this.expandAllPanels(),e.preventDefault()}.bind(this)),expandAllPanelsButton.keydown(function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||13!==e.keyCode&&32!==e.keyCode||(this.expandAllPanels(),e.preventDefault())}.bind(this))}$(document).on("user-changed",this._showActionsForm.bind(this)),this._region.find('[name="savechanges"]').on("click",this._trigger.bind(this,"save-changes")),this._region.find('[name="saveandshownext"]').on("click",this._trigger.bind(this,"save-and-show-next")),this._region.find('[name="resetbutton"]').on("click",this._trigger.bind(this,"reset")),this._region.find("form").on("submit",(function(e){e.preventDefault()}))},GradingActions})); //# sourceMappingURL=grading_actions.min.js.mapgrading_panel.min.js000064400000017522152267113500010467 0ustar00/** * Javascript controller for the "Grading" panel at the right of the page. * * @module mod_assign/grading_panel * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_assign/grading_panel",["jquery","core/yui","core/notification","core/templates","core/fragment","core/ajax","core/str","mod_assign/grading_form_change_checker","mod_assign/grading_events","core_form/events","core/toast","core_form/changechecker"],(function($,Y,notification,templates,fragment,ajax,str,checker,GradingEvents,FormEvents,Toast,FormChangeChecker){var GradingPanel=function(selector){this._regionSelector=selector,this._region=$(selector),this._userCache=[],this.registerEventListeners()};return GradingPanel.prototype._regionSelector=null,GradingPanel.prototype._lastUserId=0,GradingPanel.prototype._lastAttemptNumber=-1,GradingPanel.prototype._region=null,GradingPanel.prototype.nextUserId=null,GradingPanel.prototype.nextUser=!1,GradingPanel.prototype._niceReplaceNodeContents=function(node,html,js){var promise=$.Deferred();return node.fadeOut("fast",(function(){templates.replaceNodeContents(node,html,js),node.fadeIn("fast",(function(){promise.resolve()}))})),promise.promise()},GradingPanel.prototype._saveFormState=function(){var checked=$('[data-region="grading-actions-form"] [name="sendstudentnotifications"]').prop("checked");$('.gradeform [name="sendstudentnotifications"]').val(checked)},GradingPanel.prototype._submitForm=function(event,nextUserId,nextUser){var commentAreaElement=document.querySelector(".comment-area");commentAreaElement&&(""!==commentAreaElement.querySelector(".db > textarea").value&&commentAreaElement.querySelector('.fd a[id^="comment-action-post-"]').click());var form=$(this._region.find("form.gradeform"));$('[data-region="overlay"]').show(),FormChangeChecker.markFormSubmitted(form[0]),form.trigger("save-form-state"),FormEvents.notifyFormSubmittedByJavascript(form[0]);var data=form.serialize(),assignmentid=this._region.attr("data-assignmentid");ajax.call([{methodname:"mod_assign_submit_grading_form",args:{assignmentid:assignmentid,userid:this._lastUserId,jsonformdata:JSON.stringify(data)},done:this._handleFormSubmissionResponse.bind(this,data,nextUserId,nextUser),fail:notification.exception}])},GradingPanel.prototype._handleFormSubmissionResponse=function(formdata,nextUserId,nextUser,response){if(void 0===nextUserId&&(nextUserId=this._lastUserId),response.length)str.get_string("errorgradechangessaveddetail","mod_assign").then((function(str){return Toast.add(str,{type:"danger",delay:4e3}),str})).catch(notification.exception),$(document).trigger("reset",[this._lastUserId,formdata,!0]);else{str.get_string("gradechangessaveddetail","mod_assign").then((function(str){return Toast.add(str),str})).catch(notification.exception);var form=$(this._region.find("form.gradeform"));FormChangeChecker.resetFormDirtyState(form[0]),nextUserId==this._lastUserId?$(document).trigger("reset",nextUserId):nextUser?$(document).trigger("done-saving-show-next",!0):$(document).trigger("user-changed",nextUserId)}$('[data-region="overlay"]').hide()},GradingPanel.prototype._resetForm=function(e,userid,formdata,unresolvederror){var event=$.Event("custom");void 0===userid&&(userid=this._lastUserId),this._lastUserId=0,this._refreshGradingPanel(event,userid,formdata,-1,unresolvederror)},GradingPanel.prototype._chooseAttempt=function(e){var submissionsId=$(e.target).data("submissions"),formhtml=$(document.getElementById(submissionsId)).clone().wrap($("")).html();str.get_strings([{key:"viewadifferentattempt",component:"mod_assign"},{key:"view",component:"core"},{key:"cancel",component:"core"}]).done(function(strs){notification.confirm(strs[0],formhtml,strs[1],strs[2],function(){var attemptnumber=$("input:radio[name='select-attemptnumber']:checked").val();this._refreshGradingPanel(null,this._lastUserId,"",attemptnumber)}.bind(this))}.bind(this)).fail(notification.exception)},GradingPanel.prototype._addPopoutButtons=function(selector){var region=$(selector);templates.render("mod_assign/popout_button",{}).done(function(html){region.find('[data-fieldtype="filemanager"],[data-fieldtype="editor"],[data-fieldtype="grading"]').closest(".fitem").addClass("has-popout").find("label:first").parent().append(html),region.on("click",'[data-region="popout-button"]',this._togglePopout.bind(this))}.bind(this)).fail(notification.exception)},GradingPanel.prototype._togglePopout=function(event){event.preventDefault();var container=$(event.target).closest(".fitem");container.hasClass("popout")?$(".popout").removeClass("popout"):($(".popout").removeClass("popout"),container.addClass("popout"),container.addClass("moodle-has-zindex"))},GradingPanel.prototype._refreshGradingPanel=function(event,userid,submissiondata,attemptnumber,unresolvederror){var contextid=this._region.attr("data-contextid");void 0===submissiondata&&(submissiondata=""),void 0===attemptnumber&&(attemptnumber=-1),void 0===unresolvederror&&(unresolvederror=!1),this._lastUserId==userid&&this._lastAttemptNumber==attemptnumber&&""===submissiondata||(this._lastUserId=userid,this._lastAttemptNumber=attemptnumber,$(document).trigger("start-loading-user"),window.M.util.js_pending("mod-assign-loading-user"),templates.render("mod_assign/loading",{}).done(function(html,js){this._niceReplaceNodeContents(this._region,html,js).done(function(){if(userid>0){this._region.show();var params={userid:userid,attemptnumber:attemptnumber,jsonformdata:JSON.stringify(submissiondata)};fragment.loadFragment("mod_assign","gradingpanel",contextid,params).done(function(html,js){""===html&&$(document).trigger("reset-table",!0),this._niceReplaceNodeContents(this._region,html,js).done(function(){checker.saveFormState('[data-region="grade-panel"] .gradeform'),$(document).on("editor-content-restored",(function(){checker.saveFormState('[data-region="grade-panel"] .gradeform')})),$('[data-region="attempt-chooser"]').on("click",this._chooseAttempt.bind(this)),this._addPopoutButtons('[data-region="grade-panel"] .gradeform'),unresolvederror&&$('[data-region="grade-panel"] .gradeform').data("unresolved-error",!0),$(document).trigger("finish-loading-user"),window.M.util.js_complete("mod-assign-loading-user")}.bind(this)).fail(notification.exception)}.bind(this)).fail(notification.exception),$('[data-region="review-panel"]').show()}else this._region.hide(),$('[data-region="review-panel"]').hide(),$(document).trigger("finish-loading-user"),window.M.util.js_complete("mod-assign-loading-user")}.bind(this))}.bind(this)).fail(notification.exception))},GradingPanel.prototype._getNextUser=function(event,data){this.nextUserId=data.nextUserId,this.nextUser=data.nextUser},GradingPanel.prototype._handleSaveAndShowNext=function(){this._submitForm(null,this.nextUserId,this.nextUser)},GradingPanel.prototype.getPanelElement=function(){return $('[data-region="grade-panel"]')},GradingPanel.prototype.collapsePanel=function(){this.getPanelElement().addClass("collapsed")},GradingPanel.prototype.expandPanel=function(){this.getPanelElement().removeClass("collapsed")},GradingPanel.prototype.registerEventListeners=function(){var docElement=$(document);$(this._region).on("submit","form",(function(e){e.preventDefault()})),docElement.on("next-user",this._getNextUser.bind(this)),docElement.on("user-changed",this._refreshGradingPanel.bind(this)),docElement.on("save-changes",this._submitForm.bind(this)),docElement.on("save-and-show-next",this._handleSaveAndShowNext.bind(this)),docElement.on("reset",this._resetForm.bind(this)),docElement.on("save-form-state",this._saveFormState.bind(this)),docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL,function(){this.collapsePanel()}.bind(this)),docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL,function(){this.expandPanel()}.bind(this)),docElement.on(GradingEvents.EXPAND_GRADE_PANEL,function(){this.expandPanel()}.bind(this))},GradingPanel})); //# sourceMappingURL=grading_panel.min.js.mapgrading_events.min.js.map000064400000003011152267113500011434 0ustar00{"version":3,"file":"grading_events.min.js","sources":["../src/grading_events.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 * Events for the grading interface.\n *\n * @module mod_assign/grading_events\n * @copyright 2016 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(function() {\n return {\n COLLAPSE_REVIEW_PANEL: 'grading:collapse-review-panel',\n EXPAND_REVIEW_PANEL: 'grading:expand-review-panel',\n COLLAPSE_GRADE_PANEL: 'grading:collapse-grade-panel',\n EXPAND_GRADE_PANEL: 'grading:expand-grade-panel',\n };\n});\n"],"names":["define","COLLAPSE_REVIEW_PANEL","EXPAND_REVIEW_PANEL","COLLAPSE_GRADE_PANEL","EXPAND_GRADE_PANEL"],"mappings":";;;;;;;;AAuBAA,oCAAO,iBACI,CACHC,sBAAuB,gCACvBC,oBAAqB,8BACrBC,qBAAsB,+BACtBC,mBAAoB"}grading_actions.min.js.map000064400000026232152267113500011602 0ustar00{"version":3,"file":"grading_actions.min.js","sources":["../src/grading_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\n/**\n * Javascript controller for the \"Actions\" panel at the bottom of the page.\n *\n * @module mod_assign/grading_actions\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'mod_assign/grading_events'], function($, GradingEvents) {\n\n /**\n * GradingActions class.\n *\n * @class mod_assign/grading_actions\n * @param {String} selector The selector for the page region containing the actions panel.\n */\n var GradingActions = function(selector) {\n this._regionSelector = selector;\n this._region = $(selector);\n\n this.registerEventListeners();\n };\n\n /** @property {String} Selector for the page region containing the user navigation. */\n GradingActions.prototype._regionSelector = null;\n\n /** @property {Integer} Remember the last user id to prevent unnessecary reloads. */\n GradingActions.prototype._lastUserId = 0;\n\n /** @property {JQuery} JQuery node for the page region containing the user navigation. */\n GradingActions.prototype._region = null;\n\n /**\n * Show the actions if there is valid user.\n *\n * @method _showActionsForm\n * @private\n * @param {Event} event\n * @param {Integer} userid\n */\n GradingActions.prototype._showActionsForm = function(event, userid) {\n var form = this._region.find('[data-region=grading-actions-form]');\n\n if (userid != this._lastUserId && userid > 0) {\n this._lastUserId = userid;\n }\n if (userid > 0) {\n form.removeClass('hide');\n } else {\n form.addClass('hide');\n }\n\n };\n\n /**\n * Trigger the named action.\n *\n * @method _trigger\n * @private\n * @param {String} action\n */\n GradingActions.prototype._trigger = function(action) {\n $(document).trigger(action);\n };\n\n /**\n * Get the review panel element.\n *\n * @method getReviewPanelElement\n * @return {jQuery}\n */\n GradingActions.prototype.getReviewPanelElement = function() {\n return $('[data-region=\"review-panel\"]');\n };\n\n /**\n * Check if the page has a review panel.\n *\n * @method hasReviewPanelElement\n * @return {bool}\n */\n GradingActions.prototype.hasReviewPanelElement = function() {\n return this.getReviewPanelElement().length > 0;\n };\n\n /**\n * Get the collapse grade panel button.\n *\n * @method getCollapseGradePanelButton\n * @return {jQuery}\n */\n GradingActions.prototype.getCollapseGradePanelButton = function() {\n return $('[data-region=\"grade-actions\"] .collapse-grade-panel');\n };\n\n /**\n * Get the collapse review panel button.\n *\n * @method getCollapseReviewPanelButton\n * @return {jQuery}\n */\n GradingActions.prototype.getCollapseReviewPanelButton = function() {\n return $('[data-region=\"grade-actions\"] .collapse-review-panel');\n };\n\n /**\n * Get the expand all panels button.\n *\n * @method getExpandAllPanelsButton\n * @return {jQuery}\n */\n GradingActions.prototype.getExpandAllPanelsButton = function() {\n return $('[data-region=\"grade-actions\"] .collapse-none');\n };\n\n /**\n * Remove the active state from all layout buttons.\n *\n * @method resetLayoutButtons\n */\n GradingActions.prototype.resetLayoutButtons = function() {\n this.getCollapseGradePanelButton().removeClass('active');\n this.getCollapseReviewPanelButton().removeClass('active');\n this.getExpandAllPanelsButton().removeClass('active');\n };\n\n /**\n * Hide the review panel.\n *\n * @method collapseReviewPanel\n */\n GradingActions.prototype.collapseReviewPanel = function() {\n $(document).trigger(GradingEvents.COLLAPSE_REVIEW_PANEL);\n $(document).trigger(GradingEvents.EXPAND_GRADE_PANEL);\n this.resetLayoutButtons();\n this.getCollapseReviewPanelButton().addClass('active');\n };\n\n /**\n * Show/Hide the grade panel.\n *\n * @method collapseGradePanel\n */\n GradingActions.prototype.collapseGradePanel = function() {\n $(document).trigger(GradingEvents.COLLAPSE_GRADE_PANEL);\n $(document).trigger(GradingEvents.EXPAND_REVIEW_PANEL);\n this.resetLayoutButtons();\n this.getCollapseGradePanelButton().addClass('active');\n };\n\n /**\n * Return the layout to default.\n *\n * @method expandAllPanels\n */\n GradingActions.prototype.expandAllPanels = function() {\n $(document).trigger(GradingEvents.EXPAND_GRADE_PANEL);\n $(document).trigger(GradingEvents.EXPAND_REVIEW_PANEL);\n this.resetLayoutButtons();\n this.getExpandAllPanelsButton().addClass('active');\n };\n\n /**\n * Register event listeners for the grade panel.\n *\n * @method registerEventListeners\n */\n GradingActions.prototype.registerEventListeners = function() {\n // Don't need layout controls if there is no review panel.\n if (this.hasReviewPanelElement()) {\n var collapseReviewPanelButton = this.getCollapseReviewPanelButton();\n collapseReviewPanelButton.click(function(e) {\n this.collapseReviewPanel();\n e.preventDefault();\n }.bind(this));\n\n collapseReviewPanelButton.keydown(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode === 13 || e.keyCode === 32) {\n this.collapseReviewPanel();\n e.preventDefault();\n }\n }\n }.bind(this));\n\n var collapseGradePanelButton = this.getCollapseGradePanelButton();\n collapseGradePanelButton.click(function(e) {\n this.collapseGradePanel();\n e.preventDefault();\n }.bind(this));\n\n collapseGradePanelButton.keydown(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode === 13 || e.keyCode === 32) {\n this.collapseGradePanel();\n e.preventDefault();\n }\n }\n }.bind(this));\n\n var expandAllPanelsButton = this.getExpandAllPanelsButton();\n expandAllPanelsButton.click(function(e) {\n this.expandAllPanels();\n e.preventDefault();\n }.bind(this));\n\n expandAllPanelsButton.keydown(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode === 13 || e.keyCode === 32) {\n this.expandAllPanels();\n e.preventDefault();\n }\n }\n }.bind(this));\n }\n\n $(document).on('user-changed', this._showActionsForm.bind(this));\n\n this._region.find('[name=\"savechanges\"]').on('click', this._trigger.bind(this, 'save-changes'));\n this._region.find('[name=\"saveandshownext\"]').on('click', this._trigger.bind(this, 'save-and-show-next'));\n this._region.find('[name=\"resetbutton\"]').on('click', this._trigger.bind(this, 'reset'));\n this._region.find('form').on('submit', function(e) {\n e.preventDefault();\n });\n };\n\n return GradingActions;\n});\n"],"names":["define","$","GradingEvents","GradingActions","selector","_regionSelector","_region","registerEventListeners","prototype","_lastUserId","_showActionsForm","event","userid","form","this","find","removeClass","addClass","_trigger","action","document","trigger","getReviewPanelElement","hasReviewPanelElement","length","getCollapseGradePanelButton","getCollapseReviewPanelButton","getExpandAllPanelsButton","resetLayoutButtons","collapseReviewPanel","COLLAPSE_REVIEW_PANEL","EXPAND_GRADE_PANEL","collapseGradePanel","COLLAPSE_GRADE_PANEL","EXPAND_REVIEW_PANEL","expandAllPanels","collapseReviewPanelButton","click","e","preventDefault","bind","keydown","metaKey","shiftKey","altKey","ctrlKey","keyCode","collapseGradePanelButton","expandAllPanelsButton","on"],"mappings":";;;;;;;;AAuBAA,oCAAO,CAAC,SAAU,8BAA8B,SAASC,EAAGC,mBAQpDC,eAAiB,SAASC,eACrBC,gBAAkBD,cAClBE,QAAUL,EAAEG,eAEZG,iCAITJ,eAAeK,UAAUH,gBAAkB,KAG3CF,eAAeK,UAAUC,YAAc,EAGvCN,eAAeK,UAAUF,QAAU,KAUnCH,eAAeK,UAAUE,iBAAmB,SAASC,MAAOC,YACpDC,KAAOC,KAAKR,QAAQS,KAAK,sCAEzBH,QAAUE,KAAKL,aAAeG,OAAS,SAClCH,YAAcG,QAEnBA,OAAS,EACTC,KAAKG,YAAY,QAEjBH,KAAKI,SAAS,SAYtBd,eAAeK,UAAUU,SAAW,SAASC,QACzClB,EAAEmB,UAAUC,QAAQF,SASxBhB,eAAeK,UAAUc,sBAAwB,kBACtCrB,EAAE,iCASbE,eAAeK,UAAUe,sBAAwB,kBACtCT,KAAKQ,wBAAwBE,OAAS,GASjDrB,eAAeK,UAAUiB,4BAA8B,kBAC5CxB,EAAE,wDASbE,eAAeK,UAAUkB,6BAA+B,kBAC7CzB,EAAE,yDASbE,eAAeK,UAAUmB,yBAA2B,kBACzC1B,EAAE,iDAQbE,eAAeK,UAAUoB,mBAAqB,gBACrCH,8BAA8BT,YAAY,eAC1CU,+BAA+BV,YAAY,eAC3CW,2BAA2BX,YAAY,WAQhDb,eAAeK,UAAUqB,oBAAsB,WAC3C5B,EAAEmB,UAAUC,QAAQnB,cAAc4B,uBAClC7B,EAAEmB,UAAUC,QAAQnB,cAAc6B,yBAC7BH,0BACAF,+BAA+BT,SAAS,WAQjDd,eAAeK,UAAUwB,mBAAqB,WAC1C/B,EAAEmB,UAAUC,QAAQnB,cAAc+B,sBAClChC,EAAEmB,UAAUC,QAAQnB,cAAcgC,0BAC7BN,0BACAH,8BAA8BR,SAAS,WAQhDd,eAAeK,UAAU2B,gBAAkB,WACvClC,EAAEmB,UAAUC,QAAQnB,cAAc6B,oBAClC9B,EAAEmB,UAAUC,QAAQnB,cAAcgC,0BAC7BN,0BACAD,2BAA2BV,SAAS,WAQ7Cd,eAAeK,UAAUD,uBAAyB,cAE1CO,KAAKS,wBAAyB,KAC1Ba,0BAA4BtB,KAAKY,+BACrCU,0BAA0BC,MAAM,SAASC,QAChCT,sBACLS,EAAEC,kBACJC,KAAK1B,OAEPsB,0BAA0BK,QAAQ,SAASH,GAClCA,EAAEI,SAAYJ,EAAEK,UAAaL,EAAEM,QAAWN,EAAEO,SAC3B,KAAdP,EAAEQ,SAAgC,KAAdR,EAAEQ,eACjBjB,sBACLS,EAAEC,mBAGZC,KAAK1B,WAEHiC,yBAA2BjC,KAAKW,8BACpCsB,yBAAyBV,MAAM,SAASC,QAC/BN,qBACLM,EAAEC,kBACJC,KAAK1B,OAEPiC,yBAAyBN,QAAQ,SAASH,GACjCA,EAAEI,SAAYJ,EAAEK,UAAaL,EAAEM,QAAWN,EAAEO,SAC3B,KAAdP,EAAEQ,SAAgC,KAAdR,EAAEQ,eACjBd,qBACLM,EAAEC,mBAGZC,KAAK1B,WAEHkC,sBAAwBlC,KAAKa,2BACjCqB,sBAAsBX,MAAM,SAASC,QAC5BH,kBACLG,EAAEC,kBACJC,KAAK1B,OAEPkC,sBAAsBP,QAAQ,SAASH,GAC9BA,EAAEI,SAAYJ,EAAEK,UAAaL,EAAEM,QAAWN,EAAEO,SAC3B,KAAdP,EAAEQ,SAAgC,KAAdR,EAAEQ,eACjBX,kBACLG,EAAEC,mBAGZC,KAAK1B,OAGXb,EAAEmB,UAAU6B,GAAG,eAAgBnC,KAAKJ,iBAAiB8B,KAAK1B,YAErDR,QAAQS,KAAK,wBAAwBkC,GAAG,QAASnC,KAAKI,SAASsB,KAAK1B,KAAM,sBAC1ER,QAAQS,KAAK,4BAA4BkC,GAAG,QAASnC,KAAKI,SAASsB,KAAK1B,KAAM,4BAC9ER,QAAQS,KAAK,wBAAwBkC,GAAG,QAASnC,KAAKI,SAASsB,KAAK1B,KAAM,eAC1ER,QAAQS,KAAK,QAAQkC,GAAG,UAAU,SAASX,GAC5CA,EAAEC,qBAIHpC"}override_delete_modal.min.js.map000064400000012455152267113500012766 0ustar00{"version":3,"file":"override_delete_modal.min.js","sources":["../src/override_delete_modal.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 * Modal for deleting an override with the option to recalculate penalties.\n *\n * @module `mod_assign/override_delete_modal\n * @copyright 2025 Catalyst IT Australia Pty Ltd\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport Config from 'core/config';\nimport Modal from 'core/modal';\n\nconst SELECTORS = {\n DELETE_BUTTONS: '.delete-override',\n RECACULATION_CHECKBOX: '#recalculatepenalties',\n};\n\n/**\n * Custom Modal\n */\nexport default class OverrideDeleteModal extends Modal {\n static TYPE = \"mod_assign/override_delete_modal\";\n static TEMPLATE = \"mod_assign/override_delete_modal\";\n\n /**\n * Configure the modal.\n *\n * @param {Object} modalConfig\n */\n configure(modalConfig) {\n // Add question modals are always large.\n modalConfig.large = true;\n\n // Always show on creation.\n modalConfig.show = true;\n modalConfig.removeOnClose = true;\n\n // Apply standard configuration.\n super.configure(modalConfig);\n\n this.setOverrideId(modalConfig.overrideId);\n this.setSessionKey(modalConfig.sessionKey);\n }\n\n /**\n * Constructor.\n * Set required data to null.\n *\n * @param {HTMLElement} root\n */\n constructor(root) {\n super(root);\n\n // Recalculate penalties checkbox.\n this.recalculationCheckbox = this.getModal().find(SELECTORS.RECACULATION_CHECKBOX);\n\n // Data.\n this.setOverrideId(null);\n this.setSessionKey(null);\n }\n\n /**\n * Set the override id.\n *\n * @param {number} id The override id.\n */\n setOverrideId(id) {\n this.overrideId = id;\n }\n\n /**\n * Get the override id.\n *\n * @returns {*}\n */\n getOverrideId() {\n return this.overrideId;\n }\n\n /**\n * Set the session key.\n *\n * @param {string} key\n */\n setSessionKey(key) {\n this.sessionKey = key;\n }\n\n /**\n * Get the session key.\n *\n * @returns {*}\n */\n getSessionKey() {\n return this.sessionKey;\n }\n\n /**\n * Register events.\n *\n */\n registerEventListeners() {\n // Apply parent event listeners.\n super.registerEventListeners(this);\n\n // Register to close on cancel.\n this.registerCloseOnCancel();\n\n // Register the delete action.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('delete'), () => {\n this.deleteOverride();\n });\n }\n\n /**\n * Delete a override.\n *\n */\n deleteOverride() {\n // Check if the recalculation checkbox is checked.\n const recalculate = this.recalculationCheckbox.prop('checked');\n\n // Redirect to the delete URL.\n const targetUrl = new URL(`${Config.wwwroot}/mod/assign/overridedelete.php`);\n targetUrl.searchParams.append('id', this.getOverrideId());\n targetUrl.searchParams.append('sesskey', this.getSessionKey());\n targetUrl.searchParams.append('confirm', 1);\n\n if (recalculate) {\n targetUrl.searchParams.append('recalculate', 1);\n }\n\n window.location.href = targetUrl.href;\n }\n}\n"],"names":["SELECTORS","OverrideDeleteModal","Modal","configure","modalConfig","large","show","removeOnClose","setOverrideId","overrideId","setSessionKey","sessionKey","constructor","root","recalculationCheckbox","this","getModal","find","id","getOverrideId","key","getSessionKey","registerEventListeners","registerCloseOnCancel","on","CustomEvents","events","activate","getActionSelector","deleteOverride","recalculate","prop","targetUrl","URL","Config","wwwroot","searchParams","append","window","location","href"],"mappings":"u/CA2BMA,gCAEqB,8BAMNC,4BAA4BC,eAS7CC,UAAUC,aAENA,YAAYC,OAAQ,EAGpBD,YAAYE,MAAO,EACnBF,YAAYG,eAAgB,QAGtBJ,UAAUC,kBAEXI,cAAcJ,YAAYK,iBAC1BC,cAAcN,YAAYO,YASnCC,YAAYC,YACFA,WAGDC,sBAAwBC,KAAKC,WAAWC,KAAKjB,sCAG7CQ,cAAc,WACdE,cAAc,MAQvBF,cAAcU,SACLT,WAAaS,GAQtBC,uBACWJ,KAAKN,WAQhBC,cAAcU,UACLT,WAAaS,IAQtBC,uBACWN,KAAKJ,WAOhBW,+BAEUA,uBAAuBP,WAGxBQ,6BAGAP,WAAWQ,GAAGC,aAAaC,OAAOC,SAAUZ,KAAKa,kBAAkB,WAAW,UAC1EC,oBAQbA,uBAEUC,YAAcf,KAAKD,sBAAsBiB,KAAK,WAG9CC,UAAY,IAAIC,cAAOC,gBAAOC,2CACpCH,UAAUI,aAAaC,OAAO,KAAMtB,KAAKI,iBACzCa,UAAUI,aAAaC,OAAO,UAAWtB,KAAKM,iBAC9CW,UAAUI,aAAaC,OAAO,UAAW,GAErCP,aACAE,UAAUI,aAAaC,OAAO,cAAe,GAGjDC,OAAOC,SAASC,KAAOR,UAAUQ,kEAhHpBvC,2BACH,oDADGA,+BAEC"}grading_navigation.min.js.map000064400000076706152267113500012314 0ustar00{"version":3,"file":"grading_navigation.min.js","sources":["../src/grading_navigation.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript to handle changing users via the user selector in the header.\n *\n * @module mod_assign/grading_navigation\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',\n 'core/ajax', 'core_user/repository', 'mod_assign/grading_form_change_checker'],\n function($, notification, str, autocomplete, ajax, UserRepository, checker) {\n\n /**\n * GradingNavigation class.\n *\n * @class mod_assign/grading_navigation\n * @param {String} selector The selector for the page region containing the user navigation.\n */\n var GradingNavigation = function(selector) {\n this._regionSelector = selector;\n this._region = $(selector);\n this._filters = [];\n this._users = [];\n this._filteredUsers = [];\n this._lastXofYUpdate = 0;\n this._firstLoadUsers = true;\n\n let url = new URL(window.location);\n if (parseInt(url.searchParams.get('treset')) > 0) {\n // Remove 'treset' url parameter to make sure that\n // table preferences won't be reset on page refresh.\n url.searchParams.delete('treset');\n window.history.replaceState({}, \"\", url);\n }\n\n // Get the current user list from a webservice.\n this._loadAllUsers();\n\n // We do not allow navigation while ajax requests are pending.\n // Attach listeners to the select and arrow buttons.\n\n this._region.find('[data-action=\"previous-user\"]').on('click', this._handlePreviousUser.bind(this));\n this._region.find('[data-action=\"next-user\"]').on('click', this._handleNextUser.bind(this));\n this._region.find('[data-action=\"change-user\"]').on('change', this._handleChangeUser.bind(this));\n this._region.find('[data-region=\"user-filters\"]').on('click', this._toggleExpandFilters.bind(this));\n this._region.find('[data-region=\"user-resettable\"]').on('click', this._toggleResetTable.bind());\n\n $(document).on('user-changed', this._refreshSelector.bind(this));\n $(document).on('reset-table', this._toggleResetTable.bind(this));\n $(document).on('done-saving-show-next', this._handleNextUser.bind(this));\n\n // Position the configure filters panel under the link that expands it.\n var toggleLink = this._region.find('[data-region=\"user-filters\"]');\n var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));\n\n configPanel.on('change', 'select', this._filterChanged.bind(this));\n\n var userid = $('[data-region=\"grading-navigation-panel\"]').data('first-userid');\n if (userid) {\n this._selectUserById(userid);\n }\n\n str.get_string('changeuser', 'mod_assign').done(function(s) {\n autocomplete.enhance('[data-action=change-user]', false, 'mod_assign/participant_selector', s);\n }\n ).fail(notification.exception);\n\n $(document).bind(\"start-loading-user\", function() {\n this._isLoading = true;\n }.bind(this));\n $(document).bind(\"finish-loading-user\", function() {\n this._isLoading = false;\n }.bind(this));\n };\n\n /** @property {Boolean} Boolean tracking active ajax requests. */\n GradingNavigation.prototype._isLoading = false;\n\n /** @property {String} Selector for the page region containing the user navigation. */\n GradingNavigation.prototype._regionSelector = null;\n\n /** @property {Array} The list of active filter keys */\n GradingNavigation.prototype._filters = null;\n\n /** @property {Array} The list of users */\n GradingNavigation.prototype._users = null;\n\n /** @property {JQuery} JQuery node for the page region containing the user navigation. */\n GradingNavigation.prototype._region = null;\n\n /** @property {String} Last active filters */\n GradingNavigation.prototype._lastFilters = '';\n\n /**\n * Load the list of all users for this assignment.\n *\n * @private\n * @method _loadAllUsers\n * @return {Boolean} True if the user list was fetched.\n */\n GradingNavigation.prototype._loadAllUsers = function() {\n var select = this._region.find('[data-action=change-user]');\n var assignmentid = select.attr('data-assignmentid');\n var groupid = select.attr('data-groupid');\n\n var filterPanel = this._region.find('[data-region=\"configure-filters\"]');\n var filter = filterPanel.find('select[name=\"filter\"]').val();\n var workflowFilter = filterPanel.find('select[name=\"workflowfilter\"]');\n if (workflowFilter) {\n filter += ',' + workflowFilter.val();\n }\n var markerFilter = filterPanel.find('select[name=\"markerfilter\"]');\n if (markerFilter) {\n filter += ',' + markerFilter.val();\n }\n\n if (this._lastFilters == filter) {\n return false;\n }\n this._lastFilters = filter;\n\n ajax.call([{\n methodname: 'mod_assign_list_participants',\n args: {assignid: assignmentid, groupid: groupid, filter: '', onlyids: true, tablesort: true},\n done: this._usersLoaded.bind(this),\n fail: notification.exception\n }]);\n return true;\n };\n\n /**\n * Call back to rebuild the user selector and x of y info when the user list is updated.\n *\n * @private\n * @method _usersLoaded\n * @param {Array} users\n */\n GradingNavigation.prototype._usersLoaded = function(users) {\n this._firstLoadUsers = false;\n this._filteredUsers = this._users = users;\n if (this._users.length) {\n // Position the configure filters panel under the link that expands it.\n var toggleLink = this._region.find('[data-region=\"user-filters\"]');\n var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));\n\n configPanel.find('select[name=\"filter\"]').trigger('change');\n\n $('[data-region=\"grade-panel\"]').show();\n $('[data-region=\"grade-actions-panel\"]').show();\n } else {\n this._selectNoUser();\n }\n this._triggerNextUserEvent();\n };\n\n /**\n * Close the configure filters panel if a click is detected outside of it.\n *\n * @private\n * @method _checkClickOutsideConfigureFilters\n * @param {Event} event\n */\n GradingNavigation.prototype._checkClickOutsideConfigureFilters = function(event) {\n var configPanel = this._region.find('[data-region=\"configure-filters\"]');\n\n if (!configPanel.is(event.target) && configPanel.has(event.target).length === 0) {\n var toggleLink = this._region.find('[data-region=\"user-filters\"]');\n\n configPanel.hide();\n configPanel.attr('aria-hidden', 'true');\n toggleLink.attr('aria-expanded', 'false');\n $(document).unbind('click.mod_assign_grading_navigation');\n }\n };\n\n /**\n * Close the configure filters panel if a click is detected outside of it.\n *\n * @private\n * @method _updateFilterPreference\n * @param {Number} userId The current user id.\n * @param {Array} filterList The list of current filter values.\n * @param {Array} preferenceNames The names of the preferences to update\n * @return {Promise} Resolved when all the preferences are updated.\n */\n GradingNavigation.prototype._updateFilterPreferences = function(userId, filterList, preferenceNames) {\n var preferences = [],\n i = 0;\n\n if (filterList.length == 0 || this._firstLoadUsers) {\n // Nothing to update.\n var deferred = $.Deferred();\n deferred.resolve();\n return deferred;\n }\n // General filter.\n // Set the user preferences to the current filters.\n for (i = 0; i < filterList.length; i++) {\n var newValue = filterList[i];\n if (newValue == 'none') {\n newValue = '';\n }\n\n preferences.push({\n userid: userId,\n name: preferenceNames[i],\n value: newValue\n });\n }\n\n return UserRepository.setUserPreferences(preferences);\n };\n /**\n * Turn a filter on or off.\n *\n * @private\n * @method _filterChanged\n */\n GradingNavigation.prototype._filterChanged = function() {\n // There are 3 types of filter right now.\n var filterPanel = this._region.find('[data-region=\"configure-filters\"]');\n var filters = filterPanel.find('select');\n var preferenceNames = [];\n\n this._filters = [];\n filters.each(function(idx, ele) {\n var element = $(ele);\n this._filters.push(element.val());\n preferenceNames.push('assign_' + element.prop('name'));\n }.bind(this));\n\n // Update the active filter string.\n var filterlist = [];\n filterPanel.find('option:checked').each(function(idx, ele) {\n filterlist[filterlist.length] = $(ele).text();\n });\n if (filterlist.length) {\n this._region.find('[data-region=\"user-filters\"] span').text(filterlist.join(', '));\n } else {\n str.get_string('nofilters', 'mod_assign').done(function(s) {\n this._region.find('[data-region=\"user-filters\"] span').text(s);\n }.bind(this)).fail(notification.exception);\n }\n\n var select = this._region.find('[data-action=change-user]');\n var currentUserID = select.data('currentuserid');\n this._updateFilterPreferences(currentUserID, this._filters, preferenceNames).then(function() {\n // Reload the list of users to apply the new filters.\n if (!this._loadAllUsers()) {\n var userid = parseInt(select.attr('data-selected'));\n let foundIndex = null;\n // Search the returned users for the current selection.\n $.each(this._filteredUsers, function(index, user) {\n if (userid == user.id) {\n foundIndex = index;\n }\n });\n\n if (this._filteredUsers.length) {\n this._selectUserById(this._filteredUsers[foundIndex ?? 0].id);\n } else {\n this._selectNoUser();\n }\n\n }\n }.bind(this)).catch(notification.exception);\n this._refreshCount();\n };\n\n /**\n * Select no users, because no users match the filters.\n *\n * @private\n * @method _selectNoUser\n */\n GradingNavigation.prototype._selectNoUser = function() {\n // Detect unsaved changes, and offer to save them - otherwise change user right now.\n if (this._isLoading) {\n return;\n }\n\n $('[data-region=\"grade-panel\"]').hide();\n $('[data-region=\"grade-actions-panel\"]').hide();\n\n if (checker.checkFormForChanges('[data-region=\"grade-panel\"] .gradeform')) {\n // Form has changes, so we need to confirm before switching users.\n str.get_strings([\n {key: 'unsavedchanges', component: 'mod_assign'},\n {key: 'unsavedchangesquestion', component: 'mod_assign'},\n {key: 'saveandcontinue', component: 'mod_assign'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n $(document).trigger('save-changes', -1);\n });\n });\n } else {\n $(document).trigger('user-changed', -1);\n }\n };\n\n /**\n * Select the specified user by id.\n *\n * @private\n * @method _selectUserById\n * @param {Number} userid\n */\n GradingNavigation.prototype._selectUserById = function(userid) {\n var select = this._region.find('[data-action=change-user]');\n var useridnumber = parseInt(userid, 10);\n\n // Detect unsaved changes, and offer to save them - otherwise change user right now.\n if (this._isLoading) {\n return;\n }\n if (checker.checkFormForChanges('[data-region=\"grade-panel\"] .gradeform')) {\n // Form has changes, so we need to confirm before switching users.\n str.get_strings([\n {key: 'unsavedchanges', component: 'mod_assign'},\n {key: 'unsavedchangesquestion', component: 'mod_assign'},\n {key: 'saveandcontinue', component: 'mod_assign'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n $(document).trigger('save-changes', useridnumber);\n });\n });\n } else {\n select.attr('data-selected', userid);\n\n // If we have some filtered users, and userid is specified, then trigger change.\n if (this._filteredUsers.length > 0 && !isNaN(useridnumber) && useridnumber > 0) {\n $(document).trigger('user-changed', useridnumber);\n }\n }\n };\n\n /**\n * Expand or collapse the filter config panel.\n *\n * @private\n * @method _toggleExpandFilters\n * @param {Event} event\n */\n GradingNavigation.prototype._toggleExpandFilters = function(event) {\n event.preventDefault();\n var toggleLink = $(event.target).closest('[data-region=\"user-filters\"]');\n var expanded = toggleLink.attr('aria-expanded') == 'true';\n var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));\n\n if (expanded) {\n configPanel.hide();\n configPanel.attr('aria-hidden', 'true');\n toggleLink.attr('aria-expanded', 'false');\n $(document).unbind('click.mod_assign_grading_navigation');\n } else {\n configPanel.css('display', 'inline-block');\n configPanel.attr('aria-hidden', 'false');\n toggleLink.attr('aria-expanded', 'true');\n event.stopPropagation();\n $(document).on('click.mod_assign_grading_navigation', this._checkClickOutsideConfigureFilters.bind(this));\n }\n };\n\n /**\n * Reset table preferences.\n *\n * @private\n * @method _toggleResetTable\n */\n GradingNavigation.prototype._toggleResetTable = function() {\n let url = new URL(window.location);\n url.searchParams.set('treset', '1');\n window.location.href = url;\n };\n\n /**\n * Change to the previous user in the grading list.\n *\n * @private\n * @method _handlePreviousUser\n * @param {Event} e\n */\n GradingNavigation.prototype._handlePreviousUser = function(e) {\n e.preventDefault();\n var select = this._region.find('[data-action=change-user]');\n var currentUserId = select.attr('data-selected');\n var i = 0;\n var currentIndex = 0;\n\n for (i = 0; i < this._filteredUsers.length; i++) {\n if (this._filteredUsers[i].id == currentUserId) {\n currentIndex = i;\n break;\n }\n }\n\n var count = this._filteredUsers.length;\n var newIndex = (currentIndex - 1);\n if (newIndex < 0) {\n newIndex = count - 1;\n }\n\n if (count) {\n this._selectUserById(this._filteredUsers[newIndex].id);\n }\n };\n\n /**\n * Change to the next user in the grading list.\n *\n * @param {Event} e\n * @param {Boolean} saved Has the form already been saved? Skips checking for changes if true.\n */\n GradingNavigation.prototype._handleNextUser = function(e, saved) {\n e.preventDefault();\n var select = this._region.find('[data-action=change-user]');\n var currentUserId = select.attr('data-selected');\n var i = 0;\n var currentIndex = 0;\n\n for (i = 0; i < this._filteredUsers.length; i++) {\n if (this._filteredUsers[i].id == currentUserId) {\n currentIndex = i;\n break;\n }\n }\n\n var count = this._filteredUsers.length;\n var newIndex = (currentIndex + 1) % count;\n\n if (saved && count) {\n // If we've already saved the grade, skip checking if we've made any changes.\n var userid = this._filteredUsers[newIndex].id;\n var useridnumber = parseInt(userid, 10);\n select.attr('data-selected', userid);\n if (!isNaN(useridnumber) && useridnumber > 0) {\n $(document).trigger('user-changed', userid);\n }\n } else if (count) {\n this._selectUserById(this._filteredUsers[newIndex].id);\n }\n };\n\n /**\n * Set count string. This method only sets the value for the last time it was ever called to deal\n * with promises that return in a non-predictable order.\n *\n * @private\n * @method _setCountString\n * @param {Number} x\n * @param {Number} y\n */\n GradingNavigation.prototype._setCountString = function(x, y) {\n var updateNumber = 0;\n this._lastXofYUpdate++;\n updateNumber = this._lastXofYUpdate;\n\n var param = {x: x, y: y};\n str.get_string('xofy', 'mod_assign', param).done(function(s) {\n if (updateNumber == this._lastXofYUpdate) {\n this._region.find('[data-region=\"user-count-summary\"]').text(s);\n }\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Rebuild the x of y string.\n *\n * @private\n * @method _refreshCount\n */\n GradingNavigation.prototype._refreshCount = function() {\n var select = this._region.find('[data-action=change-user]');\n var userid = select.attr('data-selected');\n var i = 0;\n var currentIndex = 0;\n\n if (isNaN(userid) || userid <= 0) {\n this._region.find('[data-region=\"user-count\"]').hide();\n } else {\n this._region.find('[data-region=\"user-count\"]').show();\n\n for (i = 0; i < this._filteredUsers.length; i++) {\n if (this._filteredUsers[i].id == userid) {\n currentIndex = i;\n break;\n }\n }\n var count = this._filteredUsers.length;\n if (count) {\n currentIndex += 1;\n }\n this._setCountString(currentIndex, count);\n // Update window URL\n if (currentIndex > 0) {\n var url = new URL(window.location);\n if (parseInt(url.searchParams.get('blindid')) > 0) {\n var newid = this._filteredUsers[currentIndex - 1].recordid;\n url.searchParams.set('blindid', newid);\n } else {\n url.searchParams.set('userid', userid);\n }\n // We do this so a browser refresh will return to the same user.\n window.history.replaceState({}, \"\", url);\n }\n }\n };\n\n /**\n * Respond to a user-changed event by updating the selector.\n *\n * @private\n * @method _refreshSelector\n * @param {Event} event\n * @param {String} userid\n */\n GradingNavigation.prototype._refreshSelector = function(event, userid) {\n var select = this._region.find('[data-action=change-user]');\n userid = parseInt(userid, 10);\n\n if (!isNaN(userid) && userid > 0) {\n select.attr('data-selected', userid);\n }\n this._refreshCount();\n };\n\n /**\n * Trigger the next user event depending on the number of filtered users\n *\n * @private\n * @method _triggerNextUserEvent\n */\n GradingNavigation.prototype._triggerNextUserEvent = function() {\n if (this._filteredUsers.length > 1) {\n $(document).trigger('next-user', {nextUserId: null, nextUser: true});\n } else {\n $(document).trigger('next-user', {nextUser: false});\n }\n };\n\n /**\n * Change to a different user in the grading list.\n *\n * @private\n * @method _handleChangeUser\n */\n GradingNavigation.prototype._handleChangeUser = function() {\n var select = this._region.find('[data-action=change-user]');\n var userid = parseInt(select.val(), 10);\n\n if (this._isLoading) {\n return;\n }\n if (checker.checkFormForChanges('[data-region=\"grade-panel\"] .gradeform')) {\n // Form has changes, so we need to confirm before switching users.\n str.get_strings([\n {key: 'unsavedchanges', component: 'mod_assign'},\n {key: 'unsavedchangesquestion', component: 'mod_assign'},\n {key: 'saveandcontinue', component: 'mod_assign'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n $(document).trigger('save-changes', userid);\n });\n });\n } else {\n if (!isNaN(userid) && userid > 0) {\n select.attr('data-selected', userid);\n\n $(document).trigger('user-changed', userid);\n }\n }\n };\n\n return GradingNavigation;\n});\n"],"names":["define","$","notification","str","autocomplete","ajax","UserRepository","checker","GradingNavigation","selector","_regionSelector","_region","_filters","_users","_filteredUsers","_lastXofYUpdate","_firstLoadUsers","url","URL","window","location","parseInt","searchParams","get","delete","history","replaceState","_loadAllUsers","find","on","this","_handlePreviousUser","bind","_handleNextUser","_handleChangeUser","_toggleExpandFilters","_toggleResetTable","document","_refreshSelector","toggleLink","getElementById","attr","_filterChanged","userid","data","_selectUserById","get_string","done","s","enhance","fail","exception","_isLoading","prototype","_lastFilters","select","assignmentid","groupid","filterPanel","filter","val","workflowFilter","markerFilter","call","methodname","args","assignid","onlyids","tablesort","_usersLoaded","users","length","trigger","show","_selectNoUser","_triggerNextUserEvent","_checkClickOutsideConfigureFilters","event","configPanel","is","target","has","hide","unbind","_updateFilterPreferences","userId","filterList","preferenceNames","preferences","i","deferred","Deferred","resolve","newValue","push","name","value","setUserPreferences","filters","each","idx","ele","element","prop","filterlist","text","join","currentUserID","then","foundIndex","index","user","id","catch","_refreshCount","checkFormForChanges","get_strings","key","component","strs","confirm","useridnumber","isNaN","preventDefault","closest","expanded","css","stopPropagation","set","href","e","currentUserId","currentIndex","count","newIndex","saved","_setCountString","x","y","updateNumber","param","newid","recordid","nextUserId","nextUser"],"mappings":";;;;;;;;AAuBAA,uCAAO,CAAC,SAAU,oBAAqB,WAAY,yBAC3C,YAAa,uBAAwB,2CACtC,SAASC,EAAGC,aAAcC,IAAKC,aAAcC,KAAMC,eAAgBC,aAQlEC,kBAAoB,SAASC,eACxBC,gBAAkBD,cAClBE,QAAUV,EAAEQ,eACZG,SAAW,QACXC,OAAS,QACTC,eAAiB,QACjBC,gBAAkB,OAClBC,iBAAkB,MAEnBC,IAAM,IAAIC,IAAIC,OAAOC,UACrBC,SAASJ,IAAIK,aAAaC,IAAI,WAAa,IAG3CN,IAAIK,aAAaE,OAAO,UACxBL,OAAOM,QAAQC,aAAa,GAAI,GAAIT,WAInCU,qBAKAhB,QAAQiB,KAAK,iCAAiCC,GAAG,QAASC,KAAKC,oBAAoBC,KAAKF,YACxFnB,QAAQiB,KAAK,6BAA6BC,GAAG,QAASC,KAAKG,gBAAgBD,KAAKF,YAChFnB,QAAQiB,KAAK,+BAA+BC,GAAG,SAAUC,KAAKI,kBAAkBF,KAAKF,YACrFnB,QAAQiB,KAAK,gCAAgCC,GAAG,QAASC,KAAKK,qBAAqBH,KAAKF,YACxFnB,QAAQiB,KAAK,mCAAmCC,GAAG,QAASC,KAAKM,kBAAkBJ,QAExF/B,EAAEoC,UAAUR,GAAG,eAAgBC,KAAKQ,iBAAiBN,KAAKF,OAC1D7B,EAAEoC,UAAUR,GAAG,cAAeC,KAAKM,kBAAkBJ,KAAKF,OAC1D7B,EAAEoC,UAAUR,GAAG,wBAAyBC,KAAKG,gBAAgBD,KAAKF,WAG9DS,WAAaT,KAAKnB,QAAQiB,KAAK,gCACjB3B,EAAEoC,SAASG,eAAeD,WAAWE,KAAK,mBAEhDZ,GAAG,SAAU,SAAUC,KAAKY,eAAeV,KAAKF,WAExDa,OAAS1C,EAAE,4CAA4C2C,KAAK,gBAC5DD,aACKE,gBAAgBF,QAGzBxC,IAAI2C,WAAW,aAAc,cAAcC,MAAK,SAASC,GACjD5C,aAAa6C,QAAQ,6BAA6B,EAAO,kCAAmCD,MAElGE,KAAKhD,aAAaiD,WAEpBlD,EAAEoC,UAAUL,KAAK,qBAAsB,gBAC9BoB,YAAa,GACpBpB,KAAKF,OACP7B,EAAEoC,UAAUL,KAAK,sBAAuB,gBAC/BoB,YAAa,GACpBpB,KAAKF,eAIXtB,kBAAkB6C,UAAUD,YAAa,EAGzC5C,kBAAkB6C,UAAU3C,gBAAkB,KAG9CF,kBAAkB6C,UAAUzC,SAAW,KAGvCJ,kBAAkB6C,UAAUxC,OAAS,KAGrCL,kBAAkB6C,UAAU1C,QAAU,KAGtCH,kBAAkB6C,UAAUC,aAAe,GAS3C9C,kBAAkB6C,UAAU1B,cAAgB,eACpC4B,OAASzB,KAAKnB,QAAQiB,KAAK,6BAC3B4B,aAAeD,OAAOd,KAAK,qBAC3BgB,QAAUF,OAAOd,KAAK,gBAEtBiB,YAAc5B,KAAKnB,QAAQiB,KAAK,qCAChC+B,OAASD,YAAY9B,KAAK,yBAAyBgC,MACnDC,eAAiBH,YAAY9B,KAAK,iCAClCiC,iBACAF,QAAU,IAAME,eAAeD,WAE/BE,aAAeJ,YAAY9B,KAAK,sCAChCkC,eACAH,QAAU,IAAMG,aAAaF,OAG7B9B,KAAKwB,cAAgBK,cAGpBL,aAAeK,OAEpBtD,KAAK0D,KAAK,CAAC,CACPC,WAAY,+BACZC,KAAM,CAACC,SAAUV,aAAcC,QAASA,QAASE,OAAQ,GAAIQ,SAAS,EAAMC,WAAW,GACvFrB,KAAMjB,KAAKuC,aAAarC,KAAKF,MAC7BoB,KAAMhD,aAAaiD,cAEhB,IAUX3C,kBAAkB6C,UAAUgB,aAAe,SAASC,eAC3CtD,iBAAkB,OAClBF,eAAiBgB,KAAKjB,OAASyD,MAChCxC,KAAKjB,OAAO0D,OAAQ,KAEhBhC,WAAaT,KAAKnB,QAAQiB,KAAK,gCACjB3B,EAAEoC,SAASG,eAAeD,WAAWE,KAAK,mBAEhDb,KAAK,yBAAyB4C,QAAQ,UAElDvE,EAAE,+BAA+BwE,OACjCxE,EAAE,uCAAuCwE,iBAEpCC,qBAEJC,yBAUTnE,kBAAkB6C,UAAUuB,mCAAqC,SAASC,WAClEC,YAAchD,KAAKnB,QAAQiB,KAAK,yCAE/BkD,YAAYC,GAAGF,MAAMG,SAAoD,IAAzCF,YAAYG,IAAIJ,MAAMG,QAAQT,OAAc,KACzEhC,WAAaT,KAAKnB,QAAQiB,KAAK,gCAEnCkD,YAAYI,OACZJ,YAAYrC,KAAK,cAAe,QAChCF,WAAWE,KAAK,gBAAiB,SACjCxC,EAAEoC,UAAU8C,OAAO,yCAc3B3E,kBAAkB6C,UAAU+B,yBAA2B,SAASC,OAAQC,WAAYC,qBAC5EC,YAAc,GACdC,EAAI,KAEiB,GAArBH,WAAWf,QAAezC,KAAKd,gBAAiB,KAE5C0E,SAAWzF,EAAE0F,kBACjBD,SAASE,UACFF,aAIND,EAAI,EAAGA,EAAIH,WAAWf,OAAQkB,IAAK,KAChCI,SAAWP,WAAWG,GACV,QAAZI,WACAA,SAAW,IAGfL,YAAYM,KAAK,CACbnD,OAAQ0C,OACRU,KAAMR,gBAAgBE,GACtBO,MAAOH,kBAIRvF,eAAe2F,mBAAmBT,cAQ7ChF,kBAAkB6C,UAAUX,eAAiB,eAErCgB,YAAc5B,KAAKnB,QAAQiB,KAAK,qCAChCsE,QAAUxC,YAAY9B,KAAK,UAC3B2D,gBAAkB,QAEjB3E,SAAW,GAChBsF,QAAQC,KAAK,SAASC,IAAKC,SACnBC,QAAUrG,EAAEoG,UACXzF,SAASkF,KAAKQ,QAAQ1C,OAC3B2B,gBAAgBO,KAAK,UAAYQ,QAAQC,KAAK,UAChDvE,KAAKF,WAGH0E,WAAa,GACjB9C,YAAY9B,KAAK,kBAAkBuE,MAAK,SAASC,IAAKC,KAClDG,WAAWA,WAAWjC,QAAUtE,EAAEoG,KAAKI,UAEvCD,WAAWjC,YACN5D,QAAQiB,KAAK,qCAAqC6E,KAAKD,WAAWE,KAAK,OAE5EvG,IAAI2C,WAAW,YAAa,cAAcC,KAAK,SAASC,QAC/CrC,QAAQiB,KAAK,qCAAqC6E,KAAKzD,IAC9DhB,KAAKF,OAAOoB,KAAKhD,aAAaiD,eAGhCI,OAASzB,KAAKnB,QAAQiB,KAAK,6BAC3B+E,cAAgBpD,OAAOX,KAAK,sBAC3BwC,yBAAyBuB,cAAe7E,KAAKlB,SAAU2E,iBAAiBqB,KAAK,eAEzE9E,KAAKH,gBAAiB,KACnBgB,OAAStB,SAASkC,OAAOd,KAAK,sBAC9BoE,WAAa,wBAEjB5G,EAAEkG,KAAKrE,KAAKhB,gBAAgB,SAASgG,MAAOC,MACpCpE,QAAUoE,KAAKC,KACfH,WAAaC,UAIjBhF,KAAKhB,eAAeyD,YACf1B,gBAAgBf,KAAKhB,mCAAe+F,8CAAc,GAAGG,cAErDtC,kBAIf1C,KAAKF,OAAOmF,MAAM/G,aAAaiD,gBAC5B+D,iBAST1G,kBAAkB6C,UAAUqB,cAAgB,WAEpC5C,KAAKsB,aAITnD,EAAE,+BAA+BiF,OACjCjF,EAAE,uCAAuCiF,OAErC3E,QAAQ4G,oBAAoB,0CAE5BhH,IAAIiH,YAAY,CACZ,CAACC,IAAK,iBAAkBC,UAAW,cACnC,CAACD,IAAK,yBAA0BC,UAAW,cAC3C,CAACD,IAAK,kBAAmBC,UAAW,cACpC,CAACD,IAAK,SAAUC,UAAW,UAC5BvE,MAAK,SAASwE,MACbrH,aAAasH,QAAQD,KAAK,GAAIA,KAAK,GAAIA,KAAK,GAAIA,KAAK,IAAI,WACrDtH,EAAEoC,UAAUmC,QAAQ,gBAAiB,SAI7CvE,EAAEoC,UAAUmC,QAAQ,gBAAiB,KAW7ChE,kBAAkB6C,UAAUR,gBAAkB,SAASF,YAC/CY,OAASzB,KAAKnB,QAAQiB,KAAK,6BAC3B6F,aAAepG,SAASsB,OAAQ,IAGhCb,KAAKsB,aAGL7C,QAAQ4G,oBAAoB,0CAE5BhH,IAAIiH,YAAY,CACZ,CAACC,IAAK,iBAAkBC,UAAW,cACnC,CAACD,IAAK,yBAA0BC,UAAW,cAC3C,CAACD,IAAK,kBAAmBC,UAAW,cACpC,CAACD,IAAK,SAAUC,UAAW,UAC5BvE,MAAK,SAASwE,MACbrH,aAAasH,QAAQD,KAAK,GAAIA,KAAK,GAAIA,KAAK,GAAIA,KAAK,IAAI,WACrDtH,EAAEoC,UAAUmC,QAAQ,eAAgBiD,qBAI5ClE,OAAOd,KAAK,gBAAiBE,QAGzBb,KAAKhB,eAAeyD,OAAS,IAAMmD,MAAMD,eAAiBA,aAAe,GACzExH,EAAEoC,UAAUmC,QAAQ,eAAgBiD,iBAYhDjH,kBAAkB6C,UAAUlB,qBAAuB,SAAS0C,OACxDA,MAAM8C,qBACFpF,WAAatC,EAAE4E,MAAMG,QAAQ4C,QAAQ,gCACrCC,SAA+C,QAApCtF,WAAWE,KAAK,iBAC3BqC,YAAc7E,EAAEoC,SAASG,eAAeD,WAAWE,KAAK,mBAExDoF,UACA/C,YAAYI,OACZJ,YAAYrC,KAAK,cAAe,QAChCF,WAAWE,KAAK,gBAAiB,SACjCxC,EAAEoC,UAAU8C,OAAO,yCAEnBL,YAAYgD,IAAI,UAAW,gBAC3BhD,YAAYrC,KAAK,cAAe,SAChCF,WAAWE,KAAK,gBAAiB,QACjCoC,MAAMkD,kBACN9H,EAAEoC,UAAUR,GAAG,sCAAuCC,KAAK8C,mCAAmC5C,KAAKF,SAU3GtB,kBAAkB6C,UAAUjB,kBAAoB,eACxCnB,IAAM,IAAIC,IAAIC,OAAOC,UACzBH,IAAIK,aAAa0G,IAAI,SAAU,KAC/B7G,OAAOC,SAAS6G,KAAOhH,KAU3BT,kBAAkB6C,UAAUtB,oBAAsB,SAASmG,GACvDA,EAAEP,qBAEEQ,cADSrG,KAAKnB,QAAQiB,KAAK,6BACJa,KAAK,iBAC5BgD,EAAI,EACJ2C,aAAe,MAEd3C,EAAI,EAAGA,EAAI3D,KAAKhB,eAAeyD,OAAQkB,OACpC3D,KAAKhB,eAAe2E,GAAGuB,IAAMmB,cAAe,CAC5CC,aAAe3C,YAKnB4C,MAAQvG,KAAKhB,eAAeyD,OAC5B+D,SAAYF,aAAe,EAC3BE,SAAW,IACXA,SAAWD,MAAQ,GAGnBA,YACKxF,gBAAgBf,KAAKhB,eAAewH,UAAUtB,KAU3DxG,kBAAkB6C,UAAUpB,gBAAkB,SAASiG,EAAGK,OACtDL,EAAEP,qBACEpE,OAASzB,KAAKnB,QAAQiB,KAAK,6BAC3BuG,cAAgB5E,OAAOd,KAAK,iBAC5BgD,EAAI,EACJ2C,aAAe,MAEd3C,EAAI,EAAGA,EAAI3D,KAAKhB,eAAeyD,OAAQkB,OACpC3D,KAAKhB,eAAe2E,GAAGuB,IAAMmB,cAAe,CAC5CC,aAAe3C,YAKnB4C,MAAQvG,KAAKhB,eAAeyD,OAC5B+D,UAAYF,aAAe,GAAKC,SAEhCE,OAASF,MAAO,KAEZ1F,OAASb,KAAKhB,eAAewH,UAAUtB,GACvCS,aAAepG,SAASsB,OAAQ,IACpCY,OAAOd,KAAK,gBAAiBE,SACxB+E,MAAMD,eAAiBA,aAAe,GACvCxH,EAAEoC,UAAUmC,QAAQ,eAAgB7B,aAEjC0F,YACFxF,gBAAgBf,KAAKhB,eAAewH,UAAUtB,KAa3DxG,kBAAkB6C,UAAUmF,gBAAkB,SAASC,EAAGC,OAClDC,kBACC5H,kBACL4H,aAAe7G,KAAKf,oBAEhB6H,MAAQ,CAACH,EAAGA,EAAGC,EAAGA,GACtBvI,IAAI2C,WAAW,OAAQ,aAAc8F,OAAO7F,KAAK,SAASC,GAClD2F,cAAgB7G,KAAKf,sBAChBJ,QAAQiB,KAAK,sCAAsC6E,KAAKzD,IAEnEhB,KAAKF,OAAOoB,KAAKhD,aAAaiD,YASpC3C,kBAAkB6C,UAAU6D,cAAgB,eAEpCvE,OADSb,KAAKnB,QAAQiB,KAAK,6BACXa,KAAK,iBACrBgD,EAAI,EACJ2C,aAAe,KAEfV,MAAM/E,SAAWA,QAAU,OACtBhC,QAAQiB,KAAK,8BAA8BsD,WAC7C,UACEvE,QAAQiB,KAAK,8BAA8B6C,OAE3CgB,EAAI,EAAGA,EAAI3D,KAAKhB,eAAeyD,OAAQkB,OACpC3D,KAAKhB,eAAe2E,GAAGuB,IAAMrE,OAAQ,CACrCyF,aAAe3C,YAInB4C,MAAQvG,KAAKhB,eAAeyD,UAC5B8D,QACAD,cAAgB,QAEfI,gBAAgBJ,aAAcC,OAE/BD,aAAe,EAAG,KACdnH,IAAM,IAAIC,IAAIC,OAAOC,aACrBC,SAASJ,IAAIK,aAAaC,IAAI,YAAc,EAAG,KAC3CsH,MAAQ/G,KAAKhB,eAAesH,aAAe,GAAGU,SAClD7H,IAAIK,aAAa0G,IAAI,UAAWa,YAEhC5H,IAAIK,aAAa0G,IAAI,SAAUrF,QAGnCxB,OAAOM,QAAQC,aAAa,GAAI,GAAIT,QAahDT,kBAAkB6C,UAAUf,iBAAmB,SAASuC,MAAOlC,YACvDY,OAASzB,KAAKnB,QAAQiB,KAAK,6BAC/Be,OAAStB,SAASsB,OAAQ,KAErB+E,MAAM/E,SAAWA,OAAS,GAC3BY,OAAOd,KAAK,gBAAiBE,aAE5BuE,iBAST1G,kBAAkB6C,UAAUsB,sBAAwB,WAC5C7C,KAAKhB,eAAeyD,OAAS,EAC7BtE,EAAEoC,UAAUmC,QAAQ,YAAa,CAACuE,WAAY,KAAMC,UAAU,IAE9D/I,EAAEoC,UAAUmC,QAAQ,YAAa,CAACwE,UAAU,KAUpDxI,kBAAkB6C,UAAUnB,kBAAoB,eACxCqB,OAASzB,KAAKnB,QAAQiB,KAAK,6BAC3Be,OAAStB,SAASkC,OAAOK,MAAO,IAEhC9B,KAAKsB,aAGL7C,QAAQ4G,oBAAoB,0CAE5BhH,IAAIiH,YAAY,CACZ,CAACC,IAAK,iBAAkBC,UAAW,cACnC,CAACD,IAAK,yBAA0BC,UAAW,cAC3C,CAACD,IAAK,kBAAmBC,UAAW,cACpC,CAACD,IAAK,SAAUC,UAAW,UAC5BvE,MAAK,SAASwE,MACbrH,aAAasH,QAAQD,KAAK,GAAIA,KAAK,GAAIA,KAAK,GAAIA,KAAK,IAAI,WACrDtH,EAAEoC,UAAUmC,QAAQ,eAAgB7B,eAIvC+E,MAAM/E,SAAWA,OAAS,IAC3BY,OAAOd,KAAK,gBAAiBE,QAE7B1C,EAAEoC,UAAUmC,QAAQ,eAAgB7B,WAKzCnC"}quick_grading.min.js.map000064400000006426152267113500011261 0ustar00{"version":3,"file":"quick_grading.min.js","sources":["../src/quick_grading.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 * as formSubmit from 'core_form/submit';\n\n/**\n * Module for the quick grading functionality on the submissions page.\n *\n * @module mod_assign/quick_grading\n * @copyright 2024 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n quickGradingSaveRegion: '[data-region=\"quick-grading-save\"]',\n notifyStudentsCheckbox: 'input[type=\"checkbox\"][name=\"sendstudentnotifications\"]',\n notifyStudentsHidden: 'input[type=\"hidden\"][name=\"sendstudentnotifications\"]',\n saveButton: 'button[type=\"submit\"]'\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const quickGradingSaveRegion = document.querySelector(Selectors.quickGradingSaveRegion);\n if (quickGradingSaveRegion) {\n const quickGradingSaveButton = quickGradingSaveRegion.querySelector(Selectors.saveButton);\n // Initialize the submit button.\n formSubmit.init(quickGradingSaveButton);\n // Add 'change' event listener to the quick grading save region.\n quickGradingSaveRegion.addEventListener('change', e => {\n const notifyStudentsCheckbox = e.target.closest(Selectors.notifyStudentsCheckbox);\n // The target is the 'Notify student' checkbox.\n if (notifyStudentsCheckbox) {\n // The 'Notify student' option uses a hidden input and a checkbox. The hidden input is used to submit '0'\n // as a workaround when the checkbox is unchecked since unchecked checkboxes are not submitted with the\n // form. Therefore, we need to enable or disable the hidden input based on the checkbox state.\n const notifyStudentsHidden = notifyStudentsCheckbox.parentNode.querySelector(Selectors.notifyStudentsHidden);\n notifyStudentsHidden.disabled = notifyStudentsCheckbox.checked;\n }\n });\n }\n};\n"],"names":["Selectors","quickGradingSaveRegion","document","querySelector","quickGradingSaveButton","formSubmit","init","addEventListener","e","notifyStudentsCheckbox","target","closest","parentNode","disabled","checked"],"mappings":";;;;;;;wBA0BMA,iCACsB,qCADtBA,iCAEsB,0DAFtBA,+BAGoB,wDAHpBA,qBAIU,sCAMI,WACVC,uBAAyBC,SAASC,cAAcH,qCAClDC,uBAAwB,OAClBG,uBAAyBH,uBAAuBE,cAAcH,sBAEpEK,WAAWC,KAAKF,wBAEhBH,uBAAuBM,iBAAiB,UAAUC,UACxCC,uBAAyBD,EAAEE,OAAOC,QAAQX,qCAE5CS,uBAAwB,CAIKA,uBAAuBG,WAAWT,cAAcH,gCACxDa,SAAWJ,uBAAuBK"}override_delete_modal.min.js000064400000005765152267113500012220 0ustar00define("mod_assign/override_delete_modal",["exports","core/custom_interaction_events","core/config","core/modal"],(function(_exports,CustomEvents,_config,_modal){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}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,CustomEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CustomEvents),_config=_interopRequireDefault(_config),_modal=_interopRequireDefault(_modal);const SELECTORS_RECACULATION_CHECKBOX="#recalculatepenalties";class OverrideDeleteModal extends _modal.default{configure(modalConfig){modalConfig.large=!0,modalConfig.show=!0,modalConfig.removeOnClose=!0,super.configure(modalConfig),this.setOverrideId(modalConfig.overrideId),this.setSessionKey(modalConfig.sessionKey)}constructor(root){super(root),this.recalculationCheckbox=this.getModal().find(SELECTORS_RECACULATION_CHECKBOX),this.setOverrideId(null),this.setSessionKey(null)}setOverrideId(id){this.overrideId=id}getOverrideId(){return this.overrideId}setSessionKey(key){this.sessionKey=key}getSessionKey(){return this.sessionKey}registerEventListeners(){super.registerEventListeners(this),this.registerCloseOnCancel(),this.getModal().on(CustomEvents.events.activate,this.getActionSelector("delete"),(()=>{this.deleteOverride()}))}deleteOverride(){const recalculate=this.recalculationCheckbox.prop("checked"),targetUrl=new URL("".concat(_config.default.wwwroot,"/mod/assign/overridedelete.php"));targetUrl.searchParams.append("id",this.getOverrideId()),targetUrl.searchParams.append("sesskey",this.getSessionKey()),targetUrl.searchParams.append("confirm",1),recalculate&&targetUrl.searchParams.append("recalculate",1),window.location.href=targetUrl.href}}return _exports.default=OverrideDeleteModal,_defineProperty(OverrideDeleteModal,"TYPE","mod_assign/override_delete_modal"),_defineProperty(OverrideDeleteModal,"TEMPLATE","mod_assign/override_delete_modal"),_exports.default})); //# sourceMappingURL=override_delete_modal.min.js.mapbulkactions/grading/message.min.js.map000064400000004601152267113500014020 0ustar00{"version":3,"file":"message.min.js","sources":["../../../src/bulkactions/grading/message.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 * Bulk action for messaging users in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/message\n * @copyright 2024 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport Templates from 'core/templates';\nimport {showModal as showMessageModal} from 'core_message/message_send_bulk';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]:checked',\n};\n\nexport default class extends BulkAction {\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"message\"]';\n }\n\n triggerBulkAction() {\n const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);\n showMessageModal(selectedUsers);\n }\n\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('mod_assign/bulkactions/grading/bulk_message_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n}\n"],"names":["Selectors","BulkAction","getBulkActionTriggerSelector","triggerBulkAction","selectedUsers","document","querySelectorAll","map","checkbox","value","showInDropdown","index","Templates","render","showindropdown","isfirst"],"mappings":";;;;;;;2LA2BMA,iCACsB,8EAGCC,qBACzBC,qCACW,oDAGXC,0BACUC,cAAgB,IAAIC,SAASC,iBAAiBN,mCAAmCO,KAAIC,UAAYA,SAASC,yCAC/FL,6CAGSM,eAAgBC,cACnCC,mBAAUC,OAAO,sDAAuD,CAC3EC,eAAgBJ,eAChBK,QAAmB,IAAVJ"}bulkactions/grading/general_action.min.js000064400000010544152267113500014575 0ustar00define("mod_assign/bulkactions/grading/general_action",["exports","core/bulkactions/bulk_action","core/templates","core/modal_save_cancel","core/modal_events"],(function(_exports,_bulk_action,_templates,_modal_save_cancel,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_events=_interopRequireDefault(_modal_events);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';var _cmid=new WeakMap,_buttonIcon=new WeakMap,_buttonLabel=new WeakMap,_confirmationTitle=new WeakMap,_confirmationQuestion=new WeakMap,_confirmationYes=new WeakMap,_sesskey=new WeakMap;class _default extends _bulk_action.default{constructor(cmid,sesskey,actionKey,buttonLabel,buttonIcon,confirmationTitle,confirmationQuestion,confirmationYes){var obj,key,value;super(),value=void 0,(key="actionKey")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,_classPrivateFieldInitSpec(this,_cmid,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_buttonIcon,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_buttonLabel,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_confirmationTitle,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_confirmationQuestion,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_confirmationYes,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_sesskey,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_cmid,cmid),_classPrivateFieldSet(this,_sesskey,sesskey),this.actionKey=actionKey,_classPrivateFieldSet(this,_buttonLabel,buttonLabel),_classPrivateFieldSet(this,_buttonIcon,buttonIcon),_classPrivateFieldSet(this,_confirmationTitle,confirmationTitle),_classPrivateFieldSet(this,_confirmationQuestion,confirmationQuestion),_classPrivateFieldSet(this,_confirmationYes,confirmationYes)}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="'.concat(this.actionKey,'"]')}async triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value)),modal=await _modal_save_cancel.default.create({title:await _classPrivateFieldGet(this,_confirmationTitle),buttons:{save:await _classPrivateFieldGet(this,_confirmationYes)},body:_templates.default.render("mod_assign/bulkactions/grading/bulk_action_modal_body",{text:await _classPrivateFieldGet(this,_confirmationQuestion),operation:this.actionKey,cmid:_classPrivateFieldGet(this,_cmid),selectedusers:selectedUsers.join(","),sesskey:_classPrivateFieldGet(this,_sesskey)}),show:!0,removeOnClose:!0});modal.getRoot().on(_modal_events.default.save,(()=>{modal.getRoot().find("form").submit()}))}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_general_action_trigger",{action:this.actionKey,title:await _classPrivateFieldGet(this,_buttonLabel),icon:await _classPrivateFieldGet(this,_buttonIcon),showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=general_action.min.js.mapbulkactions/grading/setmarkingallocation.min.js000064400000006407152267113500016040 0ustar00define("mod_assign/bulkactions/grading/setmarkingallocation",["exports","core/bulkactions/bulk_action","core/templates","core/str","core/modal_save_cancel","core/modal_events"],(function(_exports,_bulk_action,_templates,_str,_modal_save_cancel,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_events=_interopRequireDefault(_modal_events);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';var _cmid=new WeakMap,_sesskey=new WeakMap;class _default extends _bulk_action.default{constructor(cmid,sesskey){super(),_classPrivateFieldInitSpec(this,_cmid,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_sesskey,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_cmid,cmid),_classPrivateFieldSet(this,_sesskey,sesskey)}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="setmarkingallocation"]'}async triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value)),modal=await _modal_save_cancel.default.create({title:await(0,_str.getString)("setmarkingallocation","mod_assign"),buttons:{save:await(0,_str.getString)("batchoperationsetmarkingallocation","mod_assign")},body:_templates.default.render("mod_assign/bulkactions/grading/bulk_action_modal_body",{text:await(0,_str.getString)("batchoperationconfirmsetmarkingallocation","mod_assign"),operation:"setmarkingallocation",cmid:_classPrivateFieldGet(this,_cmid),selectedusers:selectedUsers.join(","),sesskey:_classPrivateFieldGet(this,_sesskey)}),show:!0,removeOnClose:!0});modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),modal.getRoot().find("form").submit()}))}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_setmarkingallocation_trigger",{showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=setmarkingallocation.min.js.mapbulkactions/grading/setmarkingworkflowstate.min.js000064400000006437152267113500016631 0ustar00define("mod_assign/bulkactions/grading/setmarkingworkflowstate",["exports","core/bulkactions/bulk_action","core/templates","core/str","core/modal_save_cancel","core/modal_events"],(function(_exports,_bulk_action,_templates,_str,_modal_save_cancel,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_events=_interopRequireDefault(_modal_events);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';var _cmid=new WeakMap,_sesskey=new WeakMap;class _default extends _bulk_action.default{constructor(cmid,sesskey){super(),_classPrivateFieldInitSpec(this,_cmid,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_sesskey,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_cmid,cmid),_classPrivateFieldSet(this,_sesskey,sesskey)}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="setmarkingworkflowstate"]'}async triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value)),modal=await _modal_save_cancel.default.create({title:await(0,_str.getString)("setmarkingworkflowstate","mod_assign"),buttons:{save:await(0,_str.getString)("batchoperationsetmarkingworkflowstate","mod_assign")},body:_templates.default.render("mod_assign/bulkactions/grading/bulk_action_modal_body",{text:await(0,_str.getString)("batchoperationconfirmsetmarkingworkflowstate","mod_assign"),operation:"setmarkingworkflowstate",cmid:_classPrivateFieldGet(this,_cmid),selectedusers:selectedUsers.join(","),sesskey:_classPrivateFieldGet(this,_sesskey)}),show:!0,removeOnClose:!0});modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),modal.getRoot().find("form").submit()}))}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_setmarkingworkflowstate_trigger",{showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=setmarkingworkflowstate.min.js.mapbulkactions/grading/setmarkingallocation.min.js.map000064400000010452152267113500016607 0ustar00{"version":3,"file":"setmarkingallocation.min.js","sources":["../../../src/bulkactions/grading/setmarkingallocation.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 * Bulk action for allocating markers in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/setmarkingallocation\n * @copyright 2024 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport ModalEvents from 'core/modal_events';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]:checked',\n};\n\nexport default class extends BulkAction {\n /** @type {number} The course module ID. */\n #cmid;\n\n /** @type {string} The session key. */\n #sesskey;\n\n /**\n * The class constructor.\n *\n * @param {number} cmid The course module ID.\n * @param {string} sesskey The session key.\n */\n constructor(cmid, sesskey) {\n super();\n this.#cmid = cmid;\n this.#sesskey = sesskey;\n }\n\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"setmarkingallocation\"]';\n }\n\n async triggerBulkAction() {\n const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);\n\n const modal = await SaveCancelModal.create({\n title: await getString('setmarkingallocation', 'mod_assign'),\n buttons: {\n save: await getString('batchoperationsetmarkingallocation', 'mod_assign'),\n },\n body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {\n text: await getString('batchoperationconfirmsetmarkingallocation', 'mod_assign'),\n operation: 'setmarkingallocation',\n cmid: this.#cmid,\n selectedusers: selectedUsers.join(','),\n sesskey: this.#sesskey\n }),\n show: true,\n removeOnClose: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n modal.getRoot().find('form').submit();\n });\n }\n\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('mod_assign/bulkactions/grading/bulk_setmarkingallocation_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n}\n"],"names":["Selectors","BulkAction","constructor","cmid","sesskey","getBulkActionTriggerSelector","selectedUsers","document","querySelectorAll","map","checkbox","value","modal","SaveCancelModal","create","title","buttons","save","body","Templates","render","text","operation","this","selectedusers","join","show","removeOnClose","getRoot","on","ModalEvents","e","preventDefault","find","submit","showInDropdown","index","showindropdown","isfirst"],"mappings":"6oDA6BMA,iCACsB,yHAGCC,qBAazBC,YAAYC,KAAMC,yLAEDD,0CACGC,SAGpBC,qCACW,iGAIDC,cAAgB,IAAIC,SAASC,iBAAiBR,mCAAmCS,KAAIC,UAAYA,SAASC,QAE1GC,YAAcC,2BAAgBC,OAAO,CACvCC,YAAa,kBAAU,uBAAwB,cAC/CC,QAAS,CACLC,WAAY,kBAAU,qCAAsC,eAEhEC,KAAMC,mBAAUC,OAAO,wDAAyD,CAC5EC,WAAY,kBAAU,4CAA6C,cACnEC,UAAW,uBACXnB,2BAAMoB,YACNC,cAAelB,cAAcmB,KAAK,KAClCrB,8BAASmB,iBAEbG,MAAM,EACNC,eAAe,IAInBf,MAAMgB,UAAUC,GAAGC,sBAAYb,MAAOc,IAClCA,EAAEC,iBACFpB,MAAMgB,UAAUK,KAAK,QAAQC,0CAIPC,eAAgBC,cACnCjB,mBAAUC,OAAO,mEAAoE,CACxFiB,eAAgBF,eAChBG,QAAmB,IAAVF"}bulkactions/grading/bulk_actions.min.js.map000064400000027401152267113500015054 0ustar00{"version":3,"file":"bulk_actions.min.js","sources":["../../../src/bulkactions/grading/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\n/**\n * Class for defining the bulk actions area in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/bulk_actions\n * @copyright 2024 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkActions from 'core/bulkactions/bulk_actions';\nimport GeneralAction from './general_action';\nimport DeleteAction from './delete';\nimport ExtendAction from './extend';\nimport MessageAction from './message';\nimport SetMarkingAllocationAction from './setmarkingallocation';\nimport SetMarkingWorkflowStateAction from './setmarkingworkflowstate';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]',\n selectBulkItemTrigger: 'input[type=\"checkbox\"][name=\"selectedusers\"], input[type=\"checkbox\"][name=\"selectall\"]',\n};\n\nexport default class extends BulkActions {\n /** @type {number} The course module ID. */\n #cmid;\n\n /** @type {boolean} Whether to show the extend action. */\n #extend;\n\n /** @type {boolean} Whether to show the grant extension action. */\n #grantAttempt;\n\n /** @type {boolean} Whether to show the set marking allocation action. */\n #markingAllocation;\n\n /** @type {string} Whether to show the message action. */\n #message;\n\n /** @type {Array} The list of plugin operations. */\n #pluginOperations;\n\n /** @type {boolean} Whether to show the remove submission action. */\n #removeSubmission;\n\n /** @type {string} The session key. */\n #sesskey;\n\n /** @type {boolean} Whether to show the revert to draft action. */\n #submissionDrafts;\n\n /** @type {boolean} Whether to show the set workflow state action. */\n #workflowState;\n\n /**\n * Returns the instance of the class.\n *\n * @param {Object} options - The options object.\n * @param {number} options.cmid - The course module ID.\n * @param {string} options.message - Whether to show the message action.\n * @param {boolean} options.submissiondrafts - Whether to show the revert to draft action.\n * @param {boolean} options.removesubmission - Whether to show the remove submission action.\n * @param {boolean} options.extend - Whether to show the grant extension action.\n * @param {boolean} options.grantattempt - Whether to show the grant attempt action.\n * @param {boolean} options.workflowstate - Whether to show the set workflow state action.\n * @param {boolean} options.markingallocation - Whether to show the set marking allocation action.\n * @param {Array} options.pluginoperations - The list of plugin operations.\n * @param {string} options.sesskey - The session key.\n * @returns {this} An instance of the anonymous class extending BulkActions.\n */\n static init(options) {\n return new this(options);\n }\n\n /**\n * The class constructor\n *\n * @param {Object} options - The options object.\n * @param {number} options.cmid - The course module ID.\n * @param {string} options.message - Whether to show the message action.\n * @param {boolean} options.submissiondrafts - Whether to show the revert to draft action.\n * @param {boolean} options.removesubmission - Whether to show the remove submission action.\n * @param {boolean} options.extend - Whether to show the grant extension action.\n * @param {boolean} options.grantattempt - Whether to show the grant attempt action.\n * @param {boolean} options.workflowstate - Whether to show the set workflow state action.\n * @param {boolean} options.markingallocation - Whether to show the set marking allocation action.\n * @param {Array} options.pluginoperations - The list of plugin operations.\n * @param {string} options.sesskey - The session key.\n */\n constructor({\n cmid, message, submissiondrafts, removesubmission, extend,\n grantattempt, workflowstate, markingallocation, pluginoperations, sesskey\n }) {\n super();\n this.#cmid = cmid;\n this.#message = message;\n this.#submissionDrafts = submissiondrafts;\n this.#removeSubmission = removesubmission;\n this.#extend = extend;\n this.#grantAttempt = grantattempt;\n this.#workflowState = workflowstate;\n this.#markingAllocation = markingallocation;\n this.#sesskey = sesskey;\n this.#pluginOperations = pluginoperations;\n }\n\n getBulkActions() {\n const actions = [\n new GeneralAction(\n this.#cmid,\n this.#sesskey,\n 'lock',\n getString('batchoperationlock', 'mod_assign'),\n Templates.renderPix('i/lock', 'core'),\n getString('locksubmissions', 'mod_assign'),\n getString('batchoperationconfirmlock', 'mod_assign'),\n getString('batchoperationlock', 'mod_assign'),\n ),\n new GeneralAction(\n this.#cmid,\n this.#sesskey,\n 'unlock',\n getString('batchoperationunlock', 'mod_assign'),\n Templates.renderPix('i/unlock', 'core'),\n getString('unlocksubmissions', 'mod_assign'),\n getString('batchoperationconfirmunlock', 'mod_assign'),\n getString('batchoperationunlock', 'mod_assign'),\n ),\n new GeneralAction(\n this.#cmid,\n this.#sesskey,\n 'downloadselected',\n getString('batchoperationdownloadselected', 'mod_assign'),\n Templates.renderPix('t/download', 'core'),\n getString('downloadselectedsubmissions', 'mod_assign'),\n getString('batchoperationconfirmdownloadselected', 'mod_assign'),\n getString('batchoperationdownloadselected', 'mod_assign'),\n ),\n ];\n\n if (this.#removeSubmission) {\n actions.push(new DeleteAction(this.#cmid, this.#sesskey));\n }\n\n if (this.#extend) {\n actions.push(new ExtendAction(this.#cmid, this.#sesskey));\n }\n\n if (this.#grantAttempt) {\n actions.push(\n new GeneralAction(\n this.#cmid,\n this.#sesskey,\n 'addattempt',\n getString('batchoperationaddattempt', 'mod_assign'),\n Templates.renderPix('t/add', 'core'),\n getString('addattempt', 'mod_assign'),\n getString('batchoperationconfirmaddattempt', 'mod_assign'),\n getString('batchoperationaddattempt', 'mod_assign'),\n )\n );\n }\n\n if (this.#workflowState) {\n actions.push(new SetMarkingWorkflowStateAction(this.#cmid, this.#sesskey));\n }\n\n if (this.#markingAllocation) {\n actions.push(new SetMarkingAllocationAction(this.#cmid, this.#sesskey));\n }\n\n if (this.#submissionDrafts) {\n actions.push(\n new GeneralAction(\n this.#cmid,\n this.#sesskey,\n 'reverttodraft',\n getString('batchoperationreverttodraft', 'mod_assign'),\n Templates.renderPix('e/undo', 'core'),\n getString('reverttodraft', 'mod_assign'),\n getString('batchoperationconfirmreverttodraft', 'mod_assign'),\n getString('batchoperationreverttodraft', 'mod_assign'),\n )\n );\n }\n\n if (this.#message) {\n actions.push(new MessageAction());\n }\n\n for (const operation of this.#pluginOperations) {\n actions.push(\n new GeneralAction(\n this.#cmid,\n this.#sesskey,\n operation.key,\n operation.label,\n operation.icon,\n operation.confirmationtitle,\n operation.confirmationquestion,\n )\n );\n }\n\n return actions;\n }\n\n getSelectedItems() {\n return document.querySelectorAll(`${Selectors.selectBulkItemCheckbox}:checked`);\n }\n\n registerItemSelectChangeEvent(eventHandler) {\n const itemSelectCheckboxes = document.querySelectorAll(Selectors.selectBulkItemTrigger);\n itemSelectCheckboxes.forEach((checkbox) => {\n checkbox.addEventListener('change', eventHandler.bind(this));\n });\n }\n\n deselectItem(selectedItem) {\n selectedItem.checked = false;\n selectedItem.closest('tr').classList.replace('selectedrow', 'unselectedrow');\n }\n}\n"],"names":["Selectors","BulkActions","options","this","constructor","cmid","message","submissiondrafts","removesubmission","extend","grantattempt","workflowstate","markingallocation","pluginoperations","sesskey","getBulkActions","actions","GeneralAction","Templates","renderPix","push","DeleteAction","ExtendAction","SetMarkingWorkflowStateAction","SetMarkingAllocationAction","MessageAction","operation","key","label","icon","confirmationtitle","confirmationquestion","getSelectedItems","document","querySelectorAll","registerItemSelectChangeEvent","eventHandler","forEach","checkbox","addEventListener","bind","deselectItem","selectedItem","checked","closest","classList","replace"],"mappings":"i9DAiCMA,iCACsB,+CADtBA,gCAEqB,kXAGEC,kCA+CbC,gBACD,IAAIC,KAAKD,SAkBpBE,sBAAYC,KACRA,KADQC,QACFA,QADEC,iBACOA,iBADPC,iBACyBA,iBADzBC,OAC2CA,OAD3CC,aAERA,aAFQC,cAEMA,cAFNC,kBAEqBA,kBAFrBC,iBAEwCA,iBAFxCC,QAE0DA,qxBAGrDT,0CACGC,sDACSC,+DACAC,qDACVC,iDACMC,wDACCC,6DACIC,uDACVE,sDACSD,kBAG7BE,uBACUC,QAAU,CACZ,IAAIC,8CACAd,kCACAA,eACA,QACA,kBAAU,qBAAsB,cAChCe,mBAAUC,UAAU,SAAU,SAC9B,kBAAU,kBAAmB,eAC7B,kBAAU,4BAA6B,eACvC,kBAAU,qBAAsB,eAEpC,IAAIF,8CACAd,kCACAA,eACA,UACA,kBAAU,uBAAwB,cAClCe,mBAAUC,UAAU,WAAY,SAChC,kBAAU,oBAAqB,eAC/B,kBAAU,8BAA+B,eACzC,kBAAU,uBAAwB,eAEtC,IAAIF,8CACAd,kCACAA,eACA,oBACA,kBAAU,iCAAkC,cAC5Ce,mBAAUC,UAAU,aAAc,SAClC,kBAAU,8BAA+B,eACzC,kBAAU,wCAAyC,eACnD,kBAAU,iCAAkC,sCAIhDhB,yBACAa,QAAQI,KAAK,IAAIC,sCAAalB,kCAAYA,uCAG1CA,eACAa,QAAQI,KAAK,IAAIE,uCAAanB,kCAAYA,uCAG1CA,qBACAa,QAAQI,KACJ,IAAIH,8CACAd,kCACAA,eACA,cACA,kBAAU,2BAA4B,cACtCe,mBAAUC,UAAU,QAAS,SAC7B,kBAAU,aAAc,eACxB,kBAAU,kCAAmC,eAC7C,kBAAU,2BAA4B,sCAK9ChB,sBACAa,QAAQI,KAAK,IAAIG,uDAA8BpB,kCAAYA,uCAG3DA,0BACAa,QAAQI,KAAK,IAAII,oDAA2BrB,kCAAYA,uCAGxDA,yBACAa,QAAQI,KACJ,IAAIH,8CACAd,kCACAA,eACA,iBACA,kBAAU,8BAA+B,cACzCe,mBAAUC,UAAU,SAAU,SAC9B,kBAAU,gBAAiB,eAC3B,kBAAU,qCAAsC,eAChD,kBAAU,8BAA+B,sCAKjDhB,gBACAa,QAAQI,KAAK,IAAIK,uBAGhB,MAAMC,mCAAavB,wBACpBa,QAAQI,KACJ,IAAIH,8CACAd,kCACAA,eACAuB,UAAUC,IACVD,UAAUE,MACVF,UAAUG,KACVH,UAAUI,kBACVJ,UAAUK,8BAKff,QAGXgB,0BACWC,SAASC,2BAAoBlC,8CAGxCmC,8BAA8BC,cACGH,SAASC,iBAAiBlC,iCAClCqC,SAASC,WAC1BA,SAASC,iBAAiB,SAAUH,aAAaI,KAAKrC,UAI9DsC,aAAaC,cACTA,aAAaC,SAAU,EACvBD,aAAaE,QAAQ,MAAMC,UAAUC,QAAQ,cAAe"}bulkactions/grading/extend.min.js000064400000006277152267113500013122 0ustar00define("mod_assign/bulkactions/grading/extend",["exports","core/bulkactions/bulk_action","core/templates","core/str","core/modal_save_cancel","core/modal_events"],(function(_exports,_bulk_action,_templates,_str,_modal_save_cancel,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_events=_interopRequireDefault(_modal_events);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';var _cmid=new WeakMap,_sesskey=new WeakMap;class _default extends _bulk_action.default{constructor(cmid,sesskey){super(),_classPrivateFieldInitSpec(this,_cmid,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_sesskey,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_cmid,cmid),_classPrivateFieldSet(this,_sesskey,sesskey)}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="grantextension"]'}async triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value)),modal=await _modal_save_cancel.default.create({title:await(0,_str.getString)("grantextension","mod_assign"),buttons:{save:await(0,_str.getString)("batchoperationgrantextension","mod_assign")},body:_templates.default.render("mod_assign/bulkactions/grading/bulk_action_modal_body",{text:await(0,_str.getString)("batchoperationconfirmgrantextension","mod_assign"),operation:"grantextension",cmid:_classPrivateFieldGet(this,_cmid),selectedusers:selectedUsers.join(","),sesskey:_classPrivateFieldGet(this,_sesskey)}),show:!0,removeOnClose:!0});modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),modal.getRoot().find("form").submit()}))}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_extend_trigger",{showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=extend.min.js.mapbulkactions/grading/message.min.js000064400000002625152267113500013250 0ustar00define("mod_assign/bulkactions/grading/message",["exports","core/bulkactions/bulk_action","core/templates","core_message/message_send_bulk"],(function(_exports,_bulk_action,_templates,_message_send_bulk){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Bulk action for messaging users in the assignment grading page. * * @module mod_assign/bulkactions/grading/message * @copyright 2024 Shamim Rezaie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';class _default extends _bulk_action.default{getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="message"]'}triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value));(0,_message_send_bulk.showModal)(selectedUsers)}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_message_trigger",{showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=message.min.js.mapbulkactions/grading/general_action.min.js.map000064400000013401152267113500015344 0ustar00{"version":3,"file":"general_action.min.js","sources":["../../../src/bulkactions/grading/general_action.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 action for general actions in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/general_action\n * @copyright 2024 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport Templates from 'core/templates';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport ModalEvents from 'core/modal_events';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]:checked',\n};\n\nexport default class extends BulkAction {\n\n /** @type {string} The action key. */\n actionKey;\n\n /** @type {number} The course module ID. */\n #cmid;\n\n /** @type {Promise} The action button's icon. */\n #buttonIcon;\n\n /** @type {Promise} The action button's label. */\n #buttonLabel;\n\n /** @type {Promise} Title of the confirmation dialog. */\n #confirmationTitle;\n\n /** @type {Promise} Question of the confirmation dialog. */\n #confirmationQuestion;\n\n /** @type {Promise} Text for the confirmation yes button. */\n #confirmationYes;\n\n /** @type {string} The session key. */\n #sesskey;\n\n /**\n * The class constructor.\n *\n * @param {int} cmid The course module ID.\n * @param {string} sesskey The session key.\n * @param {string} actionKey The action key.\n * @param {Promise} buttonLabel The action button's label.\n * @param {Promise} buttonIcon The action button's icon.\n * @param {Promise} confirmationTitle Title of the confirmation dialog.\n * @param {Promise} confirmationQuestion Question of the confirmation dialog.\n * @param {Promise} confirmationYes Text for the confirmation yes button.\n */\n constructor(cmid, sesskey, actionKey, buttonLabel, buttonIcon, confirmationTitle, confirmationQuestion, confirmationYes) {\n super();\n this.#cmid = cmid;\n this.#sesskey = sesskey;\n this.actionKey = actionKey;\n this.#buttonLabel = buttonLabel;\n this.#buttonIcon = buttonIcon;\n this.#confirmationTitle = confirmationTitle;\n this.#confirmationQuestion = confirmationQuestion;\n this.#confirmationYes = confirmationYes;\n }\n\n getBulkActionTriggerSelector() {\n return `[data-type=\"bulkactions\"] [data-action=\"${this.actionKey}\"]`;\n }\n\n async triggerBulkAction() {\n const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);\n\n const modal = await SaveCancelModal.create({\n title: await this.#confirmationTitle,\n buttons: {\n save: await this.#confirmationYes,\n },\n body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {\n text: await this.#confirmationQuestion,\n operation: this.actionKey,\n cmid: this.#cmid,\n selectedusers: selectedUsers.join(','),\n sesskey: this.#sesskey\n }),\n show: true,\n removeOnClose: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, () => {\n modal.getRoot().find('form').submit();\n });\n }\n\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('mod_assign/bulkactions/grading/bulk_general_action_trigger', {\n action: this.actionKey,\n title: await this.#buttonLabel,\n icon: await this.#buttonIcon,\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n}\n"],"names":["Selectors","BulkAction","constructor","cmid","sesskey","actionKey","buttonLabel","buttonIcon","confirmationTitle","confirmationQuestion","confirmationYes","getBulkActionTriggerSelector","this","selectedUsers","document","querySelectorAll","map","checkbox","value","modal","SaveCancelModal","create","title","buttons","save","body","Templates","render","text","operation","selectedusers","join","show","removeOnClose","getRoot","on","ModalEvents","find","submit","showInDropdown","index","action","icon","showindropdown","isfirst"],"mappings":"unDA4BMA,iCACsB,wQAGCC,qBAsCzBC,YAAYC,KAAMC,QAASC,UAAWC,YAAaC,WAAYC,kBAAmBC,qBAAsBC,muBAEvFP,0CACGC,cACXC,UAAYA,kDACGC,oDACDC,0DACOC,oEACGC,kEACLC,iBAG5BC,uFACsDC,KAAKP,gDAIjDQ,cAAgB,IAAIC,SAASC,iBAAiBf,mCAAmCgB,KAAIC,UAAYA,SAASC,QAE1GC,YAAcC,2BAAgBC,OAAO,CACvCC,kCAAaV,yBACbW,QAAS,CACLC,iCAAYZ,wBAEhBa,KAAMC,mBAAUC,OAAO,wDAAyD,CAC5EC,iCAAYhB,4BACZiB,UAAWjB,KAAKP,UAChBF,2BAAMS,YACNkB,cAAejB,cAAckB,KAAK,KAClC3B,8BAASQ,iBAEboB,MAAM,EACNC,eAAe,IAInBd,MAAMe,UAAUC,GAAGC,sBAAYZ,MAAM,KACjCL,MAAMe,UAAUG,KAAK,QAAQC,0CAIPC,eAAgBC,cACnCd,mBAAUC,OAAO,6DAA8D,CAClFc,OAAQ7B,KAAKP,UACbiB,kCAAaV,mBACb8B,iCAAY9B,kBACZ+B,eAAgBJ,eAChBK,QAAmB,IAAVJ"}bulkactions/grading/setmarkingworkflowstate.min.js.map000064400000010550152267113500017374 0ustar00{"version":3,"file":"setmarkingworkflowstate.min.js","sources":["../../../src/bulkactions/grading/setmarkingworkflowstate.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 action for setting marking workflow state in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/setmarkingworkflowstate\n * @copyright 2024 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport ModalEvents from 'core/modal_events';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]:checked',\n};\n\nexport default class extends BulkAction {\n /** @type {number} The course module ID. */\n #cmid;\n\n /** @type {string} The session key. */\n #sesskey;\n\n /**\n * The class constructor.\n *\n * @param {number} cmid The course module ID.\n * @param {string} sesskey The session key.\n */\n constructor(cmid, sesskey) {\n super();\n this.#cmid = cmid;\n this.#sesskey = sesskey;\n }\n\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"setmarkingworkflowstate\"]';\n }\n\n async triggerBulkAction() {\n const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);\n\n const modal = await SaveCancelModal.create({\n title: await getString('setmarkingworkflowstate', 'mod_assign'),\n buttons: {\n save: await getString('batchoperationsetmarkingworkflowstate', 'mod_assign'),\n },\n body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {\n text: await getString('batchoperationconfirmsetmarkingworkflowstate', 'mod_assign'),\n operation: 'setmarkingworkflowstate',\n cmid: this.#cmid,\n selectedusers: selectedUsers.join(','),\n sesskey: this.#sesskey\n }),\n show: true,\n removeOnClose: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n modal.getRoot().find('form').submit();\n });\n }\n\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('mod_assign/bulkactions/grading/bulk_setmarkingworkflowstate_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n}\n"],"names":["Selectors","BulkAction","constructor","cmid","sesskey","getBulkActionTriggerSelector","selectedUsers","document","querySelectorAll","map","checkbox","value","modal","SaveCancelModal","create","title","buttons","save","body","Templates","render","text","operation","this","selectedusers","join","show","removeOnClose","getRoot","on","ModalEvents","e","preventDefault","find","submit","showInDropdown","index","showindropdown","isfirst"],"mappings":"gpDA6BMA,iCACsB,yHAGCC,qBAazBC,YAAYC,KAAMC,yLAEDD,0CACGC,SAGpBC,qCACW,oGAIDC,cAAgB,IAAIC,SAASC,iBAAiBR,mCAAmCS,KAAIC,UAAYA,SAASC,QAE1GC,YAAcC,2BAAgBC,OAAO,CACvCC,YAAa,kBAAU,0BAA2B,cAClDC,QAAS,CACLC,WAAY,kBAAU,wCAAyC,eAEnEC,KAAMC,mBAAUC,OAAO,wDAAyD,CAC5EC,WAAY,kBAAU,+CAAgD,cACtEC,UAAW,0BACXnB,2BAAMoB,YACNC,cAAelB,cAAcmB,KAAK,KAClCrB,8BAASmB,iBAEbG,MAAM,EACNC,eAAe,IAInBf,MAAMgB,UAAUC,GAAGC,sBAAYb,MAAOc,IAClCA,EAAEC,iBACFpB,MAAMgB,UAAUK,KAAK,QAAQC,0CAIPC,eAAgBC,cACnCjB,mBAAUC,OAAO,sEAAuE,CAC3FiB,eAAgBF,eAChBG,QAAmB,IAAVF"}bulkactions/grading/extend.min.js.map000064400000010366152267113500013670 0ustar00{"version":3,"file":"extend.min.js","sources":["../../../src/bulkactions/grading/extend.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 action to extend the assignment deadline in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/extend\n * @copyright 2024 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport ModalEvents from 'core/modal_events';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]:checked',\n};\n\nexport default class extends BulkAction {\n /** @type {number} The course module ID. */\n #cmid;\n\n /** @type {string} The session key. */\n #sesskey;\n\n /**\n * The class constructor.\n *\n * @param {number} cmid The course module ID.\n * @param {string} sesskey The session key.\n */\n constructor(cmid, sesskey) {\n super();\n this.#cmid = cmid;\n this.#sesskey = sesskey;\n }\n\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"grantextension\"]';\n }\n\n async triggerBulkAction() {\n const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);\n\n const modal = await SaveCancelModal.create({\n title: await getString('grantextension', 'mod_assign'),\n buttons: {\n save: await getString('batchoperationgrantextension', 'mod_assign'),\n },\n body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {\n text: await getString('batchoperationconfirmgrantextension', 'mod_assign'),\n operation: 'grantextension',\n cmid: this.#cmid,\n selectedusers: selectedUsers.join(','),\n sesskey: this.#sesskey\n }),\n show: true,\n removeOnClose: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n modal.getRoot().find('form').submit();\n });\n }\n\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('mod_assign/bulkactions/grading/bulk_extend_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n}\n"],"names":["Selectors","BulkAction","constructor","cmid","sesskey","getBulkActionTriggerSelector","selectedUsers","document","querySelectorAll","map","checkbox","value","modal","SaveCancelModal","create","title","buttons","save","body","Templates","render","text","operation","this","selectedusers","join","show","removeOnClose","getRoot","on","ModalEvents","e","preventDefault","find","submit","showInDropdown","index","showindropdown","isfirst"],"mappings":"+nDA6BMA,iCACsB,yHAGCC,qBAazBC,YAAYC,KAAMC,yLAEDD,0CACGC,SAGpBC,qCACW,2FAIDC,cAAgB,IAAIC,SAASC,iBAAiBR,mCAAmCS,KAAIC,UAAYA,SAASC,QAE1GC,YAAcC,2BAAgBC,OAAO,CACvCC,YAAa,kBAAU,iBAAkB,cACzCC,QAAS,CACLC,WAAY,kBAAU,+BAAgC,eAE1DC,KAAMC,mBAAUC,OAAO,wDAAyD,CAC5EC,WAAY,kBAAU,sCAAuC,cAC7DC,UAAW,iBACXnB,2BAAMoB,YACNC,cAAelB,cAAcmB,KAAK,KAClCrB,8BAASmB,iBAEbG,MAAM,EACNC,eAAe,IAInBf,MAAMgB,UAAUC,GAAGC,sBAAYb,MAAOc,IAClCA,EAAEC,iBACFpB,MAAMgB,UAAUK,KAAK,QAAQC,0CAIPC,eAAgBC,cACnCjB,mBAAUC,OAAO,qDAAsD,CAC1EiB,eAAgBF,eAChBG,QAAmB,IAAVF"}bulkactions/grading/bulk_actions.min.js000064400000017144152267113500014303 0ustar00define("mod_assign/bulkactions/grading/bulk_actions",["exports","core/bulkactions/bulk_actions","./general_action","./delete","./extend","./message","./setmarkingallocation","./setmarkingworkflowstate","core/templates","core/str"],(function(_exports,_bulk_actions,_general_action,_delete,_extend2,_message2,_setmarkingallocation,_setmarkingworkflowstate,_templates,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_actions=_interopRequireDefault(_bulk_actions),_general_action=_interopRequireDefault(_general_action),_delete=_interopRequireDefault(_delete),_extend2=_interopRequireDefault(_extend2),_message2=_interopRequireDefault(_message2),_setmarkingallocation=_interopRequireDefault(_setmarkingallocation),_setmarkingworkflowstate=_interopRequireDefault(_setmarkingworkflowstate),_templates=_interopRequireDefault(_templates);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]',Selectors_selectBulkItemTrigger='input[type="checkbox"][name="selectedusers"], input[type="checkbox"][name="selectall"]';var _cmid=new WeakMap,_extend=new WeakMap,_grantAttempt=new WeakMap,_markingAllocation=new WeakMap,_message=new WeakMap,_pluginOperations=new WeakMap,_removeSubmission=new WeakMap,_sesskey=new WeakMap,_submissionDrafts=new WeakMap,_workflowState=new WeakMap;class _default extends _bulk_actions.default{static init(options){return new this(options)}constructor(_ref){let{cmid:cmid,message:message,submissiondrafts:submissiondrafts,removesubmission:removesubmission,extend:extend,grantattempt:grantattempt,workflowstate:workflowstate,markingallocation:markingallocation,pluginoperations:pluginoperations,sesskey:sesskey}=_ref;super(),_classPrivateFieldInitSpec(this,_cmid,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_extend,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_grantAttempt,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_markingAllocation,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_message,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_pluginOperations,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_removeSubmission,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_sesskey,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_submissionDrafts,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_workflowState,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_cmid,cmid),_classPrivateFieldSet(this,_message,message),_classPrivateFieldSet(this,_submissionDrafts,submissiondrafts),_classPrivateFieldSet(this,_removeSubmission,removesubmission),_classPrivateFieldSet(this,_extend,extend),_classPrivateFieldSet(this,_grantAttempt,grantattempt),_classPrivateFieldSet(this,_workflowState,workflowstate),_classPrivateFieldSet(this,_markingAllocation,markingallocation),_classPrivateFieldSet(this,_sesskey,sesskey),_classPrivateFieldSet(this,_pluginOperations,pluginoperations)}getBulkActions(){const actions=[new _general_action.default(_classPrivateFieldGet(this,_cmid),_classPrivateFieldGet(this,_sesskey),"lock",(0,_str.getString)("batchoperationlock","mod_assign"),_templates.default.renderPix("i/lock","core"),(0,_str.getString)("locksubmissions","mod_assign"),(0,_str.getString)("batchoperationconfirmlock","mod_assign"),(0,_str.getString)("batchoperationlock","mod_assign")),new _general_action.default(_classPrivateFieldGet(this,_cmid),_classPrivateFieldGet(this,_sesskey),"unlock",(0,_str.getString)("batchoperationunlock","mod_assign"),_templates.default.renderPix("i/unlock","core"),(0,_str.getString)("unlocksubmissions","mod_assign"),(0,_str.getString)("batchoperationconfirmunlock","mod_assign"),(0,_str.getString)("batchoperationunlock","mod_assign")),new _general_action.default(_classPrivateFieldGet(this,_cmid),_classPrivateFieldGet(this,_sesskey),"downloadselected",(0,_str.getString)("batchoperationdownloadselected","mod_assign"),_templates.default.renderPix("t/download","core"),(0,_str.getString)("downloadselectedsubmissions","mod_assign"),(0,_str.getString)("batchoperationconfirmdownloadselected","mod_assign"),(0,_str.getString)("batchoperationdownloadselected","mod_assign"))];_classPrivateFieldGet(this,_removeSubmission)&&actions.push(new _delete.default(_classPrivateFieldGet(this,_cmid),_classPrivateFieldGet(this,_sesskey))),_classPrivateFieldGet(this,_extend)&&actions.push(new _extend2.default(_classPrivateFieldGet(this,_cmid),_classPrivateFieldGet(this,_sesskey))),_classPrivateFieldGet(this,_grantAttempt)&&actions.push(new _general_action.default(_classPrivateFieldGet(this,_cmid),_classPrivateFieldGet(this,_sesskey),"addattempt",(0,_str.getString)("batchoperationaddattempt","mod_assign"),_templates.default.renderPix("t/add","core"),(0,_str.getString)("addattempt","mod_assign"),(0,_str.getString)("batchoperationconfirmaddattempt","mod_assign"),(0,_str.getString)("batchoperationaddattempt","mod_assign"))),_classPrivateFieldGet(this,_workflowState)&&actions.push(new _setmarkingworkflowstate.default(_classPrivateFieldGet(this,_cmid),_classPrivateFieldGet(this,_sesskey))),_classPrivateFieldGet(this,_markingAllocation)&&actions.push(new _setmarkingallocation.default(_classPrivateFieldGet(this,_cmid),_classPrivateFieldGet(this,_sesskey))),_classPrivateFieldGet(this,_submissionDrafts)&&actions.push(new _general_action.default(_classPrivateFieldGet(this,_cmid),_classPrivateFieldGet(this,_sesskey),"reverttodraft",(0,_str.getString)("batchoperationreverttodraft","mod_assign"),_templates.default.renderPix("e/undo","core"),(0,_str.getString)("reverttodraft","mod_assign"),(0,_str.getString)("batchoperationconfirmreverttodraft","mod_assign"),(0,_str.getString)("batchoperationreverttodraft","mod_assign"))),_classPrivateFieldGet(this,_message)&&actions.push(new _message2.default);for(const operation of _classPrivateFieldGet(this,_pluginOperations))actions.push(new _general_action.default(_classPrivateFieldGet(this,_cmid),_classPrivateFieldGet(this,_sesskey),operation.key,operation.label,operation.icon,operation.confirmationtitle,operation.confirmationquestion));return actions}getSelectedItems(){return document.querySelectorAll("".concat(Selectors_selectBulkItemCheckbox,":checked"))}registerItemSelectChangeEvent(eventHandler){document.querySelectorAll(Selectors_selectBulkItemTrigger).forEach((checkbox=>{checkbox.addEventListener("change",eventHandler.bind(this))}))}deselectItem(selectedItem){selectedItem.checked=!1,selectedItem.closest("tr").classList.replace("selectedrow","unselectedrow")}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=bulk_actions.min.js.mapbulkactions/grading/delete.min.js000064400000006325152267113500013067 0ustar00define("mod_assign/bulkactions/grading/delete",["exports","core/bulkactions/bulk_action","core/templates","core/str","core/modal_delete_cancel","core/modal_events"],(function(_exports,_bulk_action,_templates,_str,_modal_delete_cancel,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_templates=_interopRequireDefault(_templates),_modal_delete_cancel=_interopRequireDefault(_modal_delete_cancel),_modal_events=_interopRequireDefault(_modal_events);const Selectors_selectBulkItemCheckbox='input[type="checkbox"][name="selectedusers"]:checked';var _cmid=new WeakMap,_sesskey=new WeakMap;class _default extends _bulk_action.default{constructor(cmid,sesskey){super(),_classPrivateFieldInitSpec(this,_cmid,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_sesskey,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_cmid,cmid),_classPrivateFieldSet(this,_sesskey,sesskey)}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="removesubmission"]'}async triggerBulkAction(){const selectedUsers=[...document.querySelectorAll(Selectors_selectBulkItemCheckbox)].map((checkbox=>checkbox.value)),modal=await _modal_delete_cancel.default.create({title:await(0,_str.getString)("removesubmission","mod_assign"),buttons:{save:await(0,_str.getString)("batchoperationremovesubmission","mod_assign")},body:_templates.default.render("mod_assign/bulkactions/grading/bulk_action_modal_body",{text:await(0,_str.getString)("batchoperationconfirmremovesubmission","mod_assign"),operation:"removesubmission",cmid:_classPrivateFieldGet(this,_cmid),selectedusers:selectedUsers.join(","),sesskey:_classPrivateFieldGet(this,_sesskey)}),show:!0,removeOnClose:!0});modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.getRoot().find("form").submit()}))}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("mod_assign/bulkactions/grading/bulk_delete_trigger",{showindropdown:showInDropdown,isfirst:0===index})}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=delete.min.js.mapbulkactions/grading/delete.min.js.map000064400000010414152267113500013635 0ustar00{"version":3,"file":"delete.min.js","sources":["../../../src/bulkactions/grading/delete.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 action for removing submissions in the assignment grading page.\n *\n * @module mod_assign/bulkactions/grading/delete\n * @copyright 2024 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport DeleteCancelModal from 'core/modal_delete_cancel';\nimport ModalEvents from 'core/modal_events';\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"][name=\"selectedusers\"]:checked',\n};\n\nexport default class extends BulkAction {\n /** @type {number} The course module ID. */\n #cmid;\n\n /** @type {string} The session key. */\n #sesskey;\n\n /**\n * The class constructor.\n *\n * @param {number} cmid The course module ID.\n * @param {string} sesskey The session key.\n */\n constructor(cmid, sesskey) {\n super();\n this.#cmid = cmid;\n this.#sesskey = sesskey;\n }\n\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"removesubmission\"]';\n }\n\n async triggerBulkAction() {\n const selectedUsers = [...document.querySelectorAll(Selectors.selectBulkItemCheckbox)].map(checkbox => checkbox.value);\n\n const modal = await DeleteCancelModal.create({\n title: await getString('removesubmission', 'mod_assign'),\n buttons: {\n save: await getString('batchoperationremovesubmission', 'mod_assign'),\n },\n body: Templates.render('mod_assign/bulkactions/grading/bulk_action_modal_body', {\n text: await getString('batchoperationconfirmremovesubmission', 'mod_assign'),\n operation: 'removesubmission',\n cmid: this.#cmid,\n selectedusers: selectedUsers.join(','),\n sesskey: this.#sesskey\n }),\n show: true,\n removeOnClose: true,\n });\n\n // Handle delete event.\n modal.getRoot().on(ModalEvents.delete, (e) => {\n e.preventDefault();\n modal.getRoot().find('form').submit();\n });\n }\n\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('mod_assign/bulkactions/grading/bulk_delete_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n}\n"],"names":["Selectors","BulkAction","constructor","cmid","sesskey","getBulkActionTriggerSelector","selectedUsers","document","querySelectorAll","map","checkbox","value","modal","DeleteCancelModal","create","title","buttons","save","body","Templates","render","text","operation","this","selectedusers","join","show","removeOnClose","getRoot","on","ModalEvents","delete","e","preventDefault","find","submit","showInDropdown","index","showindropdown","isfirst"],"mappings":"uoDA6BMA,iCACsB,yHAGCC,qBAazBC,YAAYC,KAAMC,yLAEDD,0CACGC,SAGpBC,qCACW,6FAIDC,cAAgB,IAAIC,SAASC,iBAAiBR,mCAAmCS,KAAIC,UAAYA,SAASC,QAE1GC,YAAcC,6BAAkBC,OAAO,CACzCC,YAAa,kBAAU,mBAAoB,cAC3CC,QAAS,CACLC,WAAY,kBAAU,iCAAkC,eAE5DC,KAAMC,mBAAUC,OAAO,wDAAyD,CAC5EC,WAAY,kBAAU,wCAAyC,cAC/DC,UAAW,mBACXnB,2BAAMoB,YACNC,cAAelB,cAAcmB,KAAK,KAClCrB,8BAASmB,iBAEbG,MAAM,EACNC,eAAe,IAInBf,MAAMgB,UAAUC,GAAGC,sBAAYC,QAASC,IACpCA,EAAEC,iBACFrB,MAAMgB,UAAUM,KAAK,QAAQC,0CAIPC,eAAgBC,cACnClB,mBAAUC,OAAO,qDAAsD,CAC1EkB,eAAgBF,eAChBG,QAAmB,IAAVF"}override_modal_manager.min.js000064400000003107152267113500012354 0ustar00define("mod_assign/override_modal_manager",["exports","core/str","mod_assign/override_delete_modal"],(function(_exports,_str,_override_delete_modal){var obj; /** * Modal manager for the override delete modal. * * @module mod_assign/override_modal_manager * @copyright 2025 Catalyst IT Australia Pty Ltd * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_override_delete_modal=(obj=_override_delete_modal)&&obj.__esModule?obj:{default:obj};const SELECTORS_DELETE_BUTTONS=".delete-override",SELECTORS_PARENT_CONTAINER="#assignoverrides",SELECTORS_USER_GROUP_NAME=".usergroupname";_exports.init=(mode,showRecalculationCheckBox)=>{document.querySelector(SELECTORS_PARENT_CONTAINER).addEventListener("click",(async event=>{const button=event.target.closest(SELECTORS_DELETE_BUTTONS);if(!button)return;event.preventDefault();const name=event.target.closest("tr").querySelector(SELECTORS_USER_GROUP_NAME).innerText,confirmMessage=await getConfirmMessage(mode,name);_override_delete_modal.default.create({templateContext:{confirmmessage:confirmMessage,showpenaltyrecalculation:showRecalculationCheckBox},overrideId:button.getAttribute("data-overrideid"),sessionKey:button.getAttribute("data-sesskey")})}))};const getConfirmMessage=(mode,name)=>{switch(mode){case"group":return(0,_str.get_string)("overridedeletegroupsure","assign",name);case"user":return(0,_str.get_string)("overridedeleteusersure","assign",name);default:return""}}})); //# sourceMappingURL=override_modal_manager.min.js.mapoverride_modal_manager.min.js.map000064400000007435152267113500013140 0ustar00{"version":3,"file":"override_modal_manager.min.js","sources":["../src/override_modal_manager.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 * Modal manager for the override delete modal.\n *\n * @module mod_assign/override_modal_manager\n * @copyright 2025 Catalyst IT Australia Pty Ltd\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport OverrideDeleteModal from 'mod_assign/override_delete_modal';\n\nconst SELECTORS = {\n DELETE_BUTTONS: '.delete-override',\n PARENT_CONTAINER: '#assignoverrides',\n USER_GROUP_NAME: '.usergroupname',\n};\n\n/**\n * Initialise the modal manager.\n *\n * @param {string} mode The override mode.\n * @param {boolean} showRecalculationCheckBox Whether to show the recalculation checkbox.\n */\nexport const init = (mode, showRecalculationCheckBox) => {\n document.querySelector(SELECTORS.PARENT_CONTAINER).addEventListener('click', async(event) => {\n const button = event.target.closest(SELECTORS.DELETE_BUTTONS);\n\n if (!button) {\n return;\n }\n\n event.preventDefault();\n\n // Get the name of the user or group from the first column of the row.\n const name = event.target.closest('tr').querySelector(SELECTORS.USER_GROUP_NAME).innerText;\n\n // Get the confirm message for the modal.\n const confirmMessage = await getConfirmMessage(mode, name);\n\n // Create and show the modal.\n OverrideDeleteModal.create({\n templateContext: {\n confirmmessage: confirmMessage,\n showpenaltyrecalculation: showRecalculationCheckBox,\n },\n overrideId: button.getAttribute('data-overrideid'),\n sessionKey: button.getAttribute('data-sesskey'),\n });\n });\n};\n\n/**\n * Get the confirm message for the modal.\n *\n * @param {string} mode The override mode.\n * @param {boolean} name The name of the user or group.\n * @returns {Promise} The confirm message.\n */\nconst getConfirmMessage = (mode, name) => {\n switch (mode) {\n case \"group\":\n return getString('overridedeletegroupsure', 'assign', name);\n case \"user\":\n return getString('overridedeleteusersure', 'assign', name);\n default:\n return \"\";\n }\n};\n"],"names":["SELECTORS","mode","showRecalculationCheckBox","document","querySelector","addEventListener","async","button","event","target","closest","preventDefault","name","innerText","confirmMessage","getConfirmMessage","create","templateContext","confirmmessage","showpenaltyrecalculation","overrideId","getAttribute","sessionKey"],"mappings":";;;;;;;8KA0BMA,yBACc,mBADdA,2BAEgB,mBAFhBA,0BAGe,+BASD,CAACC,KAAMC,6BACvBC,SAASC,cAAcJ,4BAA4BK,iBAAiB,SAASC,MAAAA,cACnEC,OAASC,MAAMC,OAAOC,QAAQV,8BAE/BO,cAILC,MAAMG,uBAGAC,KAAOJ,MAAMC,OAAOC,QAAQ,MAAMN,cAAcJ,2BAA2Ba,UAG3EC,qBAAuBC,kBAAkBd,KAAMW,qCAGjCI,OAAO,CACvBC,gBAAiB,CACbC,eAAgBJ,eAChBK,yBAA0BjB,2BAE9BkB,WAAYb,OAAOc,aAAa,mBAChCC,WAAYf,OAAOc,aAAa,4BAYtCN,kBAAoB,CAACd,KAAMW,eACrBX,UACC,eACM,mBAAU,0BAA2B,SAAUW,UACrD,cACM,mBAAU,yBAA0B,SAAUA,oBAE9C"}quick_grading.min.js000064400000004352152267113500010501 0ustar00define("mod_assign/quick_grading",["exports","core_form/submit"],(function(_exports,formSubmit){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,formSubmit=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj} /** * Module for the quick grading functionality on the submissions page. * * @module mod_assign/quick_grading * @copyright 2024 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */(formSubmit);const Selectors_quickGradingSaveRegion='[data-region="quick-grading-save"]',Selectors_notifyStudentsCheckbox='input[type="checkbox"][name="sendstudentnotifications"]',Selectors_notifyStudentsHidden='input[type="hidden"][name="sendstudentnotifications"]',Selectors_saveButton='button[type="submit"]';_exports.init=()=>{const quickGradingSaveRegion=document.querySelector(Selectors_quickGradingSaveRegion);if(quickGradingSaveRegion){const quickGradingSaveButton=quickGradingSaveRegion.querySelector(Selectors_saveButton);formSubmit.init(quickGradingSaveButton),quickGradingSaveRegion.addEventListener("change",(e=>{const notifyStudentsCheckbox=e.target.closest(Selectors_notifyStudentsCheckbox);if(notifyStudentsCheckbox){notifyStudentsCheckbox.parentNode.querySelector(Selectors_notifyStudentsHidden).disabled=notifyStudentsCheckbox.checked}}))}}})); //# sourceMappingURL=quick_grading.min.js.mapgrading_review_panel.min.js000064400000005130152267113500012040 0ustar00/** * Javascript controller for the "Review" panel at the left of the page. * * @module mod_assign/grading_review_panel * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_assign/grading_review_panel",["jquery","mod_assign/grading_events"],(function($,GradingEvents){var GradingReviewPanel=function(){this._region=$('[data-region="review-panel-content"]'),this.registerEventListeners()};return GradingReviewPanel.prototype._region=null,GradingReviewPanel.prototype.getReviewPanel=function(pluginname){return void 0===this._region.data("panel-owner")&&this._region.data("review-panel-plugin",pluginname),this._region.data("review-panel-plugin")==pluginname&&this._region[0]},GradingReviewPanel.prototype.getTogglePanelButton=function(){return this.getPanelElement().find('[data-region="review-panel-toggle"]')},GradingReviewPanel.prototype.getPanelElement=function(){return $('[data-region="review-panel"]')},GradingReviewPanel.prototype.getPanelContentElement=function(){return $('[data-region="review-panel-content"]')},GradingReviewPanel.prototype.togglePanel=function(){this.getPanelElement().hasClass("collapsed")?$(document).trigger(GradingEvents.EXPAND_REVIEW_PANEL):$(document).trigger(GradingEvents.COLLAPSE_REVIEW_PANEL)},GradingReviewPanel.prototype.collapsePanel=function(){this.getPanelElement().addClass("collapsed").removeClass("grade-panel-collapsed"),this.getPanelContentElement().attr("aria-hidden",!0)},GradingReviewPanel.prototype.expandPanel=function(){this.getPanelElement().removeClass("collapsed"),this.getPanelContentElement().removeAttr("aria-hidden")},GradingReviewPanel.prototype.registerEventListeners=function(){var toggleReviewPanelButton=this.getTogglePanelButton();toggleReviewPanelButton.click(function(e){this.togglePanel(),e.preventDefault()}.bind(this)),toggleReviewPanelButton.keydown(function(e){e.metaKey||e.shiftKey||e.altKey||e.ctrlKey||13!==e.keyCode&&32!==e.keyCode||(this.togglePanel(),e.preventDefault())}.bind(this));var docElement=$(document);docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL,function(){this.collapsePanel()}.bind(this)),docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL,function(){this.expandPanel(),this.getPanelElement().addClass("grade-panel-collapsed")}.bind(this)),docElement.on(GradingEvents.EXPAND_REVIEW_PANEL,function(){this.expandPanel()}.bind(this)),docElement.on(GradingEvents.EXPAND_GRADE_PANEL,function(){this.getPanelElement().removeClass("grade-panel-collapsed")}.bind(this))},GradingReviewPanel})); //# sourceMappingURL=grading_review_panel.min.js.mapgrading_form_change_checker.min.js.map000064400000005546152267113500014103 0ustar00{"version":3,"file":"grading_form_change_checker.min.js","sources":["../src/grading_form_change_checker.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 * Simple method to check for changes to a form between two points in time.\n *\n * @module mod_assign/grading_form_change_checker\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery'], function($) {\n\n return {\n /**\n * Save the values in the form to a data attribute so they can be compared later for changes.\n *\n * @method saveFormState\n * @param {String} selector The selector for the form element.\n */\n saveFormState: function(selector) {\n $(selector).trigger('save-form-state');\n var data = $(selector).serialize();\n $(selector).data('saved-form-state', data);\n },\n\n /**\n * Compare the current values in the form to the previously saved state.\n *\n * @method checkFormForChanges\n * @param {String} selector The selector for the form element.\n * @return {Boolean} True if there are changes to the form data.\n */\n checkFormForChanges: function(selector) {\n\n $(selector).trigger('save-form-state');\n\n var data = $(selector).serialize(),\n previousdata = $(selector).data('saved-form-state'),\n unresolvederror = $(selector).data('unresolved-error');\n\n if (unresolvederror) {\n return true;\n }\n if (typeof previousdata === 'undefined') {\n return false;\n }\n return (previousdata != data);\n }\n };\n});\n"],"names":["define","$","saveFormState","selector","trigger","data","serialize","checkFormForChanges","previousdata"],"mappings":";;;;;;;;AAuBAA,gDAAO,CAAC,WAAW,SAASC,SAEjB,CAOHC,cAAe,SAASC,UACpBF,EAAEE,UAAUC,QAAQ,uBAChBC,KAAOJ,EAAEE,UAAUG,YACvBL,EAAEE,UAAUE,KAAK,mBAAoBA,OAUzCE,oBAAqB,SAASJ,UAE1BF,EAAEE,UAAUC,QAAQ,uBAEhBC,KAAOJ,EAAEE,UAAUG,YACnBE,aAAeP,EAAEE,UAAUE,KAAK,4BACdJ,EAAEE,UAAUE,KAAK,0BAKX,IAAjBG,cAGHA,cAAgBH"}override_form.min.js.map000064400000004176152267113500011314 0ustar00{"version":3,"file":"override_form.min.js","sources":["../src/override_form.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 * A javascript module to enhance the override form.\n *\n * @copyright 2019 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as FormChangeChecker from 'core_form/changechecker';\n\nexport const init = (formId, selectElementName) => {\n const form = document.getElementById(formId);\n const selectElement = form.querySelector(`[name=\"${selectElementName}\"]`);\n\n $(selectElement).on('change', () => {\n const inputElement = document.createElement('input');\n inputElement.setAttribute('type', 'hidden');\n inputElement.setAttribute('name', 'userchange');\n inputElement.setAttribute('value', true);\n\n form.appendChild(inputElement);\n\n FormChangeChecker.markFormSubmitted(inputElement);\n\n form.submit();\n });\n};\n"],"names":["formId","selectElementName","form","document","getElementById","selectElement","querySelector","on","inputElement","createElement","setAttribute","appendChild","FormChangeChecker","markFormSubmitted","submit"],"mappings":";;;;;;6lCAyBoB,CAACA,OAAQC,2BACnBC,KAAOC,SAASC,eAAeJ,QAC/BK,cAAgBH,KAAKI,+BAAwBL,6CAEjDI,eAAeE,GAAG,UAAU,WACpBC,aAAeL,SAASM,cAAc,SAC5CD,aAAaE,aAAa,OAAQ,UAClCF,aAAaE,aAAa,OAAQ,cAClCF,aAAaE,aAAa,SAAS,GAEnCR,KAAKS,YAAYH,cAEjBI,kBAAkBC,kBAAkBL,cAEpCN,KAAKY"}timer.min.js000064400000003052152267113500007006 0ustar00define("mod_assign/timer",["exports","core/notification","core/str"],(function(_exports,_notification,_str){var obj; /** * A javascript module for the time in the assign module. * * @copyright 2020 Matt Porritt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};let endTime=0,timeoutId=null,timer=null;const update=()=>{const now=(new Date).getTime(),secondsLeft=Math.floor((endTime-now)/1e3);if(secondsLeft<=0)return timer.classList.add("alert","alert-danger"),timer.innerHTML="00:00:00",document.getElementById("mod_assign_timelimit_block")&&(0,_str.getString)("caneditsubmission","mod_assign").then((message=>_notification.default.addNotification({message:message}))).catch(_notification.default.exception),void(timeoutId&&clearTimeout(timeoutId));var secs;secondsLeft<300?(timer.classList.remove("alert-warning"),timer.classList.add("alert","alert-danger")):secondsLeft<900&&(timer.classList.remove("alert-danger"),timer.classList.add("alert","alert-warning")),timer.innerHTML=(secs=secondsLeft,[Math.floor(secs/3600),Math.floor(secs/60)%60,secs%60].filter(((value,index)=>0!==value||index>0)).map((value=>"".concat(value).padStart(2,"0"))).join(":")),timeoutId=setTimeout(update,500)};_exports.init=timerId=>{timer=document.getElementById(timerId),endTime=M.pageloadstarttime.getTime()+1e3*timer.dataset.starttime,update()}})); //# sourceMappingURL=timer.min.js.mapgrading_form_change_checker.min.js000064400000001473152267113500013322 0ustar00/** * Simple method to check for changes to a form between two points in time. * * @module mod_assign/grading_form_change_checker * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_assign/grading_form_change_checker",["jquery"],(function($){return{saveFormState:function(selector){$(selector).trigger("save-form-state");var data=$(selector).serialize();$(selector).data("saved-form-state",data)},checkFormForChanges:function(selector){$(selector).trigger("save-form-state");var data=$(selector).serialize(),previousdata=$(selector).data("saved-form-state");return!!$(selector).data("unresolved-error")||void 0!==previousdata&&previousdata!=data}}})); //# sourceMappingURL=grading_form_change_checker.min.js.mapgrading_navigation_user_info.min.js000064400000006151152267113500013574 0ustar00/** * Javascript controller for the "User summary" panel at the top of the page. * * @module mod_assign/grading_navigation_user_info * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_assign/grading_navigation_user_info",["jquery","core/notification","core/ajax","core/templates"],(function($,notification,ajax,templates){var UserInfo=function(selector){this._regionSelector=selector,this._region=$(selector),this._userCache={},$(document).on("user-changed",this._refreshUserInfo.bind(this))};return UserInfo.prototype._regionSelector=null,UserInfo.prototype._userCache=null,UserInfo.prototype._region=null,UserInfo.prototype._lastUserId=0,UserInfo.prototype._getAssignmentId=function(){return this._region.attr("data-assignmentid")},UserInfo.prototype._refreshUserInfo=function(event,userid){var promise=$.Deferred();this._region.attr("data-userid",userid),this._lastUserId!=userid&&(this._lastUserId=userid,templates.render("mod_assign/loading",{}).done(function(html,js){if(this._region.fadeOut("fast",function(){templates.replaceNodeContents(this._region,html,js),this._region.fadeIn("fast")}.bind(this)),userid<0)templates.render("mod_assign/grading_navigation_no_users",{}).done(function(html,js){userid==this._lastUserId&&this._region.fadeOut("fast",function(){templates.replaceNodeContents(this._region,html,js),this._region.fadeIn("fast")}.bind(this))}.bind(this)).fail(notification.exception);else{if(void 0!==this._userCache[userid])promise.resolve(this._userCache[userid]);else{var assignmentId=this._getAssignmentId();ajax.call([{methodname:"mod_assign_get_participant",args:{userid:userid,assignid:assignmentId,embeduser:!0}}])[0].done(function(participant){participant.hasOwnProperty("id")?(this._userCache[userid]=participant,promise.resolve(this._userCache[userid])):promise.reject("No users")}.bind(this)).fail(notification.exception)}promise.done(function(context){var identityfields=$("[data-showuseridentity]").data("showuseridentity").split(","),identity=[];context.courseid=$('[data-region="grading-navigation-panel"]').attr("data-courseid"),context.user&&($.each(identityfields,(function(i,k){void 0!==context.user[k]&&""!==context.user[k]&&(context.hasidentity=!0,identity.push(context.user[k]))})),context.identity=identity.join(", "),context.user.profileimageurl&&(context.profileimageurl=context.user.profileimageurl)),templates.render("mod_assign/grading_navigation_user_summary",context).done(function(html,js){userid==this._lastUserId&&this._region.fadeOut("fast",function(){templates.replaceNodeContents(this._region,html,js),this._region.fadeIn("fast")}.bind(this))}.bind(this)).fail(notification.exception)}.bind(this)).fail(function(){templates.render("mod_assign/grading_navigation_no_users",{}).done(function(html,js){this._region.fadeOut("fast",function(){templates.replaceNodeContents(this._region,html,js),this._region.fadeIn("fast")}.bind(this))}.bind(this)).fail(notification.exception)}.bind(this))}}.bind(this)).fail(notification.exception))},UserInfo})); //# sourceMappingURL=grading_navigation_user_info.min.js.mapparticipant_selector.min.js.map000064400000015572152267113500012672 0ustar00{"version":3,"file":"participant_selector.min.js","sources":["../src/participant_selector.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 * Custom auto-complete adapter to load users from the assignment list_participants webservice.\n *\n * @module mod_assign/participant_selector\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['core/ajax', 'jquery', 'core/templates'], function(ajax, $, templates) {\n\n\n return /** @alias module:mod_assign/participants_selector */ {\n\n // Public variables and functions.\n /**\n * Process the results returned from transport (convert to value + label)\n *\n * @method processResults\n * @param {String} selector\n * @param {Array} data\n * @return {Array}\n */\n processResults: function(selector, data) {\n return data;\n },\n\n /**\n * Fetch results based on the current query. This also renders each result from a template before returning them.\n *\n * @method transport\n * @param {String} selector Selector for the original select element\n * @param {String} query Current search string\n * @param {Function} success Success handler\n * @param {Function} failure Failure handler\n */\n transport: function(selector, query, success, failure) {\n var assignmentid = $(selector).attr('data-assignmentid');\n var groupid = $(selector).attr('data-groupid');\n var filters = $('[data-region=\"configure-filters\"] input[type=\"checkbox\"]');\n var filterstrings = [];\n\n filters.each(function(index, element) {\n filterstrings[$(element).attr('name')] = $(element).prop('checked');\n });\n\n ajax.call([{\n methodname: 'mod_assign_list_participants',\n args: {\n assignid: assignmentid,\n groupid: groupid,\n filter: query,\n limit: 30,\n includeenrolments: false,\n tablesort: true\n }\n }])[0].then(function(results) {\n var promises = [];\n var identityfields = $('[data-showuseridentity]').data('showuseridentity').split(',');\n\n // We got the results, now we loop over them and render each one from a template.\n $.each(results, function(index, user) {\n var ctx = user,\n identity = [],\n show = true;\n\n if (filterstrings.filter_submitted && !user.submitted) {\n show = false;\n }\n if (filterstrings.filter_notsubmitted && user.submitted) {\n show = false;\n }\n if (filterstrings.filter_requiregrading && !user.requiregrading) {\n show = false;\n }\n if (filterstrings.filter_grantedextension && !user.grantedextension) {\n show = false;\n }\n if (show) {\n $.each(identityfields, function(i, k) {\n if (typeof user[k] !== 'undefined' && user[k] !== '') {\n ctx.hasidentity = true;\n identity.push(user[k]);\n }\n });\n ctx.identity = identity.join(', ');\n promises.push(templates.render('mod_assign/list_participant_user_summary', ctx).then(function(html) {\n return {value: user.id, label: html};\n }));\n }\n });\n // Do the dance for $.when()\n return $.when.apply($, promises);\n }).then(function() {\n var users = [];\n\n // Determine if we've been passed any arguments..\n if (arguments[0]) {\n // Undo the $.when() dance from arguments object into an array..\n users = Array.prototype.slice.call(arguments);\n }\n\n success(users);\n return;\n }).catch(failure);\n }\n };\n});\n"],"names":["define","ajax","$","templates","processResults","selector","data","transport","query","success","failure","assignmentid","attr","groupid","filters","filterstrings","each","index","element","prop","call","methodname","args","assignid","filter","limit","includeenrolments","tablesort","then","results","promises","identityfields","split","user","ctx","identity","show","filter_submitted","submitted","filter_notsubmitted","filter_requiregrading","requiregrading","filter_grantedextension","grantedextension","i","k","hasidentity","push","join","render","html","value","id","label","when","apply","users","arguments","Array","prototype","slice","catch"],"mappings":";;;;;;;AAsBAA,yCAAO,CAAC,YAAa,SAAU,mBAAmB,SAASC,KAAMC,EAAGC,iBAGH,CAWzDC,eAAgB,SAASC,SAAUC,aACxBA,MAYXC,UAAW,SAASF,SAAUG,MAAOC,QAASC,aACtCC,aAAeT,EAAEG,UAAUO,KAAK,qBAChCC,QAAUX,EAAEG,UAAUO,KAAK,gBAC3BE,QAAUZ,EAAE,4DACZa,cAAgB,GAEpBD,QAAQE,MAAK,SAASC,MAAOC,SACzBH,cAAcb,EAAEgB,SAASN,KAAK,SAAWV,EAAEgB,SAASC,KAAK,cAG7DlB,KAAKmB,KAAK,CAAC,CACPC,WAAY,+BACZC,KAAM,CACFC,SAAUZ,aACVE,QAASA,QACTW,OAAQhB,MACRiB,MAAO,GACPC,mBAAmB,EACnBC,WAAW,MAEf,GAAGC,MAAK,SAASC,aACbC,SAAW,GACXC,eAAiB7B,EAAE,2BAA2BI,KAAK,oBAAoB0B,MAAM,YAGjF9B,EAAEc,KAAKa,SAAS,SAASZ,MAAOgB,UACxBC,IAAMD,KACNE,SAAW,GACXC,MAAO,EAEPrB,cAAcsB,mBAAqBJ,KAAKK,YACxCF,MAAO,GAEPrB,cAAcwB,qBAAuBN,KAAKK,YAC1CF,MAAO,GAEPrB,cAAcyB,wBAA0BP,KAAKQ,iBAC7CL,MAAO,GAEPrB,cAAc2B,0BAA4BT,KAAKU,mBAC/CP,MAAO,GAEPA,OACAlC,EAAEc,KAAKe,gBAAgB,SAASa,EAAGC,QACR,IAAZZ,KAAKY,IAAkC,KAAZZ,KAAKY,KACvCX,IAAIY,aAAc,EAClBX,SAASY,KAAKd,KAAKY,QAG3BX,IAAIC,SAAWA,SAASa,KAAK,MAC7BlB,SAASiB,KAAK5C,UAAU8C,OAAO,2CAA4Cf,KAAKN,MAAK,SAASsB,YACnF,CAACC,MAAOlB,KAAKmB,GAAIC,MAAOH,cAKpChD,EAAEoD,KAAKC,MAAMrD,EAAG4B,aACxBF,MAAK,eACA4B,MAAQ,GAGRC,UAAU,KAEVD,MAAQE,MAAMC,UAAUC,MAAMxC,KAAKqC,YAGvChD,QAAQ+C,UAETK,MAAMnD"}timer.min.js.map000064400000011761152267113500007570 0ustar00{"version":3,"file":"timer.min.js","sources":["../src/timer.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 Notification from 'core/notification';\nimport {getString} from 'core/str';\n\n/**\n * A javascript module for the time in the assign module.\n *\n * @copyright 2020 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Timestamp at which time runs out.\n *\n * @property {Number} endTime\n */\nlet endTime = 0;\n\n/**\n * ID of the timeout that updates the clock.\n *\n * @property {Number} timeoutId\n */\nlet timeoutId = null;\n\n/**\n * The timer element.\n *\n * @property {Element} timer\n */\nlet timer = null;\n\n/**\n * Helper method to convert time remaining in seconds into HH:MM:SS format.\n *\n * @method formatSeconds\n * @param {Number} secs Time remaining in seconds to get value for.\n * @return {String} Time remaining in HH:MM:SS format.\n */\nconst formatSeconds = (secs) => {\n const hours = Math.floor(secs / 3600);\n const minutes = Math.floor(secs / 60) % 60;\n const seconds = secs % 60;\n\n return [hours, minutes, seconds]\n // Remove the hours column if there is less than 1 hour left.\n .filter((value, index) => value !== 0 || index > 0)\n // Ensure that all fields are two digit numbers.\n .map(value => `${value}`.padStart(2, '0'))\n .join(\":\");\n};\n\n/**\n * Stop the timer, if it is running.\n *\n * @method stop\n */\nconst stop = () => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n};\n\n/**\n * Function to update the clock with the current time left.\n *\n * @method update\n */\nconst update = () => {\n const now = new Date().getTime();\n const secondsLeft = Math.floor((endTime - now) / 1000);\n\n // If time has expired, set the hidden form field that says time has expired.\n if (secondsLeft <= 0) {\n timer.classList.add('alert', 'alert-danger');\n timer.innerHTML = '00:00:00';\n\n // Only add a notification on the assign submission page.\n if (document.getElementById(\"mod_assign_timelimit_block\")) {\n getString('caneditsubmission', 'mod_assign')\n .then(message => Notification.addNotification({message}))\n .catch(Notification.exception);\n }\n\n stop();\n return;\n } else if (secondsLeft < 300) { // Add danger style when less than 5 minutes left.\n timer.classList.remove('alert-warning');\n timer.classList.add('alert', 'alert-danger');\n } else if (secondsLeft < 900) { // Add warning style when less than 15 minutes left.\n timer.classList.remove('alert-danger');\n timer.classList.add('alert', 'alert-warning');\n }\n\n // Update the time display.\n timer.innerHTML = formatSeconds(secondsLeft);\n\n // Arrange for this method to be called again soon.\n timeoutId = setTimeout(update, 500);\n};\n\n/**\n * Set up the submission timer.\n *\n * @method init\n * @param {Number} timerId Unique ID of the timer element.\n */\nexport const init = (timerId) => {\n timer = document.getElementById(timerId);\n endTime = M.pageloadstarttime.getTime() + (timer.dataset.starttime * 1000);\n update();\n};\n"],"names":["endTime","timeoutId","timer","update","now","Date","getTime","secondsLeft","Math","floor","classList","add","innerHTML","document","getElementById","then","message","Notification","addNotification","catch","exception","clearTimeout","secs","remove","filter","value","index","map","padStart","join","setTimeout","timerId","M","pageloadstarttime","dataset","starttime"],"mappings":";;;;;;0JA8BIA,QAAU,EAOVC,UAAY,KAOZC,MAAQ,WAsCNC,OAAS,WACLC,KAAM,IAAIC,MAAOC,UACjBC,YAAcC,KAAKC,OAAOT,QAAUI,KAAO,QAG7CG,aAAe,SACfL,MAAMQ,UAAUC,IAAI,QAAS,gBAC7BT,MAAMU,UAAY,WAGdC,SAASC,eAAe,kDACd,oBAAqB,cAC1BC,MAAKC,SAAWC,sBAAaC,gBAAgB,CAACF,QAAAA,YAC9CG,MAAMF,sBAAaG,gBAvB5BnB,WACAoB,aAAapB,YApBEqB,IAAAA,KA+CRf,YAAc,KACrBL,MAAMQ,UAAUa,OAAO,iBACvBrB,MAAMQ,UAAUC,IAAI,QAAS,iBACtBJ,YAAc,MACrBL,MAAMQ,UAAUa,OAAO,gBACvBrB,MAAMQ,UAAUC,IAAI,QAAS,kBAIjCT,MAAMU,WAxDaU,KAwDaf,YAnDzB,CAJOC,KAAKC,MAAMa,KAAO,MAChBd,KAAKC,MAAMa,KAAO,IAAM,GACxBA,KAAO,IAIlBE,QAAO,CAACC,MAAOC,QAAoB,IAAVD,OAAeC,MAAQ,IAEhDC,KAAIF,OAAS,UAAGA,OAAQG,SAAS,EAAG,OACpCC,KAAK,MAiDV5B,UAAY6B,WAAW3B,OAAQ,oBASd4B,UACjB7B,MAAQW,SAASC,eAAeiB,SAChC/B,QAAUgC,EAAEC,kBAAkB3B,UAAuC,IAA1BJ,MAAMgC,QAAQC,UACzDhC"}grading_review_panel.min.js.map000064400000016434152267113500012625 0ustar00{"version":3,"file":"grading_review_panel.min.js","sources":["../src/grading_review_panel.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript controller for the \"Review\" panel at the left of the page.\n *\n * @module mod_assign/grading_review_panel\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'mod_assign/grading_events'], function($, GradingEvents) {\n\n /**\n * GradingReviewPanel class.\n *\n * @class mod_assign/grading_review_panel\n */\n var GradingReviewPanel = function() {\n this._region = $('[data-region=\"review-panel-content\"]');\n this.registerEventListeners();\n };\n\n /** @property {JQuery} JQuery node for the page region containing the user navigation. */\n GradingReviewPanel.prototype._region = null;\n\n /**\n * It is first come first served to get ownership of the grading review panel.\n * There can be only one.\n *\n * @public\n * @method getReviewPanel\n * @param {String} pluginname - the first plugin to ask for the panel gets it.\n * @return {DOMNode} or false\n */\n GradingReviewPanel.prototype.getReviewPanel = function(pluginname) {\n var owner = this._region.data('panel-owner');\n if (typeof owner == \"undefined\") {\n this._region.data('review-panel-plugin', pluginname);\n }\n if (this._region.data('review-panel-plugin') == pluginname) {\n return this._region[0];\n }\n return false;\n };\n\n /**\n * Get the toggle review panel button.\n *\n * @method getTogglePanelButton\n * @return {jQuery}\n */\n GradingReviewPanel.prototype.getTogglePanelButton = function() {\n return this.getPanelElement().find('[data-region=\"review-panel-toggle\"]');\n };\n\n /**\n * Get the review panel element.\n *\n * @method getPanelElement\n * @return {jQuery}\n */\n GradingReviewPanel.prototype.getPanelElement = function() {\n return $('[data-region=\"review-panel\"]');\n };\n\n /**\n * Get the review panel content element.\n *\n * @method getPanelContentElement\n * @return {jQuery}\n */\n GradingReviewPanel.prototype.getPanelContentElement = function() {\n return $('[data-region=\"review-panel-content\"]');\n };\n\n /**\n * Show/Hide the review panel.\n *\n * @method togglePanel\n */\n GradingReviewPanel.prototype.togglePanel = function() {\n if (this.getPanelElement().hasClass('collapsed')) {\n $(document).trigger(GradingEvents.EXPAND_REVIEW_PANEL);\n } else {\n $(document).trigger(GradingEvents.COLLAPSE_REVIEW_PANEL);\n }\n };\n\n /**\n * Hide the review panel.\n *\n * @method collapsePanel\n */\n GradingReviewPanel.prototype.collapsePanel = function() {\n this.getPanelElement().addClass('collapsed').removeClass('grade-panel-collapsed');\n this.getPanelContentElement().attr('aria-hidden', true);\n };\n\n /**\n * Show the review panel.\n *\n * @method expandPanel\n */\n GradingReviewPanel.prototype.expandPanel = function() {\n this.getPanelElement().removeClass('collapsed');\n this.getPanelContentElement().removeAttr('aria-hidden');\n };\n\n /**\n * Register event listeners for the review panel.\n *\n * @method registerEventListeners\n */\n GradingReviewPanel.prototype.registerEventListeners = function() {\n var toggleReviewPanelButton = this.getTogglePanelButton();\n toggleReviewPanelButton.click(function(e) {\n this.togglePanel();\n e.preventDefault();\n }.bind(this));\n\n toggleReviewPanelButton.keydown(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode === 13 || e.keyCode === 32) {\n this.togglePanel();\n e.preventDefault();\n }\n }\n }.bind(this));\n\n var docElement = $(document);\n docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL, function() {\n this.collapsePanel();\n }.bind(this));\n\n // Need special styling when grade panel is collapsed.\n docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL, function() {\n this.expandPanel();\n this.getPanelElement().addClass('grade-panel-collapsed');\n }.bind(this));\n\n docElement.on(GradingEvents.EXPAND_REVIEW_PANEL, function() {\n this.expandPanel();\n }.bind(this));\n\n docElement.on(GradingEvents.EXPAND_GRADE_PANEL, function() {\n this.getPanelElement().removeClass('grade-panel-collapsed');\n }.bind(this));\n };\n\n return GradingReviewPanel;\n});\n"],"names":["define","$","GradingEvents","GradingReviewPanel","_region","registerEventListeners","prototype","getReviewPanel","pluginname","this","data","getTogglePanelButton","getPanelElement","find","getPanelContentElement","togglePanel","hasClass","document","trigger","EXPAND_REVIEW_PANEL","COLLAPSE_REVIEW_PANEL","collapsePanel","addClass","removeClass","attr","expandPanel","removeAttr","toggleReviewPanelButton","click","e","preventDefault","bind","keydown","metaKey","shiftKey","altKey","ctrlKey","keyCode","docElement","on","COLLAPSE_GRADE_PANEL","EXPAND_GRADE_PANEL"],"mappings":";;;;;;;;AAuBAA,yCAAO,CAAC,SAAU,8BAA8B,SAASC,EAAGC,mBAOpDC,mBAAqB,gBAChBC,QAAUH,EAAE,6CACZI,iCAITF,mBAAmBG,UAAUF,QAAU,KAWvCD,mBAAmBG,UAAUC,eAAiB,SAASC,wBAE/B,IADRC,KAAKL,QAAQM,KAAK,qBAErBN,QAAQM,KAAK,sBAAuBF,YAEzCC,KAAKL,QAAQM,KAAK,wBAA0BF,YACrCC,KAAKL,QAAQ,IAW5BD,mBAAmBG,UAAUK,qBAAuB,kBACzCF,KAAKG,kBAAkBC,KAAK,wCASvCV,mBAAmBG,UAAUM,gBAAkB,kBACpCX,EAAE,iCASbE,mBAAmBG,UAAUQ,uBAAyB,kBAC3Cb,EAAE,yCAQbE,mBAAmBG,UAAUS,YAAc,WACnCN,KAAKG,kBAAkBI,SAAS,aAChCf,EAAEgB,UAAUC,QAAQhB,cAAciB,qBAElClB,EAAEgB,UAAUC,QAAQhB,cAAckB,wBAS1CjB,mBAAmBG,UAAUe,cAAgB,gBACpCT,kBAAkBU,SAAS,aAAaC,YAAY,8BACpDT,yBAAyBU,KAAK,eAAe,IAQtDrB,mBAAmBG,UAAUmB,YAAc,gBAClCb,kBAAkBW,YAAY,kBAC9BT,yBAAyBY,WAAW,gBAQ7CvB,mBAAmBG,UAAUD,uBAAyB,eAC9CsB,wBAA0BlB,KAAKE,uBACnCgB,wBAAwBC,MAAM,SAASC,QAC9Bd,cACLc,EAAEC,kBACJC,KAAKtB,OAEPkB,wBAAwBK,QAAQ,SAASH,GAChCA,EAAEI,SAAYJ,EAAEK,UAAaL,EAAEM,QAAWN,EAAEO,SAC3B,KAAdP,EAAEQ,SAAgC,KAAdR,EAAEQ,eACjBtB,cACLc,EAAEC,mBAGZC,KAAKtB,WAEH6B,WAAarC,EAAEgB,UACnBqB,WAAWC,GAAGrC,cAAckB,sBAAuB,gBAC1CC,iBACPU,KAAKtB,OAGP6B,WAAWC,GAAGrC,cAAcsC,qBAAsB,gBACzCf,mBACAb,kBAAkBU,SAAS,0BAClCS,KAAKtB,OAEP6B,WAAWC,GAAGrC,cAAciB,oBAAqB,gBACxCM,eACPM,KAAKtB,OAEP6B,WAAWC,GAAGrC,cAAcuC,mBAAoB,gBACvC7B,kBAAkBW,YAAY,0BACrCQ,KAAKtB,QAGJN"}grading_events.min.js000064400000001034152267113500010663 0ustar00/** * Events for the grading interface. * * @module mod_assign/grading_events * @copyright 2016 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_assign/grading_events",(function(){return{COLLAPSE_REVIEW_PANEL:"grading:collapse-review-panel",EXPAND_REVIEW_PANEL:"grading:expand-review-panel",COLLAPSE_GRADE_PANEL:"grading:collapse-grade-panel",EXPAND_GRADE_PANEL:"grading:expand-grade-panel"}})); //# sourceMappingURL=grading_events.min.js.mapgrading_navigation.min.js000064400000026546152267113500011535 0ustar00/** * Javascript to handle changing users via the user selector in the header. * * @module mod_assign/grading_navigation * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ define("mod_assign/grading_navigation",["jquery","core/notification","core/str","core/form-autocomplete","core/ajax","core_user/repository","mod_assign/grading_form_change_checker"],(function($,notification,str,autocomplete,ajax,UserRepository,checker){var GradingNavigation=function(selector){this._regionSelector=selector,this._region=$(selector),this._filters=[],this._users=[],this._filteredUsers=[],this._lastXofYUpdate=0,this._firstLoadUsers=!0;let url=new URL(window.location);parseInt(url.searchParams.get("treset"))>0&&(url.searchParams.delete("treset"),window.history.replaceState({},"",url)),this._loadAllUsers(),this._region.find('[data-action="previous-user"]').on("click",this._handlePreviousUser.bind(this)),this._region.find('[data-action="next-user"]').on("click",this._handleNextUser.bind(this)),this._region.find('[data-action="change-user"]').on("change",this._handleChangeUser.bind(this)),this._region.find('[data-region="user-filters"]').on("click",this._toggleExpandFilters.bind(this)),this._region.find('[data-region="user-resettable"]').on("click",this._toggleResetTable.bind()),$(document).on("user-changed",this._refreshSelector.bind(this)),$(document).on("reset-table",this._toggleResetTable.bind(this)),$(document).on("done-saving-show-next",this._handleNextUser.bind(this));var toggleLink=this._region.find('[data-region="user-filters"]');$(document.getElementById(toggleLink.attr("aria-controls"))).on("change","select",this._filterChanged.bind(this));var userid=$('[data-region="grading-navigation-panel"]').data("first-userid");userid&&this._selectUserById(userid),str.get_string("changeuser","mod_assign").done((function(s){autocomplete.enhance("[data-action=change-user]",!1,"mod_assign/participant_selector",s)})).fail(notification.exception),$(document).bind("start-loading-user",function(){this._isLoading=!0}.bind(this)),$(document).bind("finish-loading-user",function(){this._isLoading=!1}.bind(this))};return GradingNavigation.prototype._isLoading=!1,GradingNavigation.prototype._regionSelector=null,GradingNavigation.prototype._filters=null,GradingNavigation.prototype._users=null,GradingNavigation.prototype._region=null,GradingNavigation.prototype._lastFilters="",GradingNavigation.prototype._loadAllUsers=function(){var select=this._region.find("[data-action=change-user]"),assignmentid=select.attr("data-assignmentid"),groupid=select.attr("data-groupid"),filterPanel=this._region.find('[data-region="configure-filters"]'),filter=filterPanel.find('select[name="filter"]').val(),workflowFilter=filterPanel.find('select[name="workflowfilter"]');workflowFilter&&(filter+=","+workflowFilter.val());var markerFilter=filterPanel.find('select[name="markerfilter"]');return markerFilter&&(filter+=","+markerFilter.val()),this._lastFilters!=filter&&(this._lastFilters=filter,ajax.call([{methodname:"mod_assign_list_participants",args:{assignid:assignmentid,groupid:groupid,filter:"",onlyids:!0,tablesort:!0},done:this._usersLoaded.bind(this),fail:notification.exception}]),!0)},GradingNavigation.prototype._usersLoaded=function(users){if(this._firstLoadUsers=!1,this._filteredUsers=this._users=users,this._users.length){var toggleLink=this._region.find('[data-region="user-filters"]');$(document.getElementById(toggleLink.attr("aria-controls"))).find('select[name="filter"]').trigger("change"),$('[data-region="grade-panel"]').show(),$('[data-region="grade-actions-panel"]').show()}else this._selectNoUser();this._triggerNextUserEvent()},GradingNavigation.prototype._checkClickOutsideConfigureFilters=function(event){var configPanel=this._region.find('[data-region="configure-filters"]');if(!configPanel.is(event.target)&&0===configPanel.has(event.target).length){var toggleLink=this._region.find('[data-region="user-filters"]');configPanel.hide(),configPanel.attr("aria-hidden","true"),toggleLink.attr("aria-expanded","false"),$(document).unbind("click.mod_assign_grading_navigation")}},GradingNavigation.prototype._updateFilterPreferences=function(userId,filterList,preferenceNames){var preferences=[],i=0;if(0==filterList.length||this._firstLoadUsers){var deferred=$.Deferred();return deferred.resolve(),deferred}for(i=0;i0&&!isNaN(useridnumber)&&useridnumber>0&&$(document).trigger("user-changed",useridnumber)))},GradingNavigation.prototype._toggleExpandFilters=function(event){event.preventDefault();var toggleLink=$(event.target).closest('[data-region="user-filters"]'),expanded="true"==toggleLink.attr("aria-expanded"),configPanel=$(document.getElementById(toggleLink.attr("aria-controls")));expanded?(configPanel.hide(),configPanel.attr("aria-hidden","true"),toggleLink.attr("aria-expanded","false"),$(document).unbind("click.mod_assign_grading_navigation")):(configPanel.css("display","inline-block"),configPanel.attr("aria-hidden","false"),toggleLink.attr("aria-expanded","true"),event.stopPropagation(),$(document).on("click.mod_assign_grading_navigation",this._checkClickOutsideConfigureFilters.bind(this)))},GradingNavigation.prototype._toggleResetTable=function(){let url=new URL(window.location);url.searchParams.set("treset","1"),window.location.href=url},GradingNavigation.prototype._handlePreviousUser=function(e){e.preventDefault();var currentUserId=this._region.find("[data-action=change-user]").attr("data-selected"),i=0,currentIndex=0;for(i=0;i0&&$(document).trigger("user-changed",userid)}else count&&this._selectUserById(this._filteredUsers[newIndex].id)},GradingNavigation.prototype._setCountString=function(x,y){var updateNumber;this._lastXofYUpdate++,updateNumber=this._lastXofYUpdate;var param={x:x,y:y};str.get_string("xofy","mod_assign",param).done(function(s){updateNumber==this._lastXofYUpdate&&this._region.find('[data-region="user-count-summary"]').text(s)}.bind(this)).fail(notification.exception)},GradingNavigation.prototype._refreshCount=function(){var userid=this._region.find("[data-action=change-user]").attr("data-selected"),i=0,currentIndex=0;if(isNaN(userid)||userid<=0)this._region.find('[data-region="user-count"]').hide();else{for(this._region.find('[data-region="user-count"]').show(),i=0;i0){var url=new URL(window.location);if(parseInt(url.searchParams.get("blindid"))>0){var newid=this._filteredUsers[currentIndex-1].recordid;url.searchParams.set("blindid",newid)}else url.searchParams.set("userid",userid);window.history.replaceState({},"",url)}}},GradingNavigation.prototype._refreshSelector=function(event,userid){var select=this._region.find("[data-action=change-user]");userid=parseInt(userid,10),!isNaN(userid)&&userid>0&&select.attr("data-selected",userid),this._refreshCount()},GradingNavigation.prototype._triggerNextUserEvent=function(){this._filteredUsers.length>1?$(document).trigger("next-user",{nextUserId:null,nextUser:!0}):$(document).trigger("next-user",{nextUser:!1})},GradingNavigation.prototype._handleChangeUser=function(){var select=this._region.find("[data-action=change-user]"),userid=parseInt(select.val(),10);this._isLoading||(checker.checkFormForChanges('[data-region="grade-panel"] .gradeform')?str.get_strings([{key:"unsavedchanges",component:"mod_assign"},{key:"unsavedchangesquestion",component:"mod_assign"},{key:"saveandcontinue",component:"mod_assign"},{key:"cancel",component:"core"}]).done((function(strs){notification.confirm(strs[0],strs[1],strs[2],strs[3],(function(){$(document).trigger("save-changes",userid)}))})):!isNaN(userid)&&userid>0&&(select.attr("data-selected",userid),$(document).trigger("user-changed",userid)))},GradingNavigation})); //# sourceMappingURL=grading_navigation.min.js.mapoverride_form.min.js000064400000003663152267113500010540 0ustar00define("mod_assign/override_form",["exports","jquery","core_form/changechecker"],(function(_exports,_jquery,FormChangeChecker){var obj; /** * A javascript module to enhance the override form. * * @copyright 2019 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj},FormChangeChecker=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(FormChangeChecker);_exports.init=(formId,selectElementName)=>{const form=document.getElementById(formId),selectElement=form.querySelector('[name="'.concat(selectElementName,'"]'));(0,_jquery.default)(selectElement).on("change",(()=>{const inputElement=document.createElement("input");inputElement.setAttribute("type","hidden"),inputElement.setAttribute("name","userchange"),inputElement.setAttribute("value",!0),form.appendChild(inputElement),FormChangeChecker.markFormSubmitted(inputElement),form.submit()}))}})); //# sourceMappingURL=override_form.min.js.mapmoodle-mod_assign-history/moodle-mod_assign-history-debug.js000064400000005116152267116010020370 0ustar00YUI.add('moodle-mod_assign-history', function (Y, NAME) { // Define a function that will run in the context of a // Node instance: var CSS = { LINK: 'mod-assign-history-link', OPEN: 'mod-assign-history-link-open', CLOSED: 'mod-assign-history-link-closed', PANEL: 'mod-assign-history-panel' }, COUNT = 0, TOGGLE = function() { var id = this.get('for'), panel = Y.one('#' + id); if (this.hasClass(CSS.OPEN)) { this.removeClass(CSS.OPEN); this.addClass(CSS.CLOSED); this.setStyle('overflow', 'hidden'); panel.hide(); } else { this.removeClass(CSS.CLOSED); this.addClass(CSS.OPEN); panel.show(); } }, HISTORY = function() { var link = null, panel = null, wrapper = null, container = this; // Loop through all the children of this container and turn // every odd node to a link to open/close the following panel. this.get('children').each(function() { if (link) { COUNT++; // First convert the link to an anchor. wrapper = Y.Node.create(''); panel = this; container.insertBefore(wrapper, link); link.remove(false); wrapper.appendChild(link); // Add a for attribute to the link to link to the id of the panel. if (!panel.get('id')) { panel.set('id', CSS.PANEL + COUNT); } wrapper.set('for', panel.get('id')); // Add an aria attribute for the live region. panel.set('aria-live', 'polite'); wrapper.addClass(CSS.LINK); if (COUNT == 1) { wrapper.addClass(CSS.OPEN); } else { wrapper.addClass(CSS.CLOSED); } panel.addClass(CSS.PANEL); if (COUNT == 1) { panel.show(); } else { panel.hide(); } link = null; } else { link = this; } }); // Setup event listeners. this.delegate('click', TOGGLE, '.' + CSS.LINK); }; // Use addMethod to add history to the Node prototype: Y.Node.addMethod("history", HISTORY); // Extend this functionality to NodeLists. Y.NodeList.importMethod(Y.Node.prototype, "history"); }, '@VERSION@', {"requires": ["node", "transition"]}); moodle-mod_assign-history/moodle-mod_assign-history-min.js000064400000001707152267116010020067 0ustar00YUI.add("moodle-mod_assign-history",function(o,s){var d={LINK:"mod-assign-history-link",OPEN:"mod-assign-history-link-open",CLOSED:"mod-assign-history-link-closed",PANEL:"mod-assign-history-panel"},a=0,h=function(){var s=this.get("for"),s=o.one("#"+s);this.hasClass(d.OPEN)?(this.removeClass(d.OPEN),this.addClass(d.CLOSED),this.setStyle("overflow","hidden"),s.hide()):(this.removeClass(d.CLOSED),this.addClass(d.OPEN),s.show())};o.Node.addMethod("history",function(){var s,i=null,e=null,t=this;this.get("children").each(function(){i=i?(a++,e=o.Node.create(""),s=this,t.insertBefore(e,i),i.remove(!1),e.appendChild(i),s.get("id")||s.set("id",d.PANEL+a),e.set("for",s.get("id")),s.set("aria-live","polite"),e.addClass(d.LINK),1==a?e.addClass(d.OPEN):e.addClass(d.CLOSED),s.addClass(d.PANEL),1==a?s.show():s.hide(),null):this}),this.delegate("click",h,"."+d.LINK)}),o.NodeList.importMethod(o.Node.prototype,"history")},"@VERSION@",{requires:["node","transition"]});moodle-mod_assign-history/moodle-mod_assign-history.js000064400000005116152267116010017304 0ustar00YUI.add('moodle-mod_assign-history', function (Y, NAME) { // Define a function that will run in the context of a // Node instance: var CSS = { LINK: 'mod-assign-history-link', OPEN: 'mod-assign-history-link-open', CLOSED: 'mod-assign-history-link-closed', PANEL: 'mod-assign-history-panel' }, COUNT = 0, TOGGLE = function() { var id = this.get('for'), panel = Y.one('#' + id); if (this.hasClass(CSS.OPEN)) { this.removeClass(CSS.OPEN); this.addClass(CSS.CLOSED); this.setStyle('overflow', 'hidden'); panel.hide(); } else { this.removeClass(CSS.CLOSED); this.addClass(CSS.OPEN); panel.show(); } }, HISTORY = function() { var link = null, panel = null, wrapper = null, container = this; // Loop through all the children of this container and turn // every odd node to a link to open/close the following panel. this.get('children').each(function() { if (link) { COUNT++; // First convert the link to an anchor. wrapper = Y.Node.create(''); panel = this; container.insertBefore(wrapper, link); link.remove(false); wrapper.appendChild(link); // Add a for attribute to the link to link to the id of the panel. if (!panel.get('id')) { panel.set('id', CSS.PANEL + COUNT); } wrapper.set('for', panel.get('id')); // Add an aria attribute for the live region. panel.set('aria-live', 'polite'); wrapper.addClass(CSS.LINK); if (COUNT == 1) { wrapper.addClass(CSS.OPEN); } else { wrapper.addClass(CSS.CLOSED); } panel.addClass(CSS.PANEL); if (COUNT == 1) { panel.show(); } else { panel.hide(); } link = null; } else { link = this; } }); // Setup event listeners. this.delegate('click', TOGGLE, '.' + CSS.LINK); }; // Use addMethod to add history to the Node prototype: Y.Node.addMethod("history", HISTORY); // Extend this functionality to NodeLists. Y.NodeList.importMethod(Y.Node.prototype, "history"); }, '@VERSION@', {"requires": ["node", "transition"]}); folder.min.js.map000064400000004376152267117500007733 0ustar00{"version":3,"file":"folder.min.js","sources":["../src/folder.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript helper function for Folder module\n *\n * @module mod_folder/folder\n * @copyright 2009 Petr Skoda {@link http://skodak.org}\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport YUI from 'core/yui';\n\nexport const initTree = (id, expandAll) => {\n YUI.use('yui2-treeview', 'node-event-simulate', function(Y) {\n var tree = new Y.YUI2.widget.TreeView(id);\n\n tree.subscribe(\"clickEvent\", function() {\n // we want normal clicking which redirects to url\n return false;\n });\n\n tree.subscribe(\"enterKeyPressed\", function(node) {\n // We want keyboard activation to trigger a click on the first link.\n Y.one(node.getContentEl()).one('a').simulate('click');\n return false;\n });\n\n if (expandAll) {\n tree.expandAll();\n } else {\n // Else just expand the top node.\n tree.getRoot().children[0].expand();\n }\n\n tree.render();\n });\n};\n"],"names":["id","expandAll","use","Y","tree","YUI2","widget","TreeView","subscribe","node","one","getContentEl","simulate","getRoot","children","expand","render"],"mappings":";;;;;;;0JAyBwB,CAACA,GAAIC,0BACrBC,IAAI,gBAAiB,uBAAuB,SAASC,OACjDC,KAAO,IAAID,EAAEE,KAAKC,OAAOC,SAASP,IAEtCI,KAAKI,UAAU,cAAc,kBAElB,KAGXJ,KAAKI,UAAU,mBAAmB,SAASC,aAEvCN,EAAEO,IAAID,KAAKE,gBAAgBD,IAAI,KAAKE,SAAS,UACtC,KAGPX,UACAG,KAAKH,YAGLG,KAAKS,UAAUC,SAAS,GAAGC,SAG/BX,KAAKY"}folder.min.js000064400000001563152267117500007152 0ustar00define("mod_folder/folder",["exports","core/yui"],(function(_exports,_yui){var obj; /** * Javascript helper function for Folder module * * @module mod_folder/folder * @copyright 2009 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initTree=void 0,_yui=(obj=_yui)&&obj.__esModule?obj:{default:obj};_exports.initTree=(id,expandAll)=>{_yui.default.use("yui2-treeview","node-event-simulate",(function(Y){var tree=new Y.YUI2.widget.TreeView(id);tree.subscribe("clickEvent",(function(){return!1})),tree.subscribe("enterKeyPressed",(function(node){return Y.one(node.getContentEl()).one("a").simulate("click"),!1})),expandAll?tree.expandAll():tree.getRoot().children[0].expand(),tree.render()}))}})); //# sourceMappingURL=folder.min.js.mapmoodle-mod_quiz-util-base/moodle-mod_quiz-util-base-debug.js000064400000000550152267121500020145 0ustar00YUI.add('moodle-mod_quiz-util-base', function (Y, NAME) { /** * The Moodle.mod_quiz.util classes provide quiz-related utility functions. * * @module moodle-mod_quiz-util * @main */ Y.namespace('Moodle.mod_quiz.util'); /** * A collection of general utility functions for use in quiz. * * @class Moodle.mod_quiz.util * @static */ }, '@VERSION@'); moodle-mod_quiz-util-base/moodle-mod_quiz-util-base.js000064400000000550152267121500017061 0ustar00YUI.add('moodle-mod_quiz-util-base', function (Y, NAME) { /** * The Moodle.mod_quiz.util classes provide quiz-related utility functions. * * @module moodle-mod_quiz-util * @main */ Y.namespace('Moodle.mod_quiz.util'); /** * A collection of general utility functions for use in quiz. * * @class Moodle.mod_quiz.util * @static */ }, '@VERSION@'); moodle-mod_quiz-util-base/moodle-mod_quiz-util-base-min.js000064400000000144152267121500017641 0ustar00YUI.add("moodle-mod_quiz-util-base",function(o,d){o.namespace("Moodle.mod_quiz.util")},"@VERSION@");moodle-mod_quiz-util-page/moodle-mod_quiz-util-page.js000064400000024406152267121500017073 0ustar00YUI.add('moodle-mod_quiz-util-page', function (Y, NAME) { /* global YUI */ /** * A collection of utility classes for use with pages. * * @module moodle-mod_quiz-util * @submodule moodle-mod_quiz-util-page */ Y.namespace('Moodle.mod_quiz.util.page'); /** * A collection of utility classes for use with pages. * * @class Moodle.mod_quiz.util.page * @static */ Y.Moodle.mod_quiz.util.page = { CSS: { PAGE: 'page' }, CONSTANTS: { ACTIONMENUIDPREFIX: 'action-menu-', ACTIONMENUBARIDSUFFIX: '-menubar', ACTIONMENUMENUIDSUFFIX: '-menu', PAGEIDPREFIX: 'page-', PAGENUMBERPREFIX: M.util.get_string('page', 'moodle') + ' ' }, SELECTORS: { ACTIONMENU: 'div.moodle-actionmenu', ACTIONMENUBAR: '.menubar', ACTIONMENUMENU: '.menu', ADDASECTION: '[data-action="addasection"]', PAGE: 'li.page', INSTANCENAME: '.instancename', NUMBER: 'h4' }, /** * Retrieve the page item from one of it's child Nodes. * * @method getPageFromComponent * @param pagecomponent {Node} The component Node. * @return {Node|null} The Page Node. */ getPageFromComponent: function(pagecomponent) { return Y.one(pagecomponent).ancestor(this.SELECTORS.PAGE, true); }, /** * Retrieve the page item from one of it's previous siblings. * * @method getPageFromSlot * @param pagecomponent {Node} The component Node. * @return {Node|null} The Page Node. */ getPageFromSlot: function(slot) { return Y.one(slot).previous(this.SELECTORS.PAGE); }, /** * Returns the page ID for the provided page. * * @method getId * @param page {Node} The page to find an ID for. * @return {Number|false} The ID of the page in question or false if no ID was found. */ getId: function(page) { // We perform a simple substitution operation to get the ID. var id = page.get('id').replace( this.CONSTANTS.PAGEIDPREFIX, ''); // Attempt to validate the ID. id = parseInt(id, 10); if (typeof id === 'number' && isFinite(id)) { return id; } return false; }, /** * Updates the page id for the provided page. * * @method setId * @param page {Node} The page to update the number for. * @param id int The id value. * @return void */ setId: function(page, id) { page.set('id', this.CONSTANTS.PAGEIDPREFIX + id); }, /** * Determines the page name for the provided page. * * @method getName * @param page {Node} The page to find a name for. * @return {string|false} The name of the page in question or false if no ID was found. */ getName: function(page) { var instance = page.one(this.SELECTORS.INSTANCENAME); if (instance) { return instance.get('firstChild').get('data'); } return null; }, /** * Determines the page number for the provided page. * * @method getNumber * @param page {Node} The page to find a number for. * @return {Number|false} The number of the page in question or false if no number was found. */ getNumber: function(page) { // We perform a simple substitution operation to get the number. var number = page.one(this.SELECTORS.NUMBER).get('text').replace( this.CONSTANTS.PAGENUMBERPREFIX, ''); // Attempt to validate the ID. number = parseInt(number, 10); if (typeof number === 'number' && isFinite(number)) { return number; } return false; }, /** * Updates the page number for the provided page. * * @method setNumber * @param page {Node} The page to update the number for. * @return void */ setNumber: function(page, number) { page.one(this.SELECTORS.NUMBER).set('text', this.CONSTANTS.PAGENUMBERPREFIX + number); }, /** * Returns a list of all page elements. * * @method getPages * @return {node[]} An array containing page nodes. */ getPages: function() { return Y.all(Y.Moodle.mod_quiz.util.slot.SELECTORS.PAGECONTENT + ' ' + Y.Moodle.mod_quiz.util.slot.SELECTORS.SECTIONUL + ' ' + this.SELECTORS.PAGE); }, /** * Is the given element a page element? * * @method isPage * @param page Page node * @return boolean */ isPage: function(page) { if (!page) { return false; } return page.hasClass(this.CSS.PAGE); }, /** * Does the page have atleast one slot? * * @method isEmpty * @param page Page node * @return boolean */ isEmpty: function(page) { var activity = page.next('li.activity'); if (!activity) { return true; } return !activity.hasClass('slot'); }, /** * Add a page and related elements to the list of slots. * * @method add * @param beforenode Int | Node | HTMLElement | String to add * @return page Page node */ add: function(beforenode) { var pagenumber = this.getNumber(this.getPageFromSlot(beforenode)) + 1; var pagehtml = M.mod_quiz.resource_toolbox.get('config').pagehtml; // Normalise the page number. pagehtml = pagehtml.replace(/%%PAGENUMBER%%/g, pagenumber); // Create the page node. var page = Y.Node.create(pagehtml); // Assign is as a drop target. YUI().use('dd-drop', function(Y) { var drop = new Y.DD.Drop({ node: page, groups: M.mod_quiz.dragres.groups }); page.drop = drop; }); // Insert in the correct place. beforenode.insert(page, 'after'); // Enhance the add menu to make if fully visible and clickable. if (typeof M.core.actionmenu !== "undefined") { M.core.actionmenu.newDOMNode(page); } return page; }, /** * Remove a page and related elements from the list of slots. * * @method remove * @param page Page node * @return void */ remove: function(page, keeppagebreak) { // Remove page break from previous slot. var previousslot = page.previous(Y.Moodle.mod_quiz.util.slot.SELECTORS.SLOT); if (!keeppagebreak && previousslot) { Y.Moodle.mod_quiz.util.slot.removePageBreak(previousslot); } page.remove(); }, /** * Reset the order of the numbers given to each page. * * @method reorderPages * @return void */ reorderPages: function() { // Get list of page nodes. var pages = this.getPages(); var currentpagenumber = 0; // Loop through pages incrementing the number each time. pages.each(function(page) { // Is the page empty? if (this.isEmpty(page)) { var keeppagebreak = page.next('li.slot') ? true : false; this.remove(page, keeppagebreak); return; } currentpagenumber++; // Set page number. this.setNumber(page, currentpagenumber); this.setId(page, currentpagenumber); }, this); // Reorder action menus this.reorderActionMenus(); }, /** * Reset the order of the numbers given to each action menu. * * @method reorderActionMenus * @return void */ reorderActionMenus: function() { // Get list of action menu nodes. var actionmenus = this.getActionMenus(); // Loop through pages incrementing the number each time. actionmenus.each(function(actionmenu, key) { var previousActionMenu = actionmenus.item(key - 1), previousActionMenunumber = 0; if (previousActionMenu) { previousActionMenunumber = this.getActionMenuId(previousActionMenu); } var id = previousActionMenunumber + 1; // Set menu id. this.setActionMenuId(actionmenu, id); // Update action-menu-1-menubar var menubar = actionmenu.one(this.SELECTORS.ACTIONMENUBAR); menubar.set('id', this.CONSTANTS.ACTIONMENUIDPREFIX + id + this.CONSTANTS.ACTIONMENUBARIDSUFFIX); // Update action-menu-1-menu var menumenu = actionmenu.one(this.SELECTORS.ACTIONMENUMENU); menumenu.set('id', this.CONSTANTS.ACTIONMENUIDPREFIX + id + this.CONSTANTS.ACTIONMENUMENUIDSUFFIX); // Update the URL of the add-section action. menumenu.one(this.SELECTORS.ADDASECTION).set('href', menumenu.one(this.SELECTORS.ADDASECTION).get('href').replace(/\baddsectionatpage=\d+\b/, 'addsectionatpage=' + id)); }, this); }, /** * Returns a list of all page elements. * * @method getActionMenus * @return {node[]} An array containing page nodes. */ getActionMenus: function() { return Y.all(Y.Moodle.mod_quiz.util.slot.SELECTORS.PAGECONTENT + ' ' + Y.Moodle.mod_quiz.util.slot.SELECTORS.SECTIONUL + ' ' + this.SELECTORS.ACTIONMENU); }, /** * Returns the ID for the provided action menu. * * @method getId * @param actionmenu {Node} The actionmenu to find an ID for. * @return {Number|false} The ID of the actionmenu in question or false if no ID was found. */ getActionMenuId: function(actionmenu) { // We perform a simple substitution operation to get the ID. var id = actionmenu.get('id').replace( this.CONSTANTS.ACTIONMENUIDPREFIX, ''); // Attempt to validate the ID. id = parseInt(id, 10); if (typeof id === 'number' && isFinite(id)) { return id; } return false; }, /** * Updates the page id for the provided page. * * @method setId * @param page {Node} The page to update the number for. * @param id int The id value. * @return void */ setActionMenuId: function(actionmenu, id) { actionmenu.set('id', this.CONSTANTS.ACTIONMENUIDPREFIX + id); } }; }, '@VERSION@', {"requires": ["node", "moodle-mod_quiz-util-base"]}); moodle-mod_quiz-util-page/moodle-mod_quiz-util-page-debug.js000064400000024406152267121500020157 0ustar00YUI.add('moodle-mod_quiz-util-page', function (Y, NAME) { /* global YUI */ /** * A collection of utility classes for use with pages. * * @module moodle-mod_quiz-util * @submodule moodle-mod_quiz-util-page */ Y.namespace('Moodle.mod_quiz.util.page'); /** * A collection of utility classes for use with pages. * * @class Moodle.mod_quiz.util.page * @static */ Y.Moodle.mod_quiz.util.page = { CSS: { PAGE: 'page' }, CONSTANTS: { ACTIONMENUIDPREFIX: 'action-menu-', ACTIONMENUBARIDSUFFIX: '-menubar', ACTIONMENUMENUIDSUFFIX: '-menu', PAGEIDPREFIX: 'page-', PAGENUMBERPREFIX: M.util.get_string('page', 'moodle') + ' ' }, SELECTORS: { ACTIONMENU: 'div.moodle-actionmenu', ACTIONMENUBAR: '.menubar', ACTIONMENUMENU: '.menu', ADDASECTION: '[data-action="addasection"]', PAGE: 'li.page', INSTANCENAME: '.instancename', NUMBER: 'h4' }, /** * Retrieve the page item from one of it's child Nodes. * * @method getPageFromComponent * @param pagecomponent {Node} The component Node. * @return {Node|null} The Page Node. */ getPageFromComponent: function(pagecomponent) { return Y.one(pagecomponent).ancestor(this.SELECTORS.PAGE, true); }, /** * Retrieve the page item from one of it's previous siblings. * * @method getPageFromSlot * @param pagecomponent {Node} The component Node. * @return {Node|null} The Page Node. */ getPageFromSlot: function(slot) { return Y.one(slot).previous(this.SELECTORS.PAGE); }, /** * Returns the page ID for the provided page. * * @method getId * @param page {Node} The page to find an ID for. * @return {Number|false} The ID of the page in question or false if no ID was found. */ getId: function(page) { // We perform a simple substitution operation to get the ID. var id = page.get('id').replace( this.CONSTANTS.PAGEIDPREFIX, ''); // Attempt to validate the ID. id = parseInt(id, 10); if (typeof id === 'number' && isFinite(id)) { return id; } return false; }, /** * Updates the page id for the provided page. * * @method setId * @param page {Node} The page to update the number for. * @param id int The id value. * @return void */ setId: function(page, id) { page.set('id', this.CONSTANTS.PAGEIDPREFIX + id); }, /** * Determines the page name for the provided page. * * @method getName * @param page {Node} The page to find a name for. * @return {string|false} The name of the page in question or false if no ID was found. */ getName: function(page) { var instance = page.one(this.SELECTORS.INSTANCENAME); if (instance) { return instance.get('firstChild').get('data'); } return null; }, /** * Determines the page number for the provided page. * * @method getNumber * @param page {Node} The page to find a number for. * @return {Number|false} The number of the page in question or false if no number was found. */ getNumber: function(page) { // We perform a simple substitution operation to get the number. var number = page.one(this.SELECTORS.NUMBER).get('text').replace( this.CONSTANTS.PAGENUMBERPREFIX, ''); // Attempt to validate the ID. number = parseInt(number, 10); if (typeof number === 'number' && isFinite(number)) { return number; } return false; }, /** * Updates the page number for the provided page. * * @method setNumber * @param page {Node} The page to update the number for. * @return void */ setNumber: function(page, number) { page.one(this.SELECTORS.NUMBER).set('text', this.CONSTANTS.PAGENUMBERPREFIX + number); }, /** * Returns a list of all page elements. * * @method getPages * @return {node[]} An array containing page nodes. */ getPages: function() { return Y.all(Y.Moodle.mod_quiz.util.slot.SELECTORS.PAGECONTENT + ' ' + Y.Moodle.mod_quiz.util.slot.SELECTORS.SECTIONUL + ' ' + this.SELECTORS.PAGE); }, /** * Is the given element a page element? * * @method isPage * @param page Page node * @return boolean */ isPage: function(page) { if (!page) { return false; } return page.hasClass(this.CSS.PAGE); }, /** * Does the page have atleast one slot? * * @method isEmpty * @param page Page node * @return boolean */ isEmpty: function(page) { var activity = page.next('li.activity'); if (!activity) { return true; } return !activity.hasClass('slot'); }, /** * Add a page and related elements to the list of slots. * * @method add * @param beforenode Int | Node | HTMLElement | String to add * @return page Page node */ add: function(beforenode) { var pagenumber = this.getNumber(this.getPageFromSlot(beforenode)) + 1; var pagehtml = M.mod_quiz.resource_toolbox.get('config').pagehtml; // Normalise the page number. pagehtml = pagehtml.replace(/%%PAGENUMBER%%/g, pagenumber); // Create the page node. var page = Y.Node.create(pagehtml); // Assign is as a drop target. YUI().use('dd-drop', function(Y) { var drop = new Y.DD.Drop({ node: page, groups: M.mod_quiz.dragres.groups }); page.drop = drop; }); // Insert in the correct place. beforenode.insert(page, 'after'); // Enhance the add menu to make if fully visible and clickable. if (typeof M.core.actionmenu !== "undefined") { M.core.actionmenu.newDOMNode(page); } return page; }, /** * Remove a page and related elements from the list of slots. * * @method remove * @param page Page node * @return void */ remove: function(page, keeppagebreak) { // Remove page break from previous slot. var previousslot = page.previous(Y.Moodle.mod_quiz.util.slot.SELECTORS.SLOT); if (!keeppagebreak && previousslot) { Y.Moodle.mod_quiz.util.slot.removePageBreak(previousslot); } page.remove(); }, /** * Reset the order of the numbers given to each page. * * @method reorderPages * @return void */ reorderPages: function() { // Get list of page nodes. var pages = this.getPages(); var currentpagenumber = 0; // Loop through pages incrementing the number each time. pages.each(function(page) { // Is the page empty? if (this.isEmpty(page)) { var keeppagebreak = page.next('li.slot') ? true : false; this.remove(page, keeppagebreak); return; } currentpagenumber++; // Set page number. this.setNumber(page, currentpagenumber); this.setId(page, currentpagenumber); }, this); // Reorder action menus this.reorderActionMenus(); }, /** * Reset the order of the numbers given to each action menu. * * @method reorderActionMenus * @return void */ reorderActionMenus: function() { // Get list of action menu nodes. var actionmenus = this.getActionMenus(); // Loop through pages incrementing the number each time. actionmenus.each(function(actionmenu, key) { var previousActionMenu = actionmenus.item(key - 1), previousActionMenunumber = 0; if (previousActionMenu) { previousActionMenunumber = this.getActionMenuId(previousActionMenu); } var id = previousActionMenunumber + 1; // Set menu id. this.setActionMenuId(actionmenu, id); // Update action-menu-1-menubar var menubar = actionmenu.one(this.SELECTORS.ACTIONMENUBAR); menubar.set('id', this.CONSTANTS.ACTIONMENUIDPREFIX + id + this.CONSTANTS.ACTIONMENUBARIDSUFFIX); // Update action-menu-1-menu var menumenu = actionmenu.one(this.SELECTORS.ACTIONMENUMENU); menumenu.set('id', this.CONSTANTS.ACTIONMENUIDPREFIX + id + this.CONSTANTS.ACTIONMENUMENUIDSUFFIX); // Update the URL of the add-section action. menumenu.one(this.SELECTORS.ADDASECTION).set('href', menumenu.one(this.SELECTORS.ADDASECTION).get('href').replace(/\baddsectionatpage=\d+\b/, 'addsectionatpage=' + id)); }, this); }, /** * Returns a list of all page elements. * * @method getActionMenus * @return {node[]} An array containing page nodes. */ getActionMenus: function() { return Y.all(Y.Moodle.mod_quiz.util.slot.SELECTORS.PAGECONTENT + ' ' + Y.Moodle.mod_quiz.util.slot.SELECTORS.SECTIONUL + ' ' + this.SELECTORS.ACTIONMENU); }, /** * Returns the ID for the provided action menu. * * @method getId * @param actionmenu {Node} The actionmenu to find an ID for. * @return {Number|false} The ID of the actionmenu in question or false if no ID was found. */ getActionMenuId: function(actionmenu) { // We perform a simple substitution operation to get the ID. var id = actionmenu.get('id').replace( this.CONSTANTS.ACTIONMENUIDPREFIX, ''); // Attempt to validate the ID. id = parseInt(id, 10); if (typeof id === 'number' && isFinite(id)) { return id; } return false; }, /** * Updates the page id for the provided page. * * @method setId * @param page {Node} The page to update the number for. * @param id int The id value. * @return void */ setActionMenuId: function(actionmenu, id) { actionmenu.set('id', this.CONSTANTS.ACTIONMENUIDPREFIX + id); } }; }, '@VERSION@', {"requires": ["node", "moodle-mod_quiz-util-base"]}); moodle-mod_quiz-util-page/moodle-mod_quiz-util-page-min.js000064400000006355152267121500017657 0ustar00YUI.add("moodle-mod_quiz-util-page",function(n,e){n.namespace("Moodle.mod_quiz.util.page"),n.Moodle.mod_quiz.util.page={CSS:{PAGE:"page"},CONSTANTS:{ACTIONMENUIDPREFIX:"action-menu-",ACTIONMENUBARIDSUFFIX:"-menubar",ACTIONMENUMENUIDSUFFIX:"-menu",PAGEIDPREFIX:"page-",PAGENUMBERPREFIX:M.util.get_string("page","moodle")+" "},SELECTORS:{ACTIONMENU:"div.moodle-actionmenu",ACTIONMENUBAR:".menubar",ACTIONMENUMENU:".menu",ADDASECTION:'[data-action="addasection"]',PAGE:"li.page",INSTANCENAME:".instancename",NUMBER:"h4"},getPageFromComponent:function(e){return n.one(e).ancestor(this.SELECTORS.PAGE,!0)},getPageFromSlot:function(e){return n.one(e).previous(this.SELECTORS.PAGE)},getId:function(e){e=e.get("id").replace(this.CONSTANTS.PAGEIDPREFIX,"");return!("number"!=typeof(e=parseInt(e,10))||!isFinite(e))&&e},setId:function(e,t){e.set("id",this.CONSTANTS.PAGEIDPREFIX+t)},getName:function(e){e=e.one(this.SELECTORS.INSTANCENAME);return e?e.get("firstChild").get("data"):null},getNumber:function(e){e=e.one(this.SELECTORS.NUMBER).get("text").replace(this.CONSTANTS.PAGENUMBERPREFIX,"");return!("number"!=typeof(e=parseInt(e,10))||!isFinite(e))&&e},setNumber:function(e,t){e.one(this.SELECTORS.NUMBER).set("text",this.CONSTANTS.PAGENUMBERPREFIX+t)},getPages:function(){return n.all(n.Moodle.mod_quiz.util.slot.SELECTORS.PAGECONTENT+" "+n.Moodle.mod_quiz.util.slot.SELECTORS.SECTIONUL+" "+this.SELECTORS.PAGE)},isPage:function(e){return!!e&&e.hasClass(this.CSS.PAGE)},isEmpty:function(e){e=e.next("li.activity");return!e||!e.hasClass("slot")},add:function(e){var t=this.getNumber(this.getPageFromSlot(e))+1,i=M.mod_quiz.resource_toolbox.get("config").pagehtml,i=i.replace(/%%PAGENUMBER%%/g,t),o=n.Node.create(i);return YUI().use("dd-drop",function(e){e=new e.DD.Drop({node:o,groups:M.mod_quiz.dragres.groups});o.drop=e}),e.insert(o,"after"),"undefined"!=typeof M.core.actionmenu&&M.core.actionmenu.newDOMNode(o),o},remove:function(e,t){var i=e.previous(n.Moodle.mod_quiz.util.slot.SELECTORS.SLOT);!t&&i&&n.Moodle.mod_quiz.util.slot.removePageBreak(i),e.remove()},reorderPages:function(){var e=this.getPages(),i=0;e.each(function(e){var t;if(this.isEmpty(e))return t=!!e.next("li.slot"),void this.remove(e,t);i++,this.setNumber(e,i),this.setId(e,i)},this),this.reorderActionMenus()},reorderActionMenus:function(){var o=this.getActionMenus();o.each(function(e,t){var t=o.item(t-1),i=0;t&&(i=this.getActionMenuId(t)),this.setActionMenuId(e,t=i+1),e.one(this.SELECTORS.ACTIONMENUBAR).set("id",this.CONSTANTS.ACTIONMENUIDPREFIX+t+this.CONSTANTS.ACTIONMENUBARIDSUFFIX),(i=e.one(this.SELECTORS.ACTIONMENUMENU)).set("id",this.CONSTANTS.ACTIONMENUIDPREFIX+t+this.CONSTANTS.ACTIONMENUMENUIDSUFFIX),i.one(this.SELECTORS.ADDASECTION).set("href",i.one(this.SELECTORS.ADDASECTION).get("href").replace(/\baddsectionatpage=\d+\b/,"addsectionatpage="+t))},this)},getActionMenus:function(){return n.all(n.Moodle.mod_quiz.util.slot.SELECTORS.PAGECONTENT+" "+n.Moodle.mod_quiz.util.slot.SELECTORS.SECTIONUL+" "+this.SELECTORS.ACTIONMENU)},getActionMenuId:function(e){e=e.get("id").replace(this.CONSTANTS.ACTIONMENUIDPREFIX,"");return!("number"!=typeof(e=parseInt(e,10))||!isFinite(e))&&e},setActionMenuId:function(e,t){e.set("id",this.CONSTANTS.ACTIONMENUIDPREFIX+t)}}},"@VERSION@",{requires:["node","moodle-mod_quiz-util-base"]});moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes.js000064400000126200152267121500017344 0ustar00YUI.add('moodle-mod_quiz-toolboxes', function (Y, NAME) { /* eslint-disable no-unused-vars */ /** * Resource and activity toolbox class. * * This class is responsible for managing AJAX interactions with activities and resources * when viewing a course in editing mode. * * @module moodle-course-toolboxes * @namespace M.course.toolboxes */ // The CSS classes we use. var CSS = { ACTIVITYINSTANCE: 'activityinstance', AVAILABILITYINFODIV: 'div.availabilityinfo', CONTENTWITHOUTLINK: 'contentwithoutlink', CONDITIONALHIDDEN: 'conditionalhidden', DIMCLASS: 'dimmed', DIMMEDTEXT: 'dimmed_text', EDITINSTRUCTIONS: 'editinstructions', EDITINGMAXMARK: 'editor_displayed', HIDE: 'hide', JOIN: 'page_join', MODINDENTCOUNT: 'mod-indent-', MODINDENTHUGE: 'mod-indent-huge', PAGE: 'page', SECTIONHIDDENCLASS: 'hidden', SECTIONIDPREFIX: 'section-', SELECTMULTIPLE: 'select-multiple', SLOT: 'slot', SHOW: 'editing_show', TITLEEDITOR: 'titleeditor' }, // The CSS selectors we use. SELECTOR = { ACTIONAREA: '.actions', ACTIONLINKTEXT: '.actionlinktext', ACTIVITYACTION: 'a.cm-edit-action[data-action], a.editing_maxmark, a.editing_section, input.shuffle_questions', ACTIVITYFORM: 'span.instancemaxmarkcontainer form', ACTIVITYINSTANCE: '.' + CSS.ACTIVITYINSTANCE, SECTIONINSTANCE: '.sectioninstance', ACTIVITYLI: 'li.activity, li.section', ACTIVITYMAXMARK: 'input[name=maxmark]', COMMANDSPAN: '.commands', CONTENTAFTERLINK: 'div.contentafterlink', CONTENTWITHOUTLINK: 'div.contentwithoutlink', DELETESECTIONICON: 'a.editing_delete .icon', EDITMAXMARK: 'a.editing_maxmark', EDITSECTION: 'a.editing_section', EDITSECTIONICON: 'a.editing_section .icon', EDITSHUFFLEQUESTIONSACTION: 'input.cm-edit-action[data-action]', EDITSHUFFLEAREA: '.instanceshufflequestions .shuffle-progress', HIDE: 'a.editing_hide', HIGHLIGHT: 'a.editing_highlight', INSTANCENAME: 'span.instancename', INSTANCEMAXMARK: 'span.instancemaxmark', INSTANCESECTION: 'span.instancesection', INSTANCESECTIONAREA: 'div.section-heading', MODINDENTDIV: '.mod-indent', MODINDENTOUTER: '.mod-indent-outer', NUMQUESTIONS: '.numberofquestions', PAGECONTENT: 'div#page-content', PAGELI: 'li.page', SECTIONLI: 'li.section', SECTIONUL: 'ul.section', SECTIONFORM: '.instancesectioncontainer form', SECTIONINPUT: 'input[name=section]', SELECTMULTIPLEBUTTON: '#selectmultiplecommand', SELECTMULTIPLECANCELBUTTON: '#selectmultiplecancelcommand', SELECTMULTIPLECHECKBOX: '.select-multiple-checkbox', SELECTMULTIPLEDELETEBUTTON: '#selectmultipledeletecommand', SELECTALL: '#questionselectall', SHOW: 'a.' + CSS.SHOW, SLOTLI: 'li.slot', SUMMARKS: '.mod_quiz_summarks' }, BODY = Y.one(document.body); // Setup the basic namespace. M.mod_quiz = M.mod_quiz || {}; /** * The toolbox class is a generic class which should never be directly * instantiated. Please extend it instead. * * @class toolbox * @constructor * @protected * @extends Base */ var TOOLBOX = function() { TOOLBOX.superclass.constructor.apply(this, arguments); }; Y.extend(TOOLBOX, Y.Base, { /** * Send a request using the REST API * * @method send_request * @param {Object} data The data to submit with the AJAX request * @param {Node} [statusspinner] A statusspinner which may contain a section loader * @param {Function} success_callback The callback to use on success * @param {Object} [optionalconfig] Any additional configuration to submit * @chainable */ send_request: function(data, statusspinner, success_callback, optionalconfig) { // Default data structure if (!data) { data = {}; } // Handle any variables which we must pass back through to var pageparams = this.get('config').pageparams, varname; for (varname in pageparams) { data[varname] = pageparams[varname]; } data.sesskey = M.cfg.sesskey; data.courseid = this.get('courseid'); data.quizid = this.get('quizid'); var uri = M.cfg.wwwroot + this.get('ajaxurl'); // Define the configuration to send with the request var responsetext = []; var config = { method: 'POST', data: data, on: { success: function(tid, response) { try { responsetext = Y.JSON.parse(response.responseText); if (responsetext.error) { new M.core.ajaxException(responsetext); } } catch (e) { // Ignore. } // Run the callback if we have one. if (responsetext.hasOwnProperty('newsummarks')) { Y.one(SELECTOR.SUMMARKS).setHTML(responsetext.newsummarks); } if (responsetext.hasOwnProperty('newnumquestions')) { Y.one(SELECTOR.NUMQUESTIONS).setHTML( M.util.get_string('numquestionsx', 'quiz', responsetext.newnumquestions) ); } if (success_callback) { Y.bind(success_callback, this, responsetext)(); } if (statusspinner) { window.setTimeout(function() { statusspinner.hide(); }, 400); } }, failure: function(tid, response) { if (statusspinner) { statusspinner.hide(); } new M.core.ajaxException(response); } }, context: this }; // Apply optional config if (optionalconfig) { for (varname in optionalconfig) { config[varname] = optionalconfig[varname]; } } if (statusspinner) { statusspinner.show(); } // Send the request Y.io(uri, config); return this; } }, { NAME: 'mod_quiz-toolbox', ATTRS: { /** * The ID of the Moodle Course being edited. * * @attribute courseid * @default 0 * @type Number */ courseid: { 'value': 0 }, /** * The Moodle course format. * * @attribute format * @default 'topics' * @type String */ quizid: { 'value': 0 }, /** * The URL to use when submitting requests. * @attribute ajaxurl * @default null * @type String */ ajaxurl: { 'value': null }, /** * Any additional configuration passed when creating the instance. * * @attribute config * @default {} * @type Object */ config: { 'value': {} } } } ); /** * Resource and activity toolbox class. * * This class is responsible for managing AJAX interactions with activities and resources * when viewing a quiz in editing mode. * * @module mod_quiz-resource-toolbox * @namespace M.mod_quiz.resource_toolbox */ /** * Resource and activity toolbox class. * * This is a class extending TOOLBOX containing code specific to resources * * This class is responsible for managing AJAX interactions with activities and resources * when viewing a quiz in editing mode. * * @class resources * @constructor * @extends M.course.toolboxes.toolbox */ var RESOURCETOOLBOX = function() { RESOURCETOOLBOX.superclass.constructor.apply(this, arguments); }; Y.extend(RESOURCETOOLBOX, TOOLBOX, { /** * An Array of events added when editing a max mark field. * These should all be detached when editing is complete. * * @property editmaxmarkevents * @protected * @type Array * @protected */ editmaxmarkevents: [], /** * */ NODE_PAGE: 1, NODE_SLOT: 2, NODE_JOIN: 3, /** * Initialize the resource toolbox * * For each activity the commands are updated and a reference to the activity is attached. * This way it doesn't matter where the commands are going to called from they have a reference to the * activity that they relate to. * This is essential as some of the actions are displayed in an actionmenu which removes them from the * page flow. * * This function also creates a single event delegate to manage all AJAX actions for all activities on * the page. * * @method initializer * @protected */ initializer: function() { M.mod_quiz.quizbase.register_module(this); Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this); Y.delegate('click', this.handle_data_action, BODY, SELECTOR.DEPENDENCY_LINK, this); this.initialise_select_multiple(); }, /** * Initialize the select multiple options * * Add actions to the buttons that enable multiple slots to be selected and managed at once. * * @method initialise_select_multiple * @protected */ initialise_select_multiple: function() { // Click select multiple button to show the select all options. Y.one(SELECTOR.SELECTMULTIPLEBUTTON).on('click', function(e) { e.preventDefault(); Y.one('body').addClass(CSS.SELECTMULTIPLE); }); // Click cancel button to show the select all options. Y.one(SELECTOR.SELECTMULTIPLECANCELBUTTON).on('click', function(e) { e.preventDefault(); Y.one('body').removeClass(CSS.SELECTMULTIPLE); }); // Assign the delete method to the delete multiple button. Y.delegate('click', this.delete_multiple_action, BODY, SELECTOR.SELECTMULTIPLEDELETEBUTTON, this); }, /** * Handles the delegation event. When this is fired someone has triggered an action. * * Note not all actions will result in an AJAX enhancement. * * @protected * @method handle_data_action * @param {EventFacade} ev The event that was triggered. * @returns {boolean} */ handle_data_action: function(ev) { // We need to get the anchor element that triggered this event. var node = ev.target; if (!node.test('a')) { node = node.ancestor(SELECTOR.ACTIVITYACTION); } // From the anchor we can get both the activity (added during initialisation) and the action being // performed (added by the UI as a data attribute). var action = node.getData('action'), activity = node.ancestor(SELECTOR.ACTIVITYLI); if (!node.test('a') || !action || !activity) { // It wasn't a valid action node. return; } // Switch based upon the action and do the desired thing. switch (action) { case 'editmaxmark': // The user wishes to edit the maxmark of the resource. this.edit_maxmark(ev, node, activity, action); break; case 'delete': // The user is deleting the activity. this.delete_with_confirmation(ev, node, activity, action); break; case 'addpagebreak': case 'removepagebreak': // The user is adding or removing a page break. this.update_page_break(ev, node, activity, action); break; case 'adddependency': case 'removedependency': // The user is adding or removing a dependency between questions. this.update_dependency(ev, node, activity, action); break; default: // Nothing to do here! break; } }, /** * Add a loading icon to the specified activity. * * The icon is added within the action area. * * @method add_spinner * @param {Node} activity The activity to add a loading icon to * @return {Node|null} The newly created icon, or null if the action area was not found. */ add_spinner: function(activity) { var actionarea = activity.one(SELECTOR.ACTIONAREA); if (actionarea) { return M.util.add_spinner(Y, actionarea); } return null; }, /** * Deletes the given activity or resource after confirmation. * * @protected * @method delete_with_confirmation * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. */ delete_with_confirmation: function(ev, button, activity) { // Prevent the default button action. ev.preventDefault(); // Get the element we're working on. var element = activity; // Create confirm string (different if element has or does not have name) var qtypename = M.util.get_string( 'pluginname', 'qtype_' + element.getAttribute('class').match(/qtype_([^\s]*)/)[1] ); // Create the confirmation dialogue. require(['core/notification'], function(Notification) { Notification.saveCancelPromise( M.util.get_string('confirm', 'moodle'), M.util.get_string('confirmremovequestion', 'quiz', qtypename), M.util.get_string('yes', 'moodle') ).then(function() { var spinner = this.add_spinner(element); var data = { 'class': 'resource', 'action': 'DELETE', 'id': Y.Moodle.mod_quiz.util.slot.getId(element) }; this.send_request(data, spinner, function(response) { if (response.deleted) { // Actually remove the element. Y.Moodle.mod_quiz.util.slot.remove(element); this.reorganise_edit_page(); if (M.core.actionmenu && M.core.actionmenu.instance) { M.core.actionmenu.instance.hideMenu(ev); } } }); return; }.bind(this)).catch(function() { // User cancelled. }); }.bind(this)); }, /** * Finds the section that would become empty if we remove the selected slots. * * @protected * @method find_sections_that_would_become_empty * @returns {String} The name of the first section found */ find_sections_that_would_become_empty: function() { var section; var sectionnodes = Y.all(SELECTOR.SECTIONLI); if (sectionnodes.size() > 1) { sectionnodes.some(function(node) { var sectionname = node.one(SELECTOR.INSTANCESECTION).getContent(); var checked = node.all(SELECTOR.SELECTMULTIPLECHECKBOX + ':checked'); var unchecked = node.all(SELECTOR.SELECTMULTIPLECHECKBOX + ':not(:checked)'); if (!checked.isEmpty() && unchecked.isEmpty()) { section = sectionname; } return section; }); } return section; }, /** * Takes care of what needs to happen when the user clicks on the delete multiple button. * * @protected * @method delete_multiple_action * @param {EventFacade} ev The event that was fired. */ delete_multiple_action: function(ev) { var problemsection = this.find_sections_that_would_become_empty(); if (typeof problemsection !== 'undefined') { require(['core/notification'], function(Notification) { Notification.alert( M.util.get_string('cannotremoveslots', 'quiz'), M.util.get_string('cannotremoveallsectionslots', 'quiz', problemsection) ); }); } else { this.delete_multiple_with_confirmation(ev); } }, /** * Deletes the given activities or resources after confirmation. * * @protected * @method delete_multiple_with_confirmation * @param {EventFacade} ev The event that was fired. */ delete_multiple_with_confirmation: function(ev) { ev.preventDefault(); var ids = ''; var slots = []; Y.all(SELECTOR.SELECTMULTIPLECHECKBOX + ':checked').each(function(node) { var slot = Y.Moodle.mod_quiz.util.slot.getSlotFromComponent(node); ids += ids === '' ? '' : ','; ids += Y.Moodle.mod_quiz.util.slot.getId(slot); slots.push(slot); }); var element = Y.one('div.mod-quiz-edit-content'); // Do nothing if no slots are selected. if (!slots || !slots.length) { return; } require(['core/notification'], function(Notification) { Notification.saveCancelPromise( M.util.get_string('confirm', 'moodle'), M.util.get_string('areyousureremoveselected', 'quiz'), M.util.get_string('yes', 'moodle') ).then(function() { var spinner = this.add_spinner(element); var data = { 'class': 'resource', field: 'deletemultiple', ids: ids }; // Delete items on server. this.send_request(data, spinner, function(response) { // Delete locally if deleted on server. if (response.deleted) { // Actually remove the element. Y.all(SELECTOR.SELECTMULTIPLECHECKBOX + ':checked').each(function(node) { Y.Moodle.mod_quiz.util.slot.remove(node.ancestor('li.activity')); }); // Update the page numbers and sections. this.reorganise_edit_page(); // Remove the select multiple options. Y.one('body').removeClass(CSS.SELECTMULTIPLE); } }); return; }.bind(this)).catch(function() { // User cancelled. }); }.bind(this)); }, /** * Edit the maxmark for the resource * * @protected * @method edit_maxmark * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @param {String} action The action that has been requested. * @return Boolean */ edit_maxmark: function(ev, button, activity) { // Get the element we're working on var instancemaxmark = activity.one(SELECTOR.INSTANCEMAXMARK), instance = activity.one(SELECTOR.ACTIVITYINSTANCE), currentmaxmark = instancemaxmark.get('firstChild'), oldmaxmark = currentmaxmark.get('data'), maxmarktext = oldmaxmark, thisevent, anchor = instancemaxmark, // Grab the anchor so that we can swap it with the edit form. data = { 'class': 'resource', 'field': 'getmaxmark', 'id': Y.Moodle.mod_quiz.util.slot.getId(activity) }; // Prevent the default actions. ev.preventDefault(); this.send_request(data, null, function(response) { if (M.core.actionmenu && M.core.actionmenu.instance) { M.core.actionmenu.instance.hideMenu(ev); } // Try to retrieve the existing string from the server. if (response.instancemaxmark) { maxmarktext = response.instancemaxmark; } // Create the editor and submit button. var editform = Y.Node.create(''); var editinstructions = Y.Node.create('') .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle')); var editor = Y.Node.create('').setAttrs({ 'value': maxmarktext, 'autocomplete': 'off', 'aria-describedby': 'id_editinstructions', 'maxLength': '12', 'size': parseInt(this.get('config').questiondecimalpoints, 10) + 2 }); // Clear the existing content and put the editor in. editform.appendChild(editor); editform.setData('anchor', anchor); instance.insert(editinstructions, 'before'); anchor.replace(editform); // We hide various components whilst editing: activity.addClass(CSS.EDITINGMAXMARK); // Focus and select the editor text. editor.focus().select(); // Cancel the edit if we lose focus or the escape key is pressed. thisevent = editor.on('blur', this.edit_maxmark_cancel, this, activity, false); this.editmaxmarkevents.push(thisevent); thisevent = editor.on('key', this.edit_maxmark_cancel, 'esc', this, activity, true); this.editmaxmarkevents.push(thisevent); // Handle form submission. thisevent = editform.on('submit', this.edit_maxmark_submit, this, activity, oldmaxmark); this.editmaxmarkevents.push(thisevent); }); }, /** * Handles the submit event when editing the activity or resources maxmark. * * @protected * @method edit_maxmark_submit * @param {EventFacade} ev The event that triggered this. * @param {Node} activity The activity whose maxmark we are altering. * @param {String} originalmaxmark The original maxmark the activity or resource had. */ edit_maxmark_submit: function(ev, activity, originalmaxmark) { // We don't actually want to submit anything. ev.preventDefault(); var newmaxmark = Y.Lang.trim(activity.one(SELECTOR.ACTIVITYFORM + ' ' + SELECTOR.ACTIVITYMAXMARK).get('value')); var spinner = this.add_spinner(activity); this.edit_maxmark_clear(activity); activity.one(SELECTOR.INSTANCEMAXMARK).setContent(newmaxmark); if (newmaxmark !== null && newmaxmark !== "" && newmaxmark !== originalmaxmark) { var data = { 'class': 'resource', 'field': 'updatemaxmark', 'maxmark': newmaxmark, 'id': Y.Moodle.mod_quiz.util.slot.getId(activity) }; this.send_request(data, spinner, function(response) { if (response.instancemaxmark) { activity.one(SELECTOR.INSTANCEMAXMARK).setContent(response.instancemaxmark); } }); } }, /** * Handles the cancel event when editing the activity or resources maxmark. * * @protected * @method edit_maxmark_cancel * @param {EventFacade} ev The event that triggered this. * @param {Node} activity The activity whose maxmark we are altering. * @param {Boolean} preventdefault If true we should prevent the default action from occuring. */ edit_maxmark_cancel: function(ev, activity, preventdefault) { if (preventdefault) { ev.preventDefault(); } this.edit_maxmark_clear(activity); }, /** * Handles clearing the editing UI and returning things to the original state they were in. * * @protected * @method edit_maxmark_clear * @param {Node} activity The activity whose maxmark we were altering. */ edit_maxmark_clear: function(activity) { // Detach all listen events to prevent duplicate triggers new Y.EventHandle(this.editmaxmarkevents).detach(); var editform = activity.one(SELECTOR.ACTIVITYFORM), instructions = activity.one('#id_editinstructions'); if (editform) { editform.replace(editform.getData('anchor')); } if (instructions) { instructions.remove(); } // Remove the editing class again to revert the display. activity.removeClass(CSS.EDITINGMAXMARK); // Refocus the link which was clicked originally so the user can continue using keyboard nav. Y.later(100, this, function() { activity.one(SELECTOR.EDITMAXMARK).focus(); }); // TODO MDL-50768 This hack is to keep Behat happy until they release a version of // MinkSelenium2Driver that fixes // https://github.com/Behat/MinkSelenium2Driver/issues/80. if (!Y.one('input[name=maxmark')) { Y.one('body').append(''); } }, /** * Joins or separates the given slot with the page of the previous slot. Reorders the pages of * the other slots * * @protected * @method update_page_break * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @param {String} action The action, addpagebreak or removepagebreak. * @chainable */ update_page_break: function(ev, button, activity, action) { // Prevent the default button action ev.preventDefault(); var nextactivity = activity.next('li.activity.slot'); var spinner = this.add_spinner(nextactivity); var value = action === 'removepagebreak' ? 1 : 2; var data = { 'class': 'resource', 'field': 'updatepagebreak', 'id': Y.Moodle.mod_quiz.util.slot.getId(nextactivity), 'value': value }; this.send_request(data, spinner, function(response) { if (response.slots) { if (action === 'addpagebreak') { Y.Moodle.mod_quiz.util.page.add(activity); } else { var page = activity.next(Y.Moodle.mod_quiz.util.page.SELECTORS.PAGE); Y.Moodle.mod_quiz.util.page.remove(page, true); } this.reorganise_edit_page(); } }); return this; }, /** * Updates a slot to either require the question in the previous slot to * have been answered, or not, * * @protected * @method update_page_break * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @param {String} action The action, adddependency or removedependency. * @chainable */ update_dependency: function(ev, button, activity, action) { // Prevent the default button action. ev.preventDefault(); var spinner = this.add_spinner(activity); var data = { 'class': 'resource', 'field': 'updatedependency', 'id': Y.Moodle.mod_quiz.util.slot.getId(activity), 'value': action === 'adddependency' ? 1 : 0 }; this.send_request(data, spinner, function(response) { if (response.hasOwnProperty('requireprevious')) { Y.Moodle.mod_quiz.util.slot.updateDependencyIcon(activity, response.requireprevious); } }); return this; }, /** * Reorganise the UI after every edit action. * * @protected * @method reorganise_edit_page */ reorganise_edit_page: function() { Y.Moodle.mod_quiz.util.slot.reorderSlots(); Y.Moodle.mod_quiz.util.slot.reorderPageBreaks(); Y.Moodle.mod_quiz.util.page.reorderPages(); Y.Moodle.mod_quiz.util.slot.updateOneSlotSections(); Y.Moodle.mod_quiz.util.slot.updateAllDependencyIcons(); }, NAME: 'mod_quiz-resource-toolbox', ATTRS: { courseid: { 'value': 0 }, quizid: { 'value': 0 } } }); M.mod_quiz.resource_toolbox = null; M.mod_quiz.init_resource_toolbox = function(config) { M.mod_quiz.resource_toolbox = new RESOURCETOOLBOX(config); return M.mod_quiz.resource_toolbox; }; /** * Section toolbox class. * * This class is responsible for managing AJAX interactions with sections * when adding, editing, removing section headings. * * @module moodle-mod_quiz-toolboxes * @namespace M.mod_quiz.toolboxes */ /** * Section toolbox class. * * This class is responsible for managing AJAX interactions with sections * when adding, editing, removing section headings when editing a quiz. * * @class section * @constructor * @extends M.mod_quiz.toolboxes.toolbox */ var SECTIONTOOLBOX = function() { SECTIONTOOLBOX.superclass.constructor.apply(this, arguments); }; Y.extend(SECTIONTOOLBOX, TOOLBOX, { /** * An Array of events added when editing a max mark field. * These should all be detached when editing is complete. * * @property editsectionevents * @protected * @type Array * @protected */ editsectionevents: [], /** * Initialize the section toolboxes module. * * Updates all span.commands with relevant handlers and other required changes. * * @method initializer * @protected */ initializer: function() { M.mod_quiz.quizbase.register_module(this); BODY.delegate('key', this.handle_data_action, 'down:enter', SELECTOR.ACTIVITYACTION, this); Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this); Y.delegate('change', this.handle_data_action, BODY, SELECTOR.EDITSHUFFLEQUESTIONSACTION, this); }, /** * Handles the delegation event. When this is fired someone has triggered an action. * * Note not all actions will result in an AJAX enhancement. * * @protected * @method handle_data_action * @param {EventFacade} ev The event that was triggered. * @returns {boolean} */ handle_data_action: function(ev) { // We need to get the anchor element that triggered this event. var node = ev.target; if (!node.test('a') && !node.test('input[data-action]')) { node = node.ancestor(SELECTOR.ACTIVITYACTION); } // From the anchor we can get both the activity (added during initialisation) and the action being // performed (added by the UI as a data attribute). var action = node.getData('action'), activity = node.ancestor(SELECTOR.ACTIVITYLI); if ((!node.test('a') && !node.test('input[data-action]')) || !action || !activity) { // It wasn't a valid action node. return; } // Switch based upon the action and do the desired thing. switch (action) { case 'edit_section_title': // The user wishes to edit the section headings. this.edit_section_title(ev, node, activity, action); break; case 'shuffle_questions': // The user wishes to edit the shuffle questions of the section (resource). this.edit_shuffle_questions(ev, node, activity, action); break; case 'deletesection': // The user is deleting the activity. this.delete_section_with_confirmation(ev, node, activity, action); break; default: // Nothing to do here! break; } }, /** * Deletes the given section heading after confirmation. * * @protected * @method delete_section_with_confirmation * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @chainable */ delete_section_with_confirmation: function(ev, button, activity) { ev.preventDefault(); require(['core/notification'], function(Notification) { Notification.saveCancelPromise( M.util.get_string('confirm', 'moodle'), M.util.get_string('confirmremovesectionheading', 'quiz', activity.getData('sectionname')), M.util.get_string('yes', 'moodle') ).then(function() { var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.ACTIONAREA)); var data = { 'class': 'section', 'action': 'DELETE', 'id': activity.get('id').replace('section-', '') }; this.send_request(data, spinner, function(response) { if (response.deleted) { window.location.reload(true); } }); return; }.bind(this)).catch(function() { // User cancelled. }); }.bind(this)); }, /** * Edit the edit section title for the section * * @protected * @method edit_section_title * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @param {String} action The action that has been requested. * @return Boolean */ edit_section_title: function(ev, button, activity) { // Get the element we're working on var activityid = activity.get('id').replace('section-', ''), instancesection = activity.one(SELECTOR.INSTANCESECTION), thisevent, anchor = instancesection, // Grab the anchor so that we can swap it with the edit form. data = { 'class': 'section', 'field': 'getsectiontitle', 'id': activityid }; // Prevent the default actions. ev.preventDefault(); this.send_request(data, null, function(response) { // Try to retrieve the existing string from the server. var oldtext = response.instancesection; // Create the editor and submit button. var editform = Y.Node.create(''); var editinstructions = Y.Node.create('') .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle')); var editor = Y.Node.create('').setAttrs({ 'value': oldtext, 'autocomplete': 'off', 'aria-describedby': 'id_editinstructions', 'maxLength': '255' // This is the maxlength in DB. }); // Clear the existing content and put the editor in. editform.appendChild(editor); editform.setData('anchor', anchor); instancesection.insert(editinstructions, 'before'); anchor.replace(editform); // Focus and select the editor text. editor.focus().select(); // Cancel the edit if we lose focus or the escape key is pressed. thisevent = editor.on('blur', this.edit_section_title_cancel, this, activity, false); this.editsectionevents.push(thisevent); thisevent = editor.on('key', this.edit_section_title_cancel, 'esc', this, activity, true); this.editsectionevents.push(thisevent); // Handle form submission. thisevent = editform.on('submit', this.edit_section_title_submit, this, activity, oldtext); this.editsectionevents.push(thisevent); }); }, /** * Handles the submit event when editing section heading. * * @protected * @method edit_section_title_submiy * @param {EventFacade} ev The event that triggered this. * @param {Node} activity The activity whose maxmark we are altering. * @param {String} oldtext The original maxmark the activity or resource had. */ edit_section_title_submit: function(ev, activity, oldtext) { // We don't actually want to submit anything. ev.preventDefault(); var newtext = Y.Lang.trim(activity.one(SELECTOR.SECTIONFORM + ' ' + SELECTOR.SECTIONINPUT).get('value')); var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.INSTANCESECTIONAREA)); this.edit_section_title_clear(activity); if (newtext !== null && newtext !== oldtext) { var instancesection = activity.one(SELECTOR.INSTANCESECTION); var instancesectiontext = newtext; if (newtext.trim() === '') { // Add a visually-hidden default section heading text to make sure we don't end up with an empty section heading. instancesectiontext = M.util.get_string('sectionnoname', 'quiz'); instancesection.addClass('visually-hidden'); } else { // Show the section heading when a non-empty value is set. instancesection.removeClass('visually-hidden'); } instancesection.setContent(instancesectiontext); var data = { 'class': 'section', 'field': 'updatesectiontitle', 'newheading': newtext, 'id': activity.get('id').replace('section-', '') }; this.send_request(data, spinner, function(response) { if (response) { // Set the content of the section heading if for some reason the response is different from the new text. // e.g. filters were applied, the update failed, etc. if (newtext !== response.instancesection) { if (response.instancesection.trim() === '') { // Add a visually-hidden default section heading text. instancesectiontext = M.util.get_string('sectionnoname', 'quiz'); instancesection.addClass('visually-hidden'); } else { instancesectiontext = response.instancesection; // Show the section heading when a non-empty value is set. instancesection.removeClass('visually-hidden'); } instancesection.setContent(instancesectiontext); } activity.one(SELECTOR.EDITSECTIONICON).set('title', M.util.get_string('sectionheadingedit', 'quiz', response.instancesection)); activity.one(SELECTOR.EDITSECTIONICON).set('alt', M.util.get_string('sectionheadingedit', 'quiz', response.instancesection)); var deleteicon = activity.one(SELECTOR.DELETESECTIONICON); if (deleteicon) { deleteicon.set('title', M.util.get_string('sectionheadingremove', 'quiz', response.instancesection)); deleteicon.set('alt', M.util.get_string('sectionheadingremove', 'quiz', response.instancesection)); } } }); } }, /** * Handles the cancel event when editing the activity or resources maxmark. * * @protected * @method edit_maxmark_cancel * @param {EventFacade} ev The event that triggered this. * @param {Node} activity The activity whose maxmark we are altering. * @param {Boolean} preventdefault If true we should prevent the default action from occuring. */ edit_section_title_cancel: function(ev, activity, preventdefault) { if (preventdefault) { ev.preventDefault(); } this.edit_section_title_clear(activity); }, /** * Handles clearing the editing UI and returning things to the original state they were in. * * @protected * @method edit_maxmark_clear * @param {Node} activity The activity whose maxmark we were altering. */ edit_section_title_clear: function(activity) { // Detach all listen events to prevent duplicate triggers new Y.EventHandle(this.editsectionevents).detach(); var editform = activity.one(SELECTOR.SECTIONFORM), instructions = activity.one('#id_editinstructions'); if (editform) { editform.replace(editform.getData('anchor')); } if (instructions) { instructions.remove(); } // Refocus the link which was clicked originally so the user can continue using keyboard nav. Y.later(100, this, function() { activity.one(SELECTOR.EDITSECTION).focus(); }); // This hack is to keep Behat happy until they release a version of // MinkSelenium2Driver that fixes // https://github.com/Behat/MinkSelenium2Driver/issues/80. if (!Y.one('input[name=section]')) { Y.one('body').append(''); } }, /** * Edit the edit shuffle questions for the section * * @protected * @method edit_shuffle_questions * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @return Boolean */ edit_shuffle_questions: function(ev, button, activity) { var newvalue; if (activity.one(SELECTOR.EDITSHUFFLEQUESTIONSACTION).get('checked')) { newvalue = 1; activity.addClass('shuffled'); } else { newvalue = 0; activity.removeClass('shuffled'); } // Prevent the default actions. ev.preventDefault(); // Get the element we're working on var data = { 'class': 'section', 'field': 'updateshufflequestions', 'id': activity.get('id').replace('section-', ''), 'newshuffle': newvalue }; // Send request. var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.EDITSHUFFLEAREA)); this.send_request(data, spinner); } }, { NAME: 'mod_quiz-section-toolbox', ATTRS: { courseid: { 'value': 0 }, quizid: { 'value': 0 } } }); M.mod_quiz.init_section_toolbox = function(config) { return new SECTIONTOOLBOX(config); }; }, '@VERSION@', { "requires": [ "base", "node", "event", "event-key", "io", "moodle-mod_quiz-quizbase", "moodle-mod_quiz-util-slot", "moodle-core-notification-ajaxexception" ] }); moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes-debug.js000064400000126200152267121500020430 0ustar00YUI.add('moodle-mod_quiz-toolboxes', function (Y, NAME) { /* eslint-disable no-unused-vars */ /** * Resource and activity toolbox class. * * This class is responsible for managing AJAX interactions with activities and resources * when viewing a course in editing mode. * * @module moodle-course-toolboxes * @namespace M.course.toolboxes */ // The CSS classes we use. var CSS = { ACTIVITYINSTANCE: 'activityinstance', AVAILABILITYINFODIV: 'div.availabilityinfo', CONTENTWITHOUTLINK: 'contentwithoutlink', CONDITIONALHIDDEN: 'conditionalhidden', DIMCLASS: 'dimmed', DIMMEDTEXT: 'dimmed_text', EDITINSTRUCTIONS: 'editinstructions', EDITINGMAXMARK: 'editor_displayed', HIDE: 'hide', JOIN: 'page_join', MODINDENTCOUNT: 'mod-indent-', MODINDENTHUGE: 'mod-indent-huge', PAGE: 'page', SECTIONHIDDENCLASS: 'hidden', SECTIONIDPREFIX: 'section-', SELECTMULTIPLE: 'select-multiple', SLOT: 'slot', SHOW: 'editing_show', TITLEEDITOR: 'titleeditor' }, // The CSS selectors we use. SELECTOR = { ACTIONAREA: '.actions', ACTIONLINKTEXT: '.actionlinktext', ACTIVITYACTION: 'a.cm-edit-action[data-action], a.editing_maxmark, a.editing_section, input.shuffle_questions', ACTIVITYFORM: 'span.instancemaxmarkcontainer form', ACTIVITYINSTANCE: '.' + CSS.ACTIVITYINSTANCE, SECTIONINSTANCE: '.sectioninstance', ACTIVITYLI: 'li.activity, li.section', ACTIVITYMAXMARK: 'input[name=maxmark]', COMMANDSPAN: '.commands', CONTENTAFTERLINK: 'div.contentafterlink', CONTENTWITHOUTLINK: 'div.contentwithoutlink', DELETESECTIONICON: 'a.editing_delete .icon', EDITMAXMARK: 'a.editing_maxmark', EDITSECTION: 'a.editing_section', EDITSECTIONICON: 'a.editing_section .icon', EDITSHUFFLEQUESTIONSACTION: 'input.cm-edit-action[data-action]', EDITSHUFFLEAREA: '.instanceshufflequestions .shuffle-progress', HIDE: 'a.editing_hide', HIGHLIGHT: 'a.editing_highlight', INSTANCENAME: 'span.instancename', INSTANCEMAXMARK: 'span.instancemaxmark', INSTANCESECTION: 'span.instancesection', INSTANCESECTIONAREA: 'div.section-heading', MODINDENTDIV: '.mod-indent', MODINDENTOUTER: '.mod-indent-outer', NUMQUESTIONS: '.numberofquestions', PAGECONTENT: 'div#page-content', PAGELI: 'li.page', SECTIONLI: 'li.section', SECTIONUL: 'ul.section', SECTIONFORM: '.instancesectioncontainer form', SECTIONINPUT: 'input[name=section]', SELECTMULTIPLEBUTTON: '#selectmultiplecommand', SELECTMULTIPLECANCELBUTTON: '#selectmultiplecancelcommand', SELECTMULTIPLECHECKBOX: '.select-multiple-checkbox', SELECTMULTIPLEDELETEBUTTON: '#selectmultipledeletecommand', SELECTALL: '#questionselectall', SHOW: 'a.' + CSS.SHOW, SLOTLI: 'li.slot', SUMMARKS: '.mod_quiz_summarks' }, BODY = Y.one(document.body); // Setup the basic namespace. M.mod_quiz = M.mod_quiz || {}; /** * The toolbox class is a generic class which should never be directly * instantiated. Please extend it instead. * * @class toolbox * @constructor * @protected * @extends Base */ var TOOLBOX = function() { TOOLBOX.superclass.constructor.apply(this, arguments); }; Y.extend(TOOLBOX, Y.Base, { /** * Send a request using the REST API * * @method send_request * @param {Object} data The data to submit with the AJAX request * @param {Node} [statusspinner] A statusspinner which may contain a section loader * @param {Function} success_callback The callback to use on success * @param {Object} [optionalconfig] Any additional configuration to submit * @chainable */ send_request: function(data, statusspinner, success_callback, optionalconfig) { // Default data structure if (!data) { data = {}; } // Handle any variables which we must pass back through to var pageparams = this.get('config').pageparams, varname; for (varname in pageparams) { data[varname] = pageparams[varname]; } data.sesskey = M.cfg.sesskey; data.courseid = this.get('courseid'); data.quizid = this.get('quizid'); var uri = M.cfg.wwwroot + this.get('ajaxurl'); // Define the configuration to send with the request var responsetext = []; var config = { method: 'POST', data: data, on: { success: function(tid, response) { try { responsetext = Y.JSON.parse(response.responseText); if (responsetext.error) { new M.core.ajaxException(responsetext); } } catch (e) { // Ignore. } // Run the callback if we have one. if (responsetext.hasOwnProperty('newsummarks')) { Y.one(SELECTOR.SUMMARKS).setHTML(responsetext.newsummarks); } if (responsetext.hasOwnProperty('newnumquestions')) { Y.one(SELECTOR.NUMQUESTIONS).setHTML( M.util.get_string('numquestionsx', 'quiz', responsetext.newnumquestions) ); } if (success_callback) { Y.bind(success_callback, this, responsetext)(); } if (statusspinner) { window.setTimeout(function() { statusspinner.hide(); }, 400); } }, failure: function(tid, response) { if (statusspinner) { statusspinner.hide(); } new M.core.ajaxException(response); } }, context: this }; // Apply optional config if (optionalconfig) { for (varname in optionalconfig) { config[varname] = optionalconfig[varname]; } } if (statusspinner) { statusspinner.show(); } // Send the request Y.io(uri, config); return this; } }, { NAME: 'mod_quiz-toolbox', ATTRS: { /** * The ID of the Moodle Course being edited. * * @attribute courseid * @default 0 * @type Number */ courseid: { 'value': 0 }, /** * The Moodle course format. * * @attribute format * @default 'topics' * @type String */ quizid: { 'value': 0 }, /** * The URL to use when submitting requests. * @attribute ajaxurl * @default null * @type String */ ajaxurl: { 'value': null }, /** * Any additional configuration passed when creating the instance. * * @attribute config * @default {} * @type Object */ config: { 'value': {} } } } ); /** * Resource and activity toolbox class. * * This class is responsible for managing AJAX interactions with activities and resources * when viewing a quiz in editing mode. * * @module mod_quiz-resource-toolbox * @namespace M.mod_quiz.resource_toolbox */ /** * Resource and activity toolbox class. * * This is a class extending TOOLBOX containing code specific to resources * * This class is responsible for managing AJAX interactions with activities and resources * when viewing a quiz in editing mode. * * @class resources * @constructor * @extends M.course.toolboxes.toolbox */ var RESOURCETOOLBOX = function() { RESOURCETOOLBOX.superclass.constructor.apply(this, arguments); }; Y.extend(RESOURCETOOLBOX, TOOLBOX, { /** * An Array of events added when editing a max mark field. * These should all be detached when editing is complete. * * @property editmaxmarkevents * @protected * @type Array * @protected */ editmaxmarkevents: [], /** * */ NODE_PAGE: 1, NODE_SLOT: 2, NODE_JOIN: 3, /** * Initialize the resource toolbox * * For each activity the commands are updated and a reference to the activity is attached. * This way it doesn't matter where the commands are going to called from they have a reference to the * activity that they relate to. * This is essential as some of the actions are displayed in an actionmenu which removes them from the * page flow. * * This function also creates a single event delegate to manage all AJAX actions for all activities on * the page. * * @method initializer * @protected */ initializer: function() { M.mod_quiz.quizbase.register_module(this); Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this); Y.delegate('click', this.handle_data_action, BODY, SELECTOR.DEPENDENCY_LINK, this); this.initialise_select_multiple(); }, /** * Initialize the select multiple options * * Add actions to the buttons that enable multiple slots to be selected and managed at once. * * @method initialise_select_multiple * @protected */ initialise_select_multiple: function() { // Click select multiple button to show the select all options. Y.one(SELECTOR.SELECTMULTIPLEBUTTON).on('click', function(e) { e.preventDefault(); Y.one('body').addClass(CSS.SELECTMULTIPLE); }); // Click cancel button to show the select all options. Y.one(SELECTOR.SELECTMULTIPLECANCELBUTTON).on('click', function(e) { e.preventDefault(); Y.one('body').removeClass(CSS.SELECTMULTIPLE); }); // Assign the delete method to the delete multiple button. Y.delegate('click', this.delete_multiple_action, BODY, SELECTOR.SELECTMULTIPLEDELETEBUTTON, this); }, /** * Handles the delegation event. When this is fired someone has triggered an action. * * Note not all actions will result in an AJAX enhancement. * * @protected * @method handle_data_action * @param {EventFacade} ev The event that was triggered. * @returns {boolean} */ handle_data_action: function(ev) { // We need to get the anchor element that triggered this event. var node = ev.target; if (!node.test('a')) { node = node.ancestor(SELECTOR.ACTIVITYACTION); } // From the anchor we can get both the activity (added during initialisation) and the action being // performed (added by the UI as a data attribute). var action = node.getData('action'), activity = node.ancestor(SELECTOR.ACTIVITYLI); if (!node.test('a') || !action || !activity) { // It wasn't a valid action node. return; } // Switch based upon the action and do the desired thing. switch (action) { case 'editmaxmark': // The user wishes to edit the maxmark of the resource. this.edit_maxmark(ev, node, activity, action); break; case 'delete': // The user is deleting the activity. this.delete_with_confirmation(ev, node, activity, action); break; case 'addpagebreak': case 'removepagebreak': // The user is adding or removing a page break. this.update_page_break(ev, node, activity, action); break; case 'adddependency': case 'removedependency': // The user is adding or removing a dependency between questions. this.update_dependency(ev, node, activity, action); break; default: // Nothing to do here! break; } }, /** * Add a loading icon to the specified activity. * * The icon is added within the action area. * * @method add_spinner * @param {Node} activity The activity to add a loading icon to * @return {Node|null} The newly created icon, or null if the action area was not found. */ add_spinner: function(activity) { var actionarea = activity.one(SELECTOR.ACTIONAREA); if (actionarea) { return M.util.add_spinner(Y, actionarea); } return null; }, /** * Deletes the given activity or resource after confirmation. * * @protected * @method delete_with_confirmation * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. */ delete_with_confirmation: function(ev, button, activity) { // Prevent the default button action. ev.preventDefault(); // Get the element we're working on. var element = activity; // Create confirm string (different if element has or does not have name) var qtypename = M.util.get_string( 'pluginname', 'qtype_' + element.getAttribute('class').match(/qtype_([^\s]*)/)[1] ); // Create the confirmation dialogue. require(['core/notification'], function(Notification) { Notification.saveCancelPromise( M.util.get_string('confirm', 'moodle'), M.util.get_string('confirmremovequestion', 'quiz', qtypename), M.util.get_string('yes', 'moodle') ).then(function() { var spinner = this.add_spinner(element); var data = { 'class': 'resource', 'action': 'DELETE', 'id': Y.Moodle.mod_quiz.util.slot.getId(element) }; this.send_request(data, spinner, function(response) { if (response.deleted) { // Actually remove the element. Y.Moodle.mod_quiz.util.slot.remove(element); this.reorganise_edit_page(); if (M.core.actionmenu && M.core.actionmenu.instance) { M.core.actionmenu.instance.hideMenu(ev); } } }); return; }.bind(this)).catch(function() { // User cancelled. }); }.bind(this)); }, /** * Finds the section that would become empty if we remove the selected slots. * * @protected * @method find_sections_that_would_become_empty * @returns {String} The name of the first section found */ find_sections_that_would_become_empty: function() { var section; var sectionnodes = Y.all(SELECTOR.SECTIONLI); if (sectionnodes.size() > 1) { sectionnodes.some(function(node) { var sectionname = node.one(SELECTOR.INSTANCESECTION).getContent(); var checked = node.all(SELECTOR.SELECTMULTIPLECHECKBOX + ':checked'); var unchecked = node.all(SELECTOR.SELECTMULTIPLECHECKBOX + ':not(:checked)'); if (!checked.isEmpty() && unchecked.isEmpty()) { section = sectionname; } return section; }); } return section; }, /** * Takes care of what needs to happen when the user clicks on the delete multiple button. * * @protected * @method delete_multiple_action * @param {EventFacade} ev The event that was fired. */ delete_multiple_action: function(ev) { var problemsection = this.find_sections_that_would_become_empty(); if (typeof problemsection !== 'undefined') { require(['core/notification'], function(Notification) { Notification.alert( M.util.get_string('cannotremoveslots', 'quiz'), M.util.get_string('cannotremoveallsectionslots', 'quiz', problemsection) ); }); } else { this.delete_multiple_with_confirmation(ev); } }, /** * Deletes the given activities or resources after confirmation. * * @protected * @method delete_multiple_with_confirmation * @param {EventFacade} ev The event that was fired. */ delete_multiple_with_confirmation: function(ev) { ev.preventDefault(); var ids = ''; var slots = []; Y.all(SELECTOR.SELECTMULTIPLECHECKBOX + ':checked').each(function(node) { var slot = Y.Moodle.mod_quiz.util.slot.getSlotFromComponent(node); ids += ids === '' ? '' : ','; ids += Y.Moodle.mod_quiz.util.slot.getId(slot); slots.push(slot); }); var element = Y.one('div.mod-quiz-edit-content'); // Do nothing if no slots are selected. if (!slots || !slots.length) { return; } require(['core/notification'], function(Notification) { Notification.saveCancelPromise( M.util.get_string('confirm', 'moodle'), M.util.get_string('areyousureremoveselected', 'quiz'), M.util.get_string('yes', 'moodle') ).then(function() { var spinner = this.add_spinner(element); var data = { 'class': 'resource', field: 'deletemultiple', ids: ids }; // Delete items on server. this.send_request(data, spinner, function(response) { // Delete locally if deleted on server. if (response.deleted) { // Actually remove the element. Y.all(SELECTOR.SELECTMULTIPLECHECKBOX + ':checked').each(function(node) { Y.Moodle.mod_quiz.util.slot.remove(node.ancestor('li.activity')); }); // Update the page numbers and sections. this.reorganise_edit_page(); // Remove the select multiple options. Y.one('body').removeClass(CSS.SELECTMULTIPLE); } }); return; }.bind(this)).catch(function() { // User cancelled. }); }.bind(this)); }, /** * Edit the maxmark for the resource * * @protected * @method edit_maxmark * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @param {String} action The action that has been requested. * @return Boolean */ edit_maxmark: function(ev, button, activity) { // Get the element we're working on var instancemaxmark = activity.one(SELECTOR.INSTANCEMAXMARK), instance = activity.one(SELECTOR.ACTIVITYINSTANCE), currentmaxmark = instancemaxmark.get('firstChild'), oldmaxmark = currentmaxmark.get('data'), maxmarktext = oldmaxmark, thisevent, anchor = instancemaxmark, // Grab the anchor so that we can swap it with the edit form. data = { 'class': 'resource', 'field': 'getmaxmark', 'id': Y.Moodle.mod_quiz.util.slot.getId(activity) }; // Prevent the default actions. ev.preventDefault(); this.send_request(data, null, function(response) { if (M.core.actionmenu && M.core.actionmenu.instance) { M.core.actionmenu.instance.hideMenu(ev); } // Try to retrieve the existing string from the server. if (response.instancemaxmark) { maxmarktext = response.instancemaxmark; } // Create the editor and submit button. var editform = Y.Node.create(''); var editinstructions = Y.Node.create('') .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle')); var editor = Y.Node.create('').setAttrs({ 'value': maxmarktext, 'autocomplete': 'off', 'aria-describedby': 'id_editinstructions', 'maxLength': '12', 'size': parseInt(this.get('config').questiondecimalpoints, 10) + 2 }); // Clear the existing content and put the editor in. editform.appendChild(editor); editform.setData('anchor', anchor); instance.insert(editinstructions, 'before'); anchor.replace(editform); // We hide various components whilst editing: activity.addClass(CSS.EDITINGMAXMARK); // Focus and select the editor text. editor.focus().select(); // Cancel the edit if we lose focus or the escape key is pressed. thisevent = editor.on('blur', this.edit_maxmark_cancel, this, activity, false); this.editmaxmarkevents.push(thisevent); thisevent = editor.on('key', this.edit_maxmark_cancel, 'esc', this, activity, true); this.editmaxmarkevents.push(thisevent); // Handle form submission. thisevent = editform.on('submit', this.edit_maxmark_submit, this, activity, oldmaxmark); this.editmaxmarkevents.push(thisevent); }); }, /** * Handles the submit event when editing the activity or resources maxmark. * * @protected * @method edit_maxmark_submit * @param {EventFacade} ev The event that triggered this. * @param {Node} activity The activity whose maxmark we are altering. * @param {String} originalmaxmark The original maxmark the activity or resource had. */ edit_maxmark_submit: function(ev, activity, originalmaxmark) { // We don't actually want to submit anything. ev.preventDefault(); var newmaxmark = Y.Lang.trim(activity.one(SELECTOR.ACTIVITYFORM + ' ' + SELECTOR.ACTIVITYMAXMARK).get('value')); var spinner = this.add_spinner(activity); this.edit_maxmark_clear(activity); activity.one(SELECTOR.INSTANCEMAXMARK).setContent(newmaxmark); if (newmaxmark !== null && newmaxmark !== "" && newmaxmark !== originalmaxmark) { var data = { 'class': 'resource', 'field': 'updatemaxmark', 'maxmark': newmaxmark, 'id': Y.Moodle.mod_quiz.util.slot.getId(activity) }; this.send_request(data, spinner, function(response) { if (response.instancemaxmark) { activity.one(SELECTOR.INSTANCEMAXMARK).setContent(response.instancemaxmark); } }); } }, /** * Handles the cancel event when editing the activity or resources maxmark. * * @protected * @method edit_maxmark_cancel * @param {EventFacade} ev The event that triggered this. * @param {Node} activity The activity whose maxmark we are altering. * @param {Boolean} preventdefault If true we should prevent the default action from occuring. */ edit_maxmark_cancel: function(ev, activity, preventdefault) { if (preventdefault) { ev.preventDefault(); } this.edit_maxmark_clear(activity); }, /** * Handles clearing the editing UI and returning things to the original state they were in. * * @protected * @method edit_maxmark_clear * @param {Node} activity The activity whose maxmark we were altering. */ edit_maxmark_clear: function(activity) { // Detach all listen events to prevent duplicate triggers new Y.EventHandle(this.editmaxmarkevents).detach(); var editform = activity.one(SELECTOR.ACTIVITYFORM), instructions = activity.one('#id_editinstructions'); if (editform) { editform.replace(editform.getData('anchor')); } if (instructions) { instructions.remove(); } // Remove the editing class again to revert the display. activity.removeClass(CSS.EDITINGMAXMARK); // Refocus the link which was clicked originally so the user can continue using keyboard nav. Y.later(100, this, function() { activity.one(SELECTOR.EDITMAXMARK).focus(); }); // TODO MDL-50768 This hack is to keep Behat happy until they release a version of // MinkSelenium2Driver that fixes // https://github.com/Behat/MinkSelenium2Driver/issues/80. if (!Y.one('input[name=maxmark')) { Y.one('body').append(''); } }, /** * Joins or separates the given slot with the page of the previous slot. Reorders the pages of * the other slots * * @protected * @method update_page_break * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @param {String} action The action, addpagebreak or removepagebreak. * @chainable */ update_page_break: function(ev, button, activity, action) { // Prevent the default button action ev.preventDefault(); var nextactivity = activity.next('li.activity.slot'); var spinner = this.add_spinner(nextactivity); var value = action === 'removepagebreak' ? 1 : 2; var data = { 'class': 'resource', 'field': 'updatepagebreak', 'id': Y.Moodle.mod_quiz.util.slot.getId(nextactivity), 'value': value }; this.send_request(data, spinner, function(response) { if (response.slots) { if (action === 'addpagebreak') { Y.Moodle.mod_quiz.util.page.add(activity); } else { var page = activity.next(Y.Moodle.mod_quiz.util.page.SELECTORS.PAGE); Y.Moodle.mod_quiz.util.page.remove(page, true); } this.reorganise_edit_page(); } }); return this; }, /** * Updates a slot to either require the question in the previous slot to * have been answered, or not, * * @protected * @method update_page_break * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @param {String} action The action, adddependency or removedependency. * @chainable */ update_dependency: function(ev, button, activity, action) { // Prevent the default button action. ev.preventDefault(); var spinner = this.add_spinner(activity); var data = { 'class': 'resource', 'field': 'updatedependency', 'id': Y.Moodle.mod_quiz.util.slot.getId(activity), 'value': action === 'adddependency' ? 1 : 0 }; this.send_request(data, spinner, function(response) { if (response.hasOwnProperty('requireprevious')) { Y.Moodle.mod_quiz.util.slot.updateDependencyIcon(activity, response.requireprevious); } }); return this; }, /** * Reorganise the UI after every edit action. * * @protected * @method reorganise_edit_page */ reorganise_edit_page: function() { Y.Moodle.mod_quiz.util.slot.reorderSlots(); Y.Moodle.mod_quiz.util.slot.reorderPageBreaks(); Y.Moodle.mod_quiz.util.page.reorderPages(); Y.Moodle.mod_quiz.util.slot.updateOneSlotSections(); Y.Moodle.mod_quiz.util.slot.updateAllDependencyIcons(); }, NAME: 'mod_quiz-resource-toolbox', ATTRS: { courseid: { 'value': 0 }, quizid: { 'value': 0 } } }); M.mod_quiz.resource_toolbox = null; M.mod_quiz.init_resource_toolbox = function(config) { M.mod_quiz.resource_toolbox = new RESOURCETOOLBOX(config); return M.mod_quiz.resource_toolbox; }; /** * Section toolbox class. * * This class is responsible for managing AJAX interactions with sections * when adding, editing, removing section headings. * * @module moodle-mod_quiz-toolboxes * @namespace M.mod_quiz.toolboxes */ /** * Section toolbox class. * * This class is responsible for managing AJAX interactions with sections * when adding, editing, removing section headings when editing a quiz. * * @class section * @constructor * @extends M.mod_quiz.toolboxes.toolbox */ var SECTIONTOOLBOX = function() { SECTIONTOOLBOX.superclass.constructor.apply(this, arguments); }; Y.extend(SECTIONTOOLBOX, TOOLBOX, { /** * An Array of events added when editing a max mark field. * These should all be detached when editing is complete. * * @property editsectionevents * @protected * @type Array * @protected */ editsectionevents: [], /** * Initialize the section toolboxes module. * * Updates all span.commands with relevant handlers and other required changes. * * @method initializer * @protected */ initializer: function() { M.mod_quiz.quizbase.register_module(this); BODY.delegate('key', this.handle_data_action, 'down:enter', SELECTOR.ACTIVITYACTION, this); Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this); Y.delegate('change', this.handle_data_action, BODY, SELECTOR.EDITSHUFFLEQUESTIONSACTION, this); }, /** * Handles the delegation event. When this is fired someone has triggered an action. * * Note not all actions will result in an AJAX enhancement. * * @protected * @method handle_data_action * @param {EventFacade} ev The event that was triggered. * @returns {boolean} */ handle_data_action: function(ev) { // We need to get the anchor element that triggered this event. var node = ev.target; if (!node.test('a') && !node.test('input[data-action]')) { node = node.ancestor(SELECTOR.ACTIVITYACTION); } // From the anchor we can get both the activity (added during initialisation) and the action being // performed (added by the UI as a data attribute). var action = node.getData('action'), activity = node.ancestor(SELECTOR.ACTIVITYLI); if ((!node.test('a') && !node.test('input[data-action]')) || !action || !activity) { // It wasn't a valid action node. return; } // Switch based upon the action and do the desired thing. switch (action) { case 'edit_section_title': // The user wishes to edit the section headings. this.edit_section_title(ev, node, activity, action); break; case 'shuffle_questions': // The user wishes to edit the shuffle questions of the section (resource). this.edit_shuffle_questions(ev, node, activity, action); break; case 'deletesection': // The user is deleting the activity. this.delete_section_with_confirmation(ev, node, activity, action); break; default: // Nothing to do here! break; } }, /** * Deletes the given section heading after confirmation. * * @protected * @method delete_section_with_confirmation * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @chainable */ delete_section_with_confirmation: function(ev, button, activity) { ev.preventDefault(); require(['core/notification'], function(Notification) { Notification.saveCancelPromise( M.util.get_string('confirm', 'moodle'), M.util.get_string('confirmremovesectionheading', 'quiz', activity.getData('sectionname')), M.util.get_string('yes', 'moodle') ).then(function() { var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.ACTIONAREA)); var data = { 'class': 'section', 'action': 'DELETE', 'id': activity.get('id').replace('section-', '') }; this.send_request(data, spinner, function(response) { if (response.deleted) { window.location.reload(true); } }); return; }.bind(this)).catch(function() { // User cancelled. }); }.bind(this)); }, /** * Edit the edit section title for the section * * @protected * @method edit_section_title * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @param {String} action The action that has been requested. * @return Boolean */ edit_section_title: function(ev, button, activity) { // Get the element we're working on var activityid = activity.get('id').replace('section-', ''), instancesection = activity.one(SELECTOR.INSTANCESECTION), thisevent, anchor = instancesection, // Grab the anchor so that we can swap it with the edit form. data = { 'class': 'section', 'field': 'getsectiontitle', 'id': activityid }; // Prevent the default actions. ev.preventDefault(); this.send_request(data, null, function(response) { // Try to retrieve the existing string from the server. var oldtext = response.instancesection; // Create the editor and submit button. var editform = Y.Node.create(''); var editinstructions = Y.Node.create('') .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle')); var editor = Y.Node.create('').setAttrs({ 'value': oldtext, 'autocomplete': 'off', 'aria-describedby': 'id_editinstructions', 'maxLength': '255' // This is the maxlength in DB. }); // Clear the existing content and put the editor in. editform.appendChild(editor); editform.setData('anchor', anchor); instancesection.insert(editinstructions, 'before'); anchor.replace(editform); // Focus and select the editor text. editor.focus().select(); // Cancel the edit if we lose focus or the escape key is pressed. thisevent = editor.on('blur', this.edit_section_title_cancel, this, activity, false); this.editsectionevents.push(thisevent); thisevent = editor.on('key', this.edit_section_title_cancel, 'esc', this, activity, true); this.editsectionevents.push(thisevent); // Handle form submission. thisevent = editform.on('submit', this.edit_section_title_submit, this, activity, oldtext); this.editsectionevents.push(thisevent); }); }, /** * Handles the submit event when editing section heading. * * @protected * @method edit_section_title_submiy * @param {EventFacade} ev The event that triggered this. * @param {Node} activity The activity whose maxmark we are altering. * @param {String} oldtext The original maxmark the activity or resource had. */ edit_section_title_submit: function(ev, activity, oldtext) { // We don't actually want to submit anything. ev.preventDefault(); var newtext = Y.Lang.trim(activity.one(SELECTOR.SECTIONFORM + ' ' + SELECTOR.SECTIONINPUT).get('value')); var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.INSTANCESECTIONAREA)); this.edit_section_title_clear(activity); if (newtext !== null && newtext !== oldtext) { var instancesection = activity.one(SELECTOR.INSTANCESECTION); var instancesectiontext = newtext; if (newtext.trim() === '') { // Add a visually-hidden default section heading text to make sure we don't end up with an empty section heading. instancesectiontext = M.util.get_string('sectionnoname', 'quiz'); instancesection.addClass('visually-hidden'); } else { // Show the section heading when a non-empty value is set. instancesection.removeClass('visually-hidden'); } instancesection.setContent(instancesectiontext); var data = { 'class': 'section', 'field': 'updatesectiontitle', 'newheading': newtext, 'id': activity.get('id').replace('section-', '') }; this.send_request(data, spinner, function(response) { if (response) { // Set the content of the section heading if for some reason the response is different from the new text. // e.g. filters were applied, the update failed, etc. if (newtext !== response.instancesection) { if (response.instancesection.trim() === '') { // Add a visually-hidden default section heading text. instancesectiontext = M.util.get_string('sectionnoname', 'quiz'); instancesection.addClass('visually-hidden'); } else { instancesectiontext = response.instancesection; // Show the section heading when a non-empty value is set. instancesection.removeClass('visually-hidden'); } instancesection.setContent(instancesectiontext); } activity.one(SELECTOR.EDITSECTIONICON).set('title', M.util.get_string('sectionheadingedit', 'quiz', response.instancesection)); activity.one(SELECTOR.EDITSECTIONICON).set('alt', M.util.get_string('sectionheadingedit', 'quiz', response.instancesection)); var deleteicon = activity.one(SELECTOR.DELETESECTIONICON); if (deleteicon) { deleteicon.set('title', M.util.get_string('sectionheadingremove', 'quiz', response.instancesection)); deleteicon.set('alt', M.util.get_string('sectionheadingremove', 'quiz', response.instancesection)); } } }); } }, /** * Handles the cancel event when editing the activity or resources maxmark. * * @protected * @method edit_maxmark_cancel * @param {EventFacade} ev The event that triggered this. * @param {Node} activity The activity whose maxmark we are altering. * @param {Boolean} preventdefault If true we should prevent the default action from occuring. */ edit_section_title_cancel: function(ev, activity, preventdefault) { if (preventdefault) { ev.preventDefault(); } this.edit_section_title_clear(activity); }, /** * Handles clearing the editing UI and returning things to the original state they were in. * * @protected * @method edit_maxmark_clear * @param {Node} activity The activity whose maxmark we were altering. */ edit_section_title_clear: function(activity) { // Detach all listen events to prevent duplicate triggers new Y.EventHandle(this.editsectionevents).detach(); var editform = activity.one(SELECTOR.SECTIONFORM), instructions = activity.one('#id_editinstructions'); if (editform) { editform.replace(editform.getData('anchor')); } if (instructions) { instructions.remove(); } // Refocus the link which was clicked originally so the user can continue using keyboard nav. Y.later(100, this, function() { activity.one(SELECTOR.EDITSECTION).focus(); }); // This hack is to keep Behat happy until they release a version of // MinkSelenium2Driver that fixes // https://github.com/Behat/MinkSelenium2Driver/issues/80. if (!Y.one('input[name=section]')) { Y.one('body').append(''); } }, /** * Edit the edit shuffle questions for the section * * @protected * @method edit_shuffle_questions * @param {EventFacade} ev The event that was fired. * @param {Node} button The button that triggered this action. * @param {Node} activity The activity node that this action will be performed on. * @return Boolean */ edit_shuffle_questions: function(ev, button, activity) { var newvalue; if (activity.one(SELECTOR.EDITSHUFFLEQUESTIONSACTION).get('checked')) { newvalue = 1; activity.addClass('shuffled'); } else { newvalue = 0; activity.removeClass('shuffled'); } // Prevent the default actions. ev.preventDefault(); // Get the element we're working on var data = { 'class': 'section', 'field': 'updateshufflequestions', 'id': activity.get('id').replace('section-', ''), 'newshuffle': newvalue }; // Send request. var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.EDITSHUFFLEAREA)); this.send_request(data, spinner); } }, { NAME: 'mod_quiz-section-toolbox', ATTRS: { courseid: { 'value': 0 }, quizid: { 'value': 0 } } }); M.mod_quiz.init_section_toolbox = function(config) { return new SECTIONTOOLBOX(config); }; }, '@VERSION@', { "requires": [ "base", "node", "event", "event-key", "io", "moodle-mod_quiz-quizbase", "moodle-mod_quiz-util-slot", "moodle-core-notification-ajaxexception" ] }); moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes-min.js000064400000033442152267121500020132 0ustar00YUI.add("moodle-mod_quiz-toolboxes",function(u,a){var e,t,i,n="activityinstance",r="editinstructions",m="editor_displayed",o="select-multiple",c="editing_show",h="titleeditor",_={ACTIONAREA:".actions",ACTIONLINKTEXT:".actionlinktext",ACTIVITYACTION:"a.cm-edit-action[data-action], a.editing_maxmark, a.editing_section, input.shuffle_questions",ACTIVITYFORM:"span.instancemaxmarkcontainer form",ACTIVITYINSTANCE:"."+n,SECTIONINSTANCE:".sectioninstance",ACTIVITYLI:"li.activity, li.section",ACTIVITYMAXMARK:"input[name=maxmark]",COMMANDSPAN:".commands",CONTENTAFTERLINK:"div.contentafterlink",CONTENTWITHOUTLINK:"div.contentwithoutlink",DELETESECTIONICON:"a.editing_delete .icon",EDITMAXMARK:"a.editing_maxmark",EDITSECTION:"a.editing_section",EDITSECTIONICON:"a.editing_section .icon",EDITSHUFFLEQUESTIONSACTION:"input.cm-edit-action[data-action]",EDITSHUFFLEAREA:".instanceshufflequestions .shuffle-progress",HIDE:"a.editing_hide",HIGHLIGHT:"a.editing_highlight",INSTANCENAME:"span.instancename",INSTANCEMAXMARK:"span.instancemaxmark",INSTANCESECTION:"span.instancesection",INSTANCESECTIONAREA:"div.section-heading",MODINDENTDIV:".mod-indent",MODINDENTOUTER:".mod-indent-outer",NUMQUESTIONS:".numberofquestions",PAGECONTENT:"div#page-content",PAGELI:"li.page",SECTIONLI:"li.section",SECTIONUL:"ul.section",SECTIONFORM:".instancesectioncontainer form",SECTIONINPUT:"input[name=section]",SELECTMULTIPLEBUTTON:"#selectmultiplecommand",SELECTMULTIPLECANCELBUTTON:"#selectmultiplecancelcommand",SELECTMULTIPLECHECKBOX:".select-multiple-checkbox",SELECTMULTIPLEDELETEBUTTON:"#selectmultipledeletecommand",SELECTALL:"#questionselectall",SHOW:"a."+c,SLOTLI:"li.slot",SUMMARKS:".mod_quiz_summarks"},s=u.one(document.body);M.mod_quiz=M.mod_quiz||{},u.extend(e=function(){e.superclass.constructor.apply(this,arguments)},u.Base,{send_request:function(e,n,o,t){var i,s,a,c,d;for(s in e=e||{},i=this.get("config").pageparams)e[s]=i[s];if(e.sesskey=M.cfg.sesskey,e.courseid=this.get("courseid"),e.quizid=this.get("quizid"),a=M.cfg.wwwroot+this.get("ajaxurl"),c=[],d={method:"POST",data:e,on:{success:function(e,t){try{(c=u.JSON.parse(t.responseText)).error&&new M.core.ajaxException(c)}catch(i){}c.hasOwnProperty("newsummarks")&&u.one(_.SUMMARKS).setHTML(c.newsummarks),c.hasOwnProperty("newnumquestions")&&u.one(_.NUMQUESTIONS).setHTML(M.util.get_string("numquestionsx","quiz",c.newnumquestions)),o&&u.bind(o,this,c)(),n&&window.setTimeout(function(){n.hide()},400)},failure:function(e,t){n&&n.hide(),new M.core.ajaxException(t)}},context:this},t)for(s in t)d[s]=t[s];return n&&n.show(),u.io(a,d),this}},{NAME:"mod_quiz-toolbox",ATTRS:{courseid:{value:0},quizid:{value:0},ajaxurl:{value:null},config:{value:{}}}}),u.extend(t=function(){t.superclass.constructor.apply(this,arguments)},e,{editmaxmarkevents:[],NODE_PAGE:1,NODE_SLOT:2,NODE_JOIN:3,initializer:function(){M.mod_quiz.quizbase.register_module(this),u.delegate("click",this.handle_data_action,s,_.ACTIVITYACTION,this),u.delegate("click",this.handle_data_action,s,_.DEPENDENCY_LINK,this),this.initialise_select_multiple()},initialise_select_multiple:function(){u.one(_.SELECTMULTIPLEBUTTON).on("click",function(e){e.preventDefault(),u.one("body").addClass(o)}),u.one(_.SELECTMULTIPLECANCELBUTTON).on("click",function(e){e.preventDefault(),u.one("body").removeClass(o)}),u.delegate("click",this.delete_multiple_action,s,_.SELECTMULTIPLEDELETEBUTTON,this)},handle_data_action:function(e){var t=e.target,i=(t=t.test("a")?t:t.ancestor(_.ACTIVITYACTION)).getData("action"),n=t.ancestor(_.ACTIVITYLI);if(t.test("a")&&i&&n)switch(i){case"editmaxmark":this.edit_maxmark(e,t,n,i);break;case"delete":this.delete_with_confirmation(e,t,n,i);break;case"addpagebreak":case"removepagebreak":this.update_page_break(e,t,n,i);break;case"adddependency":case"removedependency":this.update_dependency(e,t,n,i)}},add_spinner:function(e){e=e.one(_.ACTIONAREA);return e?M.util.add_spinner(u,e):null},delete_with_confirmation:function(i,e,t){var n,o;i.preventDefault(),n=t,o=M.util.get_string("pluginname","qtype_"+n.getAttribute("class").match(/qtype_([^\s]*)/)[1]),require(["core/notification"],function(e){e.saveCancelPromise(M.util.get_string("confirm","moodle"),M.util.get_string("confirmremovequestion","quiz",o),M.util.get_string("yes","moodle")).then(function(){var e=this.add_spinner(n),t={"class":"resource",action:"DELETE",id:u.Moodle.mod_quiz.util.slot.getId(n)};this.send_request(t,e,function(e){e.deleted&&(u.Moodle.mod_quiz.util.slot.remove(n),this.reorganise_edit_page(),M.core.actionmenu&&M.core.actionmenu.instance&&M.core.actionmenu.instance.hideMenu(i))})}.bind(this))["catch"](function(){})}.bind(this))},find_sections_that_would_become_empty:function(){var n,e=u.all(_.SECTIONLI);return 1'),t=u.Node.create('').set("innerHTML",M.util.get_string("edittitleinstructions","moodle")),i=u.Node.create('').setAttrs({value:d,autocomplete:"off","aria-describedby":"id_editinstructions",maxLength:"12",size:parseInt(this.get("config").questiondecimalpoints,10)+2}),e.appendChild(i),e.setData("anchor",l),a.insert(t,"before"),l.replace(e),o.addClass(m),i.focus().select(),s=i.on("blur",this.edit_maxmark_cancel,this,o,!1),this.editmaxmarkevents.push(s),s=i.on("key",this.edit_maxmark_cancel,"esc",this,o,!0),this.editmaxmarkevents.push(s),s=e.on("submit",this.edit_maxmark_submit,this,o,c),this.editmaxmarkevents.push(s)})},edit_maxmark_submit:function(e,t,i){var n;e.preventDefault(),e=u.Lang.trim(t.one(_.ACTIVITYFORM+" "+_.ACTIVITYMAXMARK).get("value")),n=this.add_spinner(t),this.edit_maxmark_clear(t),t.one(_.INSTANCEMAXMARK).setContent(e),null!==e&&""!==e&&e!==i&&(i={"class":"resource",field:"updatemaxmark",maxmark:e,id:u.Moodle.mod_quiz.util.slot.getId(t)},this.send_request(i,n,function(e){e.instancemaxmark&&t.one(_.INSTANCEMAXMARK).setContent(e.instancemaxmark)}))},edit_maxmark_cancel:function(e,t,i){i&&e.preventDefault(),this.edit_maxmark_clear(t)},edit_maxmark_clear:function(e){new u.EventHandle(this.editmaxmarkevents).detach();var t=e.one(_.ACTIVITYFORM),i=e.one("#id_editinstructions");t&&t.replace(t.getData("anchor")),i&&i.remove(),e.removeClass(m),u.later(100,this,function(){e.one(_.EDITMAXMARK).focus()}),u.one("input[name=maxmark")||u.one("body").append('')},update_page_break:function(e,t,i,n){var o,s;return e.preventDefault(),e=i.next("li.activity.slot"),o=this.add_spinner(e),s="removepagebreak"===n?1:2,e={"class":"resource",field:"updatepagebreak",id:u.Moodle.mod_quiz.util.slot.getId(e),value:s},this.send_request(e,o,function(e){e.slots&&("addpagebreak"===n?u.Moodle.mod_quiz.util.page.add(i):(e=i.next(u.Moodle.mod_quiz.util.page.SELECTORS.PAGE),u.Moodle.mod_quiz.util.page.remove(e,!0)),this.reorganise_edit_page())}),this},update_dependency:function(e,t,i,n){return e.preventDefault(),e=this.add_spinner(i),n={"class":"resource",field:"updatedependency",id:u.Moodle.mod_quiz.util.slot.getId(i),value:"adddependency"===n?1:0},this.send_request(n,e,function(e){e.hasOwnProperty("requireprevious")&&u.Moodle.mod_quiz.util.slot.updateDependencyIcon(i,e.requireprevious)}),this},reorganise_edit_page:function(){u.Moodle.mod_quiz.util.slot.reorderSlots(),u.Moodle.mod_quiz.util.slot.reorderPageBreaks(),u.Moodle.mod_quiz.util.page.reorderPages(),u.Moodle.mod_quiz.util.slot.updateOneSlotSections(),u.Moodle.mod_quiz.util.slot.updateAllDependencyIcons()},NAME:"mod_quiz-resource-toolbox",ATTRS:{courseid:{value:0},quizid:{value:0}}}),M.mod_quiz.resource_toolbox=null,M.mod_quiz.init_resource_toolbox=function(e){return M.mod_quiz.resource_toolbox=new t(e),M.mod_quiz.resource_toolbox},u.extend(i=function(){i.superclass.constructor.apply(this,arguments)},e,{editsectionevents:[],initializer:function(){M.mod_quiz.quizbase.register_module(this),s.delegate("key",this.handle_data_action,"down:enter",_.ACTIVITYACTION,this),u.delegate("click",this.handle_data_action,s,_.ACTIVITYACTION,this),u.delegate("change",this.handle_data_action,s,_.EDITSHUFFLEQUESTIONSACTION,this)},handle_data_action:function(e){var t=e.target,i=(t=t.test("a")||t.test("input[data-action]")?t:t.ancestor(_.ACTIVITYACTION)).getData("action"),n=t.ancestor(_.ACTIVITYLI);if((t.test("a")||t.test("input[data-action]"))&&i&&n)switch(i){case"edit_section_title":this.edit_section_title(e,t,n,i);break;case"shuffle_questions":this.edit_shuffle_questions(e,t,n,i);break;case"deletesection":this.delete_section_with_confirmation(e,t,n,i)}},delete_section_with_confirmation:function(e,t,i){e.preventDefault(),require(["core/notification"],function(e){e.saveCancelPromise(M.util.get_string("confirm","moodle"),M.util.get_string("confirmremovesectionheading","quiz",i.getData("sectionname")),M.util.get_string("yes","moodle")).then(function(){var e=M.util.add_spinner(u,i.one(_.ACTIONAREA)),t={"class":"section",action:"DELETE",id:i.get("id").replace("section-","")};this.send_request(t,e,function(e){e.deleted&&window.location.reload(!0)})}.bind(this))["catch"](function(){})}.bind(this))},edit_section_title:function(e,t,o){var s,i=o.get("id").replace("section-",""),a=o.one(_.INSTANCESECTION),c=a,i={"class":"section",field:"getsectiontitle",id:i};e.preventDefault(),this.send_request(i,null,function(e){var e=e.instancesection,t=u.Node.create(''),i=u.Node.create('').set("innerHTML",M.util.get_string("edittitleinstructions","moodle")),n=u.Node.create('').setAttrs({value:e,autocomplete:"off","aria-describedby":"id_editinstructions",maxLength:"255"});t.appendChild(n),t.setData("anchor",c),a.insert(i,"before"),c.replace(t),n.focus().select(),s=n.on("blur",this.edit_section_title_cancel,this,o,!1),this.editsectionevents.push(s),s=n.on("key",this.edit_section_title_cancel,"esc",this,o,!0),this.editsectionevents.push(s),s=t.on("submit",this.edit_section_title_submit,this,o,e),this.editsectionevents.push(s)})},edit_section_title_submit:function(e,i,t){var n,o,s;e.preventDefault(),n=u.Lang.trim(i.one(_.SECTIONFORM+" "+_.SECTIONINPUT).get("value")),e=M.util.add_spinner(u, i.one(_.INSTANCESECTIONAREA)),this.edit_section_title_clear(i),null!==n&&n!==t&&(o=i.one(_.INSTANCESECTION),""===(s=n).trim()?(s=M.util.get_string("sectionnoname","quiz"),o.addClass("visually-hidden")):o.removeClass("visually-hidden"),o.setContent(s),t={"class":"section",field:"updatesectiontitle",newheading:n,id:i.get("id").replace("section-","")},this.send_request(t,e,function(e){var t;e&&(n!==e.instancesection&&(""===e.instancesection.trim()?(s=M.util.get_string("sectionnoname","quiz"),o.addClass("visually-hidden")):(s=e.instancesection,o.removeClass("visually-hidden")),o.setContent(s)),i.one(_.EDITSECTIONICON).set("title",M.util.get_string("sectionheadingedit","quiz",e.instancesection)),i.one(_.EDITSECTIONICON).set("alt",M.util.get_string("sectionheadingedit","quiz",e.instancesection)),(t=i.one(_.DELETESECTIONICON))&&(t.set("title",M.util.get_string("sectionheadingremove","quiz",e.instancesection)),t.set("alt",M.util.get_string("sectionheadingremove","quiz",e.instancesection))))}))},edit_section_title_cancel:function(e,t,i){i&&e.preventDefault(),this.edit_section_title_clear(t)},edit_section_title_clear:function(e){new u.EventHandle(this.editsectionevents).detach();var t=e.one(_.SECTIONFORM),i=e.one("#id_editinstructions");t&&t.replace(t.getData("anchor")),i&&i.remove(),u.later(100,this,function(){e.one(_.EDITSECTION).focus()}),u.one("input[name=section]")||u.one("body").append('')},edit_shuffle_questions:function(e,t,i){var n;i.one(_.EDITSHUFFLEQUESTIONSACTION).get("checked")?(n=1,i.addClass("shuffled")):(n=0,i.removeClass("shuffled")),e.preventDefault(),e={"class":"section",field:"updateshufflequestions",id:i.get("id").replace("section-",""),newshuffle:n},n=M.util.add_spinner(u,i.one(_.EDITSHUFFLEAREA)),this.send_request(e,n)}},{NAME:"mod_quiz-section-toolbox",ATTRS:{courseid:{value:0},quizid:{value:0}}}),M.mod_quiz.init_section_toolbox=function(e){return new i(e)}},"@VERSION@",{requires:["base","node","event","event-key","io","moodle-mod_quiz-quizbase","moodle-mod_quiz-util-slot","moodle-core-notification-ajaxexception"]});moodle-mod_quiz-questionchooser/moodle-mod_quiz-questionchooser-min.js000064400000002515152267121500022557 0ustar00YUI.add("moodle-mod_quiz-questionchooser",function(t,e){var o='.menu [data-action="addquestion"]',n="div.createnewquestion",r="div.chooserdialoguebody",s="div.choosertitle",i=function(){i.superclass.constructor.apply(this,arguments)};t.extend(i,M.core.chooserdialogue,{initializer:function(){t.one("body").delegate("click",this.display_dialogue,o,this)},display_dialogue:function(e){var o,i;e.preventDefault(),o=t.one(n+" "+r),i=t.one(n+" "+s),null===this.container&&(this.setup_chooser_dialogue(o,i,{}),this.prepare_chooser()),o=t.QueryString.parse(e.currentTarget.get("search").substring(1)),i=this.container.one("form"),this.parameters_to_hidden_input(o,i,"returnurl"),this.parameters_to_hidden_input(o,i,"cmid"),this.parameters_to_hidden_input(o,i,"category"),this.parameters_to_hidden_input(o,i,"addonpage"),this.parameters_to_hidden_input(o,i,"appendqnumstring"),this.display_chooser(e)},parameters_to_hidden_input:function(e,o,i){var e=e.hasOwnProperty(i)?e[i]:"",t=o.one("input[name="+i+"]");t||(t=o.appendChild('')).set("name",i),t.set("value",e)}},{NAME:"mod_quiz-questionchooser"}),M.mod_quiz=M.mod_quiz||{},M.mod_quiz.init_questionchooser=function(){return M.mod_quiz.question_chooser=new i({}),M.mod_quiz.question_chooser}},"@VERSION@",{requires:["moodle-core-chooserdialogue","moodle-mod_quiz-util","querystring-parse"]});moodle-mod_quiz-questionchooser/moodle-mod_quiz-questionchooser-debug.js000064400000005247152267121500023067 0ustar00YUI.add('moodle-mod_quiz-questionchooser', function (Y, NAME) { var CSS = { ADDNEWQUESTIONBUTTONS: '.menu [data-action="addquestion"]', CREATENEWQUESTION: 'div.createnewquestion', CHOOSERDIALOGUE: 'div.chooserdialoguebody', CHOOSERHEADER: 'div.choosertitle' }; /** * The questionchooser class is responsible for instantiating and displaying the question chooser * when viewing a quiz in editing mode. * * @class questionchooser * @constructor * @protected * @extends M.core.chooserdialogue */ var QUESTIONCHOOSER = function() { QUESTIONCHOOSER.superclass.constructor.apply(this, arguments); }; Y.extend(QUESTIONCHOOSER, M.core.chooserdialogue, { initializer: function() { Y.one('body').delegate('click', this.display_dialogue, CSS.ADDNEWQUESTIONBUTTONS, this); }, display_dialogue: function(e) { e.preventDefault(); var dialogue = Y.one(CSS.CREATENEWQUESTION + ' ' + CSS.CHOOSERDIALOGUE), header = Y.one(CSS.CREATENEWQUESTION + ' ' + CSS.CHOOSERHEADER); if (this.container === null) { // Setup the dialogue, and then prepare the chooser if it's not already been set up. this.setup_chooser_dialogue(dialogue, header, {}); this.prepare_chooser(); } // Update all of the hidden fields within the questionbank form. var parameters = Y.QueryString.parse(e.currentTarget.get('search').substring(1)); var form = this.container.one('form'); this.parameters_to_hidden_input(parameters, form, 'returnurl'); this.parameters_to_hidden_input(parameters, form, 'cmid'); this.parameters_to_hidden_input(parameters, form, 'category'); this.parameters_to_hidden_input(parameters, form, 'addonpage'); this.parameters_to_hidden_input(parameters, form, 'appendqnumstring'); // Display the chooser dialogue. this.display_chooser(e); }, parameters_to_hidden_input: function(parameters, form, name) { var value; if (parameters.hasOwnProperty(name)) { value = parameters[name]; } else { value = ''; } var input = form.one('input[name=' + name + ']'); if (!input) { input = form.appendChild(''); input.set('name', name); } input.set('value', value); } }, { NAME: 'mod_quiz-questionchooser' }); M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.init_questionchooser = function() { M.mod_quiz.question_chooser = new QUESTIONCHOOSER({}); return M.mod_quiz.question_chooser; }; }, '@VERSION@', {"requires": ["moodle-core-chooserdialogue", "moodle-mod_quiz-util", "querystring-parse"]}); moodle-mod_quiz-questionchooser/moodle-mod_quiz-questionchooser.js000064400000005247152267121500022003 0ustar00YUI.add('moodle-mod_quiz-questionchooser', function (Y, NAME) { var CSS = { ADDNEWQUESTIONBUTTONS: '.menu [data-action="addquestion"]', CREATENEWQUESTION: 'div.createnewquestion', CHOOSERDIALOGUE: 'div.chooserdialoguebody', CHOOSERHEADER: 'div.choosertitle' }; /** * The questionchooser class is responsible for instantiating and displaying the question chooser * when viewing a quiz in editing mode. * * @class questionchooser * @constructor * @protected * @extends M.core.chooserdialogue */ var QUESTIONCHOOSER = function() { QUESTIONCHOOSER.superclass.constructor.apply(this, arguments); }; Y.extend(QUESTIONCHOOSER, M.core.chooserdialogue, { initializer: function() { Y.one('body').delegate('click', this.display_dialogue, CSS.ADDNEWQUESTIONBUTTONS, this); }, display_dialogue: function(e) { e.preventDefault(); var dialogue = Y.one(CSS.CREATENEWQUESTION + ' ' + CSS.CHOOSERDIALOGUE), header = Y.one(CSS.CREATENEWQUESTION + ' ' + CSS.CHOOSERHEADER); if (this.container === null) { // Setup the dialogue, and then prepare the chooser if it's not already been set up. this.setup_chooser_dialogue(dialogue, header, {}); this.prepare_chooser(); } // Update all of the hidden fields within the questionbank form. var parameters = Y.QueryString.parse(e.currentTarget.get('search').substring(1)); var form = this.container.one('form'); this.parameters_to_hidden_input(parameters, form, 'returnurl'); this.parameters_to_hidden_input(parameters, form, 'cmid'); this.parameters_to_hidden_input(parameters, form, 'category'); this.parameters_to_hidden_input(parameters, form, 'addonpage'); this.parameters_to_hidden_input(parameters, form, 'appendqnumstring'); // Display the chooser dialogue. this.display_chooser(e); }, parameters_to_hidden_input: function(parameters, form, name) { var value; if (parameters.hasOwnProperty(name)) { value = parameters[name]; } else { value = ''; } var input = form.one('input[name=' + name + ']'); if (!input) { input = form.appendChild(''); input.set('name', name); } input.set('value', value); } }, { NAME: 'mod_quiz-questionchooser' }); M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.init_questionchooser = function() { M.mod_quiz.question_chooser = new QUESTIONCHOOSER({}); return M.mod_quiz.question_chooser; }; }, '@VERSION@', {"requires": ["moodle-core-chooserdialogue", "moodle-mod_quiz-util", "querystring-parse"]}); moodle-mod_quiz-quizbase/moodle-mod_quiz-quizbase-debug.js000064400000010567152267121500020052 0ustar00YUI.add('moodle-mod_quiz-quizbase', function (Y, NAME) { /** * The quizbase class to provide shared functionality to Modules within Moodle. * * @module moodle-mod_quiz-quizbase */ var QUIZBASENAME = 'mod_quiz-quizbase'; var QUIZBASE = function() { QUIZBASE.superclass.constructor.apply(this, arguments); }; /** * The coursebase class to provide shared functionality to Modules within * Moodle. * * @class M.course.coursebase * @constructor */ Y.extend(QUIZBASE, Y.Base, { // Registered Modules registermodules: [], /** * Register a new Javascript Module * * @method register_module * @param {Object} The instantiated module to call functions on * @chainable */ register_module: function(object) { this.registermodules.push(object); return this; }, /** * Invoke the specified function in all registered modules with the given arguments * * @method invoke_function * @param {String} functionname The name of the function to call * @param {mixed} args The argument supplied to the function * @chainable */ invoke_function: function(functionname, args) { var module; for (module in this.registermodules) { if (functionname in this.registermodules[module]) { this.registermodules[module][functionname](args); } } return this; } }, { NAME: QUIZBASENAME, ATTRS: {} }); // Ensure that M.course exists and that coursebase is initialised correctly M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.quizbase = M.mod_quiz.quizbase || new QUIZBASE(); // Abstract functions that needs to be defined per format (course/format/somename/format.js) M.mod_quiz.edit = M.mod_quiz.edit || {}; /** * Swap section (should be defined in format.js if requred) * * @param {YUI} Y YUI3 instance * @param {string} node1 node to swap to * @param {string} node2 node to swap with * @return {NodeList} section list */ M.mod_quiz.edit.swap_sections = function(Y, node1, node2) { var CSS = { COURSECONTENT: 'mod-quiz-edit-content', SECTIONADDMENUS: 'section_add_menus' }; var sectionlist = Y.Node.all('.' + CSS.COURSECONTENT + ' li.section'); // Swap menus. sectionlist.item(node1).one('.' + CSS.SECTIONADDMENUS).swap(sectionlist.item(node2).one('.' + CSS.SECTIONADDMENUS)); }; /** * Process sections after ajax response (should be defined in format.js) * If some response is expected, we pass it over to format, as it knows better * hot to process it. * * @param {YUI} Y YUI3 instance * @param {NodeList} list of sections * @param {array} response ajax response * @param {string} sectionfrom first affected section * @param {string} sectionto last affected section * @return void */ M.mod_quiz.edit.process_sections = function(Y, sectionlist, response, sectionfrom, sectionto) { var CSS = { SECTIONNAME: 'sectionname' }, SELECTORS = { SECTIONLEFTSIDE: '.left .section-handle .icon' }; if (response.action === 'move') { // If moving up swap around 'sectionfrom' and 'sectionto' so the that loop operates. if (sectionfrom > sectionto) { var temp = sectionto; sectionto = sectionfrom; sectionfrom = temp; } // Update titles and move icons in all affected sections. var ele, str, stridx, newstr; for (var i = sectionfrom; i <= sectionto; i++) { // Update section title. sectionlist.item(i).one('.' + CSS.SECTIONNAME).setContent(response.sectiontitles[i]); // Update move icon. ele = sectionlist.item(i).one(SELECTORS.SECTIONLEFTSIDE); str = ele.getAttribute('alt'); stridx = str.lastIndexOf(' '); newstr = str.substr(0, stridx + 1) + i; ele.setAttribute('alt', newstr); ele.setAttribute('title', newstr); // For FireFox as 'alt' is not refreshed. // Remove the current class as section has been moved. sectionlist.item(i).removeClass('current'); } // If there is a current section, apply corresponding class in order to highlight it. if (response.current !== -1) { // Add current class to the required section. sectionlist.item(response.current).addClass('current'); } } }; }, '@VERSION@', {"requires": ["base", "node"]}); moodle-mod_quiz-quizbase/moodle-mod_quiz-quizbase.js000064400000010567152267121500016766 0ustar00YUI.add('moodle-mod_quiz-quizbase', function (Y, NAME) { /** * The quizbase class to provide shared functionality to Modules within Moodle. * * @module moodle-mod_quiz-quizbase */ var QUIZBASENAME = 'mod_quiz-quizbase'; var QUIZBASE = function() { QUIZBASE.superclass.constructor.apply(this, arguments); }; /** * The coursebase class to provide shared functionality to Modules within * Moodle. * * @class M.course.coursebase * @constructor */ Y.extend(QUIZBASE, Y.Base, { // Registered Modules registermodules: [], /** * Register a new Javascript Module * * @method register_module * @param {Object} The instantiated module to call functions on * @chainable */ register_module: function(object) { this.registermodules.push(object); return this; }, /** * Invoke the specified function in all registered modules with the given arguments * * @method invoke_function * @param {String} functionname The name of the function to call * @param {mixed} args The argument supplied to the function * @chainable */ invoke_function: function(functionname, args) { var module; for (module in this.registermodules) { if (functionname in this.registermodules[module]) { this.registermodules[module][functionname](args); } } return this; } }, { NAME: QUIZBASENAME, ATTRS: {} }); // Ensure that M.course exists and that coursebase is initialised correctly M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.quizbase = M.mod_quiz.quizbase || new QUIZBASE(); // Abstract functions that needs to be defined per format (course/format/somename/format.js) M.mod_quiz.edit = M.mod_quiz.edit || {}; /** * Swap section (should be defined in format.js if requred) * * @param {YUI} Y YUI3 instance * @param {string} node1 node to swap to * @param {string} node2 node to swap with * @return {NodeList} section list */ M.mod_quiz.edit.swap_sections = function(Y, node1, node2) { var CSS = { COURSECONTENT: 'mod-quiz-edit-content', SECTIONADDMENUS: 'section_add_menus' }; var sectionlist = Y.Node.all('.' + CSS.COURSECONTENT + ' li.section'); // Swap menus. sectionlist.item(node1).one('.' + CSS.SECTIONADDMENUS).swap(sectionlist.item(node2).one('.' + CSS.SECTIONADDMENUS)); }; /** * Process sections after ajax response (should be defined in format.js) * If some response is expected, we pass it over to format, as it knows better * hot to process it. * * @param {YUI} Y YUI3 instance * @param {NodeList} list of sections * @param {array} response ajax response * @param {string} sectionfrom first affected section * @param {string} sectionto last affected section * @return void */ M.mod_quiz.edit.process_sections = function(Y, sectionlist, response, sectionfrom, sectionto) { var CSS = { SECTIONNAME: 'sectionname' }, SELECTORS = { SECTIONLEFTSIDE: '.left .section-handle .icon' }; if (response.action === 'move') { // If moving up swap around 'sectionfrom' and 'sectionto' so the that loop operates. if (sectionfrom > sectionto) { var temp = sectionto; sectionto = sectionfrom; sectionfrom = temp; } // Update titles and move icons in all affected sections. var ele, str, stridx, newstr; for (var i = sectionfrom; i <= sectionto; i++) { // Update section title. sectionlist.item(i).one('.' + CSS.SECTIONNAME).setContent(response.sectiontitles[i]); // Update move icon. ele = sectionlist.item(i).one(SELECTORS.SECTIONLEFTSIDE); str = ele.getAttribute('alt'); stridx = str.lastIndexOf(' '); newstr = str.substr(0, stridx + 1) + i; ele.setAttribute('alt', newstr); ele.setAttribute('title', newstr); // For FireFox as 'alt' is not refreshed. // Remove the current class as section has been moved. sectionlist.item(i).removeClass('current'); } // If there is a current section, apply corresponding class in order to highlight it. if (response.current !== -1) { // Add current class to the required section. sectionlist.item(response.current).addClass('current'); } } }; }, '@VERSION@', {"requires": ["base", "node"]}); moodle-mod_quiz-quizbase/moodle-mod_quiz-quizbase-min.js000064400000002230152267121500017533 0ustar00YUI.add("moodle-mod_quiz-quizbase",function(e,t){var i=function(){i.superclass.constructor.apply(this,arguments)};e.extend(i,e.Base,{registermodules:[],register_module:function(e){return this.registermodules.push(e),this},invoke_function:function(e,t){for(var i in this.registermodules)e in this.registermodules[i]&&this.registermodules[i][e](t);return this}},{NAME:"mod_quiz-quizbase",ATTRS:{}}),M.mod_quiz=M.mod_quiz||{},M.mod_quiz.quizbase=M.mod_quiz.quizbase||new i,M.mod_quiz.edit=M.mod_quiz.edit||{},M.mod_quiz.edit.swap_sections=function(e,t,i){var s="mod-quiz-edit-content",o="section_add_menus",s=e.Node.all("."+s+" li.section");s.item(t).one("."+o).swap(s.item(i).one("."+o))},M.mod_quiz.edit.process_sections=function(a,e,t,i,s){var o,n,u,r,d,m="sectionname",c=".left .section-handle .icon";if("move"===t.action){for(s 0) { // Remove move icons var movedown = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEDOWN); var moveup = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEUP); // Add dragger icon var title = M.util.get_string('movesection', 'moodle', sectionid); var cssleft = sectionnode.one('.' + CSS.LEFT); if ((movedown || moveup) && cssleft) { cssleft.setStyle('cursor', 'move'); cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true)); if (moveup) { moveup.remove(); } if (movedown) { movedown.remove(); } // This section can be moved - add the class to indicate this to Y.DD. sectionnode.addClass(CSS.SECTIONDRAGGABLE); } } }, this); }, /* * Drag-dropping related functions */ drag_start: function(e) { // Get our drag object var drag = e.target; // Creat a dummy structure of the outer elemnents for clean styles application var containernode = Y.Node.create('
          '); var sectionnode = Y.Node.create('
            '); sectionnode.setStyle('margin', 0); sectionnode.setContent(drag.get('node').get('innerHTML')); containernode.appendChild(sectionnode); drag.get('dragNode').setContent(containernode); drag.get('dragNode').addClass(CSS.COURSECONTENT); }, drag_dropmiss: function(e) { // Missed the target, but we assume the user intended to drop it // on the last last ghost node location, e.drag and e.drop should be // prepared by global_drag_dropmiss parent so simulate drop_hit(e). this.drop_hit(e); }, get_section_index: function(node) { var sectionlistselector = '.' + CSS.COURSECONTENT + ' li.section', sectionList = Y.all(sectionlistselector), nodeIndex = sectionList.indexOf(node), zeroIndex = sectionList.indexOf(Y.one('#section-0')); return (nodeIndex - zeroIndex); }, drop_hit: function(e) { var drag = e.drag; // Get references to our nodes and their IDs. var dragnode = drag.get('node'), dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode), loopstart = dragnodeid, dropnodeindex = this.get_section_index(dragnode), loopend = dropnodeindex; if (dragnodeid === dropnodeindex) { Y.log("Skipping move - same location moving " + dragnodeid + " to " + dropnodeindex, 'debug', 'moodle-mod_quiz-dragdrop'); return; } Y.log("Moving from position " + dragnodeid + " to position " + dropnodeindex, 'debug', 'moodle-mod_quiz-dragdrop'); if (loopstart > loopend) { // If we're going up, we need to swap the loop order // because loops can't go backwards. loopstart = dropnodeindex; loopend = dragnodeid; } // Get the list of nodes. drag.get('dragNode').removeClass(CSS.COURSECONTENT); var sectionlist = Y.Node.all(this.sectionlistselector); // Add a lightbox if it's not there. var lightbox = M.util.add_lightbox(Y, dragnode); // Handle any variables which we must pass via AJAX. var params = {}, pageparams = this.get('config').pageparams, varname; for (varname in pageparams) { if (!pageparams.hasOwnProperty(varname)) { continue; } params[varname] = pageparams[varname]; } // Prepare request parameters params.sesskey = M.cfg.sesskey; params.courseid = this.get('courseid'); params.quizid = this.get('quizid'); params['class'] = 'section'; params.field = 'move'; params.id = dragnodeid; params.value = dropnodeindex; // Perform the AJAX request. var uri = M.cfg.wwwroot + this.get('ajaxurl'); Y.io(uri, { method: 'POST', data: params, on: { start: function() { lightbox.show(); }, success: function(tid, response) { // Update section titles, we can't simply swap them as // they might have custom title try { var responsetext = Y.JSON.parse(response.responseText); if (responsetext.error) { new M.core.ajaxException(responsetext); } M.mod_quiz.edit.process_sections(Y, sectionlist, responsetext, loopstart, loopend); } catch (e) { // Ignore. } // Update all of the section IDs - first unset them, then set them // to avoid duplicates in the DOM. var index; // Classic bubble sort algorithm is applied to the section // nodes between original drag node location and the new one. var swapped = false; do { swapped = false; for (index = loopstart; index <= loopend; index++) { if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) > Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) { Y.log("Swapping " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) + " with " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index)), "debug", "moodle-mod_quiz-dragdrop"); // Swap section id. var sectionid = sectionlist.item(index - 1).get('id'); sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id')); sectionlist.item(index).set('id', sectionid); // See what format needs to swap. M.mod_quiz.edit.swap_sections(Y, index - 1, index); // Update flag. swapped = true; } } loopend = loopend - 1; } while (swapped); window.setTimeout(function() { lightbox.hide(); }, 250); }, failure: function(tid, response) { this.ajax_failure(response); lightbox.hide(); } }, context: this }); } }, { NAME: 'mod_quiz-dragdrop-section', ATTRS: { courseid: { value: null }, quizid: { value: null }, ajaxurl: { value: 0 }, config: { value: 0 } } }); M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.init_section_dragdrop = function(params) { new DRAGSECTION(params); }; /** * Resource drag and drop. * * @class M.course.dragdrop.resource * @constructor * @extends M.core.dragdrop */ var DRAGRESOURCE = function() { DRAGRESOURCE.superclass.constructor.apply(this, arguments); }; Y.extend(DRAGRESOURCE, M.core.dragdrop, { initializer: function() { // Set group for parent class this.groups = ['resource']; this.samenodeclass = CSS.ACTIVITY; this.parentnodeclass = CSS.SECTION; this.samenodelabel = { identifier: 'dragtoafter', component: 'quiz' }; this.parentnodelabel = { identifier: 'dragtostart', component: 'quiz' }; // Go through all sections this.setup_for_section(); // Initialise drag & drop for all resources/activities var nodeselector = 'li.' + CSS.ACTIVITY; var del = new Y.DD.Delegate({ container: '.' + CSS.COURSECONTENT, nodes: nodeselector, target: true, handles: ['.' + CSS.EDITINGMOVE], dragConfig: {groups: this.groups} }); del.dd.plug(Y.Plugin.DDProxy, { // Don't move the node at the end of the drag moveOnEnd: false, cloneNode: true }); del.dd.plug(Y.Plugin.DDConstrained, { // Keep it inside the .mod-quiz-edit-content constrain: '#' + CSS.SLOTS }); del.dd.plug(Y.Plugin.DDWinScroll); M.mod_quiz.quizbase.register_module(this); M.mod_quiz.dragres = this; }, /** * Apply dragdrop features to the specified selector or node that refers to section(s) * * @method setup_for_section * @param {String} baseselector The CSS selector or node to limit scope to */ setup_for_section: function() { Y.Node.all('.mod-quiz-edit-content ul.slots ul.section').each(function(resources) { resources.setAttribute('data-draggroups', this.groups.join(' ')); // Define empty ul as droptarget, so that item could be moved to empty list new Y.DD.Drop({ node: resources, groups: this.groups, padding: '20 0 20 0' }); // Initialise each resource/activity in this section this.setup_for_resource('li.activity'); }, this); }, /** * Apply dragdrop features to the specified selector or node that refers to resource(s) * * @method setup_for_resource * @param {String} baseselector The CSS selector or node to limit scope to */ setup_for_resource: function(baseselector) { Y.Node.all(baseselector).each(function(resourcesnode) { // Replace move icons var move = resourcesnode.one('a.' + CSS.EDITINGMOVE); if (move) { var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'), CSS.EDITINGMOVE, CSS.ICONCLASS, true); move.replace(resourcedraghandle); } }, this); }, drag_start: function(e) { // Get our drag object var drag = e.target; drag.get('dragNode').setContent(drag.get('node').get('innerHTML')); drag.get('dragNode').all('.icon').setStyle('vertical-align', 'baseline'); }, drag_dropmiss: function(e) { // Missed the target, but we assume the user intended to drop it // on the last ghost node location, e.drag and e.drop should be // prepared by global_drag_dropmiss parent so simulate drop_hit(e). this.drop_hit(e); }, drop_hit: function(e) { var drag = e.drag; // Get a reference to our drag node var dragnode = drag.get('node'); // Add spinner if it not there var actionarea = dragnode.one(CSS.ACTIONAREA); var spinner = M.util.add_spinner(Y, actionarea); var params = {}; // Handle any variables which we must pass back through to var pageparams = this.get('config').pageparams; var varname; for (varname in pageparams) { params[varname] = pageparams[varname]; } // Prepare request parameters params.sesskey = M.cfg.sesskey; params.courseid = this.get('courseid'); params.quizid = this.get('quizid'); params['class'] = 'resource'; params.field = 'move'; params.id = Number(Y.Moodle.mod_quiz.util.slot.getId(dragnode)); params.sectionId = Y.Moodle.core_course.util.section.getId(dragnode.ancestor('li.section', true)); var previousslot = dragnode.previous(SELECTOR.SLOT); if (previousslot) { params.previousid = Number(Y.Moodle.mod_quiz.util.slot.getId(previousslot)); } var previouspage = dragnode.previous(SELECTOR.PAGE); if (previouspage) { params.page = Number(Y.Moodle.mod_quiz.util.page.getId(previouspage)); } // Do AJAX request var uri = M.cfg.wwwroot + this.get('ajaxurl'); Y.io(uri, { method: 'POST', data: params, on: { start: function() { this.lock_drag_handle(drag, CSS.EDITINGMOVE); spinner.show(); }, success: function(tid, response) { var responsetext = Y.JSON.parse(response.responseText); var params = {element: dragnode, visible: responsetext.visible}; M.mod_quiz.quizbase.invoke_function('set_visibility_resource_ui', params); this.unlock_drag_handle(drag, CSS.EDITINGMOVE); window.setTimeout(function() { spinner.hide(); }, 250); M.mod_quiz.resource_toolbox.reorganise_edit_page(); }, failure: function(tid, response) { this.ajax_failure(response); this.unlock_drag_handle(drag, CSS.SECTIONHANDLE); spinner.hide(); window.location.reload(true); } }, context: this }); }, global_drop_over: function(e) { // Overriding parent method so we can stop the slots being dragged before the first page node. // Check that drop object belong to correct group. if (!e.drop || !e.drop.inGroup(this.groups)) { return; } // Get a reference to our drag and drop nodes. var drag = e.drag.get('node'), drop = e.drop.get('node'); // Save last drop target for the case of missed target processing. this.lastdroptarget = e.drop; // Are we dropping within the same parent node? if (drop.hasClass(this.samenodeclass)) { var where; if (this.goingup) { where = "before"; } else { where = "after"; } drop.insert(drag, where); } else if ((drop.hasClass(this.parentnodeclass) || drop.test('[data-droptarget="1"]')) && !drop.contains(drag)) { // We are dropping on parent node and it is empty if (this.goingup) { drop.append(drag); } else { drop.prepend(drag); } } this.drop_over(e); } }, { NAME: 'mod_quiz-dragdrop-resource', ATTRS: { courseid: { value: null }, quizid: { value: null }, ajaxurl: { value: 0 }, config: { value: 0 } } }); M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.init_resource_dragdrop = function(params) { new DRAGRESOURCE(params); }; }, '@VERSION@', { "requires": [ "base", "node", "io", "dom", "dd", "dd-scroll", "moodle-core-dragdrop", "moodle-core-notification", "moodle-mod_quiz-quizbase", "moodle-mod_quiz-util-base", "moodle-mod_quiz-util-page", "moodle-mod_quiz-util-slot", "moodle-course-util" ] }); moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop.js000064400000043744152267121500016727 0ustar00YUI.add('moodle-mod_quiz-dragdrop', function (Y, NAME) { /* eslint-disable no-unused-vars */ /** * Drag and Drop for Quiz sections and slots. * * @module moodle-mod-quiz-dragdrop */ var CSS = { ACTIONAREA: '.actions', ACTIVITY: 'activity', ACTIVITYINSTANCE: 'activityinstance', CONTENT: 'content', COURSECONTENT: 'mod-quiz-edit-content', EDITINGMOVE: 'editing_move', ICONCLASS: 'iconsmall', JUMPMENU: 'jumpmenu', LEFT: 'left', LIGHTBOX: 'lightbox', MOVEDOWN: 'movedown', MOVEUP: 'moveup', PAGE: 'page', PAGECONTENT: 'page-content', RIGHT: 'right', SECTION: 'slots', SECTIONADDMENUS: 'section_add_menus', SECTIONHANDLE: 'section-handle', SLOTS: 'slots', SUMMARY: 'summary', SECTIONDRAGGABLE: 'sectiondraggable' }, // The CSS selectors we use. SELECTOR = { PAGE: 'li.page', SLOT: 'li.slot' }; /** * Section drag and drop. * * @class M.mod_quiz.dragdrop.section * @constructor * @extends M.core.dragdrop */ var DRAGSECTION = function() { DRAGSECTION.superclass.constructor.apply(this, arguments); }; Y.extend(DRAGSECTION, M.core.dragdrop, { sectionlistselector: null, initializer: function() { // Set group for parent class this.groups = [CSS.SECTIONDRAGGABLE]; this.samenodeclass = 'section'; this.parentnodeclass = 'slots'; // Check if we are in single section mode if (Y.Node.one('.' + CSS.JUMPMENU)) { return false; } // Initialise sections dragging this.sectionlistselector = 'li.section'; if (this.sectionlistselector) { this.sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + this.sectionlistselector; this.setup_for_section(this.sectionlistselector); // Make each li element in the lists of sections draggable var del = new Y.DD.Delegate({ container: '.' + CSS.COURSECONTENT, nodes: '.' + CSS.SECTIONDRAGGABLE, target: true, handles: ['.' + CSS.LEFT], dragConfig: {groups: this.groups} }); del.dd.plug(Y.Plugin.DDProxy, { // Don't move the node at the end of the drag moveOnEnd: false }); del.dd.plug(Y.Plugin.DDConstrained, { // Keep it inside the .mod-quiz-edit-content constrain: '#' + CSS.PAGECONTENT, stickY: true }); del.dd.plug(Y.Plugin.DDWinScroll); } }, /** * Apply dragdrop features to the specified selector or node that refers to section(s) * * @method setup_for_section * @param {String} baseselector The CSS selector or node to limit scope to */ setup_for_section: function(baseselector) { Y.Node.all(baseselector).each(function(sectionnode) { // Determine the section ID var sectionid = Y.Moodle.core_course.util.section.getId(sectionnode); // We skip the top section as it is not draggable if (sectionid > 0) { // Remove move icons var movedown = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEDOWN); var moveup = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEUP); // Add dragger icon var title = M.util.get_string('movesection', 'moodle', sectionid); var cssleft = sectionnode.one('.' + CSS.LEFT); if ((movedown || moveup) && cssleft) { cssleft.setStyle('cursor', 'move'); cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true)); if (moveup) { moveup.remove(); } if (movedown) { movedown.remove(); } // This section can be moved - add the class to indicate this to Y.DD. sectionnode.addClass(CSS.SECTIONDRAGGABLE); } } }, this); }, /* * Drag-dropping related functions */ drag_start: function(e) { // Get our drag object var drag = e.target; // Creat a dummy structure of the outer elemnents for clean styles application var containernode = Y.Node.create('
              '); var sectionnode = Y.Node.create('
                '); sectionnode.setStyle('margin', 0); sectionnode.setContent(drag.get('node').get('innerHTML')); containernode.appendChild(sectionnode); drag.get('dragNode').setContent(containernode); drag.get('dragNode').addClass(CSS.COURSECONTENT); }, drag_dropmiss: function(e) { // Missed the target, but we assume the user intended to drop it // on the last last ghost node location, e.drag and e.drop should be // prepared by global_drag_dropmiss parent so simulate drop_hit(e). this.drop_hit(e); }, get_section_index: function(node) { var sectionlistselector = '.' + CSS.COURSECONTENT + ' li.section', sectionList = Y.all(sectionlistselector), nodeIndex = sectionList.indexOf(node), zeroIndex = sectionList.indexOf(Y.one('#section-0')); return (nodeIndex - zeroIndex); }, drop_hit: function(e) { var drag = e.drag; // Get references to our nodes and their IDs. var dragnode = drag.get('node'), dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode), loopstart = dragnodeid, dropnodeindex = this.get_section_index(dragnode), loopend = dropnodeindex; if (dragnodeid === dropnodeindex) { return; } if (loopstart > loopend) { // If we're going up, we need to swap the loop order // because loops can't go backwards. loopstart = dropnodeindex; loopend = dragnodeid; } // Get the list of nodes. drag.get('dragNode').removeClass(CSS.COURSECONTENT); var sectionlist = Y.Node.all(this.sectionlistselector); // Add a lightbox if it's not there. var lightbox = M.util.add_lightbox(Y, dragnode); // Handle any variables which we must pass via AJAX. var params = {}, pageparams = this.get('config').pageparams, varname; for (varname in pageparams) { if (!pageparams.hasOwnProperty(varname)) { continue; } params[varname] = pageparams[varname]; } // Prepare request parameters params.sesskey = M.cfg.sesskey; params.courseid = this.get('courseid'); params.quizid = this.get('quizid'); params['class'] = 'section'; params.field = 'move'; params.id = dragnodeid; params.value = dropnodeindex; // Perform the AJAX request. var uri = M.cfg.wwwroot + this.get('ajaxurl'); Y.io(uri, { method: 'POST', data: params, on: { start: function() { lightbox.show(); }, success: function(tid, response) { // Update section titles, we can't simply swap them as // they might have custom title try { var responsetext = Y.JSON.parse(response.responseText); if (responsetext.error) { new M.core.ajaxException(responsetext); } M.mod_quiz.edit.process_sections(Y, sectionlist, responsetext, loopstart, loopend); } catch (e) { // Ignore. } // Update all of the section IDs - first unset them, then set them // to avoid duplicates in the DOM. var index; // Classic bubble sort algorithm is applied to the section // nodes between original drag node location and the new one. var swapped = false; do { swapped = false; for (index = loopstart; index <= loopend; index++) { if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) > Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) { // Swap section id. var sectionid = sectionlist.item(index - 1).get('id'); sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id')); sectionlist.item(index).set('id', sectionid); // See what format needs to swap. M.mod_quiz.edit.swap_sections(Y, index - 1, index); // Update flag. swapped = true; } } loopend = loopend - 1; } while (swapped); window.setTimeout(function() { lightbox.hide(); }, 250); }, failure: function(tid, response) { this.ajax_failure(response); lightbox.hide(); } }, context: this }); } }, { NAME: 'mod_quiz-dragdrop-section', ATTRS: { courseid: { value: null }, quizid: { value: null }, ajaxurl: { value: 0 }, config: { value: 0 } } }); M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.init_section_dragdrop = function(params) { new DRAGSECTION(params); }; /** * Resource drag and drop. * * @class M.course.dragdrop.resource * @constructor * @extends M.core.dragdrop */ var DRAGRESOURCE = function() { DRAGRESOURCE.superclass.constructor.apply(this, arguments); }; Y.extend(DRAGRESOURCE, M.core.dragdrop, { initializer: function() { // Set group for parent class this.groups = ['resource']; this.samenodeclass = CSS.ACTIVITY; this.parentnodeclass = CSS.SECTION; this.samenodelabel = { identifier: 'dragtoafter', component: 'quiz' }; this.parentnodelabel = { identifier: 'dragtostart', component: 'quiz' }; // Go through all sections this.setup_for_section(); // Initialise drag & drop for all resources/activities var nodeselector = 'li.' + CSS.ACTIVITY; var del = new Y.DD.Delegate({ container: '.' + CSS.COURSECONTENT, nodes: nodeselector, target: true, handles: ['.' + CSS.EDITINGMOVE], dragConfig: {groups: this.groups} }); del.dd.plug(Y.Plugin.DDProxy, { // Don't move the node at the end of the drag moveOnEnd: false, cloneNode: true }); del.dd.plug(Y.Plugin.DDConstrained, { // Keep it inside the .mod-quiz-edit-content constrain: '#' + CSS.SLOTS }); del.dd.plug(Y.Plugin.DDWinScroll); M.mod_quiz.quizbase.register_module(this); M.mod_quiz.dragres = this; }, /** * Apply dragdrop features to the specified selector or node that refers to section(s) * * @method setup_for_section * @param {String} baseselector The CSS selector or node to limit scope to */ setup_for_section: function() { Y.Node.all('.mod-quiz-edit-content ul.slots ul.section').each(function(resources) { resources.setAttribute('data-draggroups', this.groups.join(' ')); // Define empty ul as droptarget, so that item could be moved to empty list new Y.DD.Drop({ node: resources, groups: this.groups, padding: '20 0 20 0' }); // Initialise each resource/activity in this section this.setup_for_resource('li.activity'); }, this); }, /** * Apply dragdrop features to the specified selector or node that refers to resource(s) * * @method setup_for_resource * @param {String} baseselector The CSS selector or node to limit scope to */ setup_for_resource: function(baseselector) { Y.Node.all(baseselector).each(function(resourcesnode) { // Replace move icons var move = resourcesnode.one('a.' + CSS.EDITINGMOVE); if (move) { var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'), CSS.EDITINGMOVE, CSS.ICONCLASS, true); move.replace(resourcedraghandle); } }, this); }, drag_start: function(e) { // Get our drag object var drag = e.target; drag.get('dragNode').setContent(drag.get('node').get('innerHTML')); drag.get('dragNode').all('.icon').setStyle('vertical-align', 'baseline'); }, drag_dropmiss: function(e) { // Missed the target, but we assume the user intended to drop it // on the last ghost node location, e.drag and e.drop should be // prepared by global_drag_dropmiss parent so simulate drop_hit(e). this.drop_hit(e); }, drop_hit: function(e) { var drag = e.drag; // Get a reference to our drag node var dragnode = drag.get('node'); // Add spinner if it not there var actionarea = dragnode.one(CSS.ACTIONAREA); var spinner = M.util.add_spinner(Y, actionarea); var params = {}; // Handle any variables which we must pass back through to var pageparams = this.get('config').pageparams; var varname; for (varname in pageparams) { params[varname] = pageparams[varname]; } // Prepare request parameters params.sesskey = M.cfg.sesskey; params.courseid = this.get('courseid'); params.quizid = this.get('quizid'); params['class'] = 'resource'; params.field = 'move'; params.id = Number(Y.Moodle.mod_quiz.util.slot.getId(dragnode)); params.sectionId = Y.Moodle.core_course.util.section.getId(dragnode.ancestor('li.section', true)); var previousslot = dragnode.previous(SELECTOR.SLOT); if (previousslot) { params.previousid = Number(Y.Moodle.mod_quiz.util.slot.getId(previousslot)); } var previouspage = dragnode.previous(SELECTOR.PAGE); if (previouspage) { params.page = Number(Y.Moodle.mod_quiz.util.page.getId(previouspage)); } // Do AJAX request var uri = M.cfg.wwwroot + this.get('ajaxurl'); Y.io(uri, { method: 'POST', data: params, on: { start: function() { this.lock_drag_handle(drag, CSS.EDITINGMOVE); spinner.show(); }, success: function(tid, response) { var responsetext = Y.JSON.parse(response.responseText); var params = {element: dragnode, visible: responsetext.visible}; M.mod_quiz.quizbase.invoke_function('set_visibility_resource_ui', params); this.unlock_drag_handle(drag, CSS.EDITINGMOVE); window.setTimeout(function() { spinner.hide(); }, 250); M.mod_quiz.resource_toolbox.reorganise_edit_page(); }, failure: function(tid, response) { this.ajax_failure(response); this.unlock_drag_handle(drag, CSS.SECTIONHANDLE); spinner.hide(); window.location.reload(true); } }, context: this }); }, global_drop_over: function(e) { // Overriding parent method so we can stop the slots being dragged before the first page node. // Check that drop object belong to correct group. if (!e.drop || !e.drop.inGroup(this.groups)) { return; } // Get a reference to our drag and drop nodes. var drag = e.drag.get('node'), drop = e.drop.get('node'); // Save last drop target for the case of missed target processing. this.lastdroptarget = e.drop; // Are we dropping within the same parent node? if (drop.hasClass(this.samenodeclass)) { var where; if (this.goingup) { where = "before"; } else { where = "after"; } drop.insert(drag, where); } else if ((drop.hasClass(this.parentnodeclass) || drop.test('[data-droptarget="1"]')) && !drop.contains(drag)) { // We are dropping on parent node and it is empty if (this.goingup) { drop.append(drag); } else { drop.prepend(drag); } } this.drop_over(e); } }, { NAME: 'mod_quiz-dragdrop-resource', ATTRS: { courseid: { value: null }, quizid: { value: null }, ajaxurl: { value: 0 }, config: { value: 0 } } }); M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.init_resource_dragdrop = function(params) { new DRAGRESOURCE(params); }; }, '@VERSION@', { "requires": [ "base", "node", "io", "dom", "dd", "dd-scroll", "moodle-core-dragdrop", "moodle-core-notification", "moodle-mod_quiz-quizbase", "moodle-mod_quiz-util-base", "moodle-mod_quiz-util-page", "moodle-mod_quiz-util-slot", "moodle-course-util" ] }); moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-min.js000064400000014272152267121500017502 0ustar00YUI.add("moodle-mod_quiz-dragdrop",function(g,e){var o,u=".actions",t="activity",p="mod-quiz-edit-content",r="editing_move",s="iconsmall",c="jumpmenu",n="left",h="movedown",_="moveup",m="page-content",d="right",f="slots",a="section-handle",v="slots",l="sectiondraggable",z="li.page",q="li.slot",i=function(){i.superclass.constructor.apply(this,arguments)};g.extend(i,M.core.dragdrop,{sectionlistselector:null,initializer:function(){if(this.groups=[l],this.samenodeclass="section",this.parentnodeclass="slots",g.Node.one("."+c))return!1;var e;this.sectionlistselector="li.section",this.sectionlistselector&&(this.sectionlistselector="."+p+" "+this.sectionlistselector,this.setup_for_section(this.sectionlistselector),(e=new g.DD.Delegate({container:"."+p,nodes:"."+l,target:!0,handles:["."+n],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+m,stickY:!0}),e.dd.plug(g.Plugin.DDWinScroll))},setup_for_section:function(e){g.Node.all(e).each(function(e){var o,t,i,s=g.Moodle.core_course.util.section.getId(e);0'),t=g.Node.create('
                  ');t.setStyle("margin",0),t.setContent(e.get("node").get("innerHTML")),o.appendChild(t),e.get("dragNode").setContent(o),e.get("dragNode").addClass(p)},drag_dropmiss:function(e){this.drop_hit(e)},get_section_index:function(e){var o="."+p+" li.section",o=g.all(o);return o.indexOf(e)-o.indexOf(g.one("#section-0"))},drop_hit:function(d){var r,a,e,n,o,t=d.drag,c=t.get("node"),i=g.Moodle.core_course.util.section.getId(c),l=i,s=this.get_section_index(c),u=s;if(i!==s){for(o in ug.Moodle.core_course.util.section.getId(r.item(i))&&(n=r.item(i-1).get("id"),r.item(i-1).set("id",r.item(i).get("id")),r.item(i).set("id",n),M.mod_quiz.edit.swap_sections(g,i-1,i),s=!0)}while(u-=1,s);window.setTimeout(function(){a.hide()},250)},failure:function(e,o){this.ajax_failure(o),a.hide()}},context:this})}}},{NAME:"mod_quiz-dragdrop-section",ATTRS:{courseid:{value:null},quizid:{value:null},ajaxurl:{value:0},config:{value:0}}}),M.mod_quiz=M.mod_quiz||{},M.mod_quiz.init_section_dragdrop=function(e){new i(e)},g.extend(o=function(){o.superclass.constructor.apply(this,arguments)},M.core.dragdrop,{initializer:function(){var e;this.groups=["resource"],this.samenodeclass=t,this.parentnodeclass=f,this.samenodelabel={identifier:"dragtoafter",component:"quiz"},this.parentnodelabel={identifier:"dragtostart",component:"quiz"},this.setup_for_section(),e="li."+t,(e=new g.DD.Delegate({container:"."+p,nodes:e,target:!0,handles:["."+r],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1,cloneNode:!0}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+v}),e.dd.plug(g.Plugin.DDWinScroll),M.mod_quiz.quizbase.register_module(this),M.mod_quiz.dragres=this},setup_for_section:function(){g.Node.all(".mod-quiz-edit-content ul.slots ul.section").each(function(e){e.setAttribute("data-draggroups",this.groups.join(" ")),new g.DD.Drop({node:e,groups:this.groups,padding:"20 0 20 0"}),this.setup_for_resource("li.activity")},this)},setup_for_resource:function(e){g.Node.all(e).each(function(e){var o,e=e.one("a."+r);e&&(o=this.get_drag_handle(M.util.get_string("move","moodle"),r,s,!0),e.replace(o))},this)},drag_start:function(e){e=e.target;e.get("dragNode").setContent(e.get("node").get("innerHTML")),e.get("dragNode").all(".icon").setStyle("vertical-align","baseline")},drag_dropmiss:function(e){this.drop_hit(e)},drop_hit:function(e){var o,t=e.drag,i=t.get("node"),e=i.one(u),s=M.util.add_spinner(g,e),n={},d=this.get("config").pageparams;for(o in d)n[o]=d[o];n.sesskey=M.cfg.sesskey,n.courseid=this.get("courseid"),n.quizid=this.get("quizid"),n["class"]="resource",n.field="move",n.id=Number(g.Moodle.mod_quiz.util.slot.getId(i)),n.sectionId=g.Moodle.core_course.util.section.getId(i.ancestor("li.section",!0)),(e=i.previous(q))&&(n.previousid=Number(g.Moodle.mod_quiz.util.slot.getId(e))),(e=i.previous(z))&&(n.page=Number(g.Moodle.mod_quiz.util.page.getId(e))),e=M.cfg.wwwroot+this.get("ajaxurl"),g.io(e,{method:"POST",data:n,on:{start:function(){this.lock_drag_handle(t,r),s.show()},success:function(e,o){o=g.JSON.parse(o.responseText),o={element:i,visible:o.visible};M.mod_quiz.quizbase.invoke_function("set_visibility_resource_ui",o),this.unlock_drag_handle(t,r),window.setTimeout(function(){s.hide()},250),M.mod_quiz.resource_toolbox.reorganise_edit_page()},failure:function(e,o){this.ajax_failure(o),this.unlock_drag_handle(t,a),s.hide(),window.location.reload(!0)}},context:this})},global_drop_over:function(e){var o,t,i;e.drop&&e.drop.inGroup(this.groups)&&(o=e.drag.get("node"),t=e.drop.get("node"),this.lastdroptarget=e.drop,t.hasClass(this.samenodeclass)?(i=this.goingup?"before":"after",t.insert(o,i)):!t.hasClass(this.parentnodeclass)&&!t.test('[data-droptarget="1"]')||t.contains(o)||(this.goingup?t.append(o):t.prepend(o)),this.drop_over(e))}},{NAME:"mod_quiz-dragdrop-resource",ATTRS:{courseid:{value:null},quizid:{value:null},ajaxurl:{value:0},config:{value:0}}}), M.mod_quiz=M.mod_quiz||{},M.mod_quiz.init_resource_dragdrop=function(e){new o(e)}},"@VERSION@",{requires:["base","node","io","dom","dd","dd-scroll","moodle-core-dragdrop","moodle-core-notification","moodle-mod_quiz-quizbase","moodle-mod_quiz-util-base","moodle-mod_quiz-util-page","moodle-mod_quiz-util-slot","moodle-course-util"]});moodle-mod_quiz-modform/moodle-mod_quiz-modform-min.js000064400000001424152267121500017177 0ustar00YUI.add("moodle-mod_quiz-modform",function(e,i){var t=function(){t.superclass.constructor.apply(this,arguments)};e.extend(t,e.Base,{repaginateCheckbox:null,qppSelect:null,qppInitialValue:0,initializer:function(){this.repaginateCheckbox=e.one("#id_repaginatenow"),this.repaginateCheckbox&&(this.qppSelect=e.one("#id_questionsperpage"),this.qppInitialValue=this.qppSelect.get("value"),this.qppSelect.on("change",this.qppChanged,this))},qppChanged:function(){e.later(50,this,function(){this.repaginateCheckbox.get("disabled")||this.repaginateCheckbox.set("checked",this.qppSelect.get("value")!==this.qppInitialValue)})}}),M.mod_quiz=M.mod_quiz||{},M.mod_quiz.modform=M.mod_quiz.modform||new t,M.mod_quiz.modform.init=function(){return new t}},"@VERSION@",{requires:["base","node","event"]});moodle-mod_quiz-modform/moodle-mod_quiz-modform-debug.js000064400000002745152267121500017511 0ustar00YUI.add('moodle-mod_quiz-modform', function (Y, NAME) { /** * The modform class has all the JavaScript specific to mod/quiz/mod_form.php. * * @module moodle-mod_quiz-modform */ var MODFORM = function() { MODFORM.superclass.constructor.apply(this, arguments); }; /** * The coursebase class to provide shared functionality to Modules within * Moodle. * * @class M.course.coursebase * @constructor */ Y.extend(MODFORM, Y.Base, { repaginateCheckbox: null, qppSelect: null, qppInitialValue: 0, initializer: function() { this.repaginateCheckbox = Y.one('#id_repaginatenow'); if (!this.repaginateCheckbox) { // The checkbox only appears when editing an existing quiz. return; } this.qppSelect = Y.one('#id_questionsperpage'); this.qppInitialValue = this.qppSelect.get('value'); this.qppSelect.on('change', this.qppChanged, this); }, qppChanged: function() { Y.later(50, this, function() { if (!this.repaginateCheckbox.get('disabled')) { this.repaginateCheckbox.set('checked', this.qppSelect.get('value') !== this.qppInitialValue); } }); } }); // Ensure that M.mod_quiz exists and that coursebase is initialised correctly M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.modform = M.mod_quiz.modform || new MODFORM(); M.mod_quiz.modform.init = function() { return new MODFORM(); }; }, '@VERSION@', {"requires": ["base", "node", "event"]}); moodle-mod_quiz-modform/moodle-mod_quiz-modform.js000064400000002745152267121500016425 0ustar00YUI.add('moodle-mod_quiz-modform', function (Y, NAME) { /** * The modform class has all the JavaScript specific to mod/quiz/mod_form.php. * * @module moodle-mod_quiz-modform */ var MODFORM = function() { MODFORM.superclass.constructor.apply(this, arguments); }; /** * The coursebase class to provide shared functionality to Modules within * Moodle. * * @class M.course.coursebase * @constructor */ Y.extend(MODFORM, Y.Base, { repaginateCheckbox: null, qppSelect: null, qppInitialValue: 0, initializer: function() { this.repaginateCheckbox = Y.one('#id_repaginatenow'); if (!this.repaginateCheckbox) { // The checkbox only appears when editing an existing quiz. return; } this.qppSelect = Y.one('#id_questionsperpage'); this.qppInitialValue = this.qppSelect.get('value'); this.qppSelect.on('change', this.qppChanged, this); }, qppChanged: function() { Y.later(50, this, function() { if (!this.repaginateCheckbox.get('disabled')) { this.repaginateCheckbox.set('checked', this.qppSelect.get('value') !== this.qppInitialValue); } }); } }); // Ensure that M.mod_quiz exists and that coursebase is initialised correctly M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.modform = M.mod_quiz.modform || new MODFORM(); M.mod_quiz.modform.init = function() { return new MODFORM(); }; }, '@VERSION@', {"requires": ["base", "node", "event"]}); moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-min.js000064400000012027152267121500017762 0ustar00YUI.add("moodle-mod_quiz-util-slot",function(u,e){u.namespace("Moodle.mod_quiz.util.slot"),u.Moodle.mod_quiz.util.slot={CSS:{SLOT:"slot",QUESTIONTYPEDESCRIPTION:"qtype_description",CANNOT_DEPEND:"question_dependency_cannot_depend"},CONSTANTS:{SLOTIDPREFIX:"slot-",QUESTION:M.util.get_string("question","moodle")},SELECTORS:{SLOT:"li.slot",INSTANCENAME:".instancename",NUMBER:"span.slotnumber",PAGECONTENT:"div#page-content",PAGEBREAK:"span.page_split_join_wrapper",ICON:".icon",QUESTIONTYPEDESCRIPTION:".qtype_description",SECTIONUL:"ul.section",DEPENDENCY_WRAPPER:".question_dependency_wrapper",DEPENDENCY_LINK:".question_dependency_wrapper .cm-edit-action",DEPENDENCY_ICON:".question_dependency_wrapper .icon"},getSlotFromComponent:function(e){return u.one(e).ancestor(this.SELECTORS.SLOT,!0)},getId:function(e){e=e.get("id").replace(this.CONSTANTS.SLOTIDPREFIX,"");return!("number"!=typeof(e=parseInt(e,10))||!isFinite(e))&&e},getName:function(e){e=e.one(this.SELECTORS.INSTANCENAME);return e?e.get("firstChild").get("data"):null},getNumber:function(e){if(!e)return!1;e=e.one(this.SELECTORS.NUMBER).get("text").replace(this.CONSTANTS.QUESTION,"");return!("number"!=typeof(e=parseInt(e,10))||!isFinite(e))&&e},setNumber:function(e,t){e.one(this.SELECTORS.NUMBER).setHTML(''+this.CONSTANTS.QUESTION+" "+t)},getSlots:function(){return u.all(this.SELECTORS.PAGECONTENT+" "+this.SELECTORS.SECTIONUL+" "+this.SELECTORS.SLOT)},getNumberedSlots:function(){var e=this.SELECTORS.PAGECONTENT+" "+this.SELECTORS.SECTIONUL;return e+=" "+this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")",u.all(e)},getPrevious:function(e){return e.previous(this.SELECTORS.SLOT)},getPreviousNumbered:function(e){var t,i,n=e.previous(this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")");if(n)return n;for(t=e.ancestor("li.section").previous("li.section");t;){if(0<(i=t.all(this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")")).size())return i.item(i.size()-1);t=t.previous("li.section")}return!1},reorderSlots:function(){this.getSlots().each(function(e){var t,i;u.Moodle.mod_quiz.util.page.getPageFromSlot(e)||(t=e.next(u.Moodle.mod_quiz.util.page.SELECTORS.PAGE),e.swap(t)),t=this.getPreviousNumbered(e),i=0,e.hasClass(this.CSS.QUESTIONTYPEDESCRIPTION)||(t&&(i=this.getNumber(t)),this.setNumber(e,i+1))},this)},updateOneSlotSections:function(){u.all(".mod-quiz-edit-content ul.slots li.section").each(function(e){1' + this.CONSTANTS.QUESTION + '
                  ' + number); }, /** * Returns a list of all slot elements on the page. * * @method getSlots * @return {node[]} An array containing slot nodes. */ getSlots: function() { return Y.all(this.SELECTORS.PAGECONTENT + ' ' + this.SELECTORS.SECTIONUL + ' ' + this.SELECTORS.SLOT); }, /** * Returns a list of all slot elements on the page that have numbers. Excudes description questions. * * @method getSlots * @return {node[]} An array containing slot nodes. */ getNumberedSlots: function() { var selector = this.SELECTORS.PAGECONTENT + ' ' + this.SELECTORS.SECTIONUL; selector += ' ' + this.SELECTORS.SLOT + ':not(' + this.SELECTORS.QUESTIONTYPEDESCRIPTION + ')'; return Y.all(selector); }, /** * Returns the previous slot to the given slot. * * @method getPrevious * @param slot Slot node * @return {node|false} The previous slot node or false. */ getPrevious: function(slot) { return slot.previous(this.SELECTORS.SLOT); }, /** * Returns the previous numbered slot to the given slot. * * Ignores slots containing description question types. * * @method getPrevious * @param slot Slot node * @return {node|false} The previous slot node or false. */ getPreviousNumbered: function(slot) { var previous = slot.previous(this.SELECTORS.SLOT + ':not(' + this.SELECTORS.QUESTIONTYPEDESCRIPTION + ')'); if (previous) { return previous; } var section = slot.ancestor('li.section').previous('li.section'); while (section) { var questions = section.all(this.SELECTORS.SLOT + ':not(' + this.SELECTORS.QUESTIONTYPEDESCRIPTION + ')'); if (questions.size() > 0) { return questions.item(questions.size() - 1); } section = section.previous('li.section'); } return false; }, /** * Reset the order of the numbers given to each slot. * * @method reorderSlots * @return void */ reorderSlots: function() { // Get list of slot nodes. var slots = this.getSlots(); // Loop through slots incrementing the number each time. slots.each(function(slot) { if (!Y.Moodle.mod_quiz.util.page.getPageFromSlot(slot)) { // Move the next page to the front. var nextpage = slot.next(Y.Moodle.mod_quiz.util.page.SELECTORS.PAGE); slot.swap(nextpage); } var previousSlot = this.getPreviousNumbered(slot), previousslotnumber = 0; if (slot.hasClass(this.CSS.QUESTIONTYPEDESCRIPTION)) { return; } if (previousSlot) { previousslotnumber = this.getNumber(previousSlot); } // Set slot number. this.setNumber(slot, previousslotnumber + 1); }, this); }, /** * Add class only-has-one-slot to those sections that need it. * * @method updateOneSlotSections * @return void */ updateOneSlotSections: function() { Y.all('.mod-quiz-edit-content ul.slots li.section').each(function(section) { if (section.all(this.SELECTORS.SLOT).size() > 1) { section.removeClass('only-has-one-slot'); } else { section.addClass('only-has-one-slot'); } }, this); }, /** * Remove a slot and related elements from the list of slots. * * @method remove * @param slot Slot node * @return void */ remove: function(slot) { var page = Y.Moodle.mod_quiz.util.page.getPageFromSlot(slot); slot.remove(); // Is the page empty. if (!Y.Moodle.mod_quiz.util.page.isEmpty(page)) { return; } // If so remove it. Including add menu and page break. Y.Moodle.mod_quiz.util.page.remove(page); }, /** * Returns a list of all page break elements on the page. * * @method getPageBreaks * @return {node[]} An array containing page break nodes. */ getPageBreaks: function() { var selector = this.SELECTORS.PAGECONTENT + ' ' + this.SELECTORS.SECTIONUL; selector += ' ' + this.SELECTORS.SLOT + this.SELECTORS.PAGEBREAK; return Y.all(selector); }, /** * Retrieve the page break element item from the given slot. * * @method getPageBreak * @param slot Slot node * @return {Node|null} The Page Break Node. */ getPageBreak: function(slot) { return Y.one(slot).one(this.SELECTORS.PAGEBREAK); }, /** * Add a page break and related elements to the list of slots. * * @method addPageBreak * @param beforenode Int | Node | HTMLElement | String to add * @return pagebreak PageBreak node */ addPageBreak: function(slot) { var nodetext = M.mod_quiz.resource_toolbox.get('config').addpageiconhtml; nodetext = nodetext.replace('%%SLOT%%', this.getNumber(slot)); var pagebreak = Y.Node.create(nodetext); slot.one('div').insert(pagebreak, 'after'); return pagebreak; }, /** * Remove a pagebreak from the given slot. * * @method removePageBreak * @param slot Slot node * @return boolean */ removePageBreak: function(slot) { var pagebreak = this.getPageBreak(slot); if (!pagebreak) { return false; } pagebreak.remove(); return true; }, /** * Reorder each pagebreak by iterating through each related slot. * * @method reorderPageBreaks * @return void */ reorderPageBreaks: function() { // Get list of slot nodes. var slots = this.getSlots(); var slotnumber = 0; // Loop through slots incrementing the number each time. slots.each(function(slot, key) { slotnumber++; var pagebreak = this.getPageBreak(slot); var nextitem = slot.next('li.activity'); if (!nextitem) { // Last slot in a section. Should not have an icon. return; } // No pagebreak and not last slot. Add one. if (!pagebreak) { pagebreak = this.addPageBreak(slot); } // Remove last page break if there is one. if (pagebreak && key === slots.size() - 1) { this.removePageBreak(slot); } // Get page break anchor element. var pagebreaklink = pagebreak.get('childNodes').item(0); // Get the correct title. var action = ''; var iconname = ''; if (Y.Moodle.mod_quiz.util.page.isPage(nextitem)) { action = 'removepagebreak'; iconname = 'e/remove_page_break'; } else { action = 'addpagebreak'; iconname = 'e/insert_page_break'; } // Update the link and image titles pagebreaklink.set('title', M.util.get_string(action, 'quiz')); pagebreaklink.setData('action', action); // Update the image title. var icon = pagebreaklink.one(this.SELECTORS.ICON); icon.set('title', M.util.get_string(action, 'quiz')); icon.set('alt', M.util.get_string(action, 'quiz')); // Update the image src. icon.set('src', M.util.image_url(iconname)); // Get anchor url parameters as an associative array. var params = Y.QueryString.parse(pagebreaklink.get('href')); // Update slot number. params.slot = slotnumber; // Create the new url. var newurl = ''; for (var index in params) { if (newurl.length) { newurl += "&"; } newurl += index + "=" + params[index]; } // Update the anchor. pagebreaklink.set('href', newurl); }, this); }, /** * Update the dependency icons. * * @method updateAllDependencyIcons * @return void */ updateAllDependencyIcons: function() { // Get list of slot nodes. var slots = this.getSlots(), slotnumber = 0, previousslot = null; // Loop through slots incrementing the number each time. slots.each(function(slot) { slotnumber++; if (slotnumber == 1 || previousslot.getData('canfinish') === '0') { slot.one(this.SELECTORS.DEPENDENCY_WRAPPER).addClass(this.CSS.CANNOT_DEPEND); } else { slot.one(this.SELECTORS.DEPENDENCY_WRAPPER).removeClass(this.CSS.CANNOT_DEPEND); } this.updateDependencyIcon(slot, null); previousslot = slot; }, this); }, /** * Update the slot icon to indicate the new requiresprevious state. * * @method slot Slot node * @method requiresprevious Whether this node now requires the previous one. * @return void */ updateDependencyIcon: function(slot, requiresprevious) { var link = slot.one(this.SELECTORS.DEPENDENCY_LINK); var icon = slot.one(this.SELECTORS.DEPENDENCY_ICON); var previousSlot = this.getPrevious(slot); var a = {thisq: this.getNumber(slot)}; if (previousSlot) { a.previousq = this.getNumber(previousSlot); } if (requiresprevious === null) { requiresprevious = link.getData('action') === 'removedependency'; } if (requiresprevious) { link.set('title', M.util.get_string('questiondependencyremove', 'quiz', a)); link.setData('action', 'removedependency'); window.require(['core/templates'], function(Templates) { Templates.renderPix('t/locked', 'core', M.util.get_string('questiondependsonprevious', 'quiz')).then( function(html) { icon.replace(html); } ); }); } else { link.set('title', M.util.get_string('questiondependencyadd', 'quiz', a)); link.setData('action', 'adddependency'); window.require(['core/templates'], function(Templates) { Templates.renderPix('t/unlocked', 'core', M.util.get_string('questiondependencyfree', 'quiz')).then( function(html) { icon.replace(html); } ); }); } } }; }, '@VERSION@', {"requires": ["node", "moodle-mod_quiz-util-base"]}); moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot.js000064400000034436152267121500017211 0ustar00YUI.add('moodle-mod_quiz-util-slot', function (Y, NAME) { /** * A collection of utility classes for use with slots. * * @module moodle-mod_quiz-util * @submodule moodle-mod_quiz-util-slot */ Y.namespace('Moodle.mod_quiz.util.slot'); /** * A collection of utility classes for use with slots. * * @class Moodle.mod_quiz.util.slot * @static */ Y.Moodle.mod_quiz.util.slot = { CSS: { SLOT: 'slot', QUESTIONTYPEDESCRIPTION: 'qtype_description', CANNOT_DEPEND: 'question_dependency_cannot_depend' }, CONSTANTS: { SLOTIDPREFIX: 'slot-', QUESTION: M.util.get_string('question', 'moodle') }, SELECTORS: { SLOT: 'li.slot', INSTANCENAME: '.instancename', NUMBER: 'span.slotnumber', PAGECONTENT: 'div#page-content', PAGEBREAK: 'span.page_split_join_wrapper', ICON: '.icon', QUESTIONTYPEDESCRIPTION: '.qtype_description', SECTIONUL: 'ul.section', DEPENDENCY_WRAPPER: '.question_dependency_wrapper', DEPENDENCY_LINK: '.question_dependency_wrapper .cm-edit-action', DEPENDENCY_ICON: '.question_dependency_wrapper .icon' }, /** * Retrieve the slot item from one of it's child Nodes. * * @method getSlotFromComponent * @param slotcomponent {Node} The component Node. * @return {Node|null} The Slot Node. */ getSlotFromComponent: function(slotcomponent) { return Y.one(slotcomponent).ancestor(this.SELECTORS.SLOT, true); }, /** * Determines the slot ID for the provided slot. * * @method getId * @param slot {Node} The slot to find an ID for. * @return {Number|false} The ID of the slot in question or false if no ID was found. */ getId: function(slot) { // We perform a simple substitution operation to get the ID. var id = slot.get('id').replace( this.CONSTANTS.SLOTIDPREFIX, ''); // Attempt to validate the ID. id = parseInt(id, 10); if (typeof id === 'number' && isFinite(id)) { return id; } return false; }, /** * Determines the slot name for the provided slot. * * @method getName * @param slot {Node} The slot to find a name for. * @return {string|false} The name of the slot in question or false if no ID was found. */ getName: function(slot) { var instance = slot.one(this.SELECTORS.INSTANCENAME); if (instance) { return instance.get('firstChild').get('data'); } return null; }, /** * Determines the slot number for the provided slot. * * @method getNumber * @param slot {Node} The slot to find the number for. * @return {Number|false} The number of the slot in question or false if no number was found. */ getNumber: function(slot) { if (!slot) { return false; } // We perform a simple substitution operation to get the number. var number = slot.one(this.SELECTORS.NUMBER).get('text').replace( this.CONSTANTS.QUESTION, ''); // Attempt to validate the ID. number = parseInt(number, 10); if (typeof number === 'number' && isFinite(number)) { return number; } return false; }, /** * Updates the slot number for the provided slot. * * @method setNumber * @param slot {Node} The slot to update the number for. * @return void */ setNumber: function(slot, number) { var numbernode = slot.one(this.SELECTORS.NUMBER); numbernode.setHTML('' + this.CONSTANTS.QUESTION + ' ' + number); }, /** * Returns a list of all slot elements on the page. * * @method getSlots * @return {node[]} An array containing slot nodes. */ getSlots: function() { return Y.all(this.SELECTORS.PAGECONTENT + ' ' + this.SELECTORS.SECTIONUL + ' ' + this.SELECTORS.SLOT); }, /** * Returns a list of all slot elements on the page that have numbers. Excudes description questions. * * @method getSlots * @return {node[]} An array containing slot nodes. */ getNumberedSlots: function() { var selector = this.SELECTORS.PAGECONTENT + ' ' + this.SELECTORS.SECTIONUL; selector += ' ' + this.SELECTORS.SLOT + ':not(' + this.SELECTORS.QUESTIONTYPEDESCRIPTION + ')'; return Y.all(selector); }, /** * Returns the previous slot to the given slot. * * @method getPrevious * @param slot Slot node * @return {node|false} The previous slot node or false. */ getPrevious: function(slot) { return slot.previous(this.SELECTORS.SLOT); }, /** * Returns the previous numbered slot to the given slot. * * Ignores slots containing description question types. * * @method getPrevious * @param slot Slot node * @return {node|false} The previous slot node or false. */ getPreviousNumbered: function(slot) { var previous = slot.previous(this.SELECTORS.SLOT + ':not(' + this.SELECTORS.QUESTIONTYPEDESCRIPTION + ')'); if (previous) { return previous; } var section = slot.ancestor('li.section').previous('li.section'); while (section) { var questions = section.all(this.SELECTORS.SLOT + ':not(' + this.SELECTORS.QUESTIONTYPEDESCRIPTION + ')'); if (questions.size() > 0) { return questions.item(questions.size() - 1); } section = section.previous('li.section'); } return false; }, /** * Reset the order of the numbers given to each slot. * * @method reorderSlots * @return void */ reorderSlots: function() { // Get list of slot nodes. var slots = this.getSlots(); // Loop through slots incrementing the number each time. slots.each(function(slot) { if (!Y.Moodle.mod_quiz.util.page.getPageFromSlot(slot)) { // Move the next page to the front. var nextpage = slot.next(Y.Moodle.mod_quiz.util.page.SELECTORS.PAGE); slot.swap(nextpage); } var previousSlot = this.getPreviousNumbered(slot), previousslotnumber = 0; if (slot.hasClass(this.CSS.QUESTIONTYPEDESCRIPTION)) { return; } if (previousSlot) { previousslotnumber = this.getNumber(previousSlot); } // Set slot number. this.setNumber(slot, previousslotnumber + 1); }, this); }, /** * Add class only-has-one-slot to those sections that need it. * * @method updateOneSlotSections * @return void */ updateOneSlotSections: function() { Y.all('.mod-quiz-edit-content ul.slots li.section').each(function(section) { if (section.all(this.SELECTORS.SLOT).size() > 1) { section.removeClass('only-has-one-slot'); } else { section.addClass('only-has-one-slot'); } }, this); }, /** * Remove a slot and related elements from the list of slots. * * @method remove * @param slot Slot node * @return void */ remove: function(slot) { var page = Y.Moodle.mod_quiz.util.page.getPageFromSlot(slot); slot.remove(); // Is the page empty. if (!Y.Moodle.mod_quiz.util.page.isEmpty(page)) { return; } // If so remove it. Including add menu and page break. Y.Moodle.mod_quiz.util.page.remove(page); }, /** * Returns a list of all page break elements on the page. * * @method getPageBreaks * @return {node[]} An array containing page break nodes. */ getPageBreaks: function() { var selector = this.SELECTORS.PAGECONTENT + ' ' + this.SELECTORS.SECTIONUL; selector += ' ' + this.SELECTORS.SLOT + this.SELECTORS.PAGEBREAK; return Y.all(selector); }, /** * Retrieve the page break element item from the given slot. * * @method getPageBreak * @param slot Slot node * @return {Node|null} The Page Break Node. */ getPageBreak: function(slot) { return Y.one(slot).one(this.SELECTORS.PAGEBREAK); }, /** * Add a page break and related elements to the list of slots. * * @method addPageBreak * @param beforenode Int | Node | HTMLElement | String to add * @return pagebreak PageBreak node */ addPageBreak: function(slot) { var nodetext = M.mod_quiz.resource_toolbox.get('config').addpageiconhtml; nodetext = nodetext.replace('%%SLOT%%', this.getNumber(slot)); var pagebreak = Y.Node.create(nodetext); slot.one('div').insert(pagebreak, 'after'); return pagebreak; }, /** * Remove a pagebreak from the given slot. * * @method removePageBreak * @param slot Slot node * @return boolean */ removePageBreak: function(slot) { var pagebreak = this.getPageBreak(slot); if (!pagebreak) { return false; } pagebreak.remove(); return true; }, /** * Reorder each pagebreak by iterating through each related slot. * * @method reorderPageBreaks * @return void */ reorderPageBreaks: function() { // Get list of slot nodes. var slots = this.getSlots(); var slotnumber = 0; // Loop through slots incrementing the number each time. slots.each(function(slot, key) { slotnumber++; var pagebreak = this.getPageBreak(slot); var nextitem = slot.next('li.activity'); if (!nextitem) { // Last slot in a section. Should not have an icon. return; } // No pagebreak and not last slot. Add one. if (!pagebreak) { pagebreak = this.addPageBreak(slot); } // Remove last page break if there is one. if (pagebreak && key === slots.size() - 1) { this.removePageBreak(slot); } // Get page break anchor element. var pagebreaklink = pagebreak.get('childNodes').item(0); // Get the correct title. var action = ''; var iconname = ''; if (Y.Moodle.mod_quiz.util.page.isPage(nextitem)) { action = 'removepagebreak'; iconname = 'e/remove_page_break'; } else { action = 'addpagebreak'; iconname = 'e/insert_page_break'; } // Update the link and image titles pagebreaklink.set('title', M.util.get_string(action, 'quiz')); pagebreaklink.setData('action', action); // Update the image title. var icon = pagebreaklink.one(this.SELECTORS.ICON); icon.set('title', M.util.get_string(action, 'quiz')); icon.set('alt', M.util.get_string(action, 'quiz')); // Update the image src. icon.set('src', M.util.image_url(iconname)); // Get anchor url parameters as an associative array. var params = Y.QueryString.parse(pagebreaklink.get('href')); // Update slot number. params.slot = slotnumber; // Create the new url. var newurl = ''; for (var index in params) { if (newurl.length) { newurl += "&"; } newurl += index + "=" + params[index]; } // Update the anchor. pagebreaklink.set('href', newurl); }, this); }, /** * Update the dependency icons. * * @method updateAllDependencyIcons * @return void */ updateAllDependencyIcons: function() { // Get list of slot nodes. var slots = this.getSlots(), slotnumber = 0, previousslot = null; // Loop through slots incrementing the number each time. slots.each(function(slot) { slotnumber++; if (slotnumber == 1 || previousslot.getData('canfinish') === '0') { slot.one(this.SELECTORS.DEPENDENCY_WRAPPER).addClass(this.CSS.CANNOT_DEPEND); } else { slot.one(this.SELECTORS.DEPENDENCY_WRAPPER).removeClass(this.CSS.CANNOT_DEPEND); } this.updateDependencyIcon(slot, null); previousslot = slot; }, this); }, /** * Update the slot icon to indicate the new requiresprevious state. * * @method slot Slot node * @method requiresprevious Whether this node now requires the previous one. * @return void */ updateDependencyIcon: function(slot, requiresprevious) { var link = slot.one(this.SELECTORS.DEPENDENCY_LINK); var icon = slot.one(this.SELECTORS.DEPENDENCY_ICON); var previousSlot = this.getPrevious(slot); var a = {thisq: this.getNumber(slot)}; if (previousSlot) { a.previousq = this.getNumber(previousSlot); } if (requiresprevious === null) { requiresprevious = link.getData('action') === 'removedependency'; } if (requiresprevious) { link.set('title', M.util.get_string('questiondependencyremove', 'quiz', a)); link.setData('action', 'removedependency'); window.require(['core/templates'], function(Templates) { Templates.renderPix('t/locked', 'core', M.util.get_string('questiondependsonprevious', 'quiz')).then( function(html) { icon.replace(html); } ); }); } else { link.set('title', M.util.get_string('questiondependencyadd', 'quiz', a)); link.setData('action', 'adddependency'); window.require(['core/templates'], function(Templates) { Templates.renderPix('t/unlocked', 'core', M.util.get_string('questiondependencyfree', 'quiz')).then( function(html) { icon.replace(html); } ); }); } } }; }, '@VERSION@', {"requires": ["node", "moodle-mod_quiz-util-base"]}); moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-min.js000064400000012076152267121500017554 0ustar00YUI.add("moodle-mod_quiz-autosave",function(n,t){M.mod_quiz=M.mod_quiz||{},M.mod_quiz.autosave={TINYMCE_DETECTION_DELAY:500,TINYMCE_DETECTION_REPEATS:20,WATCH_HIDDEN_DELAY:1e3,FAILURES_BEFORE_NOTIFY:1,FIRST_SUCCESSFUL_SAVE:-1,SELECTORS:{QUIZ_FORM:"#responseform",VALUE_CHANGE_ELEMENTS:'input, textarea, [contenteditable="true"]',CHANGE_ELEMENTS:"input, select",HIDDEN_INPUTS:"input[type=hidden]",CONNECTION_ERROR:"#connection-error",CONNECTION_OK:"#connection-ok"},AUTOSAVE_HANDLER:M.cfg.wwwroot+"/mod/quiz/autosave.ajax.php",delay:12e4,form:null,dirty:!1,delay_timer:null,save_transaction:null,savefailures:0,editor_change_handler:null,hidden_field_values:{},init:function(t){this.form=n.one(this.SELECTORS.QUIZ_FORM),this.form&&(this.delay=1e3*t,this.form.delegate("valuechange",this.value_changed,this.SELECTORS.VALUE_CHANGE_ELEMENTS,this),this.form.delegate("change",this.value_changed,this.SELECTORS.CHANGE_ELEMENTS,this),this.form.on("submit",this.stop_autosaving,this),require(["core_form/events"],function(t){window.addEventListener(t.eventTypes.uploadChanged,this.value_changed.bind(this))}.bind(this)),this.init_tinymce(this.TINYMCE_DETECTION_REPEATS),this.save_hidden_field_values(),this.watch_hidden_fields())},save_hidden_field_values:function(){this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(t){var e=t.get("name");e&&(this.hidden_field_values[e]=t.get("value"))},this)},watch_hidden_fields:function(){this.detect_hidden_field_changes(),n.later(this.WATCH_HIDDEN_DELAY,this,this.watch_hidden_fields)},detect_hidden_field_changes:function(){this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(t){var e=t.get("name"),i=t.get("value");e&&(e in this.hidden_field_values&&i===this.hidden_field_values[e]||(this.hidden_field_values[e]=i,this.value_changed({target:t})))},this)},init_tinymce:function(t){var e;"undefined"!=typeof window.tinyMCE?(this.editor_change_handler=n.bind(this.editor_changed,this),window.tinyMCE.onAddEditor?window.tinyMCE.onAddEditor.add(n.bind(this.init_tinymce_editor,this)):window.tinyMCE.on&&(e=this.start_save_timer_if_necessary.bind(this),window.tinyMCE.on("AddEditor",function(t){t.editor.on("Change Undo Redo keydown",e)}),window.tinyMCE.get().forEach(function(t){t.on("Change Undo Redo keydown",e)}))):0M.mod_quiz.timer.endtime},stop_autosaving:function(){this.cancel_delay(),this.delay_timer=!0,this.save_transaction&&this.save_transaction.abort()}}},"@VERSION@",{requires:["base","node","event","event-valuechange","node-event-delegate","io-form","datatype-date-format"]});moodle-mod_quiz-autosave/moodle-mod_quiz-autosave.js000064400000032347152267121500016776 0ustar00YUI.add('moodle-mod_quiz-autosave', function (Y, NAME) { // 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 . /** * Auto-save functionality for during quiz attempts. * * @module moodle-mod_quiz-autosave */ /** * Auto-save functionality for during quiz attempts. * * @class M.mod_quiz.autosave */ M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.autosave = { /** * The amount of time (in milliseconds) to wait between TinyMCE detections. * * @property TINYMCE_DETECTION_DELAY * @type Number * @default 500 * @private */ TINYMCE_DETECTION_DELAY: 500, /** * The number of times to try redetecting TinyMCE. * * @property TINYMCE_DETECTION_REPEATS * @type Number * @default 20 * @private */ TINYMCE_DETECTION_REPEATS: 20, /** * The delay (in milliseconds) between checking hidden input fields. * * @property WATCH_HIDDEN_DELAY * @type Number * @default 1000 * @private */ WATCH_HIDDEN_DELAY: 1000, /** * The number of failures to ignore before notifying the user. * * @property FAILURES_BEFORE_NOTIFY * @type Number * @default 1 * @private */ FAILURES_BEFORE_NOTIFY: 1, /** * The value to use when resetting the successful save counter. * * @property FIRST_SUCCESSFUL_SAVE * @static * @type Number * @default -1 * @private */ FIRST_SUCCESSFUL_SAVE: -1, /** * The selectors used throughout this class. * * @property SELECTORS * @private * @type Object * @static */ SELECTORS: { QUIZ_FORM: '#responseform', VALUE_CHANGE_ELEMENTS: 'input, textarea, [contenteditable="true"]', CHANGE_ELEMENTS: 'input, select', HIDDEN_INPUTS: 'input[type=hidden]', CONNECTION_ERROR: '#connection-error', CONNECTION_OK: '#connection-ok' }, /** * The script which handles the autosaves. * * @property AUTOSAVE_HANDLER * @type String * @default M.cfg.wwwroot + '/mod/quiz/autosave.ajax.php' * @private */ AUTOSAVE_HANDLER: M.cfg.wwwroot + '/mod/quiz/autosave.ajax.php', /** * The delay (in milliseconds) between a change being made, and it being auto-saved. * * @property delay * @type Number * @default 120000 * @private */ delay: 120000, /** * A Node reference to the form we are monitoring. * * @property form * @type Node * @default null */ form: null, /** * Whether the form has been modified since the last save started. * * @property dirty * @type boolean * @default false */ dirty: false, /** * Timer object for the delay between form modifaction and the save starting. * * @property delay_timer * @type Object * @default null * @private */ delay_timer: null, /** * Y.io transaction for the save ajax request. * * @property save_transaction * @type object * @default null */ save_transaction: null, /** * Failed saves count. * * @property savefailures * @type Number * @default 0 * @private */ savefailures: 0, /** * Properly bound key change handler. * * @property editor_change_handler * @type EventHandle * @default null * @private */ editor_change_handler: null, /** * Record of the value of all the hidden fields, last time they were checked. * * @property hidden_field_values * @type Object * @default {} */ hidden_field_values: {}, /** * Initialise the autosave code. * * @method init * @param {Number} delay the delay, in seconds, between a change being detected, and * a save happening. */ init: function(delay) { this.form = Y.one(this.SELECTORS.QUIZ_FORM); if (!this.form) { return; } this.delay = delay * 1000; this.form.delegate('valuechange', this.value_changed, this.SELECTORS.VALUE_CHANGE_ELEMENTS, this); this.form.delegate('change', this.value_changed, this.SELECTORS.CHANGE_ELEMENTS, this); this.form.on('submit', this.stop_autosaving, this); require(['core_form/events'], function(FormEvent) { window.addEventListener(FormEvent.eventTypes.uploadChanged, this.value_changed.bind(this)); }.bind(this)); this.init_tinymce(this.TINYMCE_DETECTION_REPEATS); this.save_hidden_field_values(); this.watch_hidden_fields(); }, save_hidden_field_values: function() { this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(hidden) { var name = hidden.get('name'); if (!name) { return; } this.hidden_field_values[name] = hidden.get('value'); }, this); }, watch_hidden_fields: function() { this.detect_hidden_field_changes(); Y.later(this.WATCH_HIDDEN_DELAY, this, this.watch_hidden_fields); }, detect_hidden_field_changes: function() { this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(hidden) { var name = hidden.get('name'), value = hidden.get('value'); if (!name) { return; } if (!(name in this.hidden_field_values) || value !== this.hidden_field_values[name]) { this.hidden_field_values[name] = value; this.value_changed({target: hidden}); } }, this); }, /** * Initialise watching of TinyMCE specifically. * * Because TinyMCE might load slowly, and after us, we need to keep * trying, until we detect TinyMCE is there, or enough time has passed. * This is based on the TINYMCE_DETECTION_DELAY and * TINYMCE_DETECTION_REPEATS properties. * * * @method init_tinymce * @param {Number} repeatcount The number of attempts made so far. */ init_tinymce: function(repeatcount) { if (typeof window.tinyMCE === 'undefined') { if (repeatcount > 0) { Y.later(this.TINYMCE_DETECTION_DELAY, this, this.init_tinymce, [repeatcount - 1]); } else { } return; } this.editor_change_handler = Y.bind(this.editor_changed, this); if (window.tinyMCE.onAddEditor) { window.tinyMCE.onAddEditor.add(Y.bind(this.init_tinymce_editor, this)); } else if (window.tinyMCE.on) { var startSaveTimer = this.start_save_timer_if_necessary.bind(this); window.tinyMCE.on('AddEditor', function(event) { event.editor.on('Change Undo Redo keydown', startSaveTimer); }); // One or more editors might already have been added, so we have to attach // the event handlers to these as well. window.tinyMCE.get().forEach(function(editor) { editor.on('Change Undo Redo keydown', startSaveTimer); }); } }, /** * Initialise watching of a specific TinyMCE editor. * * @method init_tinymce_editor * @param {EventFacade} e * @param {Object} editor The TinyMCE editor object */ init_tinymce_editor: function(e, editor) { editor.onChange.add(this.editor_change_handler); editor.onRedo.add(this.editor_change_handler); editor.onUndo.add(this.editor_change_handler); editor.onKeyDown.add(this.editor_change_handler); }, value_changed: function(e) { var name = e.target.getAttribute('name'); if (name === 'thispage' || name === 'scrollpos' || (name && name.match(/_:flagged$/))) { return; // Not interesting. } // Fallback to the ID when the name is not present (in the case of content editable). name = name || '#' + e.target.getAttribute('id'); this.start_save_timer_if_necessary(); }, editor_changed: function(editor) { this.start_save_timer_if_necessary(); }, start_save_timer_if_necessary: function() { this.dirty = true; if (this.delay_timer || this.save_transaction) { // Already counting down or daving. return; } this.start_save_timer(); }, start_save_timer: function() { this.cancel_delay(); this.delay_timer = Y.later(this.delay, this, this.save_changes); }, cancel_delay: function() { if (this.delay_timer && this.delay_timer !== true) { this.delay_timer.cancel(); } this.delay_timer = null; }, save_changes: function() { this.cancel_delay(); this.dirty = false; if (this.is_time_nearly_over()) { this.stop_autosaving(); return; } if (typeof window.tinyMCE !== 'undefined') { window.tinyMCE.triggerSave(); } // YUI io.form incorrectly (in my opinion) sends the value of all submit // buttons in the ajax request. We don't want any submit buttons. // Therefore, temporarily change the type. // (Yes, this is a nasty hack. One day this will be re-written as AMD, hopefully). var allsubmitbuttons = this.form.all('input[type=submit], button[type=submit]'); allsubmitbuttons.setAttribute('type', 'button'); this.save_transaction = Y.io(this.AUTOSAVE_HANDLER, { method: 'POST', form: {id: this.form}, on: { success: this.save_done, failure: this.save_failed }, context: this }); // Change the button types back. allsubmitbuttons.setAttribute('type', 'submit'); }, save_done: function(transactionid, response) { var autosavedata = JSON.parse(response.responseText); if (autosavedata.status !== 'OK') { // Because IIS is useless, Moodle can't send proper HTTP response // codes, so we have to detect failures manually. this.save_failed(transactionid, response); return; } if (typeof autosavedata.timeleft !== 'undefined') { M.mod_quiz.timer.updateEndTime(autosavedata.timeleft); } this.update_saved_time_display(); this.save_transaction = null; if (this.dirty) { this.start_save_timer(); } if (this.savefailures > 0) { Y.one(this.SELECTORS.CONNECTION_ERROR).hide(); Y.one(this.SELECTORS.CONNECTION_OK).show(); this.savefailures = this.FIRST_SUCCESSFUL_SAVE; } else if (this.savefailures === this.FIRST_SUCCESSFUL_SAVE) { Y.one(this.SELECTORS.CONNECTION_OK).hide(); this.savefailures = 0; } }, save_failed: function() { this.save_transaction = null; // We want to retry soon. this.start_save_timer(); this.savefailures = Math.max(1, this.savefailures + 1); if (this.savefailures === this.FAILURES_BEFORE_NOTIFY) { Y.one(this.SELECTORS.CONNECTION_ERROR).show(); Y.one(this.SELECTORS.CONNECTION_OK).hide(); } }, /** * Inform the user that their answers have been saved. * * @method update_saved_time_display */ update_saved_time_display: function() { // We fetch the current language's preferred time format from the language pack. require(['core/user_date', 'core/notification'], function(UserDate, Notification) { UserDate.get([{ timestamp: Math.floor(Date.now() / 1000), format: M.util.get_string('strftimedatetimeshortaccurate', 'langconfig'), }]).then(function(dateStrs) { var infoDiv = Y.one('#mod_quiz_navblock .othernav .autosave_info'); infoDiv.set('text', M.util.get_string('lastautosave', 'quiz', dateStrs[0])); infoDiv.show(); return; }).catch(Notification.exception); }); }, is_time_nearly_over: function() { return M.mod_quiz.timer && M.mod_quiz.timer.endtime && (new Date().getTime() + 2 * this.delay) > M.mod_quiz.timer.endtime; }, stop_autosaving: function() { this.cancel_delay(); this.delay_timer = true; if (this.save_transaction) { this.save_transaction.abort(); } } }; }, '@VERSION@', { "requires": [ "base", "node", "event", "event-valuechange", "node-event-delegate", "io-form", "datatype-date-format" ] }); moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-debug.js000064400000034446152267121500020064 0ustar00YUI.add('moodle-mod_quiz-autosave', function (Y, NAME) { // 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 . /** * Auto-save functionality for during quiz attempts. * * @module moodle-mod_quiz-autosave */ /** * Auto-save functionality for during quiz attempts. * * @class M.mod_quiz.autosave */ M.mod_quiz = M.mod_quiz || {}; M.mod_quiz.autosave = { /** * The amount of time (in milliseconds) to wait between TinyMCE detections. * * @property TINYMCE_DETECTION_DELAY * @type Number * @default 500 * @private */ TINYMCE_DETECTION_DELAY: 500, /** * The number of times to try redetecting TinyMCE. * * @property TINYMCE_DETECTION_REPEATS * @type Number * @default 20 * @private */ TINYMCE_DETECTION_REPEATS: 20, /** * The delay (in milliseconds) between checking hidden input fields. * * @property WATCH_HIDDEN_DELAY * @type Number * @default 1000 * @private */ WATCH_HIDDEN_DELAY: 1000, /** * The number of failures to ignore before notifying the user. * * @property FAILURES_BEFORE_NOTIFY * @type Number * @default 1 * @private */ FAILURES_BEFORE_NOTIFY: 1, /** * The value to use when resetting the successful save counter. * * @property FIRST_SUCCESSFUL_SAVE * @static * @type Number * @default -1 * @private */ FIRST_SUCCESSFUL_SAVE: -1, /** * The selectors used throughout this class. * * @property SELECTORS * @private * @type Object * @static */ SELECTORS: { QUIZ_FORM: '#responseform', VALUE_CHANGE_ELEMENTS: 'input, textarea, [contenteditable="true"]', CHANGE_ELEMENTS: 'input, select', HIDDEN_INPUTS: 'input[type=hidden]', CONNECTION_ERROR: '#connection-error', CONNECTION_OK: '#connection-ok' }, /** * The script which handles the autosaves. * * @property AUTOSAVE_HANDLER * @type String * @default M.cfg.wwwroot + '/mod/quiz/autosave.ajax.php' * @private */ AUTOSAVE_HANDLER: M.cfg.wwwroot + '/mod/quiz/autosave.ajax.php', /** * The delay (in milliseconds) between a change being made, and it being auto-saved. * * @property delay * @type Number * @default 120000 * @private */ delay: 120000, /** * A Node reference to the form we are monitoring. * * @property form * @type Node * @default null */ form: null, /** * Whether the form has been modified since the last save started. * * @property dirty * @type boolean * @default false */ dirty: false, /** * Timer object for the delay between form modifaction and the save starting. * * @property delay_timer * @type Object * @default null * @private */ delay_timer: null, /** * Y.io transaction for the save ajax request. * * @property save_transaction * @type object * @default null */ save_transaction: null, /** * Failed saves count. * * @property savefailures * @type Number * @default 0 * @private */ savefailures: 0, /** * Properly bound key change handler. * * @property editor_change_handler * @type EventHandle * @default null * @private */ editor_change_handler: null, /** * Record of the value of all the hidden fields, last time they were checked. * * @property hidden_field_values * @type Object * @default {} */ hidden_field_values: {}, /** * Initialise the autosave code. * * @method init * @param {Number} delay the delay, in seconds, between a change being detected, and * a save happening. */ init: function(delay) { this.form = Y.one(this.SELECTORS.QUIZ_FORM); if (!this.form) { Y.log('No response form found. Why did you try to set up autosave?', 'debug', 'moodle-mod_quiz-autosave'); return; } this.delay = delay * 1000; this.form.delegate('valuechange', this.value_changed, this.SELECTORS.VALUE_CHANGE_ELEMENTS, this); this.form.delegate('change', this.value_changed, this.SELECTORS.CHANGE_ELEMENTS, this); this.form.on('submit', this.stop_autosaving, this); require(['core_form/events'], function(FormEvent) { window.addEventListener(FormEvent.eventTypes.uploadChanged, this.value_changed.bind(this)); }.bind(this)); this.init_tinymce(this.TINYMCE_DETECTION_REPEATS); this.save_hidden_field_values(); this.watch_hidden_fields(); }, save_hidden_field_values: function() { this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(hidden) { var name = hidden.get('name'); if (!name) { return; } this.hidden_field_values[name] = hidden.get('value'); }, this); }, watch_hidden_fields: function() { this.detect_hidden_field_changes(); Y.later(this.WATCH_HIDDEN_DELAY, this, this.watch_hidden_fields); }, detect_hidden_field_changes: function() { this.form.all(this.SELECTORS.HIDDEN_INPUTS).each(function(hidden) { var name = hidden.get('name'), value = hidden.get('value'); if (!name) { return; } if (!(name in this.hidden_field_values) || value !== this.hidden_field_values[name]) { this.hidden_field_values[name] = value; this.value_changed({target: hidden}); } }, this); }, /** * Initialise watching of TinyMCE specifically. * * Because TinyMCE might load slowly, and after us, we need to keep * trying, until we detect TinyMCE is there, or enough time has passed. * This is based on the TINYMCE_DETECTION_DELAY and * TINYMCE_DETECTION_REPEATS properties. * * * @method init_tinymce * @param {Number} repeatcount The number of attempts made so far. */ init_tinymce: function(repeatcount) { if (typeof window.tinyMCE === 'undefined') { if (repeatcount > 0) { Y.later(this.TINYMCE_DETECTION_DELAY, this, this.init_tinymce, [repeatcount - 1]); } else { Y.log('Gave up looking for TinyMCE.', 'debug', 'moodle-mod_quiz-autosave'); } return; } Y.log('Found TinyMCE.', 'debug', 'moodle-mod_quiz-autosave'); this.editor_change_handler = Y.bind(this.editor_changed, this); if (window.tinyMCE.onAddEditor) { window.tinyMCE.onAddEditor.add(Y.bind(this.init_tinymce_editor, this)); } else if (window.tinyMCE.on) { var startSaveTimer = this.start_save_timer_if_necessary.bind(this); window.tinyMCE.on('AddEditor', function(event) { event.editor.on('Change Undo Redo keydown', startSaveTimer); }); // One or more editors might already have been added, so we have to attach // the event handlers to these as well. window.tinyMCE.get().forEach(function(editor) { editor.on('Change Undo Redo keydown', startSaveTimer); }); } }, /** * Initialise watching of a specific TinyMCE editor. * * @method init_tinymce_editor * @param {EventFacade} e * @param {Object} editor The TinyMCE editor object */ init_tinymce_editor: function(e, editor) { Y.log('Found TinyMCE editor ' + editor.id + '.', 'debug', 'moodle-mod_quiz-autosave'); editor.onChange.add(this.editor_change_handler); editor.onRedo.add(this.editor_change_handler); editor.onUndo.add(this.editor_change_handler); editor.onKeyDown.add(this.editor_change_handler); }, value_changed: function(e) { var name = e.target.getAttribute('name'); if (name === 'thispage' || name === 'scrollpos' || (name && name.match(/_:flagged$/))) { return; // Not interesting. } // Fallback to the ID when the name is not present (in the case of content editable). name = name || '#' + e.target.getAttribute('id'); Y.log('Detected a value change in element ' + name + '.', 'debug', 'moodle-mod_quiz-autosave'); this.start_save_timer_if_necessary(); }, editor_changed: function(editor) { Y.log('Detected a value change in editor ' + editor.id + '.', 'debug', 'moodle-mod_quiz-autosave'); this.start_save_timer_if_necessary(); }, start_save_timer_if_necessary: function() { this.dirty = true; if (this.delay_timer || this.save_transaction) { // Already counting down or daving. return; } this.start_save_timer(); }, start_save_timer: function() { this.cancel_delay(); this.delay_timer = Y.later(this.delay, this, this.save_changes); }, cancel_delay: function() { if (this.delay_timer && this.delay_timer !== true) { this.delay_timer.cancel(); } this.delay_timer = null; }, save_changes: function() { this.cancel_delay(); this.dirty = false; if (this.is_time_nearly_over()) { Y.log('No more saving, time is nearly over.', 'debug', 'moodle-mod_quiz-autosave'); this.stop_autosaving(); return; } Y.log('Doing a save.', 'debug', 'moodle-mod_quiz-autosave'); if (typeof window.tinyMCE !== 'undefined') { window.tinyMCE.triggerSave(); } // YUI io.form incorrectly (in my opinion) sends the value of all submit // buttons in the ajax request. We don't want any submit buttons. // Therefore, temporarily change the type. // (Yes, this is a nasty hack. One day this will be re-written as AMD, hopefully). var allsubmitbuttons = this.form.all('input[type=submit], button[type=submit]'); allsubmitbuttons.setAttribute('type', 'button'); this.save_transaction = Y.io(this.AUTOSAVE_HANDLER, { method: 'POST', form: {id: this.form}, on: { success: this.save_done, failure: this.save_failed }, context: this }); // Change the button types back. allsubmitbuttons.setAttribute('type', 'submit'); }, save_done: function(transactionid, response) { var autosavedata = JSON.parse(response.responseText); if (autosavedata.status !== 'OK') { // Because IIS is useless, Moodle can't send proper HTTP response // codes, so we have to detect failures manually. this.save_failed(transactionid, response); return; } if (typeof autosavedata.timeleft !== 'undefined') { Y.log('Updating timer: ' + autosavedata.timeleft + ' seconds remain.', 'debug', 'moodle-mod_quiz-timer'); M.mod_quiz.timer.updateEndTime(autosavedata.timeleft); } this.update_saved_time_display(); Y.log('Save completed.', 'debug', 'moodle-mod_quiz-autosave'); this.save_transaction = null; if (this.dirty) { Y.log('Dirty after save.', 'debug', 'moodle-mod_quiz-autosave'); this.start_save_timer(); } if (this.savefailures > 0) { Y.one(this.SELECTORS.CONNECTION_ERROR).hide(); Y.one(this.SELECTORS.CONNECTION_OK).show(); this.savefailures = this.FIRST_SUCCESSFUL_SAVE; } else if (this.savefailures === this.FIRST_SUCCESSFUL_SAVE) { Y.one(this.SELECTORS.CONNECTION_OK).hide(); this.savefailures = 0; } }, save_failed: function() { Y.log('Save failed.', 'debug', 'moodle-mod_quiz-autosave'); this.save_transaction = null; // We want to retry soon. this.start_save_timer(); this.savefailures = Math.max(1, this.savefailures + 1); if (this.savefailures === this.FAILURES_BEFORE_NOTIFY) { Y.one(this.SELECTORS.CONNECTION_ERROR).show(); Y.one(this.SELECTORS.CONNECTION_OK).hide(); } }, /** * Inform the user that their answers have been saved. * * @method update_saved_time_display */ update_saved_time_display: function() { // We fetch the current language's preferred time format from the language pack. require(['core/user_date', 'core/notification'], function(UserDate, Notification) { UserDate.get([{ timestamp: Math.floor(Date.now() / 1000), format: M.util.get_string('strftimedatetimeshortaccurate', 'langconfig'), }]).then(function(dateStrs) { var infoDiv = Y.one('#mod_quiz_navblock .othernav .autosave_info'); infoDiv.set('text', M.util.get_string('lastautosave', 'quiz', dateStrs[0])); infoDiv.show(); return; }).catch(Notification.exception); }); }, is_time_nearly_over: function() { return M.mod_quiz.timer && M.mod_quiz.timer.endtime && (new Date().getTime() + 2 * this.delay) > M.mod_quiz.timer.endtime; }, stop_autosaving: function() { this.cancel_delay(); this.delay_timer = true; if (this.save_transaction) { this.save_transaction.abort(); } } }; }, '@VERSION@', { "requires": [ "base", "node", "event", "event-valuechange", "node-event-delegate", "io-form", "datatype-date-format" ] }); managetemplates.min.js.map000064400000005623152267135340011625 0ustar00{"version":3,"file":"managetemplates.min.js","sources":["../src/managetemplates.js"],"sourcesContent":["/**\n * Template management code.\n *\n * @module quizaccess_seb/managetemplates\n * @copyright 2020 Dmitrii Metelkin \n */\ndefine(\n ['jquery', 'core/ajax', 'core/str', 'core/notification'],\n function($, ajax, str, notification) {\n var manager = {\n /**\n * Confirm removal of the specified template.\n *\n * @method removeTemplate\n * @param {EventFacade} e The EventFacade\n */\n removeTemplate: function(e) {\n e.preventDefault();\n var targetUrl = $(e.currentTarget).attr('href');\n str.get_strings([\n {\n key: 'confirmtemplateremovaltitle',\n component: 'quizaccess_seb'\n },\n {\n key: 'confirmtemplateremovalquestion',\n component: 'quizaccess_seb'\n },\n {\n key: 'yes',\n component: 'moodle'\n },\n {\n key: 'no',\n component: 'moodle'\n }\n ])\n .then(function(s) {\n notification.confirm(s[0], s[1], s[2], s[3], function() {\n window.location = targetUrl;\n });\n\n return;\n })\n .catch();\n },\n\n /**\n * Setup the template management UI.\n *\n * @method setup\n */\n setup: function() {\n $('body').delegate('[data-action=\"delete\"]', 'click', manager.removeTemplate);\n }\n };\n\n return /** @alias module:quizaccess_seb/managetemplates */ {\n /**\n * Setup the template management UI.\n *\n * @method setup\n */\n setup: manager.setup\n };\n });\n"],"names":["define","$","ajax","str","notification","manager","removeTemplate","e","preventDefault","targetUrl","currentTarget","attr","get_strings","key","component","then","s","confirm","window","location","catch","setup","delegate"],"mappings":"AAMAA,wCACI,CAAC,SAAU,YAAa,WAAY,sBACpC,SAASC,EAAGC,KAAMC,IAAKC,kBACfC,QAAU,CAOVC,eAAgB,SAASC,GACrBA,EAAEC,qBACEC,UAAYR,EAAEM,EAAEG,eAAeC,KAAK,QACxCR,IAAIS,YAAY,CACZ,CACIC,IAAY,8BACZC,UAAY,kBAEhB,CACID,IAAY,iCACZC,UAAY,kBAEhB,CACID,IAAY,MACZC,UAAY,UAEhB,CACID,IAAY,KACZC,UAAY,YAGnBC,MAAK,SAASC,GACXZ,aAAaa,QAAQD,EAAE,GAAIA,EAAE,GAAIA,EAAE,GAAIA,EAAE,IAAI,WACzCE,OAAOC,SAAWV,gBAKzBW,SAQLC,MAAO,WACHpB,EAAE,QAAQqB,SAAS,yBAA0B,QAASjB,QAAQC,wBAIX,CAMvDe,MAAOhB,QAAQgB"}validate_quiz_access.min.js.map000064400000013345152267135340012640 0ustar00{"version":3,"file":"validate_quiz_access.min.js","sources":["../src/validate_quiz_access.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 * Validate Safe Exam Browser access keys.\n *\n * @module quizaccess_seb/validate_quiz_access\n * @author Andrew Madden \n * @copyright 2021 Catalyst IT\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport Config from 'core/config';\nimport Notification from \"core/notification\";\nimport * as View from 'quizaccess_seb/view';\n\n// SafeExamBrowser object will be automatically initialized if using the SafeExamBrowser application.\nwindow.SafeExamBrowser = window.SafeExamBrowser || null;\n\n/**\n * Once the keys are fetched, action checking access.\n *\n * @param {init} cmid Value of course module id of the quiz.\n * @param {boolean} autoreconfigure Value of Moodle setting: quizaccess_seb/autoreconfigureseb.\n */\nconst safeExamBrowserKeysUpdated = (cmid, autoreconfigure = false) => {\n // Action opening up the quiz.\n isQuizAccessValid(cmid).then((response) => {\n // Show the alert for an extra second to allow user to see it.\n setTimeout(View.clearLoadingAlert, 1000);\n\n if (response.configkey && response.browserexamkey) {\n View.allowAccess();\n } else {\n // If autoreconfigureseb is enabled, attempt to reconfigure page with quiz settings.\n if (autoreconfigure === true && response.configkey === false) {\n reconfigureSafeExamBrowser(cmid);\n }\n setTimeout(View.showValidationFailedModal, 1000);\n }\n\n return response;\n }).catch(err => {\n Notification.exception(err);\n });\n};\n\n/**\n * Validate keys in Moodle backend.\n *\n * @param {init} cmid Value of course module id of the quiz.\n * @return {Promise}\n */\nconst isQuizAccessValid = (cmid) => {\n const request = {\n methodname: 'quizaccess_seb_validate_quiz_keys',\n args: {\n cmid: cmid,\n url: window.location.href,\n configkey: window.SafeExamBrowser.security.configKey,\n browserexamkey: window.SafeExamBrowser.security.browserExamKey\n },\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Check if the key is not yet set.\n *\n * @param {string} key config key or browser exam key.\n * @return {boolean}\n */\nconst isKeyEmpty = (key) => {\n // If the SafeExamBrowser object is defined, the default 'empty' value of the configKey and browserExamKey is ':'.\n return key === \":\";\n};\n\n/**\n * Reload Safe Exam Browser with current quiz configuration.\n *\n * @param {init} cmid Value of course module id of the quiz.\n */\nconst reconfigureSafeExamBrowser = (cmid) => {\n const domain = Config.wwwroot.replace(/^http/i, 'seb');\n const redirecturl = domain + '/mod/quiz/accessrule/seb/config.php?cmid=' + cmid;\n document.location.replace(redirecturl);\n};\n\n/**\n * Initialize the process of fetching the keys.\n *\n * @param {init} cmid Value of course module id of the quiz.\n * @param {boolean} autoreconfigure Value of Moodle setting: quizaccess_seb/autoreconfigureseb.\n */\nexport const init = async(cmid, autoreconfigure = false) => {\n // If the SafeExamBrowser object is instantiated, try and use it to fetch the access keys.\n if (window.SafeExamBrowser !== null) {\n await View.addLoadingAlert();\n // If the SEB keys are already set, we can call our callback directly.\n\n if (!isKeyEmpty(window.SafeExamBrowser.security.configKey) || !isKeyEmpty(window.SafeExamBrowser.security.browserExamKey)) {\n safeExamBrowserKeysUpdated(cmid, autoreconfigure);\n } else {\n window.SafeExamBrowser.security.updateKeys(safeExamBrowserKeysUpdated);\n }\n }\n};\n"],"names":["window","SafeExamBrowser","safeExamBrowserKeysUpdated","cmid","autoreconfigure","isQuizAccessValid","then","response","setTimeout","View","clearLoadingAlert","configkey","browserexamkey","allowAccess","reconfigureSafeExamBrowser","showValidationFailedModal","catch","err","exception","request","methodname","args","url","location","href","security","configKey","browserExamKey","Ajax","call","isKeyEmpty","key","redirecturl","Config","wwwroot","replace","document","async","addLoadingAlert","updateKeys"],"mappings":";;;;;;;;02BA8BAA,OAAOC,gBAAkBD,OAAOC,iBAAmB,WAQ7CC,2BAA6B,SAACC,UAAMC,wEAEtCC,kBAAkBF,MAAMG,MAAMC,WAE1BC,WAAWC,KAAKC,kBAAmB,KAE/BH,SAASI,WAAaJ,SAASK,eAC/BH,KAAKI,gBAGmB,IAApBT,kBAAmD,IAAvBG,SAASI,WACrCG,2BAA2BX,MAE/BK,WAAWC,KAAKM,0BAA2B,MAGxCR,YACRS,OAAMC,4BACQC,UAAUD,SAUzBZ,kBAAqBF,aACjBgB,QAAU,CACZC,WAAY,oCACZC,KAAM,CACFlB,KAAMA,KACNmB,IAAKtB,OAAOuB,SAASC,KACrBb,UAAWX,OAAOC,gBAAgBwB,SAASC,UAC3Cd,eAAgBZ,OAAOC,gBAAgBwB,SAASE,wBAIjDC,cAAKC,KAAK,CAACV,UAAU,IAS1BW,WAAcC,KAED,MAARA,IAQLjB,2BAA8BX,aAE1B6B,YADSC,gBAAOC,QAAQC,QAAQ,SAAU,OACnB,4CAA8ChC,KAC3EiC,SAASb,SAASY,QAAQH,4BASVK,eAAMlC,UAAMC,wEAEG,OAA3BJ,OAAOC,wBACDQ,KAAK6B,kBAGNR,WAAW9B,OAAOC,gBAAgBwB,SAASC,YAAeI,WAAW9B,OAAOC,gBAAgBwB,SAASE,gBAGtG3B,OAAOC,gBAAgBwB,SAASc,WAAWrC,4BAF3CA,2BAA2BC,KAAMC"}managetemplates.min.js000064400000001332152267135340011042 0ustar00define("quizaccess_seb/managetemplates",["jquery","core/ajax","core/str","core/notification"],(function($,ajax,str,notification){var manager={removeTemplate:function(e){e.preventDefault();var targetUrl=$(e.currentTarget).attr("href");str.get_strings([{key:"confirmtemplateremovaltitle",component:"quizaccess_seb"},{key:"confirmtemplateremovalquestion",component:"quizaccess_seb"},{key:"yes",component:"moodle"},{key:"no",component:"moodle"}]).then((function(s){notification.confirm(s[0],s[1],s[2],s[3],(function(){window.location=targetUrl}))})).catch()},setup:function(){$("body").delegate('[data-action="delete"]',"click",manager.removeTemplate)}};return{setup:manager.setup}})); //# sourceMappingURL=managetemplates.min.js.mapvalidate_quiz_access.min.js000064400000006136152267135340012064 0ustar00define("quizaccess_seb/validate_quiz_access",["exports","core/ajax","core/config","core/notification","quizaccess_seb/view"],(function(_exports,_ajax,_config,_notification,View){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Validate Safe Exam Browser access keys. * * @module quizaccess_seb/validate_quiz_access * @author Andrew Madden * @copyright 2021 Catalyst IT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_config=_interopRequireDefault(_config),_notification=_interopRequireDefault(_notification),View=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(View),window.SafeExamBrowser=window.SafeExamBrowser||null;const safeExamBrowserKeysUpdated=function(cmid){let autoreconfigure=arguments.length>1&&void 0!==arguments[1]&&arguments[1];isQuizAccessValid(cmid).then((response=>(setTimeout(View.clearLoadingAlert,1e3),response.configkey&&response.browserexamkey?View.allowAccess():(!0===autoreconfigure&&!1===response.configkey&&reconfigureSafeExamBrowser(cmid),setTimeout(View.showValidationFailedModal,1e3)),response))).catch((err=>{_notification.default.exception(err)}))},isQuizAccessValid=cmid=>{const request={methodname:"quizaccess_seb_validate_quiz_keys",args:{cmid:cmid,url:window.location.href,configkey:window.SafeExamBrowser.security.configKey,browserexamkey:window.SafeExamBrowser.security.browserExamKey}};return _ajax.default.call([request])[0]},isKeyEmpty=key=>":"===key,reconfigureSafeExamBrowser=cmid=>{const redirecturl=_config.default.wwwroot.replace(/^http/i,"seb")+"/mod/quiz/accessrule/seb/config.php?cmid="+cmid;document.location.replace(redirecturl)};_exports.init=async function(cmid){let autoreconfigure=arguments.length>1&&void 0!==arguments[1]&&arguments[1];null!==window.SafeExamBrowser&&(await View.addLoadingAlert(),isKeyEmpty(window.SafeExamBrowser.security.configKey)&&isKeyEmpty(window.SafeExamBrowser.security.browserExamKey)?window.SafeExamBrowser.security.updateKeys(safeExamBrowserKeysUpdated):safeExamBrowserKeysUpdated(cmid,autoreconfigure))}})); //# sourceMappingURL=validate_quiz_access.min.js.mapmodal_events.min.js000064400000000725152267210130010347 0ustar00define("core/modal_events",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={shown:"modal:shown",hidden:"modal:hidden",destroyed:"modal:destroyed",bodyRendered:"modal:bodyRendered",outsideClick:"modal:outsideClick",save:"modal-save-cancel:save",delete:"modal-delete-cancel:delete",cancel:"modal-save-cancel:cancel"},_exports.default})); //# sourceMappingURL=modal_events.min.js.mapdatafilter.min.js000064400000024427152267210130010013 0ustar00define("core/datafilter",["exports","core/datafilter/filtertype","core/str","core/notification","core/pending","core/datafilter/selectors","core/templates","core/custom_interaction_events","jquery"],(function(_exports,_filtertype,_str,_notification,_pending,_selectors,_templates,_custom_interaction_events,_jquery){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filtertype=_interopRequireDefault(_filtertype),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_templates=_interopRequireDefault(_templates),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_jquery=_interopRequireDefault(_jquery);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}return _exports.default=class{constructor(filterSet,applyCallback){this.filterSet=filterSet,this.applyCallback=applyCallback,this.activeFilters={}}init(){this.filterSet.querySelector(_selectors.default.filterset.region).addEventListener("click",(e=>{e.target.closest(_selectors.default.filterset.actions.addRow)&&(e.preventDefault(),this.addFilterRow()),e.target.closest(_selectors.default.filterset.actions.applyFilters)&&(e.preventDefault(),this.updateTableFromFilter()),e.target.closest(_selectors.default.filterset.actions.resetFilters)&&(e.preventDefault(),this.removeAllFilters())})),this.filterSet.querySelector(_selectors.default.filterset.regions.filterlist).addEventListener("click",(e=>{e.target.closest(_selectors.default.filter.actions.remove)&&(e.preventDefault(),this.removeOrReplaceFilterRow(e.target.closest(_selectors.default.filter.region),!0))}));let filterRegion=(0,_jquery.default)(this.getFilterRegion());_custom_interaction_events.default.define(filterRegion,[_custom_interaction_events.default.events.accessibleChange]),filterRegion.on(_custom_interaction_events.default.events.accessibleChange,(e=>{const typeField=e.target.closest(_selectors.default.filter.fields.type);if(typeField&&typeField.value){const filter=e.target.closest(_selectors.default.filter.region);this.addFilter(filter,typeField.value)}})),this.filterSet.querySelector(_selectors.default.filterset.fields.join).addEventListener("change",(e=>{this.filterSet.dataset.filterverb=e.target.value}))}getFilterRegion(){return this.filterSet.querySelector(_selectors.default.filterset.regions.filterlist)}addFilterRow(){var _filterdata$rownum;let filterdata=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const pendingPromise=new _pending.default("core/datafilter:addFilterRow"),rownum=null!==(_filterdata$rownum=filterdata.rownum)&&void 0!==_filterdata$rownum?_filterdata$rownum:1+this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).length;return _templates.default.renderForPromise("core/datafilter/filter_row",{rownumber:rownum}).then((_ref=>{let{html:html,js:js}=_ref;return _templates.default.appendNodeContents(this.getFilterRegion(),html,js)})).then((filterRow=>{const typeList=this.filterSet.querySelector(_selectors.default.data.typeList);return filterRow.forEach((contentNode=>{const contentTypeList=contentNode.querySelector(_selectors.default.filter.fields.type);contentTypeList&&(contentTypeList.innerHTML=typeList.innerHTML)})),filterRow})).then((filterRow=>(this.updateFiltersOptions(),filterRow))).then((result=>(pendingPromise.resolve(),filterdata.filtertype&&result.forEach((filter=>{this.addFilter(filter,filterdata.filtertype,filterdata.values,filterdata.jointype,filterdata.filteroptions)})),result))).catch(_notification.default.exception)}getFilterDataSource(filterType){return this.filterSet.querySelector(_selectors.default.filterset.regions.datasource).querySelector(_selectors.default.data.fields.byName(filterType))}async addFilter(filterRow,filterType,initialFilterValues,filterJoin,filterOptions){filterRow.dataset.filterType=filterType;const filterDataNode=this.getFilterDataSource(filterType);let Filter=_filtertype.default;if(filterDataNode.dataset.filterTypeClass)try{Filter=await("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([filterDataNode.dataset.filterTypeClass],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(filterDataNode.dataset.filterTypeClass)):Promise.resolve(_systemImportTransformerGlobalIdentifier[filterDataNode.dataset.filterTypeClass]))}catch(error){_notification.default.exception(error)}this.activeFilters[filterType]=new Filter(filterType,this.filterSet,initialFilterValues,filterOptions);const typeField=filterRow.querySelector(_selectors.default.filter.fields.type);typeField.value=filterType,typeField.disabled="disabled",this.updateJoinList(JSON.parse(filterDataNode.dataset.joinList),filterRow);const joinField=filterRow.querySelector(_selectors.default.filter.fields.join);return isNaN(filterJoin)||(joinField.value=filterJoin),this.updateFiltersOptions(),this.activeFilters[filterType]}getFilterObject(name){return this.activeFilters[name]}removeOrReplaceFilterRow(filterRow,refreshContent){1===this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).length?this.replaceFilterRow(filterRow,refreshContent):this.removeFilterRow(filterRow,refreshContent)}async removeFilterRow(filterRow){let refreshContent=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if(filterRow.querySelector(_selectors.default.data.required))return;const hasFilterValue=!!filterRow.querySelector(_selectors.default.filter.fields.type).value;this.removeFilterObject(filterRow.dataset.filterType),filterRow.remove(),this.updateFiltersOptions(),hasFilterValue&&refreshContent&&this.updateTableFromFilter();const filterLegends=await this.getAvailableFilterLegends();this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).forEach(((filterRow,index)=>{filterRow.querySelector("legend").innerText=filterLegends[index]}))}replaceFilterRow(filterRow){let refreshContent=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],rowNum=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;if(!filterRow.querySelector(_selectors.default.data.required))return this.removeFilterObject(filterRow.dataset.filterType),_templates.default.renderForPromise("core/datafilter/filter_row",{rownumber:rowNum}).then((_ref2=>{let{html:html,js:js}=_ref2;return _templates.default.replaceNode(filterRow,html,js)})).then((filterRow=>{const typeList=this.filterSet.querySelector(_selectors.default.data.typeList);return filterRow.forEach((contentNode=>{const contentTypeList=contentNode.querySelector(_selectors.default.filter.fields.type);contentTypeList&&(contentTypeList.innerHTML=typeList.innerHTML)})),filterRow})).then((filterRow=>(this.updateFiltersOptions(),filterRow))).then((filterRow=>refreshContent?this.updateTableFromFilter():filterRow)).catch(_notification.default.exception)}removeFilterObject(filterName){if(filterName){const filter=this.getFilterObject(filterName);filter&&(filter.tearDown(),delete this.activeFilters[filterName])}}removeAllFilters(){return this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).forEach((filterRow=>this.removeOrReplaceFilterRow(filterRow,!1))),this.updateTableFromFilter()}removeEmptyFilters(){this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).forEach((filterRow=>{filterRow.querySelector(_selectors.default.filter.fields.type).value||this.removeOrReplaceFilterRow(filterRow,!1)}))}updateFiltersOptions(){const filters=this.getFilterRegion().querySelectorAll(_selectors.default.filter.region);filters.forEach((filterRow=>{filterRow.querySelectorAll(_selectors.default.filter.fields.type+" option").forEach((option=>{option.value===filterRow.dataset.filterType?(option.classList.remove("hidden"),option.disabled=!1):this.activeFilters[option.value]?(option.classList.add("hidden"),option.disabled=!0):(option.classList.remove("hidden"),option.disabled=!1)}))}));const addRowButton=this.filterSet.querySelector(_selectors.default.filterset.actions.addRow);this.filterSet.querySelectorAll(_selectors.default.data.fields.all).length<=filters.length?addRowButton.setAttribute("disabled","disabled"):addRowButton.removeAttribute("disabled"),1===filters.length?(this.filterSet.querySelector(_selectors.default.filterset.regions.filtermatch).classList.add("hidden"),this.filterSet.querySelector(_selectors.default.filterset.fields.join).value=2,this.filterSet.dataset.filterverb=2):this.filterSet.querySelector(_selectors.default.filterset.regions.filtermatch).classList.remove("hidden")}updateTableFromFilter(){let validate=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];const pendingPromise=new _pending.default("core/datafilter:updateTableFromFilter"),filters={};let valid=!0;Object.values(this.activeFilters).forEach((filter=>{validate&&(valid=valid&&filter.validate()),filters[filter.filterValue.name]=filter.filterValue})),validate&&(valid=valid&&document.querySelector(_selectors.default.filter.region).closest("form").reportValidity()),this.applyCallback&&valid?this.applyCallback(filters,pendingPromise):pendingPromise.resolve()}async getAvailableFilterLegends(){const maxFilters=document.querySelector(_selectors.default.data.typeListSelect).length-1;let requests=[];[...Array(maxFilters)].forEach(((_,rowIndex)=>{requests.push({key:"filterrowlegend",component:"core",param:rowIndex+1})}));return await(0,_str.getStrings)(requests).then((fetchedStrings=>fetchedStrings)).catch(_notification.default.exception)}updateJoinList(filterJoinList,filterRow){const regularJoinList=[0,1,2];if(0!==filterJoinList.length){const joinField=filterRow.querySelector(_selectors.default.filter.fields.join);regularJoinList.forEach((join=>{filterJoinList.includes(join)||(joinField.options[join].classList.add("hidden"),joinField.options[join].disabled=!0)})),joinField.options.forEach(((element,index)=>{element.disabled&&(joinField.options[index]=null)})),1===joinField.options.length&&(joinField.hidden=!0)}}},_exports.default})); //# sourceMappingURL=datafilter.min.js.maputils.min.js000064400000014073152267210130007030 0ustar00define("editor_tiny/utils",["exports","core/templates","./options","core/str"],(function(_exports,_templates,_options,_str){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateEditorState=_exports.removeToolbarButtons=_exports.removeToolbarButton=_exports.removeSubmenuItem=_exports.removeMenubarItems=_exports.removeMenubarItem=_exports.getPluginMetadata=_exports.getImagePath=_exports.getDocumentationLink=_exports.getButtonImage=_exports.ensureEditorIsValid=_exports.displayFilepicker=_exports.addToolbarSection=_exports.addToolbarButtons=_exports.addToolbarButton=_exports.addQuickbarsToolbarItem=_exports.addMenubarItem=_exports.addDynamicToolbarMenu=_exports.addContextmenuItem=void 0;const getImagePath=function(identifier){let component=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"editor_tiny";return Promise.resolve(M.util.image_url(identifier,component))};_exports.getImagePath=getImagePath;_exports.getButtonImage=async function(identifier){let component=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"editor_tiny";return(0,_templates.renderForPromise)("editor_tiny/toolbar_button",{image:await getImagePath(identifier,component)})};_exports.displayFilepicker=(editor,filetype)=>new Promise(((resolve,reject)=>{const configuration=(0,_options.getFilePicker)(editor,filetype);if(configuration){const options={...configuration,formcallback:resolve,previousActiveElement:document.activeElement};M.core_filepicker.show(Y,options)}else reject("Unknown filetype ".concat(filetype))}));_exports.addToolbarButton=function(toolbar,section,button){let after=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;if(!toolbar)return[{name:section,items:[button]}];const mutatedToolbar=JSON.parse(JSON.stringify(toolbar));return mutatedToolbar.map((item=>{if(item.name===section)if(after){let index=item.items.findIndex((value=>value==after));-1!==index&&item.items.splice(index+1,0,button)}else item.items.push(button);return item}))};_exports.addToolbarButtons=(toolbar,section,buttons)=>{if(!toolbar)return[{name:section,items:buttons}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&buttons.forEach((button=>item.items.push(button))),item)))};_exports.addToolbarSection=function(toolbar,name,relativeTo){let append=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];const newSection={name:name,items:[]},sectionInserted=toolbar.some(((section,index)=>section.name===relativeTo&&(append?toolbar.splice(index+1,0,newSection):toolbar.splice(index,0,newSection),!0)));return sectionInserted||(append?toolbar.push(newSection):toolbar.unshift(newSection)),toolbar};_exports.addMenubarItem=function(menubar,section,menuitem){let after=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;if(!menubar){({})[section]={title:section,items:menuitem}}const mutatedMenubar=JSON.parse(JSON.stringify(menubar));return Array.from(Object.entries(mutatedMenubar)).forEach((_ref=>{let[name,menu]=_ref;if(name===section)if(after){let index=menu.items.indexOf(after);-1!==index&&(index+=after.length,menu.items=menu.items.slice(0,index)+" ".concat(menuitem)+menu.items.slice(index))}else menu.items="".concat(menu.items," ").concat(menuitem)})),mutatedMenubar};_exports.addContextmenuItem=function(contextmenu){const contextmenuItems=(contextmenu||"").split(" ");for(var _len=arguments.length,menuitems=new Array(_len>1?_len-1:0),_key=1;_key<_len;_key++)menuitems[_key-1]=arguments[_key];return contextmenuItems.concat(menuitems).filter((item=>""!==item)).join(" ")};_exports.addQuickbarsToolbarItem=function(quicktoolbar){const quicktoolbarItems=(quicktoolbar||"").split(" ");for(var _len2=arguments.length,toolbaritems=new Array(_len2>1?_len2-1:0),_key2=1;_key2<_len2;_key2++)toolbaritems[_key2-1]=arguments[_key2];return quicktoolbarItems.concat(toolbaritems).filter((item=>""!==item)).join(" ")};_exports.addDynamicToolbarMenu=(editor,items,menuName,menuIcon,menuIconText,singleIcon,singleIconText,singleAction)=>{items.length>1?editor.ui.registry.addMenuButton(menuName,{icon:menuIcon,tooltip:menuIconText,fetch:callback=>callback("".concat(items.join(" ")))}):editor.ui.registry.addButton(menuName,{icon:singleIcon,tooltip:singleIconText,onAction:singleAction})};const getDocumentationLink=pluginName=>"https://docs.moodle.org/en/editor_tiny/".concat(pluginName);_exports.getDocumentationLink=getDocumentationLink;_exports.getPluginMetadata=async function(component,pluginName){let url=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const name=await(0,_str.getString)("pluginname",component);return{getMetadata:()=>({name:name,url:null!=url?url:getDocumentationLink(pluginName)})}};_exports.ensureEditorIsValid=editor=>editor.getElement().isConnected?editor:null;_exports.removeToolbarButton=(toolbar,section,button)=>{if(!toolbar)return[{name:section,items:[button]}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&item.items.splice(item.items.indexOf(button),1),item)))};_exports.removeToolbarButtons=(toolbar,section,buttons)=>{if(!toolbar)return[{name:section,items:buttons}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&buttons.forEach((button=>item.items.splice(item.items.indexOf(button),1))),item)))};_exports.removeSubmenuItem=async(editor,section,submenuitem)=>{const menuItems=editor.ui.registry.getAll().menuItems[section],submenuitemtitle=await(0,_str.getString)(submenuitem,"editor_tiny");menuItems&&editor.ui.registry.addNestedMenuItem(section,{text:menuItems.text,getSubmenuItems:()=>{let newSubmenu=[];return menuItems.getSubmenuItems().forEach((item=>{item.text.trim()!=submenuitemtitle&&newSubmenu.push(item)})),newSubmenu}})};_exports.removeMenubarItem=(menubar,section,menuitem)=>(menubar[section].items=menubar[section].items.replace(menuitem,""),menubar);_exports.removeMenubarItems=(menubar,section,menuitems)=>{const regexPattern=new RegExp(menuitems.join("|"),"ig");return menubar[section].items=menubar[section].items.replace(regexPattern,""),menubar};_exports.updateEditorState=(editor,target)=>{target.hasAttribute("readonly")?editor.mode.set("readonly"):editor.mode.set("design")}})); //# sourceMappingURL=utils.min.js.mapchart_line.min.js000064400000001335152267210130007775 0ustar00/** * Chart line. * * @copyright 2016 Frédéric Massart - FMCorz.net * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @module core/chart_line */ define("core/chart_line",["core/chart_base"],(function(Base){function Line(){Base.prototype.constructor.apply(this,arguments)}return Line.prototype=Object.create(Base.prototype),Line.prototype.TYPE="line",Line.prototype._smooth=!1,Line.prototype.create=function(Klass,data){var chart=Base.prototype.create.apply(this,arguments);return chart.setSmooth(data.smooth),chart},Line.prototype.getSmooth=function(){return this._smooth},Line.prototype.setSmooth=function(smooth){this._smooth=Boolean(smooth)},Line})); //# sourceMappingURL=chart_line.min.js.mapsortable_list.min.js.map000064400000126014152267210130011311 0ustar00{"version":3,"file":"sortable_list.min.js","sources":["../src/sortable_list.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 * A javascript module to handle list items drag and drop\n *\n * Example of usage:\n *\n * Create a list (for example `