Sindbad~EG File Manager
{"version":3,"file":"participantsfilter.min.js","sources":["../src/participantsfilter.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Participants filter managemnet.\n *\n * @module core_user/participants_filter\n * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport CourseFilter from './local/participantsfilter/filtertypes/courseid';\nimport * as DynamicTable from 'core_table/dynamic';\nimport GenericFilter from './local/participantsfilter/filter';\nimport {get_strings as getStrings} from 'core/str';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Selectors from './local/participantsfilter/selectors';\nimport Templates from 'core/templates';\nimport CustomEvents from 'core/custom_interaction_events';\nimport jQuery from 'jquery';\n\n/**\n * Initialise the participants filter on the element with the given id.\n *\n * @param {String} participantsRegionId\n */\nexport const init = participantsRegionId => {\n // Keep a reference to the filterset.\n const filterSet = document.querySelector(`#${participantsRegionId}`);\n\n // Keep a reference to all of the active filters.\n const activeFilters = {\n courseid: new CourseFilter('courseid', filterSet),\n };\n\n /**\n * Get the filter list region.\n *\n * @return {HTMLElement}\n */\n const getFilterRegion = () => filterSet.querySelector(Selectors.filterset.regions.filterlist);\n\n /**\n * Add an unselected filter row.\n *\n * @return {Promise}\n */\n const addFilterRow = () => {\n const pendingPromise = new Pending('core_user/participantsfilter:addFilterRow');\n\n const rownum = 1 + getFilterRegion().querySelectorAll(Selectors.filter.region).length;\n return Templates.renderForPromise('core_user/local/participantsfilter/filterrow', {\"rownumber\": rownum})\n .then(({html, js}) => {\n const newContentNodes = Templates.appendNodeContents(getFilterRegion(), html, js);\n\n return newContentNodes;\n })\n .then(filterRow => {\n // Note: This is a nasty hack.\n // We should try to find a better way of doing this.\n // We do not have the list of types in a readily consumable format, so we take the pre-rendered one and copy\n // it in place.\n const typeList = filterSet.querySelector(Selectors.data.typeList);\n\n filterRow.forEach(contentNode => {\n const contentTypeList = contentNode.querySelector(Selectors.filter.fields.type);\n\n if (contentTypeList) {\n contentTypeList.innerHTML = typeList.innerHTML;\n }\n });\n\n return filterRow;\n })\n .then(filterRow => {\n updateFiltersOptions();\n\n return filterRow;\n })\n .then(result => {\n pendingPromise.resolve();\n\n return result;\n })\n .catch(Notification.exception);\n };\n\n /**\n * Get the filter data source node fro the specified filter type.\n *\n * @param {String} filterType\n * @return {HTMLElement}\n */\n const getFilterDataSource = filterType => {\n const filterDataNode = filterSet.querySelector(Selectors.filterset.regions.datasource);\n\n return filterDataNode.querySelector(Selectors.data.fields.byName(filterType));\n };\n\n /**\n * Add a filter to the list of active filters, performing any necessary setup.\n *\n * @param {HTMLElement} filterRow\n * @param {String} filterType\n * @param {Array} initialFilterValues The initially selected values for the filter\n * @returns {Filter}\n */\n const addFilter = async(filterRow, filterType, initialFilterValues) => {\n // Name the filter on the filter row.\n filterRow.dataset.filterType = filterType;\n\n const filterDataNode = getFilterDataSource(filterType);\n\n // Instantiate the Filter class.\n let Filter = GenericFilter;\n if (filterDataNode?.dataset.filterTypeClass) {\n Filter = await import(filterDataNode.dataset.filterTypeClass);\n }\n activeFilters[filterType] = new Filter(filterType, filterSet, initialFilterValues);\n\n // Disable the select.\n const typeField = filterRow.querySelector(Selectors.filter.fields.type);\n typeField.value = filterType;\n typeField.disabled = 'disabled';\n\n // Update the list of available filter types.\n updateFiltersOptions();\n\n return activeFilters[filterType];\n };\n\n /**\n * Get the registered filter class for the named filter.\n *\n * @param {String} name\n * @return {Object} See the Filter class.\n */\n const getFilterObject = name => {\n return activeFilters[name];\n };\n\n /**\n * Remove or replace the specified filter row and associated class, ensuring that if there is only one filter row,\n * that it is replaced instead of being removed.\n *\n * @param {HTMLElement} filterRow\n * @param {Bool} refreshContent Whether to refresh the table content when removing\n */\n const removeOrReplaceFilterRow = (filterRow, refreshContent) => {\n const filterCount = getFilterRegion().querySelectorAll(Selectors.filter.region).length;\n\n if (filterCount === 1) {\n replaceFilterRow(filterRow, refreshContent);\n } else {\n removeFilterRow(filterRow, refreshContent);\n }\n };\n\n /**\n * Remove the specified filter row and associated class.\n *\n * @param {HTMLElement} filterRow\n * @param {Bool} refreshContent Whether to refresh the table content when removing\n */\n const removeFilterRow = async(filterRow, refreshContent = true) => {\n const filterType = filterRow.querySelector(Selectors.filter.fields.type);\n const hasFilterValue = !!filterType.value;\n\n // Remove the filter object.\n removeFilterObject(filterRow.dataset.filterType);\n\n // Remove the actual filter HTML.\n filterRow.remove();\n\n // Update the list of available filter types.\n updateFiltersOptions();\n\n if (hasFilterValue && refreshContent) {\n // Refresh the table if there was any content in this row.\n updateTableFromFilter();\n }\n\n // Update filter fieldset legends.\n const filterLegends = await getAvailableFilterLegends();\n\n getFilterRegion().querySelectorAll(Selectors.filter.region).forEach((filterRow, index) => {\n filterRow.querySelector('legend').innerText = filterLegends[index];\n });\n\n };\n\n /**\n * Replace the specified filter row with a new one.\n *\n * @param {HTMLElement} filterRow\n * @param {Bool} refreshContent Whether to refresh the table content when removing\n * @param {Number} rowNum The number used to label the filter fieldset legend (eg Row 1). Defaults to 1 (the first filter).\n * @return {Promise}\n */\n const replaceFilterRow = (filterRow, refreshContent = true, rowNum = 1) => {\n // Remove the filter object.\n removeFilterObject(filterRow.dataset.filterType);\n\n return Templates.renderForPromise('core_user/local/participantsfilter/filterrow', {\"rownumber\": rowNum})\n .then(({html, js}) => {\n const newContentNodes = Templates.replaceNode(filterRow, html, js);\n\n return newContentNodes;\n })\n .then(filterRow => {\n // Note: This is a nasty hack.\n // We should try to find a better way of doing this.\n // We do not have the list of types in a readily consumable format, so we take the pre-rendered one and copy\n // it in place.\n const typeList = filterSet.querySelector(Selectors.data.typeList);\n\n filterRow.forEach(contentNode => {\n const contentTypeList = contentNode.querySelector(Selectors.filter.fields.type);\n\n if (contentTypeList) {\n contentTypeList.innerHTML = typeList.innerHTML;\n }\n });\n\n return filterRow;\n })\n .then(filterRow => {\n updateFiltersOptions();\n\n return filterRow;\n })\n .then(filterRow => {\n // Refresh the table.\n if (refreshContent) {\n return updateTableFromFilter();\n } else {\n return filterRow;\n }\n })\n .catch(Notification.exception);\n };\n\n /**\n * Remove the Filter Object from the register.\n *\n * @param {string} filterName The name of the filter to be removed\n */\n const removeFilterObject = filterName => {\n if (filterName) {\n const filter = getFilterObject(filterName);\n if (filter) {\n filter.tearDown();\n\n // Remove from the list of active filters.\n delete activeFilters[filterName];\n }\n }\n };\n\n /**\n * Remove all filters.\n *\n * @returns {Promise}\n */\n const removeAllFilters = () => {\n const pendingPromise = new Pending('core_user/participantsfilter:setFilterFromConfig');\n\n const filters = getFilterRegion().querySelectorAll(Selectors.filter.region);\n filters.forEach(filterRow => removeOrReplaceFilterRow(filterRow, false));\n\n // Refresh the table.\n return updateTableFromFilter()\n .then(result => {\n pendingPromise.resolve();\n\n return result;\n });\n };\n\n /**\n * Remove any empty filters.\n */\n const removeEmptyFilters = () => {\n const filters = getFilterRegion().querySelectorAll(Selectors.filter.region);\n filters.forEach(filterRow => {\n const filterType = filterRow.querySelector(Selectors.filter.fields.type);\n if (!filterType.value) {\n removeOrReplaceFilterRow(filterRow, false);\n }\n });\n };\n\n /**\n * Update the list of filter types to filter out those already selected.\n */\n const updateFiltersOptions = () => {\n const filters = getFilterRegion().querySelectorAll(Selectors.filter.region);\n filters.forEach(filterRow => {\n const options = filterRow.querySelectorAll(Selectors.filter.fields.type + ' option');\n options.forEach(option => {\n if (option.value === filterRow.dataset.filterType) {\n option.classList.remove('hidden');\n option.disabled = false;\n } else if (activeFilters[option.value]) {\n option.classList.add('hidden');\n option.disabled = true;\n } else {\n option.classList.remove('hidden');\n option.disabled = false;\n }\n });\n });\n\n // Configure the state of the \"Add row\" button.\n // This button is disabled when there is a filter row available for each condition.\n const addRowButton = filterSet.querySelector(Selectors.filterset.actions.addRow);\n const filterDataNode = filterSet.querySelectorAll(Selectors.data.fields.all);\n if (filterDataNode.length <= filters.length) {\n addRowButton.setAttribute('disabled', 'disabled');\n } else {\n addRowButton.removeAttribute('disabled');\n }\n\n if (filters.length === 1) {\n filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.add('hidden');\n filterSet.querySelector(Selectors.filterset.fields.join).value = 2;\n filterSet.dataset.filterverb = 2;\n } else {\n filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.remove('hidden');\n }\n };\n\n /**\n * Set the current filter options based on a provided configuration.\n *\n * @param {Object} config\n * @param {Number} config.jointype\n * @param {Object} config.filters\n * @returns {Promise}\n */\n const setFilterFromConfig = config => {\n const filterConfig = Object.entries(config.filters);\n\n if (!filterConfig.length) {\n // There are no filters to set from.\n return Promise.resolve();\n }\n\n // Set the main join type.\n filterSet.querySelector(Selectors.filterset.fields.join).value = config.jointype;\n\n const filterPromises = filterConfig.map(([filterType, filterData]) => {\n if (filterType === 'courseid') {\n // The courseid is a special case.\n return false;\n }\n\n const filterValues = filterData.values;\n\n if (!filterValues.length) {\n // There are no values for this filter.\n // Skip it.\n return false;\n }\n\n return addFilterRow().then(([filterRow]) => addFilter(filterRow, filterType, filterValues));\n }).filter(promise => promise);\n\n if (!filterPromises.length) {\n return Promise.resolve();\n }\n\n return Promise.all(filterPromises).then(() => {\n return removeEmptyFilters();\n })\n .then(updateFiltersOptions)\n .then(updateTableFromFilter);\n };\n\n /**\n * Update the Dynamic table based upon the current filter.\n *\n * @return {Promise}\n */\n const updateTableFromFilter = () => {\n const pendingPromise = new Pending('core_user/participantsfilter:updateTableFromFilter');\n\n const filters = {};\n Object.values(activeFilters).forEach(filter => {\n filters[filter.filterValue.name] = filter.filterValue;\n });\n\n return DynamicTable.setFilters(\n DynamicTable.getTableFromId(filterSet.dataset.tableRegion),\n {\n jointype: parseInt(filterSet.querySelector(Selectors.filterset.fields.join).value, 10),\n filters,\n }\n )\n .then(result => {\n pendingPromise.resolve();\n\n return result;\n })\n .catch(Notification.exception);\n };\n\n /**\n * Fetch the strings used to populate the fieldset legends for the maximum number of filters possible.\n *\n * @return {array}\n */\n const getAvailableFilterLegends = async() => {\n const maxFilters = document.querySelector(Selectors.data.typeListSelect).length - 1;\n let requests = [];\n\n [...Array(maxFilters)].forEach((_, rowIndex) => {\n requests.push({\n \"key\": \"filterrowlegend\",\n \"component\": \"core_user\",\n // Add 1 since rows begin at 1 (index begins at zero).\n \"param\": rowIndex + 1\n });\n });\n\n const legendStrings = await getStrings(requests)\n .then(fetchedStrings => {\n return fetchedStrings;\n })\n .catch(Notification.exception);\n\n return legendStrings;\n };\n\n // Add listeners for the main actions.\n filterSet.querySelector(Selectors.filterset.region).addEventListener('click', e => {\n if (e.target.closest(Selectors.filterset.actions.addRow)) {\n e.preventDefault();\n\n addFilterRow();\n }\n\n if (e.target.closest(Selectors.filterset.actions.applyFilters)) {\n e.preventDefault();\n\n updateTableFromFilter();\n }\n\n if (e.target.closest(Selectors.filterset.actions.resetFilters)) {\n e.preventDefault();\n\n removeAllFilters();\n }\n });\n\n // Add the listener to remove a single filter.\n filterSet.querySelector(Selectors.filterset.regions.filterlist).addEventListener('click', e => {\n if (e.target.closest(Selectors.filter.actions.remove)) {\n e.preventDefault();\n\n removeOrReplaceFilterRow(e.target.closest(Selectors.filter.region), true);\n }\n });\n\n // Add listeners for the filter type selection.\n let filterRegion = jQuery(getFilterRegion());\n CustomEvents.define(filterRegion, [CustomEvents.events.accessibleChange]);\n filterRegion.on(CustomEvents.events.accessibleChange, e => {\n const typeField = e.target.closest(Selectors.filter.fields.type);\n if (typeField && typeField.value) {\n const filter = e.target.closest(Selectors.filter.region);\n\n addFilter(filter, typeField.value);\n }\n });\n\n filterSet.querySelector(Selectors.filterset.fields.join).addEventListener('change', e => {\n filterSet.dataset.filterverb = e.target.value;\n });\n\n const tableRoot = DynamicTable.getTableFromId(filterSet.dataset.tableRegion);\n const initialFilters = DynamicTable.getFilters(tableRoot);\n if (initialFilters) {\n const initialFilterPromise = new Pending('core_user/participantsfilter:setFilterFromConfig');\n // Apply the initial filter configuration.\n setFilterFromConfig(initialFilters)\n .then(() => initialFilterPromise.resolve())\n .catch();\n }\n};\n"],"names":["participantsRegionId","filterSet","document","querySelector","activeFilters","courseid","CourseFilter","getFilterRegion","Selectors","filterset","regions","filterlist","addFilterRow","pendingPromise","Pending","rownum","querySelectorAll","filter","region","length","Templates","renderForPromise","then","_ref","html","js","appendNodeContents","filterRow","typeList","data","forEach","contentNode","contentTypeList","fields","type","innerHTML","updateFiltersOptions","result","resolve","catch","Notification","exception","addFilter","async","filterType","initialFilterValues","dataset","filterDataNode","datasource","byName","getFilterDataSource","Filter","GenericFilter","filterTypeClass","typeField","value","disabled","removeOrReplaceFilterRow","refreshContent","replaceFilterRow","removeFilterRow","hasFilterValue","removeFilterObject","remove","updateTableFromFilter","filterLegends","getAvailableFilterLegends","index","innerText","rowNum","_ref2","replaceNode","filterName","tearDown","filters","option","classList","add","addRowButton","actions","addRow","all","setAttribute","removeAttribute","filtermatch","join","filterverb","setFilterFromConfig","config","filterConfig","Object","entries","Promise","jointype","filterPromises","map","_ref3","filterData","filterValues","values","_ref4","promise","filterValue","name","DynamicTable","setFilters","getTableFromId","tableRegion","parseInt","maxFilters","typeListSelect","requests","Array","_","rowIndex","push","fetchedStrings","addEventListener","e","target","closest","preventDefault","applyFilters","resetFilters","removeAllFilters","filterRegion","define","CustomEvents","events","accessibleChange","on","tableRoot","initialFilters","getFilters","initialFilterPromise"],"mappings":"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"}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists