ÿØÿà 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-Ї Ç filtertype.min.js.map000064400000025141152267246560010651 0ustar00{"version":3,"file":"filtertype.min.js","sources":["../../src/datafilter/filtertype.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 * Base Filter class for a filter type in the filter UI.\n *\n * @module core/datafilter/filtertype\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Autocomplete from 'core/form-autocomplete';\nimport Selectors from 'core/datafilter/selectors';\nimport {getString} from 'core/str';\nimport Notification from 'core/notification';\n\n/**\n * Fetch all checked options in the select.\n *\n * This is a poor-man's polyfill for select.selectedOptions, which is not available in IE11.\n *\n * @param {HTMLSelectElement} select\n * @returns {HTMLOptionElement[]} All selected options\n */\nconst getOptionsForSelect = select => {\n return select.querySelectorAll(':checked');\n};\n\nexport default class {\n\n /**\n * Constructor for a new filter.\n *\n * @param {String} filterType The type of filter that this relates to\n * @param {HTMLElement} rootNode The root node for the participants filterset\n * @param {Array} initialValues The initial values for the selector\n */\n constructor(filterType, rootNode, initialValues) {\n this.filterType = filterType;\n this.rootNode = rootNode;\n\n this.addValueSelector(initialValues).then(() => {\n const filterRoot = this.filterRoot;\n if (filterRoot && filterRoot.querySelector(Selectors.data.required)) {\n filterRoot.querySelector(Selectors.filter.actions.remove).remove();\n }\n return filterRoot;\n }).catch(Notification.exception);\n\n }\n\n /**\n * Perform any tear-down for this filter type.\n */\n tearDown() {\n // eslint-disable-line no-empty-function\n }\n\n /**\n * Get the placeholder to use when showing the value selector.\n *\n * @return {Promise} Resolving to a String\n */\n get placeholder() {\n return getString('placeholdertypeorselect', 'core');\n }\n\n /**\n * Whether to show suggestions in the autocomplete.\n *\n * @return {Boolean}\n */\n get showSuggestions() {\n return true;\n }\n\n /**\n * Add the value selector to the filter row.\n *\n * @param {Array} initialValues\n * @return {Promise}\n */\n async addValueSelector(initialValues = []) {\n const filterValueNode = this.getFilterValueNode();\n\n // Copy the data in place.\n const sourceDataNode = this.getSourceDataForFilter();\n if (!sourceDataNode) {\n throw new Error('No source data for filter.');\n }\n filterValueNode.innerHTML = sourceDataNode.outerHTML;\n\n const dataSource = filterValueNode.querySelector('select');\n\n // Set an ID for this filter value element.\n dataSource.id = 'filter-value-' + dataSource.getAttribute('data-field-name');\n\n // Create a hidden label for the filter value.\n const filterValueLabel = document.createElement('label');\n filterValueLabel.setAttribute('for', dataSource.id);\n filterValueLabel.classList.add('visually-hidden');\n filterValueLabel.innerText = dataSource.getAttribute('data-field-title');\n\n // Append this label to the filter value container.\n filterValueNode.appendChild(filterValueLabel);\n\n // If there are any initial values then attempt to apply them.\n initialValues.forEach(filterValue => {\n let selectedOption = dataSource.querySelector(`option[value=\"${filterValue}\"]`);\n if (selectedOption) {\n selectedOption.selected = true;\n } else if (!this.showSuggestions) {\n selectedOption = document.createElement('option');\n selectedOption.value = filterValue;\n selectedOption.innerHTML = filterValue;\n selectedOption.selected = true;\n\n dataSource.append(selectedOption);\n }\n });\n\n return Autocomplete.enhance(\n // The source select element.\n dataSource,\n\n // Whether to allow 'tags' (custom entries).\n dataSource.dataset.allowCustom == \"1\",\n\n // We do not require AJAX at all as standard.\n null,\n\n // The string to use as a placeholder.\n await this.placeholder,\n\n // Disable case sensitivity on searches.\n false,\n\n // Show suggestions.\n this.showSuggestions,\n\n // Do not override the 'no suggestions' string.\n null,\n\n // Close the suggestions if this is not a multi-select.\n !dataSource.multiple,\n\n // Template overrides.\n {\n items: 'core/datafilter/autocomplete_selection_items',\n layout: 'core/datafilter/autocomplete_layout',\n selection: 'core/datafilter/autocomplete_selection',\n }\n );\n }\n\n /**\n * Get the root node for this filter.\n *\n * @returns {HTMLElement}\n */\n get filterRoot() {\n return this.rootNode.querySelector(Selectors.filter.byName(this.filterType));\n }\n\n /**\n * Get the possible data for this filter type.\n *\n * @returns {Array}\n */\n getSourceDataForFilter() {\n const filterDataNode = this.rootNode.querySelector(Selectors.filterset.regions.datasource);\n\n return filterDataNode.querySelector(Selectors.data.fields.byName(this.filterType));\n }\n\n /**\n * Get the HTMLElement which contains the value selector.\n *\n * @returns {HTMLElement}\n */\n getFilterValueNode() {\n return this.filterRoot.querySelector(Selectors.filter.regions.values);\n }\n\n /**\n * Get the name of this filter.\n *\n * @returns {String}\n */\n get name() {\n return this.filterType;\n }\n\n /**\n * Get the type of join specified.\n *\n * @returns {Number}\n */\n get jointype() {\n return parseInt(this.filterRoot.querySelector(Selectors.filter.fields.join).value, 10);\n }\n\n /**\n * Get the list of raw values for this filter type.\n *\n * @returns {Array}\n */\n get rawValues() {\n const filterValueNode = this.getFilterValueNode();\n const filterValueSelect = filterValueNode.querySelector('select');\n\n return Object.values(getOptionsForSelect(filterValueSelect)).map(option => option.value);\n }\n\n /**\n * Get the list of values for this filter type.\n *\n * @returns {Array}\n */\n get values() {\n return this.rawValues.map(option => parseInt(option, 10));\n }\n\n /**\n * Get options specific to this filter type.\n *\n * @returns {Array} of {name:, value:} objects\n */\n get filterOptions() {\n return [];\n }\n\n /**\n * Get the composed value for this filter.\n *\n * @returns {Object}\n */\n get filterValue() {\n return {\n name: this.name,\n jointype: this.jointype,\n values: this.values,\n filteroptions: this.filterOptions,\n };\n }\n\n /**\n * Validate the entered values for the filter.\n *\n * Return true if all values are valid, false otherwise.\n * To display validation errors, use the standard setCustomValidity() and reportValidity() methods.\n *\n * @return {boolean}\n */\n validate() {\n return true;\n }\n}\n"],"names":["constructor","filterType","rootNode","initialValues","addValueSelector","then","filterRoot","this","querySelector","Selectors","data","required","filter","actions","remove","catch","Notification","exception","tearDown","placeholder","showSuggestions","filterValueNode","getFilterValueNode","sourceDataNode","getSourceDataForFilter","Error","innerHTML","outerHTML","dataSource","id","getAttribute","filterValueLabel","document","createElement","setAttribute","classList","add","innerText","appendChild","forEach","filterValue","selectedOption","selected","value","append","Autocomplete","enhance","dataset","allowCustom","multiple","items","layout","selection","byName","filterset","regions","datasource","fields","values","name","jointype","parseInt","join","rawValues","filterValueSelect","Object","select","querySelectorAll","map","option","filterOptions","filteroptions","validate"],"mappings":";;;;;;;iRAgDIA,YAAYC,WAAYC,SAAUC,oBACzBF,WAAaA,gBACbC,SAAWA,cAEXE,iBAAiBD,eAAeE,MAAK,WAChCC,WAAaC,KAAKD,kBACpBA,YAAcA,WAAWE,cAAcC,mBAAUC,KAAKC,WACtDL,WAAWE,cAAcC,mBAAUG,OAAOC,QAAQC,QAAQA,SAEvDR,cACRS,MAAMC,sBAAaC,WAO1BC,YASIC,yBACO,kBAAU,0BAA2B,QAQ5CC,6BACO,+BASYjB,qEAAgB,SAC7BkB,gBAAkBd,KAAKe,qBAGvBC,eAAiBhB,KAAKiB,6BACvBD,qBACK,IAAIE,MAAM,8BAEpBJ,gBAAgBK,UAAYH,eAAeI,gBAErCC,WAAaP,gBAAgBb,cAAc,UAGjDoB,WAAWC,GAAK,gBAAkBD,WAAWE,aAAa,yBAGpDC,iBAAmBC,SAASC,cAAc,gBAChDF,iBAAiBG,aAAa,MAAON,WAAWC,IAChDE,iBAAiBI,UAAUC,IAAI,mBAC/BL,iBAAiBM,UAAYT,WAAWE,aAAa,oBAGrDT,gBAAgBiB,YAAYP,kBAG5B5B,cAAcoC,SAAQC,kBACdC,eAAiBb,WAAWpB,sCAA+BgC,mBAC3DC,eACAA,eAAeC,UAAW,EAClBnC,KAAKa,kBACbqB,eAAiBT,SAASC,cAAc,UACxCQ,eAAeE,MAAQH,YACvBC,eAAef,UAAYc,YAC3BC,eAAeC,UAAW,EAE1Bd,WAAWgB,OAAOH,oBAInBI,0BAAaC,QAEhBlB,WAGkC,KAAlCA,WAAWmB,QAAQC,YAGnB,WAGMzC,KAAKY,aAGX,EAGAZ,KAAKa,gBAGL,MAGCQ,WAAWqB,SAGZ,CACIC,MAAO,+CACPC,OAAQ,sCACRC,UAAW,2CAUnB9C,wBACOC,KAAKL,SAASM,cAAcC,mBAAUG,OAAOyC,OAAO9C,KAAKN,aAQpEuB,gCAC2BjB,KAAKL,SAASM,cAAcC,mBAAU6C,UAAUC,QAAQC,YAEzDhD,cAAcC,mBAAUC,KAAK+C,OAAOJ,OAAO9C,KAAKN,aAQ1EqB,4BACWf,KAAKD,WAAWE,cAAcC,mBAAUG,OAAO2C,QAAQG,QAQ9DC,kBACOpD,KAAKN,WAQZ2D,sBACOC,SAAStD,KAAKD,WAAWE,cAAcC,mBAAUG,OAAO6C,OAAOK,MAAMnB,MAAO,IAQnFoB,sBAEMC,kBADkBzD,KAAKe,qBACad,cAAc,iBAEjDyD,OAAOP,QA3LMQ,OA2LqBF,kBA1LtCE,OAAOC,iBAAiB,cA0LkCC,KAAIC,QAAUA,OAAO1B,QA3L9DuB,IAAAA,OAmMpBR,oBACOnD,KAAKwD,UAAUK,KAAIC,QAAUR,SAASQ,OAAQ,MAQrDC,0BACO,GAQP9B,wBACO,CACHmB,KAAMpD,KAAKoD,KACXC,SAAUrD,KAAKqD,SACfF,OAAQnD,KAAKmD,OACba,cAAehE,KAAK+D,eAY5BE,kBACW"}filtertypes/courseid.min.js000064400000001500152267246560012066 0ustar00define("core/datafilter/filtertypes/courseid",["exports","core/datafilter/filtertype"],(function(_exports,_filtertype){var obj; /** * Course ID filter. * * @module core/datafilter/filtertypes/courseid * @copyright 2020 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filtertype=(obj=_filtertype)&&obj.__esModule?obj:{default:obj};class _default extends _filtertype.default{constructor(filterType,filterSet){super(filterType,filterSet)}async addValueSelector(){}get filterValue(){return{name:this.name,jointype:1,values:[parseInt(this.rootNode.dataset.tableCourseId,10)]}}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=courseid.min.js.mapfiltertypes/datetime.min.js000064400000012342152267246560012053 0ustar00define("core/datafilter/filtertypes/datetime",["exports","core/datafilter/filtertype","core/datafilter/selectors","core/templates","core/notification","core/str"],(function(_exports,_filtertype,_selectors,_templates,_notification,_str){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,_filtertype=_interopRequireDefault(_filtertype),_selectors=_interopRequireDefault(_selectors),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification);const MODES_before="before",MODES_after="after",MODES_between="between";class _default extends _filtertype.default{constructor(filterType,rootNode,initialValues){let filterOptions=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{mode:MODES_before};super(filterType,rootNode,initialValues),_defineProperty(this,"SELECTORS",{afterwrapper:".".concat(this.filterType,"-afterwrapper"),beforewrapper:".".concat(this.filterType,"-beforewrapper"),betweenwrapper:".".concat(this.filterType,"-betweenwrapper"),mode:"[data-filterfield=".concat(this.filterType,"-mode]")}),_defineProperty(this,"mode",null),this.addModeSelector(filterOptions.mode)}async getContext(initialValues){var _initialValues$,_initialValues$2;const sourceDataNode=this.getSourceDataForFilter(),defaultBefore=sourceDataNode.getElementsByTagName("option")[0].value,defaultAfter=sourceDataNode.getElementsByTagName("option")[1].value,title=sourceDataNode.getAttribute("data-field-title"),labels=await(0,_str.get_strings)([{key:"datetimefilterafter",component:"core",param:{title:title}},{key:"datetimefilterbefore",component:"core",param:{title:title}}]);return{filtertype:this.filterType,afterlabel:labels[0],beforelabel:labels[1],required:sourceDataNode.dataset.required,aftervalue:null!==(_initialValues$=initialValues[0])&&void 0!==_initialValues$?_initialValues$:defaultAfter,beforevalue:null!==(_initialValues$2=initialValues[1])&&void 0!==_initialValues$2?_initialValues$2:defaultBefore}}async addValueSelector(){let initialValues=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];const specificFilterSet=this.rootNode.querySelector(_selectors.default.filter.byName(this.filterType)),context=await this.getContext(initialValues),datetimeUi=await _templates.default.renderForPromise("core/datafilter/filtertypes/datetime_selector",context);return _templates.default.replaceNodeContents(specificFilterSet.querySelector(_selectors.default.filter.regions.values),datetimeUi.html,datetimeUi.js)}async addModeSelector(mode){const modeStrings=await(0,_str.get_strings)([{key:"selectdates"},{key:"filterdatebefore",component:"reportbuilder"},{key:"filterdateafter",component:"reportbuilder"},{key:"between"}]),context={label:modeStrings[0],filtertype:this.filterType,modeoptions:[{value:MODES_before,label:modeStrings[1],selected:mode===MODES_before?"selected":""},{value:MODES_after,label:modeStrings[2],selected:mode===MODES_after?"selected":""},{value:MODES_between,label:modeStrings[3],selected:mode===MODES_between?"selected":""}]},modeUi=await _templates.default.renderForPromise("core/datafilter/filtertypes/datetime_mode",context);this.getFilterValueNode().insertAdjacentHTML("beforebegin",modeUi.html);const modeSelect=this.filterRoot.querySelector(this.SELECTORS.mode);modeSelect.addEventListener("change",this.updateFieldVisibility.bind(this)),modeSelect.dispatchEvent(new Event("change"))}updateFieldVisibility(event){const filterValueNode=this.getFilterValueNode(),afterWrapper=filterValueNode.querySelector(this.SELECTORS.afterwrapper),beforeWrapper=filterValueNode.querySelector(this.SELECTORS.beforewrapper),betweenWrapper=filterValueNode.querySelector(this.SELECTORS.betweenwrapper),value=event.target.value;value===MODES_between?betweenWrapper.classList.remove("d-none"):betweenWrapper.classList.add("d-none"),value===MODES_after||value===MODES_between?afterWrapper.classList.remove("d-none"):afterWrapper.classList.add("d-none"),value===MODES_before||value===MODES_between?beforeWrapper.classList.remove("d-none"):beforeWrapper.classList.add("d-none")}get values(){return[this.filterRoot.querySelector('[data-filterfield="'.concat(this.name,'1"]')).value,this.filterRoot.querySelector('[data-filterfield="'.concat(this.name,'2"]')).value]}get filterOptions(){return[{name:"mode",value:this.filterRoot.querySelector(this.SELECTORS.mode).value}]}get filterValue(){return{name:this.name,jointype:this.jointype,values:this.values,filteroptions:this.filterOptions}}validate(){const mode=document.querySelector(this.SELECTORS.mode).value,before=document.querySelector(this.SELECTORS.beforewrapper+" input"),after=document.querySelector(this.SELECTORS.afterwrapper+" input");return after.setCustomValidity(""),!(mode===MODES_between&&after.value>=before.value)||((0,_str.get_strings)([{key:"invaliddatetimebetween",component:"error",param:{before:before.value,after:after.value}}]).then((strings=>(after.setCustomValidity(strings[0]),after.reportValidity(),strings))).catch(_notification.default.exception),!1)}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=datetime.min.js.mapfiltertypes/country.min.js000064400000001225152267246560011760 0ustar00define("core/datafilter/filtertypes/country",["exports","core/datafilter/filtertype"],(function(_exports,_filtertype){var obj; /** * Country filter * * @module core/datafilter/filtertypes/country * @copyright 2021 Paul Holden * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filtertype=(obj=_filtertype)&&obj.__esModule?obj:{default:obj};class _default extends _filtertype.default{get values(){return this.rawValues}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=country.min.js.mapfiltertypes/keyword.min.js.map000064400000003566152267246560012527 0ustar00{"version":3,"file":"keyword.min.js","sources":["../../../src/datafilter/filtertypes/keyword.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 * Keyword filter.\n *\n * @module core/datafilter/filtertypes/keyword\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Filter from 'core/datafilter/filtertype';\nimport {getString} from 'core/str';\n\nexport default class extends Filter {\n /**\n * For keywords the final value is an Array of strings.\n *\n * @returns {Object}\n */\n get values() {\n return this.rawValues;\n }\n\n /**\n * Get the placeholder to use when showing the value selector.\n *\n * @return {Promise} Resolving to a String\n */\n get placeholder() {\n return getString('placeholdertype', 'core_user');\n }\n\n /**\n * Whether to show suggestions in the autocomplete.\n *\n * @return {Boolean}\n */\n get showSuggestions() {\n return false;\n }\n}\n"],"names":["Filter","values","this","rawValues","placeholder","showSuggestions"],"mappings":";;;;;;;4KAyB6BA,oBAMrBC,oBACOC,KAAKC,UAQZC,yBACO,kBAAU,kBAAmB,aAQpCC,6BACO"}filtertypes/country.min.js.map000064400000002601152267246560012533 0ustar00{"version":3,"file":"country.min.js","sources":["../../../src/datafilter/filtertypes/country.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 * Country filter\n *\n * @module core/datafilter/filtertypes/country\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Filter from 'core/datafilter/filtertype';\n\nexport default class extends Filter {\n\n /**\n * For country the final value is an array of country code strings\n *\n * @return {Object}\n */\n get values() {\n return this.rawValues;\n }\n}\n"],"names":["Filter","values","this","rawValues"],"mappings":";;;;;;;4KAyB6BA,oBAOrBC,oBACOC,KAAKC"}filtertypes/keyword.min.js000064400000001421152267246560011737 0ustar00define("core/datafilter/filtertypes/keyword",["exports","core/datafilter/filtertype","core/str"],(function(_exports,_filtertype,_str){var obj; /** * Keyword filter. * * @module core/datafilter/filtertypes/keyword * @copyright 2020 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filtertype=(obj=_filtertype)&&obj.__esModule?obj:{default:obj};class _default extends _filtertype.default{get values(){return this.rawValues}get placeholder(){return(0,_str.getString)("placeholdertype","core_user")}get showSuggestions(){return!1}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=keyword.min.js.mapfiltertypes/courseid.min.js.map000064400000003570152267246560012653 0ustar00{"version":3,"file":"courseid.min.js","sources":["../../../src/datafilter/filtertypes/courseid.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 ID filter.\n *\n * @module core/datafilter/filtertypes/courseid\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Filter from 'core/datafilter/filtertype';\n\nexport default class extends Filter {\n constructor(filterType, filterSet) {\n super(filterType, filterSet);\n }\n\n async addValueSelector() {\n // eslint-disable-line no-empty-function\n }\n\n /**\n * Get the composed value for this filter.\n *\n * @returns {Object}\n */\n get filterValue() {\n return {\n name: this.name,\n jointype: 1,\n values: [parseInt(this.rootNode.dataset.tableCourseId, 10)],\n };\n }\n}\n"],"names":["Filter","constructor","filterType","filterSet","filterValue","name","this","jointype","values","parseInt","rootNode","dataset","tableCourseId"],"mappings":";;;;;;;4KAwB6BA,oBACzBC,YAAYC,WAAYC,iBACdD,WAAYC,qCAYlBC,wBACO,CACHC,KAAMC,KAAKD,KACXE,SAAU,EACVC,OAAQ,CAACC,SAASH,KAAKI,SAASC,QAAQC,cAAe"}filtertypes/binary.min.js000064400000003773152267246560011553 0ustar00define("core/datafilter/filtertypes/binary",["exports","core/datafilter/filtertype","core/datafilter/selectors","core/templates","core/str"],(function(_exports,_filtertype,_selectors,_templates,_str){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,_filtertype=_interopRequireDefault(_filtertype),_selectors=_interopRequireDefault(_selectors),_templates=_interopRequireDefault(_templates);class _default extends _filtertype.default{constructor(){super(...arguments),_defineProperty(this,"optionOne",void 0),_defineProperty(this,"optionTwo",void 0)}async addValueSelector(initialValues){[this.optionOne,this.optionTwo]=await this.getTextValues();const initialValue=initialValues?initialValues[0]:0;return this.displayBinarySelection(initialValue)}getTextValues(){return(0,_str.get_strings)([{key:"no"},{key:"yes"}])}async displayBinarySelection(){let initialValue=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;const specificFilterSet=this.rootNode.querySelector(_selectors.default.filter.byName(this.filterType)),sourceDataNode=this.getSourceDataForFilter(),context={filtertype:this.filterType,title:sourceDataNode.getAttribute("data-field-title"),required:sourceDataNode.dataset.required,options:[{text:this.optionOne,value:0,selected:0===initialValue},{text:this.optionTwo,value:1,selected:1===initialValue}]};return _templates.default.render("core/datafilter/filtertypes/binary_selector",context).then(((binaryUi,js)=>_templates.default.replaceNodeContents(specificFilterSet.querySelector(_selectors.default.filter.regions.values),binaryUi,js)))}get values(){return[parseInt(this.filterRoot.querySelector('[data-filterfield="'.concat(this.name,'"]')).value)]}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=binary.min.js.mapfiltertypes/datetime.min.js.map000064400000027205152267246560012633 0ustar00{"version":3,"file":"datetime.min.js","sources":["../../../src/datafilter/filtertypes/datetime.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 * Base filter for a date/time selector\n *\n * @module core/datafilter/filtertypes/datetime\n * @author Mark Johnson \n * @copyright 2024 Catalyst IT Europe Ltd\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Filter from 'core/datafilter/filtertype';\nimport Selectors from 'core/datafilter/selectors';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport {get_strings as getStrings} from 'core/str';\n\nconst MODES = {\n before: 'before',\n after: 'after',\n between: 'between',\n};\n\nexport default class extends Filter {\n\n SELECTORS = {\n afterwrapper: `.${this.filterType}-afterwrapper`,\n beforewrapper: `.${this.filterType}-beforewrapper`,\n betweenwrapper: `.${this.filterType}-betweenwrapper`,\n mode: `[data-filterfield=${this.filterType}-mode]`,\n };\n\n mode = null;\n\n constructor(filterType, rootNode, initialValues, filterOptions = {mode: MODES.before}) {\n super(filterType, rootNode, initialValues);\n this.addModeSelector(filterOptions.mode);\n }\n\n /**\n * Get the context object to be sent through to the mustache template.\n * This can be overridden by any filters which inherit from datetime to add/exclude data.\n *\n * @param {array} initialValues\n * @returns {Promise<{filtertype: *, afterlabel: *, beforelabel: *, required, aftervalue: *, beforevalue: *}>}\n */\n async getContext(initialValues) {\n const sourceDataNode = this.getSourceDataForFilter();\n const defaultBefore = sourceDataNode.getElementsByTagName('option')[0].value;\n const defaultAfter = sourceDataNode.getElementsByTagName('option')[1].value;\n const title = sourceDataNode.getAttribute('data-field-title');\n const labels = await getStrings([\n {\n key: 'datetimefilterafter',\n component: 'core',\n param: {title},\n },\n {\n key: 'datetimefilterbefore',\n component: 'core',\n param: {title},\n },\n ]);\n return {\n filtertype: this.filterType,\n afterlabel: labels[0],\n beforelabel: labels[1],\n required: sourceDataNode.dataset.required,\n aftervalue: initialValues[0] ?? defaultAfter,\n beforevalue: initialValues[1] ?? defaultBefore,\n };\n }\n\n async addValueSelector(initialValues = []) {\n // We specify a specific filterset in case there are multiple filtering condition - avoiding glitches.\n const specificFilterSet = this.rootNode.querySelector(Selectors.filter.byName(this.filterType));\n const context = await this.getContext(initialValues);\n const datetimeUi = await Templates.renderForPromise('core/datafilter/filtertypes/datetime_selector', context);\n return Templates.replaceNodeContents(\n specificFilterSet.querySelector(Selectors.filter.regions.values),\n datetimeUi.html,\n datetimeUi.js\n );\n }\n async addModeSelector(mode) {\n const modeStrings = await getStrings([\n {key: 'selectdates'},\n {key: 'filterdatebefore', component: 'reportbuilder'},\n {key: 'filterdateafter', component: 'reportbuilder'},\n {key: 'between'}\n ]);\n const context = {\n label: modeStrings[0],\n filtertype: this.filterType,\n modeoptions: [\n {\n value: MODES.before,\n label: modeStrings[1],\n selected: mode === MODES.before ? 'selected' : '',\n },\n {\n value: MODES.after,\n label: modeStrings[2],\n selected: mode === MODES.after ? 'selected' : '',\n },\n {\n value: MODES.between,\n label: modeStrings[3],\n selected: mode === MODES.between ? 'selected' : '',\n },\n ],\n };\n const modeUi = await Templates.renderForPromise('core/datafilter/filtertypes/datetime_mode', context);\n const filterValueNode = this.getFilterValueNode();\n filterValueNode.insertAdjacentHTML('beforebegin', modeUi.html);\n const modeSelect = this.filterRoot.querySelector(this.SELECTORS.mode);\n modeSelect.addEventListener('change', this.updateFieldVisibility.bind(this));\n modeSelect.dispatchEvent(new Event('change')); // Update field visibility based on initial mode.\n }\n\n updateFieldVisibility(event) {\n const filterValueNode = this.getFilterValueNode();\n const afterWrapper = filterValueNode.querySelector(this.SELECTORS.afterwrapper);\n const beforeWrapper = filterValueNode.querySelector(this.SELECTORS.beforewrapper);\n const betweenWrapper = filterValueNode.querySelector(this.SELECTORS.betweenwrapper);\n const value = event.target.value;\n if (value === MODES.between) {\n betweenWrapper.classList.remove('d-none');\n } else {\n betweenWrapper.classList.add('d-none');\n }\n if (value === MODES.after || value === MODES.between) {\n afterWrapper.classList.remove('d-none');\n } else {\n afterWrapper.classList.add('d-none');\n }\n if (value === MODES.before || value === MODES.between) {\n beforeWrapper.classList.remove('d-none');\n } else {\n beforeWrapper.classList.add('d-none');\n }\n }\n\n get values() {\n return [\n this.filterRoot.querySelector(`[data-filterfield=\"${this.name}1\"]`).value,\n this.filterRoot.querySelector(`[data-filterfield=\"${this.name}2\"]`).value,\n ];\n }\n\n get filterOptions() {\n return [\n {name: 'mode', value: this.filterRoot.querySelector(this.SELECTORS.mode).value}\n ];\n }\n\n get filterValue() {\n return {\n name: this.name,\n jointype: this.jointype,\n values: this.values,\n filteroptions: this.filterOptions,\n };\n }\n\n validate() {\n const mode = document.querySelector(this.SELECTORS.mode).value;\n const before = document.querySelector(this.SELECTORS.beforewrapper + ' input');\n const after = document.querySelector(this.SELECTORS.afterwrapper + ' input');\n after.setCustomValidity('');\n if (mode === MODES.between) {\n if (after.value >= before.value) {\n getStrings([\n {\n key: 'invaliddatetimebetween',\n component: 'error',\n param: {\n before: before.value,\n after: after.value,\n },\n },\n ]).then((strings) => {\n after.setCustomValidity(strings[0]);\n after.reportValidity();\n return strings;\n }).catch(Notification.exception);\n return false;\n }\n }\n\n return true;\n }\n}"],"names":["MODES","Filter","constructor","filterType","rootNode","initialValues","filterOptions","mode","afterwrapper","this","beforewrapper","betweenwrapper","addModeSelector","sourceDataNode","getSourceDataForFilter","defaultBefore","getElementsByTagName","value","defaultAfter","title","getAttribute","labels","key","component","param","filtertype","afterlabel","beforelabel","required","dataset","aftervalue","beforevalue","specificFilterSet","querySelector","Selectors","filter","byName","context","getContext","datetimeUi","Templates","renderForPromise","replaceNodeContents","regions","values","html","js","modeStrings","label","modeoptions","selected","modeUi","getFilterValueNode","insertAdjacentHTML","modeSelect","filterRoot","SELECTORS","addEventListener","updateFieldVisibility","bind","dispatchEvent","Event","event","filterValueNode","afterWrapper","beforeWrapper","betweenWrapper","target","classList","remove","add","name","filterValue","jointype","filteroptions","validate","document","before","after","setCustomValidity","then","strings","reportValidity","catch","Notification","exception"],"mappings":"wvBA8BMA,aACM,SADNA,YAEK,QAFLA,cAGO,iCAGgBC,oBAWzBC,YAAYC,WAAYC,SAAUC,mBAAeC,qEAAgB,CAACC,KAAMP,oBAC9DG,WAAYC,SAAUC,gDAVpB,CACRG,wBAAkBC,KAAKN,4BACvBO,yBAAmBD,KAAKN,6BACxBQ,0BAAoBF,KAAKN,8BACzBI,iCAA2BE,KAAKN,mDAG7B,WAIES,gBAAgBN,cAAcC,uBAUtBF,0DACPQ,eAAiBJ,KAAKK,yBACtBC,cAAgBF,eAAeG,qBAAqB,UAAU,GAAGC,MACjEC,aAAeL,eAAeG,qBAAqB,UAAU,GAAGC,MAChEE,MAAQN,eAAeO,aAAa,oBACpCC,aAAe,oBAAW,CAC5B,CACIC,IAAK,sBACLC,UAAW,OACXC,MAAO,CAACL,MAAAA,QAEZ,CACIG,IAAK,uBACLC,UAAW,OACXC,MAAO,CAACL,MAAAA,gBAGT,CACHM,WAAYhB,KAAKN,WACjBuB,WAAYL,OAAO,GACnBM,YAAaN,OAAO,GACpBO,SAAUf,eAAegB,QAAQD,SACjCE,mCAAYzB,cAAc,8CAAMa,aAChCa,qCAAa1B,cAAc,gDAAMU,4CAIlBV,qEAAgB,SAE7B2B,kBAAoBvB,KAAKL,SAAS6B,cAAcC,mBAAUC,OAAOC,OAAO3B,KAAKN,aAC7EkC,cAAgB5B,KAAK6B,WAAWjC,eAChCkC,iBAAmBC,mBAAUC,iBAAiB,gDAAiDJ,gBAC9FG,mBAAUE,oBACbV,kBAAkBC,cAAcC,mBAAUC,OAAOQ,QAAQC,QACzDL,WAAWM,KACXN,WAAWO,0BAGGvC,YACZwC,kBAAoB,oBAAW,CACjC,CAACzB,IAAK,eACN,CAACA,IAAK,mBAAoBC,UAAW,iBACrC,CAACD,IAAK,kBAAmBC,UAAW,iBACpC,CAACD,IAAK,aAEJe,QAAU,CACZW,MAAOD,YAAY,GACnBtB,WAAYhB,KAAKN,WACjB8C,YAAa,CACT,CACIhC,MAAOjB,aACPgD,MAAOD,YAAY,GACnBG,SAAU3C,OAASP,aAAe,WAAa,IAEnD,CACIiB,MAAOjB,YACPgD,MAAOD,YAAY,GACnBG,SAAU3C,OAASP,YAAc,WAAa,IAElD,CACIiB,MAAOjB,cACPgD,MAAOD,YAAY,GACnBG,SAAU3C,OAASP,cAAgB,WAAa,MAItDmD,aAAeX,mBAAUC,iBAAiB,4CAA6CJ,SACrE5B,KAAK2C,qBACbC,mBAAmB,cAAeF,OAAON,YACnDS,WAAa7C,KAAK8C,WAAWtB,cAAcxB,KAAK+C,UAAUjD,MAChE+C,WAAWG,iBAAiB,SAAUhD,KAAKiD,sBAAsBC,KAAKlD,OACtE6C,WAAWM,cAAc,IAAIC,MAAM,WAGvCH,sBAAsBI,aACZC,gBAAkBtD,KAAK2C,qBACvBY,aAAeD,gBAAgB9B,cAAcxB,KAAK+C,UAAUhD,cAC5DyD,cAAgBF,gBAAgB9B,cAAcxB,KAAK+C,UAAU9C,eAC7DwD,eAAiBH,gBAAgB9B,cAAcxB,KAAK+C,UAAU7C,gBAC9DM,MAAQ6C,MAAMK,OAAOlD,MACvBA,QAAUjB,cACVkE,eAAeE,UAAUC,OAAO,UAEhCH,eAAeE,UAAUE,IAAI,UAE7BrD,QAAUjB,aAAeiB,QAAUjB,cACnCgE,aAAaI,UAAUC,OAAO,UAE9BL,aAAaI,UAAUE,IAAI,UAE3BrD,QAAUjB,cAAgBiB,QAAUjB,cACpCiE,cAAcG,UAAUC,OAAO,UAE/BJ,cAAcG,UAAUE,IAAI,UAIhC1B,mBACO,CACHnC,KAAK8C,WAAWtB,2CAAoCxB,KAAK8D,aAAWtD,MACpER,KAAK8C,WAAWtB,2CAAoCxB,KAAK8D,aAAWtD,OAIxEX,0BACO,CACH,CAACiE,KAAM,OAAQtD,MAAOR,KAAK8C,WAAWtB,cAAcxB,KAAK+C,UAAUjD,MAAMU,QAI7EuD,wBACO,CACHD,KAAM9D,KAAK8D,KACXE,SAAUhE,KAAKgE,SACf7B,OAAQnC,KAAKmC,OACb8B,cAAejE,KAAKH,eAI5BqE,iBACUpE,KAAOqE,SAAS3C,cAAcxB,KAAK+C,UAAUjD,MAAMU,MACnD4D,OAASD,SAAS3C,cAAcxB,KAAK+C,UAAU9C,cAAgB,UAC/DoE,MAAQF,SAAS3C,cAAcxB,KAAK+C,UAAUhD,aAAe,iBACnEsE,MAAMC,kBAAkB,MACpBxE,OAASP,eACL8E,MAAM7D,OAAS4D,OAAO5D,8BACX,CACP,CACIK,IAAK,yBACLC,UAAW,QACXC,MAAO,CACHqD,OAAQA,OAAO5D,MACf6D,MAAOA,MAAM7D,UAGtB+D,MAAMC,UACLH,MAAMC,kBAAkBE,QAAQ,IAChCH,MAAMI,iBACCD,WACRE,MAAMC,sBAAaC,YACf"}filtertypes/binary.min.js.map000064400000012111152267246560012311 0ustar00{"version":3,"file":"binary.min.js","sources":["../../../src/datafilter/filtertypes/binary.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 * Base filter for binary selector ie: (Yes / No).\n *\n * @module core/datafilter/filtertypes/binary\n * @author 2022 Ghaly Marc-Alexandre \n * @copyright 2022 Catalyst IT Australia Pty Ltd\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Filter from 'core/datafilter/filtertype';\nimport Selectors from 'core/datafilter/selectors';\nimport Templates from 'core/templates';\nimport {get_strings as getStrings} from 'core/str';\n\nexport default class extends Filter {\n\n /**\n * Text string for the first binary option.\n *\n * This option (and {@see optionTwo}) are set by {@see getTextValues()}. The base class will set default values,\n * a subclass can override the method to define its own option.\n *\n * @type {String}\n */\n optionOne;\n\n /**\n * Text string for the second binary option.\n *\n * @type {String}\n */\n optionTwo;\n\n /**\n * Add the value selector to the filter row.\n *\n * @param {Array} initialValues The default value for the filter.\n */\n async addValueSelector(initialValues) {\n [this.optionOne, this.optionTwo] = await this.getTextValues();\n const initialValue = initialValues ? initialValues[0] : 0;\n return this.displayBinarySelection(initialValue);\n }\n\n /**\n * Fetch text values for select options.\n *\n * Subclasses should override this method to set their own options.\n *\n * @returns {Promise}\n */\n getTextValues() {\n return getStrings([{key: 'no'}, {key: 'yes'}]);\n }\n\n /**\n * Renders yes/no select input with proper selection.\n *\n * @param {Number} initialValue The default value for the filter.\n */\n async displayBinarySelection(initialValue = 0) {\n // We specify a specific filterset in case there are multiple filtering condition - avoiding glitches.\n const specificFilterSet = this.rootNode.querySelector(Selectors.filter.byName(this.filterType));\n const sourceDataNode = this.getSourceDataForFilter();\n const context = {\n filtertype: this.filterType,\n title: sourceDataNode.getAttribute('data-field-title'),\n required: sourceDataNode.dataset.required,\n options: [\n {\n text: this.optionOne,\n value: 0,\n selected: initialValue === 0,\n },\n {\n text: this.optionTwo,\n value: 1,\n selected: initialValue === 1,\n },\n ]\n };\n return Templates.render('core/datafilter/filtertypes/binary_selector', context)\n .then((binaryUi, js) => {\n return Templates.replaceNodeContents(specificFilterSet.querySelector(Selectors.filter.regions.values), binaryUi, js);\n });\n }\n\n /**\n * Get the list of raw values for this filter type.\n *\n * @returns {Array}\n */\n get values() {\n return [parseInt(this.filterRoot.querySelector(`[data-filterfield=\"${this.name}\"]`).value)];\n }\n\n}\n"],"names":["Filter","initialValues","this","optionOne","optionTwo","getTextValues","initialValue","displayBinarySelection","key","specificFilterSet","rootNode","querySelector","Selectors","filter","byName","filterType","sourceDataNode","getSourceDataForFilter","context","filtertype","title","getAttribute","required","dataset","options","text","value","selected","Templates","render","then","binaryUi","js","replaceNodeContents","regions","values","parseInt","filterRoot","name"],"mappings":"irBA6B6BA,+JAwBFC,gBAClBC,KAAKC,UAAWD,KAAKE,iBAAmBF,KAAKG,sBACxCC,aAAeL,cAAgBA,cAAc,GAAK,SACjDC,KAAKK,uBAAuBD,cAUvCD,uBACW,oBAAW,CAAC,CAACG,IAAK,MAAO,CAACA,IAAK,4CAQbF,oEAAe,QAElCG,kBAAoBP,KAAKQ,SAASC,cAAcC,mBAAUC,OAAOC,OAAOZ,KAAKa,aAC7EC,eAAiBd,KAAKe,yBACtBC,QAAU,CACZC,WAAYjB,KAAKa,WACjBK,MAAOJ,eAAeK,aAAa,oBACnCC,SAAUN,eAAeO,QAAQD,SACjCE,QAAS,CACL,CACIC,KAAMvB,KAAKC,UACXuB,MAAO,EACPC,SAA2B,IAAjBrB,cAEd,CACImB,KAAMvB,KAAKE,UACXsB,MAAO,EACPC,SAA2B,IAAjBrB,uBAIfsB,mBAAUC,OAAO,8CAA+CX,SACtEY,MAAK,CAACC,SAAUC,KACNJ,mBAAUK,oBAAoBxB,kBAAkBE,cAAcC,mBAAUC,OAAOqB,QAAQC,QAASJ,SAAUC,MASrHG,mBACO,CAACC,SAASlC,KAAKmC,WAAW1B,2CAAoCT,KAAKoC,YAAUZ"}selectors.min.js.map000064400000007253152267246560010471 0ustar00{"version":3,"file":"selectors.min.js","sources":["../../src/datafilter/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 * Module containing the selectors for user filters.\n *\n * @module core/datafilter/selectors\n * @copyright 2020 Michael Hawkins \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst getFilterRegion = region => `[data-filterregion=\"${region}\"]`;\nconst getFilterAction = action => `[data-filteraction=\"${action}\"]`;\nconst getFilterField = field => `[data-filterfield=\"${field}\"]`;\n\nexport default {\n filter: {\n region: getFilterRegion('filter'),\n actions: {\n remove: getFilterAction('remove'),\n },\n fields: {\n join: getFilterField('join'),\n type: getFilterField('type'),\n },\n regions: {\n values: getFilterRegion('value'),\n },\n byName: name => `${getFilterRegion('filter')}[data-filter-type=\"${name}\"]`,\n },\n filterset: {\n region: getFilterRegion('actions'),\n actions: {\n addRow: getFilterAction('add'),\n applyFilters: getFilterAction('apply'),\n resetFilters: getFilterAction('reset'),\n },\n regions: {\n filtermatch: getFilterRegion('filtermatch'),\n filterlist: getFilterRegion('filters'),\n datasource: getFilterRegion('filtertypedata'),\n emptyFilterRow: `${getFilterRegion('filter')}[data-filter-type=\"\"]`,\n },\n fields: {\n join: `${getFilterRegion('filtermatch')} ${getFilterField('join')}`,\n },\n },\n data: {\n fields: {\n byName: name => `[data-field-name=\"${name}\"]`,\n all: `${getFilterRegion('filtertypedata')} [data-field-name]`,\n },\n typeList: getFilterRegion('filtertypelist'),\n typeListSelect: `select${getFilterRegion('filtertypelist')}`,\n required: `${getFilterRegion('value')} > [data-required=\"1\"]`,\n },\n};\n"],"names":["getFilterRegion","region","getFilterAction","action","getFilterField","field","filter","actions","remove","fields","join","type","regions","values","byName","name","filterset","addRow","applyFilters","resetFilters","filtermatch","filterlist","datasource","emptyFilterRow","data","all","typeList","typeListSelect","required"],"mappings":";;;;;;;;MAuBMA,gBAAkBC,sCAAiCA,aACnDC,gBAAkBC,sCAAiCA,aACnDC,eAAiBC,oCAA+BA,yBAEvC,CACXC,OAAQ,CACJL,OAAQD,gBAAgB,UACxBO,QAAS,CACLC,OAAQN,gBAAgB,WAE5BO,OAAQ,CACJC,KAAMN,eAAe,QACrBO,KAAMP,eAAe,SAEzBQ,QAAS,CACLC,OAAQb,gBAAgB,UAE5Bc,OAAQC,gBAAWf,gBAAgB,wCAA+Be,YAEtEC,UAAW,CACPf,OAAQD,gBAAgB,WACxBO,QAAS,CACLU,OAAQf,gBAAgB,OACxBgB,aAAchB,gBAAgB,SAC9BiB,aAAcjB,gBAAgB,UAElCU,QAAS,CACLQ,YAAapB,gBAAgB,eAC7BqB,WAAYrB,gBAAgB,WAC5BsB,WAAYtB,gBAAgB,kBAC5BuB,yBAAmBvB,gBAAgB,oCAEvCS,OAAQ,CACJC,eAASV,gBAAgB,2BAAkBI,eAAe,WAGlEoB,KAAM,CACFf,OAAQ,CACJK,OAAQC,kCAA6BA,WACrCU,cAAQzB,gBAAgB,yCAE5B0B,SAAU1B,gBAAgB,kBAC1B2B,+BAAyB3B,gBAAgB,mBACzC4B,mBAAa5B,gBAAgB"}filtertype.min.js000064400000007246152267246560010103 0ustar00define("core/datafilter/filtertype",["exports","core/form-autocomplete","core/datafilter/selectors","core/str","core/notification"],(function(_exports,_formAutocomplete,_selectors,_str,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Base Filter class for a filter type in the filter UI. * * @module core/datafilter/filtertype * @copyright 2020 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_formAutocomplete=_interopRequireDefault(_formAutocomplete),_selectors=_interopRequireDefault(_selectors),_notification=_interopRequireDefault(_notification);return _exports.default=class{constructor(filterType,rootNode,initialValues){this.filterType=filterType,this.rootNode=rootNode,this.addValueSelector(initialValues).then((()=>{const filterRoot=this.filterRoot;return filterRoot&&filterRoot.querySelector(_selectors.default.data.required)&&filterRoot.querySelector(_selectors.default.filter.actions.remove).remove(),filterRoot})).catch(_notification.default.exception)}tearDown(){}get placeholder(){return(0,_str.getString)("placeholdertypeorselect","core")}get showSuggestions(){return!0}async addValueSelector(){let initialValues=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];const filterValueNode=this.getFilterValueNode(),sourceDataNode=this.getSourceDataForFilter();if(!sourceDataNode)throw new Error("No source data for filter.");filterValueNode.innerHTML=sourceDataNode.outerHTML;const dataSource=filterValueNode.querySelector("select");dataSource.id="filter-value-"+dataSource.getAttribute("data-field-name");const filterValueLabel=document.createElement("label");return filterValueLabel.setAttribute("for",dataSource.id),filterValueLabel.classList.add("visually-hidden"),filterValueLabel.innerText=dataSource.getAttribute("data-field-title"),filterValueNode.appendChild(filterValueLabel),initialValues.forEach((filterValue=>{let selectedOption=dataSource.querySelector('option[value="'.concat(filterValue,'"]'));selectedOption?selectedOption.selected=!0:this.showSuggestions||(selectedOption=document.createElement("option"),selectedOption.value=filterValue,selectedOption.innerHTML=filterValue,selectedOption.selected=!0,dataSource.append(selectedOption))})),_formAutocomplete.default.enhance(dataSource,"1"==dataSource.dataset.allowCustom,null,await this.placeholder,!1,this.showSuggestions,null,!dataSource.multiple,{items:"core/datafilter/autocomplete_selection_items",layout:"core/datafilter/autocomplete_layout",selection:"core/datafilter/autocomplete_selection"})}get filterRoot(){return this.rootNode.querySelector(_selectors.default.filter.byName(this.filterType))}getSourceDataForFilter(){return this.rootNode.querySelector(_selectors.default.filterset.regions.datasource).querySelector(_selectors.default.data.fields.byName(this.filterType))}getFilterValueNode(){return this.filterRoot.querySelector(_selectors.default.filter.regions.values)}get name(){return this.filterType}get jointype(){return parseInt(this.filterRoot.querySelector(_selectors.default.filter.fields.join).value,10)}get rawValues(){const filterValueSelect=this.getFilterValueNode().querySelector("select");return Object.values((select=filterValueSelect,select.querySelectorAll(":checked"))).map((option=>option.value));var select}get values(){return this.rawValues.map((option=>parseInt(option,10)))}get filterOptions(){return[]}get filterValue(){return{name:this.name,jointype:this.jointype,values:this.values,filteroptions:this.filterOptions}}validate(){return!0}},_exports.default})); //# sourceMappingURL=filtertype.min.js.mapselectors.min.js000064400000003355152267246560007714 0ustar00define("core/datafilter/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0; /** * Module containing the selectors for user filters. * * @module core/datafilter/selectors * @copyright 2020 Michael Hawkins * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ const getFilterRegion=region=>'[data-filterregion="'.concat(region,'"]'),getFilterAction=action=>'[data-filteraction="'.concat(action,'"]'),getFilterField=field=>'[data-filterfield="'.concat(field,'"]');var _default={filter:{region:getFilterRegion("filter"),actions:{remove:getFilterAction("remove")},fields:{join:getFilterField("join"),type:getFilterField("type")},regions:{values:getFilterRegion("value")},byName:name=>"".concat(getFilterRegion("filter"),'[data-filter-type="').concat(name,'"]')},filterset:{region:getFilterRegion("actions"),actions:{addRow:getFilterAction("add"),applyFilters:getFilterAction("apply"),resetFilters:getFilterAction("reset")},regions:{filtermatch:getFilterRegion("filtermatch"),filterlist:getFilterRegion("filters"),datasource:getFilterRegion("filtertypedata"),emptyFilterRow:"".concat(getFilterRegion("filter"),'[data-filter-type=""]')},fields:{join:"".concat(getFilterRegion("filtermatch")," ").concat(getFilterField("join"))}},data:{fields:{byName:name=>'[data-field-name="'.concat(name,'"]'),all:"".concat(getFilterRegion("filtertypedata")," [data-field-name]")},typeList:getFilterRegion("filtertypelist"),typeListSelect:"select".concat(getFilterRegion("filtertypelist")),required:"".concat(getFilterRegion("value"),' > [data-required="1"]')}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=selectors.min.js.mapfiltertypes/datetime.js000064400000017364152267766320011303 0ustar00// This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . /** * Base filter for a date/time selector * * @module core/datafilter/filtertypes/datetime * @author Mark Johnson * @copyright 2024 Catalyst IT Europe Ltd * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import Filter from 'core/datafilter/filtertype'; import Selectors from 'core/datafilter/selectors'; import Templates from 'core/templates'; import Notification from 'core/notification'; import {get_strings as getStrings} from 'core/str'; const MODES = { before: 'before', after: 'after', between: 'between', }; export default class extends Filter { SELECTORS = { afterwrapper: `.${this.filterType}-afterwrapper`, beforewrapper: `.${this.filterType}-beforewrapper`, betweenwrapper: `.${this.filterType}-betweenwrapper`, mode: `[data-filterfield=${this.filterType}-mode]`, }; mode = null; constructor(filterType, rootNode, initialValues, filterOptions = {mode: MODES.before}) { super(filterType, rootNode, initialValues); this.addModeSelector(filterOptions.mode); } /** * Get the context object to be sent through to the mustache template. * This can be overridden by any filters which inherit from datetime to add/exclude data. * * @param {array} initialValues * @returns {Promise<{filtertype: *, afterlabel: *, beforelabel: *, required, aftervalue: *, beforevalue: *}>} */ async getContext(initialValues) { const sourceDataNode = this.getSourceDataForFilter(); const defaultBefore = sourceDataNode.getElementsByTagName('option')[0].value; const defaultAfter = sourceDataNode.getElementsByTagName('option')[1].value; const title = sourceDataNode.getAttribute('data-field-title'); const labels = await getStrings([ { key: 'datetimefilterafter', component: 'core', param: {title}, }, { key: 'datetimefilterbefore', component: 'core', param: {title}, }, ]); return { filtertype: this.filterType, afterlabel: labels[0], beforelabel: labels[1], required: sourceDataNode.dataset.required, aftervalue: initialValues[0] ?? defaultAfter, beforevalue: initialValues[1] ?? defaultBefore, }; } async addValueSelector(initialValues = []) { // We specify a specific filterset in case there are multiple filtering condition - avoiding glitches. const specificFilterSet = this.rootNode.querySelector(Selectors.filter.byName(this.filterType)); const context = await this.getContext(initialValues); const datetimeUi = await Templates.renderForPromise('core/datafilter/filtertypes/datetime_selector', context); return Templates.replaceNodeContents( specificFilterSet.querySelector(Selectors.filter.regions.values), datetimeUi.html, datetimeUi.js ); } async addModeSelector(mode) { const modeStrings = await getStrings([ {key: 'selectdates'}, {key: 'filterdatebefore', component: 'reportbuilder'}, {key: 'filterdateafter', component: 'reportbuilder'}, {key: 'between'} ]); const context = { label: modeStrings[0], filtertype: this.filterType, modeoptions: [ { value: MODES.before, label: modeStrings[1], selected: mode === MODES.before ? 'selected' : '', }, { value: MODES.after, label: modeStrings[2], selected: mode === MODES.after ? 'selected' : '', }, { value: MODES.between, label: modeStrings[3], selected: mode === MODES.between ? 'selected' : '', }, ], }; const modeUi = await Templates.renderForPromise('core/datafilter/filtertypes/datetime_mode', context); const filterValueNode = this.getFilterValueNode(); filterValueNode.insertAdjacentHTML('beforebegin', modeUi.html); const modeSelect = this.filterRoot.querySelector(this.SELECTORS.mode); modeSelect.addEventListener('change', this.updateFieldVisibility.bind(this)); modeSelect.dispatchEvent(new Event('change')); // Update field visibility based on initial mode. } updateFieldVisibility(event) { const filterValueNode = this.getFilterValueNode(); const afterWrapper = filterValueNode.querySelector(this.SELECTORS.afterwrapper); const beforeWrapper = filterValueNode.querySelector(this.SELECTORS.beforewrapper); const betweenWrapper = filterValueNode.querySelector(this.SELECTORS.betweenwrapper); const value = event.target.value; if (value === MODES.between) { betweenWrapper.classList.remove('d-none'); } else { betweenWrapper.classList.add('d-none'); } if (value === MODES.after || value === MODES.between) { afterWrapper.classList.remove('d-none'); } else { afterWrapper.classList.add('d-none'); } if (value === MODES.before || value === MODES.between) { beforeWrapper.classList.remove('d-none'); } else { beforeWrapper.classList.add('d-none'); } } get values() { return [ this.filterRoot.querySelector(`[data-filterfield="${this.name}1"]`).value, this.filterRoot.querySelector(`[data-filterfield="${this.name}2"]`).value, ]; } get filterOptions() { return [ {name: 'mode', value: this.filterRoot.querySelector(this.SELECTORS.mode).value} ]; } get filterValue() { return { name: this.name, jointype: this.jointype, values: this.values, filteroptions: this.filterOptions, }; } validate() { const mode = document.querySelector(this.SELECTORS.mode).value; const before = document.querySelector(this.SELECTORS.beforewrapper + ' input'); const after = document.querySelector(this.SELECTORS.afterwrapper + ' input'); after.setCustomValidity(''); if (mode === MODES.between) { if (after.value >= before.value) { getStrings([ { key: 'invaliddatetimebetween', component: 'error', param: { before: before.value, after: after.value, }, }, ]).then((strings) => { after.setCustomValidity(strings[0]); after.reportValidity(); return strings; }).catch(Notification.exception); return false; } } return true; } }filtertypes/keyword.js000064400000003042152267766320011157 0ustar00// This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . /** * Keyword filter. * * @module core/datafilter/filtertypes/keyword * @copyright 2020 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import Filter from 'core/datafilter/filtertype'; import {getString} from 'core/str'; export default class extends Filter { /** * For keywords the final value is an Array of strings. * * @returns {Object} */ get values() { return this.rawValues; } /** * Get the placeholder to use when showing the value selector. * * @return {Promise} Resolving to a String */ get placeholder() { return getString('placeholdertype', 'core_user'); } /** * Whether to show suggestions in the autocomplete. * * @return {Boolean} */ get showSuggestions() { return false; } } filtertypes/courseid.js000064400000002637152267766320011321 0ustar00// This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . /** * Course ID filter. * * @module core/datafilter/filtertypes/courseid * @copyright 2020 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import Filter from 'core/datafilter/filtertype'; export default class extends Filter { constructor(filterType, filterSet) { super(filterType, filterSet); } async addValueSelector() { // eslint-disable-line no-empty-function } /** * Get the composed value for this filter. * * @returns {Object} */ get filterValue() { return { name: this.name, jointype: 1, values: [parseInt(this.rootNode.dataset.tableCourseId, 10)], }; } } filtertypes/country.js000064400000002204152267766320011175 0ustar00// This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . /** * Country filter * * @module core/datafilter/filtertypes/country * @copyright 2021 Paul Holden * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import Filter from 'core/datafilter/filtertype'; export default class extends Filter { /** * For country the final value is an array of country code strings * * @return {Object} */ get values() { return this.rawValues; } } filtertypes/binary.js000064400000007365152267766320010773 0ustar00// This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . /** * Base filter for binary selector ie: (Yes / No). * * @module core/datafilter/filtertypes/binary * @author 2022 Ghaly Marc-Alexandre * @copyright 2022 Catalyst IT Australia Pty Ltd * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import Filter from 'core/datafilter/filtertype'; import Selectors from 'core/datafilter/selectors'; import Templates from 'core/templates'; import {get_strings as getStrings} from 'core/str'; export default class extends Filter { /** * Text string for the first binary option. * * This option (and {@see optionTwo}) are set by {@see getTextValues()}. The base class will set default values, * a subclass can override the method to define its own option. * * @type {String} */ optionOne; /** * Text string for the second binary option. * * @type {String} */ optionTwo; /** * Add the value selector to the filter row. * * @param {Array} initialValues The default value for the filter. */ async addValueSelector(initialValues) { [this.optionOne, this.optionTwo] = await this.getTextValues(); const initialValue = initialValues ? initialValues[0] : 0; return this.displayBinarySelection(initialValue); } /** * Fetch text values for select options. * * Subclasses should override this method to set their own options. * * @returns {Promise} */ getTextValues() { return getStrings([{key: 'no'}, {key: 'yes'}]); } /** * Renders yes/no select input with proper selection. * * @param {Number} initialValue The default value for the filter. */ async displayBinarySelection(initialValue = 0) { // We specify a specific filterset in case there are multiple filtering condition - avoiding glitches. const specificFilterSet = this.rootNode.querySelector(Selectors.filter.byName(this.filterType)); const sourceDataNode = this.getSourceDataForFilter(); const context = { filtertype: this.filterType, title: sourceDataNode.getAttribute('data-field-title'), required: sourceDataNode.dataset.required, options: [ { text: this.optionOne, value: 0, selected: initialValue === 0, }, { text: this.optionTwo, value: 1, selected: initialValue === 1, }, ] }; return Templates.render('core/datafilter/filtertypes/binary_selector', context) .then((binaryUi, js) => { return Templates.replaceNodeContents(specificFilterSet.querySelector(Selectors.filter.regions.values), binaryUi, js); }); } /** * Get the list of raw values for this filter type. * * @returns {Array} */ get values() { return [parseInt(this.filterRoot.querySelector(`[data-filterfield="${this.name}"]`).value)]; } } selectors.js000064400000005011152267766320007122 0ustar00// This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . /** * Module containing the selectors for user filters. * * @module core/datafilter/selectors * @copyright 2020 Michael Hawkins * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ const getFilterRegion = region => `[data-filterregion="${region}"]`; const getFilterAction = action => `[data-filteraction="${action}"]`; const getFilterField = field => `[data-filterfield="${field}"]`; export default { filter: { region: getFilterRegion('filter'), actions: { remove: getFilterAction('remove'), }, fields: { join: getFilterField('join'), type: getFilterField('type'), }, regions: { values: getFilterRegion('value'), }, byName: name => `${getFilterRegion('filter')}[data-filter-type="${name}"]`, }, filterset: { region: getFilterRegion('actions'), actions: { addRow: getFilterAction('add'), applyFilters: getFilterAction('apply'), resetFilters: getFilterAction('reset'), }, regions: { filtermatch: getFilterRegion('filtermatch'), filterlist: getFilterRegion('filters'), datasource: getFilterRegion('filtertypedata'), emptyFilterRow: `${getFilterRegion('filter')}[data-filter-type=""]`, }, fields: { join: `${getFilterRegion('filtermatch')} ${getFilterField('join')}`, }, }, data: { fields: { byName: name => `[data-field-name="${name}"]`, all: `${getFilterRegion('filtertypedata')} [data-field-name]`, }, typeList: getFilterRegion('filtertypelist'), typeListSelect: `select${getFilterRegion('filtertypelist')}`, required: `${getFilterRegion('value')} > [data-required="1"]`, }, }; filtertype.js000064400000017531152267766320007320 0ustar00// This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . /** * Base Filter class for a filter type in the filter UI. * * @module core/datafilter/filtertype * @copyright 2020 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import Autocomplete from 'core/form-autocomplete'; import Selectors from 'core/datafilter/selectors'; import {getString} from 'core/str'; import Notification from 'core/notification'; /** * Fetch all checked options in the select. * * This is a poor-man's polyfill for select.selectedOptions, which is not available in IE11. * * @param {HTMLSelectElement} select * @returns {HTMLOptionElement[]} All selected options */ const getOptionsForSelect = select => { return select.querySelectorAll(':checked'); }; export default class { /** * Constructor for a new filter. * * @param {String} filterType The type of filter that this relates to * @param {HTMLElement} rootNode The root node for the participants filterset * @param {Array} initialValues The initial values for the selector */ constructor(filterType, rootNode, initialValues) { this.filterType = filterType; this.rootNode = rootNode; this.addValueSelector(initialValues).then(() => { const filterRoot = this.filterRoot; if (filterRoot && filterRoot.querySelector(Selectors.data.required)) { filterRoot.querySelector(Selectors.filter.actions.remove).remove(); } return filterRoot; }).catch(Notification.exception); } /** * Perform any tear-down for this filter type. */ tearDown() { // eslint-disable-line no-empty-function } /** * Get the placeholder to use when showing the value selector. * * @return {Promise} Resolving to a String */ get placeholder() { return getString('placeholdertypeorselect', 'core'); } /** * Whether to show suggestions in the autocomplete. * * @return {Boolean} */ get showSuggestions() { return true; } /** * Add the value selector to the filter row. * * @param {Array} initialValues * @return {Promise} */ async addValueSelector(initialValues = []) { const filterValueNode = this.getFilterValueNode(); // Copy the data in place. const sourceDataNode = this.getSourceDataForFilter(); if (!sourceDataNode) { throw new Error('No source data for filter.'); } filterValueNode.innerHTML = sourceDataNode.outerHTML; const dataSource = filterValueNode.querySelector('select'); // Set an ID for this filter value element. dataSource.id = 'filter-value-' + dataSource.getAttribute('data-field-name'); // Create a hidden label for the filter value. const filterValueLabel = document.createElement('label'); filterValueLabel.setAttribute('for', dataSource.id); filterValueLabel.classList.add('visually-hidden'); filterValueLabel.innerText = dataSource.getAttribute('data-field-title'); // Append this label to the filter value container. filterValueNode.appendChild(filterValueLabel); // If there are any initial values then attempt to apply them. initialValues.forEach(filterValue => { let selectedOption = dataSource.querySelector(`option[value="${filterValue}"]`); if (selectedOption) { selectedOption.selected = true; } else if (!this.showSuggestions) { selectedOption = document.createElement('option'); selectedOption.value = filterValue; selectedOption.innerHTML = filterValue; selectedOption.selected = true; dataSource.append(selectedOption); } }); return Autocomplete.enhance( // The source select element. dataSource, // Whether to allow 'tags' (custom entries). dataSource.dataset.allowCustom == "1", // We do not require AJAX at all as standard. null, // The string to use as a placeholder. await this.placeholder, // Disable case sensitivity on searches. false, // Show suggestions. this.showSuggestions, // Do not override the 'no suggestions' string. null, // Close the suggestions if this is not a multi-select. !dataSource.multiple, // Template overrides. { items: 'core/datafilter/autocomplete_selection_items', layout: 'core/datafilter/autocomplete_layout', selection: 'core/datafilter/autocomplete_selection', } ); } /** * Get the root node for this filter. * * @returns {HTMLElement} */ get filterRoot() { return this.rootNode.querySelector(Selectors.filter.byName(this.filterType)); } /** * Get the possible data for this filter type. * * @returns {Array} */ getSourceDataForFilter() { const filterDataNode = this.rootNode.querySelector(Selectors.filterset.regions.datasource); return filterDataNode.querySelector(Selectors.data.fields.byName(this.filterType)); } /** * Get the HTMLElement which contains the value selector. * * @returns {HTMLElement} */ getFilterValueNode() { return this.filterRoot.querySelector(Selectors.filter.regions.values); } /** * Get the name of this filter. * * @returns {String} */ get name() { return this.filterType; } /** * Get the type of join specified. * * @returns {Number} */ get jointype() { return parseInt(this.filterRoot.querySelector(Selectors.filter.fields.join).value, 10); } /** * Get the list of raw values for this filter type. * * @returns {Array} */ get rawValues() { const filterValueNode = this.getFilterValueNode(); const filterValueSelect = filterValueNode.querySelector('select'); return Object.values(getOptionsForSelect(filterValueSelect)).map(option => option.value); } /** * Get the list of values for this filter type. * * @returns {Array} */ get values() { return this.rawValues.map(option => parseInt(option, 10)); } /** * Get options specific to this filter type. * * @returns {Array} of {name:, value:} objects */ get filterOptions() { return []; } /** * Get the composed value for this filter. * * @returns {Object} */ get filterValue() { return { name: this.name, jointype: this.jointype, values: this.values, filteroptions: this.filterOptions, }; } /** * Validate the entered values for the filter. * * Return true if all values are valid, false otherwise. * To display validation errors, use the standard setCustomValidity() and reportValidity() methods. * * @return {boolean} */ validate() { return true; } }